Thread: [Ktutorial-commits] SF.net SVN: ktutorial:[91] trunk/ktutorial/ktutorial-library
Status: Alpha
Brought to you by:
danxuliu
From: <dan...@us...> - 2010-02-23 06:28:20
|
Revision: 91 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=91&view=rev Author: danxuliu Date: 2010-02-23 06:28:13 +0000 (Tue, 23 Feb 2010) Log Message: ----------- When a tutorial is finished the current step shouldn't be remembered. If remembered and the tutorial is started again, its tearDown method would be executed again when changing back to start step. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/Tutorial.cpp trunk/ktutorial/ktutorial-library/test/TutorialTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/Tutorial.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/Tutorial.cpp 2010-02-13 19:41:09 UTC (rev 90) +++ trunk/ktutorial/ktutorial-library/src/Tutorial.cpp 2010-02-23 06:28:13 UTC (rev 91) @@ -87,6 +87,7 @@ void Tutorial::finish() { if (mCurrentStep != 0) { mCurrentStep->setActive(false); + mCurrentStep = 0; } tearDown(); Modified: trunk/ktutorial/ktutorial-library/test/TutorialTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/test/TutorialTest.cpp 2010-02-13 19:41:09 UTC (rev 90) +++ trunk/ktutorial/ktutorial-library/test/TutorialTest.cpp 2010-02-23 06:28:13 UTC (rev 91) @@ -350,6 +350,7 @@ tutorial.start(); tutorial.finish(); + QCOMPARE(tutorial.mCurrentStep, (Step*)0); QVERIFY(!stepStart->isActive()); QCOMPARE(finishedSpy.count(), 1); QCOMPARE(tutorial.mTearDownCount, 1); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-02-23 17:42:12
|
Revision: 98 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=98&view=rev Author: danxuliu Date: 2010-02-23 17:42:05 +0000 (Tue, 23 Feb 2010) Log Message: ----------- The text area says that it is "the" text area, not "a" text area. Honour this in the step text. Spanish localization is also updated to reflect this. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/po/es.po trunk/ktutorial/ktutorial-library/po/ktutorial.pot trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp Modified: trunk/ktutorial/ktutorial-library/po/es.po =================================================================== --- trunk/ktutorial/ktutorial-library/po/es.po 2010-02-23 17:34:50 UTC (rev 97) +++ trunk/ktutorial/ktutorial-library/po/es.po 2010-02-23 17:42:05 UTC (rev 98) @@ -8,8 +8,8 @@ "Project-Id-Version: es\n" "Report-Msgid-Bugs-To: http://sourceforge." "net/tracker/?group_id=301227&atid=1270278\n" -"POT-Creation-Date: 2010-02-23 18:21+0100\n" -"PO-Revision-Date: 2010-02-23 18:25-0500\n" +"POT-Creation-Date: 2010-02-23 18:35+0100\n" +"PO-Revision-Date: 2010-02-23 18:35-0500\n" "Last-Translator: Daniel Calviño Sánchez <dan...@gm...>\n" "Language-Team: Spanish <>\n" "MIME-Version: 1.0\n" @@ -64,9 +64,8 @@ "to the next step.</para>" msgstr "" "<para>Un tutorial se compone de varios pasos. Cada paso contiene un poco de " -"información.</para><para>Hay varias formas de avanzar desde un " -"paso a otro. Por ejemplo, haciendo click en el botón de debajo el tutorial " -"cambiará al " +"información.</para><para>Hay varias formas de avanzar desde un paso a otro. " +"Por ejemplo, haciendo click en el botón de debajo el tutorial cambiará al " "siguiente paso.</para>" #: tutorials/UsingKTutorial.cpp:250 @@ -109,12 +108,12 @@ msgctxt "@info" msgid "" "<para>Do you see the new window that has appeared? Yes, the one that says " -"that it is a text area. Well, the last way to advance from one step to " +"that it is the text area. Well, the last way to advance from one step to " "another is just doing what you are asked for.</para><para>In this case, " "empty the text area erasing all its text and once you have done it look " "again to the tutorial.</para>" msgstr "" -"<para>¿Ves la nueva ventana que ha aparecido? Sí, la que dice que es un área " +"<para>¿Ves la nueva ventana que ha aparecido? Sí, la que dice que es el área " "de texto. Bien, la última forma de avanzar de un paso a otro es simplemente " "haciendo lo que se te pide.</para><para>En este caso, vacía el área de texto " "borrando todo su texto y una vez lo hayas hecho fíjate de nuevo en el " @@ -153,8 +152,7 @@ "lugar distinto. Veamos cómo.</para><para>Sobre estos párrafos puedes ver un " "espacio vacío, al lado del botón de la esquina. Presiona con el botón " "izquierdo de tu ratón en dicho espacio vacío. Simplemente presiona, no " -"sueltes el botón del ratón aún.</" -"para>" +"sueltes el botón del ratón aún.</para>" #: tutorials/UsingKTutorial.cpp:338 msgctxt "@info" Modified: trunk/ktutorial/ktutorial-library/po/ktutorial.pot =================================================================== --- trunk/ktutorial/ktutorial-library/po/ktutorial.pot 2010-02-23 17:34:50 UTC (rev 97) +++ trunk/ktutorial/ktutorial-library/po/ktutorial.pot 2010-02-23 17:42:05 UTC (rev 98) @@ -9,7 +9,7 @@ "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: http://sourceforge.net/tracker/?" "group_id=301227&atid=1270278\n" -"POT-Creation-Date: 2010-02-23 18:21+0100\n" +"POT-Creation-Date: 2010-02-23 18:35+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL...@li...>\n" @@ -91,7 +91,7 @@ msgctxt "@info" msgid "" "<para>Do you see the new window that has appeared? Yes, the one that says " -"that it is a text area. Well, the last way to advance from one step to " +"that it is the text area. Well, the last way to advance from one step to " "another is just doing what you are asked for.</para><para>In this case, " "empty the text area erasing all its text and once you have done it look " "again to the tutorial.</para>" Modified: trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp 2010-02-23 17:34:50 UTC (rev 97) +++ trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp 2010-02-23 17:42:05 UTC (rev 98) @@ -290,7 +290,7 @@ Step* clearTextStep = new ClearTextStep(this); clearTextStep->setText(i18nc("@info", "<para>Do you see the new window that has appeared? Yes, the one that says " -"that it is a text area. Well, the last way to advance from one step to " +"that it is the text area. Well, the last way to advance from one step to " "another is just doing what you are asked for.</para>" "<para>In this case, empty the text area erasing all its text and once you " "have done it look again to the tutorial.</para>")); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-02-23 18:33:48
|
Revision: 100 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=100&view=rev Author: danxuliu Date: 2010-02-23 18:33:39 +0000 (Tue, 23 Feb 2010) Log Message: ----------- Change the ui object in StepWidget and TutorialManagerDialog to a pointer, so ui_XXX.h files aren't included in the header file of these classes. The binary view directory no longer has to be included to find the ui_XXX.h files where the headers of these classes were used. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/tutorials/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/view/StepWidget.cpp trunk/ktutorial/ktutorial-library/src/view/StepWidget.h trunk/ktutorial/ktutorial-library/src/view/TutorialManagerDialog.cpp trunk/ktutorial/ktutorial-library/src/view/TutorialManagerDialog.h trunk/ktutorial/ktutorial-library/test/view/StepWidgetTest.cpp trunk/ktutorial/ktutorial-library/test/view/TutorialManagerDialogTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2010-02-23 17:44:42 UTC (rev 99) +++ trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2010-02-23 18:33:39 UTC (rev 100) @@ -6,7 +6,7 @@ add_subdirectory(tutorials) add_subdirectory(view) -include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/view ${KDE4_INCLUDES}) +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${KDE4_INCLUDES}) set(ktutorial_LIB_SRCS KTutorial.cpp @@ -25,10 +25,6 @@ kde4_add_library(ktutorial SHARED ${ktutorial_LIB_SRCS}) -# ktutorial_view must be built before, so ui_TutorialManagerDialog.h is created -# (needed in KTutorial.cpp as it includes view/TutorialManagerDialog.h) -add_dependencies(ktutorial ktutorial_view) - target_link_libraries(ktutorial ktutorial_scripting ktutorial_tutorials ktutorial_view) ####### Install the library ####### Modified: trunk/ktutorial/ktutorial-library/src/tutorials/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/tutorials/CMakeLists.txt 2010-02-23 17:44:42 UTC (rev 99) +++ trunk/ktutorial/ktutorial-library/src/tutorials/CMakeLists.txt 2010-02-23 18:33:39 UTC (rev 100) @@ -1,4 +1,4 @@ -include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/../view ${KDE4_INCLUDES}) +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${KDE4_INCLUDES}) set(ktutorial_tutorials_SRCS UsingKTutorial.cpp Modified: trunk/ktutorial/ktutorial-library/src/view/StepWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepWidget.cpp 2010-02-23 17:44:42 UTC (rev 99) +++ trunk/ktutorial/ktutorial-library/src/view/StepWidget.cpp 2010-02-23 18:33:39 UTC (rev 100) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008 by Daniel Calviño Sánchez * + * Copyright (C) 2008-2010 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 <klocalizedstring.h> #include "StepWidget.h" +#include "ui_StepWidget.h" #include "../Option.h" #include "../Step.h" @@ -38,28 +39,33 @@ setFrameShape(QFrame::Panel); setLineWidth(1); - ui.setupUi(this); + ui = new Ui::StepWidget(); + ui->setupUi(this); setWindowTitle(i18nc("@title:window", "Tutorial: %1", tutorialName)); - ui.closeButton->setIcon(KIcon("dialog-close")); + ui->closeButton->setIcon(KIcon("dialog-close")); - QSize buttonSize = ui.closeButton->size(); + QSize buttonSize = ui->closeButton->size(); buttonSize.setWidth(buttonSize.height()); // buttonSize.setWidth(ui.closeButton->iconSize().width()); // buttonSize.setHeight(ui.closeButton->iconSize().height()); - ui.closeButton->setFixedSize(buttonSize); + ui->closeButton->setFixedSize(buttonSize); - connect(ui.closeButton, SIGNAL(clicked()), this, SLOT(close())); + connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(close())); - mOptionsLayout = new QHBoxLayout(ui.optionsWidget); + mOptionsLayout = new QHBoxLayout(ui->optionsWidget); } +StepWidget::~StepWidget() { + delete ui; +} + //public slots: void StepWidget::setStep(Step* step) { - ui.textLabel->setText(step->text()); + ui->textLabel->setText(step->text()); setOptions(step->options()); adjustSize(); @@ -82,7 +88,7 @@ void StepWidget::keyPressEvent(QKeyEvent* event) { if (event->modifiers() == Qt::NoModifier && event->key() == Qt::Key_Escape) { - ui.closeButton->animateClick(); + ui->closeButton->animateClick(); event->accept(); return; } else if (event->modifiers() == Qt::ShiftModifier && @@ -131,7 +137,7 @@ //private: void StepWidget::setOptions(const QList<Option*>& options) { - QList<KPushButton*> buttons = ui.optionsWidget->findChildren<KPushButton*>(); + QList<KPushButton*> buttons = ui->optionsWidget->findChildren<KPushButton*>(); QListIterator<KPushButton*> itButtons(buttons); while (itButtons.hasNext()) { KPushButton* button = itButtons.next(); @@ -143,7 +149,7 @@ while (it.hasNext()) { Option* option = it.next(); - KPushButton* button = new KPushButton(ui.optionsWidget); + KPushButton* button = new KPushButton(ui->optionsWidget); button->setText(option->name()); mOptionsLayout->addWidget(button); Modified: trunk/ktutorial/ktutorial-library/src/view/StepWidget.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepWidget.h 2010-02-23 17:44:42 UTC (rev 99) +++ trunk/ktutorial/ktutorial-library/src/view/StepWidget.h 2010-02-23 18:33:39 UTC (rev 100) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008 by Daniel Calviño Sánchez * + * Copyright (C) 2008-2010 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -22,8 +22,6 @@ #include <QFrame> #include <QList> -#include "ui_StepWidget.h" - class QCloseEvent; class QHBoxLayout; class QKeyEvent; @@ -33,6 +31,10 @@ class Option; class Step; +namespace Ui { +class StepWidget; +} + namespace view { /** @@ -62,6 +64,11 @@ */ explicit StepWidget(const QString& tutorialName, QWidget* parent = 0); + /** + * Destroys this StepWidget. + */ + virtual ~StepWidget(); + public slots: /** @@ -166,7 +173,7 @@ /** * The user interface elements. */ - Ui::StepWidget ui; + Ui::StepWidget* ui; /** * The layout where buttons for Options are added. Modified: trunk/ktutorial/ktutorial-library/src/view/TutorialManagerDialog.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/TutorialManagerDialog.cpp 2010-02-23 17:44:42 UTC (rev 99) +++ trunk/ktutorial/ktutorial-library/src/view/TutorialManagerDialog.cpp 2010-02-23 18:33:39 UTC (rev 100) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008 by Daniel Calviño Sánchez * + * Copyright (C) 2008-2010 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -18,6 +18,7 @@ #include "TutorialManagerDialog.h" #include "TutorialListModel.h" +#include "ui_TutorialManagerDialog.h" #include "../TutorialInformation.h" #include "../TutorialManager.h" @@ -32,14 +33,15 @@ mCurrentTutorialInformation(0) { QWidget *widget = new QWidget(this); - ui.setupUi(widget); + ui = new Ui::TutorialManagerDialog(); + ui->setupUi(widget); setMainWidget(widget); setCaption(i18nc("@title:window", "Tutorial manager")); setButtons(KDialog::User1 | KDialog::Close); - ui.tutorialsList->setModel(new TutorialListModel(tutorialManager, this)); + ui->tutorialsList->setModel(new TutorialListModel(tutorialManager, this)); setButtonIcon(User1, KIcon("dialog-ok")); setButtonText(User1, i18nc("@action:button", "Start")); @@ -48,7 +50,7 @@ setDefaultButton(User1); enableButton(User1, false); - connect(ui.tutorialsList->selectionModel(), + connect(ui->tutorialsList->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(select(QItemSelection))); connect(this, SIGNAL(user1Clicked()), this, SLOT(start())); @@ -56,6 +58,10 @@ connect(mTutorialManager, SIGNAL(finished()), this, SLOT(finish())); } +TutorialManagerDialog::~TutorialManagerDialog() { + delete ui; +} + //public slots: void TutorialManagerDialog::finish() { @@ -71,7 +77,7 @@ enableButton(User1, true); - ui.descriptionLabel->setText(tutorialInformation->description()); + ui->descriptionLabel->setText(tutorialInformation->description()); mCurrentTutorialInformation = tutorialInformation; } Modified: trunk/ktutorial/ktutorial-library/src/view/TutorialManagerDialog.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/TutorialManagerDialog.h 2010-02-23 17:44:42 UTC (rev 99) +++ trunk/ktutorial/ktutorial-library/src/view/TutorialManagerDialog.h 2010-02-23 18:33:39 UTC (rev 100) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008 by Daniel Calviño Sánchez * + * Copyright (C) 2008-2010 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -19,14 +19,18 @@ #ifndef VIEW_TUTORIALMANAGERDIALOG_H #define VIEW_TUTORIALMANAGERDIALOG_H +#include <QItemSelection> + #include <kdialog.h> #include <klocalizedstring.h> -#include "ui_TutorialManagerDialog.h" - class TutorialInformation; class TutorialManager; +namespace Ui { +class TutorialManagerDialog; +} + namespace view { /** @@ -53,6 +57,11 @@ explicit TutorialManagerDialog(TutorialManager* tutorialManager, QWidget* parent = 0); + /** + * Destroys this TutorialManagerDialog. + */ + virtual ~TutorialManagerDialog(); + public slots: /** @@ -71,7 +80,7 @@ /** * The user interface elements. */ - Ui::TutorialManagerDialog ui; + Ui::TutorialManagerDialog* ui; /** * The TutorialInformation of the last activated tutorial in the list. Modified: trunk/ktutorial/ktutorial-library/test/view/StepWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/test/view/StepWidgetTest.cpp 2010-02-23 17:44:42 UTC (rev 99) +++ trunk/ktutorial/ktutorial-library/test/view/StepWidgetTest.cpp 2010-02-23 18:33:39 UTC (rev 100) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2009 by Daniel Calviño Sánchez * + * Copyright (C) 2009-2010 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -27,6 +27,7 @@ #undef private #undef protected +#include "ui_StepWidget.h" #include "../Option.h" #include "../Step.h" @@ -312,11 +313,11 @@ /////////////////////////////////// Helpers //////////////////////////////////// QLabel* StepWidgetTest::textLabel(StepWidget* stepWidget) { - return stepWidget->ui.textLabel; + return stepWidget->ui->textLabel; } KPushButton* StepWidgetTest::closeButton(StepWidget* stepWidget) { - return stepWidget->ui.closeButton; + return stepWidget->ui->closeButton; } } Modified: trunk/ktutorial/ktutorial-library/test/view/TutorialManagerDialogTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/test/view/TutorialManagerDialogTest.cpp 2010-02-23 17:44:42 UTC (rev 99) +++ trunk/ktutorial/ktutorial-library/test/view/TutorialManagerDialogTest.cpp 2010-02-23 18:33:39 UTC (rev 100) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2009 by Daniel Calviño Sánchez * + * Copyright (C) 2009-2010 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -27,6 +27,7 @@ #undef private #undef protected +#include "ui_TutorialManagerDialog.h" #include "../Step.h" #include "../Tutorial.h" #include "../TutorialInformation.h" @@ -312,12 +313,12 @@ QAbstractItemView* TutorialManagerDialogTest::tutorialList( TutorialManagerDialog* tutorialManagerDialog) { - return tutorialManagerDialog->ui.tutorialsList; + return tutorialManagerDialog->ui->tutorialsList; } QLabel* TutorialManagerDialogTest::descriptionLabel( TutorialManagerDialog* tutorialManagerDialog) { - return tutorialManagerDialog->ui.descriptionLabel; + return tutorialManagerDialog->ui->descriptionLabel; } KPushButton* TutorialManagerDialogTest::startButton( This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-03-21 03:12:33
|
Revision: 173 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=173&view=rev Author: danxuliu Date: 2010-03-21 03:12:26 +0000 (Sun, 21 Mar 2010) Log Message: ----------- Rename "test" directory to "tests" Modified Paths: -------------- trunk/ktutorial/ktutorial-library/CMakeLists.txt Added Paths: ----------- trunk/ktutorial/ktutorial-library/tests/ trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/StepTest.cpp trunk/ktutorial/ktutorial-library/tests/TutorialTest.cpp trunk/ktutorial/ktutorial-library/tests/WaitForEventTest.cpp trunk/ktutorial/ktutorial-library/tests/scripting/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingModuleTest.cpp trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingTest.cpp trunk/ktutorial/ktutorial-library/tests/view/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/view/StepWidgetTest.cpp trunk/ktutorial/ktutorial-library/tests/view/TutorialManagerDialogTest.cpp Removed Paths: ------------- trunk/ktutorial/ktutorial-library/test/ trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/StepTest.cpp trunk/ktutorial/ktutorial-library/tests/TutorialTest.cpp trunk/ktutorial/ktutorial-library/tests/scripting/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingModuleTest.cpp trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingTest.cpp trunk/ktutorial/ktutorial-library/tests/view/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/view/StepWidgetTest.cpp trunk/ktutorial/ktutorial-library/tests/view/TutorialManagerDialogTest.cpp Modified: trunk/ktutorial/ktutorial-library/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/CMakeLists.txt 2010-03-21 01:03:42 UTC (rev 172) +++ trunk/ktutorial/ktutorial-library/CMakeLists.txt 2010-03-21 03:12:26 UTC (rev 173) @@ -8,4 +8,4 @@ add_subdirectory(cmake) add_subdirectory(po) add_subdirectory(src) -add_subdirectory(test) +add_subdirectory(tests) Deleted: trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/test/CMakeLists.txt 2010-02-13 19:08:14 UTC (rev 88) +++ trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2010-03-21 03:12:26 UTC (rev 173) @@ -1,25 +0,0 @@ -add_subdirectory(scripting) -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(${CMAKE_CURRENT_BINARY_DIR} ${ktutorial_SOURCE_DIR}/src ${KDE4_INCLUDES}) - -MACRO(UNIT_TESTS) - FOREACH(_className ${ARGN}) - set(_testName ${_className}Test) - kde4_add_unit_test(${_testName} TESTNAME ktutorial-${_testName} ${_testName}.cpp) - target_link_libraries(${_testName} ktutorial ${QT_QTTEST_LIBRARY}) - ENDFOREACH(_className) -ENDMACRO(UNIT_TESTS) - -unit_tests(Option Step Tutorial TutorialInformation TutorialManager WaitFor WaitForAnd WaitForComposed WaitForNot WaitForOr WaitForSignal) - -MACRO(MEM_TESTS) - FOREACH(_testname ${ARGN}) - add_test(ktutorial-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(Option Step Tutorial TutorialInformation TutorialManager WaitFor WaitForAnd WaitForComposed WaitForNot WaitForOr WaitForSignal) Copied: trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt (from rev 140, trunk/ktutorial/ktutorial-library/test/CMakeLists.txt) =================================================================== --- trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2010-03-21 03:12:26 UTC (rev 173) @@ -0,0 +1,51 @@ +add_subdirectory(scripting) +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(${CMAKE_CURRENT_BINARY_DIR} ${ktutorial-library_SOURCE_DIR}/src ${KDE4_INCLUDES}) + +MACRO(UNIT_TESTS) + FOREACH(_className ${ARGN}) + set(_testName ${_className}Test) + kde4_add_unit_test(${_testName} TESTNAME ktutorial-${_testName} ${_testName}.cpp) + target_link_libraries(${_testName} ktutorial ${QT_QTTEST_LIBRARY}) + ENDFOREACH(_className) +ENDMACRO(UNIT_TESTS) + +unit_tests( + Option + Step + Tutorial + TutorialInformation + TutorialManager + WaitFor + WaitForAnd + WaitForComposed + WaitForEvent + WaitForNot + WaitForOr + WaitForSignal +) + +MACRO(MEM_TESTS) + FOREACH(_testname ${ARGN}) + add_test(ktutorial-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( + Option + Step + Tutorial + TutorialInformation + TutorialManager + WaitFor + WaitForAnd + WaitForComposed + WaitForEvent + WaitForNot + WaitForOr + WaitForSignal +) Deleted: trunk/ktutorial/ktutorial-library/tests/StepTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/test/StepTest.cpp 2010-02-13 19:08:14 UTC (rev 88) +++ trunk/ktutorial/ktutorial-library/tests/StepTest.cpp 2010-03-21 03:12:26 UTC (rev 173) @@ -1,551 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008-2009 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> - -#define protected public -#define private public -#include "Step.h" -#undef private -#undef protected - -#include "Option.h" -#include "WaitForSignal.h" - -class StepTest: public QObject { -Q_OBJECT - -public slots: - - void dummySlot() { - mDummySlotCallCount++; - } - - void anotherDummySlot() { - mAnotherDummySlotCallCount++; - } - -signals: - - void dummySignal(); - - void anotherDummySignal(); - - void thirdDummySignal(); - -private: - - int mDummySlotCallCount; - int mAnotherDummySlotCallCount; - -private slots: - - void init() { - mDummySlotCallCount = 0; - mAnotherDummySlotCallCount = 0; - } - - void testConstructor(); - - void testSetText(); - - void testSetActiveTrue(); - void testSetActiveFalse(); - - void testAddOption(); - void testAddOptionWithoutSlotMacro(); - void testAddOptionSeveralOptions(); - void testAddOptionTwice(); - void testAddOptionDifferentOptionsWithSameName(); - - void testAddWaitFor(); - void testAddWaitForWithoutSlotMacro(); - void testAddWaitForSeveralWaitFors(); - void testAddWaitForTwice(); - - void testRemoveOption(); - void testRemoveOptionSeveralOptions(); - - void testRemoveWaitFor(); - void testRemoveWaitForSeveralWaitFors(); - -}; - -class InspectedStep: public Step { -public: - - int mSetupCount; - int mTearDownCount; - - InspectedStep(const QString& id): Step(id) { - mSetupCount = 0; - mTearDownCount = 0; - } - -protected: - - virtual void setup() { - mSetupCount++; - } - - virtual void tearDown() { - mTearDownCount++; - } - -}; - -void StepTest::testConstructor() { - InspectedStep step("doSomethingConstructive"); - - QCOMPARE(step.id(), QString("doSomethingConstructive")); - QCOMPARE(step.options().size(), 0); - QCOMPARE(step.text(), QString("")); - QVERIFY(!step.isActive()); - QCOMPARE(step.mSetupCount, 0); - QCOMPARE(step.mTearDownCount, 0); -} - -void StepTest::testSetText() { - Step step("doSomethingConstructive"); - - step.setText("OK, you beat the game, now turn off your computer and do something constructive"); - - QCOMPARE(step.text(), QString("OK, you beat the game, now turn off your computer and do something constructive")); -} - -void StepTest::testSetActiveTrue() { - InspectedStep step("doSomethingConstructive"); - - step.setActive(true); - - QVERIFY(step.isActive()); - QCOMPARE(step.mSetupCount, 1); - QCOMPARE(step.mTearDownCount, 0); -} - -void StepTest::testSetActiveFalse() { - InspectedStep step("doSomethingConstructive"); - - step.setActive(false); - - QVERIFY(!step.isActive()); - QCOMPARE(step.mSetupCount, 0); - QCOMPARE(step.mTearDownCount, 1); -} - -void StepTest::testAddOption() { - Step step("doSomethingConstructive"); - - Option* option1 = new Option("Bathe your iguana"); - - step.addOption(option1, this, SLOT(dummySlot())); - connect(this, SIGNAL(dummySignal()), option1, SIGNAL(selected())); - - QCOMPARE(option1->parent(), &step); - QCOMPARE(step.options().count(), 1); - QVERIFY(step.options().contains(option1)); - QCOMPARE(mDummySlotCallCount, 0); - - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 0); - - step.setActive(true); - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 1); -} - -void StepTest::testAddOptionWithoutSlotMacro() { - Step step("doSomethingConstructive"); - - Option* option1 = new Option("Bathe your iguana"); - - step.addOption(option1, this, "dummySlot()"); - connect(this, SIGNAL(dummySignal()), option1, SIGNAL(selected())); - - QCOMPARE(option1->parent(), &step); - QCOMPARE(step.options().count(), 1); - QVERIFY(step.options().contains(option1)); - QCOMPARE(mDummySlotCallCount, 0); - - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 0); - - step.setActive(true); - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 1); -} - -void StepTest::testAddOptionSeveralOptions() { - Step step("doSomethingConstructive"); - - Option* option1 = new Option("Bathe your iguana"); - step.addOption(option1, this, SLOT(dummySlot())); - connect(this, SIGNAL(dummySignal()), option1, SIGNAL(selected())); - - Option* option2 = new Option("Feed a toucan"); - step.addOption(option2, this, SLOT(dummySlot())); - connect(this, SIGNAL(anotherDummySignal()), option2, SIGNAL(selected())); - - Option* option3 = new Option("Walk the platypus"); - step.addOption(option3, this, SLOT(dummySlot())); - connect(this, SIGNAL(dummySignal()), option3, SIGNAL(selected())); - - QCOMPARE(option1->parent(), &step); - QCOMPARE(option2->parent(), &step); - QCOMPARE(option3->parent(), &step); - QCOMPARE(step.options().count(), 3); - QVERIFY(step.options().contains(option1)); - QVERIFY(step.options().contains(option2)); - QVERIFY(step.options().contains(option3)); - QCOMPARE(mDummySlotCallCount, 0); - - emit dummySignal(); - emit anotherDummySignal(); - QCOMPARE(mDummySlotCallCount, 0); - - step.setActive(true); - emit anotherDummySignal(); - QCOMPARE(mDummySlotCallCount, 1); - - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 3); -} - -void StepTest::testAddOptionTwice() { - Step step("doSomethingConstructive"); - - Option* option1 = new Option("Bathe your iguana"); - step.addOption(option1, this, SLOT(dummySlot())); - connect(this, SIGNAL(dummySignal()), option1, SIGNAL(selected())); - - //This second option isn't really needed, but it is used to be sure that no - //strange side effects occur after adding the first option again - Option* option2 = new Option("Feed a toucan"); - step.addOption(option2, this, SLOT(dummySlot())); - - step.addOption(option1, this, SLOT(anotherDummySlot())); - - QCOMPARE(option1->parent(), &step); - QCOMPARE(option2->parent(), &step); - QCOMPARE(step.options().count(), 2); - QVERIFY(step.options().contains(option1)); - QVERIFY(step.options().contains(option2)); - - step.setActive(true); - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 1); - QCOMPARE(mAnotherDummySlotCallCount, 0); -} - -void StepTest::testAddOptionDifferentOptionsWithSameName() { - Step step("doSomethingConstructive"); - - Option* option1 = new Option("Bathe your iguana"); - step.addOption(option1, this, SLOT(dummySlot())); - connect(this, SIGNAL(dummySignal()), option1, SIGNAL(selected())); - - //This second option isn't really needed, but it is used to be sure that no - //strange side effects occur after adding the option with the repeated name - Option* option2 = new Option("Feed a toucan"); - step.addOption(option2, this, SLOT(dummySlot())); - - //It will not be added and thus not deleted by parent Step, so it is created - //in stack - Option option3("Bathe your iguana"); - - step.addOption(&option3, this, SLOT(anotherDummySlot())); - connect(this, SIGNAL(dummySignal()), &option3, SIGNAL(selected())); - - QCOMPARE(option1->parent(), &step); - QCOMPARE(option2->parent(), &step); - QCOMPARE(option3.parent(), (QObject*)0); - QCOMPARE(step.options().count(), 2); - QVERIFY(step.options().contains(option1)); - QVERIFY(step.options().contains(option2)); - - step.setActive(true); - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 1); - QCOMPARE(mAnotherDummySlotCallCount, 0); -} - -void StepTest::testAddWaitFor() { - Step step("doSomethingConstructive"); - - WaitFor* waitFor1 = new WaitForSignal(this, SIGNAL(dummySignal())); - - step.addWaitFor(waitFor1, this, SLOT(dummySlot())); - - QCOMPARE(waitFor1->parent(), &step); - QCOMPARE(step.mWaitsFor.count(), 1); - QVERIFY(step.mWaitsFor.contains(waitFor1)); - QCOMPARE(mDummySlotCallCount, 0); - - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 0); - - step.setActive(true); - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 1); -} - -void StepTest::testAddWaitForWithoutSlotMacro() { - Step step("doSomethingConstructive"); - - WaitFor* waitFor1 = new WaitForSignal(this, SIGNAL(dummySignal())); - - step.addWaitFor(waitFor1, this, "dummySlot()"); - - QCOMPARE(waitFor1->parent(), &step); - QCOMPARE(step.mWaitsFor.count(), 1); - QVERIFY(step.mWaitsFor.contains(waitFor1)); - QCOMPARE(mDummySlotCallCount, 0); - - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 0); - - step.setActive(true); - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 1); -} - -void StepTest::testAddWaitForSeveralWaitFors() { - Step step("doSomethingConstructive"); - - WaitFor* waitFor1 = new WaitForSignal(this, SIGNAL(dummySignal())); - step.addWaitFor(waitFor1, this, SLOT(dummySlot())); - - WaitFor* waitFor2 = new WaitForSignal(this, SIGNAL(anotherDummySignal())); - step.addWaitFor(waitFor2, this, SLOT(dummySlot())); - - WaitFor* waitFor3 = new WaitForSignal(this, SIGNAL(thirdDummySignal())); - step.addWaitFor(waitFor3, this, SLOT(dummySlot())); - - QCOMPARE(waitFor1->parent(), &step); - QCOMPARE(waitFor2->parent(), &step); - QCOMPARE(waitFor3->parent(), &step); - QCOMPARE(step.mWaitsFor.count(), 3); - QVERIFY(step.mWaitsFor.contains(waitFor1)); - QVERIFY(step.mWaitsFor.contains(waitFor2)); - QVERIFY(step.mWaitsFor.contains(waitFor3)); - QCOMPARE(mDummySlotCallCount, 0); - - emit dummySignal(); - emit anotherDummySignal(); - emit thirdDummySignal(); - QCOMPARE(mDummySlotCallCount, 0); - - step.setActive(true); - emit anotherDummySignal(); - QCOMPARE(mDummySlotCallCount, 1); - - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 2); - - emit thirdDummySignal(); - QCOMPARE(mDummySlotCallCount, 3); -} - -void StepTest::testAddWaitForTwice() { - Step step("doSomethingConstructive"); - - WaitFor* waitFor1 = new WaitForSignal(this, SIGNAL(dummySignal())); - step.addWaitFor(waitFor1, this, SLOT(dummySlot())); - - //This second WaitFor isn't really needed, but it is used to be sure that no - //strange side effects occur after adding the first WaitFor again - WaitFor* waitFor2 = new WaitForSignal(this, SIGNAL(anotherDummySignal())); - step.addWaitFor(waitFor2, this, SLOT(dummySlot())); - - step.addWaitFor(waitFor1, this, SLOT(anotherDummySlot())); - - QCOMPARE(waitFor1->parent(), &step); - QCOMPARE(waitFor2->parent(), &step); - QCOMPARE(step.mWaitsFor.count(), 2); - QVERIFY(step.mWaitsFor.contains(waitFor1)); - QVERIFY(step.mWaitsFor.contains(waitFor2)); - - step.setActive(true); - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 1); - QCOMPARE(mAnotherDummySlotCallCount, 0); -} - -void StepTest::testRemoveOption() { - Step step("doSomethingConstructive"); - - Option* option1 = new Option("Bathe your iguana"); - step.addOption(option1, this, SLOT(dummySlot())); - connect(this, SIGNAL(dummySignal()), option1, SIGNAL(selected())); - - //It will be removed and not deleted by parent Step, so it is created in - //stack - Option option2("Feed a toucan"); - step.addOption(&option2, this, SLOT(anotherDummySlot())); - connect(this, SIGNAL(dummySignal()), &option2, SIGNAL(selected())); - - step.removeOption(&option2); - - QCOMPARE(option1->parent(), &step); - QCOMPARE(option2.parent(), (QObject*)0); - QCOMPARE(step.options().count(), 1); - QVERIFY(step.options().contains(option1)); - QVERIFY(!step.options().contains(&option2)); - QCOMPARE(mDummySlotCallCount, 0); - QCOMPARE(mAnotherDummySlotCallCount, 0); - - step.setActive(true); - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 1); - QCOMPARE(mAnotherDummySlotCallCount, 0); -} - -void StepTest::testRemoveOptionSeveralOptions() { - Step step("doSomethingConstructive"); - - //They will be removed and not deleted by parent Step, so they are created - //in stack - Option option1("Bathe your iguana"); - step.addOption(&option1, this, SLOT(anotherDummySlot())); - connect(this, SIGNAL(dummySignal()), &option1, SIGNAL(selected())); - - Option option2("Feed a toucan"); - step.addOption(&option2, this, SLOT(dummySlot())); - connect(this, SIGNAL(dummySignal()), &option2, SIGNAL(selected())); - - Option option3("Walk the platypus"); - step.addOption(&option3, this, SLOT(anotherDummySlot())); - connect(this, SIGNAL(dummySignal()), &option3, SIGNAL(selected())); - - step.removeOption(&option1); - step.removeOption(&option3); - - QCOMPARE(option1.parent(), (QObject*)0); - QCOMPARE(option2.parent(), &step); - QCOMPARE(option3.parent(), (QObject*)0); - QCOMPARE(step.options().count(), 1); - QVERIFY(step.options().contains(&option2)); - QVERIFY(!step.options().contains(&option1)); - QVERIFY(!step.options().contains(&option3)); - QCOMPARE(mDummySlotCallCount, 0); - QCOMPARE(mAnotherDummySlotCallCount, 0); - - step.setActive(true); - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 1); - QCOMPARE(mAnotherDummySlotCallCount, 0); - - step.removeOption(&option2); - - QCOMPARE(option2.parent(), (QObject*)0); - QCOMPARE(step.options().count(), 0); - QVERIFY(!step.options().contains(&option2)); - - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 1); - QCOMPARE(mAnotherDummySlotCallCount, 0); -} - -void StepTest::testRemoveWaitFor() { - Step step("doSomethingConstructive"); - - WaitForSignal* waitFor1 = new WaitForSignal(this, SIGNAL(dummySignal())); - step.addWaitFor(waitFor1, this, SLOT(dummySlot())); - - //It will be removed and not deleted by parent Step, so it is created in - //stack - WaitForSignal waitFor2(this, SIGNAL(anotherDummySignal())); - step.addWaitFor(&waitFor2, this, SLOT(anotherDummySlot())); - - step.setActive(true); - - step.removeWaitFor(&waitFor2); - - QCOMPARE(waitFor1->parent(), &step); - QCOMPARE(waitFor2.parent(), (QObject*)0); - QCOMPARE(step.mWaitsFor.count(), 1); - QVERIFY(step.mWaitsFor.contains(waitFor1)); - QVERIFY(!step.mWaitsFor.contains(&waitFor2)); - QVERIFY(waitFor1->isActive()); - QVERIFY(!waitFor2.isActive()); - QCOMPARE(mDummySlotCallCount, 0); - QCOMPARE(mAnotherDummySlotCallCount, 0); - - waitFor2.setActive(true); - emit dummySignal(); - emit anotherDummySignal(); - QCOMPARE(mDummySlotCallCount, 1); - QCOMPARE(mAnotherDummySlotCallCount, 0); -} - -void StepTest::testRemoveWaitForSeveralWaitFors() { - Step step("doSomethingConstructive"); - - //They will be removed and not deleted by parent Step, so they are created - //in stack - WaitForSignal waitFor1(this, SIGNAL(dummySignal())); - step.addWaitFor(&waitFor1, this, SLOT(anotherDummySlot())); - - WaitForSignal waitFor2(this, SIGNAL(anotherDummySignal())); - step.addWaitFor(&waitFor2, this, SLOT(dummySlot())); - - WaitForSignal waitFor3(this, SIGNAL(thirdDummySignal())); - step.addWaitFor(&waitFor3, this, SLOT(anotherDummySlot())); - - step.setActive(true); - - step.removeWaitFor(&waitFor1); - step.removeWaitFor(&waitFor3); - - QCOMPARE(waitFor1.parent(), (QObject*)0); - QCOMPARE(waitFor2.parent(), &step); - QCOMPARE(waitFor3.parent(), (QObject*)0); - QCOMPARE(step.mWaitsFor.count(), 1); - QVERIFY(step.mWaitsFor.contains(&waitFor2)); - QVERIFY(!step.mWaitsFor.contains(&waitFor1)); - QVERIFY(!step.mWaitsFor.contains(&waitFor3)); - QVERIFY(!waitFor1.isActive()); - QVERIFY(waitFor2.isActive()); - QVERIFY(!waitFor3.isActive()); - QCOMPARE(mDummySlotCallCount, 0); - QCOMPARE(mAnotherDummySlotCallCount, 0); - - step.setActive(true); - waitFor1.setActive(true); - waitFor3.setActive(true); - emit dummySignal(); - emit anotherDummySignal(); - emit thirdDummySignal(); - QCOMPARE(mDummySlotCallCount, 1); - QCOMPARE(mAnotherDummySlotCallCount, 0); - - step.removeWaitFor(&waitFor2); - - QCOMPARE(waitFor2.parent(), (QObject*)0); - QCOMPARE(step.mWaitsFor.count(), 0); - QVERIFY(!step.mWaitsFor.contains(&waitFor2)); - QVERIFY(!waitFor2.isActive()); - - waitFor2.setActive(true); - emit dummySignal(); - QCOMPARE(mDummySlotCallCount, 1); -} - -QTEST_MAIN(StepTest) - -#include "StepTest.moc" Copied: trunk/ktutorial/ktutorial-library/tests/StepTest.cpp (from rev 108, trunk/ktutorial/ktutorial-library/test/StepTest.cpp) =================================================================== --- trunk/ktutorial/ktutorial-library/tests/StepTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/StepTest.cpp 2010-03-21 03:12:26 UTC (rev 173) @@ -0,0 +1,939 @@ +/*************************************************************************** + * Copyright (C) 2008-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> + +#define protected public +#define private public +#include "Step.h" +#undef private +#undef protected + +#include "Option.h" +#include "WaitForSignal.h" + +class StepTest: public QObject { +Q_OBJECT + +public slots: + + void dummySlot() { + mDummySlotCallCount++; + } + + void anotherDummySlot() { + mAnotherDummySlotCallCount++; + } + +signals: + + void dummySignal(); + + void anotherDummySignal(); + + void thirdDummySignal(); + + void fourthDummySignal(); + +private: + + int mDummySlotCallCount; + int mAnotherDummySlotCallCount; + + void assertStepId(const QSignalSpy& spy, int index, const QString& stepId); + +private slots: + + void init() { + mDummySlotCallCount = 0; + mAnotherDummySlotCallCount = 0; + } + + void testConstructor(); + + void testSetText(); + + void testSetActiveTrue(); + void testSetActiveFalse(); + + void testAddOption(); + void testAddOptionWithoutSlotMacro(); + void testAddOptionAssociatedToStepId(); + void testAddOptionSeveralOptions(); + void testAddOptionDuringSetup(); + void testAddOptionNormalAndDuringSetup(); + void testAddOptionTwice(); + void testAddOptionDifferentOptionsWithSameName(); + + void testAddWaitFor(); + void testAddWaitForWithoutSlotMacro(); + void testAddWaitForAssociatedToStepId(); + void testAddWaitForSeveralWaitFors(); + void testAddWaitForDuringSetup(); + void testAddWaitForNormalAndDuringSetup(); + void testAddWaitForTwice(); + + void testRemoveOption(); + void testRemoveOptionAssociatedToStepId(); + void testRemoveOptionSeveralOptions(); + + void testRemoveWaitFor(); + void testRemoveWaitForAssociatedToStepId(); + void testRemoveWaitForSeveralWaitFors(); + +}; + +class InspectedStep: public Step { +public: + + int mSetupCount; + int mTearDownCount; + + InspectedStep(const QString& id): Step(id) { + mSetupCount = 0; + mTearDownCount = 0; + } + +protected: + + virtual void setup() { + mSetupCount++; + } + + virtual void tearDown() { + mTearDownCount++; + } + +}; + +class StepWithWaitForAddedInSetup: public Step { +public: + + StepTest* mStepTest; + WaitFor* mWaitFor; + + StepWithWaitForAddedInSetup(const QString& id, StepTest* stepTest): + Step(id), + mStepTest(stepTest), + mWaitFor(0) { + } + +protected: + + virtual void setup() { + mWaitFor = new WaitForSignal(mStepTest, SIGNAL(dummySignal())); + addWaitFor(mWaitFor, mStepTest, SLOT(dummySlot())); + } + +}; + +class StepWithOptionAddedInSetup: public Step { +public: + + StepTest* mStepTest; + Option* mOption; + + StepWithOptionAddedInSetup(const QString& id, StepTest* stepTest): + Step(id), + mStepTest(stepTest), + mOption(0) { + } + +protected: + + virtual void setup() { + mOption = new Option("Bathe your iguana"); + addOption(mOption, mStepTest, SLOT(dummySlot())); + } + +}; + +void StepTest::testConstructor() { + InspectedStep step("doSomethingConstructive"); + + QCOMPARE(step.id(), QString("doSomethingConstructive")); + QCOMPARE(step.options().size(), 0); + QCOMPARE(step.text(), QString("")); + QVERIFY(!step.isActive()); + QCOMPARE(step.mSetupCount, 0); + QCOMPARE(step.mTearDownCount, 0); +} + +void StepTest::testSetText() { + Step step("doSomethingConstructive"); + + step.setText("OK, you beat the game, now turn off your computer and do something constructive"); + + QCOMPARE(step.text(), QString("OK, you beat the game, now turn off your computer and do something constructive")); +} + +void StepTest::testSetActiveTrue() { + InspectedStep step("doSomethingConstructive"); + + step.setActive(true); + + QVERIFY(step.isActive()); + QCOMPARE(step.mSetupCount, 1); + QCOMPARE(step.mTearDownCount, 0); +} + +void StepTest::testSetActiveFalse() { + InspectedStep step("doSomethingConstructive"); + + step.setActive(false); + + QVERIFY(!step.isActive()); + QCOMPARE(step.mSetupCount, 0); + QCOMPARE(step.mTearDownCount, 1); +} + +void StepTest::testAddOption() { + Step step("doSomethingConstructive"); + + Option* option1 = new Option("Bathe your iguana"); + + step.addOption(option1, this, SLOT(dummySlot())); + connect(this, SIGNAL(dummySignal()), option1, SIGNAL(selected())); + + QCOMPARE(option1->parent(), &step); + QCOMPARE(step.options().count(), 1); + QVERIFY(step.options().contains(option1)); + QCOMPARE(mDummySlotCallCount, 0); + + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 0); + + step.setActive(true); + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); +} + +void StepTest::testAddOptionWithoutSlotMacro() { + Step step("doSomethingConstructive"); + + Option* option1 = new Option("Bathe your iguana"); + + step.addOption(option1, this, "dummySlot()"); + connect(this, SIGNAL(dummySignal()), option1, SIGNAL(selected())); + + QCOMPARE(option1->parent(), &step); + QCOMPARE(step.options().count(), 1); + QVERIFY(step.options().contains(option1)); + QCOMPARE(mDummySlotCallCount, 0); + + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 0); + + step.setActive(true); + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); +} + +void StepTest::testAddOptionAssociatedToStepId() { + Step step("doSomethingConstructive"); + + Option* option1 = new Option("Bathe your iguana"); + + step.addOption(option1, "batheYourIguanaStep"); + connect(this, SIGNAL(dummySignal()), option1, SIGNAL(selected())); + + QSignalSpy nextStepRequestedSpy(&step, SIGNAL(nextStepRequested(QString))); + + QCOMPARE(option1->parent(), &step); + QCOMPARE(step.options().count(), 1); + QVERIFY(step.options().contains(option1)); + QCOMPARE(nextStepRequestedSpy.count(), 0); + + emit dummySignal(); + QCOMPARE(nextStepRequestedSpy.count(), 0); + + step.setActive(true); + emit dummySignal(); + QCOMPARE(nextStepRequestedSpy.count(), 1); + assertStepId(nextStepRequestedSpy, 0, "batheYourIguanaStep"); +} + +void StepTest::testAddOptionSeveralOptions() { + Step step("doSomethingConstructive"); + + Option* option1 = new Option("Bathe your iguana"); + step.addOption(option1, this, SLOT(dummySlot())); + connect(this, SIGNAL(dummySignal()), option1, SIGNAL(selected())); + + Option* option2 = new Option("Feed a toucan"); + step.addOption(option2, this, SLOT(dummySlot())); + connect(this, SIGNAL(anotherDummySignal()), option2, SIGNAL(selected())); + + Option* option3 = new Option("Walk the platypus"); + step.addOption(option3, this, SLOT(dummySlot())); + connect(this, SIGNAL(dummySignal()), option3, SIGNAL(selected())); + + Option* option4 = new Option("Lull the penguin"); + step.addOption(option4, "lullThePenguinStep"); + connect(this, SIGNAL(anotherDummySignal()), option4, SIGNAL(selected())); + + Option* option5 = new Option("Pamper the Tasmanian devil"); + step.addOption(option5, "pamperTheTasmanianDevilStep"); + connect(this, SIGNAL(dummySignal()), option5, SIGNAL(selected())); + + QSignalSpy nextStepRequestedSpy(&step, SIGNAL(nextStepRequested(QString))); + + QCOMPARE(option1->parent(), &step); + QCOMPARE(option2->parent(), &step); + QCOMPARE(option3->parent(), &step); + QCOMPARE(option4->parent(), &step); + QCOMPARE(option5->parent(), &step); + QCOMPARE(step.options().count(), 5); + QVERIFY(step.options().contains(option1)); + QVERIFY(step.options().contains(option2)); + QVERIFY(step.options().contains(option3)); + QVERIFY(step.options().contains(option4)); + QVERIFY(step.options().contains(option5)); + QCOMPARE(mDummySlotCallCount, 0); + QCOMPARE(nextStepRequestedSpy.count(), 0); + + emit dummySignal(); + emit anotherDummySignal(); + QCOMPARE(mDummySlotCallCount, 0); + QCOMPARE(nextStepRequestedSpy.count(), 0); + + step.setActive(true); + emit anotherDummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + QCOMPARE(nextStepRequestedSpy.count(), 1); + assertStepId(nextStepRequestedSpy, 0, "lullThePenguinStep"); + + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 3); + QCOMPARE(nextStepRequestedSpy.count(), 2); + assertStepId(nextStepRequestedSpy, 1, "pamperTheTasmanianDevilStep"); +} + +void StepTest::testAddOptionDuringSetup() { + StepWithOptionAddedInSetup step("doSomethingConstructive", this); + + step.setActive(true); + + connect(this, SIGNAL(dummySignal()), step.mOption, SIGNAL(selected())); + + QCOMPARE(step.mOption->parent(), &step); + QCOMPARE(step.options().count(), 1); + QVERIFY(step.options().contains(step.mOption)); + QCOMPARE(mDummySlotCallCount, 0); + + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + + QSignalSpy destroyedSpy(step.mOption, SIGNAL(destroyed(QObject*))); + + step.setActive(false); + + QCOMPARE(step.options().count(), 0); + QCOMPARE(step.mWaitsForToBeDeletedInTearDown.count(), 0); + QCOMPARE(step.mOptionsToBeDeletedInTearDown.count(), 0); + QCOMPARE(destroyedSpy.count(), 1); +} + +void StepTest::testAddOptionNormalAndDuringSetup() { + StepWithOptionAddedInSetup step("doSomethingConstructive", this); + + Option* option1 = new Option("Feed a toucan"); + + step.addOption(option1, this, SLOT(anotherDummySlot())); + connect(this, SIGNAL(anotherDummySignal()), option1, SIGNAL(selected())); + + step.setActive(true); + + connect(this, SIGNAL(dummySignal()), step.mOption, SIGNAL(selected())); + + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + + emit anotherDummySignal(); + QCOMPARE(mAnotherDummySlotCallCount, 1); + + QSignalSpy destroyedSpy(step.mOption, SIGNAL(destroyed(QObject*))); + + step.setActive(false); + + QCOMPARE(step.options().count(), 1); + QVERIFY(step.options().contains(option1)); + QCOMPARE(step.mWaitsForToBeDeletedInTearDown.count(), 0); + QCOMPARE(step.mOptionsToBeDeletedInTearDown.count(), 0); + QCOMPARE(destroyedSpy.count(), 1); +} + +void StepTest::testAddOptionTwice() { + Step step("doSomethingConstructive"); + + Option* option1 = new Option("Bathe your iguana"); + step.addOption(option1, this, SLOT(dummySlot())); + connect(this, SIGNAL(dummySignal()), option1, SIGNAL(selected())); + + Option* option2 = new Option("Feed a toucan"); + step.addOption(option2, "feedAToucanStep"); + connect(this, SIGNAL(dummySignal()), option2, SIGNAL(selected())); + + step.addOption(option1, this, SLOT(anotherDummySlot())); + step.addOption(option2, "feedAPigeonStep"); + + QCOMPARE(option1->parent(), &step); + QCOMPARE(option2->parent(), &step); + QCOMPARE(step.options().count(), 2); + QVERIFY(step.options().contains(option1)); + QVERIFY(step.options().contains(option2)); + + QSignalSpy nextStepRequestedSpy(&step, SIGNAL(nextStepRequested(QString))); + + step.setActive(true); + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + QCOMPARE(mAnotherDummySlotCallCount, 0); + QCOMPARE(nextStepRequestedSpy.count(), 1); + assertStepId(nextStepRequestedSpy, 0, "feedAToucanStep"); +} + +void StepTest::testAddOptionDifferentOptionsWithSameName() { + Step step("doSomethingConstructive"); + + Option* option1 = new Option("Bathe your iguana"); + step.addOption(option1, this, SLOT(dummySlot())); + connect(this, SIGNAL(dummySignal()), option1, SIGNAL(selected())); + + Option* option2 = new Option("Feed a toucan"); + step.addOption(option2, "feedAToucanStep"); + connect(this, SIGNAL(dummySignal()), option2, SIGNAL(selected())); + + //They will not be added and thus not deleted by parent Step, so they are + //created in stack + Option option3("Bathe your iguana"); + Option option4("Feed a toucan"); + + step.addOption(&option3, this, SLOT(anotherDummySlot())); + connect(this, SIGNAL(dummySignal()), &option3, SIGNAL(selected())); + + step.addOption(&option4, "feedAToucanStep2"); + connect(this, SIGNAL(dummySignal()), &option4, SIGNAL(selected())); + + QCOMPARE(option1->parent(), &step); + QCOMPARE(option2->parent(), &step); + QCOMPARE(option3.parent(), (QObject*)0); + QCOMPARE(option4.parent(), (QObject*)0); + QCOMPARE(step.options().count(), 2); + QVERIFY(step.options().contains(option1)); + QVERIFY(step.options().contains(option2)); + + QSignalSpy nextStepRequestedSpy(&step, SIGNAL(nextStepRequested(QString))); + + step.setActive(true); + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + QCOMPARE(mAnotherDummySlotCallCount, 0); + QCOMPARE(nextStepRequestedSpy.count(), 1); + assertStepId(nextStepRequestedSpy, 0, "feedAToucanStep"); +} + +void StepTest::testAddWaitFor() { + Step step("doSomethingConstructive"); + + WaitFor* waitFor1 = new WaitForSignal(this, SIGNAL(dummySignal())); + + step.addWaitFor(waitFor1, this, SLOT(dummySlot())); + + QCOMPARE(waitFor1->parent(), &step); + QCOMPARE(step.mWaitsFor.count(), 1); + QVERIFY(step.mWaitsFor.contains(waitFor1)); + QCOMPARE(mDummySlotCallCount, 0); + + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 0); + + step.setActive(true); + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); +} + +void StepTest::testAddWaitForWithoutSlotMacro() { + Step step("doSomethingConstructive"); + + WaitFor* waitFor1 = new WaitForSignal(this, SIGNAL(dummySignal())); + + step.addWaitFor(waitFor1, this, "dummySlot()"); + + QCOMPARE(waitFor1->parent(), &step); + QCOMPARE(step.mWaitsFor.count(), 1); + QVERIFY(step.mWaitsFor.contains(waitFor1)); + QCOMPARE(mDummySlotCallCount, 0); + + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 0); + + step.setActive(true); + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); +} + +void StepTest::testAddWaitForAssociatedToStepId() { + Step step("doSomethingConstructive"); + + WaitFor* waitFor1 = new WaitForSignal(this, SIGNAL(dummySignal())); + + step.addWaitFor(waitFor1, "batheYourIguanaStep"); + + QSignalSpy nextStepRequestedSpy(&step, SIGNAL(nextStepRequested(QString))); + + QCOMPARE(waitFor1->parent(), &step); + QCOMPARE(step.mWaitsFor.count(), 1); + QVERIFY(step.mWaitsFor.contains(waitFor1)); + QCOMPARE(nextStepRequestedSpy.count(), 0); + + emit dummySignal(); + QCOMPARE(nextStepRequestedSpy.count(), 0); + + step.setActive(true); + emit dummySignal(); + QCOMPARE(nextStepRequestedSpy.count(), 1); + assertStepId(nextStepRequestedSpy, 0, "batheYourIguanaStep"); +} + +void StepTest::testAddWaitForSeveralWaitFors() { + Step step("doSomethingConstructive"); + + WaitFor* waitFor1 = new WaitForSignal(this, SIGNAL(dummySignal())); + step.addWaitFor(waitFor1, this, SLOT(dummySlot())); + + WaitFor* waitFor2 = new WaitForSignal(this, SIGNAL(anotherDummySignal())); + step.addWaitFor(waitFor2, this, SLOT(dummySlot())); + + WaitFor* waitFor3 = new WaitForSignal(this, SIGNAL(thirdDummySignal())); + step.addWaitFor(waitFor3, "batheYourIguanaStep"); + + WaitFor* waitFor4 = new WaitForSignal(this, SIGNAL(fourthDummySignal())); + step.addWaitFor(waitFor4, "feedAToucanStep"); + + QSignalSpy nextStepRequestedSpy(&step, SIGNAL(nextStepRequested(QString))); + + QCOMPARE(waitFor1->parent(), &step); + QCOMPARE(waitFor2->parent(), &step); + QCOMPARE(waitFor3->parent(), &step); + QCOMPARE(waitFor4->parent(), &step); + QCOMPARE(step.mWaitsFor.count(), 4); + QVERIFY(step.mWaitsFor.contains(waitFor1)); + QVERIFY(step.mWaitsFor.contains(waitFor2)); + QVERIFY(step.mWaitsFor.contains(waitFor3)); + QVERIFY(step.mWaitsFor.contains(waitFor4)); + QCOMPARE(mDummySlotCallCount, 0); + QCOMPARE(nextStepRequestedSpy.count(), 0); + + emit dummySignal(); + emit anotherDummySignal(); + emit thirdDummySignal(); + emit fourthDummySignal(); + QCOMPARE(mDummySlotCallCount, 0); + QCOMPARE(nextStepRequestedSpy.count(), 0); + + step.setActive(true); + emit anotherDummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + QCOMPARE(nextStepRequestedSpy.count(), 0); + + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 2); + QCOMPARE(nextStepRequestedSpy.count(), 0); + + emit thirdDummySignal(); + QCOMPARE(mDummySlotCallCount, 2); + QCOMPARE(nextStepRequestedSpy.count(), 1); + assertStepId(nextStepRequestedSpy, 0, "batheYourIguanaStep"); + + emit fourthDummySignal(); + QCOMPARE(mDummySlotCallCount, 2); + QCOMPARE(nextStepRequestedSpy.count(), 2); + assertStepId(nextStepRequestedSpy, 1, "feedAToucanStep"); +} + +void StepTest::testAddWaitForDuringSetup() { + StepWithWaitForAddedInSetup step("doSomethingConstructive", this); + + step.setActive(true); + + QCOMPARE(step.mWaitFor->parent(), &step); + QCOMPARE(step.mWaitsFor.count(), 1); + QVERIFY(step.mWaitsFor.contains(step.mWaitFor)); + QCOMPARE(mDummySlotCallCount, 0); + + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + + QSignalSpy destroyedSpy(step.mWaitFor, SIGNAL(destroyed(QObject*))); + + step.setActive(false); + + QCOMPARE(step.mWaitsFor.count(), 0); + QCOMPARE(step.mWaitsForToBeDeletedInTearDown.count(), 0); + QCOMPARE(step.mOptionsToBeDeletedInTearDown.count(), 0); + QCOMPARE(destroyedSpy.count(), 1); +} + +void StepTest::testAddWaitForNormalAndDuringSetup() { + StepWithWaitForAddedInSetup step("doSomethingConstructive", this); + + WaitFor* waitFor1 = new WaitForSignal(this, SIGNAL(anotherDummySignal())); + + step.addWaitFor(waitFor1, this, SLOT(anotherDummySlot())); + + step.setActive(true); + + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + + emit anotherDummySignal(); + QCOMPARE(mAnotherDummySlotCallCount, 1); + + QSignalSpy destroyedSpy(step.mWaitFor, SIGNAL(destroyed(QObject*))); + + step.setActive(false); + + QCOMPARE(step.mWaitsFor.count(), 1); + QVERIFY(step.mWaitsFor.contains(waitFor1)); + QCOMPARE(step.mWaitsForToBeDeletedInTearDown.count(), 0); + QCOMPARE(step.mOptionsToBeDeletedInTearDown.count(), 0); + QCOMPARE(destroyedSpy.count(), 1); +} + +void StepTest::testAddWaitForTwice() { + Step step("doSomethingConstructive"); + + WaitFor* waitFor1 = new WaitForSignal(this, SIGNAL(dummySignal())); + step.addWaitFor(waitFor1, this, SLOT(dummySlot())); + + WaitFor* waitFor2 = new WaitForSignal(this, SIGNAL(anotherDummySignal())); + step.addWaitFor(waitFor2, "batheYourIguanaStep"); + + step.addWaitFor(waitFor1, this, SLOT(anotherDummySlot())); + step.addWaitFor(waitFor2, "batheYourChameleonStep"); + + QCOMPARE(waitFor1->parent(), &step); + QCOMPARE(waitFor2->parent(), &step); + QCOMPARE(step.mWaitsFor.count(), 2); + QVERIFY(step.mWaitsFor.contains(waitFor1)); + QVERIFY(step.mWaitsFor.contains(waitFor2)); + + step.setActive(true); + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + QCOMPARE(mAnotherDummySlotCallCount, 0); + + QSignalSpy nextStepRequestedSpy(&step, SIGNAL(nextStepRequested(QString))); + + emit anotherDummySignal(); + QCOMPARE(nextStepRequestedSpy.count(), 1); + assertStepId(nextStepRequestedSpy, 0, "batheYourIguanaStep"); +} + +void StepTest::testRemoveOption() { + Step step("doSomethingConstructive"); + + Option* option1 = new Option("Bathe your iguana"); + step.addOption(option1, this, SLOT(dummySlot())); + connect(this, SIGNAL(dummySignal()), option1, SIGNAL(selected())); + + //It will be removed and not deleted by parent Step, so it is created in + //stack + Option option2("Feed a toucan"); + step.addOption(&option2, this, SLOT(anotherDummySlot())); + connect(this, SIGNAL(dummySignal()), &option2, SIGNAL(selected())); + + step.removeOption(&option2); + + QCOMPARE(option1->parent(), &step); + QCOMPARE(option2.parent(), (QObject*)0); + QCOMPARE(step.options().count(), 1); + QVERIFY(step.options().contains(option1)); + QVERIFY(!step.options().contains(&option2)); + QCOMPARE(mDummySlotCallCount, 0); + QCOMPARE(mAnotherDummySlotCallCount, 0); + + step.setActive(true); + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + QCOMPARE(mAnotherDummySlotCallCount, 0); +} + +void StepTest::testRemoveOptionAssociatedToStepId() { + Step step("doSomethingConstructive"); + + Option* option1 = new Option("Bathe your iguana"); + step.addOption(option1, "batheYourIguanaStep"); + connect(this, SIGNAL(dummySignal()), option1, SIGNAL(selected())); + + //It will be removed and not deleted by parent Step, so it is created in + //stack + Option option2("Feed a toucan"); + step.addOption(&option2, "feedAToucanStep"); + connect(this, SIGNAL(dummySignal()), &option2, SIGNAL(selected())); + + step.removeOption(&option2); + + QSignalSpy nextStepRequestedSpy(&step, SIGNAL(nextStepRequested(QString))); + + QCOMPARE(option1->parent(), &step); + QCOMPARE(option2.parent(), (QObject*)0); + QCOMPARE(step.options().count(), 1); + QVERIFY(step.options().contains(option1)); + QVERIFY(!step.options().contains(&option2)); + QCOMPARE(nextStepRequestedSpy.count(), 0); + + step.setActive(true); + emit dummySignal(); + QCOMPARE(nextStepRequestedSpy.count(), 1); + assertStepId(nextStepRequestedSpy, 0, "batheYourIguanaStep"); +} + +void StepTest::testRemoveOptionSeveralOptions() { + Step step("doSomethingConstructive"); + + //They will be removed and not deleted by parent Step, so they are created + //in stack + Option option1("Bathe your iguana"); + step.addOption(&option1, this, SLOT(anotherDummySlot())); + connect(this, SIGNAL(dummySignal()), &option1, SIGNAL(selected())); + + Option option2("Feed a toucan"); + step.addOption(&option2, this, SLOT(dummySlot())); + connect(this, SIGNAL(dummySignal()), &option2, SIGNAL(selected())); + + Option option3("Walk the platypus"); + step.addOption(&option3, this, SLOT(anotherDummySlot())); + connect(this, SIGNAL(dummySignal()), &option3, SIGNAL(selected())); + + Option option4("Lull the penguin"); + step.addOption(&option4, "lullThePenguinStep"); + connect(this, SIGNAL(dummySignal()), &option4, SIGNAL(selected())); + + Option option5("Pamper the Tasmanian Devil"); + step.addOption(&option5, "pamperTheTasmanianDevilStep"); + connect(this, SIGNAL(dummySignal()), &option5, SIGNAL(selected())); + + QSignalSpy nextStepRequestedSpy(&step, SIGNAL(nextStepRequested(QString))); + + step.removeOption(&option1); + step.removeOption(&option3); + step.removeOption(&option5); + + QCOMPARE(option1.parent(), (QObject*)0); + QCOMPARE(option2.parent(), &step); + QCOMPARE(option3.parent(), (QObject*)0); + QCOMPARE(option4.parent(), &step); + QCOMPARE(option5.parent(), (QObject*)0); + QCOMPARE(step.options().count(), 2); + QVERIFY(step.options().contains(&option2)); + QVERIFY(step.options().contains(&option4)); + QVERIFY(!step.options().contains(&option1)); + QVERIFY(!step.options().contains(&option3)); + QVERIFY(!step.options().contains(&option5)); + QCOMPARE(mDummySlotCallCount, 0); + QCOMPARE(mAnotherDummySlotCallCount, 0); + QCOMPARE(nextStepRequestedSpy.count(), 0); + + step.setActive(true); + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + QCOMPARE(mAnotherDummySlotCallCount, 0); + QCOMPARE(nextStepRequestedSpy.count(), 1); + assertStepId(nextStepRequestedSpy, 0, "lullThePenguinStep"); + + step.removeOption(&option2); + + QCOMPARE(option2.parent(), (QObject*)0); + QCOMPARE(step.options().count(), 1); + QVERIFY(step.options().contains(&option4)); + QVERIFY(!step.options().contains(&option2)); + + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + QCOMPARE(mAnotherDummySlotCallCount, 0); + QCOMPARE(nextStepRequestedSpy.count(), 2); + assertStepId(nextStepRequestedSpy, 1, "lullThePenguinStep"); + + step.removeOption(&option4); + + QCOMPARE(option4.parent(), (QObject*)0); + QCOMPARE(step.options().count(), 0); + QVERIFY(!step.options().contains(&option4)); + + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + QCOMPARE(mAnotherDummySlotCallCount, 0); + QCOMPARE(nextStepRequestedSpy.count(), 2); +} + +void StepTest::testRemoveWaitFor() { + Step step("doSomethingConstructive"); + + WaitForSignal* waitFor1 = new WaitForSignal(this, SIGNAL(dummySignal())); + step.addWaitFor(waitFor1, this, SLOT(dummySlot())); + + //It will be removed and not deleted by parent Step, so it is created in + //stack + WaitForSignal waitFor2(this, SIGNAL(anotherDummySignal())); + step.addWaitFor(&waitFor2, this, SLOT(anotherDummySlot())); + + step.setActive(true); + + step.removeWaitFor(&waitFor2); + + QCOMPARE(waitFor1->parent(), &step); + QCOMPARE(waitFor2.parent(), (QObject*)0); + QCOMPARE(step.mWaitsFor.count(), 1); + QVERIFY(step.mWaitsFor.contains(waitFor1)); + QVERIFY(!step.mWaitsFor.contains(&waitFor2)); + QVERIFY(waitFor1->isActive()); + QVERIFY(!waitFor2.isActive()); + QCOMPARE(mDummySlotCallCount, 0); + QCOMPARE(mAnotherDummySlotCallCount, 0); + + waitFor2.setActive(true); + emit dummySignal(); + emit anotherDummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + QCOMPARE(mAnotherDummySlotCallCount, 0); +} + +void StepTest::testRemoveWaitForAssociatedToStepId() { + Step step("doSomethingConstructive"); + + WaitForSignal* waitFor1 = new WaitForSignal(this, SIGNAL(dummySignal())); + step.addWaitFor(waitFor1, "batheYourIguanaStep"); + + //It will be removed and not deleted by parent Step, so it is created in + //stack + WaitForSignal waitFor2(this, SIGNAL(anotherDummySignal())); + step.addWaitFor(&waitFor2, "feedAToucanStep"); + + QSignalSpy nextStepRequestedSpy(&step, SIGNAL(nextStepRequested(QString))); + + step.setActive(true); + + step.removeWaitFor(&waitFor2); + + QCOMPARE(waitFor1->parent(), &step); + QCOMPARE(waitFor2.parent(), (QObject*)0); + QCOMPARE(step.mWaitsFor.count(), 1); + QVERIFY(step.mWaitsFor.contains(waitFor1)); + QVERIFY(!step.mWaitsFor.contains(&waitFor2)); + QVERIFY(waitFor1->isActive()); + QVERIFY(!waitFor2.isActive()); + QCOMPARE(nextStepRequestedSpy.count(), 0); + + waitFor2.setActive(true); + emit dummySignal(); + emit anotherDummySignal(); + QCOMPARE(nextStepRequestedSpy.count(), 1); + assertStepId(nextStepRequestedSpy, 0, "batheYourIguanaStep"); +} + +void StepTest::testRemoveWaitForSeveralWaitFors() { + Step step("doSomethingConstructive"); + + //They will be removed and not deleted by parent Step, so they are created + //in stack + WaitForSignal waitFor1(this, SIGNAL(dummySignal())); + step.addWaitFor(&waitFor1, this, SLOT(anotherDummySlot())); + + WaitForSignal waitFor2(this, SIGNAL(anotherDummySignal())); + step.addWaitFor(&waitFor2, this, SLOT(dummySlot())); + + WaitForSignal waitFor3(this, SIGNAL(thirdDummySignal())); + step.addWaitFor(&waitFor3, "batheYourIguanaStep"); + + WaitForSignal waitFor4(this, SIGNAL(fourthDummySignal())); + step.addWaitFor(&waitFor4, "feedAToucanStep"); + + QSignalSpy nextStepRequestedSpy(&step, SIGNAL(nextStepRequested(QString))); + + step.setActive(true); + + step.removeWaitFor(&waitFor1); + step.removeWaitFor(&waitFor3); + + QCOMPARE(waitFor1.parent(), (QObject*)0); + QCOMPARE(waitFor2.parent(), &step); + QCOMPARE(waitFor3.parent(), (QObject*)0); + QCOMPARE(waitFor4.parent(), &step); + QCOMPARE(step.mWaitsFor.count(), 2); + QVERIFY(step.mWaitsFor.contains(&waitFor2)); + QVERIFY(step.mWaitsFor.contains(&waitFor4)); + QVERIFY(!step.mWaitsFor.contains(&waitFor1)); + QVERIFY(!step.mWaitsFor.contains(&waitFor3)); + QVERIFY(!waitFor1.isActive()); + QVERIFY(waitFor2.isActive()); + QVERIFY(!waitFor3.isActive()); + QVERIFY(waitFor4.isActive()); + QCOMPARE(mDummySlotCallCount, 0); + QCOMPARE(mAnotherDummySlotCallCount, 0); + QCOMPARE(nextStepRequestedSpy.count(), 0); + + step.setActive(true); + waitFor1.setActive(true); + waitFor3.setActive(true); + emit dummySignal(); + emit anotherDummySignal(); + emit thirdDummySignal(); + emit fourthDummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + QCOMPARE(mAnotherDummySlotCallCount, 0); + QCOMPARE(nextStepRequestedSpy.count(), 1); + assertStepId(nextStepRequestedSpy, 0, "feedAToucanStep"); + + step.removeWaitFor(&waitFor2); + + QCOMPARE(waitFor2.parent(), (QObject*)0); + QCOMPARE(step.mWaitsFor.count(), 1); + QVERIFY(step.mWaitsFor.contains(&waitFor4)); + QVERIFY(!step.mWaitsFor.contains(&waitFor2)); + QVERIFY(!waitFor2.isActive()); + + waitFor2.setActive(true); + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + + step.removeWaitFor(&waitFor4); + + QCOMPARE(waitFor4.parent(), (QObject*)0); + QCOMPARE(step.mWaitsFor.count(), 0); + QVERIFY(!step.mWaitsFor.contains(&waitFor4)); + QVERIFY(!waitFor4.isActive()); + + waitFor4.setActive(true); + emit fourthDummySignal(); + QCOMPARE(nextStepRequestedSpy.count(), 1); +} + +/////////////////////////////////// Helpers //////////////////////////////////// + +void StepTest::assertStepId(const QSignalSpy& spy, int index, + const QString& stepId) { + QVariant argument = spy.at(index).at(0); + QCOMPARE(argument.type(), QVariant::String); + QCOMPARE(argument.toString(), stepId); +} + +QTEST_MAIN(StepTest) + +#include "StepTest.moc" Deleted: trunk/ktutorial/ktutorial-library/tests/TutorialTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/test/TutorialTest.cpp 2010-02-13 19:08:14 UTC (rev 88) +++ trunk/ktutorial/ktutorial-library/tests/TutorialTest.cpp 2010-03-21 03:12:26 UTC (rev 173) @@ -1,360 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009-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 <qtest_kde.h> - -#define protected public -#define private public -#include "Tutorial.h" -#undef private -#undef protected - -#include "Step.h" -#include "TutorialInformation.h" - -class TutorialTest: public QObject { -Q_OBJECT - -private slots: - - void testConstructor(); - - void testAddStep(); - void testAddStepSeveralSteps(); - void testAddStepTwice(); - void testAddStepDifferentStepWithSameId(); - - void testStart(); - void testStartNoStartStep(); - - void testNextStepId(); - void testNextStepIdWithInvalidId(); - - void testNextStepStep(); - void testNextStepStepWithInvalidStep(); - - void testFinish(); - -}; - -class MockTutorial: public Tutorial { -public: - - int mSetupCount; - int mTearDownCount; - - MockTutorial(TutorialInformation* tutorialInformation): - Tutorial(tutorialInformation) { - mSetupCount = 0; - mTearDownCount = 0; - } - -protected: - - virtual void setup() { - mSetupCount++; - } - - virtual void tearDown() { - mTearDownCount++; - } - -}; - -void TutorialTest::testConstructor() { - TutorialInformation* tutorialInformation = - new TutorialInformation("pearlOrientation"); - Tutorial tutorial(tutorialInformation); - - QCOMPARE(tutorial.tutorialInformation(), tutorialInformation); - QCOMPARE(tutorial.mCurrentStep, (Step*)0); -} - -void TutorialTest::testAddStep() { - Tutorial tutorial(new TutorialInformation("pearlOrientation")); - - Step* step1 = new Step("record"); - - tutorial.addStep(step1); - - QCOMPARE(step1->parent(), &tutorial); - QCOMPARE(tutorial.mSteps.size(), 1); - QCOMPARE(tutorial.mSteps.value("record"), step1); - QCOMPARE(tutorial.mCurrentStep, (Step*)0); -} - -void TutorialTest::testAddStepSeveralSteps() { - Tutorial tutorial(new TutorialInformation("pearlOrientation")); - - Step* step1 = new Step("record"); - tutorial.addStep(step1); - - Step* step2 = new Step("roll"); - tutorial.addStep(step2); - - Step* step3 = new Step("send"); - tutorial.addStep(step3); - - QCOMPARE(step1->parent(), &tutorial); - QCOMPARE(step2->parent(), &tutorial); - QCOMPARE(step3->parent(), &tutorial); - QCOMPARE(tutorial.mSteps.size(), 3); - QCOMPARE(tutorial.mSteps.value("record"), step1); - QCOMPARE(tutorial.mSteps.value("roll"), step2); - QCOMPARE(tutorial.mSteps.value("send"), step3); - QCOMPARE(tutorial.mCurrentStep, (Step*)0); -} - -void TutorialTest::testAddStepTwice() { - Tutorial tutorial(new TutorialInformation("pearlOrientation")); - - Step* step1 = new Step("record"); - tutorial.addStep(step1); - - Step* step2 = new Step("roll"); - tutorial.addStep(step2); - - Step* step3 = new Step("send"); - tutorial.addStep(step3); - - tutorial.addStep(step1); - - QCOMPARE(step1->parent(), &tutorial); - QCOMPARE(step2->parent(), &tutorial); - QCOMPARE(step3->parent(), &tutorial); - QCOMPARE(tutorial.mSteps.size(), 3); - QCOMPARE(tutorial.mSteps.value("record"), step1); - QCOMPARE(tutorial.mSteps.value("roll"), step2); - QCOMPARE(tutorial.mSteps.value("send"), step3); - QCOMPARE(tutorial.mCurrentStep, (Step*)0); -} - -void TutorialTest::testAddStepDifferentStepWithSameId() { - Tutorial tutorial(new TutorialInformation("pearlOrientation")); - - Step* step1 = new Step("record"); - tutorial.addStep(step1); - - Step* step2 = new Step("roll"); - tutorial.addStep(step2); - - Step* step3 = new Step("send"); - tutorial.addStep(step3); - - //It will not be added and thus not deleted by parent Tutorial, so it is - //created in stack - Step step4("roll"); - tutorial.addStep(&step4); - - QCOMPARE(step1->parent(), &tutorial); - QCOMPARE(step2->parent(), &tutorial); - QCOMPARE(step3->parent(), &tutorial); - QCOMPARE(tutorial.mSteps.size(), 3); - QCOMPARE(tutorial.mSteps.value("record"), step1); - QCOMPARE(tutorial.mSteps.value("roll"), step2); - QCOMPARE(tutorial.mSteps.value("send"), step3); - QCOMPARE(tutorial.mCurrentStep, (Step*)0); -} - -//Step* must be declared as a metatype to be used in qvariant_cast -Q_DECLARE_METATYPE(... [truncated message content] |
From: <dan...@us...> - 2010-04-13 18:06:34
|
Revision: 228 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=228&view=rev Author: danxuliu Date: 2010-04-13 18:06:28 +0000 (Tue, 13 Apr 2010) Log Message: ----------- Add utility classes to highlight widgets with an animation. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt Added Paths: ----------- trunk/ktutorial/ktutorial-library/src/extendedinformation/ trunk/ktutorial/ktutorial-library/src/extendedinformation/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.cpp trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.h trunk/ktutorial/ktutorial-library/tests/extendedinformation/ trunk/ktutorial/ktutorial-library/tests/extendedinformation/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2010-04-01 03:46:38 UTC (rev 227) +++ trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2010-04-13 18:06:28 UTC (rev 228) @@ -2,6 +2,7 @@ # In order to work, they must be compiled using -fPIC add_definitions("-fPIC") +add_subdirectory(extendedinformation) add_subdirectory(scripting) add_subdirectory(tutorials) add_subdirectory(view) Added: trunk/ktutorial/ktutorial-library/src/extendedinformation/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/extendedinformation/CMakeLists.txt (rev 0) +++ trunk/ktutorial/ktutorial-library/src/extendedinformation/CMakeLists.txt 2010-04-13 18:06:28 UTC (rev 228) @@ -0,0 +1,10 @@ +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${KDE4_INCLUDES}) + +set(ktutorial_extendedinformation_SRCS + WidgetHighlighter.cpp + WidgetHighlighterManager.cpp +) + +kde4_add_library(ktutorial_extendedinformation ${ktutorial_extendedinformation_SRCS}) + +target_link_libraries(ktutorial_extendedinformation ${KDE4_KDEUI_LIBS}) Property changes on: trunk/ktutorial/ktutorial-library/src/extendedinformation/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp 2010-04-13 18:06:28 UTC (rev 228) @@ -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/>. * + ***************************************************************************/ + +#include <QWidget> + +#include <KColorUtils> + +#include "WidgetHighlighter.h" + +namespace extendedinformation { + +//public: + +WidgetHighlighter::WidgetHighlighter(QWidget* targetWidget): + QObject(targetWidget), + mTargetWidget(targetWidget) { + Q_ASSERT(targetWidget); + + mOriginalPalette = targetWidget->palette(); + + //TODO Use QPropertyAnimation instead? Increase Qt version requirement in + //CMakeLists.txt to Qt 4.6 if done. + mProgress = 0; + mIncreasing = true; + mStopping = false; + + int interval = 25; + int duration = 500; + mProgressForEachTick = interval / (qreal)duration; + + mTimer.setInterval(interval); + connect(&mTimer, SIGNAL(timeout()), this, SLOT(update())); +} + +WidgetHighlighter::~WidgetHighlighter() { + mTargetWidget->setPalette(mOriginalPalette); +} + +//public slots: + +void WidgetHighlighter::start() { + mStopping = false; + mIncreasing = true; + + mTimer.start(); +} + +void WidgetHighlighter::stop() { + mStopping = true; + mIncreasing = false; + + if (mProgress == 0) { + mTimer.stop(); + emit stopped(this); + } +} + +//private: + +void WidgetHighlighter::updateColorGroup(QPalette& palette, + QPalette::ColorGroup colorGroup) { + updateColorRole(palette, colorGroup, QPalette::Window, QPalette::Highlight); + updateColorRole(palette, colorGroup, QPalette::WindowText, + QPalette::HighlightedText); + updateColorRole(palette, colorGroup, QPalette::Base, QPalette::Highlight); + updateColorRole(palette, colorGroup, QPalette::Text, + QPalette::HighlightedText); + updateColorRole(palette, colorGroup, QPalette::Button, QPalette::Highlight); + updateColorRole(palette, colorGroup, QPalette::ButtonText, + QPalette::HighlightedText); +} + +void WidgetHighlighter::updateColorRole(QPalette& palette, + QPalette::ColorGroup colorGroup, + QPalette::ColorRole base, + QPalette::ColorRole tint) { + qreal amount = 0.6 * mProgress; + QColor color = KColorUtils::tint(palette.color(colorGroup, base), + palette.color(colorGroup, tint), + amount); + palette.setColor(colorGroup, base, color); +} + +//private slots: + +void WidgetHighlighter::update(){ + if (mIncreasing) { + mProgress += mProgressForEachTick; + mProgress = qMin<qreal>(1, mProgress); + mIncreasing = mProgress < 1; + } else { + mProgress -= mProgressForEachTick; + mProgress = qMax<qreal>(0, mProgress); + mIncreasing = mProgress == 0; + } + + QPalette updatedPalette = mOriginalPalette; + updateColorGroup(updatedPalette, QPalette::Active); + updateColorGroup(updatedPalette, QPalette::Inactive); + updateColorGroup(updatedPalette, QPalette::Disabled); + + mTargetWidget->setPalette(updatedPalette); + + if (mStopping && mProgress == 0) { + mTimer.stop(); + emit stopped(this); + } +} + +} Property changes on: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h 2010-04-13 18:06:28 UTC (rev 228) @@ -0,0 +1,162 @@ +/*************************************************************************** + * 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 WIDGETHIGHLIGHTER_H +#define WIDGETHIGHLIGHTER_H + +#include <QObject> +#include <QPalette> +#include <QTimer> + +namespace extendedinformation { + +/** + * Utility class to highlight a widget. + * WidgetHighlighter executes an animation that modifies the color palette of + * some widget, changing its colors from normal to highlighted and back again. + * + * Once started, the animation goes on indefinitely until the stop slot is + * called. The colors of the palette are set back to the original colors + * animating the change from the current color of the widget to the normal one. + * If the highlighting is started again while the stop animation is running, the + * highlighting animation is started again from the current color (that is, the + * stop animation is cancelled and a new highlight animation is started from + * that point). + * + * WidgetHighlighter should not be created directly. Instead, + * WidgetHighlighterManager should be used, as it takes care of deleting the + * highlighter when no longer needed. + * + * @see WidgetHighlighterManager + */ +class WidgetHighlighter: public QObject { +Q_OBJECT +public: + + /** + * Creates a new WidgetHighlighter for the given widget. + * + * @param targetWidget The widget to highlight. + */ + explicit WidgetHighlighter(QWidget* targetWidget); + + /** + * Destroys this WidgetHighlighter. + * It restores the original palette to the target widget. + */ + virtual ~WidgetHighlighter(); + +public Q_SLOTS: + + /** + * Starts highlighting the target widget. + * If there is a normal animation already running nothing is done. However, + * if there is a stop animation running, it is cancelled and the + * highlighting animation is started again from the current color. + */ + void start(); + + /** + * Stops highlighting the target widget. + * The animation is not stopped sharply, but a stop animation (the widget + * recovering its normal color) is executed. + */ + void stop(); + +Q_SIGNALS: + + /** + * Emitted when the animation has stopped. + * Note that it is emitted when the animation has truly stopped, that is, + * when there is not even an stop animation. + * + * @param widgetHighlighter This WidgetHighlighter. + */ + void stopped(extendedinformation::WidgetHighlighter* widgetHighlighter); + +private: + + /** + * The widget to highlight. + */ + QWidget* mTargetWidget; + + /** + * The original palette used by the widget. + */ + QPalette mOriginalPalette; + + /** + * Timer to update the colors. + */ + QTimer mTimer; + + /** + * The current progress from normal to highlighted colors. + * Range [0-1]. + */ + qreal mProgress; + + /** + * How much advances the progress when an update is done. + */ + qreal mProgressForEachTick; + + /** + * True if the widget is being highlighted, false it is being dehighlighted. + */ + bool mIncreasing; + + /** + * True if the stop animation is being run, false otherwise. + */ + bool mStopping; + + /** + * Updates the color group of the given palette based on the current + * progress. + * + * @param palette The palette to update its colors. + * @param colorGroup The color group of the palette to update. + */ + void updateColorGroup(QPalette& palette, QPalette::ColorGroup colorGroup); + + /** + * Updates the color role "base" tinting it with the color role "tint". + * How much the base color role is tinted depends on the current progress. + * + * @param palette The palette to update its colors. + * @param colorGroup The color group of the palette to update. + * @param base The color role to update. + * @param tint The color role to tint the base color role with. + */ + void updateColorRole(QPalette& palette, QPalette::ColorGroup colorGroup, + QPalette::ColorRole from, QPalette::ColorRole to); + +private Q_SLOTS: + + /** + * Updates the palette of the target widget based on the animation progress. + */ + void update(); + +}; + +} + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.cpp 2010-04-13 18:06:28 UTC (rev 228) @@ -0,0 +1,68 @@ +/*************************************************************************** + * 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 <QWidget> + +#include "WidgetHighlighterManager.h" +#include "WidgetHighlighter.h" + +namespace extendedinformation { + +//public: + +WidgetHighlighterManager* WidgetHighlighterManager::self() { + return sSelf; +} + +void WidgetHighlighterManager::highlight(QWidget* widget) { + WidgetHighlighter* highlighter = widget->findChild<WidgetHighlighter*>(); + if (!highlighter) { + highlighter = new WidgetHighlighter(widget); + connect(highlighter, SIGNAL(stopped(extendedinformation::WidgetHighlighter*)), + this, SLOT(remove(extendedinformation::WidgetHighlighter*))); + } + + highlighter->start(); +} + +void WidgetHighlighterManager::stopHighlighting(QWidget* widget) { + WidgetHighlighter* highlighter = widget->findChild<WidgetHighlighter*>(); + if (!highlighter) { + return; + } + + highlighter->stop(); +} + +//private: + +WidgetHighlighterManager* WidgetHighlighterManager::sSelf = + new WidgetHighlighterManager(); + +WidgetHighlighterManager::WidgetHighlighterManager(): QObject() { +} + +//private slots: + +void WidgetHighlighterManager::remove( + extendedinformation::WidgetHighlighter* highlighter) { + highlighter->setParent(0); + delete highlighter; +} + +} Property changes on: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.h 2010-04-13 18:06:28 UTC (rev 228) @@ -0,0 +1,96 @@ +/*************************************************************************** + * 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 WIDGETHIGHLIGHTERMANAGER_H +#define WIDGETHIGHLIGHTERMANAGER_H + +#include <QHash> +#include <QObject> + +namespace extendedinformation { +class WidgetHighlighter; +} + +namespace extendedinformation { + +/** + * Utility class to manage WidgetHighlighters. + * Starting and stopping the highlighting from WidgetHighlighterManager instead + * of using directly WidgetHighlighter ensures that no overlapping animations + * will be executed. + * + * WidgetHighlighterManager also takes care of deleting the WidgetHighlighter + * when no longer needed, that is, when they are fully stopped (once the stop + * animation has ended). + * + * @see WidgetHighlighter + */ +class WidgetHighlighterManager: public QObject { +Q_OBJECT +public: + + /** + * Returns the only instance of this class. + * + * @return The only instance of this class. + */ + static WidgetHighlighterManager* self(); + + /** + * Starts a WidgetHighlighter for the given widget. + * If the widget was already being highlighted nothing is done. + * + * @param widget The widget to highlight. + */ + void highlight(QWidget* widget); + + /** + * Stops the WidgetHighlighter of the given widget. + * + * @param widget The widget to stop highlighting. + */ + void stopHighlighting(QWidget* widget); + +private: + + /** + * The instance of this class. + */ + static WidgetHighlighterManager* sSelf; + + /** + * Creates a new WidgetHighlighterManager. + * Private to avoid classes other than self to create instances. + */ + WidgetHighlighterManager(); + +private Q_SLOTS: + + /** + * Removes the highlighter from its widget and destroys it. + * Called when the highlighter stopped. + * + * @param highlighter The highlighter to remove. + */ + void remove(extendedinformation::WidgetHighlighter* highlighter); + +}; + +} + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2010-04-01 03:46:38 UTC (rev 227) +++ trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2010-04-13 18:06:28 UTC (rev 228) @@ -1,3 +1,4 @@ +add_subdirectory(extendedinformation) add_subdirectory(scripting) add_subdirectory(view) Added: trunk/ktutorial/ktutorial-library/tests/extendedinformation/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/extendedinformation/CMakeLists.txt (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/extendedinformation/CMakeLists.txt 2010-04-13 18:06:28 UTC (rev 228) @@ -0,0 +1,33 @@ +# Used by kde4_add_unit_test to set the full path to test executables +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) + +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${ktutorial-library_SOURCE_DIR}/src/extendedinformation ${KDE4_INCLUDES}) + +# Since Qt 4.6.0, this definition is needed for GUI testing. +# It is backwards compatible with previous Qt versions, unlike the alternative +# which is to add #include <QTestGui> in the test files. +add_definitions(-DQT_GUI_LIB) + +MACRO(UNIT_TESTS) + FOREACH(_className ${ARGN}) + set(_testName ${_className}Test) + kde4_add_unit_test(${_testName} TESTNAME ktutorial-${_testName} ${_testName}.cpp) + target_link_libraries(${_testName} ktutorial_extendedinformation ${QT_QTTEST_LIBRARY}) + ENDFOREACH(_className) +ENDMACRO(UNIT_TESTS) + +unit_tests( + WidgetHighlighter + WidgetHighlighterManager +) + +MACRO(MEM_TESTS) + FOREACH(_testname ${ARGN}) + add_test(ktutorial-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( + WidgetHighlighter + WidgetHighlighterManager +) Property changes on: trunk/ktutorial/ktutorial-library/tests/extendedinformation/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp 2010-04-13 18:06:28 UTC (rev 228) @@ -0,0 +1,144 @@ +/*************************************************************************** + * 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 <QWidget> + +#include "WidgetHighlighterManager.h" +#include "WidgetHighlighter.h" + +namespace extendedinformation { + +class WidgetHighlighterManagerTest: public QObject { +Q_OBJECT +private slots: + + void testSelf(); + + void testHighlight(); + void testHighlightWidgetAlreadyHighlighted(); + void testHighlightAfterStopHighlighting(); + + void testStopHighlighting(); + + void testDeleteWidgetWhileHighlighting(); + +}; + +void WidgetHighlighterManagerTest::testSelf() { + WidgetHighlighterManager* manager1 = WidgetHighlighterManager::self(); + WidgetHighlighterManager* manager2 = WidgetHighlighterManager::self(); + + QVERIFY(manager1); + QVERIFY(manager2); + QVERIFY(manager1 == manager2); +} + +void WidgetHighlighterManagerTest::testHighlight() { + QWidget widget; + QPalette palette = widget.palette(); + WidgetHighlighterManager* manager = WidgetHighlighterManager::self(); + + manager->highlight(&widget); + + //Give it some time to update + QTest::qWait(100); + + QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 1); + QVERIFY(widget.findChild<WidgetHighlighter*>()); + QVERIFY(widget.palette() != palette); +} + +void WidgetHighlighterManagerTest::testHighlightWidgetAlreadyHighlighted() { + QWidget widget; + WidgetHighlighterManager* manager = WidgetHighlighterManager::self(); + + manager->highlight(&widget); + + WidgetHighlighter* highlighter = widget.findChild<WidgetHighlighter*>(); + + manager->highlight(&widget); + + QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 1); + QCOMPARE(widget.findChild<WidgetHighlighter*>(), highlighter); +} + +void WidgetHighlighterManagerTest::testHighlightAfterStopHighlighting() { + QWidget widget; + WidgetHighlighterManager* manager = WidgetHighlighterManager::self(); + + manager->highlight(&widget); + + QPointer<WidgetHighlighter> highlighter = + widget.findChild<WidgetHighlighter*>(); + QVERIFY(highlighter); + + manager->stopHighlighting(&widget); + + QVERIFY(!highlighter); + + QPalette palette = widget.palette(); + manager->highlight(&widget); + + //Give it some time to update + QTest::qWait(100); + + QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 1); + QVERIFY(widget.findChild<WidgetHighlighter*>()); + QVERIFY(widget.palette() != palette); +} + +void WidgetHighlighterManagerTest::testStopHighlighting() { + QWidget widget; + QPalette palette = widget.palette(); + WidgetHighlighterManager* manager = WidgetHighlighterManager::self(); + + manager->highlight(&widget); + + //Give it some time to update + QTest::qWait(100); + + manager->stopHighlighting(&widget); + + //Give it some time to update + QTest::qWait(200); + + QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 0); + QVERIFY(widget.palette() == palette); +} + +void WidgetHighlighterManagerTest::testDeleteWidgetWhileHighlighting() { + QWidget* widget = new QWidget(); + WidgetHighlighterManager* manager = WidgetHighlighterManager::self(); + + manager->highlight(widget); + + //Give it some time to update + QTest::qWait(100); + + delete widget; + + //No explicit check is made, if it does not crash everything is fine ;) +} + +} + +QTEST_MAIN(extendedinformation::WidgetHighlighterManagerTest) + +#include "WidgetHighlighterManagerTest.moc" Property changes on: trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterTest.cpp 2010-04-13 18:06:28 UTC (rev 228) @@ -0,0 +1,293 @@ +/*************************************************************************** + * 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 <QApplication> +#include <QWidget> + +#define protected public +#define private public +#include "WidgetHighlighter.h" +#undef private +#undef protected + +//extendedinformation::WidgetHighlighter* must be declared as a metatype to be +//used in qvariant_cast +Q_DECLARE_METATYPE(extendedinformation::WidgetHighlighter*); + +namespace extendedinformation { + +class WidgetHighlighterTest: public QObject { +Q_OBJECT +private slots: + + void testConstructor(); + + void testDestructor(); + + void testStart(); + void testStartAlreadyStarted(); + void testStartWhileStopAnimationIsRunning(); + + void testUpdate(); + void testUpdateWhenProgressIsAlmostOne(); + void testUpdateWhenProgressIsAlmostZero(); + + void testStop(); + void testStopAfterStopAnimationEnded(); + void testStopImmediatelyAfterStart(); + +}; + +void WidgetHighlighterTest::testConstructor() { + QWidget widget; + widget.setPalette(Qt::blue); + QApplication::setPalette(Qt::green); + WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); + + QCOMPARE(highlighter->parent(), &widget); + QVERIFY(highlighter->mOriginalPalette != QApplication::palette()); + QVERIFY(highlighter->mOriginalPalette == widget.palette()); +} + +void WidgetHighlighterTest::testDestructor() { + QWidget widget; + QPalette palette = widget.palette(); + WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); + + widget.setPalette(QPalette(Qt::green)); + delete highlighter; + + QVERIFY(widget.palette() == palette); +} + +void WidgetHighlighterTest::testStart() { + QWidget widget; + QPalette palette = widget.palette(); + WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); + + highlighter->start(); + + //Give it some time to update + QTest::qWait(100); + + QVERIFY(widget.palette() != palette); +} + +void WidgetHighlighterTest::testStartAlreadyStarted() { + QWidget widget; + QPalette palette = widget.palette(); + WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); + + highlighter->start(); + + //Give it some time to update + QTest::qWait(100); + + qreal previousProgress = highlighter->mProgress; + highlighter->start(); + + //Ensure that progress is not reseted + QCOMPARE(highlighter->mProgress, previousProgress); + QVERIFY(widget.palette() != palette); +} + +void WidgetHighlighterTest::testStartWhileStopAnimationIsRunning() { + QWidget widget; + QPalette palette = widget.palette(); + WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); + + highlighter->mProgress = 0.5; + highlighter->start(); + + //Give it some time to update + QTest::qWait(100); + + highlighter->stop(); + + //Give it some time to update + QTest::qWait(100); + + highlighter->start(); + + //Give it some time to update + QTest::qWait(100); + + QVERIFY(highlighter->mProgress > 0.5); + QVERIFY(highlighter->mIncreasing); + QVERIFY(!highlighter->mStopping); + QVERIFY(widget.palette() != palette); +} + +void WidgetHighlighterTest::testUpdate() { + QWidget widget; + WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); + highlighter->mTimer.stop(); + + QPalette palette = widget.palette(); + int previousProgress = highlighter->mProgress; + + highlighter->update(); + + QCOMPARE(highlighter->mProgress, + previousProgress + highlighter->mProgressForEachTick); + QVERIFY(widget.palette().color(QPalette::Window) != + palette.color(QPalette::Window)); + QVERIFY(widget.palette().color(QPalette::WindowText) != + palette.color(QPalette::WindowText)); + QVERIFY(widget.palette().color(QPalette::Base) != + palette.color(QPalette::Base)); + QVERIFY(widget.palette().color(QPalette::Text) != + palette.color(QPalette::Text)); + QVERIFY(widget.palette().color(QPalette::Button) != + palette.color(QPalette::Button)); + QVERIFY(widget.palette().color(QPalette::ButtonText) != + palette.color(QPalette::ButtonText)); +} + +void WidgetHighlighterTest::testUpdateWhenProgressIsAlmostOne() { + QWidget widget; + WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); + highlighter->mTimer.stop(); + + highlighter->mProgress = 0.995; + highlighter->mIncreasing = true; + highlighter->update(); + + //Don't check palette changes here, as with such a small update it could + //have been left unchanged + QCOMPARE(highlighter->mProgress, 1.0); + QCOMPARE(highlighter->mIncreasing, false); + + QPalette palette = widget.palette(); + highlighter->update(); + + QCOMPARE(highlighter->mProgress, 1 - highlighter->mProgressForEachTick); + QCOMPARE(highlighter->mIncreasing, false); + QVERIFY(widget.palette() != palette); +} + +void WidgetHighlighterTest::testUpdateWhenProgressIsAlmostZero() { + QWidget widget; + WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); + highlighter->mTimer.stop(); + + highlighter->mProgress = 0.005; + highlighter->mIncreasing = false; + highlighter->update(); + + //Don't check palette changes here, as with such a small update it could + //have been left unchanged + QCOMPARE(highlighter->mProgress, 0.0); + QCOMPARE(highlighter->mIncreasing, true); + + QPalette palette = widget.palette(); + highlighter->update(); + + QCOMPARE(highlighter->mProgress, highlighter->mProgressForEachTick); + QCOMPARE(highlighter->mIncreasing, true); + QVERIFY(widget.palette() != palette); +} + +void WidgetHighlighterTest::testStop() { + QWidget widget; + QPalette palette = widget.palette(); + WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); + + highlighter->mProgress = 0.5; + highlighter->start(); + + //Give it some time to update + QTest::qWait(100); + + highlighter->stop(); + + //Give it some time to update + QTest::qWait(200); + + QVERIFY(highlighter->mTimer.isActive()); + QVERIFY(highlighter->mProgress < 0.5); + QVERIFY(!highlighter->mIncreasing); + QVERIFY(highlighter->mStopping); + QVERIFY(widget.palette() != palette); +} + +void WidgetHighlighterTest::testStopAfterStopAnimationEnded() { + QWidget widget; + QPalette palette = widget.palette(); + WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); + + highlighter->start(); + + //Give it some time to update + QTest::qWait(100); + + //extendedinformation::WidgetHighlighter* must be registered in order to be + //used with QSignalSpy + int widgetHighlighterStarType = + qRegisterMetaType<extendedinformation::WidgetHighlighter*>( + "extendedinformation::WidgetHighlighter*"); + QSignalSpy stoppedSpy(highlighter, + SIGNAL(stopped(extendedinformation::WidgetHighlighter*))); + + highlighter->stop(); + + //Give it some time to update + QTest::qWait(200); + + QVERIFY(!highlighter->mTimer.isActive()); + QVERIFY(widget.palette() == palette); + QCOMPARE(stoppedSpy.count(), 1); + QVariant argument = stoppedSpy.at(0).at(0); + QCOMPARE(argument.userType(), widgetHighlighterStarType); + QCOMPARE(qvariant_cast<extendedinformation::WidgetHighlighter*>(argument), + highlighter); +} + +void WidgetHighlighterTest::testStopImmediatelyAfterStart() { + QWidget widget; + QPalette palette = widget.palette(); + WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); + + highlighter->start(); + + //extendedinformation::WidgetHighlighter* must be registered in order to be + //used with QSignalSpy + int widgetHighlighterStarType = + qRegisterMetaType<extendedinformation::WidgetHighlighter*>( + "extendedinformation::WidgetHighlighter*"); + QSignalSpy stoppedSpy(highlighter, + SIGNAL(stopped(extendedinformation::WidgetHighlighter*))); + + highlighter->stop(); + + QVERIFY(!highlighter->mTimer.isActive()); + QVERIFY(widget.palette() == palette); + QCOMPARE(stoppedSpy.count(), 1); + QVariant argument = stoppedSpy.at(0).at(0); + QCOMPARE(argument.userType(), widgetHighlighterStarType); + QCOMPARE(qvariant_cast<extendedinformation::WidgetHighlighter*>(argument), + highlighter); +} + +} + +QTEST_MAIN(extendedinformation::WidgetHighlighterTest) + +#include "WidgetHighlighterTest.moc" Property changes on: trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterTest.cpp ___________________________________________________________________ Added: svn:eol-style + native This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-04-13 18:49:19
|
Revision: 229 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=229&view=rev Author: danxuliu Date: 2010-04-13 18:49:12 +0000 (Tue, 13 Apr 2010) Log Message: ----------- Add initial version of KTutorial editor support module to be able to introspect a running application that uses KTutorial from KTutorial editor. The purpose is ease the design of tutorials being able to know, for example, the name of the objects accessible by KTutorial in the application. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/KTutorial.cpp trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt Added Paths: ----------- trunk/ktutorial/ktutorial-library/src/editorsupport/ trunk/ktutorial/ktutorial-library/src/editorsupport/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.h trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpy.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpy.h trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpyAdaptor.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpyAdaptor.h trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.h trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegisterAdaptor.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegisterAdaptor.h trunk/ktutorial/ktutorial-library/tests/editorsupport/ trunk/ktutorial/ktutorial-library/tests/editorsupport/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportAdaptorTest.cpp trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp trunk/ktutorial/ktutorial-library/tests/editorsupport/EventSpyAdaptorTest.cpp trunk/ktutorial/ktutorial-library/tests/editorsupport/EventSpyTest.cpp trunk/ktutorial/ktutorial-library/tests/editorsupport/ObjectRegisterAdaptorTest.cpp trunk/ktutorial/ktutorial-library/tests/editorsupport/ObjectRegisterTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2010-04-13 18:06:28 UTC (rev 228) +++ trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2010-04-13 18:49:12 UTC (rev 229) @@ -7,6 +7,11 @@ add_subdirectory(tutorials) add_subdirectory(view) +if (QT_QTDBUS_FOUND) + add_definitions(-DQT_QTDBUS_FOUND) + add_subdirectory(editorsupport) +endif (QT_QTDBUS_FOUND) + include_directories(${CMAKE_CURRENT_BINARY_DIR} ${KDE4_INCLUDES}) set(ktutorial_LIB_SRCS @@ -29,6 +34,10 @@ target_link_libraries(ktutorial ktutorial_scripting ktutorial_tutorials ktutorial_view) +if (QT_QTDBUS_FOUND) + target_link_libraries(ktutorial ktutorial_editorsupport) +endif (QT_QTDBUS_FOUND) + ####### Install the library ####### install(TARGETS ktutorial DESTINATION ${LIB_INSTALL_DIR}) Modified: trunk/ktutorial/ktutorial-library/src/KTutorial.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/KTutorial.cpp 2010-04-13 18:06:28 UTC (rev 228) +++ trunk/ktutorial/ktutorial-library/src/KTutorial.cpp 2010-04-13 18:49:12 UTC (rev 229) @@ -29,6 +29,10 @@ #include "view/StepWidget.h" #include "view/TutorialManagerDialog.h" +#ifdef QT_QTDBUS_FOUND +#include "editorsupport/EditorSupport.h" +#endif + using scripting::ScriptingModule; using scripting::ScriptManager; using view::StepWidget; @@ -72,6 +76,10 @@ registerTutorial(new UsingKTutorial()); ScriptManager().loadTutorials(mTutorialmanager); + +#ifdef QT_QTDBUS_FOUND + (new editorsupport::EditorSupport(this))->setup(window); +#endif } //private: Added: trunk/ktutorial/ktutorial-library/src/editorsupport/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/CMakeLists.txt (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/CMakeLists.txt 2010-04-13 18:49:12 UTC (rev 229) @@ -0,0 +1,18 @@ +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${KDE4_INCLUDES}) + +set(ktutorial_editorsupport_SRCS + EditorSupport.cpp + EditorSupportAdaptor.cpp + EventSpy.cpp + EventSpyAdaptor.cpp + ObjectRegister.cpp + ObjectRegisterAdaptor.cpp +) + +kde4_add_library(ktutorial_editorsupport ${ktutorial_editorsupport_SRCS}) + +target_link_libraries(ktutorial_editorsupport + ktutorial_extendedinformation + ${QT_QTDBUS_LIBRARY} + ${KDE4_KDECORE_LIBS} +) Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp 2010-04-13 18:49:12 UTC (rev 229) @@ -0,0 +1,102 @@ +/*************************************************************************** + * 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 <QWidget> +#include <QtDBus/QtDBus> + +#include <KDebug> + +#include "EditorSupport.h" +#include "EditorSupportAdaptor.h" +#include "EventSpy.h" +#include "EventSpyAdaptor.h" +#include "ObjectRegister.h" +#include "ObjectRegisterAdaptor.h" +#include "../extendedinformation/WidgetHighlighterManager.h" + +using extendedinformation::WidgetHighlighterManager; + +namespace editorsupport { + +//public: + +EditorSupport::EditorSupport(QObject* parent /*= 0*/): QObject(parent), + mObjectRegister(0), + mEventSpy(0) { +} + +void EditorSupport::setup(QObject* window) { + if (!QDBusConnection::sessionBus().isConnected()) { + kWarning() << "Cannot connect to the D-Bus session bus!\n" + << "KTutorial editor support will not be enabled"; + return; + } + + mWindow = window; + + new EditorSupportAdaptor(this); + QDBusConnection::sessionBus().registerObject("/ktutorial", this); + + mObjectRegister = new ObjectRegister(this); + new ObjectRegisterAdaptor(mObjectRegister); + + QDBusConnection::sessionBus().registerObject("/ktutorial/ObjectRegister", + mObjectRegister); +} + +int EditorSupport::mainWindowObjectId() { + return mObjectRegister->idForObject(mWindow); +} + +void EditorSupport::highlight(int objectId) { + QObject* object = mObjectRegister->objectForId(objectId); + QWidget* widget = qobject_cast<QWidget*>(object); + if (!widget) { + return; + } + + WidgetHighlighterManager::self()->highlight(widget); +} + +void EditorSupport::stopHighlighting(int objectId) { + QObject* object = mObjectRegister->objectForId(objectId); + QWidget* widget = qobject_cast<QWidget*>(object); + if (!widget) { + return; + } + + WidgetHighlighterManager::self()->stopHighlighting(widget); +} + +void EditorSupport::enableEventSpy() { + mEventSpy = new EventSpy(this); + mEventSpy->addObjectToSpy(mWindow); + new EventSpyAdaptor(mEventSpy, mObjectRegister); + + QDBusConnection::sessionBus().registerObject("/ktutorial/EventSpy", + mEventSpy); +} + +void EditorSupport::disableEventSpy() { + QDBusConnection::sessionBus().unregisterObject("/ktutorial/EventSpy"); + + delete mEventSpy; + mEventSpy = 0; +} + +} Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h 2010-04-13 18:49:12 UTC (rev 229) @@ -0,0 +1,137 @@ +/*************************************************************************** + * 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 EDITORSUPPORT_EDITORSUPPORT_H +#define EDITORSUPPORT_EDITORSUPPORT_H + +#include <QObject> + +namespace editorsupport { +class EventSpy; +class EventSupportAdaptor; +class ObjectRegister; +} + +namespace editorsupport { + +/** + * Support module for KTutorial editor to be accessed through D-Bus. + * EditorSupport provides a way to introspect a running application from + * KTutorial editor to help the author to design a tutorial. + * + * EditorSupport is composed by three elements: the main object (this class), + * an object register and an event spy. + * + * The main object sets up the D-Bus objects and provides a way to enable and + * disable the EventSpy (as notifying all the events sent by an application + * through D-Bus is very costly, the EventSpy should be enabled only when + * needed), and highlight and stop the highlighting of widgets. + * + * The object register assigns an id to QObjects to be identified by the remote + * KTutorial editor. Using that id, KTutorial editor can request further + * information about an object to the ObjectRegister (for example, the object + * name or the class name of an object). + * + * The event spy filters all the events received by the main window and, + * recursively, all its children objects. It is used in the remote KTutorial + * editor to know, for example, that some widget got the focus, or that the + * mouse entered in some widget. Using the information provided by the EventSpy, + * the KTutorial editor can provide a way to the tutorial author to select + * widgets directly from a running application just with the mouse. + * + * The main object is registered at "/ktutorial" path, and provides the + * "org.kde.ktutorial.EditorSupport" interface. The object register is + * registered at "/ktutorial/ObjectRegister" path and provides the + * "org.kde.ktutorial.ObjectRegister" interface. Finally, when it is enabled, + * the EventSpy is registered at "/ktutorial/EVentSpy" path and provides the + * "org.kde.ktutorial.EventSpy" interface. + */ +class EditorSupport: public QObject { +Q_OBJECT +public: + + /** + * Creates a new EditorSupport with the given parent. + * + * @param parent The parent QObject. + */ + explicit EditorSupport(QObject* parent = 0); + + /** + * Exposes the editor support interfaces through DBus. + * The window and all its children will be spied when EventSpy is enabled. + * + * @param window The main window in the application. + */ + void setup(QObject* window); + + /** + * Returns the object id of the application main window. + * + * @return The object id of the application main window. + */ + int mainWindowObjectId(); + + /** + * Starts the highlighting animation for the widget associated to the given + * id. + * + * @param objectId The id of the widget to highlight. + */ + void highlight(int objectId); + + /** + * Stops the highlighting animation for the widget associated to the given + * id. + * + * @param objectId The id of the widget to stop highlighting. + */ + void stopHighlighting(int objectId); + + /** + * Enables the EventSpy. + */ + void enableEventSpy(); + + /** + * Disables the EventSpy. + */ + void disableEventSpy(); + +private: + + /** + * The object register. + */ + ObjectRegister* mObjectRegister; + + /** + * The event spy. + */ + EventSpy* mEventSpy; + + /** + * The object to spy its events and the events of its children. + */ + QObject* mWindow; + +}; + +} + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.cpp 2010-04-13 18:49:12 UTC (rev 229) @@ -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 <QCoreApplication> + +#include "EditorSupportAdaptor.h" +#include "EditorSupport.h" +#include "ObjectRegister.h" + +namespace editorsupport { + +//public: + +EditorSupportAdaptor::EditorSupportAdaptor(EditorSupport* editorSupport): + QDBusAbstractAdaptor(editorSupport), + mEditorSupport(editorSupport) { +} + +//public slots: + +QString EditorSupportAdaptor::applicationFilePath() const { + return QCoreApplication::applicationFilePath(); +} + +int EditorSupportAdaptor::mainWindowObjectId() const { + return mEditorSupport->mainWindowObjectId(); +} + +void EditorSupportAdaptor::highlight(int objectId) { + mEditorSupport->highlight(objectId); +} + +void EditorSupportAdaptor::stopHighlighting(int objectId) { + mEditorSupport->stopHighlighting(objectId); +} + +void EditorSupportAdaptor::enableEventSpy() { + mEditorSupport->enableEventSpy(); +} + +void EditorSupportAdaptor::disableEventSpy() { + mEditorSupport->disableEventSpy(); +} + +} Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.h 2010-04-13 18:49:12 UTC (rev 229) @@ -0,0 +1,101 @@ +/*************************************************************************** + * 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 EDITORSUPPORT_EDITORSUPPORTADAPTOR_H +#define EDITORSUPPORT_EDITORSUPPORTADAPTOR_H + +#include <QDBusAbstractAdaptor> + +namespace editorsupport { +class EditorSupport; +class ObjectRegister; +} + +namespace editorsupport { + +/** + * Adaptor to expose an EditorSupport through DBus. + * + * @see EditorSupport + */ +class EditorSupportAdaptor: public QDBusAbstractAdaptor { +Q_OBJECT +Q_CLASSINFO("D-Bus Interface", "org.kde.ktutorial.EditorSupport") +public: + + /** + * Creates a new EditorSupportAdaptor for the given EditorSupport. + * + * @param editorSupport The EditorSupport to adapt. + */ + explicit EditorSupportAdaptor(EditorSupport* editorSupport); + +public Q_SLOTS: + + /** + * Returns the path to the application file. + * + * @return The path to the application file. + */ + QString applicationFilePath() const; + + /** + * Returns the object id of the application main window. + * + * @return The object id of the application main window. + */ + int mainWindowObjectId() const; + + /** + * Starts the highlighting animation for the widget associated to the given + * id. + * + * @param objectId The id of the widget to highlight. + */ + void highlight(int objectId); + + /** + * Stops the highlighting animation for the widget associated to the given + * id. + * + * @param objectId The id of the widget to stop highlighting. + */ + void stopHighlighting(int objectId); + + /** + * Enables the EventSpy. + */ + void enableEventSpy(); + + /** + * Disables the EventSpy. + */ + void disableEventSpy(); + +private: + + /** + * The EditorSupport to adapt. + */ + EditorSupport* mEditorSupport; + +}; + +} + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpy.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpy.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpy.cpp 2010-04-13 18:49:12 UTC (rev 229) @@ -0,0 +1,50 @@ +/*************************************************************************** + * 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 <QEvent> + +#include "EventSpy.h" + +namespace editorsupport { + +//public: + +EventSpy::EventSpy(QObject* parent /*= 0*/): QObject(parent) { +} + +void EventSpy::addObjectToSpy(QObject* object) { + object->installEventFilter(this); + + foreach (QObject* child, object->children()) { + addObjectToSpy(child); + } +} + +//protected: + +bool EventSpy::eventFilter(QObject* object, QEvent* event) { + emit eventReceived(object, event); + + if (event->type() == QEvent::ChildAdded) { + addObjectToSpy(static_cast<QChildEvent*>(event)->child()); + } + + return false; +} + +} Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpy.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpy.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpy.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpy.h 2010-04-13 18:49:12 UTC (rev 229) @@ -0,0 +1,77 @@ +/*************************************************************************** + * 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 EDITORSUPPORT_EVENTSPY_H +#define EDITORSUPPORT_EVENTSPY_H + +#include <QObject> + +namespace editorsupport { + +/** + * Spy to know all the events received in an object hierarchy. + * EventSpy emitts a signal whenever an event is received in any of the spied + * objects or its children (recursively). Even children added to a spied object + * after it was added are spied. + */ +class EventSpy: public QObject { +Q_OBJECT +public: + + /** + * Creates a new EventSpy with the given parent. + * + * @param parent The parent QObject. + */ + explicit EventSpy(QObject* parent = 0); + + /** + * Add object and all its children to spy. + * + * @param object The object to spy. + */ + void addObjectToSpy(QObject* object); + +Q_SIGNALS: + + /** + * Emitted when an event is received in any of the spied objects or their + * children. + * + * @param object The object that received the event. + * @param event The event received. + */ + void eventReceived(QObject* object, QEvent* event); + +protected: + + /** + * Filters the events received in the spied object hierarchies. + * A eventReceived(QObject*, QEvent*) is emitted for each event. + * + * @param object The object that received the event. + * @param event The event received. + * @return False, the let events be handled as necessary. + */ + virtual bool eventFilter(QObject* object, QEvent* event); + +}; + +} + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpy.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpyAdaptor.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpyAdaptor.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpyAdaptor.cpp 2010-04-13 18:49:12 UTC (rev 229) @@ -0,0 +1,50 @@ +/*************************************************************************** + * 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 <QEvent> +#include <QMetaEnum> + +#include "EventSpyAdaptor.h" +#include "EventSpy.h" +#include "ObjectRegister.h" + +namespace editorsupport { + +//public: + +EventSpyAdaptor::EventSpyAdaptor(EventSpy* eventSpy, + ObjectRegister* objectRegister): + QDBusAbstractAdaptor(eventSpy), + mObjectRegister(objectRegister) { + connect(eventSpy, SIGNAL(eventReceived(QObject*,QEvent*)), + this, SLOT(handleEventReceived(QObject*,QEvent*))); +} + +//private: + +void EventSpyAdaptor::handleEventReceived(QObject* object, QEvent* event) { + int id = mObjectRegister->idForObject(object); + + int index = QEvent::staticMetaObject.indexOfEnumerator("Type"); + QMetaEnum eventTypeEnumerator = QEvent::staticMetaObject.enumerator(index); + QString eventType = eventTypeEnumerator.valueToKey(event->type()); + + emit eventReceived(id, eventType); +} + +} Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpyAdaptor.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpyAdaptor.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpyAdaptor.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpyAdaptor.h 2010-04-13 18:49:12 UTC (rev 229) @@ -0,0 +1,83 @@ +/*************************************************************************** + * 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 EDITORSUPPORT_EVENTSPYADAPTOR_H +#define EDITORSUPPORT_EVENTSPYADAPTOR_H + +#include <QDBusAbstractAdaptor> + +namespace editorsupport { +class EventSpy; +class ObjectRegister; +} + +namespace editorsupport { + +/** + * Adaptor to expose an EventSpy through DBus. + * + * @see EditorSupport + */ +class EventSpyAdaptor: public QDBusAbstractAdaptor { +Q_OBJECT +Q_CLASSINFO("D-Bus Interface", "org.kde.ktutorial.EventSpy") +public: + + /** + * Creates a new EventSpyAdaptor for the given EventSpy. + * + * @param eventSpy The EventSpy to adapt. + * @param objectRegister The object register to get the object ids from. + */ + explicit EventSpyAdaptor(EventSpy* eventSpy, + ObjectRegister* objectRegister); + +Q_SIGNALS: + + /** + * Emitted when an event is received in any of the spied objects or their + * children. + * + * @param objectId The id of the object that received the event. + * @param eventType The type of the event received. + */ + void eventReceived(int objectId, const QString& eventType); + +private: + + /** + * The register to associate objects with their id. + */ + ObjectRegister* mObjectRegister; + +private Q_SLOTS: + + /** + * Adapts the eventReceived(QObject*, QEvent*) sent by the EventSpy to be + * sent as eventReceived(int, QString). + * + * @param object The object that received the event. + * @param event The event received. + */ + void handleEventReceived(QObject* object, QEvent* event); + +}; + +} + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/EventSpyAdaptor.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.cpp 2010-04-13 18:49:12 UTC (rev 229) @@ -0,0 +1,61 @@ +/*************************************************************************** + * 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 "ObjectRegister.h" + +namespace editorsupport { + +//public: + +ObjectRegister::ObjectRegister(QObject* parent /*= 0*/): QObject(parent), + mNextId(1) { +} + +int ObjectRegister::idForObject(QObject* object) { + int id = mRegisteredIds.value(object); + + if (!id) { + id = mNextId; + mRegisteredObjects.insert(id, object); + mRegisteredIds.insert(object, id); + connect(object, SIGNAL(destroyed(QObject*)), + this, SLOT(deregister(QObject*))); + mNextId++; + } + + return id; +} + +QObject* ObjectRegister::objectForId(int objectId) { + return mRegisteredObjects.value(objectId); +} + +void ObjectRegister::clear() { + mRegisteredIds.clear(); + mRegisteredObjects.clear(); +} + +//private slots: + +void ObjectRegister::deregister(QObject* object) { + int id = mRegisteredIds.value(object); + mRegisteredIds.remove(object); + mRegisteredObjects.remove(id); +} + +} Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.h 2010-04-13 18:49:12 UTC (rev 229) @@ -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 EDITORSUPPORT_OBJECTREGISTER_H +#define EDITORSUPPORT_OBJECTREGISTER_H + +#include <QHash> +#include <QObject> + +namespace editorsupport { + +/** + * Register to associate objects with an id. + * The id for an object is assigned after the first time the id is requested + * for that object, and are valid until the register is cleared or the object is + * destroyed (if an object is destroyed it is automatically removed from the + * register). + * + * Its purpose is assign QObjects an id to allow the remote KTutorial editor to + * refer to the objects in the target application. + */ +class ObjectRegister: public QObject { +Q_OBJECT +public: + + /** + * Creates a new ObjectRegister with the given parent. + * + * @param parent The parent QObject. + */ + explicit ObjectRegister(QObject* parent = 0); + + /** + * Returns the id assigned to the object. + * + * @param object The object to get its id. + * @return The id assigned to the object. + */ + int idForObject(QObject* object); + + /** + * Returns object associated to the id. + * + * @param objectId The id to get its associated object. + * @return The object associated to the id. + */ + QObject* objectForId(int objectId); + + /** + * Removes all the entries in this ObjectRegister. + */ + void clear(); + +private: + + /** + * The next id to assign. + */ + int mNextId; + + /** + * The registered ids mapped by associated object. + */ + QHash<QObject*, int> mRegisteredIds; + + /** + * The registered objects mapped by id. + */ + QHash<int, QObject*> mRegisteredObjects; + +private Q_SLOTS: + + /** + * Deregisters an object automatically when it is destroyed. + * + * @param object The object to deregister. + */ + void deregister(QObject* object); + +}; + +} + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegisterAdaptor.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegisterAdaptor.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegisterAdaptor.cpp 2010-04-13 18:49:12 UTC (rev 229) @@ -0,0 +1,69 @@ +/*************************************************************************** + * 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 "ObjectRegisterAdaptor.h" +#include "ObjectRegister.h" + +namespace editorsupport { + +//public: + +ObjectRegisterAdaptor::ObjectRegisterAdaptor(ObjectRegister* objectRegister): + QDBusAbstractAdaptor(objectRegister), + mObjectRegister(objectRegister) { +} + +//public slots: + +QString ObjectRegisterAdaptor::objectName(int objectId) const { + QObject* object = mObjectRegister->objectForId(objectId); + if (!object) { + return ""; + } + + return object->objectName(); +} + +QString ObjectRegisterAdaptor::className(int objectId) const { + QObject* object = mObjectRegister->objectForId(objectId); + if (!object) { + return ""; + } + + return object->metaObject()->className(); +} + +QList<int> ObjectRegisterAdaptor::childObjectIds(int objectId) const { + QObject* object = mObjectRegister->objectForId(objectId); + if (!object) { + return QList<int>(); + } + + QList<int> ids; + foreach (QObject* childObject, object->children()) { + ids.append(mObjectRegister->idForObject(childObject)); + } + + return ids; +} + +void ObjectRegisterAdaptor::clear() { + mObjectRegister->clear(); +} + +} Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegisterAdaptor.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegisterAdaptor.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegisterAdaptor.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegisterAdaptor.h 2010-04-13 18:49:12 UTC (rev 229) @@ -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 EDITORSUPPORT_OBJECTREGISTERADAPTOR_H +#define EDITORSUPPORT_OBJECTREGISTERADAPTOR_H + +#include <QDBusAbstractAdaptor> + +namespace editorsupport { +class ObjectRegister; +} + +namespace editorsupport { + +/** + * Adaptor to expose an ObjectRegister through DBus. + * + * @see EditorSupport + */ +class ObjectRegisterAdaptor: public QDBusAbstractAdaptor { +Q_OBJECT +Q_CLASSINFO("D-Bus Interface", "org.kde.ktutorial.ObjectRegister") +public: + + /** + * Creates a new ObjectRegisterAdaptor for the given ObjectRegister. + * + * @param objectRegister The ObjectRegister to adapt. + */ + explicit ObjectRegisterAdaptor(ObjectRegister* objectRegister); + +public Q_SLOTS: + + /** + * Returns the name of the object with the given id. + * If the id is not registered, an empty string is returned. + * + * @param objectId The id of the object. + * @return The name of the object. + */ + QString objectName(int objectId) const; + + /** + * Returns the class name of the object with the given id. + * If the id is not registered, an empty string is returned. + * + * @param objectId The id of the object. + * @return The class name of the object. + */ + QString className(int objectId) const; + + /** + * Returns a list with the ids of the child objects of the object with the + * given id. + * If the id is not registered, an empty list is returned. + * + * @param objectId The id of the object. + * @return The ids of the child objects. + */ + QList<int> childObjectIds(int objectId) const; + + /** + * Removes all the entries in the ObjectRegister. + */ + void clear(); + +private: + + /** + * The ObjectRegister to adapt. + */ + ObjectRegister* mObjectRegister; + +}; + +} + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegisterAdaptor.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2010-04-13 18:06:28 UTC (rev 228) +++ trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2010-04-13 18:49:12 UTC (rev 229) @@ -2,6 +2,10 @@ add_subdirectory(scripting) add_subdirectory(view) +if (QT_QTDBUS_FOUND) + add_subdirectory(editorsupport) +endif (QT_QTDBUS_FOUND) + # Used by kde4_add_unit_test to set the full path to test executables set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) Added: trunk/ktutorial/ktutorial-library/tests/editorsupport/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/editorsupport/CMakeLists.txt (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/editorsupport/CMakeLists.txt 2010-04-13 18:49:12 UTC (rev 229) @@ -0,0 +1,41 @@ +# Used by kde4_add_unit_test to set the full path to test executables +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) + +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${ktutorial-library_SOURCE_DIR}/src/editorsupport ${KDE4_INCLUDES}) + +# Since Qt 4.6.0, this definition is needed for GUI testing. +# It is backwards compatible with previous Qt versions, unlike the alternative +# which is to add #include <QTestGui> in the test files. +add_definitions(-DQT_GUI_LIB) + +MACRO(UNIT_TESTS) + FOREACH(_className ${ARGN}) + set(_testName ${_className}Test) + kde4_add_unit_test(${_testName} TESTNAME ktutorial-${_testName} ${_testName}.cpp) + target_link_libraries(${_testName} ktutorial_editorsupport ${QT_QTTEST_LIBRARY}) + ENDFOREACH(_className) +ENDMACRO(UNIT_TESTS) + +unit_tests( + EditorSupport + EditorSupportAdaptor + EventSpy + EventSpyAdaptor + ObjectRegister + ObjectRegisterAdaptor +) + +MACRO(MEM_TESTS) + FOREACH(_testname ${ARGN}) + add_test(ktutorial-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( + EditorSupport + EditorSupportAdaptor + EventSpy + EventSpyAdaptor + ObjectRegister + ObjectRegisterAdaptor +) Property changes on: trunk/ktutorial/ktutorial-library/tests/editorsupport/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportAdaptorTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportAdaptorTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportAdaptorTest.cpp 2010-04-13 18:49:12 UTC (rev 229) @@ -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 <QWidget> +#include <QtDBus/QtDBus> + +#include "EditorSupportAdaptor.h" + +#include "EditorSupport.h" +#include "EventSpy.h" +#include "ObjectRegister.h" +#include "../extendedinformation/WidgetHighlighter.h" + +using extendedinformation::WidgetHighlighter; + +namespace editorsupport { + +class EditorSupportAdaptorTest: public QObject { +Q_OBJECT + +private slots: + + void testConstructor(); + + void testApplicationFilePath(); + + void testMainWindowObjectId(); + + void testHighlight(); + + void testStopHighlighting(); + + void testEnableEventSpy(); + + void testDisableEventSpy(); + +}; + +void EditorSupportAdaptorTest::testConstructor() { + EditorSupport editorSupport; + EditorSupportAdaptor* adaptor = new EditorSupportAdaptor(&editorSupport); + + QCOMPARE(adaptor->parent(), &editorSupport); +} + +void EditorSupportAdaptorTest::testApplicationFilePath() { + EditorSupport editorSupport; + EditorSupportAdaptor* adaptor = new EditorSupportAdaptor(&editorSupport); + + QCOMPARE(adaptor->applicationFilePath(), + QCoreApplication::applicationFilePath()); +} + +void EditorSupportAdaptorTest::testMainWindowObjectId() { + EditorSupport editorSupport; + QWidget window; + editorSupport.setup(&window); + EditorSupportAdaptor* adaptor = new EditorSupportAdaptor(&editorSupport); + + QCOMPARE(adaptor->mainWindowObjectId(), editorSupport.mainWindowObjectId()); +} + +void EditorSupportAdaptorTest::testHighlight() { + EditorSupport editorSupport; + QWidget window; + editorSupport.setup(&window); + EditorSupportAdaptor* adaptor = new EditorSupportAdaptor(&editorSupport); + + adaptor->highlight(adaptor->mainWindowObjectId()); + + QCOMPARE(window.findChildren<WidgetHighlighter*>().count(), 1); + QVERIFY(window.findChild<WidgetHighlighter*>()); +} + +void EditorSupportAdaptorTest::testStopHighlighting() { + EditorSupport editorSupport; + QWidget window; + editorSupport.setup(&window); + EditorSupportAdaptor* adaptor = new EditorSupportAdaptor(&editorSupport); + + adaptor->highlight(adaptor->mainWindowObjectId()); + adaptor->stopHighlighting(adaptor->mainWindowObjectId()); + + QCOMPARE(window.findChildren<WidgetHighlighter*>().count(), 0); +} + +void EditorSupportAdaptorTest::testEnableEventSpy() { + QDBusConnection bus = QDBusConnection::sessionBus(); + QVERIFY(bus.isConnected()); + + EditorSupport editorSupport; + QWidget window; + editorSupport.setup(&window); + EditorSupportAdaptor* adaptor = new EditorSupportAdaptor(&editorSupport); + + adaptor->enableEventSpy(); + + QObject* spyObject = bus.objectRegisteredAt("/ktutorial/EventSpy"); + QVERIFY(spyObject); + QVERIFY(qobject_cast<EventSpy*>(spyObject)); +} + +void EditorSupportAdaptorTest::testDisableEventSpy() { + QDBusConnection bus = QDBusConnection::sessionBus(); + QVERIFY(bus.isConnected()); + + EditorSupport editorSupport; + QWidget window; + editorSupport.setup(&window); + EditorSupportAdaptor* adaptor = new EditorSupportAdaptor(&editorSupport); + + adaptor->enableEventSpy(); + adaptor->disableEventSpy(); + + QVERIFY(!bus.objectRegisteredAt("/ktutorial/EventSpy")); +} + +} + +QTEST_MAIN(editorsupport::EditorSupportAdaptorTest) + +#include "EditorSupportAdaptorTest.moc" Property changes on: trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportAdaptorTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp 2010-04-13 18:49:12 UTC (rev 229) @@ -0,0 +1,213 @@ +/*************************************************************************** + * 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 <QApplication> +#include <QtDBus/QtDBus> + +#define protected public +#define private public +#include "EditorSupport.h" +#undef private +#undef protected + +#include "EventSpy.h" +#include "ObjectRegister.h" +#include "../extendedinformation/WidgetHighlighter.h" + +using extendedinformation::WidgetHighlighter; + +namespace editorsupport { + +class EditorSupportTest: public QObject { +Q_OBJECT + +public slots: + + void handleEventReceived(int objectId, const QString& eventType) { + Q_UNUSED(objectId); + mEventTypes.append(eventType); + } + +private slots: + + void init(); + + void testConstructor(); + + void testSetup(); + + void testMainWindowObjectId(); + + void testHighlight(); + + void testStopHighlighting(); + + void testEnableEventSpy(); + + void testDisableEventSpy(); + +private: + + QStringList mEventTypes; + +}; + +void EditorSupportTest::init() { + mEventTypes.clear(); +} + +void EditorSupportTest::testConstructor() { + QObject parent; + EditorSupport* editorSupport = new EditorSupport(&parent); + + QCOMPARE(editorSupport->parent(), &parent); +} + +void EditorSupportTest::testSetup() { + QDBusConnection bus = QDBusConnection::sessionBus(); + QVERIFY(bus.isConnected()); + + EditorSupport editorSupport; + QObject mainObject; + + editorSupport.setup(&mainObject); + + QObject* ktutorialObject = bus.objectRegisteredAt("/ktutorial"); + QVERIFY(ktutorialObject); + QVERIFY(qobject_cast<EditorSupport*>(ktutorialObject)); + + QObject* objectRegisterObject = + bus.objectRegisteredAt("/ktutorial/ObjectRegister"); + QVERIFY(objectRegisterObject); + QVERIFY(qobject_cast<ObjectRegister*>(objectRegisterObject)); + + QVERIFY(!bus.objectRegisteredAt("/ktutorial/EventSpy")); +} + +void EditorSupportTest::testMainWindowObjectId() { + EditorSupport editorSupport; + QWidget window; + editorSupport.setup(&window); + + int mainWindowObjectId = editorSupport.mainWindowObjectId(); + + QCOMPARE(editorSupport.mObjectRegister->idForObject(&window), + mainWindowObjectId); +} + +void EditorSupportTest::testHighlight() { + EditorSupport editorSupport; + QWidget window; + editorSupport.setup(&window); + + QWidget* widget = new QWidget(&window); + + int widgetId = editorSupport.mObjectRegister->idForObject(widget); + editorSupport.highlight(widgetId); + + QCOMPARE(widget->findChildren<WidgetHighlighter*>().count(), 1); + QVERIFY(widget->findChild<WidgetHighlighter*>()); +} + +void EditorSupportTest::testStopHighlighting() { + EditorSupport editorSupport; + QWidget window; + editorSupport.setup(&window); + + QWidget* widget = new QWidget(&window); + + int widgetId = editorSupport.mObjectRegister->idForObject(widget); + editorSupport.highlight(widgetId); + editorSupport.stopHighlighting(widgetId); + + QCOMPARE(widget->findChildren<WidgetHighlighter*>().count(), 0); +} + +void EditorSupportTest::testEnableEventSpy() { + QDBusConnection bus = QDBusConnection::sessionBus(); + QVERIFY(bus.isConnected()); + + EditorSupport editorSupport; + QObject mainObject; + editorSupport.setup(&mainObject); + editorSupport.enableEventSpy(); + + QDBusInterface* iface = new QDBusInterface(bus.baseService(), + "/ktutorial/EventSpy", "", + bus, this); + QVERIFY(iface->isValid()); + + connect(iface, SIGNAL(eventReceived(int, QString)), + this, SLOT(handleEventReceived(int, QString))); + + QObject* eventSpyObject = bus.objectRegisteredAt("/ktutorial/EventSpy"); + QVERIFY(eventSpyObject); + QVERIFY(qobject_cast<EventSpy*>(eventSpyObject)); + + //Send an event not managed by QObject to avoid messing up it... [truncated message content] |
From: <dan...@us...> - 2010-04-25 23:02:01
|
Revision: 233 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=233&view=rev Author: danxuliu Date: 2010-04-25 23:01:54 +0000 (Sun, 25 Apr 2010) Log Message: ----------- Provide more information about classes in editor support module, so the superclass or the signal list of a class can be fetched by the editor. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/editorsupport/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.h trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegisterAdaptor.h trunk/ktutorial/ktutorial-library/tests/editorsupport/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp trunk/ktutorial/ktutorial-library/tests/editorsupport/ObjectRegisterTest.cpp Added Paths: ----------- trunk/ktutorial/ktutorial-library/src/editorsupport/ClassRegisterAdaptor.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/ClassRegisterAdaptor.h trunk/ktutorial/ktutorial-library/tests/editorsupport/ClassRegisterAdaptorTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/editorsupport/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/CMakeLists.txt 2010-04-14 01:14:27 UTC (rev 232) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/CMakeLists.txt 2010-04-25 23:01:54 UTC (rev 233) @@ -1,6 +1,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR} ${KDE4_INCLUDES}) set(ktutorial_editorsupport_SRCS + ClassRegisterAdaptor.cpp EditorSupport.cpp EditorSupportAdaptor.cpp EventSpy.cpp Added: trunk/ktutorial/ktutorial-library/src/editorsupport/ClassRegisterAdaptor.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/ClassRegisterAdaptor.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/ClassRegisterAdaptor.cpp 2010-04-25 23:01:54 UTC (rev 233) @@ -0,0 +1,85 @@ +/*************************************************************************** + * 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 <QMetaClassInfo> + +#include "ClassRegisterAdaptor.h" +#include "ObjectRegister.h" + +namespace editorsupport { + +//public: + +ClassRegisterAdaptor::ClassRegisterAdaptor(ObjectRegister* objectRegister): + QDBusAbstractAdaptor(objectRegister), + mObjectRegister(objectRegister) { +} + +//public slots: + +QString ClassRegisterAdaptor::superClass(const QString& className) const { + const QMetaObject* metaObject = + mObjectRegister->metaObjectForClassName(className); + if (!metaObject) { + return ""; + } + + if (!metaObject->superClass()) { + return ""; + } + + return metaObject->superClass()->className(); +} + +QStringList ClassRegisterAdaptor::signalList(const QString& className) const { + const QMetaObject* metaObject = + mObjectRegister->metaObjectForClassName(className); + if (!metaObject) { + return QStringList(); + } + + QStringList signalList; + for (int i=0; i<metaObject->methodCount(); ++i) { + QMetaMethod method = metaObject->method(i); + if (isSignalDefinedInClass(method, metaObject)) { + signalList.append(method.signature()); + } + } + + return signalList; +} + +//private: + +bool ClassRegisterAdaptor::isSignalDefinedInClass(const QMetaMethod& metaMethod, + const QMetaObject* metaObject) const { + if (metaMethod.methodType() != QMetaMethod::Signal) { + return false; + } + + const QMetaObject* superClass = metaObject; + while ((superClass = superClass->superClass())) { + if (superClass->indexOfSignal(metaMethod.signature()) != -1) { + return false; + } + } + + return true; +} + +} Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/ClassRegisterAdaptor.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/editorsupport/ClassRegisterAdaptor.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/ClassRegisterAdaptor.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/ClassRegisterAdaptor.h 2010-04-25 23:01:54 UTC (rev 233) @@ -0,0 +1,98 @@ +/*************************************************************************** + * 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 EDITORSUPPORT_CLASSREGISTERADAPTOR_H +#define EDITORSUPPORT_CLASSREGISTERADAPTOR_H + +#include <QDBusAbstractAdaptor> +#include <QStringList> + +namespace editorsupport { +class ObjectRegister; +} + +namespace editorsupport { + +/** + * Adaptor to expose an ObjectRegister through DBus. + * It provides methods to get information about the registered QMetaObjects. + * + * @see EditorSupport + */ +class ClassRegisterAdaptor: public QDBusAbstractAdaptor { +Q_OBJECT +Q_CLASSINFO("D-Bus Interface", "org.kde.ktutorial.ClassRegister") +public: + + /** + * Creates a new ClassRegisterAdaptor for the given ObjectRegister. + * + * @param objectRegister The ObjectRegister to adapt. + */ + explicit ClassRegisterAdaptor(ObjectRegister* objectRegister); + +public Q_SLOTS: + + /** + * Returns super class name of the class with the given name. + * If the class name is not registered, or it has no super class, an empty + * string is returned. + * + * @param className The name of the class. + * @return The name of the super class. + */ + QString superClass(const QString& className) const; + + /** + * Returns the signals of the class with the given name. + * If the class is not registered, an empty list is returned. + * + * Only the signals defined in the given class are included in the list. + * Signals from parent classes must be got using the parent class name. + * + * @param className The name of the class. + * @return The list of signals. + */ + QStringList signalList(const QString& className) const; + +private: + + /** + * The ObjectRegister to adapt. + */ + ObjectRegister* mObjectRegister; + + /** + * Checks whether the given meta method is a signal defined in the given + * meta object. + * If the signal is inherited in the given meta object instead of defined in + * it, false is also returned. + * + * @param metaMethod The meta method to check. + * @param metaObject The meta object to check the meta method with. + * @return True if the meta method is a signal defined in the meta object, + * false otherwise. + */ + bool isSignalDefinedInClass(const QMetaMethod& metaMethod, + const QMetaObject* metaObject) const; + +}; + +} + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/editorsupport/ClassRegisterAdaptor.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp 2010-04-14 01:14:27 UTC (rev 232) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp 2010-04-25 23:01:54 UTC (rev 233) @@ -22,6 +22,7 @@ #include <KDebug> #include "EditorSupport.h" +#include "ClassRegisterAdaptor.h" #include "EditorSupportAdaptor.h" #include "EventSpy.h" #include "EventSpyAdaptor.h" @@ -53,6 +54,7 @@ QDBusConnection::sessionBus().registerObject("/ktutorial", this); mObjectRegister = new ObjectRegister(this); + new ClassRegisterAdaptor(mObjectRegister); new ObjectRegisterAdaptor(mObjectRegister); QDBusConnection::sessionBus().registerObject("/ktutorial/ObjectRegister", Modified: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h 2010-04-14 01:14:27 UTC (rev 232) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h 2010-04-25 23:01:54 UTC (rev 233) @@ -45,7 +45,9 @@ * The object register assigns an id to QObjects to be identified by the remote * KTutorial editor. Using that id, KTutorial editor can request further * information about an object to the ObjectRegister (for example, the object - * name or the class name of an object). + * name or the class name of an object). Moreover, the object register also + * provides information about the classes of the registered objects, like the + * super class or the list of signals defined in each class. * * The event spy filters all the events received by the main window and, * recursively, all its children objects. It is used in the remote KTutorial @@ -57,9 +59,10 @@ * The main object is registered at "/ktutorial" path, and provides the * "org.kde.ktutorial.EditorSupport" interface. The object register is * registered at "/ktutorial/ObjectRegister" path and provides the - * "org.kde.ktutorial.ObjectRegister" interface. Finally, when it is enabled, - * the EventSpy is registered at "/ktutorial/EVentSpy" path and provides the - * "org.kde.ktutorial.EventSpy" interface. + * "org.kde.ktutorial.ObjectRegister" and "org.kde.ktutorial.ClassRegister" + * interfaces. Finally, when it is enabled, the EventSpy is registered at + * "/ktutorial/EVentSpy" path and provides the "org.kde.ktutorial.EventSpy" + * interface. */ class EditorSupport: public QObject { Q_OBJECT Modified: trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.cpp 2010-04-14 01:14:27 UTC (rev 232) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.cpp 2010-04-25 23:01:54 UTC (rev 233) @@ -36,6 +36,8 @@ connect(object, SIGNAL(destroyed(QObject*)), this, SLOT(deregister(QObject*))); mNextId++; + + registerMetaObject(object->metaObject()); } return id; @@ -45,11 +47,31 @@ return mRegisteredObjects.value(objectId); } +const QMetaObject* ObjectRegister::metaObjectForClassName( + const QString& className) const { + return mRegisteredMetaObjects.value(className); +} + void ObjectRegister::clear() { mRegisteredIds.clear(); mRegisteredObjects.clear(); + mRegisteredMetaObjects.clear(); } +//private: + +void ObjectRegister::registerMetaObject(const QMetaObject* metaObject) { + if (mRegisteredMetaObjects.contains(metaObject->className())) { + return; + } + + mRegisteredMetaObjects.insert(metaObject->className(), metaObject); + + if (metaObject->superClass()) { + registerMetaObject(metaObject->superClass()); + } +} + //private slots: void ObjectRegister::deregister(QObject* object) { Modified: trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.h 2010-04-14 01:14:27 UTC (rev 232) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegister.h 2010-04-25 23:01:54 UTC (rev 233) @@ -31,6 +31,10 @@ * destroyed (if an object is destroyed it is automatically removed from the * register). * + * When an object is registered, its QMetaObject and the QMetaObject of all its + * super classes are also registered. The QMetaObject can be got using the class + * name since then until the register is cleared. + * * Its purpose is assign QObjects an id to allow the remote KTutorial editor to * refer to the objects in the target application. */ @@ -62,6 +66,14 @@ QObject* objectForId(int objectId); /** + * Returns the meta object with the given class name. + * + * @param className The class name to get its meta object. + * @return The meta object with the given class name. + */ + const QMetaObject* metaObjectForClassName(const QString& className) const; + + /** * Removes all the entries in this ObjectRegister. */ void clear(); @@ -83,6 +95,18 @@ */ QHash<int, QObject*> mRegisteredObjects; + /** + * The registered meta objects mapped by their class name. + */ + QHash<QString, const QMetaObject*> mRegisteredMetaObjects; + + /** + * Registers a meta object and the meta objects of all its super classes. + * + * @param metaObject The meta object to register. + */ + void registerMetaObject(const QMetaObject* metaObject); + private Q_SLOTS: /** Modified: trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegisterAdaptor.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegisterAdaptor.h 2010-04-14 01:14:27 UTC (rev 232) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/ObjectRegisterAdaptor.h 2010-04-25 23:01:54 UTC (rev 233) @@ -29,6 +29,7 @@ /** * Adaptor to expose an ObjectRegister through DBus. + * It provides methods to get information about the registered QObjects. * * @see EditorSupport */ Modified: trunk/ktutorial/ktutorial-library/tests/editorsupport/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/editorsupport/CMakeLists.txt 2010-04-14 01:14:27 UTC (rev 232) +++ trunk/ktutorial/ktutorial-library/tests/editorsupport/CMakeLists.txt 2010-04-25 23:01:54 UTC (rev 233) @@ -17,6 +17,7 @@ ENDMACRO(UNIT_TESTS) unit_tests( + ClassRegisterAdaptor EditorSupport EditorSupportAdaptor EventSpy @@ -32,6 +33,7 @@ ENDMACRO(MEM_TESTS) mem_tests( + ClassRegisterAdaptor EditorSupport EditorSupportAdaptor EventSpy Added: trunk/ktutorial/ktutorial-library/tests/editorsupport/ClassRegisterAdaptorTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/editorsupport/ClassRegisterAdaptorTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/editorsupport/ClassRegisterAdaptorTest.cpp 2010-04-25 23:01:54 UTC (rev 233) @@ -0,0 +1,96 @@ +/*************************************************************************** + * 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 "ClassRegisterAdaptor.h" +#include "ObjectRegister.h" + +namespace editorsupport { + +class ClassRegisterAdaptorTest: public QObject { +Q_OBJECT + +Q_SIGNALS: + + void dummySignal(); + void dummySignal(int argument1, const QString& argument2); + +private slots: + + void testConstructor(); + + void testSuperClass(); + void testSuperClassWithUnknownClassName(); + + void testSignalList(); + void testSignalListWithUnknownClassName(); + +}; + +void ClassRegisterAdaptorTest::testConstructor() { + ObjectRegister objectRegister; + ClassRegisterAdaptor* adaptor = new ClassRegisterAdaptor(&objectRegister); + + QCOMPARE(adaptor->parent(), &objectRegister); +} + +void ClassRegisterAdaptorTest::testSuperClass() { + ObjectRegister objectRegister; + ClassRegisterAdaptor* adaptor = new ClassRegisterAdaptor(&objectRegister); + + objectRegister.idForObject(this); + + QCOMPARE(adaptor->superClass("editorsupport::ClassRegisterAdaptorTest"), + QString("QObject")); + QCOMPARE(adaptor->superClass("QObject"), QString("")); +} + +void ClassRegisterAdaptorTest::testSuperClassWithUnknownClassName() { + ObjectRegister objectRegister; + ClassRegisterAdaptor* adaptor = new ClassRegisterAdaptor(&objectRegister); + + QCOMPARE(adaptor->superClass("UnknownClassName"), QString("")); +} + +void ClassRegisterAdaptorTest::testSignalList() { + ObjectRegister objectRegister; + ClassRegisterAdaptor* adaptor = new ClassRegisterAdaptor(&objectRegister); + + objectRegister.idForObject(this); + + QStringList signalList = + adaptor->signalList("editorsupport::ClassRegisterAdaptorTest"); + QCOMPARE(signalList.count(), 2); + QCOMPARE(signalList[0], QString("dummySignal()")); + QCOMPARE(signalList[1], QString("dummySignal(int,QString)")); +} + +void ClassRegisterAdaptorTest::testSignalListWithUnknownClassName() { + ObjectRegister objectRegister; + ClassRegisterAdaptor* adaptor = new ClassRegisterAdaptor(&objectRegister); + + QStringList signalList = adaptor->signalList("UnknownClassName"); + QCOMPARE(signalList.count(), 0); +} + +} + +QTEST_MAIN(editorsupport::ClassRegisterAdaptorTest) + +#include "ClassRegisterAdaptorTest.moc" Property changes on: trunk/ktutorial/ktutorial-library/tests/editorsupport/ClassRegisterAdaptorTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp 2010-04-14 01:14:27 UTC (rev 232) +++ trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp 2010-04-25 23:01:54 UTC (rev 233) @@ -27,8 +27,10 @@ #undef private #undef protected +#include "ClassRegisterAdaptor.h" #include "EventSpy.h" #include "ObjectRegister.h" +#include "ObjectRegisterAdaptor.h" #include "../extendedinformation/WidgetHighlighter.h" using extendedinformation::WidgetHighlighter; @@ -96,7 +98,11 @@ QObject* objectRegisterObject = bus.objectRegisteredAt("/ktutorial/ObjectRegister"); QVERIFY(objectRegisterObject); - QVERIFY(qobject_cast<ObjectRegister*>(objectRegisterObject)); + ObjectRegister* objectRegister = + qobject_cast<ObjectRegister*>(objectRegisterObject); + QVERIFY(objectRegister); + QVERIFY(objectRegister->findChild<ClassRegisterAdaptor*>("")); + QVERIFY(objectRegister->findChild<ObjectRegisterAdaptor*>("")); QVERIFY(!bus.objectRegisteredAt("/ktutorial/EventSpy")); } Modified: trunk/ktutorial/ktutorial-library/tests/editorsupport/ObjectRegisterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/editorsupport/ObjectRegisterTest.cpp 2010-04-14 01:14:27 UTC (rev 232) +++ trunk/ktutorial/ktutorial-library/tests/editorsupport/ObjectRegisterTest.cpp 2010-04-25 23:01:54 UTC (rev 233) @@ -20,6 +20,18 @@ #include "ObjectRegister.h" +class DummyClass: public QObject { +Q_OBJECT +}; + +class DummyChildClass1: public DummyClass { +Q_OBJECT +}; + +class DummyChildClass2: public DummyClass { +Q_OBJECT +}; + namespace editorsupport { class ObjectRegisterTest: public QObject { @@ -48,17 +60,23 @@ void ObjectRegisterTest::testRegisterObject() { ObjectRegister objectRegister; - QObject object; + DummyChildClass1 object; int id = objectRegister.idForObject(&object); QCOMPARE(objectRegister.objectForId(id), &object); + QCOMPARE(objectRegister.metaObjectForClassName("DummyChildClass1"), + &DummyChildClass1::staticMetaObject); + QCOMPARE(objectRegister.metaObjectForClassName("DummyClass"), + &DummyClass::staticMetaObject); + QCOMPARE(objectRegister.metaObjectForClassName("QObject"), + &QObject::staticMetaObject); } void ObjectRegisterTest::testRegisterObjectSeveralObjects() { ObjectRegister objectRegister; - QObject object1; - QObject object2; + DummyChildClass1 object1; + DummyChildClass2 object2; QObject object3; int id1 = objectRegister.idForObject(&object1); @@ -71,6 +89,14 @@ QCOMPARE(objectRegister.objectForId(id1), &object1); QCOMPARE(objectRegister.objectForId(id2), &object2); QCOMPARE(objectRegister.objectForId(id3), &object3); + QCOMPARE(objectRegister.metaObjectForClassName("DummyChildClass1"), + &DummyChildClass1::staticMetaObject); + QCOMPARE(objectRegister.metaObjectForClassName("DummyChildClass2"), + &DummyChildClass2::staticMetaObject); + QCOMPARE(objectRegister.metaObjectForClassName("DummyClass"), + &DummyClass::staticMetaObject); + QCOMPARE(objectRegister.metaObjectForClassName("QObject"), + &QObject::staticMetaObject); } void ObjectRegisterTest::testRegisterObjectTwice() { @@ -82,6 +108,8 @@ QCOMPARE(id2, id1); QCOMPARE(objectRegister.objectForId(id1), &object); + QCOMPARE(objectRegister.metaObjectForClassName("QObject"), + &QObject::staticMetaObject); } void ObjectRegisterTest::testObjectForIdWithDestroyedObject() { @@ -93,6 +121,8 @@ delete object; QCOMPARE(objectRegister.objectForId(id), (QObject*)0); + QCOMPARE(objectRegister.metaObjectForClassName("QObject"), + &QObject::staticMetaObject); } void ObjectRegisterTest::testClear() { @@ -105,6 +135,7 @@ objectRegister.clear(); QCOMPARE(objectRegister.objectForId(id), (QObject*)0); + QCOMPARE(objectRegister.metaObjectForClassName("QObject"), (QMetaObject*)0); } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-05-11 03:39:01
|
Revision: 238 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=238&view=rev Author: danxuliu Date: 2010-05-11 03:38:55 +0000 (Tue, 11 May 2010) Log Message: ----------- Add StepTextWidget to show the text of a step. HTML links in the text with the format "widget:nameOfTheWidget" can be used to highlight the referenced widget when a tutorial is being executed. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/view/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/view/StepWidget.cpp trunk/ktutorial/ktutorial-library/src/view/StepWidget.ui trunk/ktutorial/ktutorial-library/tests/view/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/view/StepWidgetTest.cpp Added Paths: ----------- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/CMakeLists.txt 2010-04-26 05:30:40 UTC (rev 237) +++ trunk/ktutorial/ktutorial-library/src/view/CMakeLists.txt 2010-05-11 03:38:55 UTC (rev 238) @@ -1,6 +1,7 @@ -include_directories(${CMAKE_CURRENT_BINARY_DIR} ${KDE4_INCLUDES}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${KDE4_INCLUDES}) set(ktutorial_view_SRCS + StepTextWidget.cpp StepWidget.cpp TutorialListModel.cpp TutorialManagerDialog.cpp @@ -13,4 +14,4 @@ kde4_add_library(ktutorial_view ${ktutorial_view_SRCS}) -target_link_libraries(ktutorial_view ${KDE4_KDEUI_LIBS}) +target_link_libraries(ktutorial_view ktutorial_extendedinformation ${KDE4_KDEUI_LIBS}) Added: trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp 2010-05-11 03:38:55 UTC (rev 238) @@ -0,0 +1,208 @@ +/*************************************************************************** + * 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 <QContextMenuEvent> +#include <QMenu> + +#include <KDebug> +#include <KLocalizedString> + +#include "StepTextWidget.h" +#include "../KTutorial.h" +#include "../extendedinformation/WidgetHighlighterManager.h" + +using extendedinformation::WidgetHighlighterManager; + +namespace view { + +//public: + +StepTextWidget::StepTextWidget(QWidget* parent /*= 0*/): + KTextEdit(parent), + mCurrentHighlightedWidget(0) { + setReadOnly(true); + setFrameShape(QFrame::NoFrame); + setFrameShadow(QFrame::Plain); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + viewport()->setCursor(Qt::ArrowCursor); + + QPalette palette = this->palette(); + palette.setColor(QPalette::Base, Qt::transparent); + setPalette(palette); + + setAlignment(Qt::AlignJustify | Qt::AlignVCenter); + + //Set a explicit text width to avoid some strange behavior: if the text + //width is set to QWIDGETSIZE_MAX without a previous text width set, the + //size returned by the document has weird values. Anyway, sizeHint is + //usually called before minimumSizeHint, so a text width will be already set + //when minimumSizeHint is called, but just in case. + document()->setTextWidth(0); + + connect(this, SIGNAL(textChanged()), this, SLOT(updateText())); +} + +bool StepTextWidget::eventFilter(QObject* watched, QEvent* event) { + if (watched != mCurrentHighlightedWidget) { + return false; + } + + if (event->type() != QEvent::FocusIn) { + return false; + } + + stopHighlightingCurrentWidget(); + + return false; +} + +int StepTextWidget::heightForWidth(int width) const { + return sizeForWidth(width).height(); +} + +QSize StepTextWidget::minimumSizeHint() const { + QSize size; + size.setHeight(sizeForWidth(QWIDGETSIZE_MAX).height()); + size.setWidth(sizeForWidth(0).width()); + + return size; +} + +QSize StepTextWidget::sizeHint() const { + return sizeForWidth(-1); +} + +//protected: + +void StepTextWidget::contextMenuEvent(QContextMenuEvent* event) { + QString anchor = anchorAt(event->pos()); + if (!anchor.startsWith(QLatin1String("widget:"))) { + KTextEdit::contextMenuEvent(event); + return; + } + + QMenu* menu = new QMenu(this); + + if (mCurrentHighlightedWidget && + mCurrentHighlightedWidget != widgetForAnchor(anchor)) { + stopHighlightingCurrentWidget(); + } + + if (!mCurrentHighlightedWidget) { + menu->addAction(i18nc("@item:inmenu", "Highlight"), + this, SLOT(highlightCurrentWidget())); + + mCurrentHighlightedWidget = widgetForAnchor(anchor); + } else { + menu->addAction(i18nc("@item:inmenu", "Stop highlighting"), + this, SLOT(stopHighlightingCurrentWidget())); + } + + menu->exec(event->globalPos()); + delete menu; +} + +void StepTextWidget::mouseMoveEvent(QMouseEvent* event) { + KTextEdit::mouseMoveEvent(event); + + if (anchorAt(event->pos()).startsWith(QLatin1String("widget:"))) { + viewport()->setCursor(Qt::PointingHandCursor); + } else { + viewport()->setCursor(Qt::ArrowCursor); + } +} + +void StepTextWidget::mousePressEvent(QMouseEvent* event) { + QString anchor = anchorAt(event->pos()); + if (event->button() != Qt::LeftButton || + !anchor.startsWith(QLatin1String("widget:"))) { + KTextEdit::mousePressEvent(event); + return; + } + + if (mCurrentHighlightedWidget && + mCurrentHighlightedWidget != widgetForAnchor(anchor)) { + stopHighlightingCurrentWidget(); + } + + if (!mCurrentHighlightedWidget) { + mCurrentHighlightedWidget = widgetForAnchor(anchor); + + highlightCurrentWidget(); + } else { + stopHighlightingCurrentWidget(); + } +} + +//private: + +QSize StepTextWidget::sizeForWidth(int width) const { + const qreal oldTextWidth = document()->textWidth(); + + if (width >= 0) { + document()->setTextWidth(width); + } else { + document()->adjustSize(); + } + + QSize size = document()->size().toSize(); + + document()->setTextWidth(oldTextWidth); + + return size; +} + +QWidget* StepTextWidget::widgetForAnchor(const QString& anchor) { + QString widgetName = anchor.mid(QString("widget:").length()); + return KTutorial::self()->findObject<QWidget*>(widgetName); +} + +//private slots: + +void StepTextWidget::updateText() { + updateGeometry(); + + if (mCurrentHighlightedWidget) { + stopHighlightingCurrentWidget(); + } +} + +void StepTextWidget::highlightCurrentWidget() { + if (!mCurrentHighlightedWidget) { + kWarning() << "The widget to highlight was not found!"; + } + + WidgetHighlighterManager::self()->highlight(mCurrentHighlightedWidget); + + mCurrentHighlightedWidget->installEventFilter(this); +} + +void StepTextWidget::stopHighlightingCurrentWidget() { + if (!mCurrentHighlightedWidget) { + kWarning() << "The widget to stop highlighting was not found!"; + } + + WidgetHighlighterManager::self()->stopHighlighting(mCurrentHighlightedWidget); + + mCurrentHighlightedWidget->removeEventFilter(this); + mCurrentHighlightedWidget = 0; +} + +} Property changes on: trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h 2010-05-11 03:38:55 UTC (rev 238) @@ -0,0 +1,181 @@ +/*************************************************************************** + * 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 VIEW_STEPTEXTWIDGET_H +#define VIEW_STEPTEXTWIDGET_H + +#include <KTextEdit> + +namespace view { + +/** + * A TextEdit with the appearance of a label to show the text of a step. + * It behaves like a QLabel with custom behavior for links. However, as QLabel + * doesn't provide a way to easily extend its link related behavior, a KTextEdit + * with a QLabel appearance (read only, size fit to text, no scroll bars...) is + * used instead. + * + * Links that exhibit a custom behavior are those with the format + * "widget:nameOfTheWidget". When the link is pressed with the left mouse + * button, the widget with the specified name is highlighted. When it is pressed + * again, the highlighting is stopped. If the right button is pressed, a context + * menu with an item to highlight or stop highlighting is shown. + * + * If a widget was being highlighted when the highglighting is started in + * another widget, the highlighting in the previous widget is stopped. The + * highlighting is also stopped when the widget gets the focus, or when the text + * is changed in the StepTextWidget. + * + * When the mouse cursor is moved over a "widget:" link, the arrow cursor is + * changed to a pointing hand cursor. + * + * The links are specified using HTML markup, for example, + * <a href="widget:theNameOfTheWidget">the text of the link</a> + */ +class StepTextWidget: public KTextEdit { +Q_OBJECT +public: + + /** + * Creates a new StepTextWidget. + * + * @param parent The parent widget. + */ + explicit StepTextWidget(QWidget* parent = 0); + + /** + * Watches the widget currently being highlighted and stops the highlighting + * when it is focused. + * + * @param watched The filtered object that received an event. + * @param event The event received. + * @return False, to allow the event to be handled further. + */ + virtual bool eventFilter(QObject* watched, QEvent* event); + + /** + * Returns the height of the size for the given width. + * + * @param width The width to get its height. + * @return The height for the width. + */ + virtual int heightForWidth(int width) const; + + /** + * Returns the recommended minimum size for this StepTextWidget. + * The size has the width of the longest word in the text and the height of + * a single line. + * + * @return The recommended minimum size. + */ + virtual QSize minimumSizeHint() const; + + /** + * Returns the recommended size for this StepTextWidget. + * The size is adjusted to fit the text as a rectangle, with a width bigger + * then the height. + * + * @return The recommended size. + */ + virtual QSize sizeHint() const; + +protected: + + /** + * Shows a custom context menu when a context menu is requested on a + * "widget:" anchor. + * The context menu will contain a "Stop highlighting" or a "Highlight" item + * depending on whether the widget is the one currently being highlighted or + * not. If the widget currently being highlighted is another one, it is + * stopped. + * + * @param event The context menu event. + */ + virtual void contextMenuEvent(QContextMenuEvent* event); + + /** + * Changes the cursor to a pointing hand when it is over a "widget:" anchor. + * + * @param event The mouse move event. + */ + virtual void mouseMoveEvent(QMouseEvent* event); + + /** + * When the mouse is pressed on a "widget:" anchor, it is highlighted or + * stopped being highlighted. + * If the widget currently being highlighted is another one, it is stopped. + * + * @param event The mouse press event. + */ + virtual void mousePressEvent(QMouseEvent* event); + +private: + + /** + * The widget currently being highlighted as a result of activating a link + * in this StepTextWidget. + */ + QWidget* mCurrentHighlightedWidget; + + /** + * Returns the size for the given text width. + * If the width is < 0, the size is adjusted to the text as a rectangle, + * with a width bigger than the height. + * If the width is >= 0, the size is the given width and a height bigger + * enough to show the whole text. The width may be bigger than the given one + * if the given one is not big enough to show the longest word in the text. + * + * @param width The width to get its size. + * @return The size for the width. + */ + QSize sizeForWidth(int width) const; + + /** + * Returns the widget referenced in the given anchor. + * The anchor must have a "widget:name" format. The widget is looked for + * using its name in KTutorial::findObject(QString). + * + * @param anchor The anchor to get its widget. + * @return The widget referenced in the anchor. + */ + QWidget* widgetForAnchor(const QString& anchor); + +private Q_SLOTS: + + /** + * Notifies the layout that the geometry of the widget has changed and stops + * the highlighting of the current widget, if any. + */ + void updateText(); + + /** + * Starts highlighting the current widget. + */ + void highlightCurrentWidget(); + + /** + * Stops highlighting the current widget. + * The current widget is cleared. + */ + void stopHighlightingCurrentWidget(); + +}; + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/src/view/StepWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepWidget.cpp 2010-04-26 05:30:40 UTC (rev 237) +++ trunk/ktutorial/ktutorial-library/src/view/StepWidget.cpp 2010-05-11 03:38:55 UTC (rev 238) @@ -65,7 +65,7 @@ //public slots: void StepWidget::setStep(Step* step) { - ui->textLabel->setText(step->text()); + ui->textWidget->setText(step->text()); setOptions(step->options()); adjustSize(); Modified: trunk/ktutorial/ktutorial-library/src/view/StepWidget.ui =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepWidget.ui 2010-04-26 05:30:40 UTC (rev 237) +++ trunk/ktutorial/ktutorial-library/src/view/StepWidget.ui 2010-05-11 03:38:55 UTC (rev 238) @@ -1,7 +1,8 @@ -<ui version="4.0" > +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> <class>StepWidget</class> - <widget class="QWidget" name="StepWidget" > - <property name="geometry" > + <widget class="QWidget" name="StepWidget"> + <property name="geometry"> <rect> <x>0</x> <y>0</y> @@ -9,39 +10,30 @@ <height>306</height> </rect> </property> - <layout class="QVBoxLayout" > - <property name="spacing" > + <layout class="QVBoxLayout"> + <property name="spacing"> <number>0</number> </property> - <property name="leftMargin" > + <property name="margin"> <number>4</number> </property> - <property name="topMargin" > - <number>4</number> - </property> - <property name="rightMargin" > - <number>4</number> - </property> - <property name="bottomMargin" > - <number>4</number> - </property> <item> - <layout class="QHBoxLayout" > - <property name="spacing" > + <layout class="QHBoxLayout"> + <property name="spacing"> <number>0</number> </property> - <property name="leftMargin" > + <property name="leftMargin"> <number>0</number> </property> - <property name="topMargin" > + <property name="topMargin"> <number>0</number> </property> <item> <spacer> - <property name="orientation" > + <property name="orientation"> <enum>Qt::Horizontal</enum> </property> - <property name="sizeHint" > + <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> @@ -50,17 +42,17 @@ </spacer> </item> <item> - <widget class="KPushButton" name="closeButton" > - <property name="focusPolicy" > + <widget class="KPushButton" name="closeButton"> + <property name="focusPolicy"> <enum>Qt::NoFocus</enum> </property> - <property name="toolTip" > - <string comment="@info:tooltip" >Close this tutorial</string> + <property name="toolTip"> + <string comment="@info:tooltip">Close this tutorial</string> </property> - <property name="whatsThis" > - <string comment="@info:whatsthis" >Click here to close the tutorial.<nl/>The tutorial can be closed when you have finished it, or at any time to cancel it.</string> + <property name="whatsThis"> + <string comment="@info:whatsthis">Click here to close the tutorial.<nl/>The tutorial can be closed when you have finished it, or at any time to cancel it.</string> </property> - <property name="flat" > + <property name="flat"> <bool>true</bool> </property> </widget> @@ -68,32 +60,23 @@ </layout> </item> <item> - <widget class="QLabel" name="textLabel" > - <property name="sizePolicy" > - <sizepolicy vsizetype="MinimumExpanding" hsizetype="MinimumExpanding" > + <widget class="view::StepTextWidget" name="textWidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="toolTip" > - <string comment="@info:tooltip" >The instructions for this step</string> + <property name="toolTip"> + <string comment="@info:tooltip">The instructions for this step</string> </property> - <property name="whatsThis" > - <string comment="@info:whatsthis" >Here appear the instructions for each step of the tutorial.<nl/>Once you complete one step, a new step with new instructions will be shown.</string> + <property name="whatsThis"> + <string comment="@info:whatsthis">Here appear the instructions for each step of the tutorial.<nl/>Once you complete one step, a new step with new instructions will be shown.</string> </property> - <property name="text" > - <string/> - </property> - <property name="alignment" > - <set>Qt::AlignJustify|Qt::AlignVCenter</set> - </property> - <property name="wordWrap" > - <bool>true</bool> - </property> </widget> </item> <item> - <widget class="QWidget" native="1" name="optionsWidget" /> + <widget class="QWidget" name="optionsWidget" native="true"/> </item> </layout> </widget> @@ -103,6 +86,11 @@ <extends>QPushButton</extends> <header>kpushbutton.h</header> </customwidget> + <customwidget> + <class>view::StepTextWidget</class> + <extends>QTextEdit</extends> + <header>StepTextWidget.h</header> + </customwidget> </customwidgets> <resources/> <connections/> Modified: trunk/ktutorial/ktutorial-library/tests/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/CMakeLists.txt 2010-04-26 05:30:40 UTC (rev 237) +++ trunk/ktutorial/ktutorial-library/tests/view/CMakeLists.txt 2010-05-11 03:38:55 UTC (rev 238) @@ -17,6 +17,7 @@ ENDMACRO(UNIT_TESTS) unit_tests( + StepTextWidget StepWidget TutorialListModel TutorialManagerDialog @@ -29,6 +30,7 @@ ENDMACRO(MEM_TESTS) mem_tests( + StepTextWidget StepWidget TutorialListModel TutorialManagerDialog Added: trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2010-05-11 03:38:55 UTC (rev 238) @@ -0,0 +1,365 @@ +/*************************************************************************** + * 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 <qtest_kde.h> + +#include <KXmlGuiWindow> + +#include "StepTextWidget.h" +#define protected public +#define private public +#include "../KTutorial.h" +#undef private +#undef protected +#include "../extendedinformation/WidgetHighlighter.h" + +using extendedinformation::WidgetHighlighter; + +namespace view { + +class StepTextWidgetTest: public QObject { +Q_OBJECT +public slots: + + void selectFirstContextMenuOption(); + +private slots: + + void init(); + void cleanup(); + + void testConstructor(); + + void testSizeHintWithBiggerText(); + void testSizeHintWithSmallerText(); + + void testHighlightWidgetClickingOnAnchor(); + void testStopHighlightingWidgetClickingOnAnchor(); + void testHighlightWidgetUsingContextMenu(); + void testStopHighlightingWidgetUsingContextMenu(); + void testHighlightSeveralWidgets(); + + void testStopHighlightingWidgetWhenFocused(); + + void testSetTextWhenWidgetIsBeingHighlighted(); + +private: + + QPoint centerOfText(const StepTextWidget& widget, const QString& text); + + void showContextMenuAndSelectFirstOption(const StepTextWidget& widget, + const QPoint& position); + +}; + +void StepTextWidgetTest::init() { + delete KTutorial::sSelf; + KTutorial::sSelf = new KTutorial(); +} + +void StepTextWidgetTest::cleanup() { +} + +void StepTextWidgetTest::testConstructor() { + QWidget parent; + StepTextWidget* widget = new StepTextWidget(&parent); + + QCOMPARE(widget->parentWidget(), &parent); + QVERIFY(widget->isReadOnly()); + QCOMPARE(widget->frameShape(), QFrame::NoFrame); + QCOMPARE(widget->frameShadow(), QFrame::Plain); + QCOMPARE(widget->horizontalScrollBarPolicy(), Qt::ScrollBarAlwaysOff); + QCOMPARE(widget->verticalScrollBarPolicy(), Qt::ScrollBarAlwaysOff); + QCOMPARE(widget->palette().color(QPalette::Base), QColor(Qt::transparent)); +} + +void StepTextWidgetTest::testSizeHintWithBiggerText() { + StepTextWidget widget; + widget.setText("Some short text"); + + QSize oldSizeHint = widget.sizeHint(); + + widget.setText("Some bigger text to be shown hopefully in several lines"); + + QSize newSizeHint = widget.sizeHint(); + QCOMPARE(widget.heightForWidth(newSizeHint.width()), newSizeHint.height()); + QVERIFY(newSizeHint.width() > newSizeHint.height()); + QVERIFY(newSizeHint.height() > widget.minimumSizeHint().height()); + QVERIFY(newSizeHint.width() > widget.minimumSizeHint().width()); + QVERIFY(newSizeHint.height() > oldSizeHint.height()); + QVERIFY(newSizeHint.width() > oldSizeHint.width()); +} + +void StepTextWidgetTest::testSizeHintWithSmallerText() { + StepTextWidget widget; + widget.setText("Some bigger text to be shown hopefully in several lines"); + + QSize oldSizeHint = widget.sizeHint(); + + widget.setText("Some short text"); + + QSize newSizeHint = widget.sizeHint(); + QCOMPARE(widget.heightForWidth(newSizeHint.width()), newSizeHint.height()); + QVERIFY(newSizeHint.width() > newSizeHint.height()); + QVERIFY(newSizeHint.height() < oldSizeHint.height()); + QVERIFY(newSizeHint.width() < oldSizeHint.width()); +} + +void StepTextWidgetTest::testHighlightWidgetClickingOnAnchor() { + KXmlGuiWindow mainWindow; + KTutorial::self()->setup(&mainWindow); + QWidget* widgetToHighlight = new QWidget(&mainWindow); + widgetToHighlight->setObjectName("widgetName"); + + StepTextWidget widget; + widget.setText("The <a href=\"widget:widgetName\">widget to highlight</a>"); + widget.show(); + + QPoint position = centerOfText(widget, "widget to highlight"); + QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, + position, 500); + + QVERIFY(widgetToHighlight->findChild<WidgetHighlighter*>("")); +} + +void StepTextWidgetTest::testStopHighlightingWidgetClickingOnAnchor() { + KXmlGuiWindow mainWindow; + KTutorial::self()->setup(&mainWindow); + QWidget* widgetToHighlight = new QWidget(&mainWindow); + widgetToHighlight->setObjectName("widgetName"); + + StepTextWidget widget; + widget.setText("The <a href=\"widget:widgetName\">widget to highlight</a>"); + widget.show(); + + QPoint position = centerOfText(widget, "widget to highlight"); + QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, + position, 500); + QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, + position, 500); + + //Give the highlighter time to stop + QTest::qWait(500); + + QVERIFY(!widgetToHighlight->findChild<WidgetHighlighter*>("")); +} + +void StepTextWidgetTest::testHighlightWidgetUsingContextMenu() { + KXmlGuiWindow mainWindow; + KTutorial::self()->setup(&mainWindow); + QWidget* widgetToHighlight = new QWidget(&mainWindow); + widgetToHighlight->setObjectName("widgetName"); + + StepTextWidget widget; + widget.setText("The <a href=\"widget:widgetName\">widget to highlight</a>"); + widget.show(); + + //Give the widget time to be shown + QTest::qWait(500); + + QPoint position = centerOfText(widget, "widget to highlight"); + showContextMenuAndSelectFirstOption(widget, position); + + QVERIFY(widgetToHighlight->findChild<WidgetHighlighter*>("")); +} + +void StepTextWidgetTest::testStopHighlightingWidgetUsingContextMenu() { + KXmlGuiWindow mainWindow; + KTutorial::self()->setup(&mainWindow); + QWidget* widgetToHighlight = new QWidget(&mainWindow); + widgetToHighlight->setObjectName("widgetName"); + + StepTextWidget widget; + widget.setText("The <a href=\"widget:widgetName\">widget to highlight</a>"); + widget.show(); + + //Give the widget time to be shown + QTest::qWait(500); + + QPoint position = centerOfText(widget, "widget to highlight"); + showContextMenuAndSelectFirstOption(widget, position); + QTest::qWait(500); + showContextMenuAndSelectFirstOption(widget, position); + + //Give the highlighter time to stop + QTest::qWait(500); + + QVERIFY(!widgetToHighlight->findChild<WidgetHighlighter*>("")); +} + +void StepTextWidgetTest::testHighlightSeveralWidgets() { + KXmlGuiWindow mainWindow; + KTutorial::self()->setup(&mainWindow); + QWidget* widgetToHighlight1 = new QWidget(&mainWindow); + widgetToHighlight1->setObjectName("widget1"); + QWidget* widgetToHighlight2 = new QWidget(&mainWindow); + widgetToHighlight2->setObjectName("widget2"); + QWidget* widgetToHighlight3 = new QWidget(&mainWindow); + widgetToHighlight3->setObjectName("widget3"); + + StepTextWidget widget; + widget.setText("The <a href=\"widget:widget1\">first widget</a>, " +"<a href=\"widget:widget2\">second widget</a> and " +"<a href=\"widget:widget3\">third widget</a>"); + widget.show(); + + QPoint position1 = centerOfText(widget, "first widget"); + QPoint position2 = centerOfText(widget, "second widget"); + QPoint position3 = centerOfText(widget, "third widget"); + + QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, + position1, 500); + QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, + position2, 500); + + //Give the highlighter time to stop + QTest::qWait(500); + + QVERIFY(!widgetToHighlight1->findChild<WidgetHighlighter*>("")); + QVERIFY(widgetToHighlight2->findChild<WidgetHighlighter*>("")); + QVERIFY(!widgetToHighlight3->findChild<WidgetHighlighter*>("")); + + showContextMenuAndSelectFirstOption(widget, position3); + + //Give the highlighter time to stop + QTest::qWait(500); + + QVERIFY(!widgetToHighlight1->findChild<WidgetHighlighter*>("")); + QVERIFY(!widgetToHighlight2->findChild<WidgetHighlighter*>("")); + QVERIFY(widgetToHighlight3->findChild<WidgetHighlighter*>("")); + + showContextMenuAndSelectFirstOption(widget, position1); + + //Give the highlighter time to stop + QTest::qWait(500); + + QVERIFY(widgetToHighlight1->findChild<WidgetHighlighter*>("")); + QVERIFY(!widgetToHighlight2->findChild<WidgetHighlighter*>("")); + QVERIFY(!widgetToHighlight3->findChild<WidgetHighlighter*>("")); + + QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, + position1, 500); + + //Give the highlighter time to stop + QTest::qWait(500); + + QVERIFY(!widgetToHighlight1->findChild<WidgetHighlighter*>("")); + QVERIFY(!widgetToHighlight2->findChild<WidgetHighlighter*>("")); + QVERIFY(!widgetToHighlight3->findChild<WidgetHighlighter*>("")); + + QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, + position2, 500); + QTest::qWait(500); + showContextMenuAndSelectFirstOption(widget, position2); + + //Give the highlighter time to stop + QTest::qWait(500); + + QVERIFY(!widgetToHighlight1->findChild<WidgetHighlighter*>("")); + QVERIFY(!widgetToHighlight2->findChild<WidgetHighlighter*>("")); + QVERIFY(!widgetToHighlight3->findChild<WidgetHighlighter*>("")); +} + +void StepTextWidgetTest::testStopHighlightingWidgetWhenFocused() { + KXmlGuiWindow mainWindow; + KTutorial::self()->setup(&mainWindow); + QWidget* widgetToHighlight = new QWidget(&mainWindow); + widgetToHighlight->setObjectName("widgetName"); + mainWindow.setCentralWidget(widgetToHighlight); + mainWindow.show(); + + StepTextWidget widget; + widget.setText("The <a href=\"widget:widgetName\">widget to highlight</a>"); + widget.show(); + + QPoint position = centerOfText(widget, "widget to highlight"); + QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, + position, 500); + + //To get the focus, the parent window of the widget must be active + mainWindow.activateWindow(); + widgetToHighlight->setFocus(); + + //Give the highlighter time to stop + QTest::qWait(500); + + QVERIFY(!widgetToHighlight->findChild<WidgetHighlighter*>("")); +} + +void StepTextWidgetTest::testSetTextWhenWidgetIsBeingHighlighted() { + KXmlGuiWindow mainWindow; + KTutorial::self()->setup(&mainWindow); + QWidget* widgetToHighlight = new QWidget(&mainWindow); + widgetToHighlight->setObjectName("widgetName"); + + StepTextWidget widget; + widget.setText("The <a href=\"widget:widgetName\">widget to highlight</a>"); + widget.show(); + + QPoint position = centerOfText(widget, "widget to highlight"); + QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, + position, 500); + + widget.setText("Another text"); + + QVERIFY(!widgetToHighlight->findChild<WidgetHighlighter*>("")); +} + +/////////////////////////////////// Helpers //////////////////////////////////// + +void StepTextWidgetTest::selectFirstContextMenuOption() { + QVERIFY(QApplication::activePopupWidget()); + QTest::keyClick(QApplication::activePopupWidget(), Qt::Key_Down); + QTest::keyClick(QApplication::activePopupWidget(), Qt::Key_Enter); +} + +QPoint StepTextWidgetTest::centerOfText(const StepTextWidget& widget, + const QString& text) { + QTextCursor cursor = widget.document()->find(text); + + //The cursor rect doesn't include the selection, just a tiny rectangle for + //the cursor position. To ensure that the menu is shown on the anchor, set + //the cursor in the middle of the selection + cursor.setPosition((cursor.selectionStart() + cursor.selectionEnd()) / 2); + return widget.cursorRect(cursor).center(); +} + +void StepTextWidgetTest::showContextMenuAndSelectFirstOption( + const StepTextWidget& widget, const QPoint& position) { + //The context menu can't be triggered sending a right mouse button press + //event, as that is platform dependent (that event is not handled by + //QTextEdit or its parents, but by the QApplication for the platform that + //creates a context menu event when needed). A explicit QContextMenuEvent + //must be sent for it to work. + QContextMenuEvent event(QContextMenuEvent::Mouse, position, + widget.mapToGlobal(position)); + + //The context menu contains its own event loop, 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 QTest::keyClick after the button click won't + //work. + QTimer::singleShot(500, this, SLOT(selectFirstContextMenuOption())); + QApplication::sendEvent(widget.viewport(), &event); +} + +} + +QTEST_KDEMAIN(view::StepTextWidgetTest, GUI) + +#include "StepTextWidgetTest.moc" Property changes on: trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/tests/view/StepWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/StepWidgetTest.cpp 2010-04-26 05:30:40 UTC (rev 237) +++ trunk/ktutorial/ktutorial-library/tests/view/StepWidgetTest.cpp 2010-05-11 03:38:55 UTC (rev 238) @@ -28,6 +28,7 @@ #undef protected #include "ui_StepWidget.h" +#include "StepTextWidget.h" #include "../Option.h" #include "../Step.h" @@ -76,7 +77,7 @@ int mDummySlotCallCount; int mAnotherDummySlotCallCount; - QLabel* textLabel(StepWidget* stepWidget); + StepTextWidget* textWidget(StepWidget* stepWidget); KPushButton* closeButton(StepWidget* stepWidget); }; @@ -88,8 +89,8 @@ i18n("Tutorial: %1", QString("Test tutorial"))); QCOMPARE(stepWidget.windowFlags(), Qt::Dialog | Qt::FramelessWindowHint); QVERIFY(closeButton(&stepWidget)); - QVERIFY(textLabel(&stepWidget)); - QCOMPARE(textLabel(&stepWidget)->text(), QString("")); + QVERIFY(textWidget(&stepWidget)); + QCOMPARE(textWidget(&stepWidget)->toPlainText(), QString("")); QVERIFY(stepWidget.mOptionsLayout); QCOMPARE(stepWidget.mOptionsLayout->count(), 0); } @@ -104,7 +105,7 @@ stepWidget.setStep(&step); - QCOMPARE(textLabel(&stepWidget)->text(), QString("First step")); + QCOMPARE(textWidget(&stepWidget)->toPlainText(), QString("First step")); QCOMPARE(stepWidget.mOptionsLayout->count(), 1); KPushButton* button1 = qobject_cast<KPushButton*>( stepWidget.mOptionsLayout->itemAt(0)->widget()); @@ -123,7 +124,7 @@ stepWidget.setStep(&step1); - QCOMPARE(textLabel(&stepWidget)->text(), QString("First step")); + QCOMPARE(textWidget(&stepWidget)->toPlainText(), QString("First step")); QCOMPARE(stepWidget.mOptionsLayout->count(), 1); KPushButton* button = qobject_cast<KPushButton*>( stepWidget.mOptionsLayout->itemAt(0)->widget()); @@ -138,7 +139,7 @@ stepWidget.setStep(&step2); - QCOMPARE(textLabel(&stepWidget)->text(), QString("Second step")); + QCOMPARE(textWidget(&stepWidget)->toPlainText(), QString("Second step")); QCOMPARE(stepWidget.mOptionsLayout->count(), 2); button = qobject_cast<KPushButton*>( stepWidget.mOptionsLayout->itemAt(0)->widget()); @@ -155,7 +156,7 @@ stepWidget.setStep(&step3); - QCOMPARE(textLabel(&stepWidget)->text(), QString("Third step")); + QCOMPARE(textWidget(&stepWidget)->toPlainText(), QString("Third step")); QCOMPARE(stepWidget.mOptionsLayout->count(), 0); QVERIFY(stepWidget.isVisible()); } @@ -169,7 +170,7 @@ stepWidget.setStep(&step); - QCOMPARE(textLabel(&stepWidget)->text(), QString("First step")); + QCOMPARE(textWidget(&stepWidget)->toPlainText(), QString("First step")); QCOMPARE(stepWidget.mOptionsLayout->count(), 1); KPushButton* button1 = qobject_cast<KPushButton*>( stepWidget.mOptionsLayout->itemAt(0)->widget()); @@ -312,8 +313,8 @@ /////////////////////////////////// Helpers //////////////////////////////////// -QLabel* StepWidgetTest::textLabel(StepWidget* stepWidget) { - return stepWidget->ui->textLabel; +StepTextWidget* StepWidgetTest::textWidget(StepWidget* stepWidget) { + return stepWidget->ui->textWidget; } KPushButton* StepWidgetTest::closeButton(StepWidget* stepWidget) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-05-18 15:37:19
|
Revision: 240 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=240&view=rev Author: danxuliu Date: 2010-05-18 15:37:12 +0000 (Tue, 18 May 2010) Log Message: ----------- Change the way widgets are highlighted: use a semi-transparent child widget over the widget to be highlighted instead of changing its palette, as some widgets like QToolButtons don't paint a background, so changing its palette had no noticeable visual effect. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp 2010-05-18 15:29:49 UTC (rev 239) +++ trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp 2010-05-18 15:37:12 UTC (rev 240) @@ -16,10 +16,9 @@ * along with this program; If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ -#include <QWidget> +#include <QPainter> +#include <QPaintEvent> -#include <KColorUtils> - #include "WidgetHighlighter.h" namespace extendedinformation { @@ -27,12 +26,16 @@ //public: WidgetHighlighter::WidgetHighlighter(QWidget* targetWidget): - QObject(targetWidget), + QWidget(targetWidget), mTargetWidget(targetWidget) { Q_ASSERT(targetWidget); - mOriginalPalette = targetWidget->palette(); + setAttribute(Qt::WA_TransparentForMouseEvents); + setFocusPolicy(Qt::NoFocus); + resize(mTargetWidget->size()); + mTargetWidget->installEventFilter(this); + //TODO Use QPropertyAnimation instead? Increase Qt version requirement in //CMakeLists.txt to Qt 4.6 if done. mProgress = 0; @@ -44,11 +47,20 @@ mProgressForEachTick = interval / (qreal)duration; mTimer.setInterval(interval); - connect(&mTimer, SIGNAL(timeout()), this, SLOT(update())); + connect(&mTimer, SIGNAL(timeout()), this, SLOT(updateProgress())); + + show(); } -WidgetHighlighter::~WidgetHighlighter() { - mTargetWidget->setPalette(mOriginalPalette); +bool WidgetHighlighter::eventFilter(QObject* watched, QEvent* event) { + if (watched != mTargetWidget || event->type() != QEvent::Resize) { + return false; + } + + QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(event); + resize(resizeEvent->size()); + + return false; } //public slots: @@ -70,35 +82,59 @@ } } -//private: +//protected: -void WidgetHighlighter::updateColorGroup(QPalette& palette, - QPalette::ColorGroup colorGroup) { - updateColorRole(palette, colorGroup, QPalette::Window, QPalette::Highlight); - updateColorRole(palette, colorGroup, QPalette::WindowText, - QPalette::HighlightedText); - updateColorRole(palette, colorGroup, QPalette::Base, QPalette::Highlight); - updateColorRole(palette, colorGroup, QPalette::Text, - QPalette::HighlightedText); - updateColorRole(palette, colorGroup, QPalette::Button, QPalette::Highlight); - updateColorRole(palette, colorGroup, QPalette::ButtonText, - QPalette::HighlightedText); -} +void WidgetHighlighter::paintEvent(QPaintEvent* event) { + //Painting the WidgetHighlighter over its parent widget with a + //semi-transpaernt color is the best I could get. However, it has some + //flaws. For example, a QMenu does not highlight its menu button. And some + //widgets may be hardly highlighted if their background color is almost the + //same as the highlight color (like QStatusBar in Oxygen style and default + //colors). + // + //Changing the parent widget palette does not work because some widgets + //(like tool buttons) don't paint a background, only paint the text and get + //its parent widget background. Forcing the painting of the background with + //some color role does not help, as it may look ugly in some styles that use + //a gradient for the background. Changing the palette has one benefit, + //though, as it also changes the palette from the children widgets, which + //means that a QMenu would highlight its menu button. + // + //Ideally, the highlighter should lighten its parent widget but when it is + //too bright (for example, the white background of a text edit). In that + //case, the parent widget should be darkened. To do this, however, the + //WidgetHighlighter must know how its parent widget is painted. + // + //Calling QPixmap::grabWidget from the WidgetHighlighter::paintEvent is not + //good, as it triggers a recursive paint event in its parent (provided the + //WidgetHighlighter paintEvent is guarded against a recursive call, else the + //application would directly hang). Calling it from the updateProgress and + //storing the QPixmap in memory would theoretically work, but it showed some + //strange artifacts. + // + //Setting a custom QGraphicsEffect does not seem like a good idea, as Qt can + //be compiled without them, and because, as far as I know, only one effect + //can be used on a widget at a time (as making a Composite design pattern + //with something like QComposedGraphicsEffect class is pretty easy and there + //is no such class, I presume that it is not a good idea to use several + //effects on the same widget, at least for now). + // + //Anyway, I do not know how to check whether a picture is light or dark + //quickly enough to be done in a realtime animation, so... a + //semi-transparent colored child widget is good enough until someone makes + //something better ;) -void WidgetHighlighter::updateColorRole(QPalette& palette, - QPalette::ColorGroup colorGroup, - QPalette::ColorRole base, - QPalette::ColorRole tint) { - qreal amount = 0.6 * mProgress; - QColor color = KColorUtils::tint(palette.color(colorGroup, base), - palette.color(colorGroup, tint), - amount); - palette.setColor(colorGroup, base, color); + QColor color = mTargetWidget->palette().color(QPalette::Highlight); + + QPainter painter(this); + painter.setOpacity(mProgress / 2.0f); + painter.fillRect(event->rect(), color); + painter.end(); } //private slots: -void WidgetHighlighter::update(){ +void WidgetHighlighter::updateProgress(){ if (mIncreasing) { mProgress += mProgressForEachTick; mProgress = qMin<qreal>(1, mProgress); @@ -109,13 +145,8 @@ mIncreasing = mProgress == 0; } - QPalette updatedPalette = mOriginalPalette; - updateColorGroup(updatedPalette, QPalette::Active); - updateColorGroup(updatedPalette, QPalette::Inactive); - updateColorGroup(updatedPalette, QPalette::Disabled); + update(); - mTargetWidget->setPalette(updatedPalette); - if (mStopping && mProgress == 0) { mTimer.stop(); emit stopped(this); Modified: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h 2010-05-18 15:29:49 UTC (rev 239) +++ trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h 2010-05-18 15:37:12 UTC (rev 240) @@ -19,32 +19,35 @@ #ifndef WIDGETHIGHLIGHTER_H #define WIDGETHIGHLIGHTER_H -#include <QObject> -#include <QPalette> #include <QTimer> +#include <QWidget> namespace extendedinformation { /** * Utility class to highlight a widget. - * WidgetHighlighter executes an animation that modifies the color palette of - * some widget, changing its colors from normal to highlighted and back again. + * WidgetHighlighter executes an animation that tints its parent widget, + * changing its colors from normal to highlighted and back again. * * Once started, the animation goes on indefinitely until the stop slot is - * called. The colors of the palette are set back to the original colors - * animating the change from the current color of the widget to the normal one. + * called. The parent widget recovers its original appearance animating the + * change from the current color of the widget to the normal one. * If the highlighting is started again while the stop animation is running, the * highlighting animation is started again from the current color (that is, the * stop animation is cancelled and a new highlight animation is started from * that point). * + * To tint the widget, the WidgetHighlighter paints itself over the whole parent + * widget with a semi-transparent highlight color, so the parent widget can + * still be seen but tinted with the highlight color. + * * WidgetHighlighter should not be created directly. Instead, * WidgetHighlighterManager should be used, as it takes care of deleting the * highlighter when no longer needed. * * @see WidgetHighlighterManager */ -class WidgetHighlighter: public QObject { +class WidgetHighlighter: public QWidget { Q_OBJECT public: @@ -56,10 +59,14 @@ explicit WidgetHighlighter(QWidget* targetWidget); /** - * Destroys this WidgetHighlighter. - * It restores the original palette to the target widget. + * Resizes this WidgetHighlighter to the size of its parent widget when it + * receives a resize event. + * + * @param watched The filtered object that received an event. + * @param event The event received. + * @return False, to allow the event to be handled further. */ - virtual ~WidgetHighlighter(); + virtual bool eventFilter(QObject* watched, QEvent* event); public Q_SLOTS: @@ -89,6 +96,16 @@ */ void stopped(extendedinformation::WidgetHighlighter* widgetHighlighter); +protected: + + /** + * Fills the widget with a semi-transparent highlight color. + * The degree of opacity depends on the progress of the animation. + * + * @param event The paint event. + */ + virtual void paintEvent(QPaintEvent* event); + private: /** @@ -97,11 +114,6 @@ QWidget* mTargetWidget; /** - * The original palette used by the widget. - */ - QPalette mOriginalPalette; - - /** * Timer to update the colors. */ QTimer mTimer; @@ -127,33 +139,12 @@ */ bool mStopping; - /** - * Updates the color group of the given palette based on the current - * progress. - * - * @param palette The palette to update its colors. - * @param colorGroup The color group of the palette to update. - */ - void updateColorGroup(QPalette& palette, QPalette::ColorGroup colorGroup); - - /** - * Updates the color role "base" tinting it with the color role "tint". - * How much the base color role is tinted depends on the current progress. - * - * @param palette The palette to update its colors. - * @param colorGroup The color group of the palette to update. - * @param base The color role to update. - * @param tint The color role to tint the base color role with. - */ - void updateColorRole(QPalette& palette, QPalette::ColorGroup colorGroup, - QPalette::ColorRole from, QPalette::ColorRole to); - private Q_SLOTS: /** - * Updates the palette of the target widget based on the animation progress. + * Updates the animation progress. */ - void update(); + void updateProgress(); }; Modified: trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp 2010-05-18 15:29:49 UTC (rev 239) +++ trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp 2010-05-18 15:37:12 UTC (rev 240) @@ -21,7 +21,11 @@ #include <QWidget> #include "WidgetHighlighterManager.h" +#define protected public +#define private public #include "WidgetHighlighter.h" +#undef private +#undef protected namespace extendedinformation { @@ -39,6 +43,10 @@ void testDeleteWidgetWhileHighlighting(); +private: + + WidgetHighlighter* highlighterOf(const QWidget* widget) const; + }; void WidgetHighlighterManagerTest::testSelf() { @@ -52,7 +60,6 @@ void WidgetHighlighterManagerTest::testHighlight() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighterManager* manager = WidgetHighlighterManager::self(); manager->highlight(&widget); @@ -61,8 +68,8 @@ QTest::qWait(100); QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 1); - QVERIFY(widget.findChild<WidgetHighlighter*>()); - QVERIFY(widget.palette() != palette); + QVERIFY(highlighterOf(&widget)); + QVERIFY(highlighterOf(&widget)->mProgress > 0); } void WidgetHighlighterManagerTest::testHighlightWidgetAlreadyHighlighted() { @@ -71,12 +78,12 @@ manager->highlight(&widget); - WidgetHighlighter* highlighter = widget.findChild<WidgetHighlighter*>(); + WidgetHighlighter* highlighter = highlighterOf(&widget); manager->highlight(&widget); QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 1); - QCOMPARE(widget.findChild<WidgetHighlighter*>(), highlighter); + QCOMPARE(highlighterOf(&widget), highlighter); } void WidgetHighlighterManagerTest::testHighlightAfterStopHighlighting() { @@ -85,28 +92,25 @@ manager->highlight(&widget); - QPointer<WidgetHighlighter> highlighter = - widget.findChild<WidgetHighlighter*>(); + QPointer<WidgetHighlighter> highlighter = highlighterOf(&widget); QVERIFY(highlighter); manager->stopHighlighting(&widget); QVERIFY(!highlighter); - QPalette palette = widget.palette(); manager->highlight(&widget); //Give it some time to update QTest::qWait(100); QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 1); - QVERIFY(widget.findChild<WidgetHighlighter*>()); - QVERIFY(widget.palette() != palette); + QVERIFY(highlighterOf(&widget)); + QVERIFY(highlighterOf(&widget)->mProgress > 0); } void WidgetHighlighterManagerTest::testStopHighlighting() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighterManager* manager = WidgetHighlighterManager::self(); manager->highlight(&widget); @@ -120,7 +124,6 @@ QTest::qWait(200); QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 0); - QVERIFY(widget.palette() == palette); } void WidgetHighlighterManagerTest::testDeleteWidgetWhileHighlighting() { @@ -137,8 +140,20 @@ //No explicit check is made, if it does not crash everything is fine ;) } +WidgetHighlighter* WidgetHighlighterManagerTest::highlighterOf( + const QWidget* widget) const { + WidgetHighlighter* highlighter = widget->findChild<WidgetHighlighter*>(); + + //Ensure that it is a direct child + if (widget->children().contains(highlighter)) { + return highlighter; + } + + return 0; } +} + QTEST_MAIN(extendedinformation::WidgetHighlighterManagerTest) #include "WidgetHighlighterManagerTest.moc" Modified: trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterTest.cpp 2010-05-18 15:29:49 UTC (rev 239) +++ trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterTest.cpp 2010-05-18 15:37:12 UTC (rev 240) @@ -39,47 +39,33 @@ void testConstructor(); - void testDestructor(); - void testStart(); void testStartAlreadyStarted(); void testStartWhileStopAnimationIsRunning(); - void testUpdate(); - void testUpdateWhenProgressIsAlmostOne(); - void testUpdateWhenProgressIsAlmostZero(); + void testUpdateProgress(); + void testUpdateProgressWhenProgressIsAlmostOne(); + void testUpdateProgressWhenProgressIsAlmostZero(); void testStop(); void testStopAfterStopAnimationEnded(); void testStopImmediatelyAfterStart(); + void testParentWidgetResized(); + }; void WidgetHighlighterTest::testConstructor() { QWidget widget; - widget.setPalette(Qt::blue); - QApplication::setPalette(Qt::green); WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); QCOMPARE(highlighter->parent(), &widget); - QVERIFY(highlighter->mOriginalPalette != QApplication::palette()); - QVERIFY(highlighter->mOriginalPalette == widget.palette()); + QCOMPARE(highlighter->size(), widget.size()); + QCOMPARE(highlighter->mProgress, 0.0); } -void WidgetHighlighterTest::testDestructor() { - QWidget widget; - QPalette palette = widget.palette(); - WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); - - widget.setPalette(QPalette(Qt::green)); - delete highlighter; - - QVERIFY(widget.palette() == palette); -} - void WidgetHighlighterTest::testStart() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->start(); @@ -87,12 +73,11 @@ //Give it some time to update QTest::qWait(100); - QVERIFY(widget.palette() != palette); + QVERIFY(highlighter->mProgress > 0); } void WidgetHighlighterTest::testStartAlreadyStarted() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->start(); @@ -105,12 +90,10 @@ //Ensure that progress is not reseted QCOMPARE(highlighter->mProgress, previousProgress); - QVERIFY(widget.palette() != palette); } void WidgetHighlighterTest::testStartWhileStopAnimationIsRunning() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->mProgress = 0.5; @@ -132,82 +115,59 @@ QVERIFY(highlighter->mProgress > 0.5); QVERIFY(highlighter->mIncreasing); QVERIFY(!highlighter->mStopping); - QVERIFY(widget.palette() != palette); } -void WidgetHighlighterTest::testUpdate() { +void WidgetHighlighterTest::testUpdateProgress() { QWidget widget; WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->mTimer.stop(); - QPalette palette = widget.palette(); int previousProgress = highlighter->mProgress; - highlighter->update(); + highlighter->updateProgress(); QCOMPARE(highlighter->mProgress, previousProgress + highlighter->mProgressForEachTick); - QVERIFY(widget.palette().color(QPalette::Window) != - palette.color(QPalette::Window)); - QVERIFY(widget.palette().color(QPalette::WindowText) != - palette.color(QPalette::WindowText)); - QVERIFY(widget.palette().color(QPalette::Base) != - palette.color(QPalette::Base)); - QVERIFY(widget.palette().color(QPalette::Text) != - palette.color(QPalette::Text)); - QVERIFY(widget.palette().color(QPalette::Button) != - palette.color(QPalette::Button)); - QVERIFY(widget.palette().color(QPalette::ButtonText) != - palette.color(QPalette::ButtonText)); } -void WidgetHighlighterTest::testUpdateWhenProgressIsAlmostOne() { +void WidgetHighlighterTest::testUpdateProgressWhenProgressIsAlmostOne() { QWidget widget; WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->mTimer.stop(); highlighter->mProgress = 0.995; highlighter->mIncreasing = true; - highlighter->update(); + highlighter->updateProgress(); - //Don't check palette changes here, as with such a small update it could - //have been left unchanged QCOMPARE(highlighter->mProgress, 1.0); QCOMPARE(highlighter->mIncreasing, false); - QPalette palette = widget.palette(); - highlighter->update(); + highlighter->updateProgress(); QCOMPARE(highlighter->mProgress, 1 - highlighter->mProgressForEachTick); QCOMPARE(highlighter->mIncreasing, false); - QVERIFY(widget.palette() != palette); } -void WidgetHighlighterTest::testUpdateWhenProgressIsAlmostZero() { +void WidgetHighlighterTest::testUpdateProgressWhenProgressIsAlmostZero() { QWidget widget; WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->mTimer.stop(); highlighter->mProgress = 0.005; highlighter->mIncreasing = false; - highlighter->update(); + highlighter->updateProgress(); - //Don't check palette changes here, as with such a small update it could - //have been left unchanged QCOMPARE(highlighter->mProgress, 0.0); QCOMPARE(highlighter->mIncreasing, true); - QPalette palette = widget.palette(); - highlighter->update(); + highlighter->updateProgress(); QCOMPARE(highlighter->mProgress, highlighter->mProgressForEachTick); QCOMPARE(highlighter->mIncreasing, true); - QVERIFY(widget.palette() != palette); } void WidgetHighlighterTest::testStop() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->mProgress = 0.5; @@ -225,12 +185,10 @@ QVERIFY(highlighter->mProgress < 0.5); QVERIFY(!highlighter->mIncreasing); QVERIFY(highlighter->mStopping); - QVERIFY(widget.palette() != palette); } void WidgetHighlighterTest::testStopAfterStopAnimationEnded() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->start(); @@ -252,7 +210,7 @@ QTest::qWait(200); QVERIFY(!highlighter->mTimer.isActive()); - QVERIFY(widget.palette() == palette); + QCOMPARE(highlighter->mProgress, 0.0); QCOMPARE(stoppedSpy.count(), 1); QVariant argument = stoppedSpy.at(0).at(0); QCOMPARE(argument.userType(), widgetHighlighterStarType); @@ -262,7 +220,6 @@ void WidgetHighlighterTest::testStopImmediatelyAfterStart() { QWidget widget; - QPalette palette = widget.palette(); WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); highlighter->start(); @@ -278,7 +235,7 @@ highlighter->stop(); QVERIFY(!highlighter->mTimer.isActive()); - QVERIFY(widget.palette() == palette); + QCOMPARE(highlighter->mProgress, 0.0); QCOMPARE(stoppedSpy.count(), 1); QVariant argument = stoppedSpy.at(0).at(0); QCOMPARE(argument.userType(), widgetHighlighterStarType); @@ -286,8 +243,23 @@ highlighter); } +void WidgetHighlighterTest::testParentWidgetResized() { + QWidget widget; + WidgetHighlighter* highlighter = new WidgetHighlighter(&widget); + + //The widget must be visible to ensure that it receives the resize event + widget.show(); + + //Resize twice to prevent a false test if the first size was the current + //widget size + widget.resize(4, 4); + widget.resize(108, 108); + + QCOMPARE(highlighter->size(), widget.size()); } +} + QTEST_MAIN(extendedinformation::WidgetHighlighterTest) #include "WidgetHighlighterTest.moc" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-05-18 15:39:24
|
Revision: 241 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=241&view=rev Author: danxuliu Date: 2010-05-18 15:39:17 +0000 (Tue, 18 May 2010) Log Message: ----------- Fix highlighting a parent widget of a currently highlighted widget. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.cpp trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.h trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.cpp 2010-05-18 15:37:12 UTC (rev 240) +++ trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.cpp 2010-05-18 15:39:17 UTC (rev 241) @@ -31,6 +31,11 @@ void WidgetHighlighterManager::highlight(QWidget* widget) { WidgetHighlighter* highlighter = widget->findChild<WidgetHighlighter*>(); + if (highlighter && !widget->children().contains(highlighter)) { + stopHighlighting(highlighter->parentWidget()); + highlighter = 0; + } + if (!highlighter) { highlighter = new WidgetHighlighter(widget); connect(highlighter, SIGNAL(stopped(extendedinformation::WidgetHighlighter*)), @@ -42,7 +47,7 @@ void WidgetHighlighterManager::stopHighlighting(QWidget* widget) { WidgetHighlighter* highlighter = widget->findChild<WidgetHighlighter*>(); - if (!highlighter) { + if (!highlighter || !widget->children().contains(highlighter)) { return; } Modified: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.h 2010-05-18 15:37:12 UTC (rev 240) +++ trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighterManager.h 2010-05-18 15:39:17 UTC (rev 241) @@ -53,7 +53,9 @@ /** * Starts a WidgetHighlighter for the given widget. - * If the widget was already being highlighted nothing is done. + * If the widget was already being highlighted nothing is done. If a child + * widget was already being highlighted it is stopped, so only the parent + * highlighting is active. * * @param widget The widget to highlight. */ Modified: trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp 2010-05-18 15:37:12 UTC (rev 240) +++ trunk/ktutorial/ktutorial-library/tests/extendedinformation/WidgetHighlighterManagerTest.cpp 2010-05-18 15:39:17 UTC (rev 241) @@ -37,9 +37,11 @@ void testHighlight(); void testHighlightWidgetAlreadyHighlighted(); + void testHighlightChildAlreadyHighlighted(); void testHighlightAfterStopHighlighting(); void testStopHighlighting(); + void testStopHighlightingWhenChildIsBeingHighlighted(); void testDeleteWidgetWhileHighlighting(); @@ -86,6 +88,23 @@ QCOMPARE(highlighterOf(&widget), highlighter); } +void WidgetHighlighterManagerTest::testHighlightChildAlreadyHighlighted() { + QWidget widget; + QWidget* child = new QWidget(&widget); + WidgetHighlighterManager* manager = WidgetHighlighterManager::self(); + + manager->highlight(child); + + WidgetHighlighter* childHighlighter = highlighterOf(child); + + manager->highlight(&widget); + + QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 1); + QVERIFY(highlighterOf(&widget)); + QVERIFY(highlighterOf(&widget) != childHighlighter); + QVERIFY(!highlighterOf(child)); +} + void WidgetHighlighterManagerTest::testHighlightAfterStopHighlighting() { QWidget widget; WidgetHighlighterManager* manager = WidgetHighlighterManager::self(); @@ -126,6 +145,30 @@ QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 0); } +void WidgetHighlighterManagerTest:: + testStopHighlightingWhenChildIsBeingHighlighted() { + QWidget widget; + QWidget* child = new QWidget(&widget); + WidgetHighlighterManager* manager = WidgetHighlighterManager::self(); + + manager->highlight(child); + + //Give it some time to update + QTest::qWait(100); + + manager->stopHighlighting(&widget); + + //Give it some time to update + QTest::qWait(200); + + //The highlighter should have not been stopped, as it was not highlighting + //the parent widget, but the child + QCOMPARE(widget.findChildren<WidgetHighlighter*>().count(), 1); + QVERIFY(highlighterOf(child)); + QVERIFY(highlighterOf(child)->mProgress > 0); + QVERIFY(highlighterOf(child)->mIncreasing); +} + void WidgetHighlighterManagerTest::testDeleteWidgetWhileHighlighting() { QWidget* widget = new QWidget(); WidgetHighlighterManager* manager = WidgetHighlighterManager::self(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-09-25 14:39:15
|
Revision: 256 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=256&view=rev Author: danxuliu Date: 2010-09-25 14:39:08 +0000 (Sat, 25 Sep 2010) Log Message: ----------- Fix emitting Tutorial::stepActivated(Step*) before really activating the step, which lead to options created in the step setup to not to be shown in the StepWidget (as they weren't created yet when StepWidget::setStep(Step*) was called). Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/Tutorial.cpp trunk/ktutorial/ktutorial-library/tests/TutorialTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/Tutorial.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/Tutorial.cpp 2010-09-25 03:40:16 UTC (rev 255) +++ trunk/ktutorial/ktutorial-library/src/Tutorial.cpp 2010-09-25 14:39:08 UTC (rev 256) @@ -79,10 +79,10 @@ //TODO remove kDebug() << "Next step: " << step->id() << endl; - emit stepActivated(step); - mCurrentStep = step; mCurrentStep->setActive(true); + + emit stepActivated(step); } //public slots: Modified: trunk/ktutorial/ktutorial-library/tests/TutorialTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/TutorialTest.cpp 2010-09-25 03:40:16 UTC (rev 255) +++ trunk/ktutorial/ktutorial-library/tests/TutorialTest.cpp 2010-09-25 14:39:08 UTC (rev 256) @@ -30,7 +30,12 @@ class TutorialTest: public QObject { Q_OBJECT +public slots: + void assertStepActive(Step* step) { + QVERIFY(step->isActive()); + } + private slots: void testConstructor(); @@ -267,6 +272,10 @@ int stepStarType = qRegisterMetaType<Step*>("Step*"); QSignalSpy stepActivatedSpy(&tutorial, SIGNAL(stepActivated(Step*))); + //Ensure that the step is already active when the signal is emitted + connect(&tutorial, SIGNAL(stepActivated(Step*)), + this, SLOT(assertStepActive(Step*))); + tutorial.nextStep("record"); QCOMPARE(stepActivatedSpy.count(), 1); @@ -296,6 +305,10 @@ int stepStarType = qRegisterMetaType<Step*>("Step*"); QSignalSpy stepActivatedSpy(&tutorial, SIGNAL(stepActivated(Step*))); + //Ensure that the step is already active when the signal is emitted + connect(&tutorial, SIGNAL(stepActivated(Step*)), + this, SLOT(assertStepActive(Step*))); + stepStart->emitNextStepRequested("record"); QCOMPARE(stepActivatedSpy.count(), 1); @@ -352,6 +365,10 @@ int stepStarType = qRegisterMetaType<Step*>("Step*"); QSignalSpy stepActivatedSpy(&tutorial, SIGNAL(stepActivated(Step*))); + //Ensure that the step is already active when the signal is emitted + connect(&tutorial, SIGNAL(stepActivated(Step*)), + this, SLOT(assertStepActive(Step*))); + tutorial.nextStep(step1); QCOMPARE(stepActivatedSpy.count(), 1); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-09-28 21:32:19
|
Revision: 265 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=265&view=rev Author: danxuliu Date: 2010-09-28 21:32:13 +0000 (Tue, 28 Sep 2010) Log Message: ----------- Fix not stopping the highlighting of a widget when the StepTextWidget is deleted. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp 2010-09-28 20:55:26 UTC (rev 264) +++ trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp 2010-09-28 21:32:13 UTC (rev 265) @@ -59,6 +59,12 @@ connect(this, SIGNAL(textChanged()), this, SLOT(updateText())); } +StepTextWidget::~StepTextWidget() { + if (mCurrentHighlightedWidget) { + stopHighlightingCurrentWidget(); + } +} + bool StepTextWidget::eventFilter(QObject* watched, QEvent* event) { if (watched != mCurrentHighlightedWidget) { return false; Modified: trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h 2010-09-28 20:55:26 UTC (rev 264) +++ trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h 2010-09-28 21:32:13 UTC (rev 265) @@ -59,6 +59,12 @@ explicit StepTextWidget(QWidget* parent = 0); /** + * Destroys this StepTextWidget. + * If there is a widget being highlighted, the highlighting is stopped. + */ + virtual ~StepTextWidget(); + + /** * Watches the widget currently being highlighted and stops the highlighting * when it is focused. * Modified: trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2010-09-28 20:55:26 UTC (rev 264) +++ trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2010-09-28 21:32:13 UTC (rev 265) @@ -58,6 +58,7 @@ void testStopHighlightingWidgetWhenFocused(); void testSetTextWhenWidgetIsBeingHighlighted(); + void testDeleteWhenWidgetIsBeingHighlighted(); private: @@ -321,6 +322,25 @@ QVERIFY(!widgetToHighlight->findChild<WidgetHighlighter*>("")); } +void StepTextWidgetTest::testDeleteWhenWidgetIsBeingHighlighted() { + KXmlGuiWindow mainWindow; + KTutorial::self()->setup(&mainWindow); + QWidget* widgetToHighlight = new QWidget(&mainWindow); + widgetToHighlight->setObjectName("widgetName"); + + StepTextWidget* widget = new StepTextWidget(); + widget->setText("The <a href=\"widget:widgetName\">widget to highlight</a>"); + widget->show(); + + QPoint position = centerOfText(*widget, "widget to highlight"); + QTest::mouseClick(widget->viewport(), Qt::LeftButton, Qt::NoModifier, + position, 500); + + delete widget; + + QVERIFY(!widgetToHighlight->findChild<WidgetHighlighter*>("")); +} + /////////////////////////////////// Helpers //////////////////////////////////// void StepTextWidgetTest::selectFirstContextMenuOption() { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-09-30 15:35:41
|
Revision: 266 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=266&view=rev Author: danxuliu Date: 2010-09-30 15:35:32 +0000 (Thu, 30 Sep 2010) Log Message: ----------- Fix crash when waiting for an event received by a null object. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/WaitForEvent.cpp trunk/ktutorial/ktutorial-library/tests/WaitForEventTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/WaitForEvent.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/WaitForEvent.cpp 2010-09-28 21:32:13 UTC (rev 265) +++ trunk/ktutorial/ktutorial-library/src/WaitForEvent.cpp 2010-09-30 15:35:32 UTC (rev 266) @@ -35,10 +35,20 @@ mEventType(type), mConditionMet(false) { + if (!object) { + kWarning() << "The object that receives the event to wait for is null!"; + return; + } + object->installEventFilter(this); } void WaitForEvent::setEvent(QObject* object, const QString& typeName) { + if (!object) { + kWarning() << "The object that receives the event to wait for is null!"; + return; + } + int index = QEvent::staticMetaObject.indexOfEnumerator("Type"); QMetaEnum eventTypeEnumerator = QEvent::staticMetaObject.enumerator(index); Modified: trunk/ktutorial/ktutorial-library/tests/WaitForEventTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/WaitForEventTest.cpp 2010-09-28 21:32:13 UTC (rev 265) +++ trunk/ktutorial/ktutorial-library/tests/WaitForEventTest.cpp 2010-09-30 15:35:32 UTC (rev 266) @@ -29,7 +29,9 @@ private slots: void testConstructor(); + void testConstructorWithNullObject(); void testConstructorDefault(); + void testConstructorDefaultWithNullObject(); void testSetActive(); @@ -74,6 +76,13 @@ QVERIFY(!waitForEvent.conditionMet()); } +void WaitForEventTest::testConstructorWithNullObject() { + WaitForEvent waitForEvent(0, QEvent::ChildAdded); + + QVERIFY(!waitForEvent.isActive()); + QVERIFY(!waitForEvent.conditionMet()); +} + void WaitForEventTest::testConstructorDefault() { WaitForEvent waitForEvent; waitForEvent.setEvent(this, "ChildAdded"); @@ -82,6 +91,14 @@ QVERIFY(!waitForEvent.conditionMet()); } +void WaitForEventTest::testConstructorDefaultWithNullObject() { + WaitForEvent waitForEvent; + waitForEvent.setEvent(0, "ChildAdded"); + + QVERIFY(!waitForEvent.isActive()); + QVERIFY(!waitForEvent.conditionMet()); +} + void WaitForEventTest::testSetActive() { WaitForEvent waitForEvent(this, QEvent::ChildAdded); waitForEvent.mConditionMet = true; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-09-30 15:43:14
|
Revision: 268 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=268&view=rev Author: danxuliu Date: 2010-09-30 15:43:08 +0000 (Thu, 30 Sep 2010) Log Message: ----------- Fix crash when using a link to try to highlight an unknown widget. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp 2010-09-30 15:38:20 UTC (rev 267) +++ trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp 2010-09-30 15:43:08 UTC (rev 268) @@ -193,6 +193,7 @@ void StepTextWidget::highlightCurrentWidget() { if (!mCurrentHighlightedWidget) { kWarning() << "The widget to highlight was not found!"; + return; } WidgetHighlighterManager::self()->highlight(mCurrentHighlightedWidget); @@ -203,6 +204,7 @@ void StepTextWidget::stopHighlightingCurrentWidget() { if (!mCurrentHighlightedWidget) { kWarning() << "The widget to stop highlighting was not found!"; + return; } WidgetHighlighterManager::self()->stopHighlighting(mCurrentHighlightedWidget); Modified: trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2010-09-30 15:38:20 UTC (rev 267) +++ trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2010-09-30 15:43:08 UTC (rev 268) @@ -54,6 +54,7 @@ void testHighlightWidgetUsingContextMenu(); void testStopHighlightingWidgetUsingContextMenu(); void testHighlightSeveralWidgets(); + void testHighlightUnknownWidget(); void testStopHighlightingWidgetWhenFocused(); @@ -277,6 +278,23 @@ QVERIFY(!widgetToHighlight3->findChild<WidgetHighlighter*>("")); } +void StepTextWidgetTest::testHighlightUnknownWidget() { + KXmlGuiWindow mainWindow; + KTutorial::self()->setup(&mainWindow); + + StepTextWidget widget; + widget.setText("The <a href=\"widget:widgetName\">widget to highlight</a>"); + widget.show(); + + QPoint position = centerOfText(widget, "widget to highlight"); + QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, + position, 500); + QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, + position, 500); + + //No explicit check is made, if it does not crash everything is fine ;) +} + void StepTextWidgetTest::testStopHighlightingWidgetWhenFocused() { KXmlGuiWindow mainWindow; KTutorial::self()->setup(&mainWindow); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-10-29 19:45:23
|
Revision: 280 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=280&view=rev Author: danxuliu Date: 2010-10-29 19:45:17 +0000 (Fri, 29 Oct 2010) Log Message: ----------- Fix several bugs in WindowOnTopEnforcer behaviour. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.cpp trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.h trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.cpp 2010-10-29 19:41:20 UTC (rev 279) +++ trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.cpp 2010-10-29 19:45:17 UTC (rev 280) @@ -46,40 +46,72 @@ this, SLOT(handleWindowHidden(QWidget*))); } +//private: + +bool WindowOnTopEnforcer::isAncestorOf(QObject* object, + QObject* childObject) const { + if (childObject->parent() == 0) { + return false; + } + + if (childObject->parent() == object) { + return true; + } + + return isAncestorOf(object, childObject->parent()); +} + +void WindowOnTopEnforcer::reparentWindowTo(QWidget* window, + QWidget* parent) const { + //When a widget is reparented it is hidden and its window flags are cleared, + //so they must be restored and the widget shown again + Qt::WindowFlags flags = window->windowFlags(); + window->setParent(parent); + window->setWindowFlags(flags); + window->show(); +} + //private slots: void WindowOnTopEnforcer::handleWindowShown(QWidget* window) { Q_ASSERT(window); - if (!window->isModal()) { + if (!window->isModal() || mParentStack.contains(window)) { return; } + //If the modal window shown is ancestor of any of the modal windows already + //shown just insert it at the appropriate place in the parent stack. + for (int i=1; i<mParentStack.size(); ++i) { + QWidget* widgetInStack = mParentStack[i]; + + if (!isAncestorOf(widgetInStack, window) && + isAncestorOf(window, widgetInStack)) { + mParentStack.insert(i, window); + return; + } + } + mParentStack.push(window); - //When a widget is reparented it is hidden and its window flags are cleared, - //so they must be restored and the widget shown again - Qt::WindowFlags flags = mWidgetToKeepOnTop->windowFlags(); - mWidgetToKeepOnTop->setParent(window); - mWidgetToKeepOnTop->setWindowFlags(flags); - mWidgetToKeepOnTop->show(); + reparentWindowTo(mWidgetToKeepOnTop, window); } void WindowOnTopEnforcer::handleWindowHidden(QWidget* window) { Q_ASSERT(window); - if (!window->isModal()) { + if (!window->isModal() || !mParentStack.contains(window)) { return; } + if (window != mParentStack.top()) { + mParentStack.remove(mParentStack.indexOf(window)); + return; + } + mParentStack.pop(); - //When a widget is reparented it is hidden and its window flags are cleared - //so they must be restored and the widget shown again - Qt::WindowFlags flags = mWidgetToKeepOnTop->windowFlags(); - mWidgetToKeepOnTop->setParent(mParentStack.top()); - mWidgetToKeepOnTop->setWindowFlags(flags); - mWidgetToKeepOnTop->show(); + reparentWindowTo(mWidgetToKeepOnTop, mParentStack.top()); } } Modified: trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.h 2010-10-29 19:41:20 UTC (rev 279) +++ trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.h 2010-10-29 19:45:17 UTC (rev 280) @@ -29,10 +29,10 @@ namespace view { /** - * Utility class to avoid windows being blocked by modal dialogs. - * When a modal dialog is shown, the widget to keep on top is reparented to the - * shown dialog. When the modal dialog is hidden, the widget is reparented to - * its previous parent. + * Utility class to avoid windows being blocked by modal widgets. + * When a modal widget is shown, the widget to keep on top is reparented to the + * shown widget (if necessary). When the modal widget is hidden, the widget is + * reparented to its first previous ancestor still visible. */ class WindowOnTopEnforcer: public QObject { Q_OBJECT @@ -64,14 +64,36 @@ /** * A stack with the parents of the widget to keep on top. * It is used to restore the previous parent when the latest one is hidden. + * It always contains, at least, the base window as its first item. */ QStack<QWidget*> mParentStack; + /** + * Checks whether the given object is ancestor of the given child object. + * + * @param object The object to check if it is the ancestor. + * @param childObject The child object. + * @return True if it is ancestor, false otherwise. + */ + bool isAncestorOf(QObject* object, QObject* childObject) const; + + /** + * Reparents the window to the given parent, preserving its window flags (as + * they are cleared when a new parent is set). + * + * @param window The window to reparent. + * @param parent The new parent. + */ + void reparentWindowTo(QWidget* window, QWidget* parent) const; + private Q_SLOTS: /** - * Reparents the widget to keep on top to the window if it is a modal - * dialog. + * Reparents the widget to keep on top to the window if it is the top most + * modal widget. + * If the window is modal but is parent of the top most modal widget the + * window is just inserted at the appropriate place in the stack. + * If the window is modal but it is already in the stack nothing is done. * * @param window The window that has been shown. */ @@ -79,7 +101,10 @@ /** * Reparents the widget to keep on top to its previous parent if the hidden - * window is a modal dialog. + * window is the top most modal widget. + * If the window is modal but is parent of the top most modal widget the + * window is just removed from the stack. + * If the window is modal but it is not part of the stack nothing is done. * * @param window The window that has been hidden. */ Modified: trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp 2010-10-29 19:41:20 UTC (rev 279) +++ trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp 2010-10-29 19:45:17 UTC (rev 280) @@ -43,6 +43,22 @@ void testNestedModalDialogOnChildWindow(); + void testModalWidget(); + + void testModalWidgetSiblingOfParent(); + + void testShowModalWidgetTwice(); + + void testHideNestedModalWidgetsInOrder(); + + void testHideNestedModalWidgetsInReverseOrder(); + + void testShowNestedModalWidgetsInOrder(); + + void testShowNestedModalWidgetsInReverseOrder(); + + void testShowNestedModalWidgetsInMixedOrder(); + private: void queueAssertParent(QWidget* widget, QWidget* parent, int timeToWait); @@ -72,6 +88,14 @@ assertWindow(windowToKeepOnTop, window); + window->hide(); + + assertWindow(windowToKeepOnTop, window); + + window->show(); + + assertWindow(windowToKeepOnTop, window); + delete window; QVERIFY(!windowToKeepOnTop); @@ -94,6 +118,14 @@ assertWindow(windowToKeepOnTop, window); + childWindow->hide(); + + assertWindow(windowToKeepOnTop, window); + + childWindow->show(); + + assertWindow(windowToKeepOnTop, window); + delete childWindow; assertWindow(windowToKeepOnTop, window); @@ -316,6 +348,429 @@ QVERIFY(!windowToKeepOnTop); } +void WindowOnTopEnforcerTest::testModalWidget() { + QWidget* window = new QWidget(); + window->show(); + + QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowFlags(Qt::Window); + windowToKeepOnTop->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowFlags(Qt::Window); + modalWidget->setWindowModality(Qt::ApplicationModal); + modalWidget->show(); + + assertWindow(windowToKeepOnTop, modalWidget); + + modalWidget->hide(); + + assertWindow(windowToKeepOnTop, window); + + modalWidget->show(); + + assertWindow(windowToKeepOnTop, modalWidget); + + delete modalWidget; + + assertWindow(windowToKeepOnTop, window); + + delete window; + + QVERIFY(!windowToKeepOnTop); +} + +//This test may cause next test to fail (with a segmentation fault) when the +//modal widget in the next test is shown. +//The crash seems to be random, but has nothing to do (checked by isolating the +//code that crashes) with KTutorial classes. +//Under some unknown circumstances (I have yet to see the crash when GDB is +//recording the execution to analyze it properly :( ), "delete window" in +//testModalWidgetSiblingOfParent method makes qt_last_mouse_receiver (declared +//in qapplication_x11.cpp) to get the address of the window (or one of its child +//windows) when it is deleted. +//As the address is no longer valid, when that variable is used internally in +//the next modalWidget->show() ugly things happen. +//Hopefully I'll get enough information about this weird behavior to fill a bug. +void WindowOnTopEnforcerTest::testModalWidgetSiblingOfParent() { + QWidget* window = new QWidget(); + window->show(); + + QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowFlags(Qt::Window); + windowToKeepOnTop->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + QWidget* siblingModalWidget = new QWidget(window); + siblingModalWidget->setWindowFlags(Qt::Window); + siblingModalWidget->setWindowModality(Qt::ApplicationModal); + siblingModalWidget->show(); + + QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowFlags(Qt::Window); + modalWidget->setWindowModality(Qt::ApplicationModal); + modalWidget->show(); + + assertWindow(windowToKeepOnTop, modalWidget); + + modalWidget->hide(); + + assertWindow(windowToKeepOnTop, siblingModalWidget); + + modalWidget->show(); + + assertWindow(windowToKeepOnTop, modalWidget); + + delete siblingModalWidget; + + assertWindow(windowToKeepOnTop, modalWidget); + + delete modalWidget; + + assertWindow(windowToKeepOnTop, window); + + delete window; + + QVERIFY(!windowToKeepOnTop); +} + +//If a step contains a WaitForWindow and the step it changes to adds two +//WaitForWindows or more in its setup, the WindowOnTopEnforcer will get the Show +//event for the new window twice. +//This is a pretty strange and complex phenomenon that involves some Qt +//internals (http://bugreports.qt.nokia.com/browse/QTBUG-14651): +//Each object contains an internal list with its event filters. When a new +//filter is installed, it is prepended to the list. This list is traversed when +//the events for the object are being filtered; the first filter in the list is +//executed, then the second filter, then the third... and so on until there are +//no more filters in the list (or some filter stopped the event from being +//handled further). +//If a new filter is installed in an object while an event for that object is +//being filtered, the new filter will be prepended, the list will be modified, +//and the filter being executed will now take the next position in the list. +//When the next filter to be executed is fetch it will be the same filter that +//has just been executed. +//When the window that WaitForWindow is waiting for is shown, WaitForWindow will +//change to the next step in the tutorial. When this new step is set up, it adds +//two new WaitForWindows. The WaitForWindows will install a filter for each +//widget, which includes the window that has been shown. So, although it happens +//deep in the call stack, two event filters are installed while an event filter +//for that object is being executed. +//But, why has it to be two and not just one? And why does it affect +//WindowOnTopEnforcer if they are installed in a WaitForWindow? +//The reason is that the filter of the WaitForWindow that changes to the next +//step is the last filter executed, just after the filter of the +//WindowOnTopEnforcer. When the tutorial changes to the next step, it deletes +//the WaitForWindow of the deactivated step, thus removing its filter. Then it +//activates the next step, which causes the two WaitForWindow to be setup, and +//thus two filters are prepended to the filter list. So, as the filter after +//the WindowOnTopEnforcer filter is removed and two new ones are prepended, the +//WindowOnTopEnforcer filter ends one place after the one that was being +//executed. When the execution of that one ends, the end of the list was not +//reached yet, so the loop executes the next filter, and thus causes the +//WindowOnTopEnforcer filter to be executed again. +//And that's all. Strange? I know, I was the one who had to debug it :P +//This test is a synthesized version of all the things explained above. +void WindowOnTopEnforcerTest::testShowModalWidgetTwice() { + QWidget* window = new QWidget(); + window->show(); + + QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowFlags(Qt::Window); + windowToKeepOnTop->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowFlags(Qt::Window); + modalWidget->setWindowModality(Qt::ApplicationModal); + modalWidget->show(); + + QShowEvent showEvent; + QCoreApplication::sendEvent(modalWidget, &showEvent); + + assertWindow(windowToKeepOnTop, modalWidget); + + modalWidget->hide(); + + assertWindow(windowToKeepOnTop, window); + + delete window; + + QVERIFY(!windowToKeepOnTop); +} + +void WindowOnTopEnforcerTest::testHideNestedModalWidgetsInOrder() { + QWidget* window = new QWidget(); + window->show(); + + QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowFlags(Qt::Window); + windowToKeepOnTop->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowFlags(Qt::Window); + modalWidget->setWindowModality(Qt::ApplicationModal); + modalWidget->show(); + + QWidget* nestedModalWidget = new QWidget(modalWidget); + nestedModalWidget->setWindowFlags(Qt::Window); + nestedModalWidget->setWindowModality(Qt::ApplicationModal); + nestedModalWidget->show(); + + assertWindow(windowToKeepOnTop, nestedModalWidget); + + nestedModalWidget->hide(); + + assertWindow(windowToKeepOnTop, modalWidget); + + modalWidget->hide(); + + assertWindow(windowToKeepOnTop, window); + + window->hide(); + + assertWindow(windowToKeepOnTop, window); + + delete window; + + QVERIFY(!windowToKeepOnTop); +} + +void WindowOnTopEnforcerTest::testHideNestedModalWidgetsInReverseOrder() { + QWidget* window = new QWidget(); + window->show(); + + QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowFlags(Qt::Window); + windowToKeepOnTop->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowFlags(Qt::Window); + modalWidget->setWindowModality(Qt::ApplicationModal); + modalWidget->show(); + + QWidget* nestedModalWidget = new QWidget(modalWidget); + nestedModalWidget->setWindowFlags(Qt::Window); + nestedModalWidget->setWindowModality(Qt::ApplicationModal); + nestedModalWidget->show(); + + assertWindow(windowToKeepOnTop, nestedModalWidget); + + window->hide(); + + assertWindow(windowToKeepOnTop, nestedModalWidget); + + modalWidget->hide(); + + assertWindow(windowToKeepOnTop, nestedModalWidget); + + nestedModalWidget->hide(); + + assertWindow(windowToKeepOnTop, window); + + delete window; + + QVERIFY(!windowToKeepOnTop); +} + +void WindowOnTopEnforcerTest::testShowNestedModalWidgetsInOrder() { + QWidget* window = new QWidget(); + window->show(); + + QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowFlags(Qt::Window); + windowToKeepOnTop->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowFlags(Qt::Window); + modalWidget->setWindowModality(Qt::ApplicationModal); + modalWidget->show(); + + QWidget* nestedModalWidget = new QWidget(modalWidget); + nestedModalWidget->setWindowFlags(Qt::Window); + nestedModalWidget->setWindowModality(Qt::ApplicationModal); + nestedModalWidget->show(); + + assertWindow(windowToKeepOnTop, nestedModalWidget); + + nestedModalWidget->hide(); + modalWidget->hide(); + window->hide(); + + window->show(); + + assertWindow(windowToKeepOnTop, window); + + modalWidget->show(); + + assertWindow(windowToKeepOnTop, modalWidget); + + nestedModalWidget->show(); + + assertWindow(windowToKeepOnTop, nestedModalWidget); + + delete nestedModalWidget; + + assertWindow(windowToKeepOnTop, modalWidget); + + delete modalWidget; + + assertWindow(windowToKeepOnTop, window); + + delete window; + + QVERIFY(!windowToKeepOnTop); +} + +void WindowOnTopEnforcerTest::testShowNestedModalWidgetsInReverseOrder() { + QWidget* window = new QWidget(); + window->show(); + + QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowFlags(Qt::Window); + windowToKeepOnTop->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowFlags(Qt::Window); + modalWidget->setWindowModality(Qt::ApplicationModal); + modalWidget->show(); + + QWidget* nestedModalWidget = new QWidget(modalWidget); + nestedModalWidget->setWindowFlags(Qt::Window); + nestedModalWidget->setWindowModality(Qt::ApplicationModal); + nestedModalWidget->show(); + + assertWindow(windowToKeepOnTop, nestedModalWidget); + + window->hide(); + modalWidget->hide(); + nestedModalWidget->hide(); + + nestedModalWidget->show(); + + assertWindow(windowToKeepOnTop, nestedModalWidget); + + modalWidget->show(); + + assertWindow(windowToKeepOnTop, nestedModalWidget); + + window->show(); + + assertWindow(windowToKeepOnTop, nestedModalWidget); + + delete nestedModalWidget; + + assertWindow(windowToKeepOnTop, modalWidget); + + delete modalWidget; + + assertWindow(windowToKeepOnTop, window); + + delete window; + + QVERIFY(!windowToKeepOnTop); +} + +void WindowOnTopEnforcerTest::testShowNestedModalWidgetsInMixedOrder() { + QWidget* window = new QWidget(); + window->show(); + + QPointer<QWidget> windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowFlags(Qt::Window); + windowToKeepOnTop->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + QWidget* modalWidget = new QWidget(window); + modalWidget->setWindowFlags(Qt::Window); + modalWidget->setWindowModality(Qt::ApplicationModal); + modalWidget->show(); + + QWidget* nestedModalWidget = new QWidget(modalWidget); + nestedModalWidget->setWindowFlags(Qt::Window); + nestedModalWidget->setWindowModality(Qt::ApplicationModal); + nestedModalWidget->show(); + + QWidget* nestedNestedModalWidget = new QWidget(nestedModalWidget); + nestedNestedModalWidget->setWindowFlags(Qt::Window); + nestedNestedModalWidget->setWindowModality(Qt::ApplicationModal); + nestedNestedModalWidget->show(); + + QWidget* nestedNestedNestedModalWidget = + new QWidget(nestedNestedModalWidget); + nestedNestedNestedModalWidget->setWindowFlags(Qt::Window); + nestedNestedNestedModalWidget->setWindowModality(Qt::ApplicationModal); + nestedNestedNestedModalWidget->show(); + + assertWindow(windowToKeepOnTop, nestedNestedNestedModalWidget); + + window->hide(); + modalWidget->hide(); + nestedModalWidget->hide(); + nestedNestedModalWidget->hide(); + nestedNestedNestedModalWidget->hide(); + + nestedNestedModalWidget->show(); + + assertWindow(windowToKeepOnTop, nestedNestedModalWidget); + + modalWidget->show(); + + assertWindow(windowToKeepOnTop, nestedNestedModalWidget); + + nestedNestedNestedModalWidget->show(); + + assertWindow(windowToKeepOnTop, nestedNestedNestedModalWidget); + + window->show(); + + assertWindow(windowToKeepOnTop, nestedNestedNestedModalWidget); + + nestedModalWidget->show(); + + assertWindow(windowToKeepOnTop, nestedNestedNestedModalWidget); + + delete nestedNestedNestedModalWidget; + + assertWindow(windowToKeepOnTop, nestedNestedModalWidget); + + delete nestedNestedModalWidget; + + assertWindow(windowToKeepOnTop, nestedModalWidget); + + delete nestedModalWidget; + + assertWindow(windowToKeepOnTop, modalWidget); + + delete modalWidget; + + assertWindow(windowToKeepOnTop, window); + + delete window; + + QVERIFY(!windowToKeepOnTop); +} + /////////////////////////////////// Helpers //////////////////////////////////// //The dialogs are modal, so they won't return to the test code until they are This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2011-01-28 10:59:17
|
Revision: 288 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=288&view=rev Author: danxuliu Date: 2011-01-28 10:59:11 +0000 (Fri, 28 Jan 2011) Log Message: ----------- Fix crash when the StepTextWidget highlighted a widget, the widget was destroyed and then the StepTextWidget tried to stop the highlighting. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h 2010-10-31 20:37:40 UTC (rev 287) +++ trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h 2011-01-28 10:59:11 UTC (rev 288) @@ -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 * @@ -19,6 +19,8 @@ #ifndef VIEW_STEPTEXTWIDGET_H #define VIEW_STEPTEXTWIDGET_H +#include <QPointer> + #include <KTextEdit> namespace view { @@ -136,7 +138,7 @@ * The widget currently being highlighted as a result of activating a link * in this StepTextWidget. */ - QWidget* mCurrentHighlightedWidget; + QPointer<QWidget> mCurrentHighlightedWidget; /** * Returns the size for the given text width. Modified: trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2010-10-31 20:37:40 UTC (rev 287) +++ trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2011-01-28 10:59:11 UTC (rev 288) @@ -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 * @@ -60,6 +60,7 @@ void testSetTextWhenWidgetIsBeingHighlighted(); void testDeleteWhenWidgetIsBeingHighlighted(); + void testDeleteWhenHighlightedWidgetWasDestroyed(); private: @@ -359,6 +360,27 @@ QVERIFY(!widgetToHighlight->findChild<WidgetHighlighter*>("")); } +void StepTextWidgetTest::testDeleteWhenHighlightedWidgetWasDestroyed() { + KXmlGuiWindow mainWindow; + KTutorial::self()->setup(&mainWindow); + QWidget* widgetToHighlight = new QWidget(&mainWindow); + widgetToHighlight->setObjectName("widgetName"); + + StepTextWidget* widget = new StepTextWidget(); + widget->setText("The <a href=\"widget:widgetName\">widget to highlight</a>"); + widget->show(); + + QPoint position = centerOfText(*widget, "widget to highlight"); + QTest::mouseClick(widget->viewport(), Qt::LeftButton, Qt::NoModifier, + position, 500); + + delete widgetToHighlight; + + delete widget; + + //No explicit check is made, if it does not crash everything is fine ;) +} + /////////////////////////////////// Helpers //////////////////////////////////// void StepTextWidgetTest::selectFirstContextMenuOption() { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2011-03-08 02:47:21
|
Revision: 289 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=289&view=rev Author: danxuliu Date: 2011-03-08 02:47:15 +0000 (Tue, 08 Mar 2011) Log Message: ----------- Modify how widgets are highlighted. Due to performance reasons (specially in interfaces with a lot of widgets) instead of tinting the whole widget now only a frame is tinted. The color used now is always got from the active palette, as the highlight color in the inactive palette could be too subtle. The animation interval was increased also for performance reasons, and now only the frame is updated instead of the whole widget. However, the duration of the animation was increased just for stylistic purposes (and StepTextWidgetTest had to be updated due to the new animation length). Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp 2011-01-28 10:59:11 UTC (rev 288) +++ trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.cpp 2011-03-08 02:47:15 UTC (rev 289) @@ -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 * @@ -42,13 +42,15 @@ mIncreasing = true; mStopping = false; - int interval = 25; - int duration = 500; + int interval = 80; + int duration = 1000; mProgressForEachTick = interval / (qreal)duration; mTimer.setInterval(interval); connect(&mTimer, SIGNAL(timeout()), this, SLOT(updateProgress())); + mFrameWidth = 6; + show(); } @@ -86,11 +88,10 @@ void WidgetHighlighter::paintEvent(QPaintEvent* event) { //Painting the WidgetHighlighter over its parent widget with a - //semi-transpaernt color is the best I could get. However, it has some + //semi-transparent color is the best I could get. However, it has some //flaws. For example, a QMenu does not highlight its menu button. And some //widgets may be hardly highlighted if their background color is almost the - //same as the highlight color (like QStatusBar in Oxygen style and default - //colors). + //same as the highlight color (although it should not happen very often). // //Changing the parent widget palette does not work because some widgets //(like tool buttons) don't paint a background, only paint the text and get @@ -123,12 +124,33 @@ //quickly enough to be done in a realtime animation, so... a //semi-transparent colored child widget is good enough until someone makes //something better ;) + // + //However, filling the whole widget and animating it is too CPU intensive + //when tinting a parent widget with lots of child widgets (for example, the + //main widget in showfoto). So, instead of tinting the whole parent only + //a frame is painted and animated. In this case, the frame gets its full + //opacity at the peak of the animation, instead of half the opacity as used + //when tinting the whole widget. - QColor color = mTargetWidget->palette().color(QPalette::Highlight); + //The inactive palette does not usually have a lot of contrast between + //normal color and highlight color (for example, a QStatusBar in Oxygen + //style). The highlight color from the active palette is used in every case + //to ensure that the widget is clearly highlighted, even if the window is + //not the active one. + QColor color = mTargetWidget->palette().color(QPalette::Active, + QPalette::Highlight); QPainter painter(this); - painter.setOpacity(mProgress / 2.0f); - painter.fillRect(event->rect(), color); + painter.setOpacity(mProgress); + + QPen pen; + pen.setWidth(mFrameWidth); + pen.setColor(color); + painter.setPen(pen); + + //Third and fourth arguments are width and height, not end coordinates + painter.drawRect(pen.width() / 2, pen.width() / 2, + width() - pen.width(), height() - pen.width()); painter.end(); } @@ -145,7 +167,12 @@ mIncreasing = mProgress == 0; } - update(); + //Update just the frame of the widget instead of the whole widget for + //performance + update(0, 0, width(), mFrameWidth); + update(0, 0, mFrameWidth, height()); + update(width() - mFrameWidth, 0, width(), height()); + update(0, height() - mFrameWidth, width(), height()); if (mStopping && mProgress == 0) { mTimer.stop(); Modified: trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h 2011-01-28 10:59:11 UTC (rev 288) +++ trunk/ktutorial/ktutorial-library/src/extendedinformation/WidgetHighlighter.h 2011-03-08 02:47:15 UTC (rev 289) @@ -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 * @@ -37,9 +37,10 @@ * stop animation is cancelled and a new highlight animation is started from * that point). * - * To tint the widget, the WidgetHighlighter paints itself over the whole parent - * widget with a semi-transparent highlight color, so the parent widget can - * still be seen but tinted with the highlight color. + * To tint the widget, the WidgetHighlighter paints itself over the parent + * widget with a semi-transparent highlight color. However, due to performance + * reasons, only a frame is tinted instead of the whole widget. The center of + * the frame is always transparent. * * WidgetHighlighter should not be created directly. Instead, * WidgetHighlighterManager should be used, as it takes care of deleting the @@ -99,7 +100,7 @@ protected: /** - * Fills the widget with a semi-transparent highlight color. + * Paints a frame with a semi-transparent highlight color. * The degree of opacity depends on the progress of the animation. * * @param event The paint event. @@ -139,6 +140,11 @@ */ bool mStopping; + /** + * The width of the frame used to highlight the widget. + */ + int mFrameWidth; + private Q_SLOTS: /** Modified: trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2011-01-28 10:59:11 UTC (rev 288) +++ trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2011-03-08 02:47:15 UTC (rev 289) @@ -201,7 +201,7 @@ showContextMenuAndSelectFirstOption(widget, position); //Give the highlighter time to stop - QTest::qWait(500); + QTest::qWait(1000); QVERIFY(!widgetToHighlight->findChild<WidgetHighlighter*>("")); } @@ -260,7 +260,7 @@ position1, 500); //Give the highlighter time to stop - QTest::qWait(500); + QTest::qWait(1000); QVERIFY(!widgetToHighlight1->findChild<WidgetHighlighter*>("")); QVERIFY(!widgetToHighlight2->findChild<WidgetHighlighter*>("")); @@ -272,7 +272,7 @@ showContextMenuAndSelectFirstOption(widget, position2); //Give the highlighter time to stop - QTest::qWait(500); + QTest::qWait(1000); QVERIFY(!widgetToHighlight1->findChild<WidgetHighlighter*>("")); QVERIFY(!widgetToHighlight2->findChild<WidgetHighlighter*>("")); 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:39:34
|
Revision: 305 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=305&view=rev Author: danxuliu Date: 2011-05-16 16:39:27 +0000 (Mon, 16 May 2011) Log Message: ----------- Add WaitForProperty class to wait for a property to change its value to the expected one. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/scripting/ScriptingModule.cpp trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingModuleTest.cpp trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingTest.cpp Added Paths: ----------- trunk/ktutorial/ktutorial-library/src/WaitForProperty.cpp trunk/ktutorial/ktutorial-library/src/WaitForProperty.h trunk/ktutorial/ktutorial-library/tests/WaitForPropertyTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2011-05-08 15:16:11 UTC (rev 304) +++ trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2011-05-16 16:39:27 UTC (rev 305) @@ -28,6 +28,7 @@ WaitForEvent.cpp WaitForNot.cpp WaitForOr.cpp + WaitForProperty.cpp WaitForSignal.cpp WaitForWindow.cpp ) @@ -58,6 +59,7 @@ WaitForEvent.h WaitForNot.h WaitForOr.h + WaitForProperty.h WaitForSignal.h WaitForWindow.h ) Added: trunk/ktutorial/ktutorial-library/src/WaitForProperty.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/WaitForProperty.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/WaitForProperty.cpp 2011-05-16 16:39:27 UTC (rev 305) @@ -0,0 +1,94 @@ +/*************************************************************************** + * 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" + +#include <QMetaProperty> + +#include <KDebug> + +//public: + +WaitForProperty::WaitForProperty(): WaitFor() { +} + +WaitForProperty::WaitForProperty(QObject* object, const QString& propertyName, + const QVariant& value): WaitFor() { + setProperty(object, propertyName, value); +} + +void WaitForProperty::setProperty(QObject* object, const QString& propertyName, + const QVariant& value) { + mObject = object; + mPropertyName = propertyName; + mValue = value; + + if (!mObject) { + kWarning() << "The object that contains the property" << mPropertyName + << "to wait for is null!"; + return; + } + + const QMetaObject* metaObject = mObject->metaObject(); + int propertyIndex = metaObject->indexOfProperty(mPropertyName.toUtf8()); + + if (propertyIndex == -1) { + kWarning() << "The class" << metaObject->className() << "does not" + << "contain a property named" << mPropertyName << "!"; + return; + } + + QMetaProperty metaProperty = metaObject->property(propertyIndex); + + if (!metaProperty.hasNotifySignal()) { + kWarning() << "The property" << mPropertyName << "in the class" + << metaObject->className() << "does not have a notification" + << "signal!"; + return; + } + + const char* notifySignalSignature = metaProperty.notifySignal().signature(); + QString notifySignal = QString("2%1").arg(notifySignalSignature); + + connect(object, notifySignal.toUtf8(), + this, SLOT(checkPropertyValueToEndTheWait())); +} + +bool WaitForProperty::conditionMet() const { + if (!mObject) { + return false; + } + + if (mObject->property(mPropertyName.toUtf8()) != mValue) { + return false; + } + + return true; +} + +//private slots: + +void WaitForProperty::checkPropertyValueToEndTheWait() { + if (!isActive()) { + return; + } + + if (conditionMet()) { + emit waitEnded(this); + } +} Property changes on: trunk/ktutorial/ktutorial-library/src/WaitForProperty.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/WaitForProperty.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/WaitForProperty.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/WaitForProperty.h 2011-05-16 16:39:27 UTC (rev 305) @@ -0,0 +1,145 @@ +/*************************************************************************** + * 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 <QVariant> + +#include "ktutorial_export.h" + +#include "WaitFor.h" + +/** + * Waits for a property to have a specific value. + * When the property changes its value to the specified one and the + * WaitForProperty is active, the wait ends. + * + * Note that the condition can be met even if the wait did not end. The + * condition is met if the property has the expected value. However, the wait + * ends only when the property changes to the expected value while the WaitFor + * is active. That is, in an active WaitFor, the condition would be met even if + * the expected value was set in the property when the WaitFor was inactive, but + * the wait would not end until the expected value was set again in the property + * after the WaitFor was activated. + * + * Not every property can be used to wait for its value to change. Only + * properties that have a notify signal can be used with that purpose. + * + * WaitForProperty with properties that do not have a notify signal can still be + * used to enrich other WaitFors (for example, a WaitForAnd containing a + * WaitForSignal and a WaitForProperty: 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 WaitFors (for example, + * waiting just for the property "visible" of some widget to have some value + * would never end). + * + * Using a WaitForProperty just to perform further checks when other WaitFor + * ends its waiting should be done only with properties that do not have a + * notify signal. For example, if a WaitForAnd contains a WaitForSignal and a + * WaitForProperty, and the WaitForProperty uses a property with a notify + * signal, it may happen that the property changes to the expected value after + * the signal was emitted. If that happens, the WaitForProperty would end its + * waiting, and so the WaitForAnd as the WaitForSignal had already ended its + * waiting too. It may not be the desired behavior if the WaitForProperty was + * there to be checked only when the WaitForSignal ended. In that case, instead + * of using a WaitForProperty, the proper way to do it is executing a custom + * slot when the WaitForSignal ends its waiting, and checking the value of the + * property in a "if" construction in that slot. + */ +class KTUTORIAL_EXPORT WaitForProperty: public WaitFor { +Q_OBJECT +public: + + /** + * Creates a new WaitForProperty. + * This constructor is needed to dynamically create WaitForProperty objects + * in scripts using ScriptingModule::newWaitFor(const QString&). Method + * setProperty(QObject*, const QString&, const QVariant&) must be called to + * finish setting up the object. For C++ tutorials, use + * WaitForProperty(QObject*, const QString&, const QVariant&) constructor + * instead of this one. + */ + Q_INVOKABLE WaitForProperty(); + + /** + * Creates a new WaitForProperty. + * + * @param object The object that contains the property. + * @param propertyName The name of the property. + * @param value The value of the property to wait for. + */ + WaitForProperty(QObject* object, const QString& propertyName, + const QVariant& value); + + /** + * Sets the property to wait for. + * This method can be invoked from a script. + * + * In fact, you should only invoke this method from a script, and only once, + * to set up the object. For C++ tutorials, use + * WaitForProperty(QObject*, const QString&, const QVariant&) constructor + * when creating this WaitForProperty. + * + * @param object The object that contains the property. + * @param propertyName The name of the property. + * @param value The value of the property to wait for. + */ + Q_INVOKABLE void setProperty(QObject* sender, const QString& propertyName, + const QVariant& value); + + /** + * Returns true if the property has the expected value, false otherwise. + * Note that it will return true even if the property got the expected value + * when this WaitForProperty was inactive. That is, as long as the property + * has the expected value, it will return true, no matter when that value + * was set. + * + * @return True if the property has the expected value, false otherwise. + */ + virtual bool conditionMet() const; + +private: + + /** + * The object that contains the property. + */ + QObject* mObject; + + /** + * The name of the property. + */ + QString mPropertyName; + + /** + * The value of the property to wait for. + */ + QVariant mValue; + +private slots: + + /** + * When the property value changes to the expected one, this method notifies + * that the wait for the property ended. + * The wait is only ended if this WaitForProperty is active. + */ + void checkPropertyValueToEndTheWait(); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/WaitForProperty.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/src/scripting/ScriptingModule.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/scripting/ScriptingModule.cpp 2011-05-08 15:16:11 UTC (rev 304) +++ trunk/ktutorial/ktutorial-library/src/scripting/ScriptingModule.cpp 2011-05-16 16:39:27 UTC (rev 305) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2009-2010 by Daniel Calviño Sánchez * + * Copyright (C) 2009-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -27,6 +27,7 @@ #include "../WaitForEvent.h" #include "../WaitForNot.h" #include "../WaitForOr.h" +#include "../WaitForProperty.h" #include "../WaitForSignal.h" #include "../WaitForWindow.h" @@ -39,6 +40,7 @@ sSelf->registerWaitForMetaObject(WaitForEvent::staticMetaObject); sSelf->registerWaitForMetaObject(WaitForNot::staticMetaObject); sSelf->registerWaitForMetaObject(WaitForOr::staticMetaObject); + sSelf->registerWaitForMetaObject(WaitForProperty::staticMetaObject); sSelf->registerWaitForMetaObject(WaitForSignal::staticMetaObject); sSelf->registerWaitForMetaObject(WaitForWindow::staticMetaObject); } Modified: trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2011-05-08 15:16:11 UTC (rev 304) +++ trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2011-05-16 16:39:27 UTC (rev 305) @@ -38,6 +38,7 @@ WaitForEvent WaitForNot WaitForOr + WaitForProperty WaitForSignal WaitForWindow ) @@ -61,6 +62,7 @@ WaitForEvent WaitForNot WaitForOr + WaitForProperty WaitForSignal WaitForWindow ) Added: trunk/ktutorial/ktutorial-library/tests/WaitForPropertyTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/WaitForPropertyTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/WaitForPropertyTest.cpp 2011-05-16 16:39:27 UTC (rev 305) @@ -0,0 +1,273 @@ +/*************************************************************************** + * 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> + +#define protected public +#define private public +#include "WaitForProperty.h" +#undef private +#undef protected + +class WaitForPropertyTest: public QObject { +Q_OBJECT +Q_PROPERTY(QString stringProperty READ stringProperty + NOTIFY stringPropertyChanged) +Q_PROPERTY(int intProperty READ intProperty) + +public: + + QString stringProperty() const { + return mStringProperty; + } + + int intProperty() const { + return mIntProperty; + } + +signals: + + void stringPropertyChanged(); + +protected: + + void connectNotify(const char* signal) { + if (QLatin1String(signal) == SIGNAL(stringPropertyChanged())) { + mStringPropertyChangedConnectionCount++; + } + } + +private: + + QString mStringProperty; + int mStringPropertyChangedConnectionCount; + + int mIntProperty; + +private slots: + + void init() { + mStringProperty = ""; + mStringPropertyChangedConnectionCount = 0; + + mIntProperty = 0; + } + + void testConstructor(); + void testConstructorWithPropertyWithoutNotifySignal(); + void testConstructorWithNullObject(); + void testConstructorWithUnknownPropertyName(); + void testConstructorDefault(); + void testConstructorDefaultWithPropertyWithoutNotifySignal(); + void testConstructorDefaultWithNullObject(); + void testConstructorDefaultWithUnknownPropertyName(); + + void testConditionMet(); + void testConditionMetWhenStepWasNotActive(); + void testConditionMetWithPropertyWithoutNotifySignal(); + void testConditionMetWithValueTypeDifferentFromPropertyType(); + + void testPropertyChangeToExpectedValue(); + void testPropertyChangeToExpectedValueWhenNotActive(); + +}; + +void WaitForPropertyTest::testConstructor() { + WaitForProperty waitForProperty(this, "stringProperty", "Expected value"); + + QVERIFY(!waitForProperty.isActive()); + QVERIFY(!waitForProperty.conditionMet()); + QCOMPARE(mStringPropertyChangedConnectionCount, 1); +} + +void WaitForPropertyTest::testConstructorWithPropertyWithoutNotifySignal() { + WaitForProperty waitForProperty(this, "intProperty", 42); + + QVERIFY(!waitForProperty.isActive()); + QVERIFY(!waitForProperty.conditionMet()); +} + +void WaitForPropertyTest::testConstructorWithNullObject() { + WaitForProperty waitForProperty(0, "stringProperty", "Expected value"); + + QVERIFY(!waitForProperty.isActive()); + QVERIFY(!waitForProperty.conditionMet()); +} + +void WaitForPropertyTest::testConstructorWithUnknownPropertyName() { + WaitForProperty waitForProperty(this, "unknownProperty", "Expected value"); + + QVERIFY(!waitForProperty.isActive()); + QVERIFY(!waitForProperty.conditionMet()); +} + +void WaitForPropertyTest::testConstructorDefault() { + WaitForProperty waitForProperty; + waitForProperty.setProperty(this, "stringProperty", "Expected value"); + + QVERIFY(!waitForProperty.isActive()); + QVERIFY(!waitForProperty.conditionMet()); + QCOMPARE(mStringPropertyChangedConnectionCount, 1); +} + +void WaitForPropertyTest:: + testConstructorDefaultWithPropertyWithoutNotifySignal() { + WaitForProperty waitForProperty; + waitForProperty.setProperty(this, "intProperty", 42); + + QVERIFY(!waitForProperty.isActive()); + QVERIFY(!waitForProperty.conditionMet()); +} + +void WaitForPropertyTest::testConstructorDefaultWithNullObject() { + WaitForProperty waitForProperty; + waitForProperty.setProperty(0, "stringProperty", "Expected value"); + + QVERIFY(!waitForProperty.isActive()); + QVERIFY(!waitForProperty.conditionMet()); +} + +void WaitForPropertyTest::testConstructorDefaultWithUnknownPropertyName() { + WaitForProperty waitForProperty; + waitForProperty.setProperty(this, "unknownProperty", "Expected value"); + + QVERIFY(!waitForProperty.isActive()); + QVERIFY(!waitForProperty.conditionMet()); +} + +void WaitForPropertyTest::testConditionMet() { + WaitForProperty waitForProperty(this, "stringProperty", "Expected value"); + + waitForProperty.setActive(true); + + QVERIFY(!waitForProperty.conditionMet()); + + mStringProperty = "Another value"; + emit stringPropertyChanged(); + + QVERIFY(!waitForProperty.conditionMet()); + + mStringProperty = "Expected value"; + emit stringPropertyChanged(); + + QVERIFY(waitForProperty.conditionMet()); +} + +void WaitForPropertyTest::testConditionMetWhenStepWasNotActive() { + WaitForProperty waitForProperty(this, "stringProperty", "Expected value"); + + mStringProperty = "Another value"; + emit stringPropertyChanged(); + + waitForProperty.setActive(true); + + QVERIFY(!waitForProperty.conditionMet()); + + waitForProperty.setActive(false); + + mStringProperty = "Expected value"; + emit stringPropertyChanged(); + + waitForProperty.setActive(true); + + QVERIFY(waitForProperty.conditionMet()); +} + +void WaitForPropertyTest::testConditionMetWithPropertyWithoutNotifySignal() { + WaitForProperty waitForProperty(this, "intProperty", 42); + + waitForProperty.setActive(true); + + QVERIFY(!waitForProperty.conditionMet()); + + mIntProperty = 4; + + QVERIFY(!waitForProperty.conditionMet()); + + mIntProperty = 42; + + QVERIFY(waitForProperty.conditionMet()); +} + +void WaitForPropertyTest:: + testConditionMetWithValueTypeDifferentFromPropertyType() { + WaitForProperty waitForProperty(this, "intProperty", "42"); + + waitForProperty.setActive(true); + + QVERIFY(!waitForProperty.conditionMet()); + + mIntProperty = 4; + + QVERIFY(!waitForProperty.conditionMet()); + + mIntProperty = 42; + + QVERIFY(waitForProperty.conditionMet()); +} + +//WaitFor* must be declared as a metatype to be used in qvariant_cast +Q_DECLARE_METATYPE(WaitFor*); + +void WaitForPropertyTest::testPropertyChangeToExpectedValue() { + WaitForProperty waitForProperty(this, "stringProperty", "Expected value"); + waitForProperty.setActive(true); + + //WaitFor* must be registered in order to be used with QSignalSpy + int waitForStarType = qRegisterMetaType<WaitFor*>("WaitFor*"); + QSignalSpy waitEndedSpy(&waitForProperty, SIGNAL(waitEnded(WaitFor*))); + + mStringProperty = "Another value"; + emit stringPropertyChanged(); + + QVERIFY(!waitForProperty.conditionMet()); + QCOMPARE(waitEndedSpy.count(), 0); + + mStringProperty = "Expected value"; + emit stringPropertyChanged(); + + QVERIFY(waitForProperty.conditionMet()); + QCOMPARE(waitEndedSpy.count(), 1); + QVariant argument = waitEndedSpy.at(0).at(0); + QCOMPARE(argument.userType(), waitForStarType); + QCOMPARE(qvariant_cast<WaitFor*>(argument), &waitForProperty); +} + +void WaitForPropertyTest::testPropertyChangeToExpectedValueWhenNotActive() { + WaitForProperty waitForProperty(this, "stringProperty", "Expected value"); + waitForProperty.setActive(false); + + qRegisterMetaType<WaitFor*>("WaitFor*"); + QSignalSpy waitEndedSpy(&waitForProperty, SIGNAL(waitEnded(WaitFor*))); + + mStringProperty = "Another value"; + emit stringPropertyChanged(); + + QVERIFY(!waitForProperty.conditionMet()); + QCOMPARE(waitEndedSpy.count(), 0); + + mStringProperty = "Expected value"; + emit stringPropertyChanged(); + + QVERIFY(waitForProperty.conditionMet()); + QCOMPARE(waitEndedSpy.count(), 0); +} + +QTEST_MAIN(WaitForPropertyTest) + +#include "WaitForPropertyTest.moc" Property changes on: trunk/ktutorial/ktutorial-library/tests/WaitForPropertyTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingModuleTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingModuleTest.cpp 2011-05-08 15:16:11 UTC (rev 304) +++ trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingModuleTest.cpp 2011-05-16 16:39:27 UTC (rev 305) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2009-2010 by Daniel Calviño Sánchez * + * Copyright (C) 2009-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -33,6 +33,7 @@ #include "../WaitForEvent.h" #include "../WaitForNot.h" #include "../WaitForOr.h" +#include "../WaitForProperty.h" #include "../WaitForSignal.h" #include "../WaitForWindow.h" @@ -169,6 +170,10 @@ type = metaObject(scriptingModule, "WaitForOr"); QCOMPARE(type.className(), WaitForOr::staticMetaObject.className()); + QVERIFY(containsMetaObject(scriptingModule, "WaitForProperty")); + type = metaObject(scriptingModule, "WaitForProperty"); + QCOMPARE(type.className(), WaitForProperty::staticMetaObject.className()); + QVERIFY(containsMetaObject(scriptingModule, "WaitForSignal")); type = metaObject(scriptingModule, "WaitForSignal"); QCOMPARE(type.className(), WaitForSignal::staticMetaObject.className()); Modified: trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingTest.cpp 2011-05-08 15:16:11 UTC (rev 304) +++ trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingTest.cpp 2011-05-16 16:39:27 UTC (rev 305) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2009-2010 by Daniel Calviño Sánchez * + * Copyright (C) 2009-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -42,6 +42,13 @@ //tutorials work as expected class ScriptingTest: public QObject { Q_OBJECT +Q_PROPERTY(int intProperty READ intProperty NOTIFY intPropertyChanged) +public: + + int intProperty() const { + return mIntProperty; + } + public slots: bool booleanValue() { @@ -62,6 +69,8 @@ void otherDummySignal(); void anotherDummySignal(); + void intPropertyChanged(); + private slots: void initTestCase(); @@ -83,6 +92,8 @@ bool mBooleanValue; + int mIntProperty; + int mDummySlotCallCount; KTemporaryFile* mTemporaryFile; @@ -98,6 +109,7 @@ void ScriptingTest::init() { mBooleanValue = false; + mIntProperty = 0; mDummySlotCallCount = 0; mTemporaryFile = new KTemporaryFile(); @@ -314,6 +326,8 @@ out << "tutorial.addStep(secondStep);\n"; out << "thirdStep = ktutorial.newStep(\"third\");\n"; out << "tutorial.addStep(thirdStep);\n"; + out << "fourthStep = ktutorial.newStep(\"fourth\");\n"; + out << "tutorial.addStep(fourthStep);\n"; out << "endStep = ktutorial.newStep(\"end\");\n"; out << "endStep.setText(\"The tutorial has ended.\");\n"; out << "tutorial.addStep(endStep);\n"; @@ -351,8 +365,14 @@ out << "waitForChildAddedEvent = ktutorial.newWaitFor(\"WaitForEvent\");\n"; out << "waitForChildAddedEvent.setEvent(testObject, \"ChildAdded\");\n"; - out << "thirdStep.addWaitFor(waitForChildAddedEvent, \"end\");\n"; + out << "thirdStep.addWaitFor(waitForChildAddedEvent, \"fourth\");\n"; + out << "waitForIntProperty = ktutorial.newWaitFor(\"WaitForProperty\");\n"; + out << "waitForIntProperty.setProperty(testObject, \"intProperty\", \ +\"42\");\n"; + + out << "fourthStep.addWaitFor(waitForIntProperty, \"end\");\n"; + out.flush(); ScriptedTutorial scriptedTutorial(mTemporaryFile->fileName()); @@ -397,6 +417,11 @@ QObject* childObject = new QObject(); childObject->setParent(this); + QCOMPARE(scriptedTutorial.mCurrentStep->id(), QString("fourth")); + + mIntProperty = 42; + emit intPropertyChanged(); + QCOMPARE(scriptedTutorial.mCurrentStep->id(), QString("end")); QCOMPARE(scriptedTutorial.mCurrentStep->text(), QString("The tutorial has ended.")); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2011-05-24 14:42:01
|
Revision: 310 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=310&view=rev Author: danxuliu Date: 2011-05-24 14:41:54 +0000 (Tue, 24 May 2011) Log Message: ----------- Add WaitForStepActivation class to wait for its step to be activated. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/scripting/ScriptingModule.cpp trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingModuleTest.cpp trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingTest.cpp Added Paths: ----------- trunk/ktutorial/ktutorial-library/src/WaitForStepActivation.cpp trunk/ktutorial/ktutorial-library/src/WaitForStepActivation.h trunk/ktutorial/ktutorial-library/tests/WaitForStepActivationTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2011-05-24 14:33:07 UTC (rev 309) +++ trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2011-05-24 14:41:54 UTC (rev 310) @@ -30,6 +30,7 @@ WaitForOr.cpp WaitForProperty.cpp WaitForSignal.cpp + WaitForStepActivation.cpp WaitForWindow.cpp ) @@ -61,6 +62,7 @@ WaitForOr.h WaitForProperty.h WaitForSignal.h + WaitForStepActivation.h WaitForWindow.h ) Added: trunk/ktutorial/ktutorial-library/src/WaitForStepActivation.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/WaitForStepActivation.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/WaitForStepActivation.cpp 2011-05-24 14:41:54 UTC (rev 310) @@ -0,0 +1,70 @@ +/*************************************************************************** + * 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 "WaitForStepActivation.h" + +#include <QEvent> + +#include <KDebug> + +#include "Tutorial.h" + +//public: + +WaitForStepActivation::WaitForStepActivation(): WaitFor(), + mDuringStepActivation(false) { +} + +WaitForStepActivation::WaitForStepActivation(const Tutorial* tutorial, + const Step* step): WaitFor(), + mDuringStepActivation(false) { + setStep(tutorial, step); +} + +void WaitForStepActivation::setStep(const Tutorial* tutorial, + const Step* step) { + if (!tutorial) { + kWarning() << "The tutorial that contains the step to wait for its" + << "activation is null!"; + return; + } + + if (!step) { + kWarning() << "The step to wait for its activation is null!"; + return; + } + + connect(tutorial, SIGNAL(stepActivated(Step*)), + this, SLOT(checkStepActivatedToEndTheWait(Step*))); +} + +bool WaitForStepActivation::conditionMet() const { + return mDuringStepActivation; +} + +//private slots: + +void WaitForStepActivation::checkStepActivatedToEndTheWait(Step* step) { + if (!isActive()) { + return; + } + + mDuringStepActivation = true; + emit waitEnded(this); + mDuringStepActivation = false; +} Property changes on: trunk/ktutorial/ktutorial-library/src/WaitForStepActivation.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/WaitForStepActivation.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/WaitForStepActivation.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/WaitForStepActivation.h 2011-05-24 14:41:54 UTC (rev 310) @@ -0,0 +1,119 @@ +/*************************************************************************** + * 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 WAITFORSTEPACTIVATION_H +#define WAITFORSTEPACTIVATION_H + +#include "ktutorial_export.h" + +#include "WaitFor.h" + +class Step; +class Tutorial; + +/** + * Waits for the activation of a step. + * When the step is activated, the wait ends. + * + * The purpose of WaitForStepActivations is to be combined with one or more + * WaitForProperties using a WaitForAnd. If the properties have the expected + * value when the step is activated, the associated slot is executed or the next + * step is requested (depending on the Step::addWaitFor() method used). It can + * be used to set "preconditions" for a step, and change to another step if + * some things that should have been done before that step were not done yet. + * + * Note that the condition is met only while the step is being activated. Once + * the step is active, the condition is no longer met (until the next time that + * the step is being activated again). Due to this, it only makes sense to use + * WaitForStepActivation with the same step that the WaitForStepActivation is + * going to be added to (if used to wait for the activation of another step, the + * WaitForStepActivation will always be inactive when that step is being + * activated). + * + * Despite waiting for the step activation, WaitForStepActivation can be safely + * created and added to a step in its setup, and several WaitForStepActivation + * can be added to the same step without problems. + * + * @see WaitForAnd + * @see WaitForProperty + */ +class KTUTORIAL_EXPORT WaitForStepActivation: public WaitFor { +Q_OBJECT +public: + + /** + * Creates a new WaitForStepActivation. + * This constructor is needed to dynamically create WaitForStepActivation + * objects in scripts using ScriptingModule::newWaitFor(const QString&). + * Method setStep(const Tutorial*, const Step*) must be called to finish + * setting up the object. For C++ tutorials, use + * WaitForStepActivation(const Tutorial*, const Step*) constructor instead + * of this one. + */ + Q_INVOKABLE WaitForStepActivation(); + + /** + * Creates a new WaitForStepActivation. + * + * @param tutorial The tutorial that contains the step. + * @param step The step to wait for its activation. + */ + WaitForStepActivation(const Tutorial* tutorial, const Step* step); + + /** + * Sets the step to wait for its activation. + * This method can be invoked from a script. + * + * In fact, you should only invoke this method from a script, and only once, + * to set up the object. For C++ tutorials, use + * WaitForStepActivation(const Tutorial*, const Step*) constructor when + * creating this WaitForStepActivation. + * + * @param tutorial The tutorial that contains the step. + * @param step The step to wait for its activation. + */ + Q_INVOKABLE void setStep(const Tutorial* tutorial, const Step* step); + + /** + * Returns true if the step is being activated. + * Note that true is only returned during the step activation, but not once + * it is already active. + * + * @return True if the step is being activated, false otherwise. + */ + virtual bool conditionMet() const; + +private: + + /** + * Whether the step is being activated or not. + */ + bool mDuringStepActivation; + +private slots: + + /** + * When the step is activated, this method notifies that the wait for the + * activation ended. + * The wait is only ended if this WaitForStepActivation is active. + */ + void checkStepActivatedToEndTheWait(Step* step); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/WaitForStepActivation.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/src/scripting/ScriptingModule.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/scripting/ScriptingModule.cpp 2011-05-24 14:33:07 UTC (rev 309) +++ trunk/ktutorial/ktutorial-library/src/scripting/ScriptingModule.cpp 2011-05-24 14:41:54 UTC (rev 310) @@ -29,6 +29,7 @@ #include "../WaitForOr.h" #include "../WaitForProperty.h" #include "../WaitForSignal.h" +#include "../WaitForStepActivation.h" #include "../WaitForWindow.h" namespace scripting { @@ -42,6 +43,8 @@ sSelf->registerWaitForMetaObject(WaitForOr::staticMetaObject); sSelf->registerWaitForMetaObject(WaitForProperty::staticMetaObject); sSelf->registerWaitForMetaObject(WaitForSignal::staticMetaObject); + sSelf->registerWaitForMetaObject( + WaitForStepActivation::staticMetaObject); sSelf->registerWaitForMetaObject(WaitForWindow::staticMetaObject); } Modified: trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2011-05-24 14:33:07 UTC (rev 309) +++ trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2011-05-24 14:41:54 UTC (rev 310) @@ -40,6 +40,7 @@ WaitForOr WaitForProperty WaitForSignal + WaitForStepActivation WaitForWindow ) @@ -64,5 +65,6 @@ WaitForOr WaitForProperty WaitForSignal + WaitForStepActivation WaitForWindow ) Added: trunk/ktutorial/ktutorial-library/tests/WaitForStepActivationTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/WaitForStepActivationTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/WaitForStepActivationTest.cpp 2011-05-24 14:41:54 UTC (rev 310) @@ -0,0 +1,179 @@ +/*************************************************************************** + * 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 "WaitForStepActivation.h" + +#include "Step.h" +#include "Tutorial.h" + +class WaitForStepActivationTest: public QObject { +Q_OBJECT + +public slots: + + void assertConditionMet(WaitFor* waitForToAssertItsCondition) { + QVERIFY(waitForToAssertItsCondition); + QVERIFY(waitForToAssertItsCondition->conditionMet()); + } + +private slots: + + void testConstructor(); + void testConstructorWithNullTutorial(); + void testConstructorWithNullStep(); + void testConstructorDefault(); + void testConstructorDefaultWithNullTutorial(); + void testConstructorDefaultWithNullStep(); + + void testStepActivation(); + void testStepActivationWhenWaitForIsInactive(); + +}; + +class InspectedTutorial: public Tutorial { +public: + + int mstepActivatedConnectionCount; + + InspectedTutorial(TutorialInformation* tutorialInformation): + Tutorial(tutorialInformation), + mstepActivatedConnectionCount(0) { + } + +protected: + + void connectNotify(const char* signal) { + if (QLatin1String(signal) == SIGNAL(stepActivated(Step*))) { + mstepActivatedConnectionCount++; + } + } + +}; + +void WaitForStepActivationTest::testConstructor() { + InspectedTutorial tutorial(0); + Step step("stepName"); + WaitForStepActivation waitForStepActivation(&tutorial, &step); + + QVERIFY(!waitForStepActivation.isActive()); + QVERIFY(!waitForStepActivation.conditionMet()); + QCOMPARE(tutorial.mstepActivatedConnectionCount, 1); +} + +void WaitForStepActivationTest::testConstructorWithNullTutorial() { + Step step("stepName"); + WaitForStepActivation waitForStepActivation(0, &step); + + QVERIFY(!waitForStepActivation.isActive()); + QVERIFY(!waitForStepActivation.conditionMet()); +} + +void WaitForStepActivationTest::testConstructorWithNullStep() { + InspectedTutorial tutorial(0); + WaitForStepActivation waitForStepActivation(&tutorial, 0); + + QVERIFY(!waitForStepActivation.isActive()); + QVERIFY(!waitForStepActivation.conditionMet()); + QCOMPARE(tutorial.mstepActivatedConnectionCount, 0); +} + +void WaitForStepActivationTest::testConstructorDefault() { + InspectedTutorial tutorial(0); + Step step("stepName"); + WaitForStepActivation waitForStepActivation; + waitForStepActivation.setStep(&tutorial, &step); + + QVERIFY(!waitForStepActivation.isActive()); + QVERIFY(!waitForStepActivation.conditionMet()); + QCOMPARE(tutorial.mstepActivatedConnectionCount, 1); +} + +void WaitForStepActivationTest::testConstructorDefaultWithNullTutorial() { + Step step("stepName"); + WaitForStepActivation waitForStepActivation; + waitForStepActivation.setStep(0, &step); + + QVERIFY(!waitForStepActivation.isActive()); + QVERIFY(!waitForStepActivation.conditionMet()); +} + +void WaitForStepActivationTest::testConstructorDefaultWithNullStep() { + InspectedTutorial tutorial(0); + WaitForStepActivation waitForStepActivation; + waitForStepActivation.setStep(&tutorial, 0); + + QVERIFY(!waitForStepActivation.isActive()); + QVERIFY(!waitForStepActivation.conditionMet()); + QCOMPARE(tutorial.mstepActivatedConnectionCount, 0); +} + +//WaitFor* must be declared as a metatype to be used in qvariant_cast +Q_DECLARE_METATYPE(WaitFor*); + +void WaitForStepActivationTest::testStepActivation() { + InspectedTutorial tutorial(0); + Step* startStep = new Step("start"); + tutorial.addStep(startStep); + + WaitForStepActivation* waitForStepActivation = + new WaitForStepActivation(&tutorial, startStep); + waitForStepActivation->setActive(true); + + QVERIFY(!waitForStepActivation->conditionMet()); + + //WaitFor* must be registered in order to be used with QSignalSpy + int waitForStarType = qRegisterMetaType<WaitFor*>("WaitFor*"); + QSignalSpy waitEndedSpy(waitForStepActivation, SIGNAL(waitEnded(WaitFor*))); + + connect(waitForStepActivation, SIGNAL(waitEnded(WaitFor*)), + this, SLOT(assertConditionMet(WaitFor*))); + + tutorial.start(); + + QVERIFY(!waitForStepActivation->conditionMet()); + QCOMPARE(waitEndedSpy.count(), 1); + QVariant argument = waitEndedSpy.at(0).at(0); + QCOMPARE(argument.userType(), waitForStarType); + QCOMPARE(qvariant_cast<WaitFor*>(argument), waitForStepActivation); +} + +//Should not happen, as WaitForStepActivation is meant to be used only with the +//step that contains the WaitFor... but just in case ;) +void WaitForStepActivationTest::testStepActivationWhenWaitForIsInactive() { + InspectedTutorial tutorial(0); + Step* startStep = new Step("start"); + tutorial.addStep(startStep); + + WaitForStepActivation* waitForStepActivation = + new WaitForStepActivation(&tutorial, startStep); + + //WaitFor* must be registered in order to be used with QSignalSpy + qRegisterMetaType<WaitFor*>("WaitFor*"); + QSignalSpy waitEndedSpy(waitForStepActivation, SIGNAL(waitEnded(WaitFor*))); + + tutorial.start(); + + QVERIFY(!waitForStepActivation->conditionMet()); + QCOMPARE(waitEndedSpy.count(), 0); +} + +QTEST_MAIN(WaitForStepActivationTest) + +#include "WaitForStepActivationTest.moc" Property changes on: trunk/ktutorial/ktutorial-library/tests/WaitForStepActivationTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingModuleTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingModuleTest.cpp 2011-05-24 14:33:07 UTC (rev 309) +++ trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingModuleTest.cpp 2011-05-24 14:41:54 UTC (rev 310) @@ -35,6 +35,7 @@ #include "../WaitForOr.h" #include "../WaitForProperty.h" #include "../WaitForSignal.h" +#include "../WaitForStepActivation.h" #include "../WaitForWindow.h" class MockWaitForDefaultNamespace: public WaitFor { @@ -178,6 +179,11 @@ type = metaObject(scriptingModule, "WaitForSignal"); QCOMPARE(type.className(), WaitForSignal::staticMetaObject.className()); + QVERIFY(containsMetaObject(scriptingModule, "WaitForStepActivation")); + type = metaObject(scriptingModule, "WaitForStepActivation"); + QCOMPARE(type.className(), + WaitForStepActivation::staticMetaObject.className()); + QVERIFY(containsMetaObject(scriptingModule, "WaitForWindow")); type = metaObject(scriptingModule, "WaitForWindow"); QCOMPARE(type.className(), WaitForWindow::staticMetaObject.className()); Modified: trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingTest.cpp 2011-05-24 14:33:07 UTC (rev 309) +++ trunk/ktutorial/ktutorial-library/tests/scripting/ScriptingTest.cpp 2011-05-24 14:41:54 UTC (rev 310) @@ -328,6 +328,8 @@ out << "tutorial.addStep(thirdStep);\n"; out << "fourthStep = ktutorial.newStep(\"fourth\");\n"; out << "tutorial.addStep(fourthStep);\n"; + out << "fifthStep = ktutorial.newStep(\"fifth\");\n"; + out << "tutorial.addStep(fifthStep);\n"; out << "endStep = ktutorial.newStep(\"end\");\n"; out << "endStep.setText(\"The tutorial has ended.\");\n"; out << "tutorial.addStep(endStep);\n"; @@ -371,8 +373,27 @@ out << "waitForIntProperty.setProperty(testObject, \"intProperty\", \ \"42\");\n"; - out << "fourthStep.addWaitFor(waitForIntProperty, \"end\");\n"; + out << "fourthStep.addWaitFor(waitForIntProperty, \"fifth\");\n"; + out << "function fifthStepSetup(step) {\n"; + out << " waitForStepActivation = ktutorial.newWaitFor(\ +\"WaitForStepActivation\");\n"; + out << " waitForStepActivation.setStep(tutorial, step);\n"; + + out << " waitForIntProperty = ktutorial.newWaitFor(\ +\"WaitForProperty\");\n"; + out << " waitForIntProperty.setProperty(testObject, \"intProperty\", \ +\"42\");\n"; + + out << " waitForAnd = ktutorial.newWaitFor(\"WaitForAnd\");\n"; + out << " waitForAnd.add(waitForStepActivation);\n"; + out << " waitForAnd.add(waitForIntProperty);\n"; + out << " step.addWaitFor(waitForAnd, \"end\");\n"; + out << "}\n"; + + out << "connect(fifthStep, \"setup(QObject*)\",\n" + << " this, \"fifthStepSetup(QObject*)\");\n"; + out.flush(); ScriptedTutorial scriptedTutorial(mTemporaryFile->fileName()); @@ -422,6 +443,8 @@ mIntProperty = 42; emit intPropertyChanged(); + //Fifth step changes to end step when activated + QCOMPARE(scriptedTutorial.mCurrentStep->id(), QString("end")); QCOMPARE(scriptedTutorial.mCurrentStep->text(), QString("The tutorial has ended.")); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2011-06-08 12:46:14
|
Revision: 316 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=316&view=rev Author: danxuliu Date: 2011-06-08 12:46:04 +0000 (Wed, 08 Jun 2011) Log Message: ----------- Add support for testing a tutorial from a specific step instead of just from the start. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.cpp trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.h trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportAdaptorTest.cpp trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp 2011-05-28 04:00:15 UTC (rev 315) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.cpp 2011-06-08 12:46:04 UTC (rev 316) @@ -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 * @@ -103,7 +103,8 @@ mEventSpy = 0; } -void EditorSupport::testScriptedTutorial(const QString& filename) { +void EditorSupport::testScriptedTutorial(const QString& filename, + const QString& stepId) { ScriptedTutorial* scriptedTutorial = new ScriptedTutorial(filename); if (!scriptedTutorial->isValid()) { @@ -119,6 +120,10 @@ emit started(scriptedTutorial); scriptedTutorial->start(); + + if (!stepId.isEmpty()) { + scriptedTutorial->nextStep(stepId); + } } //private: Modified: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h 2011-05-28 04:00:15 UTC (rev 315) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupport.h 2011-06-08 12:46:04 UTC (rev 316) @@ -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 * @@ -43,7 +43,8 @@ * disable the EventSpy (as notifying all the events sent by an application * through D-Bus is very costly, the EventSpy should be enabled only when * needed), highlight and stop the highlighting of widgets, and test a scripted - * tutorial (starting the tutorial stored in the given filename). + * tutorial (starting the tutorial stored in the given filename and, optionally, + * from the given step id). * * The object register assigns an id to QObjects to be identified by the remote * KTutorial editor. Using that id, KTutorial editor can request further @@ -121,10 +122,14 @@ /** * Starts the scripted tutorial stored in the given filename. + * If a step id is given, the tutorial is changed to that step after + * starting. * * @param filename The name of the file to read the scripted tutorial from. + * @param stepId The id of the step to change to, if any. */ - void testScriptedTutorial(const QString& filename); + void testScriptedTutorial(const QString& filename, + const QString& stepId = QString()); Q_SIGNALS: Modified: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.cpp 2011-05-28 04:00:15 UTC (rev 315) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.cpp 2011-06-08 12:46:04 UTC (rev 316) @@ -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 * @@ -57,8 +57,9 @@ mEditorSupport->disableEventSpy(); } -void EditorSupportAdaptor::testScriptedTutorial(const QString& filename) { - mEditorSupport->testScriptedTutorial(filename); +void EditorSupportAdaptor::testScriptedTutorial(const QString& filename, + const QString& stepId) { + mEditorSupport->testScriptedTutorial(filename, stepId); } } Modified: trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.h 2011-05-28 04:00:15 UTC (rev 315) +++ trunk/ktutorial/ktutorial-library/src/editorsupport/EditorSupportAdaptor.h 2011-06-08 12:46:04 UTC (rev 316) @@ -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 * @@ -89,10 +89,14 @@ /** * Starts the scripted tutorial stored in the given filename. + * If a step id is given, the tutorial is changed to that step after + * starting. * * @param filename The name of the file to read the scripted tutorial from. + * @param stepId The id of the step to change to, if any. */ - void testScriptedTutorial(const QString& filename); + void testScriptedTutorial(const QString& filename, + const QString& stepId = QString()); private: Modified: trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportAdaptorTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportAdaptorTest.cpp 2011-05-28 04:00:15 UTC (rev 315) +++ trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportAdaptorTest.cpp 2011-06-08 12:46:04 UTC (rev 316) @@ -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,13 +25,23 @@ #include "EditorSupportAdaptor.h" +#define protected public +#define private public +#include "../Tutorial.h" +#undef private +#undef protected + #include "EditorSupport.h" #include "EventSpy.h" #include "ObjectRegister.h" +#include "../Step.h" #include "../extendedinformation/WidgetHighlighter.h" using extendedinformation::WidgetHighlighter; +//Tutorial* must be declared as a metatype to be used in qvariant_cast +Q_DECLARE_METATYPE(Tutorial*); + namespace editorsupport { class EditorSupportAdaptorTest: public QObject { @@ -54,6 +64,7 @@ void testDisableEventSpy(); void testTestScriptedTutorial(); + void testTestScriptedTutorialWithStepId(); }; @@ -158,8 +169,38 @@ QCOMPARE(startedSpy.count(), 1); } +void EditorSupportAdaptorTest::testTestScriptedTutorialWithStepId() { + KTemporaryFile temporaryFile; + temporaryFile.setSuffix(".js"); + temporaryFile.open(); + + QTextStream out(&temporaryFile); + out << "tutorial.tutorialInformationAsObject().setName(\ +\"Test tutorial\");\n"; + out << "tutorial.addStep(ktutorial.newStep(\"start\"));\n"; + out << "tutorial.addStep(ktutorial.newStep(\"second step\"));\n"; + out << "tutorial.addStep(ktutorial.newStep(\"third step\"));\n"; + out.flush(); + + EditorSupport editorSupport; + EditorSupportAdaptor* adaptor = new EditorSupportAdaptor(&editorSupport); + + //Tutorial* must be registered in order to be used with QSignalSpy + qRegisterMetaType<Tutorial*>("Tutorial*"); + QSignalSpy startedSpy(&editorSupport, SIGNAL(started(Tutorial*))); + + adaptor->testScriptedTutorial(temporaryFile.fileName(), "third step"); + + QCOMPARE(startedSpy.count(), 1); + QVariant argument = startedSpy.at(0).at(0); + + Tutorial* tutorial = qvariant_cast<Tutorial*>(argument); + QVERIFY(tutorial->mCurrentStep); + QCOMPARE(tutorial->mCurrentStep->id(), QString("third step")); } +} + QTEST_MAIN(editorsupport::EditorSupportAdaptorTest) #include "EditorSupportAdaptorTest.moc" Modified: trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp 2011-05-28 04:00:15 UTC (rev 315) +++ trunk/ktutorial/ktutorial-library/tests/editorsupport/EditorSupportTest.cpp 2011-06-08 12:46:04 UTC (rev 316) @@ -26,6 +26,7 @@ #define protected public #define private public #include "EditorSupport.h" +#include "../Tutorial.h" #undef private #undef protected @@ -33,7 +34,7 @@ #include "EventSpy.h" #include "ObjectRegister.h" #include "ObjectRegisterAdaptor.h" -#include "../Tutorial.h" +#include "../Step.h" #include "../TutorialInformation.h" #include "../extendedinformation/WidgetHighlighter.h" @@ -87,6 +88,7 @@ void testDisableEventSpy(); void testTestScriptedTutorial(); + void testTestScriptedTutorialWithStepId(); void testTestScriptedTutorialWithInvalidTutorial(); void testObjectRegisterWhenEventSpyNotifiesEventBeforeEndingConstructor(); @@ -267,7 +269,8 @@ temporaryFile.fileName()); QCOMPARE(tutorial->tutorialInformation()->name(), QString("Test tutorial")); - //Ensuring that the tutorial was really started is too cumbersome + QVERIFY(tutorial->mCurrentStep); + QCOMPARE(tutorial->mCurrentStep->id(), QString("start")); QSignalSpy destroyedSpy(tutorial, SIGNAL(destroyed())); @@ -287,6 +290,58 @@ QCOMPARE(destroyedSpy.count(), 1); } +void EditorSupportTest::testTestScriptedTutorialWithStepId() { + KTemporaryFile temporaryFile; + temporaryFile.setSuffix(".js"); + temporaryFile.open(); + + QTextStream out(&temporaryFile); + out << "tutorial.tutorialInformationAsObject().setName(\ +\"Test tutorial\");\n"; + out << "tutorial.addStep(ktutorial.newStep(\"start\"));\n"; + out << "tutorial.addStep(ktutorial.newStep(\"second step\"));\n"; + out << "tutorial.addStep(ktutorial.newStep(\"third step\"));\n"; + out.flush(); + + EditorSupport editorSupport; + + //Tutorial* must be registered in order to be used with QSignalSpy + int tutorialStarType = qRegisterMetaType<Tutorial*>("Tutorial*"); + QSignalSpy startedSpy(&editorSupport, SIGNAL(started(Tutorial*))); + + editorSupport.testScriptedTutorial(temporaryFile.fileName(), "third step"); + + QCOMPARE(startedSpy.count(), 1); + QVariant argument = startedSpy.at(0).at(0); + QCOMPARE(argument.userType(), tutorialStarType); + + Tutorial* tutorial = qvariant_cast<Tutorial*>(argument); + QVERIFY(tutorial->tutorialInformation()); + QCOMPARE(tutorial->tutorialInformation()->id(), + temporaryFile.fileName()); + QCOMPARE(tutorial->tutorialInformation()->name(), QString("Test tutorial")); + + QVERIFY(tutorial->mCurrentStep); + QCOMPARE(tutorial->mCurrentStep->id(), QString("third step")); + + QSignalSpy destroyedSpy(tutorial, SIGNAL(destroyed())); + + //Process deleteLater() + QCoreApplication::sendPostedEvents(tutorial, QEvent::DeferredDelete); + + //Ensure that the tutorial is not deleted before explicitly calling finish + //(for example, if the test tutorial written in the text stream does not + //have a start step) + QCOMPARE(destroyedSpy.count(), 0); + + tutorial->finish(); + + //Process deleteLater() + QCoreApplication::sendPostedEvents(tutorial, QEvent::DeferredDelete); + + QCOMPARE(destroyedSpy.count(), 1); +} + void EditorSupportTest::testTestScriptedTutorialWithInvalidTutorial() { KTemporaryFile temporaryFile; temporaryFile.setSuffix(".js"); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-06-27 16:49:51
|
Revision: 348 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=348&view=rev Author: danxuliu Date: 2012-06-27 16:49:40 +0000 (Wed, 27 Jun 2012) Log Message: ----------- Fix stopping the highlighting of a widget when a context menu was just shown for the link to another widget, even if the action to highlight the other widget was not activated. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp 2012-06-27 11:35:47 UTC (rev 347) +++ trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.cpp 2012-06-27 16:49:40 UTC (rev 348) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2012 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -34,8 +34,8 @@ StepTextWidget::StepTextWidget(QWidget* parent /*= 0*/): KTextEdit(parent), - mCurrentWidget(0), - mCurrentWidgetIsBeingHighlighted(false) { + mLastReferencedWidget(0), + mWidgetBeingHighlighted(0) { setReadOnly(true); setFrameShape(QFrame::NoFrame); setFrameShadow(QFrame::Plain); @@ -61,13 +61,13 @@ } StepTextWidget::~StepTextWidget() { - if (mCurrentWidgetIsBeingHighlighted) { + if (mWidgetBeingHighlighted) { stopHighlightingCurrentWidget(); } } bool StepTextWidget::eventFilter(QObject* watched, QEvent* event) { - if (watched != mCurrentWidget) { + if (watched != mWidgetBeingHighlighted) { return false; } @@ -107,16 +107,10 @@ QMenu* menu = new QMenu(this); - if (mCurrentWidgetIsBeingHighlighted && - mCurrentWidget != widgetForAnchor(anchor)) { - stopHighlightingCurrentWidget(); - } - - if (!mCurrentWidgetIsBeingHighlighted) { + mLastReferencedWidget = widgetForAnchor(anchor); + if (mLastReferencedWidget != mWidgetBeingHighlighted) { menu->addAction(i18nc("@item:inmenu", "Highlight"), - this, SLOT(highlightCurrentWidget())); - - mCurrentWidget = widgetForAnchor(anchor); + this, SLOT(highlightLastReferencedWidget())); } else { menu->addAction(i18nc("@item:inmenu", "Stop highlighting"), this, SLOT(stopHighlightingCurrentWidget())); @@ -144,15 +138,9 @@ return; } - if (mCurrentWidgetIsBeingHighlighted && - mCurrentWidget != widgetForAnchor(anchor)) { - stopHighlightingCurrentWidget(); - } - - if (!mCurrentWidgetIsBeingHighlighted) { - mCurrentWidget = widgetForAnchor(anchor); - - highlightCurrentWidget(); + mLastReferencedWidget = widgetForAnchor(anchor); + if (mLastReferencedWidget != mWidgetBeingHighlighted) { + highlightLastReferencedWidget(); } else { stopHighlightingCurrentWidget(); } @@ -186,36 +174,37 @@ void StepTextWidget::updateText() { updateGeometry(); - if (mCurrentWidgetIsBeingHighlighted) { + if (mWidgetBeingHighlighted) { stopHighlightingCurrentWidget(); } } -void StepTextWidget::highlightCurrentWidget() { - if (!mCurrentWidget) { +void StepTextWidget::highlightLastReferencedWidget() { + if (!mLastReferencedWidget) { kWarning() << "The widget to highlight was not found!"; return; } - WidgetHighlighterManager::self()->highlight(mCurrentWidget); + if (mWidgetBeingHighlighted) { + stopHighlightingCurrentWidget(); + } - mCurrentWidget->installEventFilter(this); + WidgetHighlighterManager::self()->highlight(mLastReferencedWidget); - mCurrentWidgetIsBeingHighlighted = true; + mLastReferencedWidget->installEventFilter(this); + mWidgetBeingHighlighted = mLastReferencedWidget; } void StepTextWidget::stopHighlightingCurrentWidget() { - if (!mCurrentWidget) { + if (!mWidgetBeingHighlighted) { kWarning() << "The widget to stop highlighting was not found!"; return; } - WidgetHighlighterManager::self()->stopHighlighting(mCurrentWidget); + WidgetHighlighterManager::self()->stopHighlighting(mWidgetBeingHighlighted); - mCurrentWidget->removeEventFilter(this); - mCurrentWidget = 0; - - mCurrentWidgetIsBeingHighlighted = false; + mWidgetBeingHighlighted->removeEventFilter(this); + mWidgetBeingHighlighted = 0; } } Modified: trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h 2012-06-27 11:35:47 UTC (rev 347) +++ trunk/ktutorial/ktutorial-library/src/view/StepTextWidget.h 2012-06-27 16:49:40 UTC (rev 348) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2012 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -109,8 +109,7 @@ * "widget:" anchor. * The context menu will contain a "Stop highlighting" or a "Highlight" item * depending on whether the widget is the one currently being highlighted or - * not. If the widget currently being highlighted is another one, it is - * stopped. + * not. * * @param event The context menu event. */ @@ -126,7 +125,6 @@ /** * When the mouse is pressed on a "widget:" anchor, it is highlighted or * stopped being highlighted. - * If the widget currently being highlighted is another one, it is stopped. * * @param event The mouse press event. */ @@ -138,12 +136,12 @@ * The last widget referenced by a link activated or selected in this * StepTextWidget. */ - QPointer<QWidget> mCurrentWidget; + QPointer<QWidget> mLastReferencedWidget; /** - * Whether the current widget is being highlighted or not. + * The widget being highlighted. */ - bool mCurrentWidgetIsBeingHighlighted; + QPointer<QWidget> mWidgetBeingHighlighted; /** * Returns the size for the given text width. @@ -172,18 +170,18 @@ /** * Notifies the layout that the geometry of the widget has changed and stops - * the highlighting of the current widget, if any. + * the widget currently being highlighted, if any. */ void updateText(); /** - * Starts highlighting the current widget. + * Starts highlighting the last referenced widget. + * If there is any widget currently being highlighted, it is stopped. */ - void highlightCurrentWidget(); + void highlightLastReferencedWidget(); /** - * Stops highlighting the current widget. - * The current widget is cleared. + * Stops highlighting the widget currently being highlighted. */ void stopHighlightingCurrentWidget(); Modified: trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2012-06-27 11:35:47 UTC (rev 347) +++ trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2012-06-27 16:49:40 UTC (rev 348) @@ -51,6 +51,7 @@ void testHighlightWidgetUsingContextMenu(); void testStopHighlightingWidgetUsingContextMenu(); void testCancelContextMenu(); + void testCancelContextMenuOnOtherLink(); void testHighlightSeveralWidgets(); void testHighlightUnknownWidget(); @@ -232,11 +233,46 @@ showContextMenuCheckFirstOptionAndCancel(widget, position, i18n("Stop highlighting")); + //Ensure that just showing the context menu does not stop the highlighting + QVERIFY(!waitForHighlighterToStop(widgetToHighlight, 1000)); + //Ensure that the option text is the right one after cancelling the menu showContextMenuCheckFirstOptionAndCancel(widget, position, i18n("Stop highlighting")); } +void StepTextWidgetTest::testCancelContextMenuOnOtherLink() { + KXmlGuiWindow mainWindow; + KTutorial::self()->setup(&mainWindow); + QWidget* widgetToHighlight1 = new QWidget(&mainWindow); + widgetToHighlight1->setObjectName("widget1"); + QWidget* widgetToHighlight2 = new QWidget(&mainWindow); + widgetToHighlight2->setObjectName("widget2"); + + StepTextWidget widget; + widget.setText("The <a href=\"widget:widget1\">first widget</a> and the " +"<a href=\"widget:widget2\">second widget</a>"); + widget.show(); + + QTest::qWaitForWindowShown(&widget); + + QPoint position1 = centerOfText(widget, "first widget"); + QPoint position2 = centerOfText(widget, "second widget"); + + showContextMenuAndSelectFirstOption(widget, position1); + + showContextMenuCheckFirstOptionAndCancel(widget, position2, + i18n("Highlight")); + + //Ensure that just showing the context menu on the second link does not stop + //the highlighting of the first widget + QVERIFY(!waitForHighlighterToStop(widgetToHighlight1, 1000)); + + //Ensure that the option text is the right one after cancelling the menu + showContextMenuCheckFirstOptionAndCancel(widget, position2, + i18n("Highlight")); +} + void StepTextWidgetTest::testHighlightSeveralWidgets() { KXmlGuiWindow mainWindow; KTutorial::self()->setup(&mainWindow); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2012-07-05 18:19:12
|
Revision: 354 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=354&view=rev Author: danxuliu Date: 2012-07-05 18:19:04 +0000 (Thu, 05 Jul 2012) Log Message: ----------- Refactor to extract object finding logic from KTutorial to its own class, ObjectFinder. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/KTutorial.cpp trunk/ktutorial/ktutorial-library/src/KTutorial.h trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt Added Paths: ----------- trunk/ktutorial/ktutorial-library/src/ObjectFinder.cpp trunk/ktutorial/ktutorial-library/src/ObjectFinder.h trunk/ktutorial/ktutorial-library/tests/ObjectFinderTest.cpp Removed Paths: ------------- trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2012-07-05 12:38:07 UTC (rev 353) +++ trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2012-07-05 18:19:04 UTC (rev 354) @@ -17,6 +17,7 @@ set(ktutorial_LIB_SRCS KTutorial.cpp + ObjectFinder.cpp Option.cpp Step.cpp Tutorial.cpp @@ -49,6 +50,7 @@ set(ktutorial_LIB_HEADERS ktutorial_export.h KTutorial.h + ObjectFinder.h Option.h Step.h Tutorial.h Modified: trunk/ktutorial/ktutorial-library/src/KTutorial.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/KTutorial.cpp 2012-07-05 12:38:07 UTC (rev 353) +++ trunk/ktutorial/ktutorial-library/src/KTutorial.cpp 2012-07-05 18:19:04 UTC (rev 354) @@ -96,146 +96,6 @@ KTutorial* KTutorial::sSelf = new KTutorial(); -QList<QObject*> KTutorial::getBestMatches(const QString& name, - QList< QList<QObject*> > objectPaths) const { - if (name.isEmpty() || objectPaths.isEmpty()) { - return QList<QObject*>(); - } - - if (name.indexOf('/') == -1) { - QList< QList<QObject*> > filteredObjectPaths = - filterObjectPaths(name, objectPaths); - - QList<QObject*> filteredObjects; - foreach (QList<QObject*> filteredObjectPath, filteredObjectPaths) { - filteredObjects.append(filteredObjectPath.first()); - } - - return filteredObjects; - } - - QRegExp slashPattern("/+"); - QString ancestorName = name.left(name.indexOf(slashPattern)); - QString descendantName = name.mid(ancestorName.length() + - slashPattern.matchedLength()); - - return getBestMatches(descendantName, filterObjectPaths(ancestorName, - objectPaths)); -} - -QList< QList<QObject*> > KTutorial::filterObjectPaths(const QString& name, - const QList< QList<QObject*> >& objectPaths) const { - QList< QList<QObject*> > filteredPaths = filterDirectChildren(name, - objectPaths); - if (filteredPaths.size() > 0) { - return filteredPaths; - } - - filteredPaths = filterNestedChildrenWithUnnamedAncestors(name, objectPaths); - if (filteredPaths.size() > 0) { - return filteredPaths; - } - - return filterNestedChildren(name, objectPaths); -} - -QList< QList<QObject*> > KTutorial::filterDirectChildren(const QString& name, - const QList< QList<QObject*> >& objectPaths) const { - QList< QList<QObject*> > filteredPaths; - - foreach (QList<QObject*> objectPath, objectPaths) { - if (objectPath.size() >= 2 && objectPath[1]->objectName() == name) { - objectPath.removeAt(0); - filteredPaths.append(objectPath); - } - } - - return filteredPaths; -} - -QList< QList<QObject*> > KTutorial::filterNestedChildrenWithUnnamedAncestors( - const QString& name, - const QList< QList<QObject*> >& objectPaths) const { - QList< QList<QObject*> > candidatePaths; - - //No need to use std::numeric_limits, as there would never be a 100000 - //levels deep object. - int minimumNumberOfUnnamedAncestors = 100000; - foreach (QList<QObject*> objectPath, objectPaths) { - objectPath.removeAt(0); - - int unnamedAncestorCount = 0; - while (objectPath.size() > unnamedAncestorCount && - objectPath[unnamedAncestorCount]->objectName() == "") { - unnamedAncestorCount++; - } - - if (unnamedAncestorCount > 0 && - objectPath.size() > unnamedAncestorCount && - objectPath[unnamedAncestorCount]->objectName() == name) { - candidatePaths.append(objectPath); - - if (unnamedAncestorCount < minimumNumberOfUnnamedAncestors) { - minimumNumberOfUnnamedAncestors = unnamedAncestorCount; - } - } - } - - QList< QList<QObject*> > filteredPaths; - - foreach (QList<QObject*> candidatePath, candidatePaths) { - if (candidatePath[minimumNumberOfUnnamedAncestors]->objectName() == - name) { - for (int i=0; i<minimumNumberOfUnnamedAncestors; ++i) { - candidatePath.removeAt(0); - } - filteredPaths.append(candidatePath); - } - } - - return filteredPaths; -} - -QList< QList<QObject*> > KTutorial::filterNestedChildren(const QString& name, - const QList< QList<QObject*> >& objectPaths) const { - QList< QList<QObject*> > candidatePaths; - - //No need to use std::numeric_limits, as there would never be a 100000 - //levels deep object. - int minimumNumberOfAncestors = 100000; - foreach (QList<QObject*> objectPath, objectPaths) { - objectPath.removeAt(0); - - int ancestorCount = 0; - while (objectPath.size() > ancestorCount && - objectPath[ancestorCount]->objectName() != name) { - ancestorCount++; - } - - if (ancestorCount > 0 && objectPath.size() > ancestorCount && - objectPath[ancestorCount]->objectName() == name) { - candidatePaths.append(objectPath); - - if (ancestorCount < minimumNumberOfAncestors) { - minimumNumberOfAncestors = ancestorCount; - } - } - } - - QList< QList<QObject*> > filteredPaths; - - foreach (QList<QObject*> candidatePath, candidatePaths) { - if (candidatePath[minimumNumberOfAncestors]->objectName() == name) { - for (int i=0; i<minimumNumberOfAncestors; ++i) { - candidatePath.removeAt(0); - } - filteredPaths.append(candidatePath); - } - } - - return filteredPaths; -} - //private slots: void KTutorial::showTutorialManagerDialog() const { Modified: trunk/ktutorial/ktutorial-library/src/KTutorial.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/KTutorial.h 2012-07-05 12:38:07 UTC (rev 353) +++ trunk/ktutorial/ktutorial-library/src/KTutorial.h 2012-07-05 18:19:04 UTC (rev 354) @@ -23,6 +23,7 @@ #include "ktutorial_export.h" +#include "ObjectFinder.h" #include "TutorialManager.h" class KAction; @@ -171,18 +172,7 @@ */ template <typename T> T findObject(const QString& name) const { - QList<T> candidateObjects; - findObjects<T>(name, mParent, candidateObjects); - - if (candidateObjects.isEmpty()) { - return 0; - } - - if (candidateObjects.count() == 1) { - return candidateObjects.first(); - } - - return getBestMatch(name, candidateObjects); + return mObjectFinder->findObject<T>(name, mParent); } private: @@ -207,6 +197,8 @@ */ KAction* mTutorialsAction; + ObjectFinder* mObjectFinder; + /** * Creates a new KTutorial. * Private to avoid classes other than self to create instances. @@ -216,174 +208,9 @@ mTutorialmanager->setParent(this); mTutorialsAction = 0; mParent = 0; + mObjectFinder = new ObjectFinder(this); } - /** - * Adds to the foundObjects list the objects with the specified name that - * are descendant of the given ancestor, if any. - * The name of the objects can contain ancestor names separated by "/". Note - * that ancestors are not necessarily direct parents. - * - * @param name The name of the objects to find. - * @param ancestor The ancestor to look the objects in. - * @param foundObjects The list to add to the objects with the specified - * name to. - */ - template <typename T> - void findObjects(const QString& name, const QObject* ancestor, - QList<T>& foundObjects) const { - if (name.isEmpty() || ancestor == 0) { - return; - } - - if (name.indexOf('/') == -1) { - foundObjects.append(ancestor->findChildren<T>(name)); - return; - } - - QRegExp slashPattern("/+"); - QString ancestorName = name.left(name.indexOf(slashPattern)); - QString descendantName = name.mid(ancestorName.length() + - slashPattern.matchedLength()); - - QList<QObject*> namedAncestors = - ancestor->findChildren<QObject*>(ancestorName); - foreach (QObject* namedAncestor, namedAncestors) { - findObjects<T>(descendantName, namedAncestor, foundObjects); - } - } - - /** - * Resolves the ambiguity between several objects that match the given name. - * The ambiguity resolving rules are those specified in - * findObject(const QString&). - * - * @param name The name of the object to find. - * @param candidateObjects A list with objects that match the given name. - * @return The object that matches the best the given name. - */ - template <typename T> - T getBestMatch(const QString& name, QList<T> candidateObjects) const { - QList< QList<QObject*> > objectPaths = getObjectPaths(candidateObjects); - - QList<QObject*> bestMatches = getBestMatches(name, objectPaths); - - //Should not happen, but just in case - if (bestMatches.isEmpty()) { - return 0; - } - - return static_cast<T>(bestMatches[0]); - } - - /** - * Returns a list with the paths to the given objects. - * Each path is a list that contains the object and all its ancestors. The - * first object in the list is the more distant ancestor, and the last - * object is the object itself. - * - * @param objects The objects to get their paths. - * @return A list with the paths to the given objects. - */ - template <typename T> - QList< QList<QObject*> > getObjectPaths(const QList<T> objects) const { - QList< QList<QObject*> > objectPaths; - - foreach (T candidateObject, objects) { - QList<QObject*> objectPath; - - QObject* ancestor = candidateObject; - while (ancestor) { - objectPath.prepend(ancestor); - ancestor = ancestor->parent(); - } - - objectPaths.append(objectPath); - } - - return objectPaths; - } - - /** - * Gets the objects from the given object paths that match the best the - * given name. - * The name can contain ancestor names. The ambiguity resolving rules are - * applied recursively for each component of the name, so the object paths - * used to find each component are the ones filtered with the name of its - * ancestor. - * - * @param name The name of the object to get. - * @param objectPaths The paths to get the object from. - * @return The list of objects that match the best the given name. - */ - QList<QObject*> getBestMatches(const QString& name, - QList< QList<QObject*> > objectPaths) const; - - /** - * Returns the object paths that contain a descendant of the base object - * with the given name. - * If direct children are found, their path is used. If not, if descendants - * without named objects between them and the base object are found, their - * path is used. If not, the path of the shallower descendants is used. - * The name must be a single object name, without any ancestor name. - * The returned paths are trimmed to make the object with the given name the - * new base object of the path. - * - * @param name The name of the descendant to find. - * @param objectPaths The paths to search the object in. - * @return The filtered and trimmed object paths. - */ - QList< QList<QObject*> > filterObjectPaths(const QString& name, - const QList< QList<QObject*> >& objectPaths) const; - - /** - * Returns the object paths that contain a direct child from the base object - * with the given name. - * The name must be a single object name, without any ancestor name. - * The returned paths are trimmed to make the object with the given name the - * new base object of the path. - * - * @param name The name of the direct child to find. - * @param objectPaths The paths to search the object in. - * @return The filtered and trimmed object paths. - */ - QList< QList<QObject*> > filterDirectChildren(const QString& name, - const QList< QList<QObject*> >& objectPaths) const; - - /** - * Returns the object paths that contain a descendant from the base object - * with the given name. - * All the objects between the base object and the descendant with the given - * name must have no name. - * If there is more than one descendant with the given name, only the - * shallower ones are taken into account. - * The name must be a single object name, without any ancestor name. - * The returned paths are trimmed to make the object with the given name the - * new base object of the path. - * - * @param name The name of the descendant to find. - * @param objectPaths The paths to search the object in. - * @return The filtered and trimmed object paths. - */ - QList< QList<QObject*> > filterNestedChildrenWithUnnamedAncestors( - const QString& name, const QList< QList<QObject*> >& objectPaths) const; - - /** - * Returns the object paths that contain a descendant from the base object - * with the given name. - * If there is more than one descendant with the given name, only the - * shallower ones are taken into account. - * The name must be a single object name, without any ancestor name. - * The returned paths are trimmed to make the object with the given name the - * new base object of the path. - * - * @param name The name of the descendant to find. - * @param objectPaths The paths to search the object in. - * @return The filtered and trimmed object paths. - */ - QList< QList<QObject*> > filterNestedChildren(const QString& name, - const QList< QList<QObject*> >& objectPaths) const; - private slots: /** Added: trunk/ktutorial/ktutorial-library/src/ObjectFinder.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/ObjectFinder.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/ObjectFinder.cpp 2012-07-05 18:19:04 UTC (rev 354) @@ -0,0 +1,161 @@ +/*************************************************************************** + * Copyright (C) 2012 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 "ObjectFinder.h" + +//private: + +QList<QObject*> ObjectFinder::getBestMatches(const QString& name, + QList< QList<QObject*> > objectPaths) const { + if (name.isEmpty() || objectPaths.isEmpty()) { + return QList<QObject*>(); + } + + if (name.indexOf('/') == -1) { + QList< QList<QObject*> > filteredObjectPaths = + filterObjectPaths(name, objectPaths); + + QList<QObject*> filteredObjects; + foreach (QList<QObject*> filteredObjectPath, filteredObjectPaths) { + filteredObjects.append(filteredObjectPath.first()); + } + + return filteredObjects; + } + + QRegExp slashPattern("/+"); + QString ancestorName = name.left(name.indexOf(slashPattern)); + QString descendantName = name.mid(ancestorName.length() + + slashPattern.matchedLength()); + + return getBestMatches(descendantName, filterObjectPaths(ancestorName, + objectPaths)); +} + +QList< QList<QObject*> > ObjectFinder::filterObjectPaths(const QString& name, + const QList< QList<QObject*> >& objectPaths) const { + QList< QList<QObject*> > filteredPaths = filterDirectChildren(name, + objectPaths); + if (filteredPaths.size() > 0) { + return filteredPaths; + } + + filteredPaths = filterNestedChildrenWithUnnamedAncestors(name, objectPaths); + if (filteredPaths.size() > 0) { + return filteredPaths; + } + + return filterNestedChildren(name, objectPaths); +} + +QList< QList<QObject*> > ObjectFinder::filterDirectChildren(const QString& name, + const QList< QList<QObject*> >& objectPaths) const { + QList< QList<QObject*> > filteredPaths; + + foreach (QList<QObject*> objectPath, objectPaths) { + if (objectPath.size() >= 2 && objectPath[1]->objectName() == name) { + objectPath.removeAt(0); + filteredPaths.append(objectPath); + } + } + + return filteredPaths; +} + +QList< QList<QObject*> > ObjectFinder::filterNestedChildrenWithUnnamedAncestors( + const QString& name, + const QList< QList<QObject*> >& objectPaths) const { + QList< QList<QObject*> > candidatePaths; + + //No need to use std::numeric_limits, as there would never be a 100000 + //levels deep object. + int minimumNumberOfUnnamedAncestors = 100000; + foreach (QList<QObject*> objectPath, objectPaths) { + objectPath.removeAt(0); + + int unnamedAncestorCount = 0; + while (objectPath.size() > unnamedAncestorCount && + objectPath[unnamedAncestorCount]->objectName() == "") { + unnamedAncestorCount++; + } + + if (unnamedAncestorCount > 0 && + objectPath.size() > unnamedAncestorCount && + objectPath[unnamedAncestorCount]->objectName() == name) { + candidatePaths.append(objectPath); + + if (unnamedAncestorCount < minimumNumberOfUnnamedAncestors) { + minimumNumberOfUnnamedAncestors = unnamedAncestorCount; + } + } + } + + QList< QList<QObject*> > filteredPaths; + + foreach (QList<QObject*> candidatePath, candidatePaths) { + if (candidatePath[minimumNumberOfUnnamedAncestors]->objectName() == + name) { + for (int i=0; i<minimumNumberOfUnnamedAncestors; ++i) { + candidatePath.removeAt(0); + } + filteredPaths.append(candidatePath); + } + } + + return filteredPaths; +} + +QList< QList<QObject*> > ObjectFinder::filterNestedChildren(const QString& name, + const QList< QList<QObject*> >& objectPaths) const { + QList< QList<QObject*> > candidatePaths; + + //No need to use std::numeric_limits, as there would never be a 100000 + //levels deep object. + int minimumNumberOfAncestors = 100000; + foreach (QList<QObject*> objectPath, objectPaths) { + objectPath.removeAt(0); + + int ancestorCount = 0; + while (objectPath.size() > ancestorCount && + objectPath[ancestorCount]->objectName() != name) { + ancestorCount++; + } + + if (ancestorCount > 0 && objectPath.size() > ancestorCount && + objectPath[ancestorCount]->objectName() == name) { + candidatePaths.append(objectPath); + + if (ancestorCount < minimumNumberOfAncestors) { + minimumNumberOfAncestors = ancestorCount; + } + } + } + + QList< QList<QObject*> > filteredPaths; + + foreach (QList<QObject*> candidatePath, candidatePaths) { + if (candidatePath[minimumNumberOfAncestors]->objectName() == name) { + for (int i=0; i<minimumNumberOfAncestors; ++i) { + candidatePath.removeAt(0); + } + filteredPaths.append(candidatePath); + } + } + + return filteredPaths; +} Property changes on: trunk/ktutorial/ktutorial-library/src/ObjectFinder.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/ObjectFinder.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/ObjectFinder.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/ObjectFinder.h 2012-07-05 18:19:04 UTC (rev 354) @@ -0,0 +1,242 @@ +/*************************************************************************** + * Copyright (C) 2012 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 OBJECTFINDER_H +#define OBJECTFINDER_H + +#include <QObject> +#include <QRegExp> + +#include "ktutorial_export.h" + +/** + * Helper class to find objects. + * This class is not intended to be used directly. Instead, use + * KTutorial::findObject(const QString&). + */ +class KTUTORIAL_EXPORT ObjectFinder: public QObject { +Q_OBJECT +public: + + /** + * Creates a new ObjectFinder with the given parent. + * + * @param parent The parent object. + */ + ObjectFinder(QObject* parent): QObject(parent) { + } + + /** + * Returns the object with the specified name, if any. + * Objects are searched in the children of the given base object. + * + * For details please refer to KTutorial::findObject(const QString&) + * documentation. + * + * @param name The name of the object to find. + * @param baseObject The base object to search from. + * @return The object with the specified name, or null if there is none. + * @see KTutorial::findObject(const QString&) + */ + template <typename T> + T findObject(const QString& name, const QObject* baseObject) const { + QList<T> candidateObjects; + findObjects<T>(name, baseObject, candidateObjects); + + if (candidateObjects.isEmpty()) { + return 0; + } + + if (candidateObjects.count() == 1) { + return candidateObjects.first(); + } + + return getBestMatch(name, candidateObjects); + } + +private: + + /** + * Adds to the foundObjects list the objects with the specified name that + * are descendant of the given ancestor, if any. + * The name of the objects can contain ancestor names separated by "/". Note + * that ancestors are not necessarily direct parents. + * + * @param name The name of the objects to find. + * @param ancestor The ancestor to look the objects in. + * @param foundObjects The list to add to the objects with the specified + * name to. + */ + template <typename T> + void findObjects(const QString& name, const QObject* ancestor, + QList<T>& foundObjects) const { + if (name.isEmpty() || ancestor == 0) { + return; + } + + if (name.indexOf('/') == -1) { + foundObjects.append(ancestor->findChildren<T>(name)); + return; + } + + QRegExp slashPattern("/+"); + QString ancestorName = name.left(name.indexOf(slashPattern)); + QString descendantName = name.mid(ancestorName.length() + + slashPattern.matchedLength()); + + QList<QObject*> namedAncestors = + ancestor->findChildren<QObject*>(ancestorName); + foreach (QObject* namedAncestor, namedAncestors) { + findObjects<T>(descendantName, namedAncestor, foundObjects); + } + } + + /** + * Resolves the ambiguity between several objects that match the given name. + * The ambiguity resolving rules are those specified in + * findObject(const QString&). + * + * @param name The name of the object to find. + * @param candidateObjects A list with objects that match the given name. + * @return The object that matches the best the given name. + */ + template <typename T> + T getBestMatch(const QString& name, QList<T> candidateObjects) const { + QList< QList<QObject*> > objectPaths = getObjectPaths(candidateObjects); + + QList<QObject*> bestMatches = getBestMatches(name, objectPaths); + + //Should not happen, but just in case + if (bestMatches.isEmpty()) { + return 0; + } + + return static_cast<T>(bestMatches[0]); + } + + /** + * Returns a list with the paths to the given objects. + * Each path is a list that contains the object and all its ancestors. The + * first object in the list is the more distant ancestor, and the last + * object is the object itself. + * + * @param objects The objects to get their paths. + * @return A list with the paths to the given objects. + */ + template <typename T> + QList< QList<QObject*> > getObjectPaths(const QList<T> objects) const { + QList< QList<QObject*> > objectPaths; + + foreach (T candidateObject, objects) { + QList<QObject*> objectPath; + + QObject* ancestor = candidateObject; + while (ancestor) { + objectPath.prepend(ancestor); + ancestor = ancestor->parent(); + } + + objectPaths.append(objectPath); + } + + return objectPaths; + } + + /** + * Gets the objects from the given object paths that match the best the + * given name. + * The name can contain ancestor names. The ambiguity resolving rules are + * applied recursively for each component of the name, so the object paths + * used to find each component are the ones filtered with the name of its + * ancestor. + * + * @param name The name of the object to get. + * @param objectPaths The paths to get the object from. + * @return The list of objects that match the best the given name. + */ + QList<QObject*> getBestMatches(const QString& name, + QList< QList<QObject*> > objectPaths) const; + + /** + * Returns the object paths that contain a descendant of the base object + * with the given name. + * If direct children are found, their path is used. If not, if descendants + * without named objects between them and the base object are found, their + * path is used. If not, the path of the shallower descendants is used. + * The name must be a single object name, without any ancestor name. + * The returned paths are trimmed to make the object with the given name the + * new base object of the path. + * + * @param name The name of the descendant to find. + * @param objectPaths The paths to search the object in. + * @return The filtered and trimmed object paths. + */ + QList< QList<QObject*> > filterObjectPaths(const QString& name, + const QList< QList<QObject*> >& objectPaths) const; + + /** + * Returns the object paths that contain a direct child from the base object + * with the given name. + * The name must be a single object name, without any ancestor name. + * The returned paths are trimmed to make the object with the given name the + * new base object of the path. + * + * @param name The name of the direct child to find. + * @param objectPaths The paths to search the object in. + * @return The filtered and trimmed object paths. + */ + QList< QList<QObject*> > filterDirectChildren(const QString& name, + const QList< QList<QObject*> >& objectPaths) const; + + /** + * Returns the object paths that contain a descendant from the base object + * with the given name. + * All the objects between the base object and the descendant with the given + * name must have no name. + * If there is more than one descendant with the given name, only the + * shallower ones are taken into account. + * The name must be a single object name, without any ancestor name. + * The returned paths are trimmed to make the object with the given name the + * new base object of the path. + * + * @param name The name of the descendant to find. + * @param objectPaths The paths to search the object in. + * @return The filtered and trimmed object paths. + */ + QList< QList<QObject*> > filterNestedChildrenWithUnnamedAncestors( + const QString& name, const QList< QList<QObject*> >& objectPaths) const; + + /** + * Returns the object paths that contain a descendant from the base object + * with the given name. + * If there is more than one descendant with the given name, only the + * shallower ones are taken into account. + * The name must be a single object name, without any ancestor name. + * The returned paths are trimmed to make the object with the given name the + * new base object of the path. + * + * @param name The name of the descendant to find. + * @param objectPaths The paths to search the object in. + * @return The filtered and trimmed object paths. + */ + QList< QList<QObject*> > filterNestedChildren(const QString& name, + const QList< QList<QObject*> >& objectPaths) const; + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/ObjectFinder.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2012-07-05 12:38:07 UTC (rev 353) +++ trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2012-07-05 18:19:04 UTC (rev 354) @@ -26,7 +26,7 @@ ENDMACRO(UNIT_TESTS) unit_tests( - KTutorial + ObjectFinder Option Step Tutorial @@ -51,7 +51,7 @@ ENDMACRO(MEM_TESTS) mem_tests( - KTutorial + ObjectFinder Option Step Tutorial Deleted: trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp 2012-07-05 12:38:07 UTC (rev 353) +++ trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp 2012-07-05 18:19:04 UTC (rev 354) @@ -1,454 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2011-2012 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 <QAction> - -#include "KTutorial.h" - -class KTutorialTest: public QObject { -Q_OBJECT - -private slots: - - void initTestCase(); - void cleanupTestCase(); - - void testFindObjectSingleName(); - void testFindObjectSingleNameUnknown(); - void testFindObjectComplexNameDirectChild(); - void testFindObjectComplexNameNestedChild(); - void testFindObjectComplexNameAncestorNameNotUnique(); - void testFindObjectComplexNameUnknownParent(); - - void testFindObjectAmbiguousSingleNameDirectChild(); - void testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestors(); - void testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestorsDeeperThanNamed(); - void testFindObjectAmbiguousSingleNameNestedChildNamedAncestors(); - void testFindObjectAmbiguousSingleNameNestedChildNamedAncestorsSameDeepThanMixed(); - void testFindObjectAmbiguousSingleNameNestedChildMixedAncestorsSameDeepThanNamed(); - void testFindObjectAmbiguousComplexName(); - void testFindObjectAmbiguousComplexNameUniqueAncestor(); - void testFindObjectComplexNameDifferentAncestorIfSolvingAmbiguity(); - - void testFindObjectEmptyName(); - void testFindObjectSingleSlash(); - void testFindObjectSlashEndedName(); - void testFindObjectSeveralSlashes(); - -private: - - KXmlGuiWindow* mMainWindow; - QObject* mObject1_1_1; - QAction* mAction1_1_2; - QObject* mObject1_2_1; - QObject* mObject2_1_1; - QObject* mObject2_1_2; - QAction* mAction2_1_3; - QAction* mAction2_1_4; - QObject* mObject3_1_1; - QObject* mObject3_2_1_1; - QAction* mAction3_3_1_1; - QObject* mAmbiguousObject5; - QObject* mAmbiguousObject8_1_2; - QObject* mAmbiguousObject10_1; - QObject* mAmbiguousObject11_1_2; - QObject* mAmbiguousObject12_1; - QObject* mAmbiguousObject12_2_3; - QObject* mObject14_1_2; - QObject* mAmbiguousObject15_2_2_1; - QObject* mAmbiguousObject16_2_1_1_1; - - void assertFindObject(const QString& objectName, QObject* object) const; - void assertFindAction(const QString& objectName, QAction* action) const; - -}; - -void KTutorialTest::initTestCase() { - mMainWindow = new KXmlGuiWindow(); - KTutorial::self()->setup(mMainWindow); - - //-Grand parent1 - // |-Parent1 - // | |-The object - // | |-The action - // |-Parent2 - // |-The object - //-Grand parent2 - // |-Parent1 - // |-The object - // |-Another object - // |-The action - // |-Another action - //-Grand parent3 - // |-Parent1 - // | |-The object - // |-Nested parent - // | |-Another object - // |-Nested timer - // |-Another action - QObject* grandParent1 = new QObject(mMainWindow); - grandParent1->setObjectName("Grand parent1"); - QObject* parent1_1 = new QObject(grandParent1); - parent1_1->setObjectName("Parent1"); - mObject1_1_1 = new QObject(parent1_1); - mObject1_1_1->setObjectName("The object"); - mAction1_1_2 = new QAction(parent1_1); - mAction1_1_2->setObjectName("The action"); - - QObject* parent1_2 = new QObject(grandParent1); - parent1_2->setObjectName("Parent2"); - mObject1_2_1 = new QObject(parent1_2); - mObject1_2_1->setObjectName("The object"); - - QObject* grandParent2 = new QObject(mMainWindow); - grandParent2->setObjectName("Grand parent2"); - QObject* parent2_1 = new QObject(grandParent2); - parent2_1->setObjectName("Parent1"); - mObject2_1_1 = new QObject(parent2_1); - mObject2_1_1->setObjectName("The object"); - mObject2_1_2 = new QObject(parent2_1); - mObject2_1_2->setObjectName("Another object"); - mAction2_1_3 = new QAction(parent2_1); - mAction2_1_3->setObjectName("The action"); - mAction2_1_4 = new QAction(parent2_1); - mAction2_1_4->setObjectName("Another action"); - - QObject* grandParent3 = new QObject(mMainWindow); - grandParent3->setObjectName("Grand parent3"); - QObject* parent3_1 = new QObject(grandParent3); - parent3_1->setObjectName("Parent1"); - mObject3_1_1 = new QObject(parent3_1); - mObject3_1_1->setObjectName("The object"); - - QObject* parent3_2 = new QObject(grandParent3); - parent3_2->setObjectName("Nested parent"); - QObject* parent3_2_1 = new QObject(parent3_2); - mObject3_2_1_1 = new QObject(parent3_2_1); - mObject3_2_1_1->setObjectName("Another object"); - - QTimer* parent3_3 = new QTimer(grandParent3); - parent3_3->setObjectName("Nested timer"); - QTimer* parent3_3_1 = new QTimer(parent3_3); - mAction3_3_1_1 = new QAction(parent3_3_1); - mAction3_3_1_1->setObjectName("Another action"); - - //-??? - // |-??? - // | |-Ambiguous object - // |-Ambiguous object - //-Ambiguous object - //-Object 6 - // |-Ambiguous object - // |-Object 6_2 - // |-Ambiguous object - //-Ambiguous object - QObject* unnamedObject4 = new QObject(mMainWindow); - QObject* unnamedObject4_1 = new QObject(unnamedObject4); - QObject* ambiguousObject4_1_1 = new QObject(unnamedObject4_1); - ambiguousObject4_1_1->setObjectName("Ambiguous object"); - QObject* ambiguousObject4_2 = new QObject(unnamedObject4); - ambiguousObject4_2->setObjectName("Ambiguous object"); - - mAmbiguousObject5 = new QObject(mMainWindow); - mAmbiguousObject5->setObjectName("Ambiguous object"); - - QObject* namedObject6 = new QObject(mMainWindow); - namedObject6->setObjectName("Object 6"); - QObject* ambiguousObject6_1 = new QObject(namedObject6); - ambiguousObject6_1->setObjectName("Ambiguous object"); - QObject* namedObject6_2 = new QObject(namedObject6); - namedObject6_2->setObjectName("Object 6_2"); - QObject* ambiguousObject6_2_1 = new QObject(namedObject6_2); - ambiguousObject6_2_1->setObjectName("Ambiguous object"); - - QObject* ambiguousObject7 = new QObject(mMainWindow); - ambiguousObject7->setObjectName("Ambiguous object"); - - //-??? - // |-??? - // | |-Ambiguous object2 - // | |-Ambiguous object3 - //-Object 9 - // |-Ambiguous object2 - // |-Object 9_2 - // | |-Ambiguous object2 - // |-Ambiguous object3 - //-??? - // |-Ambiguous object2 - // |-Ambiguous object2 - QObject* unnamedObject8 = new QObject(mMainWindow); - QObject* unnamedObject8_1 = new QObject(unnamedObject8); - QObject* ambiguousObject8_1_1 = new QObject(unnamedObject8_1); - ambiguousObject8_1_1->setObjectName("Ambiguous object2"); - mAmbiguousObject8_1_2 = new QObject(unnamedObject8_1); - mAmbiguousObject8_1_2->setObjectName("Ambiguous object3"); - - QObject* namedObject9 = new QObject(mMainWindow); - namedObject9->setObjectName("Object 9"); - QObject* ambiguousObject9_1 = new QObject(namedObject9); - ambiguousObject9_1->setObjectName("Ambiguous object2"); - QObject* namedObject9_2 = new QObject(namedObject9); - namedObject9_2->setObjectName("Object 9_2"); - QObject* ambiguousObject9_2_1 = new QObject(namedObject9_2); - ambiguousObject9_2_1->setObjectName("Ambiguous object2"); - QObject* ambiguousObject9_3 = new QObject(namedObject9); - ambiguousObject9_3->setObjectName("Ambiguous object3"); - - QObject* unnamedObject10 = new QObject(mMainWindow); - mAmbiguousObject10_1 = new QObject(unnamedObject10); - mAmbiguousObject10_1->setObjectName("Ambiguous object2"); - QObject* ambiguousObject10_2 = new QObject(unnamedObject10); - ambiguousObject10_2->setObjectName("Ambiguous object2"); - - //-Object 11 - // |-Object 11_1 - // |-Ambiguous object4 - // |-Ambiguous object5 - //-Object 12 - // |-Ambiguous object4 - // |-??? - // |-Ambiguous object4 - // |-Ambiguous object5 - // |-Ambiguous object6 - //-Object 13 - // |-Ambiguous object4 - // |-Object 13_2 - // |-Ambiguous object6 - QObject* namedObject11 = new QObject(mMainWindow); - namedObject11->setObjectName("Object 11"); - QObject* namedObject11_1 = new QObject(namedObject11); - namedObject11_1->setObjectName("Object 11_1"); - QObject* ambiguousObject11_1_1 = new QObject(namedObject11_1); - ambiguousObject11_1_1->setObjectName("Ambiguous object4"); - mAmbiguousObject11_1_2 = new QObject(namedObject11_1); - mAmbiguousObject11_1_2->setObjectName("Ambiguous object5"); - - QObject* namedObject12 = new QObject(mMainWindow); - namedObject12->setObjectName("Object 12"); - mAmbiguousObject12_1 = new QObject(namedObject12); - mAmbiguousObject12_1->setObjectName("Ambiguous object4"); - QObject* unnamedObject12_2 = new QObject(namedObject12); - QObject* ambiguousObject12_2_1 = new QObject(unnamedObject12_2); - ambiguousObject12_2_1->setObjectName("Ambiguous object4"); - QObject* ambiguousObject12_2_2 = new QObject(unnamedObject12_2); - ambiguousObject12_2_2->setObjectName("Ambiguous object5"); - mAmbiguousObject12_2_3 = new QObject(unnamedObject12_2); - mAmbiguousObject12_2_3->setObjectName("Ambiguous object6"); - - QObject* namedObject13 = new QObject(mMainWindow); - namedObject13->setObjectName("Object 13"); - QObject* ambiguousObject13_1 = new QObject(namedObject13); - ambiguousObject13_1->setObjectName("Ambiguous object4"); - QObject* namedObject13_2 = new QObject(namedObject13); - namedObject13_2->setObjectName("Object 13_2"); - QObject* ambiguousObject13_2_1 = new QObject(namedObject13_2); - ambiguousObject13_2_1->setObjectName("Ambiguous object6"); - - //-Object 14 - // |-Ambiguous ancestor - // |-Ambiguous object7 - // |-The object - //-??? - // |-Ambiguous ancestor - // | |-Object 15_1_1 - // | |-Ambiguous object7 - // |-Unique ancestor - // |-Object 15_2_1 - // | |-Ambiguous object7 - // |-??? - // |-Ambiguous object7 - //-??? - // |-??? - // | |-Ambiguous ancestor - // | |-Ambiguous object7 - // |-Ambiguous ancestor - // |-??? - // | |-??? - // | |-Ambiguous object7 - // |-Object 16_2_2 - // |-Ambiguous object7 - QObject* namedObject14 = new QObject(mMainWindow); - namedObject14->setObjectName("Object 14"); - QObject* ambiguousAncestor14_1 = new QObject(namedObject14); - ambiguousAncestor14_1->setObjectName("Ambiguous ancestor"); - QObject* ambiguousObject14_1_1 = new QObject(ambiguousAncestor14_1); - ambiguousObject14_1_1->setObjectName("Ambiguous object7"); - mObject14_1_2 = new QObject(ambiguousAncestor14_1); - mObject14_1_2->setObjectName("The object"); - - QObject* unnamedObject15 = new QObject(mMainWindow); - QObject* ambiguousAncestor15_1 = new QObject(unnamedObject15); - ambiguousAncestor15_1->setObjectName("Ambiguous ancestor"); - QObject* namedObject15_1_1 = new QObject(ambiguousAncestor15_1); - namedObject15_1_1->setObjectName("Object 15_1_1"); - QObject* ambiguousObject15_1_1_1 = new QObject(namedObject15_1_1); - ambiguousObject15_1_1_1->setObjectName("Ambiguous object7"); - QObject* uniqueAncestor15_2 = new QObject(unnamedObject15); - uniqueAncestor15_2->setObjectName("Unique ancestor"); - QObject* namedObject15_2_1 = new QObject(uniqueAncestor15_2); - namedObject15_2_1->setObjectName("Object 15_2_1"); - QObject* ambiguousObject15_2_1_1 = new QObject(namedObject15_2_1); - ambiguousObject15_2_1_1->setObjectName("Ambiguous object7"); - QObject* unnamedObject15_2_2 = new QObject(uniqueAncestor15_2); - mAmbiguousObject15_2_2_1 = new QObject(unnamedObject15_2_2); - mAmbiguousObject15_2_2_1->setObjectName("Ambiguous object7"); - - QObject* unnamedObject16 = new QObject(mMainWindow); - QObject* unnamedObject16_1 = new QObject(unnamedObject16); - QObject* ambiguousAncestor16_1_1 = new QObject(unnamedObject16_1); - ambiguousAncestor16_1_1->setObjectName("Ambiguous ancestor"); - QObject* ambiguousObject16_1_1_1 = new QObject(ambiguousAncestor16_1_1); - ambiguousObject16_1_1_1->setObjectName("Ambiguous object7"); - QObject* ambiguousAncestor16_2 = new QObject(unnamedObject16); - ambiguousAncestor16_2->setObjectName("Ambiguous ancestor"); - QObject* unnamedObject16_2_1 = new QObject(ambiguousAncestor16_2); - QObject* unnamedObject16_2_1_1 = new QObject(unnamedObject16_2_1); - mAmbiguousObject16_2_1_1_1 = new QObject(unnamedObject16_2_1_1); - mAmbiguousObject16_2_1_1_1->setObjectName("Ambiguous object7"); - QObject* namedObject16_2_2 = new QObject(ambiguousAncestor16_2); - namedObject16_2_2->setObjectName("Object 16_2_2"); - QObject* ambiguousObject16_2_2_1 = new QObject(namedObject16_2_2); - ambiguousObject16_2_2_1->setObjectName("Ambiguous object7"); -} - -void KTutorialTest::cleanupTestCase() { - delete mMainWindow; -} - -void KTutorialTest::testFindObjectSingleName() { - assertFindObject("The object", mObject1_1_1); -} - -void KTutorialTest::testFindObjectSingleNameUnknown() { - assertFindObject("Unknown object", 0); -} - -void KTutorialTest::testFindObjectComplexNameDirectChild() { - assertFindObject("Grand parent1/Parent1/The object", mObject1_1_1); - assertFindObject("Grand parent1/Parent2/The object", mObject1_2_1); - assertFindObject("Grand parent2/Parent1/The object", mObject2_1_1); - - assertFindAction("Grand parent1/Parent1/The action", mAction1_1_2); - assertFindAction("Grand parent2/Parent1/The action", mAction2_1_3); -} - -void KTutorialTest::testFindObjectComplexNameNestedChild() { - assertFindObject("Grand parent2/The object", mObject2_1_1); - assertFindObject("Nested parent/Another object", mObject3_2_1_1); - - assertFindAction("Grand parent2/The action", mAction2_1_3); - assertFindAction("Nested timer/Another action", mAction3_3_1_1); -} - -void KTutorialTest::testFindObjectComplexNameAncestorNameNotUnique() { - //The ancestor name is not unique, but the full path is - assertFindObject("Parent1/Another object", mObject2_1_2); - - assertFindAction("Parent1/Another action", mAction2_1_4); -} - -void KTutorialTest::testFindObjectComplexNameUnknownParent() { - assertFindObject("Unknown grand parent/The object", 0); - assertFindObject("Grand parent1/Unknown parent/The object", 0); -} - -void KTutorialTest::testFindObjectAmbiguousSingleNameDirectChild() { - assertFindObject("Ambiguous object", mAmbiguousObject5); -} - -void KTutorialTest:: -testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestors() { - assertFindObject("Ambiguous object2", mAmbiguousObject10_1); -} - -void KTutorialTest:: -testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestorsDeeperThanNamed() { - assertFindObject("Ambiguous object3", mAmbiguousObject8_1_2); -} - -void KTutorialTest:: -testFindObjectAmbiguousSingleNameNestedChildNamedAncestors() { - assertFindObject("Ambiguous object4", mAmbiguousObject12_1); -} - -void KTutorialTest:: -testFindObjectAmbiguousSingleNameNestedChildNamedAncestorsSameDeepThanMixed() { - assertFindObject("Ambiguous object5", mAmbiguousObject11_1_2); -} - -void KTutorialTest:: -testFindObjectAmbiguousSingleNameNestedChildMixedAncestorsSameDeepThanNamed() { - assertFindObject("Ambiguous object6", mAmbiguousObject12_2_3); -} - -void KTutorialTest::testFindObjectAmbiguousComplexName() { - //The ancestor is selected by the rule of shallower unnamed ancestors. The - //object is selected by the rule of unnamed ancestor even if there are - //shallower objects, but with named ancestors. - assertFindObject("Ambiguous ancestor/Ambiguous object7", - mAmbiguousObject16_2_1_1_1); -} - -void KTutorialTest::testFindObjectAmbiguousComplexNameUniqueAncestor() { - //The ancestor is unique, although the object itself is ambiguous (even - //among the descendants of that unique ancestor). - assertFindObject("Unique ancestor/Ambiguous object7", - mAmbiguousObject15_2_2_1); -} - -void KTutorialTest:: -testFindObjectComplexNameDifferentAncestorIfSolvingAmbiguity() { - //The full name is unique, but if the rules to resolve ambiguity were - //applied no object would be found, as the "Ambiguous ancestor" found using - //the ambiguity resolving rules have no "The object" descendants. - assertFindObject("Ambiguous ancestor/The object", mObject14_1_2); -} - -void KTutorialTest::testFindObjectEmptyName() { - assertFindObject("", 0); -} - -void KTutorialTest::testFindObjectSingleSlash() { - assertFindObject("/", 0); -} - -void KTutorialTest::testFindObjectSlashEndedName() { - assertFindObject("Parent/", 0); -} - -void KTutorialTest::testFindObjectSeveralSlashes() { - assertFindObject("Parent1///The object", mObject1_1_1); -} - -/////////////////////////////////Helpers//////////////////////////////////////// - -void KTutorialTest::assertFindObject(const QString& objectName, - QObject* object) const { - QCOMPARE(KTutorial::self()->findObject<QObject*>(objectName), object); -} - -void KTutorialTest::assertFindAction(const QString& objectName, - QAction* action) const { - QCOMPARE(KTutorial::self()->findObject<QAction*>(objectName), action); -} - -QTEST_MAIN(KTutorialTest) - -#include "KTutorialTest.moc" Copied: trunk/ktutorial/ktutorial-library/tests/ObjectFinderTest.cpp (from rev 353, trunk/ktutorial/ktutorial-library/tests/KTutorialTest.cpp) =================================================================== --- trunk/ktutorial/ktutorial-library/tests/ObjectFinderTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/ObjectFinderTest.cpp 2012-07-05 18:19:04 UTC (rev 354) @@ -0,0 +1,457 @@ +/*************************************************************************** + * Copyright (C) 2011-2012 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 <QAction> + +#include <KXmlGuiWindow> + +#include "ObjectFinder.h" + +class ObjectFinderTest: public QObject { +Q_OBJECT + +private slots: + + void initTestCase(); + void cleanupTestCase(); + + void testFindObjectSingleName(); + void testFindObjectSingleNameUnknown(); + void testFindObjectComplexNameDirectChild(); + void testFindObjectComplexNameNestedChild(); + void testFindObjectComplexNameAncestorNameNotUnique(); + void testFindObjectComplexNameUnknownParent(); + + void testFindObjectAmbiguousSingleNameDirectChild(); + void testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestors(); + void testFindObjectAmbiguousSingleNameNestedChildUnnamedAncestorsDeeperThanNamed(); + void testFindObjectAmbiguousSingleNameNestedChildNamedAncestors(); + void testFindObjectAmbiguousSingleNameNestedChildNamedAncestorsSameDeepThanMixed(); + void testFindObjectAmbiguousSingleNameNestedChildMixedAncestorsSameDeepThanNamed(); + void testFindObjectAmbiguousComplexName(); + void testFindObjectAmbiguousComplexNameUniqueAncestor(); + void testFindObjectComplexNameDifferentAncestorIfSolvingAmbiguity(); + + void testFindObjectEmptyName(); + void testFindObjectSingleSlash(); + void testFindObjectSlashEndedName(); + void testFindObjectSeveralSlashes(); + +private: + + KXmlGuiWindow* mMainWindow; + QObject* mObject1_1_1; + QAction* mAction1_1_2; + QObject* mObject1_2_1; + QObject* mObject2_1_1; + QObject* mObject2_1_2; + QAction* mAction2_1_3; + QAction* mAction2_1_4; + QObject* mObject3_1_1; + QObject* mObject3_2_1_1; + QAction* mAction3_3_1_1; + QObject* mAmbiguousObject5; + QObject* mAmbiguousObject8_1_2; + QObject* mAmbiguousObject10_1; + QObject* mAmbiguousObject11_1_2; + QObject* mAmbiguousObject12_1; + QObject* mAmbiguousObject12_2_3; + QObject* mObject14_1_2; + QObject* mAmbiguousObject15_2_2_1; + QObject* mAmbiguousObject16_2_1_1_1; + + void assertFindObject(const QString& objectName, QObject* object) const; + void assertFindAction(const QString& objectName, QAction* action) const; + +}; + +void ObjectFinderTest::initTestCase() { + mMainWindow = new KXmlGuiWindow(); + + //-Grand parent1 + // |-Parent1 + // | |-The object + // | |-The action + // |-Parent2 + // |-The object + //-Grand parent2 + // |-Parent1 + // |-The object + // |-Another object + // |-The action + // |-Another action + //-Grand parent3 + // |-Parent1 + // | |-The object + // |-Nested parent + // | |-Another object + // |-Nested timer + // |-Another action + QObject* grandParent1 = new QObject(mMainWindow); + grandParent1->setObjectName("Grand parent1"); + QObject* parent1_1 = new QObject(grandParent1); + parent1_1->setObjectName("Parent1"); + mObject1_1_1 = new QObject(parent1_1); + mObject1_1_1->setObjectName("The object"); + mAction1_1_2 = new QAction(parent1_1); + mAction1_1_2->setObjectName("The action"); + + QObject* parent1_2 = new QObject(grandParent1); + parent1_2->setObjectName("Parent2"); + mObject1_2_1 = new QObject(parent1_2); + mObject1_2_1->setObjectName("The object"); + + QObject* grandParent2 = new QObject(mMainWindow); + grandParent2->setObjectName("Grand parent2"); + QObject* parent2_1 = new QObject(grandParent2); + parent2_1->setObjectName("Parent1"); + mObject2_1_1 = new QObject(parent2_1); + mObject2_1_1->setObjectName("The object"); + mObject2_1_2 = new QObject(parent2_1); + mObject2_1_2->setObjectName("Another object"); + mAction2_1_3 = new QAction(parent2_1); + mAction2_1_3->setObjectName("The action"); + mAction2_1_4 = new QAction(parent2_1); + mAction2_1_4->setObjectName("Another action"); + + QObject* grandParent3 = new QObject(mMainWindow); + grandParent3->setObjectName("Grand parent3"); + QObject* parent3_1 = new QObject(grandParent3); + parent3_1->setObjectName("Parent1"); + mObject3_1_1 = new QObject(parent3_1); + mObject3_1_1->setObjectName("The object"); + + QObject* parent3_2 = new QObject(grandParent3); + parent3_2->setObjectName("Nested parent"); + QObject* parent3_2_1 = new QObject(parent3_2); + mObject3_2_1_1 = new QObject(parent3_2_1); + mObject3_2_1_1->setObjectName("Another object"); + + QTimer* parent3_3 = new QTimer(grandParent3); + parent3_3->setObjectName("Nested timer"); + QTimer* parent3_3_1 = new QTimer(parent3_3); + mAction3_3_1_1 = new QAction(parent3_3_1); + mAction3_3_1_1->setObjectName("Another action"); + + //-??? + // |-??? + // | |-Ambiguous object + // |-Ambiguous object + //-Ambiguous object + //-Object 6 + // |-Ambiguous object + // |-Object 6_2 + // |-Ambiguous object + //-Ambiguous object + QObject* unnamedObject4 = new QObject(mMainWindow); + QObject* unnamedObject4_1 = new QObject(unnamedObject4); + QObject* ambiguousObject4_1_1 = new QObject(unnamedObject4_1); + ambiguousObject4_1_1->setObjectName("Ambiguous object"); + QObject* ambiguousObject4_2 = new QObject(unnamedObject4); + ambiguousObject4_2->setObjectName("Ambiguous object"); + + mAmbiguousObject5 = new QObject(mMainWindow); + mAmbiguousObject5->setObjectName("Ambiguous object"); + + QObject* namedObject6 = new QObject(mMainWindow); + namedObject6->setObjectName("Object 6"); + QObject* ambiguousObject6_1 = new QObject(namedObject6); + ambiguousObject6_1->setObjectName("Ambiguous object"); + QObject* namedObject6_2 = new QObject(namedObject6); + namedObject6_2->setObjectName("Object 6_2"); + QObject* ambiguousObject6_2_1 = new QObject(namedObject6_2); + ambiguousObject6_2_1->setObjectName("Ambiguous object"); + + QObject* ambiguousObject7 = new QObject(mMainWindow); + ambiguousObject7->setObjectName("Ambiguous object"); + + //-??? + // |-??? + // | |-Ambiguous object2 + // | |-Ambiguous object3 + //-Object 9 + // |-Ambiguous object2 + // |-Object 9_2 + // | |-Ambiguous object2 + // |-Ambiguous object3 + //-??? + // |-Ambiguous object2 ... [truncated message content] |
From: <dan...@us...> - 2012-08-20 10:59:49
|
Revision: 372 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=372&view=rev Author: danxuliu Date: 2012-08-20 10:59:41 +0000 (Mon, 20 Aug 2012) Log Message: ----------- Add system for the applications using KTutorial to customize its user interface. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/KTutorial.cpp trunk/ktutorial/ktutorial-library/src/KTutorial.h trunk/ktutorial/ktutorial-library/src/Option.h trunk/ktutorial/ktutorial-library/src/Tutorial.h trunk/ktutorial/ktutorial-library/src/TutorialManager.h trunk/ktutorial/ktutorial-library/src/WaitForWindow.cpp trunk/ktutorial/ktutorial-library/src/scripting/ScriptingModule.cpp trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt Added Paths: ----------- trunk/ktutorial/ktutorial-library/src/KTutorialCustomization.cpp trunk/ktutorial/ktutorial-library/src/KTutorialCustomization.h trunk/ktutorial/ktutorial-library/src/customization/ trunk/ktutorial/ktutorial-library/src/customization/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/customization/DefaultKdeCustomization.cpp trunk/ktutorial/ktutorial-library/src/customization/DefaultKdeCustomization.h trunk/ktutorial/ktutorial-library/tests/customization/ trunk/ktutorial/ktutorial-library/tests/customization/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/customization/DefaultKdeCustomizationTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2012-08-15 02:08:03 UTC (rev 371) +++ trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2012-08-20 10:59:41 UTC (rev 372) @@ -3,6 +3,7 @@ add_definitions("-fPIC") add_subdirectory(common) +add_subdirectory(customization) add_subdirectory(extendedinformation) add_subdirectory(scripting) add_subdirectory(tutorials) @@ -20,6 +21,7 @@ set(ktutorial_LIB_SRCS KTutorial.cpp + KTutorialCustomization.cpp ObjectFinder.cpp Option.cpp Step.cpp @@ -40,7 +42,7 @@ kde4_add_library(ktutorial SHARED ${ktutorial_LIB_SRCS}) -target_link_libraries(ktutorial ktutorial_scripting ktutorial_tutorials ktutorial_view) +target_link_libraries(ktutorial ktutorial_customization ktutorial_scripting) if (QT_QTDBUS_FOUND) target_link_libraries(ktutorial ktutorial_editorsupport) Modified: trunk/ktutorial/ktutorial-library/src/KTutorial.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/KTutorial.cpp 2012-08-15 02:08:03 UTC (rev 371) +++ trunk/ktutorial/ktutorial-library/src/KTutorial.cpp 2012-08-20 10:59:41 UTC (rev 372) @@ -16,46 +16,23 @@ * along with this program; If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ -#include <kaction.h> -#include <kactioncollection.h> -#include <klocalizedstring.h> - #include "KTutorial.h" -#include "Tutorial.h" -#include "TutorialInformation.h" +#include "customization/DefaultKdeCustomization.h" #include "scripting/ScriptingModule.h" #include "scripting/ScriptManager.h" -#include "tutorials/UsingKTutorial.h" -#include "view/StepWidget.h" -#include "view/TutorialManagerDialog.h" #ifdef QT_QTDBUS_FOUND #include "editorsupport/EditorSupport.h" #endif +using ktutorial::customization::DefaultKdeCustomization; using ktutorial::scripting::ScriptingModule; using ktutorial::scripting::ScriptManager; -using ktutorial::view::StepWidget; -using ktutorial::view::TutorialManagerDialog; namespace ktutorial { //public: -//It inherits from QObject to be automatically destroyed when the parent -//KXmlGuiWindow is destroyed. It uses the same strategy as -//KDEPrivate::ToolBarHandler. -//Usually I do not like multiple inheritance, but it comes very handy here :) -class TutorialKXmlGuiClient: public QObject, public KXMLGUIClient { -Q_OBJECT -public: - TutorialKXmlGuiClient(KXmlGuiWindow* parent): - QObject(parent), KXMLGUIClient(parent) { - setComponentData(KComponentData("ktutorial")); - setXMLFile("ktutorialui.rc"); - } -}; - bool KTutorial::registerWaitForMetaObject(const QMetaObject& waitForMetaObject, const QString& typeName /*= QString()*/) { return ScriptingModule::self()->registerWaitForMetaObject(waitForMetaObject, @@ -63,79 +40,37 @@ } void KTutorial::setup(KXmlGuiWindow* window) { - TutorialKXmlGuiClient* tutorialClient= new TutorialKXmlGuiClient(window); + DefaultKdeCustomization* defaultKdeCustomization = + new DefaultKdeCustomization(window); + setup(defaultKdeCustomization); +} - mTutorialsAction = new KAction(window); - mTutorialsAction->setText(i18nc("@action:inmenu", "Tutorials...")); - tutorialClient->actionCollection()->addAction("tutorials", mTutorialsAction); - connect(mTutorialsAction, SIGNAL(triggered(bool)), - this, SLOT(showTutorialManagerDialog())); +void KTutorial::setup(KTutorialCustomization* ktutorialCustomization) { + mCustomization = ktutorialCustomization; + mCustomization->setParent(this); - connect(mTutorialmanager, SIGNAL(started(Tutorial*)), - this, SLOT(showStepWidget(Tutorial*))); + mCustomization->setup(mTutorialmanager); - connect(mTutorialmanager, SIGNAL(started(Tutorial*)), - this, SLOT(disableTutorialsAction())); - connect(mTutorialmanager, SIGNAL(finished()), - this, SLOT(enableTutorialsAction())); - - mParent = window; - - registerTutorial(new UsingKTutorial()); - ScriptManager().loadTutorials(mTutorialmanager); #ifdef QT_QTDBUS_FOUND editorsupport::EditorSupport* editorSupport = new editorsupport::EditorSupport(this); editorSupport->setObjectFinder(mObjectFinder); - editorSupport->setup(window); + editorSupport->setup(mainApplicationWindow()); connect(editorSupport, SIGNAL(started(Tutorial*)), - this, SLOT(showStepWidget(Tutorial*))); + ktutorialCustomization, SLOT(showTutorialUI(Tutorial*))); #endif } +QWidget* KTutorial::mainApplicationWindow() const { + return mCustomization->mainApplicationWindow(); +} + //private: KTutorial* KTutorial::sSelf = new KTutorial(); -//private slots: - -void KTutorial::showTutorialManagerDialog() const { - QDialog* dialog = new TutorialManagerDialog(mTutorialmanager, mParent); - dialog->setAttribute(Qt::WA_DeleteOnClose); - dialog->setModal(true); - dialog->setObjectName("ktutorial_TutorialManagerDialog"); - - dialog->show(); } -void KTutorial::showStepWidget(Tutorial* tutorial) const { - QString tutorialName = tutorial->tutorialInformation()->name(); - - StepWidget* stepWidget = new StepWidget(tutorialName, mParent); - stepWidget->setMainApplicationWindow(mParent); - stepWidget->setObjectName("ktutorial_StepWidget"); - connect(tutorial, SIGNAL(stepActivated(Step*)), - stepWidget, SLOT(setStep(Step*))); - connect(stepWidget, SIGNAL(finished()), tutorial, SLOT(finish())); - //Invalid tutorials finish just after being started. Deleting the StepWidget - //when the tutorial finishes ensures that it is deleted in those cases and, - //as deleteLater() is used, it does not interfere with the deletion when the - //StepWidget is closed by the user. - connect(tutorial, SIGNAL(finished(Tutorial*)), - stepWidget, SLOT(deleteLater())); -} - -void KTutorial::disableTutorialsAction() { - mTutorialsAction->setEnabled(false); -} - -void KTutorial::enableTutorialsAction() { - mTutorialsAction->setEnabled(true); -} - -} - -#include "moc_KTutorial.cpp" #include "KTutorial.moc" Modified: trunk/ktutorial/ktutorial-library/src/KTutorial.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/KTutorial.h 2012-08-15 02:08:03 UTC (rev 371) +++ trunk/ktutorial/ktutorial-library/src/KTutorial.h 2012-08-20 10:59:41 UTC (rev 372) @@ -19,27 +19,35 @@ #ifndef KTUTORIAL_KTUTORIAL_H #define KTUTORIAL_KTUTORIAL_H -#include <kxmlguiwindow.h> +#include <QWidget> #include "ktutorial_export.h" #include "ObjectFinder.h" #include "TutorialManager.h" -class KAction; +class KXmlGuiWindow; namespace ktutorial { +class KTutorialCustomization; +} +namespace ktutorial { + /** * Main class of KTutorial. * This class is used to setup KTutorial, register WaitFor classes to be created * from scripts, register new Tutorials embedded in the application and find * objects in the Tutorials. It uses a Singleton design pattern. * - * When using KTutorial, KTutorial::setup(KxmlGuiWindow*) must be called to set - * up everything needed for KTutorial to work. It needs to know the main window - * of the application. + * When using KTutorial and no customization is needed, + * KTutorial::setup(KxmlGuiWindow*) must be called to set up everything needed + * for KTutorial to work. It needs to know the main window of the application. * + * If a KTutorial customization is needed, + * KTutorial::setup(KTutorialCustomization*) must be called instead providing + * the desired customization. + * * The setup method will be the first one to be called, but when custom WaitFor * objects are used in scripts. In that case, all the needed WaitFor classes * must be registered using registerWaitForMetaObject(const QMetaObject&) before @@ -50,14 +58,18 @@ * Just use KTutorial::registerTutorial(Tutorial*) so it is registered in the * system. Only registered tutorial are seen by the user. * - * Finally, any of the children objects of the main window of the application - * (which will usually be at least all widgets) can be got with - * KTutorial::findObject(const QString&), provided the looked for object has its - * object name set. + * Note, however, that when a customization is used, the WaitFor classes and the + * tutorials embedded in the application can be registered during the + * customization setup itself. * - * The TutorialManagerDialog is shown as modal through the "tutorials" action in - * Help menu. This action will be automatically disabled whenever a tutorial is - * being executed. + * When no customization is used, the TutorialManagerDialog is shown as modal + * through the "tutorials" action in the "Help" menu. This action will be + * automatically disabled whenever a tutorial is being executed. + * + * Finally, no matter whether a customization is used or not, any of the + * children objects of the main window of the application (which will usually be + * at least all widgets) can be got with KTutorial::findObject(const QString&), + * provided the looked for object has its object name set. */ class KTUTORIAL_EXPORT KTutorial: public QObject { Q_OBJECT @@ -86,10 +98,10 @@ const QString& typeName = QString()); /** - * Sets up everything for KTutorial to work. - * This is usually the first method that must be called when using KTutorial - * in an application. However, if any WaitFor class has to be registered, it - * must be done before calling this method. + * Sets up everything for KTutorial to work with the default customization. + * If no customization is needed, this is usually the first method that must + * be called when using KTutorial in an application. However, if any WaitFor + * class has to be registered, it must be done before calling this method. * * It adds the Tutorials action and the menu entry for it in Help menu and * loads the scripted tutorials from the application standard directories. @@ -99,6 +111,15 @@ void setup(KXmlGuiWindow* window); /** + * Sets up everything for KTutorial to work with the given customization. + * After setting up the customization, it loads the scripted tutorials from + * the application standard directories. + * + * @param ktutorialCustomization The customization to use. + */ + void setup(KTutorialCustomization* ktutorialCustomization); + + /** * Registers the Tutorial. * Only Tutorials with a identifier not added yet can be added. If the * Tutorial couldn't be added, false is returned. @@ -113,14 +134,11 @@ } /** - * Returns the parent widget. - * It is the main window of the application. + * Returns the main window of the application. * - * @return The parent widget. + * @return The main window of the application. */ - KXmlGuiWindow* parentWidget() const { - return mParent; - } + QWidget* mainApplicationWindow() const; /** * Returns the object with the specified name, if any. @@ -174,7 +192,7 @@ */ template <typename T> T findObject(const QString& name) const { - return mObjectFinder->findObject<T>(name, mParent); + return mObjectFinder->findObject<T>(name, mainApplicationWindow()); } private: @@ -190,62 +208,26 @@ TutorialManager* mTutorialmanager; /** - * The parent widget of the TutorialManagerDialog to show. + * The helper used to find objects. */ - KXmlGuiWindow* mParent; + ObjectFinder* mObjectFinder; /** - * The KAction to show the TutorialManagerDialog. + * The KTutorialCustomization used. */ - KAction* mTutorialsAction; + KTutorialCustomization* mCustomization; /** - * The helper used to find objects. - */ - ObjectFinder* mObjectFinder; - - /** * Creates a new KTutorial. * Private to avoid classes other than self to create instances. */ - KTutorial(): QObject(), + KTutorial(): mTutorialmanager(new TutorialManager()) { mTutorialmanager->setParent(this); - mTutorialsAction = 0; - mParent = 0; mObjectFinder = new ObjectFinder(this); + mCustomization = 0; } -private slots: - - /** - * Shows a modal TutorialManagerDialog. - * Called when the tutorials action is triggered. - */ - void showTutorialManagerDialog() const; - - /** - * Shows the GUI for the given Tutorial. - * Called when the tutorial is about to be started. - * - * @param tutorial The tutorial to show its StepWidget. - */ - void showStepWidget(Tutorial* tutorial) const; - - /** - * Disables mTutorialsAction. - * Just a wrapper to be connected with signals that can't pass false to the - * setEnabled(bool) slot. - */ - void disableTutorialsAction(); - - /** - * Enables mTutorialsAction. - * Just a wrapper to be connected with signals that can't pass true to the - * setEnabled(bool) slot. - */ - void enableTutorialsAction(); - }; } Added: trunk/ktutorial/ktutorial-library/src/KTutorialCustomization.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/KTutorialCustomization.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/KTutorialCustomization.cpp 2012-08-20 10:59:41 UTC (rev 372) @@ -0,0 +1,21 @@ +/*************************************************************************** + * Copyright (C) 2012 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 "KTutorialCustomization.h" + +#include "KTutorialCustomization.moc" Property changes on: trunk/ktutorial/ktutorial-library/src/KTutorialCustomization.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/KTutorialCustomization.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/KTutorialCustomization.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/KTutorialCustomization.h 2012-08-20 10:59:41 UTC (rev 372) @@ -0,0 +1,110 @@ +/*************************************************************************** + * Copyright (C) 2012 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 KTUTORIAL_KTUTORIALCUSTOMIZATION_H +#define KTUTORIAL_KTUTORIALCUSTOMIZATION_H + +#include <QObject> + +#include "ktutorial_export.h" + +namespace ktutorial { +class Tutorial; +class TutorialManager; +} + +namespace ktutorial { + +/** + * Interface to customize the UI of KTutorial. + * The classes that implement this interface are used by KTutorial to set up its + * user interface. You can provide your own class to KTutorial through + * KTutorial::setup(KTutorialCustomization*) to control the user interface for + * the tutorial manager and the tutorials. + * + * As this is an interface, all its methods must be implemented. + * + * Besides implementing the methods, you must provide your own classes for the + * tutorial manager and tutorial UIs. The tutorial manager UI must call the + * TutorialManager::start(QString) with the id of the tutorial to be started. If + * you need to know when the tutorial finishes (for example, to show again the + * tutorial manager UI, or to know when the tutorial UI is no longer needed), + * you can connect to the TutorialManager::finished() or the + * Tutorial::finished(Tutorial*) signals. Anyway, the tutorial UI must call + * Tutorial::finish() when the user finishes the tutorial from the UI. The + * tutorial UI must also emit the appropriate Option::selected() (connecting it + * to another signal) when the user selects an option in the UI. Finally, the + * tutorial UI must connect to the Tutorial::stepActivated(Step*) to know when + * a new step has to be shown. + * + * If you do not want to implement your own KTutorial user interface just use + * the default one for KDE through KTutorial::setup(KXmlGuiWindow*). + */ +class KTUTORIAL_EXPORT KTutorialCustomization: public QObject { +Q_OBJECT +public: + + /** + * Sets up the KTutorial user interface. + * This method will be called before loading the scripted tutorials, and + * before setting up the editor support (if available). + * + * Two things must be done in this method: preparing the user interface for + * the tutorial manager, and preparing the user interface for the tutorials. + * Note that preparing the UI for the tutorial manager also implies setting + * up a method to show the UI itself, for example, through a menu item. + * Also, to show the tutorial UI when a tutorial is started you can just + * connect the TutorialManager::started(Tutorial*) signal with the + * showTutorialUI(Tutorial*) slot in the KTutorialCustomization implementing + * class. + * + * Besides setting up the user interface, you may also register meta objects + * and tutorials here if needed. + * + * @param tutorialManager The TutorialManager used by KTutorial. + */ + virtual void setup(TutorialManager* tutorialManager) = 0; + + /** + * Returns the main window of the application. + * + * @return The main window of the application. + */ + virtual QWidget* mainApplicationWindow() = 0; + +public slots: + + /** + * Shows the tutorial UI. + * This method is used by KTutorial to show the UI of a tutorial when tested + * from the editor. + * + * Of course, it can be used when showing regular tutorials too, but it is + * the KTutorialCustomization implementing class the one which must call + * this method when needed, for example, connecting the slot to the + * started(Tutorial*) signal of the TutorialManager. + * + * @param tutorial The tutorial to show its UI. + */ + virtual void showTutorialUI(Tutorial* tutorial) = 0; + +}; + +} + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/KTutorialCustomization.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/src/Option.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/Option.h 2012-08-15 02:08:03 UTC (rev 371) +++ trunk/ktutorial/ktutorial-library/src/Option.h 2012-08-20 10:59:41 UTC (rev 372) @@ -74,8 +74,10 @@ /** * This signal is emitted when this Option is selected by the user. - * Don't connect nor emit this signal yourself. It is connected - * automatically by KTutorial. + * + * If a KTutorial customization is used, you must emit this signal + * (connecting it to another signal) when the user selects the option in the + * UI. */ void selected(); Modified: trunk/ktutorial/ktutorial-library/src/Tutorial.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/Tutorial.h 2012-08-15 02:08:03 UTC (rev 371) +++ trunk/ktutorial/ktutorial-library/src/Tutorial.h 2012-08-20 10:59:41 UTC (rev 372) @@ -151,8 +151,8 @@ * The current step is deactivated, this tutorial is cleaned, and finished * signal is emitted. * - * This slot is used internally. Do not call or connect to this slot - * yourself. + * If a KTutorial customization is used, this method must be called when the + * user finished the tutorial from the UI. */ void finish(); @@ -160,18 +160,20 @@ /** * This signal is emitted when this Tutorial finishes. - * Don't connect nor emit this signal yourself. It is connected - * automatically by KTutorial. * + * If a KTutorial customization is used, you may connect to this signal to + * know when the tutorial UI is no longer needed. + * * @param tutorial This tutorial. */ void finished(Tutorial* tutorial); /** * This signal is emitted when a Step is activated. - * Don't connect nor emit this signal yourself. It is connected - * automatically by KTutorial. * + * If a KTutorial customization is used, you must connect to this signal to + * know when the tutorial UI has to show a new step. + * * @param step The activated Step. */ void stepActivated(Step* step); Modified: trunk/ktutorial/ktutorial-library/src/TutorialManager.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/TutorialManager.h 2012-08-15 02:08:03 UTC (rev 371) +++ trunk/ktutorial/ktutorial-library/src/TutorialManager.h 2012-08-20 10:59:41 UTC (rev 372) @@ -84,6 +84,9 @@ * emitted if there is no tutorial with the specified id. In this case, no * tutorial is started. * + * If a KTutorial customization is used, this method must be called when the + * tutorial has to be started. + * * @param id The id of the tutorial. */ void start(const QString& id); Modified: trunk/ktutorial/ktutorial-library/src/WaitForWindow.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/WaitForWindow.cpp 2012-08-15 02:08:03 UTC (rev 371) +++ trunk/ktutorial/ktutorial-library/src/WaitForWindow.cpp 2012-08-20 10:59:41 UTC (rev 372) @@ -30,7 +30,7 @@ mConditionMet(false) { WindowVisibilitySpy* spy = new WindowVisibilitySpy(this); - spy->addWidgetToSpy(KTutorial::self()->parentWidget()); + spy->addWidgetToSpy(KTutorial::self()->mainApplicationWindow()); connect(spy, SIGNAL(windowShown(QWidget*)), this, SLOT(checkWindowShown(QWidget*))); } @@ -39,7 +39,7 @@ mConditionMet(false) { WindowVisibilitySpy* spy = new WindowVisibilitySpy(this); - spy->addWidgetToSpy(KTutorial::self()->parentWidget()); + spy->addWidgetToSpy(KTutorial::self()->mainApplicationWindow()); connect(spy, SIGNAL(windowShown(QWidget*)), this, SLOT(checkWindowShown(QWidget*))); Added: trunk/ktutorial/ktutorial-library/src/customization/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/customization/CMakeLists.txt (rev 0) +++ trunk/ktutorial/ktutorial-library/src/customization/CMakeLists.txt 2012-08-20 10:59:41 UTC (rev 372) @@ -0,0 +1,9 @@ +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${KDE4_INCLUDES}) + +set(ktutorial_customization_SRCS + DefaultKdeCustomization.cpp +) + +kde4_add_library(ktutorial_customization ${ktutorial_customization_SRCS}) + +target_link_libraries(ktutorial_customization ktutorial_tutorials ktutorial_view) Property changes on: trunk/ktutorial/ktutorial-library/src/customization/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/customization/DefaultKdeCustomization.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/customization/DefaultKdeCustomization.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/customization/DefaultKdeCustomization.cpp 2012-08-20 10:59:41 UTC (rev 372) @@ -0,0 +1,125 @@ +/*************************************************************************** + * Copyright (C) 2012 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 <KAction> +#include <KActionCollection> +#include <KLocalizedString> + +#include "DefaultKdeCustomization.h" +#include "../Tutorial.h" +#include "../TutorialInformation.h" +#include "../TutorialManager.h" +#include "../tutorials/UsingKTutorial.h" +#include "../view/StepWidget.h" +#include "../view/TutorialManagerDialog.h" + +using ktutorial::view::StepWidget; +using ktutorial::view::TutorialManagerDialog; + +namespace ktutorial { +namespace customization { + +//public: + +//It inherits from QObject to be automatically destroyed when the parent +//KXmlGuiWindow is destroyed. It uses the same strategy as +//KDEPrivate::ToolBarHandler. +//Usually I do not like multiple inheritance, but it comes very handy here :) +class TutorialKXmlGuiClient: public QObject, public KXMLGUIClient { +Q_OBJECT +public: + TutorialKXmlGuiClient(KXmlGuiWindow* parent): + QObject(parent), KXMLGUIClient(parent) { + setComponentData(KComponentData("ktutorial")); + setXMLFile("ktutorialui.rc"); + } +}; + +DefaultKdeCustomization::DefaultKdeCustomization(KXmlGuiWindow* window): + mWindow(window) { +} + +void DefaultKdeCustomization::setup(TutorialManager* tutorialManager) { + mTutorialManager = tutorialManager; + + TutorialKXmlGuiClient* tutorialClient= new TutorialKXmlGuiClient(mWindow); + + mTutorialsAction = new KAction(mWindow); + mTutorialsAction->setText(i18nc("@action:inmenu", "Tutorials...")); + tutorialClient->actionCollection()->addAction("tutorials", mTutorialsAction); + connect(mTutorialsAction, SIGNAL(triggered(bool)), + this, SLOT(showTutorialManagerDialog())); + + connect(mTutorialManager, SIGNAL(started(Tutorial*)), + this, SLOT(showTutorialUI(Tutorial*))); + + connect(mTutorialManager, SIGNAL(started(Tutorial*)), + this, SLOT(disableTutorialsAction())); + connect(mTutorialManager, SIGNAL(finished()), + this, SLOT(enableTutorialsAction())); + + mTutorialManager->registerTutorial(new UsingKTutorial()); +} + +QWidget* DefaultKdeCustomization::mainApplicationWindow() { + return mWindow; +} + +//public slots: + +void DefaultKdeCustomization::showTutorialUI(Tutorial* tutorial) { + QString tutorialName = tutorial->tutorialInformation()->name(); + + StepWidget* stepWidget = new StepWidget(tutorialName, mWindow); + stepWidget->setMainApplicationWindow(mWindow); + stepWidget->setObjectName("ktutorial_StepWidget"); + connect(tutorial, SIGNAL(stepActivated(Step*)), + stepWidget, SLOT(setStep(Step*))); + connect(stepWidget, SIGNAL(finished()), tutorial, SLOT(finish())); + //Invalid tutorials finish just after being started. Deleting the StepWidget + //when the tutorial finishes ensures that it is deleted in those cases and, + //as deleteLater() is used, it does not interfere with the deletion when the + //StepWidget is closed by the user. + connect(tutorial, SIGNAL(finished(Tutorial*)), + stepWidget, SLOT(deleteLater())); +} + +//private slots: + +void DefaultKdeCustomization::showTutorialManagerDialog() const { + QDialog* dialog = new TutorialManagerDialog(mTutorialManager, mWindow); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->setModal(true); + dialog->setObjectName("ktutorial_TutorialManagerDialog"); + + dialog->show(); +} + +void DefaultKdeCustomization::disableTutorialsAction() { + mTutorialsAction->setEnabled(false); +} + +void DefaultKdeCustomization::enableTutorialsAction() { + mTutorialsAction->setEnabled(true); +} + +} +} + +#include "moc_DefaultKdeCustomization.cpp" +#include "DefaultKdeCustomization.moc" Property changes on: trunk/ktutorial/ktutorial-library/src/customization/DefaultKdeCustomization.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/customization/DefaultKdeCustomization.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/customization/DefaultKdeCustomization.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/customization/DefaultKdeCustomization.h 2012-08-20 10:59:41 UTC (rev 372) @@ -0,0 +1,128 @@ +/*************************************************************************** + * Copyright (C) 2012 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 KTUTORIAL_CUSTOMIZATION_DEFAULTKDECUSTOMIZATION_H +#define KTUTORIAL_CUSTOMIZATION_DEFAULTKDECUSTOMIZATION_H + +#include <KXmlGuiWindow> + +#include "../KTutorialCustomization.h" + +class KAction; + +namespace ktutorial { +namespace customization { + +/** + * Default KTutorial customization for KDE. + * The default customization adds a "Tutorials" menu entry in the "Help" menu + * that shows a TutorialManagerDialog; the tutorial UI shown is a StepWidget. It + * also registers the default tutorial, "Using KTutorial". + * + * Note that the default customization can be used only with XMLGUI applications + * (applications where the main window is a KXmlGuiWindow). In order to add the + * menu entry in the proper place in the menu a change must be done in your + * "applicationnameui.rc". Please refer to KTutorial manual for further + * information. + */ +class DefaultKdeCustomization: public KTutorialCustomization { +Q_OBJECT +public: + + /** + * Creates a new DefaultKdeCustomization for the given window. + * + * @param window The main window of the application. + */ + DefaultKdeCustomization(KXmlGuiWindow* window); + + /** + * Sets up the KTutorial user interface. + * It adds the Tutorials action and the menu entry for it in Help menu, and + * prepares the TutorialManagerDialog and the StepWidget to be shown when + * needed. + * + * Besides setting up the user interface, the default "Using KTutorial" + * tutorial is registered. + * + * @param tutorialManager The TutorialManager used by KTutorial. + */ + virtual void setup(TutorialManager* tutorialManager); + + /** + * Returns the main window of the application. + * + * @return The main window of the application. + */ + virtual QWidget* mainApplicationWindow(); + +public slots: + + /** + * Shows the tutorial UI. + * A StepWidget is used. + * + * @param tutorial The tutorial to show its StepWidget. + */ + virtual void showTutorialUI(Tutorial* tutorial); + +private: + + /** + * The main application window. + */ + KXmlGuiWindow* mWindow; + + /** + * The manager for the tutorials. + */ + TutorialManager* mTutorialManager; + + /** + * The KAction to show the TutorialManagerDialog. + */ + KAction* mTutorialsAction; + +private slots: + + /** + * Shows a modal TutorialManagerDialog. + * Called when the tutorials action is triggered. + */ + void showTutorialManagerDialog() const; + + /** + * Disables mTutorialsAction. + * Just a wrapper to be connected with signals that can't pass false to the + * setEnabled(bool) slot. + */ + void disableTutorialsAction(); + + /** + * Enables mTutorialsAction. + * Just a wrapper to be connected with signals that can't pass true to the + * setEnabled(bool) slot. + */ + void enableTutorialsAction(); + +}; + +} +} + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/customization/DefaultKdeCustomization.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/src/scripting/ScriptingModule.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/scripting/ScriptingModule.cpp 2012-08-15 02:08:03 UTC (rev 371) +++ trunk/ktutorial/ktutorial-library/src/scripting/ScriptingModule.cpp 2012-08-20 10:59:41 UTC (rev 372) @@ -16,6 +16,8 @@ * along with this program; If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ +#include <QMetaMethod> + #include <KDebug> #include "ScriptingModule.h" Modified: trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp 2012-08-15 02:08:03 UTC (rev 371) +++ trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp 2012-08-20 10:59:41 UTC (rev 372) @@ -62,7 +62,8 @@ } virtual void setup() { - QTextEdit* textEdit = new QTextEdit(KTutorial::self()->parentWidget()); + QTextEdit* textEdit = new QTextEdit( + KTutorial::self()->mainApplicationWindow()); textEdit->setAttribute(Qt::WA_DeleteOnClose); textEdit->setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint); textEdit->setObjectName("usingKTutorialTextEdit"); Modified: trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2012-08-15 02:08:03 UTC (rev 371) +++ trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2012-08-20 10:59:41 UTC (rev 372) @@ -1,4 +1,5 @@ add_subdirectory(common) +add_subdirectory(customization) add_subdirectory(extendedinformation) add_subdirectory(scripting) add_subdirectory(view) Added: trunk/ktutorial/ktutorial-library/tests/customization/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/customization/CMakeLists.txt (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/customization/CMakeLists.txt 2012-08-20 10:59:41 UTC (rev 372) @@ -0,0 +1,31 @@ +# Used by kde4_add_unit_test to set the full path to test executables +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) + +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${ktutorial-library_SOURCE_DIR}/src/customization ${KDE4_INCLUDES}) + +# Since Qt 4.6.0, this definition is needed for GUI testing. +# It is backwards compatible with previous Qt versions, unlike the alternative +# which is to add #include <QTestGui> in the test files. +add_definitions(-DQT_GUI_LIB) + +MACRO(UNIT_TESTS) + FOREACH(_className ${ARGN}) + set(_testName ${_className}Test) + kde4_add_unit_test(${_testName} TESTNAME ktutorial-${_testName} ${_testName}.cpp) + target_link_libraries(${_testName} ktutorial ktutorial_customization ${QT_QTTEST_LIBRARY}) + ENDFOREACH(_className) +ENDMACRO(UNIT_TESTS) + +unit_tests( + DefaultKdeCustomization +) + +MACRO(MEM_TESTS) + FOREACH(_testname ${ARGN}) + add_test(ktutorial-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( + DefaultKdeCustomization +) Property changes on: trunk/ktutorial/ktutorial-library/tests/customization/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/tests/customization/DefaultKdeCustomizationTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/customization/DefaultKdeCustomizationTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/customization/DefaultKdeCustomizationTest.cpp 2012-08-20 10:59:41 UTC (rev 372) @@ -0,0 +1,154 @@ +/*************************************************************************** + * Copyright (C) 2012 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 <KAction> +#include <KActionCollection> + +#include "DefaultKdeCustomization.h" +#include "../Step.h" +#include "../Tutorial.h" +#include "../TutorialInformation.h" +#include "../TutorialManager.h" +#include "../view/StepTextWidget.h" +#include "../view/StepWidget.h" +#include "../view/TutorialManagerDialog.h" + +using ktutorial::Step; +using ktutorial::Tutorial; +using ktutorial::TutorialInformation; +using ktutorial::view::StepTextWidget; +using ktutorial::view::StepWidget; +using ktutorial::view::TutorialManagerDialog; + +namespace ktutorial { +namespace customization { + +class DefaultKdeCustomizationTest: public QObject { +Q_OBJECT +private slots: + + void initTestCase(); + + void testSetup(); + + void testMainApplicationWindow(); + + void testShowTutorialUI(); + + void testShowTutorialManagerUsingAction(); + +}; + +void DefaultKdeCustomizationTest::initTestCase() { + //Set the application name to prevent a QtDBus assert complaining that + //"Invalid object path given", as KXmlGuiWindow seems to register itself in + //DBus. + QApplication::setApplicationName("DefaultKdeCustomizationTest"); +} + +void DefaultKdeCustomizationTest::testSetup() { + KXmlGuiWindow window; + DefaultKdeCustomization customization(&window); + TutorialManager tutorialManager; + + QVERIFY(!window.actionCollection()->action("tutorials")); + QCOMPARE(tutorialManager.tutorialInformations().size(), 0); + + customization.setup(&tutorialManager); + + QAction* action = window.findChild<QAction*>("tutorials"); + QVERIFY(action); + QVERIFY(action->isEnabled()); + + QCOMPARE(tutorialManager.tutorialInformations().size(), 1); + QCOMPARE(tutorialManager.tutorialInformations()[0]->id(), + QString("usingKTutorial")); +} + +void DefaultKdeCustomizationTest::testMainApplicationWindow() { + KXmlGuiWindow window; + DefaultKdeCustomization customization(&window); + + QCOMPARE(customization.mainApplicationWindow(), &window); +} + +void DefaultKdeCustomizationTest::testShowTutorialUI() { + KXmlGuiWindow window; + DefaultKdeCustomization customization(&window); + + Tutorial tutorial(new TutorialInformation("test")); + + //Tutorial* must be registered in order to be used with QSignalSpy + qRegisterMetaType<Tutorial*>("Tutorial*"); + QSignalSpy finishedSpy(&tutorial, SIGNAL(finished(Tutorial*))); + + Step* startStep = new Step("start"); + startStep->setText("Start step"); + tutorial.addStep(startStep); + + Step* endStep = new Step("end"); + endStep->setText("End step"); + tutorial.addStep(endStep); + + customization.showTutorialUI(&tutorial); + tutorial.start(); + + StepWidget* stepWidget = window.findChild<StepWidget*>(); + QVERIFY(stepWidget); + + StepTextWidget* stepTextWidget = stepWidget->findChild<StepTextWidget*>(); + QVERIFY(stepTextWidget); + QCOMPARE(stepTextWidget->toPlainText(), QString("Start step")); + + tutorial.nextStep("end"); + + QCOMPARE(stepTextWidget->toPlainText(), QString("End step")); + + QCOMPARE(finishedSpy.count(), 0); + + stepWidget->close(); + + QCOMPARE(finishedSpy.count(), 1); +} + +void DefaultKdeCustomizationTest::testShowTutorialManagerUsingAction() { + KXmlGuiWindow window; + DefaultKdeCustomization customization(&window); + TutorialManager tutorialManager; + customization.setup(&tutorialManager); + + QAction* action = window.findChild<QAction*>("tutorials"); + QVERIFY(action); + QVERIFY(action->isEnabled()); + + action->trigger(); + TutorialManagerDialog* dialog = window.findChild<TutorialManagerDialog*>(); + + QVERIFY(dialog); + QVERIFY(dialog->isModal()); + QVERIFY(dialog->testAttribute(Qt::WA_DeleteOnClose)); +} + +} +} + +QTEST_MAIN(ktutorial::customization::DefaultKdeCustomizationTest) + +#include "DefaultKdeCustomizationTest.moc" Property changes on: trunk/ktutorial/ktutorial-library/tests/customization/DefaultKdeCustomizationTest.cpp ___________________________________________________________________ Added: svn:eol-style + native This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-02-24 02:24:10
|
Revision: 101 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=101&view=rev Author: danxuliu Date: 2010-02-24 02:24:04 +0000 (Wed, 24 Feb 2010) Log Message: ----------- WaitFors and Options added in the setup() method of a Step derived class are now removed and deleted automatically when the step is deactivated. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/Step.cpp trunk/ktutorial/ktutorial-library/src/Step.h trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp trunk/ktutorial/ktutorial-library/test/StepTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/Step.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/Step.cpp 2010-02-23 18:33:39 UTC (rev 100) +++ trunk/ktutorial/ktutorial-library/src/Step.cpp 2010-02-24 02:24:04 UTC (rev 101) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008-2009 by Daniel Calviño Sánchez * + * Copyright (C) 2008-2010 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -27,9 +27,9 @@ void Step::setActive(bool active) { if (active) { - setup(); + setupWrapper(); } else { - tearDown(); + tearDownWrapper(); } mActive = active; @@ -58,10 +58,21 @@ mOptions.append(option); + + bool deleteAddedObjectsInTearDownValue = mDeleteAddedObjectsInTearDown; + mDeleteAddedObjectsInTearDown = false; + WaitForSignal* waitFor = new WaitForSignal(option, SIGNAL(selected())); addWaitFor(waitFor, receiver, slot); mOptionsWaitsFor.append(waitFor); + + mDeleteAddedObjectsInTearDown = deleteAddedObjectsInTearDownValue; + + + if (mDeleteAddedObjectsInTearDown) { + mOptionsToBeDeletedInTearDown.append(option); + } } void Step::addWaitFor(WaitFor* waitFor, QObject* receiver, const QString& slot) { @@ -76,6 +87,10 @@ mWaitsFor.append(waitFor); connectWaitFor(waitFor, receiver, slot); + + if (mDeleteAddedObjectsInTearDown) { + mWaitsForToBeDeletedInTearDown.append(waitFor); + } } void Step::removeOption(Option* option) { @@ -126,3 +141,27 @@ void Step::disconnectWaitFor(WaitFor* waitFor) { disconnect(waitFor, SIGNAL(waitEnded(WaitFor*)), 0, 0); } + +//private: + +void Step::setupWrapper() { + mDeleteAddedObjectsInTearDown = true; + setup(); + mDeleteAddedObjectsInTearDown = false; +} + +void Step::tearDownWrapper() { + tearDown(); + + foreach (Option* option, mOptionsToBeDeletedInTearDown) { + removeOption(option); + delete option; + } + mOptionsToBeDeletedInTearDown.clear(); + + foreach (WaitFor* waitFor, mWaitsForToBeDeletedInTearDown) { + removeWaitFor(waitFor); + delete waitFor; + } + mWaitsForToBeDeletedInTearDown.clear(); +} Modified: trunk/ktutorial/ktutorial-library/src/Step.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/Step.h 2010-02-23 18:33:39 UTC (rev 100) +++ trunk/ktutorial/ktutorial-library/src/Step.h 2010-02-24 02:24:04 UTC (rev 101) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008-2009 by Daniel Calviño Sánchez * + * Copyright (C) 2008-2010 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -105,8 +105,9 @@ * This may be needed, for example, when a Step needs to use an object that * didn't exist when the Tutorial was created (like something in a dialog * deleted every time it is closed). In this case, a subclass of Step should be - * made with setup() and tearDown() methods adding and removing the WaitFor for - * the "volatile" object. + * made with a setup() method adding the WaitFor for the "volatile" object. + * Note, however, that you don't have to remove nor delete the WaitFor added in + * the setup() method. It is done automatically when the step is deactivated. * * \code * void StepSubclass::setup() { @@ -115,11 +116,6 @@ * waitForAttribute = new WaitForSignal(textArea, SIGNAL(textChanged())); * addWaitFor(waitForAttribute, this, SLOT(startDone())); * } - * - * void StepSubclass::tearDown() { - * removeWaitFor(waitForAttribute); - * delete waitForAttribute; - * } * \endcode */ class KTUTORIAL_EXPORT Step: public QObject { @@ -134,7 +130,8 @@ */ explicit Step(const QString& id): QObject(), mId(id), - mActive(false) { + mActive(false), + mDeleteAddedObjectsInTearDown(false) { } /** @@ -202,7 +199,10 @@ * Note that the slot name can be set with or without using the SLOT macro. * * The Option is reparented to this Step, and thus deleted when this Step - * is deleted. + * is deleted. However, if the Option is added in the setup() method of a + * Step derived class, the Option is automatically removed and deleted when + * the Step is deactivated (that is, you don't have to do it explicitly in + * the tearDown() method). * * If you try to add the same Option (or different ones with the same name) * twice, nothing will happen. Only the Option added in first place and its @@ -224,7 +224,10 @@ * Note that the slot name can be set with or without using the SLOT macro. * * The WaitFor is reparented to this Step, and thus deleted when this Step - * is deleted. + * is deleted. However, if the WaitFor is added in the setup() method of a + * Step derived class, the WaitFor is automatically removed and deleted when + * the Step is deactivated (that is, you don't have to do it explicitly in + * the tearDown() method). * * If you try to add the same WaitFor twice, nothing will happen. Only the * WaitFor added in first place and its associated slot will be taken in @@ -328,11 +331,22 @@ bool mActive; /** + * When this flag is on, the conditions and conditions to wait for added are + * removed and deleted the next time this Step is deactivated. + */ + bool mDeleteAddedObjectsInTearDown; + + /** * The Options for this Step. */ QList<Option*> mOptions; /** + * The Options added in the setup to be deleted in the tearDown. + */ + QList<Option*> mOptionsToBeDeletedInTearDown; + + /** * The conditions to wait for in each Option. * The order of both lists is the same, so the index in the Options list * is the index of it associated WaitFor. @@ -344,6 +358,26 @@ */ QList<WaitFor*> mWaitsFor; + /** + * The conditions to wait for added in the setup to be deleted in the + * tearDown. + */ + QList<WaitFor*> mWaitsForToBeDeletedInTearDown; + + /** + * Wraps setup method to ensure that some code is executed before and after + * inherited setup method. + * It follows a Template Design Pattern. + */ + void setupWrapper(); + + /** + * Wraps tearDown method to ensure that some code is executed before and + * after inherited tearDown method. + * It follows a Template Design Pattern. + */ + void tearDownWrapper(); + }; #endif Modified: trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp 2010-02-23 18:33:39 UTC (rev 100) +++ trunk/ktutorial/ktutorial-library/src/tutorials/UsingKTutorial.cpp 2010-02-24 02:24:04 UTC (rev 101) @@ -47,21 +47,13 @@ "Look at me! I am the text area!")); textEdit->show(); - mWaitForTextChanged = new WaitForSignal(textEdit, - SIGNAL(textChanged())); - addWaitFor(mWaitForTextChanged, + addWaitFor(new WaitForSignal(textEdit, SIGNAL(textChanged())), mUsingKTutorial, SLOT(clearTextTextModified())); } - virtual void tearDown() { - removeWaitFor(mWaitForTextChanged); - delete mWaitForTextChanged; - } - private: UsingKTutorial* mUsingKTutorial; - WaitFor* mWaitForTextChanged; }; @@ -81,17 +73,10 @@ //is closed. textEdit->installEventFilter(this); - mWaitForWidgetClosed = new WaitForSignal(this, - SIGNAL(textEditClosed())); - addWaitFor(mWaitForWidgetClosed, + addWaitFor(new WaitForSignal(this, SIGNAL(textEditClosed())), mUsingKTutorial, SLOT(closeTextEditDone())); } - virtual void tearDown() { - removeWaitFor(mWaitForWidgetClosed); - delete mWaitForWidgetClosed; - } - bool eventFilter(QObject* object, QEvent* event) { Q_UNUSED(object); if (event->type() == QEvent::Close) { @@ -108,7 +93,6 @@ private: UsingKTutorial* mUsingKTutorial; - WaitFor* mWaitForWidgetClosed; }; @@ -128,17 +112,10 @@ //tutorial is closed. mStepWidget->installEventFilter(this); - mWaitForMousePressed = new WaitForSignal(this, - SIGNAL(mousePressedOnWidget())); - addWaitFor(mWaitForMousePressed, + addWaitFor(new WaitForSignal(this, SIGNAL(mousePressedOnWidget())), mUsingKTutorial, SLOT(moveWidgetPressDone())); } - virtual void tearDown() { - removeWaitFor(mWaitForMousePressed); - delete mWaitForMousePressed; - } - bool eventFilter(QObject* object, QEvent* event) { Q_UNUSED(object); if (event->type() == QEvent::MouseButtonPress) { @@ -158,7 +135,6 @@ private: UsingKTutorial* mUsingKTutorial; - WaitFor* mWaitForMousePressed; }; @@ -178,17 +154,10 @@ //tutorial is closed. mStepWidget->installEventFilter(this); - mWaitForMouseReleased = new WaitForSignal(this, - SIGNAL(mouseReleasedOnWidget())); - addWaitFor(mWaitForMouseReleased, + addWaitFor(new WaitForSignal(this, SIGNAL(mouseReleasedOnWidget())), mUsingKTutorial, SLOT(moveWidgetReleaseDone())); } - virtual void tearDown() { - removeWaitFor(mWaitForMouseReleased); - delete mWaitForMouseReleased; - } - bool eventFilter(QObject* object, QEvent* event) { Q_UNUSED(object); if (event->type() == QEvent::MouseButtonRelease) { @@ -205,7 +174,6 @@ private: UsingKTutorial* mUsingKTutorial; - WaitFor* mWaitForMouseReleased; }; Modified: trunk/ktutorial/ktutorial-library/test/StepTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/test/StepTest.cpp 2010-02-23 18:33:39 UTC (rev 100) +++ trunk/ktutorial/ktutorial-library/test/StepTest.cpp 2010-02-24 02:24:04 UTC (rev 101) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008-2009 by Daniel Calviño Sánchez * + * Copyright (C) 2008-2010 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -70,12 +70,14 @@ void testAddOption(); void testAddOptionWithoutSlotMacro(); void testAddOptionSeveralOptions(); + void testAddOptionDuringSetup(); void testAddOptionTwice(); void testAddOptionDifferentOptionsWithSameName(); void testAddWaitFor(); void testAddWaitForWithoutSlotMacro(); void testAddWaitForSeveralWaitFors(); + void testAddWaitForDuringSetup(); void testAddWaitForTwice(); void testRemoveOption(); @@ -109,6 +111,48 @@ }; +class StepWithWaitForAddedInSetup: public Step { +public: + + WaitFor* mWaitFor; + StepTest* mStepTest; + + StepWithWaitForAddedInSetup(const QString& id, StepTest* stepTest): + Step(id), + mStepTest(stepTest), + mWaitFor(0) { + } + +protected: + + virtual void setup() { + mWaitFor = new WaitForSignal(mStepTest, SIGNAL(dummySignal())); + addWaitFor(mWaitFor, mStepTest, SLOT(dummySlot())); + } + +}; + +class StepWithOptionAddedInSetup: public Step { +public: + + Option* mOption; + StepTest* mStepTest; + + StepWithOptionAddedInSetup(const QString& id, StepTest* stepTest): + Step(id), + mStepTest(stepTest), + mOption(0) { + } + +protected: + + virtual void setup() { + mOption = new Option("Bathe your iguana"); + addOption(mOption, mStepTest, SLOT(dummySlot())); + } + +}; + void StepTest::testConstructor() { InspectedStep step("doSomethingConstructive"); @@ -226,6 +270,31 @@ QCOMPARE(mDummySlotCallCount, 3); } +void StepTest::testAddOptionDuringSetup() { + StepWithOptionAddedInSetup step("doSomethingConstructive", this); + + step.setActive(true); + + connect(this, SIGNAL(dummySignal()), step.mOption, SIGNAL(selected())); + + QCOMPARE(step.mOption->parent(), &step); + QCOMPARE(step.options().count(), 1); + QVERIFY(step.options().contains(step.mOption)); + QCOMPARE(mDummySlotCallCount, 0); + + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + + QSignalSpy destroyedSpy(step.mOption, SIGNAL(destroyed(QObject*))); + + step.setActive(false); + + QCOMPARE(step.options().count(), 0); + QCOMPARE(step.mWaitsForToBeDeletedInTearDown.count(), 0); + QCOMPARE(step.mOptionsToBeDeletedInTearDown.count(), 0); + QCOMPARE(destroyedSpy.count(), 1); +} + void StepTest::testAddOptionTwice() { Step step("doSomethingConstructive"); @@ -361,6 +430,29 @@ QCOMPARE(mDummySlotCallCount, 3); } +void StepTest::testAddWaitForDuringSetup() { + StepWithWaitForAddedInSetup step("doSomethingConstructive", this); + + step.setActive(true); + + QCOMPARE(step.mWaitFor->parent(), &step); + QCOMPARE(step.mWaitsFor.count(), 1); + QVERIFY(step.mWaitsFor.contains(step.mWaitFor)); + QCOMPARE(mDummySlotCallCount, 0); + + emit dummySignal(); + QCOMPARE(mDummySlotCallCount, 1); + + QSignalSpy destroyedSpy(step.mWaitFor, SIGNAL(destroyed(QObject*))); + + step.setActive(false); + + QCOMPARE(step.mWaitsFor.count(), 0); + QCOMPARE(step.mWaitsForToBeDeletedInTearDown.count(), 0); + QCOMPARE(step.mOptionsToBeDeletedInTearDown.count(), 0); + QCOMPARE(destroyedSpy.count(), 1); +} + void StepTest::testAddWaitForTwice() { Step step("doSomethingConstructive"); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dan...@us...> - 2010-09-19 16:40:41
|
Revision: 249 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=249&view=rev Author: danxuliu Date: 2010-09-19 16:40:33 +0000 (Sun, 19 Sep 2010) Log Message: ----------- Fix StepWidget being blocked when a modal dialog is shown: now when a modal dialog is shown, the StepWidget is reparented to the modal dialog so it is no longer blocked, and when the modal dialog is closed, the StepWidget is reparented back to its previous parent. Modified Paths: -------------- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/KTutorial.cpp trunk/ktutorial/ktutorial-library/src/view/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/view/StepWidget.cpp trunk/ktutorial/ktutorial-library/src/view/StepWidget.h trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/view/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/view/StepWidgetTest.cpp Added Paths: ----------- trunk/ktutorial/ktutorial-library/src/common/ trunk/ktutorial/ktutorial-library/src/common/CMakeLists.txt trunk/ktutorial/ktutorial-library/src/common/WindowVisibilitySpy.cpp trunk/ktutorial/ktutorial-library/src/common/WindowVisibilitySpy.h trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.cpp trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.h trunk/ktutorial/ktutorial-library/tests/common/ trunk/ktutorial/ktutorial-library/tests/common/CMakeLists.txt trunk/ktutorial/ktutorial-library/tests/common/WindowVisibilitySpyTest.cpp trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp Modified: trunk/ktutorial/ktutorial-library/src/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2010-08-14 17:30:49 UTC (rev 248) +++ trunk/ktutorial/ktutorial-library/src/CMakeLists.txt 2010-09-19 16:40:33 UTC (rev 249) @@ -2,6 +2,7 @@ # In order to work, they must be compiled using -fPIC add_definitions("-fPIC") +add_subdirectory(common) add_subdirectory(extendedinformation) add_subdirectory(scripting) add_subdirectory(tutorials) Modified: trunk/ktutorial/ktutorial-library/src/KTutorial.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/KTutorial.cpp 2010-08-14 17:30:49 UTC (rev 248) +++ trunk/ktutorial/ktutorial-library/src/KTutorial.cpp 2010-09-19 16:40:33 UTC (rev 249) @@ -105,6 +105,7 @@ QString tutorialName = tutorial->tutorialInformation()->name(); StepWidget* stepWidget = new StepWidget(tutorialName, mParent); + stepWidget->setMainApplicationWindow(mParent); stepWidget->setObjectName("ktutorial_StepWidget"); connect(tutorial, SIGNAL(stepActivated(Step*)), stepWidget, SLOT(setStep(Step*))); Added: trunk/ktutorial/ktutorial-library/src/common/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/common/CMakeLists.txt (rev 0) +++ trunk/ktutorial/ktutorial-library/src/common/CMakeLists.txt 2010-09-19 16:40:33 UTC (rev 249) @@ -0,0 +1,9 @@ +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${KDE4_INCLUDES}) + +set(ktutorial_common_SRCS + WindowVisibilitySpy.cpp +) + +kde4_add_library(ktutorial_common ${ktutorial_common_SRCS}) + +target_link_libraries(ktutorial_common ${KDE4_KDEUI_LIBS}) Property changes on: trunk/ktutorial/ktutorial-library/src/common/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/common/WindowVisibilitySpy.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/common/WindowVisibilitySpy.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/common/WindowVisibilitySpy.cpp 2010-09-19 16:40:33 UTC (rev 249) @@ -0,0 +1,72 @@ +/*************************************************************************** + * 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 <QEvent> +#include <QWidget> + +#include "WindowVisibilitySpy.h" + +namespace common { + +//public: + +WindowVisibilitySpy::WindowVisibilitySpy(QObject* parent /*= 0*/): + QObject(parent) { +} + +void WindowVisibilitySpy::addWidgetToSpy(QWidget* widget) { + Q_ASSERT(widget); + + widget->installEventFilter(this); + + foreach (QObject* childObject, widget->children()) { + if (childObject->isWidgetType()) { + addWidgetToSpy(static_cast<QWidget*>(childObject)); + } + } +} + +//protected: + +bool WindowVisibilitySpy::eventFilter(QObject* object, QEvent* event) { + if (event->type() == QEvent::ChildAdded) { + QChildEvent* childEvent = static_cast<QChildEvent*>(event); + if (childEvent->child()->isWidgetType()) { + addWidgetToSpy(static_cast<QWidget*>(childEvent->child())); + } + return false; + } + + QWidget* widget = static_cast<QWidget*>(object); + + if (!(widget->windowFlags() & (Qt::Window | Qt::Dialog))) { + return false; + } + + if (event->type() == QEvent::Show) { + emit windowShown(widget); + } else if (event->type() == QEvent::Hide) { + emit windowHidden(widget); + } + + return false; +} + +} + +#include "WindowVisibilitySpy.moc" Property changes on: trunk/ktutorial/ktutorial-library/src/common/WindowVisibilitySpy.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/common/WindowVisibilitySpy.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/common/WindowVisibilitySpy.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/common/WindowVisibilitySpy.h 2010-09-19 16:40:33 UTC (rev 249) @@ -0,0 +1,89 @@ +/*************************************************************************** + * 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 COMMON_WINDOWVISIBILITYSPY_H +#define COMMON_WINDOWVISIBILITYSPY_H + +#include <QObject> + +namespace common { + +/** + * Spy to know when windows from a widget hierarchy are shown or hidden. + * WindowVisibilitySpy emitts windowShown(QWidget*) whenever any of the spied + * widgets or its child widgets (recursively) are shown as a window. The signal + * windowHidden(QWidget*) is emitted when any of them is hidden. The signals are + * emitted for windows but also for dialogs. + * + * Children added to a spied widget after it was added to the + * WindowVisibilitySpy are also automatically spied. + */ +class WindowVisibilitySpy: public QObject { +Q_OBJECT +public: + + /** + * Creates a new WindowVisibilitySpy with the given parent. + * + * @param parent The parent QObject. + */ + explicit WindowVisibilitySpy(QObject* parent = 0); + + /** + * Add widget and all its child widgets to spy. + * + * @param widget The widget to spy. + */ + void addWidgetToSpy(QWidget* widget); + +Q_SIGNALS: + + /** + * Emitted when any of the spied widgets or its children is shown as a + * window. + * + * @param window The widget shown. + */ + void windowShown(QWidget* window); + + /** + * Emitted when any of the spied widgets or its children is hidden (and it + * was a window). + * + * @param window The widget hidden. + */ + void windowHidden(QWidget* window); + +protected: + + /** + * Filters the events received in the spied widget hierarchies. + * A windowShown(QWidget*) is emitted when a window is shown, and a + * windowHidden(QWidget*) is emitted when a window is hidden. + * + * @param object The widget that received the event. + * @param event The event received. + * @return False, to let the events be handled as necessary. + */ + virtual bool eventFilter(QObject* object, QEvent* event); + +}; + +} + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/common/WindowVisibilitySpy.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/src/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/CMakeLists.txt 2010-08-14 17:30:49 UTC (rev 248) +++ trunk/ktutorial/ktutorial-library/src/view/CMakeLists.txt 2010-09-19 16:40:33 UTC (rev 249) @@ -5,6 +5,7 @@ StepWidget.cpp TutorialListModel.cpp TutorialManagerDialog.cpp + WindowOnTopEnforcer.cpp ) kde4_add_ui_files(ktutorial_view_SRCS @@ -14,4 +15,8 @@ kde4_add_library(ktutorial_view ${ktutorial_view_SRCS}) -target_link_libraries(ktutorial_view ktutorial_extendedinformation ${KDE4_KDEUI_LIBS}) +target_link_libraries(ktutorial_view + ktutorial_common + ktutorial_extendedinformation + ${KDE4_KDEUI_LIBS} +) Modified: trunk/ktutorial/ktutorial-library/src/view/StepWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepWidget.cpp 2010-08-14 17:30:49 UTC (rev 248) +++ trunk/ktutorial/ktutorial-library/src/view/StepWidget.cpp 2010-09-19 16:40:33 UTC (rev 249) @@ -25,6 +25,7 @@ #include "StepWidget.h" #include "ui_StepWidget.h" +#include "WindowOnTopEnforcer.h" #include "../Option.h" #include "../Step.h" @@ -62,6 +63,11 @@ delete ui; } +void StepWidget::setMainApplicationWindow(QWidget* mainApplicationWindow) { + WindowOnTopEnforcer* windowOnTopEnforcer = new WindowOnTopEnforcer(this); + windowOnTopEnforcer->setBaseWindow(mainApplicationWindow); +} + //public slots: void StepWidget::setStep(Step* step) { Modified: trunk/ktutorial/ktutorial-library/src/view/StepWidget.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/StepWidget.h 2010-08-14 17:30:49 UTC (rev 248) +++ trunk/ktutorial/ktutorial-library/src/view/StepWidget.h 2010-09-19 16:40:33 UTC (rev 249) @@ -69,6 +69,16 @@ */ virtual ~StepWidget(); + /** + * Sets the main application window to spy for child dialogs. + * When a modal dialog is shown, this StepWidget will be reparented to the + * dialog to avoid being blocked by it. The previous parent will be restored + * when the modal dialog is hidden. + * + * @param mainApplicationWindow The main application window. + */ + void setMainApplicationWindow(QWidget* mainApplicationWindow); + public slots: /** Added: trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.cpp 2010-09-19 16:40:33 UTC (rev 249) @@ -0,0 +1,85 @@ +/*************************************************************************** + * 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 <QWidget> + +#include "WindowOnTopEnforcer.h" +#include "../common/WindowVisibilitySpy.h" + +using common::WindowVisibilitySpy; + +namespace view { + +//public: + +WindowOnTopEnforcer::WindowOnTopEnforcer(QWidget* widget): QObject(widget), + mWidgetToKeepOnTop(widget) { + + Q_ASSERT(widget); + + mParentStack.push(mWidgetToKeepOnTop->parentWidget()); +} + +void WindowOnTopEnforcer::setBaseWindow(QWidget* baseWindow) { + Q_ASSERT(baseWindow); + + WindowVisibilitySpy* spy = new WindowVisibilitySpy(this); + spy->addWidgetToSpy(baseWindow); + connect(spy, SIGNAL(windowShown(QWidget*)), + this, SLOT(handleWindowShown(QWidget*))); + connect(spy, SIGNAL(windowHidden(QWidget*)), + this, SLOT(handleWindowHidden(QWidget*))); +} + +//private slots: + +void WindowOnTopEnforcer::handleWindowShown(QWidget* window) { + Q_ASSERT(window); + + if (!window->isModal()) { + return; + } + + mParentStack.push(window); + + //When a widget is reparented it is hidden and its window flags are cleared, + //so they must be restored and the widget shown again + Qt::WindowFlags flags = mWidgetToKeepOnTop->windowFlags(); + mWidgetToKeepOnTop->setParent(window); + mWidgetToKeepOnTop->setWindowFlags(flags); + mWidgetToKeepOnTop->show(); +} + +void WindowOnTopEnforcer::handleWindowHidden(QWidget* window) { + Q_ASSERT(window); + + if (!window->isModal()) { + return; + } + + mParentStack.pop(); + + //When a widget is reparented it is hidden and its window flags are cleared + //so they must be restored and the widget shown again + Qt::WindowFlags flags = mWidgetToKeepOnTop->windowFlags(); + mWidgetToKeepOnTop->setParent(mParentStack.top()); + mWidgetToKeepOnTop->setWindowFlags(flags); + mWidgetToKeepOnTop->show(); +} + +} Property changes on: trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.h =================================================================== --- trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.h (rev 0) +++ trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.h 2010-09-19 16:40:33 UTC (rev 249) @@ -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 VIEW_WINDOWONTOPENFORCER_H +#define VIEW_WINDOWONTOPENFORCER_H + +#include <QObject> +#include <QStack> + +namespace common { +class WindowVisibilitySpy; +} + +namespace view { + +/** + * Utility class to avoid windows being blocked by modal dialogs. + * When a modal dialog is shown, the widget to keep on top is reparented to the + * shown dialog. When the modal dialog is hidden, the widget is reparented to + * its previous parent. + */ +class WindowOnTopEnforcer: public QObject { +Q_OBJECT +public: + + /** + * Creates a new WindowOnTopEnforcer with the widget to keep on top. + * The widget to keep on top is set as the parent of the + * WindowOnTopEnforcer. + * + * @param widget The widget to keep on top. + */ + explicit WindowOnTopEnforcer(QWidget* widget); + + /** + * Sets the base window to spy for its children dialogs. + * + * @param baseWindow The window to spy. + */ + void setBaseWindow(QWidget* baseWindow); + +private: + + /** + * The widget to keep on top. + */ + QWidget* mWidgetToKeepOnTop; + + /** + * A stack with the parents of the widget to keep on top. + * It is used to restore the previous parent when the latest one is hidden. + */ + QStack<QWidget*> mParentStack; + +private Q_SLOTS: + + /** + * Reparents the widget to keep on top to the window if it is a modal + * dialog. + * + * @param window The window that has been shown. + */ + void handleWindowShown(QWidget* window); + + /** + * Reparents the widget to keep on top to its previous parent if the hidden + * window is a modal dialog. + * + * @param window The window that has been hidden. + */ + void handleWindowHidden(QWidget* window); + +}; + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-library/src/view/WindowOnTopEnforcer.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2010-08-14 17:30:49 UTC (rev 248) +++ trunk/ktutorial/ktutorial-library/tests/CMakeLists.txt 2010-09-19 16:40:33 UTC (rev 249) @@ -1,3 +1,4 @@ +add_subdirectory(common) add_subdirectory(extendedinformation) add_subdirectory(scripting) add_subdirectory(view) Added: trunk/ktutorial/ktutorial-library/tests/common/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/common/CMakeLists.txt (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/common/CMakeLists.txt 2010-09-19 16:40:33 UTC (rev 249) @@ -0,0 +1,31 @@ +# Used by kde4_add_unit_test to set the full path to test executables +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) + +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${ktutorial-library_SOURCE_DIR}/src/common ${KDE4_INCLUDES}) + +# Since Qt 4.6.0, this definition is needed for GUI testing. +# It is backwards compatible with previous Qt versions, unlike the alternative +# which is to add #include <QTestGui> in the test files. +add_definitions(-DQT_GUI_LIB) + +MACRO(UNIT_TESTS) + FOREACH(_className ${ARGN}) + set(_testName ${_className}Test) + kde4_add_unit_test(${_testName} TESTNAME ktutorial-${_testName} ${_testName}.cpp) + target_link_libraries(${_testName} ktutorial_common ${QT_QTTEST_LIBRARY}) + ENDFOREACH(_className) +ENDMACRO(UNIT_TESTS) + +unit_tests( + WindowVisibilitySpy +) + +MACRO(MEM_TESTS) + FOREACH(_testname ${ARGN}) + add_test(ktutorial-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( + WindowVisibilitySpy +) Property changes on: trunk/ktutorial/ktutorial-library/tests/common/CMakeLists.txt ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-library/tests/common/WindowVisibilitySpyTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/common/WindowVisibilitySpyTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/common/WindowVisibilitySpyTest.cpp 2010-09-19 16:40:33 UTC (rev 249) @@ -0,0 +1,200 @@ +/*************************************************************************** + * 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 <QWidget> + +#include "WindowVisibilitySpy.h" + +namespace common { + +class WindowVisibilitySpyTest: public QObject { +Q_OBJECT +private slots: + + void testConstructor(); + + void testShowWidgetAsWindow(); + void testHideWidgetAsWindow(); + void testDeleteWidgetShownAsWindow(); + void testShowWidgetAsWidget(); + void testHideWidgetAsWidget(); + void testDeleteWidgetShownAsWidget(); + + void testShowChildWidgetAddedBeforeSpy(); + void testShowChildWidgetAddedAfterSpy(); + +}; + +void WindowVisibilitySpyTest::testConstructor() { + QObject parent; + WindowVisibilitySpy* spy = new WindowVisibilitySpy(&parent); + + QCOMPARE(spy->parent(), &parent); +} + +void WindowVisibilitySpyTest::testShowWidgetAsWindow() { + QWidget widget; + widget.setWindowFlags(Qt::Window); + + WindowVisibilitySpy spy; + spy.addWidgetToSpy(&widget); + + QSignalSpy shownSpy(&spy, SIGNAL(windowShown(QWidget*))); + + widget.show(); + + QCOMPARE(shownSpy.count(), 1); + QVariant argument = shownSpy.at(0).at(0); + QCOMPARE(argument.userType(), (int)QMetaType::QWidgetStar); + QCOMPARE(qvariant_cast<QWidget*>(argument), &widget); +} + +void WindowVisibilitySpyTest::testHideWidgetAsWindow() { + QWidget widget; + widget.setWindowFlags(Qt::Window); + widget.show(); + + WindowVisibilitySpy spy; + spy.addWidgetToSpy(&widget); + + QSignalSpy hiddenSpy(&spy, SIGNAL(windowHidden(QWidget*))); + + widget.hide(); + + QCOMPARE(hiddenSpy.count(), 1); + QVariant argument = hiddenSpy.at(0).at(0); + QCOMPARE(argument.userType(), (int)QMetaType::QWidgetStar); + QCOMPARE(qvariant_cast<QWidget*>(argument), &widget); +} + +void WindowVisibilitySpyTest::testDeleteWidgetShownAsWindow() { + QWidget* widget = new QWidget(); + widget->setWindowFlags(Qt::Window); + widget->show(); + + WindowVisibilitySpy spy; + spy.addWidgetToSpy(widget); + + QSignalSpy hiddenSpy(&spy, SIGNAL(windowHidden(QWidget*))); + + delete widget; + + QCOMPARE(hiddenSpy.count(), 1); + QVariant argument = hiddenSpy.at(0).at(0); + QCOMPARE(argument.userType(), (int)QMetaType::QWidgetStar); + QCOMPARE(qvariant_cast<QWidget*>(argument), widget); +} + +void WindowVisibilitySpyTest::testShowWidgetAsWidget() { + QWidget window; + window.show(); + + QWidget* widget = new QWidget(&window); + + WindowVisibilitySpy spy; + spy.addWidgetToSpy(widget); + + QSignalSpy shownSpy(&spy, SIGNAL(windowShown(QWidget*))); + + widget->show(); + + QCOMPARE(shownSpy.count(), 0); +} + +void WindowVisibilitySpyTest::testHideWidgetAsWidget() { + QWidget window; + window.show(); + + QWidget* widget = new QWidget(&window); + widget->show(); + + WindowVisibilitySpy spy; + spy.addWidgetToSpy(widget); + + QSignalSpy hiddenSpy(&spy, SIGNAL(windowHidden(QWidget*))); + + widget->hide(); + + QCOMPARE(hiddenSpy.count(), 0); +} + +void WindowVisibilitySpyTest::testDeleteWidgetShownAsWidget() { + QWidget window; + window.show(); + + QWidget* widget = new QWidget(&window); + widget->show(); + + WindowVisibilitySpy spy; + spy.addWidgetToSpy(widget); + + QSignalSpy shownSpy(&spy, SIGNAL(windowShown(QWidget*))); + + delete widget; + + QCOMPARE(shownSpy.count(), 0); +} + +void WindowVisibilitySpyTest::testShowChildWidgetAddedBeforeSpy() { + QWidget window; + QWidget* childWidget = new QWidget(&window); + window.show(); + + QWidget* grandChildWindow = new QWidget(childWidget); + grandChildWindow->setWindowFlags(Qt::Window); + + WindowVisibilitySpy spy; + spy.addWidgetToSpy(&window); + + QSignalSpy shownSpy(&spy, SIGNAL(windowShown(QWidget*))); + + grandChildWindow->show(); + + QCOMPARE(shownSpy.count(), 1); + QVariant argument = shownSpy.at(0).at(0); + QCOMPARE(argument.userType(), (int)QMetaType::QWidgetStar); + QCOMPARE(qvariant_cast<QWidget*>(argument), grandChildWindow); +} + +void WindowVisibilitySpyTest::testShowChildWidgetAddedAfterSpy() { + QWidget window; + QWidget* childWidget = new QWidget(&window); + window.show(); + + WindowVisibilitySpy spy; + spy.addWidgetToSpy(&window); + + QSignalSpy shownSpy(&spy, SIGNAL(windowShown(QWidget*))); + + QWidget* grandChildWindow = new QWidget(childWidget); + grandChildWindow->setWindowFlags(Qt::Window); + grandChildWindow->show(); + + QCOMPARE(shownSpy.count(), 1); + QVariant argument = shownSpy.at(0).at(0); + QCOMPARE(argument.userType(), (int)QMetaType::QWidgetStar); + QCOMPARE(qvariant_cast<QWidget*>(argument), grandChildWindow); +} + +} + +QTEST_MAIN(common::WindowVisibilitySpyTest) + +#include "WindowVisibilitySpyTest.moc" Property changes on: trunk/ktutorial/ktutorial-library/tests/common/WindowVisibilitySpyTest.cpp ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-library/tests/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/CMakeLists.txt 2010-08-14 17:30:49 UTC (rev 248) +++ trunk/ktutorial/ktutorial-library/tests/view/CMakeLists.txt 2010-09-19 16:40:33 UTC (rev 249) @@ -21,6 +21,7 @@ StepWidget TutorialListModel TutorialManagerDialog + WindowOnTopEnforcer ) MACRO(MEM_TESTS) @@ -34,4 +35,5 @@ StepWidget TutorialListModel TutorialManagerDialog + WindowOnTopEnforcer ) Modified: trunk/ktutorial/ktutorial-library/tests/view/StepWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/StepWidgetTest.cpp 2010-08-14 17:30:49 UTC (rev 248) +++ trunk/ktutorial/ktutorial-library/tests/view/StepWidgetTest.cpp 2010-09-19 16:40:33 UTC (rev 249) @@ -72,6 +72,8 @@ //Closing with ALT+F4 can't be tested, as it depends on the window manager //rather than the widget + void moveAfterShowingModalDialogAndThenClose(); + private: int mDummySlotCallCount; @@ -80,6 +82,8 @@ StepTextWidget* textWidget(StepWidget* stepWidget); KPushButton* closeButton(StepWidget* stepWidget); + void queueAssertWidgetDragged(StepWidget* stepWidget, int timeToWait); + }; void StepWidgetTest::testConstructor() { @@ -311,6 +315,36 @@ QCOMPARE(destroyedSpy.count(), 1); } +void StepWidgetTest::moveAfterShowingModalDialogAndThenClose() { + QWidget* window = new QWidget(); + window->show(); + + StepWidget* stepWidget = new StepWidget("Test tutorial"); + stepWidget->setMainApplicationWindow(window); + stepWidget->show(); + + QDialog* modalDialog = new QDialog(window); + + QTimer timerAccept; + timerAccept.setSingleShot(true); + timerAccept.setInterval(2000); + connect(&timerAccept, SIGNAL(timeout()), modalDialog, SLOT(accept())); + + queueAssertWidgetDragged(stepWidget, 500); + + timerAccept.start(); + modalDialog->exec(); + + QSignalSpy destroyedSpy(stepWidget, SIGNAL(destroyed(QObject*))); + + QTest::mouseClick(closeButton(stepWidget), Qt::LeftButton, + Qt::NoModifier, QPoint(), 500); + //Give it time to die + QTest::qWait(500); + + QCOMPARE(destroyedSpy.count(), 1); +} + /////////////////////////////////// Helpers //////////////////////////////////// StepTextWidget* StepWidgetTest::textWidget(StepWidget* stepWidget) { @@ -321,8 +355,66 @@ return stepWidget->ui->closeButton; } +//Modal dialogs don't return to the test code until they are closed. Thus, the +//actions or asserts to be performed while a modal dialog is being shown (like +//checking the position of the widget) must be "queued". +class QueuedActionsHelper: public QObject { +Q_OBJECT +public: + + QueuedActionsHelper(QObject* object = 0): QObject(object) { + } + + void setStepWidget(StepWidget* stepWidget) { + mStepWidget = stepWidget; + } + +public slots: + + void assertWidgetDragged() { + QVERIFY(mStepWidget->isVisible()); + + QPoint previousPosition = mStepWidget->pos(); + + QPoint widgetCenter(mStepWidget->size().width()/2, + mStepWidget->size().height()/2); + + QTest::mouseMove(mStepWidget); + + //Use setMouseTracking and QCursor::setPos as a workaround. + //QTest::mouseMove, due to unknown reasons for me, doesn't make + //StepWidget to receive QMouseEvent for move. Neither QCursor::setPos + //does if tracking isn't enabled + mStepWidget->setMouseTracking(true); + QTest::mousePress(mStepWidget, Qt::LeftButton, + Qt::NoModifier, QPoint(), 500); + QCursor::setPos(previousPosition + widgetCenter + QPoint(42, 23)); + QTest::mouseRelease(mStepWidget, Qt::LeftButton, + Qt::NoModifier, QPoint(), 500); + mStepWidget->setMouseTracking(false); + + QVERIFY(previousPosition.x() + 41 <= mStepWidget->pos().x() && + previousPosition.x() + 43 >= mStepWidget->pos().x()); + QVERIFY(previousPosition.y() + 22 <= mStepWidget->pos().y() && + previousPosition.y() + 24 >= mStepWidget->pos().y()); + } + +private: + + StepWidget* mStepWidget; + +}; + +void StepWidgetTest::queueAssertWidgetDragged(StepWidget* stepWidget, + int timeToWait) { + QueuedActionsHelper* helper = new QueuedActionsHelper(); + helper->setStepWidget(stepWidget); + QTimer::singleShot(timeToWait, helper, SLOT(assertWidgetDragged())); + QTimer::singleShot(timeToWait + 1000, helper, SLOT(deleteLater())); } +} + QTEST_KDEMAIN(view::StepWidgetTest, GUI) #include "StepWidgetTest.moc" Added: trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp 2010-09-19 16:40:33 UTC (rev 249) @@ -0,0 +1,419 @@ +/*************************************************************************** + * 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 <QDialog> + +#include "WindowOnTopEnforcer.h" + +namespace view { + +class WindowOnTopEnforcerTest: public QObject { +Q_OBJECT + +private slots: + + void testConstructor(); + + void testBaseWindow(); + + void testChildWindow(); + + void testModalDialog(); + + void testNestedModalDialog(); + + void testSeveralModalDialogs(); + + void testNestedModalDialogOnChildWindow(); + +private: + + void queueAssertParent(QWidget* widget, QWidget* parent, int timeToWait); + void queueAssertIsVisibleWindow(QWidget* widget, int timeToWait); + +}; + +void WindowOnTopEnforcerTest::testConstructor() { + QWidget widget; + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(&widget); + + QCOMPARE(enforcer->parent(), &widget); +} + +void WindowOnTopEnforcerTest::testBaseWindow() { + QWidget* window = new QWidget(); + window->show(); + + QWidget* windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowFlags(Qt::Window); + windowToKeepOnTop->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + QSignalSpy destroyedSpy(windowToKeepOnTop, SIGNAL(destroyed(QObject*))); + + delete window; + + QCOMPARE(destroyedSpy.count(), 1); + QVariant argument = destroyedSpy.at(0).at(0); + QCOMPARE(argument.userType(), (int)QMetaType::QObjectStar); + QCOMPARE(qvariant_cast<QObject*>(argument), windowToKeepOnTop); +} + +void WindowOnTopEnforcerTest::testChildWindow() { + QWidget* window = new QWidget(); + window->show(); + + QWidget* windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowFlags(Qt::Window); + windowToKeepOnTop->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + QWidget* childWindow = new QWidget(window); + childWindow->setWindowFlags(Qt::Window); + childWindow->show(); + + QCOMPARE(windowToKeepOnTop->parentWidget(), window); + QVERIFY(windowToKeepOnTop->isVisible()); + QVERIFY(windowToKeepOnTop->windowFlags() & Qt::Window); + + delete childWindow; + + QCOMPARE(windowToKeepOnTop->parentWidget(), window); + QVERIFY(windowToKeepOnTop->isVisible()); + QVERIFY(windowToKeepOnTop->windowFlags() & Qt::Window); + + QSignalSpy destroyedSpy(windowToKeepOnTop, SIGNAL(destroyed(QObject*))); + + delete window; + + QCOMPARE(destroyedSpy.count(), 1); + QVariant argument = destroyedSpy.at(0).at(0); + QCOMPARE(argument.userType(), (int)QMetaType::QObjectStar); + QCOMPARE(qvariant_cast<QObject*>(argument), windowToKeepOnTop); +} + +void WindowOnTopEnforcerTest::testModalDialog() { + QWidget* window = new QWidget(); + window->show(); + + QWidget* windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowFlags(Qt::Window); + windowToKeepOnTop->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + QDialog* modalDialog = new QDialog(window); + + QTimer timerAccept; + timerAccept.setSingleShot(true); + timerAccept.setInterval(1000); + connect(&timerAccept, SIGNAL(timeout()), modalDialog, SLOT(accept())); + + queueAssertParent(windowToKeepOnTop, modalDialog, 500); + queueAssertIsVisibleWindow(windowToKeepOnTop, 500); + + timerAccept.start(); + modalDialog->exec(); + + QCOMPARE(windowToKeepOnTop->parentWidget(), window); + QVERIFY(windowToKeepOnTop->isVisible()); + QVERIFY(windowToKeepOnTop->windowFlags() & Qt::Window); + + QSignalSpy destroyedSpy(windowToKeepOnTop, SIGNAL(destroyed(QObject*))); + + delete window; + + QCOMPARE(destroyedSpy.count(), 1); + QVariant argument = destroyedSpy.at(0).at(0); + QCOMPARE(argument.userType(), (int)QMetaType::QObjectStar); + QCOMPARE(qvariant_cast<QObject*>(argument), windowToKeepOnTop); +} + +void WindowOnTopEnforcerTest::testNestedModalDialog() { + QWidget* window = new QWidget(); + window->show(); + + QWidget* windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowFlags(Qt::Window); + windowToKeepOnTop->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + QDialog* modalDialog = new QDialog(window); + QDialog* nestedModalDialog = new QDialog(modalDialog); + + QTimer timerAccept; + timerAccept.setSingleShot(true); + timerAccept.setInterval(2500); + connect(&timerAccept, SIGNAL(timeout()), modalDialog, SLOT(accept())); + + QTimer timerExecNested; + timerExecNested.setSingleShot(true); + timerExecNested.setInterval(500); + connect(&timerExecNested, SIGNAL(timeout()), + nestedModalDialog, SLOT(exec())); + + QTimer timerAcceptNested; + timerAcceptNested.setSingleShot(true); + timerAcceptNested.setInterval(1500); + connect(&timerAcceptNested, SIGNAL(timeout()), + nestedModalDialog, SLOT(accept())); + + queueAssertParent(windowToKeepOnTop, nestedModalDialog, 1000); + queueAssertIsVisibleWindow(windowToKeepOnTop, 1000); + queueAssertParent(windowToKeepOnTop, modalDialog, 2000); + queueAssertIsVisibleWindow(windowToKeepOnTop, 2000); + + timerAccept.start(); + timerExecNested.start(); + timerAcceptNested.start(); + modalDialog->exec(); + + QCOMPARE(windowToKeepOnTop->parentWidget(), window); + QVERIFY(windowToKeepOnTop->isVisible()); + QVERIFY(windowToKeepOnTop->windowFlags() & Qt::Window); + + QSignalSpy destroyedSpy(windowToKeepOnTop, SIGNAL(destroyed(QObject*))); + + delete window; + + QCOMPARE(destroyedSpy.count(), 1); + QVariant argument = destroyedSpy.at(0).at(0); + QCOMPARE(argument.userType(), (int)QMetaType::QObjectStar); + QCOMPARE(qvariant_cast<QObject*>(argument), windowToKeepOnTop); +} + +void WindowOnTopEnforcerTest::testSeveralModalDialogs() { + QWidget* window = new QWidget(); + window->show(); + + QWidget* windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowFlags(Qt::Window); + windowToKeepOnTop->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + QDialog* modalDialog = new QDialog(window); + QDialog* nestedModalDialog = new QDialog(modalDialog); + QDialog* nestedModalDialog2 = new QDialog(modalDialog); + + QTimer timerAccept; + timerAccept.setSingleShot(true); + timerAccept.setInterval(4500); + connect(&timerAccept, SIGNAL(timeout()), modalDialog, SLOT(accept())); + + QTimer timerExecNested; + timerExecNested.setSingleShot(true); + timerExecNested.setInterval(500); + connect(&timerExecNested, SIGNAL(timeout()), + nestedModalDialog, SLOT(exec())); + + QTimer timerAcceptNested; + timerAcceptNested.setSingleShot(true); + timerAcceptNested.setInterval(1500); + connect(&timerAcceptNested, SIGNAL(timeout()), + nestedModalDialog, SLOT(accept())); + + QTimer timerExecNested2; + timerExecNested2.setSingleShot(true); + timerExecNested2.setInterval(2500); + connect(&timerExecNested2, SIGNAL(timeout()), + nestedModalDialog2, SLOT(exec())); + + QTimer timerAcceptNested2; + timerAcceptNested2.setSingleShot(true); + timerAcceptNested2.setInterval(3500); + connect(&timerAcceptNested2, SIGNAL(timeout()), + nestedModalDialog2, SLOT(accept())); + + queueAssertParent(windowToKeepOnTop, nestedModalDialog, 1000); + queueAssertIsVisibleWindow(windowToKeepOnTop, 1000); + queueAssertParent(windowToKeepOnTop, modalDialog, 2000); + queueAssertIsVisibleWindow(windowToKeepOnTop, 2000); + queueAssertParent(windowToKeepOnTop, nestedModalDialog2, 3000); + queueAssertIsVisibleWindow(windowToKeepOnTop, 3000); + queueAssertParent(windowToKeepOnTop, modalDialog, 4000); + queueAssertIsVisibleWindow(windowToKeepOnTop, 4000); + + timerAccept.start(); + timerExecNested.start(); + timerAcceptNested.start(); + timerExecNested2.start(); + timerAcceptNested2.start(); + modalDialog->exec(); + + QCOMPARE(windowToKeepOnTop->parentWidget(), window); + QVERIFY(windowToKeepOnTop->isVisible()); + QVERIFY(windowToKeepOnTop->windowFlags() & Qt::Window); + + QDialog* modalDialog2 = new QDialog(window); + QDialog* nestedModalDialog3 = new QDialog(modalDialog); + + QTimer timerAccept2; + timerAccept2.setSingleShot(true); + timerAccept2.setInterval(2500); + connect(&timerAccept2, SIGNAL(timeout()), modalDialog2, SLOT(accept())); + + QTimer timerExecNested3; + timerExecNested3.setSingleShot(true); + timerExecNested3.setInterval(500); + connect(&timerExecNested3, SIGNAL(timeout()), + nestedModalDialog3, SLOT(exec())); + + QTimer timerAcceptNested3; + timerAcceptNested3.setSingleShot(true); + timerAcceptNested3.setInterval(1500); + connect(&timerAcceptNested3, SIGNAL(timeout()), + nestedModalDialog3, SLOT(accept())); + + queueAssertParent(windowToKeepOnTop, nestedModalDialog3, 1000); + queueAssertIsVisibleWindow(windowToKeepOnTop, 1000); + queueAssertParent(windowToKeepOnTop, modalDialog2, 2000); + queueAssertIsVisibleWindow(windowToKeepOnTop, 2000); + + timerAccept2.start(); + timerExecNested3.start(); + timerAcceptNested3.start(); + modalDialog2->exec(); + + QCOMPARE(windowToKeepOnTop->parentWidget(), window); + QVERIFY(windowToKeepOnTop->isVisible()); + QVERIFY(windowToKeepOnTop->windowFlags() & Qt::Window); + + QSignalSpy destroyedSpy(windowToKeepOnTop, SIGNAL(destroyed(QObject*))); + + delete window; + + QCOMPARE(destroyedSpy.count(), 1); + QVariant argument = destroyedSpy.at(0).at(0); + QCOMPARE(argument.userType(), (int)QMetaType::QObjectStar); + QCOMPARE(qvariant_cast<QObject*>(argument), windowToKeepOnTop); +} + +void WindowOnTopEnforcerTest::testNestedModalDialogOnChildWindow() { + QWidget* window = new QWidget(); + window->show(); + + QWidget* windowToKeepOnTop = new QWidget(window); + windowToKeepOnTop->setWindowFlags(Qt::Window); + windowToKeepOnTop->show(); + + WindowOnTopEnforcer* enforcer = new WindowOnTopEnforcer(windowToKeepOnTop); + enforcer->setBaseWindow(window); + + QWidget* childWindow = new QWidget(window); + childWindow->setWindowFlags(Qt::Window); + childWindow->show(); + + QDialog* nestedModalDialog = new QDialog(childWindow); + + QTimer timerAccept; + timerAccept.setSingleShot(true); + timerAccept.setInterval(1000); + connect(&timerAccept, SIGNAL(timeout()), nestedModalDialog, SLOT(accept())); + + queueAssertParent(windowToKeepOnTop, nestedModalDialog, 500); + queueAssertIsVisibleWindow(windowToKeepOnTop, 500); + + timerAccept.start(); + nestedModalDialog->exec(); + + QCOMPARE(windowToKeepOnTop->parentWidget(), window); + QVERIFY(windowToKeepOnTop->isVisible()); + QVERIFY(windowToKeepOnTop->windowFlags() & Qt::Window); + + QSignalSpy destroyedSpy(windowToKeepOnTop, SIGNAL(destroyed(QObject*))); + + delete window; + + QCOMPARE(destroyedSpy.count(), 1); + QVariant argument = destroyedSpy.at(0).at(0); + QCOMPARE(argument.userType(), (int)QMetaType::QObjectStar); + QCOMPARE(qvariant_cast<QObject*>(argument), windowToKeepOnTop); +} + +/////////////////////////////////// Helpers //////////////////////////////////// + +//The dialogs are modal, so they won't return to the test code until they are +//closed. Thus, the asserts to be performed while the dialogs are being shown +//(like checking the parent of a widget) must be "queued". +class QueuedActionsHelper: public QObject { +Q_OBJECT +public: + + QueuedActionsHelper(QObject* object = 0): QObject(object) { + } + + void setAssertWidget(QWidget* widget) { + mAssertWidget = widget; + } + + void setAssertParent(QWidget* parent) { + mAssertParent = parent; + } + +public slots: + + void assertParent() { + QCOMPARE(mAssertWidget->parentWidget(), mAssertParent); + } + + void assertIsVisibleWindow() { + QVERIFY(mAssertWidget->isVisible()); + QVERIFY(mAssertWidget->windowFlags() & Qt::Window); + } + +private: + + QWidget* mAssertWidget; + QWidget* mAssertParent; + +}; + +void WindowOnTopEnforcerTest::queueAssertParent(QWidget* widget, + QWidget* parent, + int timeToWait) { + QueuedActionsHelper* helper = new QueuedActionsHelper(); + helper->setAssertWidget(widget); + helper->setAssertParent(parent); + QTimer::singleShot(timeToWait, helper, SLOT(assertParent())); + QTimer::singleShot(timeToWait, helper, SLOT(deleteLater())); +} + +void WindowOnTopEnforcerTest::queueAssertIsVisibleWindow(QWidget* widget, + int timeToWait) { + QueuedActionsHelper* helper = new QueuedActionsHelper(); + helper->setAssertWidget(widget); + QTimer::singleShot(timeToWait, helper, SLOT(assertIsVisibleWindow())); + QTimer::singleShot(timeToWait, helper, SLOT(deleteLater())); +} + +} + +QTEST_MAIN(view::WindowOnTopEnforcerTest) + +#include "WidgetOnTopEnforcerTest.moc" Property changes on: trunk/ktutorial/ktutorial-library/tests/view/WindowOnTopEnforcerTest.cpp ___________________________________________________________________ Added: svn:eol-style + native This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |