[Ktutorial-commits] SF.net SVN: ktutorial:[301] trunk/ktutorial/ktutorial-editor
Status: Alpha
Brought to you by:
danxuliu
From: <dan...@us...> - 2011-05-05 14:39:24
|
Revision: 301 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=301&view=rev Author: danxuliu Date: 2011-05-05 14:39:15 +0000 (Thu, 05 May 2011) Log Message: ----------- Add a basic helper to edit KUIT semantic markup in the text of a step. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt trunk/ktutorial/ktutorial-editor/src/view/StepDataWidget.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt Added Paths: ----------- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.cpp trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.h trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.cpp trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.h trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.ui trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.cpp trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.h trunk/ktutorial/ktutorial-editor/tests/unit/view/SemanticMarkupEditionTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/SemanticMarkupLinkWidgetTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/SemanticMarkupParserTest.cpp Modified: trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2011-04-30 17:31:13 UTC (rev 300) +++ trunk/ktutorial/ktutorial-editor/src/view/CMakeLists.txt 2011-05-05 14:39:15 UTC (rev 301) @@ -9,6 +9,9 @@ NewWaitForWidget.cpp ReactionTreeItem.cpp ReactionWidget.cpp + SemanticMarkupEdition.cpp + SemanticMarkupLinkWidget.cpp + SemanticMarkupParser.cpp StepCustomCodeWidget.cpp StepDataWidget.cpp StepTreeItem.cpp @@ -53,6 +56,7 @@ NewWaitForWidget.ui ReactionWidget.ui RemoteObjectNameWidget.ui + SemanticMarkupLinkWidget.ui StepDataWidget.ui TutorialInformationWidget.ui WaitForEventWidget.ui Added: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.cpp 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,418 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "SemanticMarkupEdition.h" + +#include <QRegExp> +#include <QTextEdit> + +#include <KAction> +#include <KActionCollection> +#include <KDialog> +#include <KLocalizedString> +#include <KPushButton> + +#include "DialogRunner.h" +#include "SemanticMarkupLinkWidget.h" +#include "SemanticMarkupParser.h" + +//public: + +SemanticMarkupEdition::SemanticMarkupEdition(QTextEdit* textEdit): + QObject(textEdit), + mTextEdit(textEdit) { + Q_ASSERT(textEdit); + + connect(mTextEdit, SIGNAL(cursorPositionChanged()), + this, SLOT(updateActionStates())); + //TODO Needed to update the action states when the user deletes a character; + //look how to do that without updating twice in any other text change. + connect(mTextEdit, SIGNAL(textChanged()), + this, SLOT(updateActionStates())); +} + +void SemanticMarkupEdition::createActions(KActionCollection* actionCollection) { + Q_ASSERT(actionCollection); + + mEmphasisAction = new KAction("emphasis", actionCollection); + mEmphasisAction->setCheckable(true); + mEmphasisAction->setToolTip(i18nc("@info:tooltip", "Emphasized text")); + mEmphasisAction->setWhatsThis(i18nc("@info:whatsthis", + "Phrase tag to emphasize a word or phrase in the text.<nl/>" + "Example: <emphasis>emphasized text</emphasis>")); + actionCollection->addAction("kuit-edition-phrase-emphasis", + mEmphasisAction); + connect(mEmphasisAction, SIGNAL(triggered()), this, SLOT(emphasis())); + mActions.append(mEmphasisAction); + + mEmphasisStrongAction = new KAction("emphasis (strong)", actionCollection); + mEmphasisStrongAction->setCheckable(true); + mEmphasisStrongAction->setToolTip(i18nc("@info:tooltip", + "Strongly emphasized text")); + mEmphasisStrongAction->setWhatsThis(i18nc("@info:whatsthis", + "Phrase tag to strongly emphasize a word or phrase in the text.<nl/>" + "Example: <emphasis strong=\"1\">strongly emphasized text</emphasis>")); + actionCollection->addAction("kuit-edition-phrase-emphasis-strong", + mEmphasisStrongAction); + connect(mEmphasisStrongAction, SIGNAL(triggered()), + this, SLOT(emphasisStrong())); + mActions.append(mEmphasisStrongAction); + + mFilenameAction = new KAction("filename", actionCollection); + mFilenameAction->setCheckable(true); + mFilenameAction->setToolTip(i18nc("@info:tooltip", "Filename or path")); + mFilenameAction->setWhatsThis(i18nc("@info:whatsthis", + "Phrase tag for file or folder name or path.<nl/>" + "The path separators will be transformed into what is native to the " + "platform.<nl/>" + "Example: <filename>/home/user/Music/song.ogg</filename>")); + actionCollection->addAction("kuit-edition-phrase-filename", + mFilenameAction); + connect(mFilenameAction, SIGNAL(triggered()), this, SLOT(filename())); + mActions.append(mFilenameAction); + + mInterfaceAction = new KAction("interface", actionCollection); + mInterfaceAction->setCheckable(true); + mInterfaceAction->setToolTip(i18nc("@info:tooltip", + "GUI interface element")); + mInterfaceAction->setWhatsThis(i18nc("@info:whatsthis", + "Phrase tag for paths to GUI interface elements.<nl/>" + "If there is more than one element in the path, use \"|\" to delimit " + "elements, which will be converted into canonical delimiter.<nl/>" + "Example: <interface>File|Open</interface>")); + actionCollection->addAction("kuit-edition-phrase-interface", + mInterfaceAction); + connect(mInterfaceAction, SIGNAL(triggered()), this, SLOT(interface())); + mActions.append(mInterfaceAction); + + mLinkAction = new KAction("link", actionCollection); + mLinkAction->setCheckable(true); + mLinkAction->setToolTip(i18nc("@info:tooltip", "Link to URL or widget")); + mLinkAction->setWhatsThis(i18nc("@info:whatsthis", + "Phrase tag to link to a URL-addressable resource.<nl/>" + "Widgets in the target application interface can be linked using " + "<emphasis>widget:theObjectNameOfTheWidget</emphasis><nl/>" + "Example: <link url=\"http://www.kde.org\">a link</link>")); + actionCollection->addAction("kuit-edition-phrase-link", mLinkAction); + connect(mLinkAction, SIGNAL(triggered()), this, SLOT(link())); + mActions.append(mLinkAction); + + mNlAction = new KAction("nl", actionCollection); + mNlAction->setToolTip(i18nc("@info:tooltip", "Line break")); + mNlAction->setWhatsThis(i18nc("@info:whatsthis", + "Phrase tag for line breaks.<nl/>" + "Example: line<nl/>break")); + actionCollection->addAction("kuit-edition-phrase-nl", mNlAction); + connect(mNlAction, SIGNAL(triggered()), this, SLOT(nl())); + mActions.append(mNlAction); + + mShortcutAction = new KAction("shortcut", actionCollection); + mShortcutAction->setCheckable(true); + mShortcutAction->setToolTip(i18nc("@info:tooltip", + "Combination of keys to press")); + mShortcutAction->setWhatsThis(i18nc("@info:whatsthis", + "Phrase tag for combinations of keys to press.<nl/>" + "Separate the keys by \"+\" or \"-\", and the shortcut will be " + "converted into canonical form.<nl/>" + "Example: <shortcut>Ctrl+N</shortcut>")); + actionCollection->addAction("kuit-edition-phrase-shortcut", + mShortcutAction); + connect(mShortcutAction, SIGNAL(triggered()), this, SLOT(shortcut())); + mActions.append(mShortcutAction); + + mParaAction = new KAction("para", actionCollection); + mParaAction->setCheckable(true); + mParaAction->setToolTip(i18nc("@info:tooltip", "Paragraph")); + mParaAction->setWhatsThis(i18nc("@info:whatsthis", + "<para>Structure tag for text paragraphs.<nl/>" + "Example: one paragraph</para><para>Other paragraph</para>")); + actionCollection->addAction("kuit-edition-structure-para", mParaAction); + connect(mParaAction, SIGNAL(triggered()), this, SLOT(para())); + mActions.append(mParaAction); + + mListAction = new KAction("list", actionCollection); + mListAction->setCheckable(true); + mListAction->setToolTip(i18nc("@info:tooltip", "List of items")); + mListAction->setWhatsThis(i18nc("@info:whatsthis", + "<para>Structure tag for lists of items.<nl/>" + "Can contain only <item> as subtags. List is considered an " + "element of the paragraph, so the <list> must be found inside " + "<para>.<nl/>" + "Example: <list>" + " <item>One item</item>" + " <item>Other item</item>" + " </list></para>")); + actionCollection->addAction("kuit-edition-structure-list", mListAction); + connect(mListAction, SIGNAL(triggered()), this, SLOT(list())); + mActions.append(mListAction); + + mItemAction = new KAction("item", actionCollection); + mItemAction->setCheckable(true); + mItemAction->setToolTip(i18nc("@info:tooltip", "List items")); + mItemAction->setWhatsThis(i18nc("@info:whatsthis", + "<para>Structure tag for list items.<nl/>" + "Example: <list>" + " <item>One item</item>" + " <item>Other item</item>" + " </list></para>")); + actionCollection->addAction("kuit-edition-structure-item", mItemAction); + connect(mItemAction, SIGNAL(triggered()), this, SLOT(item())); + mActions.append(mItemAction); + + updateActionStates(); +} + +//private: + +QAction* SemanticMarkupEdition::actionForElement(const StartTag& startTag) + const { + QAction* action = 0; + + QRegExp emphasisStrongRegExp("strong=\"yes|true|1\""); + if (startTag.mName == "emphasis" && + startTag.mAttributes.indexOf(emphasisStrongRegExp) < 0) { + action = mEmphasisAction; + } else if (startTag.mName == "emphasis" && + startTag.mAttributes.indexOf(emphasisStrongRegExp) >= 0) { + action = mEmphasisStrongAction; + } else if (startTag.mName == "filename") { + action = mFilenameAction; + } else if (startTag.mName == "interface") { + action = mInterfaceAction; + } else if (startTag.mName == "link") { + action = mLinkAction; + } else if (startTag.mName == "nl") { + action = mNlAction; + } else if (startTag.mName == "shortcut") { + action = mShortcutAction; + } else if (startTag.mName == "para") { + action = mParaAction; + } else if (startTag.mName == "list") { + action = mListAction; + } else if (startTag.mName == "item") { + action = mItemAction; + } + + return action; +} + +void SemanticMarkupEdition::writeSimpleTagFor(const QAction* action) { + if (action->isChecked()) { + mTextEdit->insertPlainText('<' + action->text() + '>'); + } else { + mTextEdit->insertPlainText("</" + action->text() + '>'); + } +} + +QDialog* SemanticMarkupEdition::newLinkDialog(const QString& url) const { + KDialog* dialog = new KDialog(mTextEdit); + dialog->setModal(true); + + dialog->setButtons(KDialog::Ok | KDialog::Cancel); + + dialog->button(KDialog::Ok)->setObjectName("okButton"); + dialog->button(KDialog::Cancel)->setObjectName("cancelButton"); + + SemanticMarkupLinkWidget* linkWidget = new SemanticMarkupLinkWidget(dialog); + linkWidget->setUrl(url); + dialog->setMainWidget(linkWidget); + dialog->setWindowTitle(linkWidget->windowTitle()); + + return dialog; +} + +//private slots: + +void SemanticMarkupEdition::updateActionStates() { + Q_ASSERT(!mActions.isEmpty()); + + foreach (QAction* action, mActions) { + action->setEnabled(true); + action->setChecked(false); + } + + mLinkElementClosed = false; + + SemanticMarkupParser parser; + parser.setCursorIndex(mTextEdit->textCursor().position()); + parser.parse(mTextEdit->toPlainText()); + + if (parser.isCursorInsideTag()) { + foreach (QAction* action, mActions) { + action->setEnabled(false); + } + + return; + } + + QList<StartTag> openElementsAtCursor = parser.openElementsAtCursor(); + if (openElementsAtCursor.isEmpty()) { + mListAction->setEnabled(false); + mItemAction->setEnabled(false); + + return; + } + + QList<QAction*> phraseActions; + phraseActions << mEmphasisAction << mEmphasisStrongAction << mFilenameAction + << mInterfaceAction << mLinkAction << mNlAction + << mShortcutAction; + + foreach (const StartTag& startTag, openElementsAtCursor) { + QAction* action = actionForElement(startTag); + if (!action) { + continue; + } + + action->setChecked(true); + + bool elementClosed = parser.isElementClosed(startTag); + if (elementClosed && action != mLinkAction) { + action->setEnabled(false); + } else if (elementClosed && action == mLinkAction) { + action->setEnabled(true); + mLinkElementClosed = true; + } + + if (phraseActions.contains(action)) { + foreach (QAction* actionToDisable, mActions) { + if (actionToDisable != action) { + actionToDisable->setEnabled(false); + } + } + } else if (action == mParaAction) { + if (!mListAction->isChecked()) { + mItemAction->setEnabled(false); + } + } else if (action == mListAction) { + mParaAction->setEnabled(false); + if (!mItemAction->isChecked()) { + foreach (QAction* phraseAction, phraseActions) { + phraseAction->setEnabled(false); + } + } + } else if (action == mItemAction) { + mParaAction->setEnabled(false); + mListAction->setEnabled(false); + } + } +} + +void SemanticMarkupEdition::emphasis() { + writeSimpleTagFor(mEmphasisAction); +} + +void SemanticMarkupEdition::emphasisStrong() { + if (mEmphasisStrongAction->isChecked()) { + mTextEdit->insertPlainText("<emphasis strong=\"yes\">"); + } else { + mTextEdit->insertPlainText("</emphasis>"); + } +} + +void SemanticMarkupEdition::filename() { + writeSimpleTagFor(mFilenameAction); +} + +void SemanticMarkupEdition::interface() { + writeSimpleTagFor(mInterfaceAction); +} + +void SemanticMarkupEdition::link() { + if (!mLinkAction->isChecked() && !mLinkElementClosed) { + mTextEdit->insertPlainText("</link>"); + return; + } + + if (!mLinkElementClosed) { + QScopedPointer<QDialog> dialog(newLinkDialog("")); + if (DialogRunner(dialog.data()).exec() == QDialog::Rejected) { + mLinkAction->setChecked(false); + return; + } + + QString newUrl = dialog->findChild<SemanticMarkupLinkWidget*>()->url(); + mTextEdit->insertPlainText(QString("<link url=\"%1\">").arg(newUrl)); + + return; + } + + mLinkAction->setChecked(true); + + QString text = mTextEdit->toPlainText(); + int cursorIndex = mTextEdit->textCursor().position(); + QRegExp linkRegExp("<\\s*link\\s*(\\w+=\"[^\"]*\")*\\s*>"); + int linkElementIndex = text.lastIndexOf(linkRegExp, cursorIndex); + + QString linkText = linkRegExp.capturedTexts().at(0); + QRegExp urlRegExp(" url=\"([^\"]*)\""); + int urlAttributeIndex = linkText.indexOf(urlRegExp); + + if (urlAttributeIndex == -1) { + urlAttributeIndex = QString("<link").length(); + } + + int absoluteUrlAttributeIndex = linkElementIndex + urlAttributeIndex; + QString oldUrlAttribute = urlRegExp.capturedTexts().at(0); + QString oldUrl = urlRegExp.capturedTexts().at(1); + + QScopedPointer<QDialog> dialog(newLinkDialog(oldUrl)); + if (DialogRunner(dialog.data()).exec() == QDialog::Rejected) { + return; + } + + QString newUrl = dialog->findChild<SemanticMarkupLinkWidget*>()->url(); + + QTextCursor cursor = mTextEdit->textCursor(); + cursor.setPosition(absoluteUrlAttributeIndex); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, + oldUrlAttribute.length()); + cursor.insertText(QString(" url=\"%1\"").arg(newUrl)); +} + +void SemanticMarkupEdition::nl() { + mTextEdit->insertPlainText("<nl/>\n"); +} + +void SemanticMarkupEdition::shortcut() { + writeSimpleTagFor(mShortcutAction); +} + +void SemanticMarkupEdition::para() { + if (mParaAction->isChecked()) { + mTextEdit->insertPlainText("<para>"); + } else { + mTextEdit->insertPlainText("</para>\n\n"); + } +} + +void SemanticMarkupEdition::list() { + if (mListAction->isChecked()) { + mTextEdit->insertPlainText("<list>\n"); + } else { + mTextEdit->insertPlainText("</list>\n"); + } +} + +void SemanticMarkupEdition::item() { + if (mItemAction->isChecked()) { + mTextEdit->insertPlainText("<item>"); + } else { + mTextEdit->insertPlainText("</item>\n"); + } +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.h 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,248 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef SEMANTICMARKUPEDITION_H +#define SEMANTICMARKUPEDITION_H + +#include <QObject> + +class QAction; +class QDialog; +class QTextEdit; + +class KActionCollection; + +class StartTag; + +/** + * Helper to edit KUIT semantic markup in a QTextEdit widget. + * SemanticMarkupEdition provides several actions that ease the edition of the + * most common KUIT semantic markup tags. The actions are added to a + * KActionCollection using createActions(KActionCollection*). + * + * Triggering an action will write its associated start tag in the current + * position of the text edit cursor. Triggering the action again will write its + * associated end tag. When a tag needs extra information from the user to be + * written (for example, the URL of a link element), a dialog will be shown. + * Also, triggering the action when the cursor is between two paired start and + * end tags will show the dialog and update the attribute. + * + * The action states will be updated based on the cursor of the QTextEdit. + * Depending on its position or the current selection some actions may be + * disabled or checked. + * + * When the cursor is inside any tag all the actions will be disabled. If it is + * between two paired start and end tags they will also be disabled (except the + * ones that show a dialog), although the action for that tag (and its paired + * parent elements) will be checked. If the cursor is after a start tag, the + * action for that tag will be enabled and checked (and triggering it will write + * the end tag). + * + * The actions may also be disabled when their element can not be written at the + * current position of the cursor. For example, the list action will be disabled + * unless the cursor is after a para tag. It will be disabled too if after the + * para tag there is a phrase element tag, as list elements can be child only of + * para elements. + * + * Only the cursor position is used to update the state of the actions. + * Selections are not taken into account. + */ +class SemanticMarkupEdition: public QObject { +Q_OBJECT +public: + + /** + * Creates a new SemanticMarkupEdition. + * The QTextEdit will also act as parent of the SemanticMarkupEdition. + * + * @param textEdit The QTextEdit used for the edition of the text. + */ + SemanticMarkupEdition(QTextEdit* textEdit); + + /** + * Creates the actions for markup edition and adds them to the given + * collection. + * The collection is made parent of the actions. + * + * @param actionCollection The collection to add the actions to. + */ + void createActions(KActionCollection* actionCollection); + +private: + + /** + * All the edition actions. + */ + QList<QAction*> mActions; + + /** + * Action for "emphasis" phrase tag. + */ + QAction* mEmphasisAction; + + /** + * Action for "emphasis" phrase tag with "strong" attribute. + */ + QAction* mEmphasisStrongAction; + + /** + * Action for "filename" phrase tag. + */ + QAction* mFilenameAction; + + /** + * Action for "interface" phrase tag. + */ + QAction* mInterfaceAction; + + /** + * Action for "link" phrase tag. + */ + QAction* mLinkAction; + + /** + * Action for "nl" phrase tag. + */ + QAction* mNlAction; + + /** + * Action for "shortcut" phrase tag. + */ + QAction* mShortcutAction; + + /** + * Action for "para" structure tag. + */ + QAction* mParaAction; + + /** + * Action for "list" structure tag. + */ + QAction* mListAction; + + /** + * Action for "item" structure tag. + */ + QAction* mItemAction; + + /** + * The QTextEdit used for the edition of the text. + */ + QTextEdit* mTextEdit; + + /** + * Whether the cursor is between two paired start and end link elements or + * not. + */ + bool mLinkElementClosed; + + /** + * Returns the action that represents the given start tag. + * + * @param startTag The start tag to get its action. + * @return The action for the tag. + */ + QAction* actionForElement(const StartTag& startTag) const; + + /** + * Writes a simple tag for the given action. + * If the action is checked, a start tag for the element of the action is + * inserted in the text edit. If the action is not checked, an end tag is + * inserted. + * + * @param action The action to write its tag. + */ + void writeSimpleTagFor(const QAction* action); + + /** + * Creates a new dialog for link elements. + * The dialog contains a SemanticMarkupLinkWidget and two dialog buttons: Ok + * and Cancel. + * + * @param url The URL to show in the link widget of the dialog. + * @return The new dialog. + */ + QDialog* newLinkDialog(const QString& url) const; + +private Q_SLOTS: + + /** + * Updates the state of the edition actions based on the cursor position. + */ + void updateActionStates(); + + /** + * Behavior for "emphasis" phrase tag edition. + */ + void emphasis(); + + /** + * Behavior for "emphasis" phrase tag with "strong" attribute edition. + */ + void emphasisStrong(); + + /** + * Behavior for "filename" phrase tag edition. + */ + void filename(); + + /** + * Behavior for "interface" phrase tag edition. + */ + void interface(); + + /** + * Behavior for "link" phrase tag edition. + * Unlike other tags, to write the start link" element the user has to + * provide some information. A dialog is shown to the user to set the URL of + * the link. + * + * Moreover, the URL can be modified even on a closed "link" element. The + * "link" action can be triggered even on a closed element, and when the + * action is triggered, the dialog is shown to modify the URL. + */ + void link(); + + /** + * Behavior for "nl" phrase tag edition. + */ + void nl(); + + /** + * Behavior for "shortcut" phrase tag edition. + */ + void shortcut(); + + /** + * Behavior for "para" structure tag edition. + */ + void para(); + + /** + * Behavior for "list" structure tag edition. + */ + void list(); + + /** + * Behavior for "item" structure tag edition. + */ + void item(); + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupEdition.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.cpp 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "SemanticMarkupLinkWidget.h" + +#include "ui_SemanticMarkupLinkWidget.h" + +#ifdef QT_QTDBUS_FOUND +#include "RemoteObjectNameWidget.h" +#endif + +//public: + +SemanticMarkupLinkWidget::SemanticMarkupLinkWidget(QWidget* parent): + QWidget(parent) { + + ui = new Ui::SemanticMarkupLinkWidget(); + ui->setupUi(this); + +#ifdef QT_QTDBUS_FOUND + mRemoteObjectNameWidget = new RemoteObjectNameWidget(this); + + //Replace ui->widgetLinkLineEdit with mRemoteObjectNameWidget + ui->valueVerticalLayout->removeWidget(ui->widgetLinkLineEdit); + delete ui->widgetLinkLineEdit; + + ui->valueVerticalLayout->insertWidget(1, mRemoteObjectNameWidget); +#endif + + ui->genericLinkRadioButton->setChecked(true); + + connect(ui->genericLinkRadioButton, SIGNAL(toggled(bool)), + ui->genericLinkLineEdit, SLOT(setEnabled(bool))); + +#ifdef QT_QTDBUS_FOUND + mRemoteObjectNameWidget->setEnabled(false); + + connect(ui->widgetLinkRadioButton, SIGNAL(toggled(bool)), + mRemoteObjectNameWidget, SLOT(setEnabled(bool))); +#else + ui->widgetLinkLineEdit->setEnabled(false); + + connect(ui->widgetLinkRadioButton, SIGNAL(toggled(bool)), + ui->widgetLinkLineEdit, SLOT(setEnabled(bool))); +#endif +} + +SemanticMarkupLinkWidget::~SemanticMarkupLinkWidget() { + delete ui; +} + +QString SemanticMarkupLinkWidget::url() const { + if (ui->genericLinkRadioButton->isChecked()) { + return ui->genericLinkLineEdit->text(); + } + +#ifdef QT_QTDBUS_FOUND + return "widget:" + mRemoteObjectNameWidget->name(); +#else + return "widget:" + ui->widgetLinkLineEdit->text(); +#endif +} + +void SemanticMarkupLinkWidget::setUrl(const QString& url) { + if (!url.startsWith(QLatin1String("widget:"))) { + ui->genericLinkRadioButton->setChecked(true); + ui->genericLinkLineEdit->setText(url); + return; + } + + ui->widgetLinkRadioButton->setChecked(true); + QString widgetName = url.mid(QString("widget:").length()); +#ifdef QT_QTDBUS_FOUND + mRemoteObjectNameWidget->setName(widgetName); +#else + ui->widgetLinkLineEdit->setText(widgetName); +#endif +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.h 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,96 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef SEMANTICMARKUPLINKWIDGET_H +#define SEMANTICMARKUPLINKWIDGET_H + +#include <QWidget> + +#ifdef QT_QTDBUS_FOUND +class RemoteObjectNameWidget; +#endif + +namespace Ui { +class SemanticMarkupLinkWidget; +} + +/** + * Helper widget for the semantic markup edition to input the URL of a link. + * The widget contains a line edit where any URL can be written. However, it + * also offers support for a specific type of URLs: widget URLs. There are two + * radio buttons to select between a generic URL and a widget URL. + * + * Widget URLs represent a widget in the target application. When DBus module is + * enabled, the widget to link to can be selected using a RemoteObjectChooser. + * + * When the URL is got, the URL returned depends on the current type of URL + * selected. Widget URLs also contain the "widget:" protocol. When the URL is + * set, the line edit filled and radio button checked depend on the type of URL + * set. + */ +class SemanticMarkupLinkWidget: public QWidget { +Q_OBJECT +public: + + /** + * Creates a new SemanticMarkupLinkWidget. + * + * @param parent The parent QWidget. + */ + explicit SemanticMarkupLinkWidget(QWidget* parent = 0); + + /** + * Destroys this widget. + */ + virtual ~SemanticMarkupLinkWidget(); + + /** + * Returns the URL. + * If the URL is a widget URL, the returned URL contains the "widget:" + * protocol. + * + * @return The URL. + */ + QString url() const; + + /** + * Sets the URL. + * If the URL is a widget URL, the "widget:" protocol is not shown in its + * line edit. + * + * @param url The URL to set. + */ + void setUrl(const QString& url); + +private: + + /** + * The Ui Designer generated class. + */ + Ui::SemanticMarkupLinkWidget* ui; + +#ifdef QT_QTDBUS_FOUND + /** + * The widget to get the name of a remote object. + */ + RemoteObjectNameWidget* mRemoteObjectNameWidget; +#endif + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.h ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.ui =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.ui (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupLinkWidget.ui 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SemanticMarkupLinkWidget</class> + <widget class="QWidget" name="SemanticMarkupLinkWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string comment="@title">URL for <emphasis>link</emphasis> element</string> + </property> + <property name="whatsThis"> + <string comment="@info:whatsthis"><para>Set the <emphasis>url</emphasis> attribute for the <emphasis>link</emphasis> semantic element.</para> + +<para>Any URL can be set in the <interface>Generic</interface> field. For example, the URL of a webpage or the URL to link to a widget.</para> + +<para>However, to link to a widget it is better to just write the object name of the desired widget in the <interface>Widget</interface> field. Moreover, that field provides also text completion for the names and a dialog to choose the widget from a running target application.</para> + +<para>These advanced features, though, are not available in every system; if <application>KTutorial editor</application> was not built with <application>QtDBus</application> support only a plain text line will be shown. Again, only the name has to be written in the text line for the widget; <emphasis>widget:</emphasis> is automatically added to the URL when the dialog is accepted.</para></string> + </property> + <layout class="QVBoxLayout" name="SemanticMarkupLinkWidgetLayout"> + <item> + <layout class="QHBoxLayout" name="semanticMarkupLinkWidgetHorizontalLayout"> + <item> + <layout class="QVBoxLayout" name="radioButtonVerticalLayout"> + <item> + <widget class="QRadioButton" name="genericLinkRadioButton"> + <property name="text"> + <string comment="@option:radio Generic link URL">Generic</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="widgetLinkRadioButton"> + <property name="text"> + <string comment="@option:radio Widget link URL">Widget</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="valueVerticalLayout"> + <item> + <widget class="KLineEdit" name="genericLinkLineEdit"/> + </item> + <item> + <widget class="KLineEdit" name="widgetLinkLineEdit"/> + </item> + </layout> + </item> + </layout> + </item> + <item> + <spacer name="semanticMarkupLinkWidgetSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>KLineEdit</class> + <extends>QLineEdit</extends> + <header>klineedit.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> Added: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.cpp 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,137 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "SemanticMarkupParser.h" + +#include <QRegExp> +#include <QStringList> + +//public: + +SemanticMarkupParser::SemanticMarkupParser(): + mCursorIndex(-1), + mCursorInsideTag(false) { +} + +void SemanticMarkupParser::setCursorIndex(int cursorIndex) { + mCursorIndex = cursorIndex; +} + +void SemanticMarkupParser::parse(const QString& text) { + mCursorInsideTag = false; + mOpenElements.clear(); + mOpenElementsAtCursor.clear(); + mNotClosedChildElements.clear(); + + QRegExp startTagRegExp("<\\s*(\\w+)\\s*((\\w+=\"[^\"]*\"\\s*)*)>"); + QRegExp endTagRegExp("</\\s*(\\w+)\\s*>"); + QRegExp emptyTagRegExp("<\\s*(\\w+)\\s*((\\w+=\"[^\"]*\"\\s*)*)/>"); + QRegExp tagRegExp(startTagRegExp.pattern() + '|' + + endTagRegExp.pattern() + '|' + + emptyTagRegExp.pattern()); + + bool parsingReachedCursor = false; + + int parsingIndex = tagRegExp.indexIn(text); + while (parsingIndex >= 0) { + QString tag = tagRegExp.capturedTexts().at(0); + + if (mCursorIndex > parsingIndex && + mCursorIndex < parsingIndex + tagRegExp.matchedLength()) { + mCursorInsideTag = true; + mOpenElementsAtCursor.clear(); + return; + } + + if (!parsingReachedCursor && parsingIndex >= mCursorIndex) { + parsingReachedCursor = true; + mOpenElementsAtCursor = mOpenElements; + } + + if (startTagRegExp.exactMatch(tag)) { + StartTag startTag; + startTag.mName = startTagRegExp.capturedTexts().at(1); + startTag.mAttributes = startTagRegExp.capturedTexts().at(2); + startTag.mIndex = parsingIndex; + mOpenElements.insert(0, startTag); + } else if (endTagRegExp.exactMatch(tag)) { + QString endTagName = endTagRegExp.capturedTexts().at(1); + + int endElementIndex = indexOf(mOpenElements, endTagName); + if (endElementIndex >= 0) { + int index = 0; + while (index < endElementIndex) { + mNotClosedChildElements.append(mOpenElements.first()); + mOpenElements.removeFirst(); + index++; + } + mOpenElements.removeFirst(); + } + } + + parsingIndex = parsingIndex + tagRegExp.matchedLength(); + parsingIndex = tagRegExp.indexIn(text, parsingIndex); + } + + if (!parsingReachedCursor) { + mOpenElementsAtCursor = mOpenElements; + } +} + +bool SemanticMarkupParser::isElementClosed(const StartTag& startTag) const { + if (indexOf(mOpenElements, startTag) > -1 || + indexOf(mNotClosedChildElements, startTag) > -1) { + return false; + } + + return true; +} + +bool SemanticMarkupParser::isCursorInsideTag() const { + return mCursorInsideTag; +} + +QList<StartTag> SemanticMarkupParser::openElementsAtCursor() const { + return mOpenElementsAtCursor; +} + +//private: + +int SemanticMarkupParser::indexOf(const QList<StartTag>& startTags, + const QString& tagName) const { + for (int i=0; i<startTags.count(); ++i) { + if (startTags[i].mName == tagName) { + return i; + } + } + + return -1; +} + +int SemanticMarkupParser::indexOf(const QList<StartTag>& startTags, + const StartTag& startTag) const { + for (int i=0; i<startTags.count(); ++i) { + if (startTags[i].mName == startTag.mName && + startTags[i].mAttributes == startTag.mAttributes && + startTags[i].mIndex == startTag.mIndex) { + return i; + } + } + + return -1; +} Property changes on: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.cpp ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.h =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.h (rev 0) +++ trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.h 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,182 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef SEMANTICMARKUPPARSER_H +#define SEMANTICMARKUPPARSER_H + +#include <QList> +#include <QString> + +/** + * Structure to store the data of a XML start tag. + */ +struct StartTag { + + /** + * The tag name. + */ + QString mName; + + /** + * The attributes, if any. + */ + QString mAttributes; + + /** + * The index of the tag in the text (the position of the '<' character). + */ + int mIndex; + + /** + * Creates a new StartTag. + */ + StartTag(): + mName(""), + mAttributes(""), + mIndex(-1) { + } + +}; + +/** + * Simple parser for texts containing KUIT semantic markup. + * The parser is meant to provide SemanticMarkupEdition with information about + * the markup in the text. Thus, it is designed just for that purpose; it is not + * a general purpose parser. + * + * The parser supports XML where not all the elements have been closed. For + * example, it can parse a text with just a start tag, or a text where there + * is an unpaired start tag inside a valid element (one with paired start and + * end tags). + * + * The parser stores which elements have been opened, and when it reaches an end + * tag it regardes as closed the newest element that matches the end tag. For + * example, in <a><a><b></a></b>, when </a> + * is reached the second "a" element is regarded as closed. The </b> will + * be ignored, as the "b" element is no longer taken into account since its + * parent element was closed. Note, however, that the "b" element is not closed, + * and isElementClosed(StartTag) with that "b" element would return false. + * Passing the first "a" element would also return false; true would be returned + * only for the second "a" element. + * + * Before parsing a text, the cursor index has to be set using + * setCursorIndex(int). Then the text can be parsed using parse(QString). + * + * Once parsed, the SemanticMarkupParser object provides information about the + * parsed text regarding the cursor position. To update the information provided + * the text must be parsed again; that is, even if the only change is in the + * cursor index, parse(QString) has to be called again. + */ +class SemanticMarkupParser { +public: + + /** + * Creates a new SemanticMarkupParser. + */ + SemanticMarkupParser(); + + /** + * Sets the index of the cursor. + * + * @param cursorIndex The index to set. + */ + void setCursorIndex(int cursorIndex); + + /** + * Parses the given text, updating the state of this parser. + * + * @param text The text to parse. + */ + void parse(const QString& text); + + /** + * Returns whether the cursor is inside a tag or not. + * + * @return True if the cursor is inside a tag, false otherwise. + */ + bool isCursorInsideTag() const; + + /** + * Returns whether the given start tag was closed or not. + * + * @param startTag The tag to check. + * @return True if the start tag was closed, false otherwise. + */ + bool isElementClosed(const StartTag& startTag) const; + + /** + * Returns a list with all the start tags that were open at the cursor + * position. + * The order of the tags goes from the deepest one to the more general one. + * That is, for <a><b>, the first tag is "b" and the second one + * is "a". + * + * @return The start tags open at the cursor. + */ + QList<StartTag> openElementsAtCursor() const; + +private: + + /** + * The position of the cursor in the text. + */ + int mCursorIndex; + + /** + * Whether the cursor was inside a tag or not in the last parsed text. + */ + bool mCursorInsideTag; + + /** + * A list with all the open elements at the end of the last parsed text. + */ + QList<StartTag> mOpenElements; + + /** + * A list with all the open elements at the cursor of the last parsed text. + */ + QList<StartTag> mOpenElementsAtCursor; + + /** + * A list with all the elements that were not closed but some ancestor + * element was at the end of the last parsed text. + */ + QList<StartTag> mNotClosedChildElements; + + /** + * The position in the given list of a tag with the given name. + * + * @param startTags The list to check. + * @param tagName The name of the tag to find. + * @return The position of the tag name, or -1 if it is not found. + */ + int indexOf(const QList<StartTag>& startTags, const QString& tagName) const; + + /** + * The position in the given list of the given tag. + * + * @param startTags The list to check. + * @param startTag The tag to find. + * @return The position of the tag, or -1 if it is not found. + */ + int indexOf(const QList<StartTag>& startTags, + const StartTag& startTag) const; + +}; + +#endif Property changes on: trunk/ktutorial/ktutorial-editor/src/view/SemanticMarkupParser.h ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/ktutorial/ktutorial-editor/src/view/StepDataWidget.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/src/view/StepDataWidget.cpp 2011-04-30 17:31:13 UTC (rev 300) +++ trunk/ktutorial/ktutorial-editor/src/view/StepDataWidget.cpp 2011-05-05 14:39:15 UTC (rev 301) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -18,7 +18,11 @@ #include "StepDataWidget.h" +#include <KActionCollection> +#include <KToolBar> + #include "ui_StepDataWidget.h" +#include "SemanticMarkupEdition.h" #include "../commands/StepCommands.h" #include "../data/Step.h" @@ -30,6 +34,17 @@ ui = new Ui::StepDataWidget(); ui->setupUi(this); + KToolBar* toolBar = new KToolBar(this); + int textEditIndex = ui->textLayout->indexOf(ui->textTextEdit); + ui->textLayout->insertWidget(textEditIndex, toolBar); + + KActionCollection* editionActions = new KActionCollection(this); + editionActions->addAssociatedWidget(toolBar); + + SemanticMarkupEdition* semanticMarkupEdition = + new SemanticMarkupEdition(ui->textTextEdit); + semanticMarkupEdition->createActions(editionActions); + ui->idLineEdit->setText(step->id()); ui->textTextEdit->setPlainText(step->text()); } Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt 2011-04-30 17:31:13 UTC (rev 300) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/CMakeLists.txt 2011-05-05 14:39:15 UTC (rev 301) @@ -26,6 +26,9 @@ NewWaitForWidget ReactionTreeItem ReactionWidget + SemanticMarkupEdition + SemanticMarkupLinkWidget + SemanticMarkupParser StepCustomCodeWidget StepDataWidget StepTreeItem @@ -79,6 +82,9 @@ NewWaitForWidget ReactionTreeItem ReactionWidget + SemanticMarkupEdition + SemanticMarkupLinkWidget + SemanticMarkupParser StepCustomCodeWidget StepDataWidget StepTreeItem Added: trunk/ktutorial/ktutorial-editor/tests/unit/view/SemanticMarkupEditionTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/SemanticMarkupEditionTest.cpp (rev 0) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/SemanticMarkupEditionTest.cpp 2011-05-05 14:39:15 UTC (rev 301) @@ -0,0 +1,842 @@ +/*************************************************************************** + * Copyright (C) 2011 by Daniel Calviño Sánchez * + * dan...@gm... * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include <QtTest> + +#include "SemanticMarkupEdition.h" + +#include <QAction> +#include <QTextEdit> + +#include <KActionCollection> +#include <KPushButton> + +#include "SemanticMarkupLinkWidget.h" + +class SemanticMarkupEditionTest: public QObject { +Q_OBJECT + +private slots: + + void init(); + void cleanup(); + + void testConstructor(); + + void testCreateActions(); + + void testEmphasis(); + void testEmphasisStrong(); + void testFilename(); + void testInterface(); + void testLink(); + void testLinkUpdate(); + void testLinkUpdateWithoutUrlAttribute(); + void testNl(); + void testShortcut(); + void testPara(); + void testList(); + void testItem(); + + void testCursorInStartTag(); + void testCursorInEndTag(); + void testCursorInEmptyElementTag(); + + void testCursorBetweenPhraseTags(); + void testCursorBetweenLinkTags(); + void testCursorBetweenLinkTagsWithChildElement(); + void testCursorBetweenParaTags(); + void testCursorBetweenParaTagsWithPhraseTagNotClosed(); + void testCursorBetweenListTags(); + void testCursorBetweenListTagsWithItemTagNotClosed(); + void testCursorBetweenItemTags(); + void testCursorBetweenItemTagsWithPhraseTagNotClosed(); + + void testCursorBetweenEndAndStartTags(); + void testCursorBetweenUnpairedTags(); + void testCursorBetweenUnpairedTagsClosedLater(); + void testCursorBetweenTwoStartTags(); + void testCursorBetweenTwoEndTags(); + + void testChangeTextWithoutChangingCursorPosition(); + +private: + + SemanticMarkupEdition* mSemanticMarkupEdition; + KActionCollection* mActionCollection; + QTextEdit* mTextEdit; + + QAction* mEmphasisAction; + QAction* mEmphasisStrongAction; + QAction* mFilenameAction; + QAction* mInterfaceAction; + QAction* mLinkAction; + QAction* mNlAction; + QAction* mShortcutAction; + QAction* mParaAction; + QAction* mListAction; + QAction* mItemAction; + + void queueAssertLinkUrl(const QString& url, int timeToWait); + void queueSetLinkUrl(const QString& url, int timeToWait); + void queueCancelSetLinkUrl(const QString& url, int timeToWait); + + void moveCursorTo(int position) const; + + void assertOnlyEnabled(const QList<QAction*>& enabledActions) const; + void assertOnlyDisabled(const QList<QAction*>& disabledActions) const; + void assertOnlyChecked(const QList<QAction*>& checkedActions) const; + + void assertTextStartTriggerTextEndTriggerText(QAction* action, + const QString& tagName) const; + +}; + +void SemanticMarkupEditionTest::init() { + mTextEdit = new QTextEdit(); + mSemanticMarkupEdition = new SemanticMarkupEdition(mTextEdit); + mActionCollection = new KActionCollection(this); + + mSemanticMarkupEdition->createActions(mActionCollection); + + mEmphasisAction = mActionCollection->action("kuit-edition-phrase-emphasis"); + mEmphasisStrongAction = + mActionCollection->action("kuit-edition-phrase-emphasis-strong"); + mFilenameAction = mActionCollection->action("kuit-edition-phrase-filename"); + mInterfaceAction = + mActionCollection->action("kuit-edition-phrase-interface"); + mLinkAction = mActionCollection->action("kuit-edition-phrase-link"); + mNlAction = mActionCollection->action("kuit-edition-phrase-nl"); + mShortcutAction = mActionCollection->action("kuit-edition-phrase-shortcut"); + mParaAction = mActionCollection->action("kuit-edition-structure-para"); + mListAction = mActionCollection->action("kuit-edition-structure-list"); + mItemAction = mActionCollection->action("kuit-edition-structure-item"); +} + +void SemanticMarkupEditionTest::cleanup() { + delete mActionCollection; + delete mTextEdit; +} + +void SemanticMarkupEditionTest::testConstructor() { + QTextEdit textEdit; + SemanticMarkupEdition* semanticMarkupEdition = + new SemanticMarkupEdition(&textEdit); + + QCOMPARE(semanticMarkupEdition->parent(), &textEdit); +} + +void SemanticMarkupEditionTest::testCreateActions() { + QTextEdit textEdit; + SemanticMarkupEdition* semanticMarkupEdition = + new SemanticMarkupEdition(&textEdit); + KActionCollection actionCollection(this); + + semanticMarkupEdition->createActions(&actionCollection); + + QCOMPARE(actionCollection.count(), 10); + QVERIFY(actionCollection.action("kuit-edition-phrase-emphasis")); + QVERIFY(actionCollectio... [truncated message content] |