diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 2c9f24cf48f7469b701de70cee8eeb945e1a8ed7..7cc4087a8e40d7ad5daa3782ac915d6755a389c2 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -426,6 +426,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er cmd = am->registerAction(m_showInFinder, ProjectExplorer::Constants::SHOWINFINDER, globalcontext); mfilec->addAction(cmd, Constants::G_FILE_OPEN); + mfolder->addAction(cmd, Constants::G_FOLDER_FILES); #endif // Open With menu @@ -592,6 +593,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er globalcontext); mproject->addAction(cmd, Constants::G_PROJECT_FILES); msubProject->addAction(cmd, Constants::G_PROJECT_FILES); + mfolder->addAction(cmd, Constants::G_FOLDER_FILES); // add existing file action m_addExistingFilesAction = new QAction(tr("Add Existing Files..."), this); @@ -599,6 +601,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er globalcontext); mproject->addAction(cmd, Constants::G_PROJECT_FILES); msubProject->addAction(cmd, Constants::G_PROJECT_FILES); + mfolder->addAction(cmd, Constants::G_FOLDER_FILES); // remove file action m_removeFileAction = new QAction(tr("Remove File..."), this); @@ -1676,9 +1679,9 @@ void ProjectExplorerPlugin::updateContextMenuActions() void ProjectExplorerPlugin::addNewFile() { - if (!m_currentNode && m_currentNode->nodeType() == ProjectNodeType) - return; - const QString location = QFileInfo(m_currentNode->path()).dir().absolutePath(); + QTC_ASSERT(m_currentNode, return) + QFileInfo fi(m_currentNode->path()); + const QString location = (fi.isDir() ? fi.absoluteFilePath() : fi.absolutePath()); Core::ICore::instance()->showNewItemDialog(tr("New File", "Title of dialog"), Core::IWizard::wizardsOfKind(Core::IWizard::FileWizard) + Core::IWizard::wizardsOfKind(Core::IWizard::ClassWizard), @@ -1687,11 +1690,12 @@ void ProjectExplorerPlugin::addNewFile() void ProjectExplorerPlugin::addExistingFiles() { - if (!m_currentNode && m_currentNode->nodeType() == ProjectNodeType) - return; - ProjectNode *projectNode = qobject_cast<ProjectNode*>(m_currentNode); + QTC_ASSERT(m_currentNode, return) + + ProjectNode *projectNode = qobject_cast<ProjectNode*>(m_currentNode->projectNode()); Core::ICore *core = Core::ICore::instance(); - const QString dir = QFileInfo(m_currentNode->path()).dir().absolutePath(); + QFileInfo fi(m_currentNode->path()); + const QString dir = (fi.isDir() ? fi.absoluteFilePath() : fi.absolutePath()); QStringList fileNames = QFileDialog::getOpenFileNames(core->mainWindow(), tr("Add Existing Files"), dir); if (fileNames.isEmpty()) return; @@ -1741,8 +1745,7 @@ void ProjectExplorerPlugin::addExistingFiles() void ProjectExplorerPlugin::openFile() { - if (!m_currentNode) - return; + QTC_ASSERT(m_currentNode, return) Core::EditorManager *em = Core::EditorManager::instance(); em->openEditor(m_currentNode->path()); em->ensureEditorManagerVisible(); @@ -1751,8 +1754,7 @@ void ProjectExplorerPlugin::openFile() #ifdef Q_OS_MAC void ProjectExplorerPlugin::showInFinder() { - if (!m_currentNode) - return; + QTC_ASSERT(m_currentNode, return) QProcess::execute("/usr/bin/osascript", QStringList() << "-e" << QString("tell application \"Finder\" to reveal POSIX file \"%1\"") @@ -1765,8 +1767,7 @@ void ProjectExplorerPlugin::showInFinder() void ProjectExplorerPlugin::removeFile() { - if (!m_currentNode && m_currentNode->nodeType() == FileNodeType) - return; + QTC_ASSERT(m_currentNode && m_currentNode->nodeType() == FileNodeType, return) FileNode *fileNode = qobject_cast<FileNode*>(m_currentNode); Core::ICore *core = Core::ICore::instance(); @@ -1937,7 +1938,7 @@ void ProjectExplorerPlugin::populateOpenWithMenu() } } // matches } - m_openWithMenu->setEnabled(anyMatches); + m_openWithMenu->setEnabled(anyMatches); } void ProjectExplorerPlugin::openWithMenuTriggered(QAction *action) diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp index dcde9968e59e52bda4c658319f2435c4a7de6698..da793af74a83eb705fab463faa22042141703085 100644 --- a/src/plugins/projectexplorer/projectmodels.cpp +++ b/src/plugins/projectexplorer/projectmodels.cpp @@ -961,11 +961,15 @@ FolderNode *FlatModel::visibleFolderNode(FolderNode *node) const bool FlatModel::filter(Node *node) const { bool isHidden = false; - if (ProjectNode *projectNode = qobject_cast<ProjectNode*>(node)) { + if (node->nodeType() == SessionNodeType) { + isHidden = false; + } else if (ProjectNode *projectNode = qobject_cast<ProjectNode*>(node)) { if (m_filterProjects && projectNode->parentFolderNode() != m_rootNode) isHidden = !projectNode->hasTargets(); - } - if (FileNode *fileNode = qobject_cast<FileNode*>(node)) { + } else if (node->nodeType() == FolderNodeType) { + if (m_filterProjects) + isHidden = true; + } else if (FileNode *fileNode = qobject_cast<FileNode*>(node)) { if (m_filterGeneratedFiles) isHidden = fileNode->isGenerated(); } diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp index 6bd2205908b27a73662cacfac1a39bc2e1b532d6..7b7b54cf388e50d5daff8358e1b9a81546215810 100644 --- a/src/plugins/projectexplorer/projectnodes.cpp +++ b/src/plugins/projectexplorer/projectnodes.cpp @@ -153,7 +153,11 @@ FolderNode::FolderNode(const QString &folderPath) : Node(FolderNodeType, folderPath), m_folderName(folderPath) { - m_icon = QApplication::style()->standardIcon(QStyle::SP_DirIcon); + static QIcon dirIcon; + if (dirIcon.isNull()) { + dirIcon = QApplication::style()->standardIcon(QStyle::SP_DirIcon); + } + m_icon = dirIcon; } FolderNode::~FolderNode() @@ -549,8 +553,13 @@ void ProjectNode::watcherDestroyed(QObject *watcher) /*! Sort pointers to FileNodes */ -bool ProjectNode::sortNodesByPath(Node *f1, Node *f2) { - return f1->path() < f2->path(); +bool ProjectNode::sortNodesByPath(Node *n1, Node *n2) { + return n1->path() < n2->path(); +} + +bool ProjectNode::sortFolderNodesByName(FolderNode *f1, FolderNode *f2) +{ + return f1->name() < f2->name(); } /*! diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h index 958d524b048d0d2aa798c6e94acfac4a49b1ee1f..cfd0d7ca49f0be3f8121949285e2eaa1c5d98a78 100644 --- a/src/plugins/projectexplorer/projectnodes.h +++ b/src/plugins/projectexplorer/projectnodes.h @@ -136,10 +136,10 @@ public: virtual void accept(NodesVisitor *visitor); -protected: void setFolderName(const QString &name); void setIcon(const QIcon &icon); +protected: QList<FolderNode*> m_subFolderNodes; QList<FileNode*> m_fileNodes; @@ -193,6 +193,7 @@ public: void accept(NodesVisitor *visitor); static bool sortNodesByPath(Node *n1, Node *n2); + static bool sortFolderNodesByName(FolderNode *f1, FolderNode *f2); protected: // this is just the in-memory representation, a subclass diff --git a/src/plugins/qt4projectmanager/images/forms.png b/src/plugins/qt4projectmanager/images/forms.png new file mode 100644 index 0000000000000000000000000000000000000000..04059d99f0eb4db720066140f8b361ea24238d6c Binary files /dev/null and b/src/plugins/qt4projectmanager/images/forms.png differ diff --git a/src/plugins/qt4projectmanager/images/headers.png b/src/plugins/qt4projectmanager/images/headers.png new file mode 100644 index 0000000000000000000000000000000000000000..ff7797fa91aac20425d04c122debd96beb3ba4b2 Binary files /dev/null and b/src/plugins/qt4projectmanager/images/headers.png differ diff --git a/src/plugins/qt4projectmanager/images/qt_qrc.png b/src/plugins/qt4projectmanager/images/qt_qrc.png new file mode 100644 index 0000000000000000000000000000000000000000..3643f37e1a3ede6454fe3d14224e6fc72f73a5db Binary files /dev/null and b/src/plugins/qt4projectmanager/images/qt_qrc.png differ diff --git a/src/plugins/qt4projectmanager/images/sources.png b/src/plugins/qt4projectmanager/images/sources.png new file mode 100644 index 0000000000000000000000000000000000000000..60dc177b86430077a9413f4419cba0c2f9a1c979 Binary files /dev/null and b/src/plugins/qt4projectmanager/images/sources.png differ diff --git a/src/plugins/qt4projectmanager/images/unknown.png b/src/plugins/qt4projectmanager/images/unknown.png new file mode 100644 index 0000000000000000000000000000000000000000..0f2ad1818e23e7a6297b24ee61ffee458dc7fcb8 Binary files /dev/null and b/src/plugins/qt4projectmanager/images/unknown.png differ diff --git a/src/plugins/qt4projectmanager/qt4nodes.cpp b/src/plugins/qt4projectmanager/qt4nodes.cpp index 1b7f1ea22270db833158819bbb77e9bf5bb92a18..a47f43001efbd7bed506cedb77c1d7a61ed46ade 100644 --- a/src/plugins/qt4projectmanager/qt4nodes.cpp +++ b/src/plugins/qt4projectmanager/qt4nodes.cpp @@ -116,6 +116,217 @@ void Qt4PriFileNode::scheduleUpdate() m_qt4ProFileNode->scheduleUpdate(); } +namespace Qt4ProjectManager { +namespace Internal { + struct InternalNode + { + QMap<QString, InternalNode*> subnodes; + QStringList files; + ProjectExplorer::FileType type; + QString fullName; + QIcon icon; + + ~InternalNode() + { + qDeleteAll(subnodes); + } + + // Creates a tree structure from a list of absolute file paths. + // Empty directories are compressed into a single entry with a longer path. + // * project + // * /absolute/path + // * file1 + // * relative + // * path1 + // * file1 + // * file2 + // * path2 + // * file1 + void create(const QString &projectDir, const QStringList &newFilePaths, ProjectExplorer::FileType type) + { + static const QChar separator = QChar('/'); + const QString projectDirWithSeparator = projectDir + separator; + int projectDirWithSeparatorLength = projectDirWithSeparator.length(); + foreach (const QString &file, newFilePaths) { + QString fileWithoutPrefix; + bool isRelative; + if (file.startsWith(projectDirWithSeparator)) { + isRelative = true; + fileWithoutPrefix = file.mid(projectDirWithSeparatorLength); + } else { + isRelative = false; + fileWithoutPrefix = file; + } + QStringList parts = fileWithoutPrefix.split(separator, QString::SkipEmptyParts); + if (!isRelative && parts.count() > 0) + parts[0].prepend(separator); + QStringListIterator it(parts); + InternalNode *currentNode = this; + QString path = (isRelative ? projectDir : ""); + while (it.hasNext()) { + const QString &key = it.next(); + path += separator + key; + if (it.hasNext()) { // key is directory + if (!currentNode->subnodes.contains(key)) { + InternalNode *val = new InternalNode; + val->type = type; + val->fullName = path; + currentNode->subnodes.insert(key, val); + currentNode = val; + } else { + currentNode = currentNode->subnodes.value(key); + } + } else { // key is filename + currentNode->files.append(file); + } + } + } + this->compress(); + } + + // Removes folder nodes with only a single sub folder in it + void compress() + { + static const QChar separator = QChar('/'); + QMap<QString, InternalNode*> newSubnodes; + QMapIterator<QString, InternalNode*> i(subnodes); + while (i.hasNext()) { + i.next(); + i.value()->compress(); + if (i.value()->files.isEmpty() && i.value()->subnodes.size() == 1) { + QString key = i.value()->subnodes.keys().at(0); + newSubnodes.insert(i.key()+separator+key, i.value()->subnodes.value(key)); + i.value()->subnodes.clear(); + delete i.value(); + } else { + newSubnodes.insert(i.key(), i.value()); + } + } + subnodes = newSubnodes; + } + + // Makes the projectNode's subtree below the given folder match this internal node's subtree + void updateSubFolders(Qt4PriFileNode *projectNode, ProjectExplorer::FolderNode *folder) + { + updateFiles(projectNode, folder, type); + + // update folders + QList<FolderNode *> existingFolderNodes; + foreach (FolderNode *node, folder->subFolderNodes()) { + if (node->nodeType() != ProjectNodeType) + existingFolderNodes << node; + } + + QList<FolderNode *> foldersToRemove; + QList<FolderNode *> foldersToAdd; + QList<InternalNode *> internalNodesToUpdate; + QList<FolderNode *> folderNodesToUpdate; + + // newFolders is already sorted + qSort(existingFolderNodes.begin(), existingFolderNodes.end(), ProjectNode::sortFolderNodesByName); + + QList<FolderNode*>::const_iterator existingNodeIter = existingFolderNodes.constBegin(); + QMap<QString, InternalNode*>::const_iterator newNodeIter = subnodes.constBegin();; + while (existingNodeIter != existingFolderNodes.constEnd() + && newNodeIter != subnodes.constEnd()) { + if ((*existingNodeIter)->name() < newNodeIter.key()) { + foldersToRemove << *existingNodeIter; + ++existingNodeIter; + } else if ((*existingNodeIter)->name() > newNodeIter.key()) { + FolderNode *newNode = new FolderNode(newNodeIter.value()->fullName); + newNode->setFolderName(newNodeIter.key()); + if (!newNodeIter.value()->icon.isNull()) + newNode->setIcon(newNodeIter.value()->icon); + foldersToAdd << newNode; + internalNodesToUpdate << newNodeIter.value(); + folderNodesToUpdate << newNode; + ++newNodeIter; + } else { // *existingNodeIter->path() == *newPathIter + internalNodesToUpdate << newNodeIter.value(); + folderNodesToUpdate << *existingNodeIter; + ++existingNodeIter; + ++newNodeIter; + } + } + + while (existingNodeIter != existingFolderNodes.constEnd()) { + foldersToRemove << *existingNodeIter; + ++existingNodeIter; + } + while (newNodeIter != subnodes.constEnd()) { + FolderNode *newNode = new FolderNode(newNodeIter.value()->fullName); + newNode->setFolderName(newNodeIter.key()); + if (!newNodeIter.value()->icon.isNull()) + newNode->setIcon(newNodeIter.value()->icon); + foldersToAdd << newNode; + internalNodesToUpdate << newNodeIter.value(); + folderNodesToUpdate << newNode; + ++newNodeIter; + } + + if (!foldersToRemove.isEmpty()) + projectNode->removeFolderNodes(foldersToRemove, folder); + if (!foldersToAdd.isEmpty()) + projectNode->addFolderNodes(foldersToAdd, folder); + + QList<FolderNode *>::const_iterator folderIt = folderNodesToUpdate.constBegin(); + QList<InternalNode *>::const_iterator iNodeIt = internalNodesToUpdate.constBegin(); + while (folderIt != folderNodesToUpdate.constEnd() + && iNodeIt != internalNodesToUpdate.constEnd()) { + (*iNodeIt)->updateSubFolders(projectNode, *folderIt); + ++folderIt; + ++iNodeIt; + } + } + + // Makes the folder's files match this internal node's file list + void updateFiles(Qt4PriFileNode *projectNode, FolderNode *folder, FileType type) + { + QList<FileNode*> existingFileNodes; + foreach (FileNode *fileNode, folder->fileNodes()) { + if (fileNode->fileType() == type && !fileNode->isGenerated()) + existingFileNodes << fileNode; + } + + QList<FileNode*> filesToRemove; + QList<FileNode*> filesToAdd; + + qSort(files); + qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath); + + QList<FileNode*>::const_iterator existingNodeIter = existingFileNodes.constBegin(); + QList<QString>::const_iterator newPathIter = files.constBegin(); + while (existingNodeIter != existingFileNodes.constEnd() + && newPathIter != files.constEnd()) { + if ((*existingNodeIter)->path() < *newPathIter) { + filesToRemove << *existingNodeIter; + ++existingNodeIter; + } else if ((*existingNodeIter)->path() > *newPathIter) { + filesToAdd << new FileNode(*newPathIter, type, false); + ++newPathIter; + } else { // *existingNodeIter->path() == *newPathIter + ++existingNodeIter; + ++newPathIter; + } + } + while (existingNodeIter != existingFileNodes.constEnd()) { + filesToRemove << *existingNodeIter; + ++existingNodeIter; + } + while (newPathIter != files.constEnd()) { + filesToAdd << new FileNode(*newPathIter, type, false); + ++newPathIter; + } + + if (!filesToRemove.isEmpty()) + projectNode->removeFileNodes(filesToRemove, folder); + if (!filesToAdd.isEmpty()) + projectNode->addFileNodes(filesToAdd, folder); + } + }; +} // Internal namespace +} // namespace + void Qt4PriFileNode::update(ProFile *includeFile, ProFileReader *reader) { Q_ASSERT(includeFile); @@ -131,6 +342,30 @@ void Qt4PriFileNode::update(ProFile *includeFile, ProFileReader *reader) << ProjectExplorer::FormType << ProjectExplorer::ResourceType << ProjectExplorer::UnknownFileType); + static QStringList fileTypeNames = + QStringList() << tr("Headers") + << tr("Sources") + << tr("Forms") + << tr("Resources") + << tr("Other files"); + static QList<QIcon> fileTypeIcons; + if (fileTypeIcons.isEmpty()) { + QStringList iconPaths; + iconPaths << ":/qt4projectmanager/images/headers.png" + << ":/qt4projectmanager/images/sources.png" + << ":/qt4projectmanager/images/forms.png" + << ":/qt4projectmanager/images/qt_qrc.png" + << ":/qt4projectmanager/images/unknown.png"; + foreach (const QString &iconPath, iconPaths) { + QIcon dirIcon; + Core::FileIconProvider *iconProvider = Core::FileIconProvider::instance(); + QPixmap dirIconPixmap = iconProvider->overlayIcon(QStyle::SP_DirIcon, + QIcon(iconPath), + QSize(16, 16)); + dirIcon.addPixmap(dirIconPixmap); + fileTypeIcons.append(dirIcon); + } + } const QString &projectDir = m_qt4ProFileNode->m_projectDir; @@ -140,8 +375,11 @@ void Qt4PriFileNode::update(ProFile *includeFile, ProFileReader *reader) baseVPaths += reader->absolutePathValues("DEPENDPATH", projectDir); baseVPaths.removeDuplicates(); + InternalNode contents; + // update files - foreach (FileType type, fileTypes) { + for (int i = 0; i < fileTypes.size(); ++i) { + FileType type = fileTypes.at(i); const QStringList qmakeVariables = varNames(type); QStringList newFilePaths; @@ -157,47 +395,17 @@ void Qt4PriFileNode::update(ProFile *includeFile, ProFileReader *reader) } newFilePaths.removeDuplicates(); - QList<FileNode*> existingFileNodes; - foreach (FileNode *fileNode, fileNodes()) { - if (fileNode->fileType() == type && !fileNode->isGenerated()) - existingFileNodes << fileNode; - } - - QList<FileNode*> toRemove; - QList<FileNode*> toAdd; - - qSort(newFilePaths); - qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath); - - QList<FileNode*>::const_iterator existingNodeIter = existingFileNodes.constBegin(); - QList<QString>::const_iterator newPathIter = newFilePaths.constBegin(); - while (existingNodeIter != existingFileNodes.constEnd() - && newPathIter != newFilePaths.constEnd()) { - if ((*existingNodeIter)->path() < *newPathIter) { - toRemove << *existingNodeIter; - ++existingNodeIter; - } else if ((*existingNodeIter)->path() > *newPathIter) { - toAdd << new FileNode(*newPathIter, type, false); - ++newPathIter; - } else { // *existingNodeIter->path() == *newPathIter - ++existingNodeIter; - ++newPathIter; - } - } - while (existingNodeIter != existingFileNodes.constEnd()) { - toRemove << *existingNodeIter; - ++existingNodeIter; - } - while (newPathIter != newFilePaths.constEnd()) { - toAdd << new FileNode(*newPathIter, type, false); - ++newPathIter; + if (!newFilePaths.isEmpty()) { + InternalNode *subfolder = new InternalNode; + subfolder->type = type; + subfolder->icon = fileTypeIcons.at(i); + subfolder->fullName = m_projectDir; + contents.subnodes.insert(fileTypeNames.at(i), subfolder); + // create the hierarchy with subdirectories + subfolder->create(m_projectDir, newFilePaths, type); } - - if (!toRemove.isEmpty()) - removeFileNodes(toRemove, this); - if (!toAdd.isEmpty()) - addFileNodes(toAdd, this); } + contents.updateSubFolders(this, this); } QList<ProjectNode::ProjectAction> Qt4PriFileNode::supportedActions() const @@ -1084,7 +1292,7 @@ void Qt4ProFileNode::createUiCodeModelSupport() foreach (FileNode *uiFile, uiFiles) { QString uiHeaderFilePath = QString("%1/ui_%2.h").arg(uiDir, QFileInfo(uiFile->path()).completeBaseName()); - uiHeaderFilePath = QDir::cleanPath(uiHeaderFilePath); + uiHeaderFilePath = QDir::cleanPath(uiHeaderFilePath); // qDebug()<<"code model support for "<<uiFile->path()<<" "<<uiHeaderFilePath; QMap<QString, Qt4UiCodeModelSupport *>::iterator it = oldCodeModelSupport.find(uiFile->path()); diff --git a/src/plugins/qt4projectmanager/qt4nodes.h b/src/plugins/qt4projectmanager/qt4nodes.h index 6a491dc6e4a3ebe9bb723922666d9139d5600384..f1240e7cdda174167a0f2f5aa7f2a45e15135c9a 100644 --- a/src/plugins/qt4projectmanager/qt4nodes.h +++ b/src/plugins/qt4projectmanager/qt4nodes.h @@ -104,6 +104,7 @@ enum Qt4Variable { class Qt4PriFileNode; class Qt4ProFileNode; +struct InternalNode; // Implements ProjectNode for qt4 pro files class Qt4PriFileNode : public ProjectExplorer::ProjectNode @@ -175,6 +176,8 @@ private: // managed by Qt4ProFileNode friend class Qt4ProFileNode; + // internal temporary subtree representation + friend class InternalNode; }; // Implements ProjectNode for qt4 pro files diff --git a/src/plugins/qt4projectmanager/qt4projectmanager.qrc b/src/plugins/qt4projectmanager/qt4projectmanager.qrc index f733770e2f4ec0cfdd07971989e6bfbe07f2a600..6b17ee4244e1e1633e8712c5db59c78e13396c4d 100644 --- a/src/plugins/qt4projectmanager/qt4projectmanager.qrc +++ b/src/plugins/qt4projectmanager/qt4projectmanager.qrc @@ -1,9 +1,14 @@ <RCC> - <qresource prefix="/qt4projectmanager" > + <qresource prefix="/qt4projectmanager"> <file>images/window.png</file> <file>images/run_qmake.png</file> <file>images/run_qmake_small.png</file> <file>images/qt_project.png</file> <file>Qt4ProjectManager.mimetypes.xml</file> + <file>images/forms.png</file> + <file>images/headers.png</file> + <file>images/qt_qrc.png</file> + <file>images/sources.png</file> + <file>images/unknown.png</file> </qresource> </RCC> diff --git a/tests/manual/qt4projectmanager/projecttree/foo.txt b/tests/manual/qt4projectmanager/projecttree/foo.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/headers/bar.h b/tests/manual/qt4projectmanager/projecttree/headers/bar.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/headers/foo.h b/tests/manual/qt4projectmanager/projecttree/headers/foo.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/main.cpp b/tests/manual/qt4projectmanager/projecttree/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/main.h b/tests/manual/qt4projectmanager/projecttree/main.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/prifile/headers/prifileinc.h b/tests/manual/qt4projectmanager/projecttree/prifile/headers/prifileinc.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/prifile/prifile.pri b/tests/manual/qt4projectmanager/projecttree/prifile/prifile.pri new file mode 100644 index 0000000000000000000000000000000000000000..5b1c723c8d29cbeb5b7becb753dc9b9da5da91c4 --- /dev/null +++ b/tests/manual/qt4projectmanager/projecttree/prifile/prifile.pri @@ -0,0 +1,2 @@ +SOURCES += $$PWD/sources/prifileinc.cpp +HEADERS += $$PWD/headers/prifileinc.h diff --git a/tests/manual/qt4projectmanager/projecttree/prifile/sources/prifileinc.cpp b/tests/manual/qt4projectmanager/projecttree/prifile/sources/prifileinc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/projecttree.pro b/tests/manual/qt4projectmanager/projecttree/projecttree.pro new file mode 100644 index 0000000000000000000000000000000000000000..ec8fdc57013e9081300563bef03d5aef74800915 --- /dev/null +++ b/tests/manual/qt4projectmanager/projecttree/projecttree.pro @@ -0,0 +1,48 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2009-05-12T12:31:37 +# +#------------------------------------------------- + +TARGET = projecttree +TEMPLATE = app + +include(prifile/prifile.pri) + +SOURCES += main.cpp\ + widget.cpp \ + /etc/cups/cupsd.conf \ + /etc/cups/printers.conf \ + /etc/apache2/mime.types \ + /etc/apache2/extra/httpd-info.conf \ + ../projecttree_data1/a/foo.cpp \ + ../projecttree_data1/b/foo.cpp \ + ../projecttree_data2/a/bar.cpp \ + ../projecttree_data2/a/sub/bar2.cpp \ + ../projecttree_data3/path/bar.cpp \ + subpath/a/foo.cpp \ + subpath/b/foo.cpp \ + sub2/a/bar.cpp \ + sub2/a/sub/bar2.cpp \ + uniquesub/path/bar.cpp \ + sources/foo.cpp \ + sources/bar.cpp + +HEADERS += main.h\ + widget.h \ + ../projecttree_data1/a/foo.h \ + ../projecttree_data1/b/foo.h \ + ../projecttree_data2/a/bar.h \ + ../projecttree_data2/a/sub/bar2.h \ + ../projecttree_data3/path/bar.h \ + subpath/a/foo.h \ + subpath/b/foo.h \ + sub2/a/bar.h \ + sub2/a/sub/bar2.h \ + uniquesub/path/bar.h \ + headers/foo.h \ + headers/bar.h + +FORMS += widget.ui +RESOURCES += resource.qrc +OTHER_FILES += foo.txt diff --git a/tests/manual/qt4projectmanager/projecttree/resource.qrc b/tests/manual/qt4projectmanager/projecttree/resource.qrc new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/sources/bar.cpp b/tests/manual/qt4projectmanager/projecttree/sources/bar.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/sources/foo.cpp b/tests/manual/qt4projectmanager/projecttree/sources/foo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/sub2/a/bar.cpp b/tests/manual/qt4projectmanager/projecttree/sub2/a/bar.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/sub2/a/bar.h b/tests/manual/qt4projectmanager/projecttree/sub2/a/bar.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/sub2/a/sub/bar2.cpp b/tests/manual/qt4projectmanager/projecttree/sub2/a/sub/bar2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/sub2/a/sub/bar2.h b/tests/manual/qt4projectmanager/projecttree/sub2/a/sub/bar2.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/subpath/a/foo.cpp b/tests/manual/qt4projectmanager/projecttree/subpath/a/foo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/subpath/a/foo.h b/tests/manual/qt4projectmanager/projecttree/subpath/a/foo.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/subpath/b/foo.cpp b/tests/manual/qt4projectmanager/projecttree/subpath/b/foo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/subpath/b/foo.h b/tests/manual/qt4projectmanager/projecttree/subpath/b/foo.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/uniquesub/path/bar.cpp b/tests/manual/qt4projectmanager/projecttree/uniquesub/path/bar.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/uniquesub/path/bar.h b/tests/manual/qt4projectmanager/projecttree/uniquesub/path/bar.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/widget.cpp b/tests/manual/qt4projectmanager/projecttree/widget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/widget.h b/tests/manual/qt4projectmanager/projecttree/widget.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree/widget.ui b/tests/manual/qt4projectmanager/projecttree/widget.ui new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree_data1/a/foo.cpp b/tests/manual/qt4projectmanager/projecttree_data1/a/foo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree_data1/a/foo.h b/tests/manual/qt4projectmanager/projecttree_data1/a/foo.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree_data1/b/foo.cpp b/tests/manual/qt4projectmanager/projecttree_data1/b/foo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree_data1/b/foo.h b/tests/manual/qt4projectmanager/projecttree_data1/b/foo.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree_data2/a/bar.cpp b/tests/manual/qt4projectmanager/projecttree_data2/a/bar.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree_data2/a/bar.h b/tests/manual/qt4projectmanager/projecttree_data2/a/bar.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree_data2/a/sub/bar2.cpp b/tests/manual/qt4projectmanager/projecttree_data2/a/sub/bar2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree_data2/a/sub/bar2.h b/tests/manual/qt4projectmanager/projecttree_data2/a/sub/bar2.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree_data3/path/bar.cpp b/tests/manual/qt4projectmanager/projecttree_data3/path/bar.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/qt4projectmanager/projecttree_data3/path/bar.h b/tests/manual/qt4projectmanager/projecttree_data3/path/bar.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/manual/trk/adapter.cpp b/tests/manual/trk/adapter.cpp index 18d06b5bd131b3d1e7e47805605b1a4d7952d22a..dda0bb43fd66a5bd061128425b4cd86b0852c197 100644 --- a/tests/manual/trk/adapter.cpp +++ b/tests/manual/trk/adapter.cpp @@ -28,6 +28,7 @@ **************************************************************************/ #include "trkutils.h" +#include "trkdevice.h" #include <QtCore/QPointer> #include <QtCore/QCoreApplication> @@ -40,50 +41,6 @@ #include <QtNetwork/QLocalServer> #include <QtNetwork/QLocalSocket> -#ifdef Q_OS_WIN -#include <windows.h> - -// Non-blocking replacement for win-api ReadFile function -BOOL WINAPI TryReadFile(HANDLE hFile, - LPVOID lpBuffer, - DWORD nNumberOfBytesToRead, - LPDWORD lpNumberOfBytesRead, - LPOVERLAPPED lpOverlapped) -{ - COMSTAT comStat; - if(!ClearCommError(hFile, NULL, &comStat)){ - qDebug() << "ClearCommError() failed"; - return FALSE; - } - if (!comStat.cbInQue) - return FALSE; - return ReadFile(hFile, - lpBuffer, - qMin(comStat.cbInQue, nNumberOfBytesToRead), - lpNumberOfBytesRead, - lpOverlapped); -} - -// Format windows error from GetLastError() value. -QString winErrorMessage(unsigned long error) -{ - QString rc = QString::fromLatin1("#%1: ").arg(error); - ushort *lpMsgBuf; - - const int len = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL); - if (len) { - rc = QString::fromUtf16(lpMsgBuf, len); - LocalFree(lpMsgBuf); - } else { - rc += QString::fromLatin1("<unknown error>"); - } - return rc; -} - -#endif - #ifdef Q_OS_UNIX #include <signal.h> @@ -97,9 +54,6 @@ void signalHandler(int) using namespace trk; -enum { TRK_SYNC = 0x7f }; - - enum { KnownRegisters = RegisterPSGdb + 1}; static const char *registerNames[KnownRegisters] = @@ -137,13 +91,13 @@ struct AdapterOptions { QString trkServer; }; -#define CB(s) &Adapter::s - class Adapter : public QObject { Q_OBJECT public: + typedef TrkFunctor1<const TrkResult &> Callback; + Adapter(); ~Adapter(); void setGdbServerName(const QString &name); @@ -154,38 +108,22 @@ public: void setUseSocket(bool s) { m_useSocket = s; } bool startServer(); +private slots: + void handleResult(const trk::TrkResult &data); + private: // // TRK // - Q_SLOT void readFromTrk(); - - typedef void (Adapter::*TrkCallBack)(const TrkResult &); - - struct TrkMessage - { - TrkMessage() : code(0), token(0), callBack(0), invokeOnFailure(0) {} - byte code; - byte token; - QByteArray data; - QVariant cookie; - TrkCallBack callBack; - bool invokeOnFailure; - }; bool openTrkPort(const QString &port, QString *errorMessage); // or server name for local server void sendTrkMessage(byte code, - TrkCallBack calBack = 0, + Callback callBack = Callback(), const QByteArray &data = QByteArray(), const QVariant &cookie = QVariant(), bool invokeOnFailure = false); - // adds message to 'send' queue - void queueTrkMessage(const TrkMessage &msg); - void tryTrkWrite(); - void tryTrkRead(); - // actually writes a message to the device - void trkWrite(const TrkMessage &msg); - // convienience messages + + // convenience messages void sendTrkInitialPing(); void sendTrkContinue(); void waitForTrkFinished(); @@ -194,10 +132,7 @@ private: // kill process and breakpoints void cleanUp(); - void timerEvent(QTimerEvent *ev); - byte nextTrkWriteToken(); - - void handleCpuType(const TrkResult &result); + void handleCpuType(const TrkResult &result); void handleCreateProcess(const TrkResult &result); void handleClearBreakpoint(const TrkResult &result); void handleSignalContinue(const TrkResult &result); @@ -216,25 +151,17 @@ private: void reportToGdb(const TrkResult &result); void clearTrkBreakpoint(const Breakpoint &bp); - void handleResult(const TrkResult &data); void readMemory(uint addr, uint len); void startInferiorIfNeeded(); void interruptInferior(); -#ifdef Q_OS_WIN - HANDLE m_winComDevice; -#endif - QLocalSocket *m_socketDevice; + QSharedPointer<TrkWriteQueueDevice> m_trkDevice; + QSharedPointer<QIODevice> m_socket; + QSharedPointer<TrkWriteQueueIODevice> m_socketDevice; QString m_trkServerName; QByteArray m_trkReadBuffer; - unsigned char m_trkWriteToken; - QQueue<TrkMessage> m_trkWriteQueue; - QHash<byte, TrkMessage> m_writtenTrkMessages; - QByteArray m_trkReadQueue; - bool m_trkWriteBusy; - QList<Breakpoint> m_breakpoints; // @@ -268,12 +195,6 @@ private: }; Adapter::Adapter() : -#ifdef Q_OS_WIN - m_winComDevice(NULL), -#endif - m_socketDevice(0), - m_trkWriteToken(0), - m_trkWriteBusy(false), m_gdbConnection(0), m_gdbServerPort(0), m_gdbAckMode(true), @@ -283,20 +204,14 @@ Adapter::Adapter() : m_serialFrame(true), m_startInferiorTriggered(false) { - startTimer(100); } Adapter::~Adapter() { // Trk -#ifdef Q_OS_WIN - if (m_winComDevice) - CloseHandle(m_winComDevice); -#endif - if (m_socketDevice) { - m_socketDevice->abort(); - delete m_socketDevice; - } + if (!m_socket.isNull()) + if (QLocalSocket *sock = qobject_cast<QLocalSocket *>(m_socket.data())) + sock->abort(); // Gdb m_gdbServer.close(); @@ -325,9 +240,9 @@ bool Adapter::startServer() sendTrkInitialPing(); sendTrkMessage(0x01); // Connect - sendTrkMessage(0x05, CB(handleSupportMask)); - sendTrkMessage(0x06, CB(handleCpuType)); - sendTrkMessage(0x04, CB(handleTrkVersions)); // Versions + sendTrkMessage(0x05, Callback(this, &Adapter::handleSupportMask)); + sendTrkMessage(0x06, Callback(this, &Adapter::handleCpuType)); + sendTrkMessage(0x04, Callback(this, &Adapter::handleTrkVersions)); // Versions //sendTrkMessage(0x09); // Unrecognized command //sendTrkMessage(0x4a, 0, // "10 " + formatString("C:\\data\\usingdlls.sisx")); // Open File @@ -491,7 +406,7 @@ void Adapter::sendGdbMessage(const QByteArray &msg, const QByteArray &logNote) void Adapter::sendGdbMessageAfterSync(const QByteArray &msg, const QByteArray &logNote) { QByteArray ba = msg + char(1) + logNote; - sendTrkMessage(TRK_SYNC, CB(reportToGdb), "", ba); // Answer gdb + sendTrkMessage(TRK_WRITE_QUEUE_NOOP_CODE, Callback(this, &Adapter::reportToGdb), "", ba); // Answer gdb } void Adapter::reportToGdb(const TrkResult &result) @@ -545,7 +460,7 @@ void Adapter::handleGdbResponse(const QByteArray &response) appendInt(&ba, 0); // end address appendInt(&ba, m_session.pid); appendInt(&ba, m_session.tid); - sendTrkMessage(0x18, 0, ba); + sendTrkMessage(0x18, Callback(), ba); // FIXME: should be triggered by real stop //sendGdbMessageAfterSync("S11", "target stopped"); } @@ -561,7 +476,7 @@ void Adapter::handleGdbResponse(const QByteArray &response) QByteArray ba; appendInt(&ba, m_session.pid); appendInt(&ba, m_session.tid); - sendTrkMessage(0x18, CB(handleSignalContinue), ba, signalNumber); // Continue + sendTrkMessage(0x18, Callback(this, &Adapter::handleSignalContinue), ba, signalNumber); // Continue } else if (response.startsWith("D")) { @@ -582,7 +497,7 @@ void Adapter::handleGdbResponse(const QByteArray &response) appendShort(&ba, RegisterCount - 1); // last register appendInt(&ba, m_session.pid); appendInt(&ba, m_session.tid); - sendTrkMessage(0x12, CB(handleAndReportReadRegisters), ba, QVariant(), true); + sendTrkMessage(0x12, Callback(this, &Adapter::handleAndReportReadRegisters), ba, QVariant(), true); } else if (response.startsWith("Hc")) { @@ -615,7 +530,7 @@ void Adapter::handleGdbResponse(const QByteArray &response) QByteArray ba; appendByte(&ba, 0); // Sub-command: Delete Process appendInt(&ba, m_session.pid); - sendTrkMessage(0x41, 0, ba, "Delete process"); // Delete Item + sendTrkMessage(0x41, Callback(), ba, "Delete process"); // Delete Item sendGdbMessageAfterSync("", "process killed"); } @@ -793,7 +708,7 @@ void Adapter::handleGdbResponse(const QByteArray &response) appendInt(&ba, m_snapshot.registers[RegisterPC] + 4); // end address appendInt(&ba, m_session.pid); appendInt(&ba, m_session.tid); - sendTrkMessage(0x19, 0, ba, "Step range"); + sendTrkMessage(0x19, Callback(), ba, "Step range"); // FIXME: should be triggered by "real" stop" //sendGdbMessageAfterSync("S05", "target halted"); } @@ -815,7 +730,7 @@ void Adapter::handleGdbResponse(const QByteArray &response) QByteArray ba; appendByte(&ba, 0); // Sub-command: Delete Process appendInt(&ba, m_session.pid); - sendTrkMessage(0x41, 0, ba, "Delete process"); // Delete Item + sendTrkMessage(0x41, Callback(), ba, "Delete process"); // Delete Item sendGdbMessageAfterSync("", "process killed"); } @@ -850,7 +765,7 @@ void Adapter::handleGdbResponse(const QByteArray &response) appendInt(&ba, m_session.pid); appendInt(&ba, 0xFFFFFFFF); - sendTrkMessage(0x1B, CB(handleAndReportSetBreakpoint), ba); + sendTrkMessage(0x1B, Callback(this, &Adapter::handleAndReportSetBreakpoint), ba); //m_session.toekn //---TRK------------------------------------------------------ @@ -864,95 +779,47 @@ void Adapter::handleGdbResponse(const QByteArray &response) } } -void Adapter::readFromTrk() -{ - //QByteArray ba = m_gdbConnection->readAll(); - //logMessage("Read from gdb: " + ba); -} - bool Adapter::openTrkPort(const QString &port, QString *errorMessage) { if (m_useSocket) { - m_socketDevice = new QLocalSocket(this); - m_socketDevice->connectToServer(port); - const bool rc = m_socketDevice->waitForConnected(); - if (!rc) - *errorMessage = "Unable to connect to TRK server " + m_trkServerName + ' ' + m_socketDevice->errorString(); - return rc; - } -#ifdef Q_OS_WIN - m_winComDevice = CreateFile(port.toStdWString().c_str(), - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - - if (INVALID_HANDLE_VALUE == m_winComDevice){ - *errorMessage = "Could not open device " + port + ' ' + winErrorMessage(GetLastError()); - return false; - } - return true; -#else - logMessage("Not implemented", true); - return false; -#endif - -#if 0 - m_socketDevice = new Win_QextSerialPort(port); - m_socketDevice->setBaudRate(BAUD115200); - m_socketDevice->setDataBits(DATA_8); - m_socketDevice->setParity(PAR_NONE); - //m_socketDevice->setStopBits(STO); - m_socketDevice->setFlowControl(FLOW_OFF); - m_socketDevice->setTimeout(0, 500); - - if (!m_socketDevice->open(QIODevice::ReadWrite)) { - QByteArray ba = m_socketDevice->errorString().toLatin1(); - logMessage("Could not open device " << ba); - return false; + QLocalSocket *socket = new QLocalSocket; + socket->connectToServer(port); + if (!socket->waitForConnected()) { + *errorMessage = "Unable to connect to TRK server " + m_trkServerName + ' ' + m_socket->errorString(); + delete socket; + return false; + } + m_socket = QSharedPointer<QIODevice>(socket); + m_socketDevice = QSharedPointer<TrkWriteQueueIODevice>(new TrkWriteQueueIODevice(m_socket)); + connect(m_socketDevice.data(), SIGNAL(messageReceived(trk::TrkResult)), this, SLOT(handleResult(trk::TrkResult))); + if (m_verbose > 1) + m_socketDevice->setVerbose(true); + m_socketDevice->setSerialFrame(m_serialFrame); + return true; } - return true -#endif + m_trkDevice = QSharedPointer<TrkWriteQueueDevice>(new TrkWriteQueueDevice); + connect(m_trkDevice.data(), SIGNAL(messageReceived(trk::TrkResult)), this, SLOT(handleResult(trk::TrkResult))); + if (m_verbose > 1) + m_trkDevice->setVerbose(true); + m_trkDevice->setSerialFrame(m_serialFrame); + return m_trkDevice->open(port, errorMessage); } -void Adapter::timerEvent(QTimerEvent *) -{ - //qDebug("."); - tryTrkWrite(); - tryTrkRead(); -} - -byte Adapter::nextTrkWriteToken() -{ - ++m_trkWriteToken; - if (m_trkWriteToken == 0) - ++m_trkWriteToken; - return m_trkWriteToken; -} - -void Adapter::sendTrkMessage(byte code, TrkCallBack callBack, +void Adapter::sendTrkMessage(byte code, Callback callBack, const QByteArray &data, const QVariant &cookie, bool invokeOnFailure) { - TrkMessage msg; - msg.code = code; - // Tokens must be strictly sequential - if (msg.code != TRK_SYNC) - msg.token = nextTrkWriteToken(); - msg.callBack = callBack; - msg.data = data; - msg.cookie = cookie; - msg.invokeOnFailure = invokeOnFailure; - queueTrkMessage(msg); + if (m_useSocket) + m_socketDevice->sendTrkMessage(code, callBack, data, cookie, invokeOnFailure); + else + m_trkDevice->sendTrkMessage(code, callBack, data, cookie, invokeOnFailure); } void Adapter::sendTrkInitialPing() { - TrkMessage msg; - msg.code = 0x00; // Ping - msg.token = 0; // reset sequence count - queueTrkMessage(msg); + if (m_useSocket) + m_socketDevice->sendTrkInitialPing(); + else + m_trkDevice->sendTrkInitialPing(); } void Adapter::sendTrkContinue() @@ -960,165 +827,39 @@ void Adapter::sendTrkContinue() QByteArray ba; appendInt(&ba, m_session.pid); appendInt(&ba, m_session.tid); - sendTrkMessage(0x18, 0, ba, "CONTINUE"); + sendTrkMessage(0x18, Callback(), ba, "CONTINUE"); } void Adapter::waitForTrkFinished() { - TrkMessage msg; // initiate one last roundtrip to ensure all is flushed - msg.code = 0x00; // Ping - msg.token = nextTrkWriteToken(); - msg.callBack = CB(handleWaitForFinished); - queueTrkMessage(msg); + sendTrkMessage(0x0, Callback(this, &Adapter::handleWaitForFinished)); } void Adapter::sendTrkAck(byte token) { logMessage(QString("SENDING ACKNOWLEDGEMENT FOR TOKEN %1").arg(int(token))); - TrkMessage msg; - msg.code = 0x80; - msg.token = token; - msg.data.append('\0'); - // The acknowledgement must not be queued! - //queueMessage(msg); - trkWrite(msg); - // 01 90 00 07 7e 80 01 00 7d 5e 7e + if (m_useSocket) + m_socketDevice->sendTrkAck(token); + else + m_trkDevice->sendTrkAck(token); } -void Adapter::queueTrkMessage(const TrkMessage &msg) -{ - m_trkWriteQueue.append(msg); -} - -void Adapter::tryTrkWrite() -{ - if (m_trkWriteBusy) - return; - if (m_trkWriteQueue.isEmpty()) - return; - - TrkMessage msg = m_trkWriteQueue.dequeue(); - if (msg.code == TRK_SYNC) { - logMessage(QString::fromLatin1("TRK SYNC [token=%1]").arg(msg.token)); - TrkResult result; - result.code = msg.code; - result.token = msg.token; - result.data = msg.data; - result.cookie = msg.cookie; - TrkCallBack cb = msg.callBack; - if (cb) - (this->*cb)(result); - } else { - trkWrite(msg); - } -} - -void Adapter::trkWrite(const TrkMessage &msg) -{ - QByteArray ba = frameMessage(msg.code, msg.token, msg.data, m_serialFrame); - - m_writtenTrkMessages.insert(msg.token, msg); - m_trkWriteBusy = true; - - if (m_verbose > 1) { - const QString logMsg = QString::fromLatin1("WRITE: 0x%1 [token=%2]: %3").arg(msg.code, 0, 16).arg(msg.token).arg(stringFromArray(ba)); - logMessage(logMsg); - } - - if (m_useSocket) { - if (!m_socketDevice->write(ba)) - logMessage("WRITE ERROR: " + m_socketDevice->errorString()); - m_socketDevice->flush(); - } else { -#ifdef Q_OS_WIN - DWORD charsWritten; - if (!WriteFile(m_winComDevice, ba.data(), ba.size(), &charsWritten, NULL)) - logMessage("WRITE ERROR: " + winErrorMessage(GetLastError())); - FlushFileBuffers(m_winComDevice); -#endif - } -} - -void Adapter::tryTrkRead() +void Adapter::handleResult(const TrkResult &result) { - //logMessage("TRY READ: " << m_socketDevice->bytesAvailable() - // << stringFromArray(m_trkReadQueue); - if (m_useSocket) { - if (m_socketDevice->bytesAvailable() == 0 && m_trkReadQueue.isEmpty()) - return; - - QByteArray res = m_socketDevice->readAll(); - m_trkReadQueue.append(res); - } else { -#ifdef Q_OS_WIN - const DWORD BUFFERSIZE = 1024; - char buffer[BUFFERSIZE]; - DWORD charsRead; - DWORD totalCharsRead = 0; - while (TryReadFile(m_winComDevice, buffer, BUFFERSIZE, &charsRead, NULL)) { - m_trkReadQueue.append(buffer, charsRead); - totalCharsRead += charsRead; - if (isValidTrkResult(m_trkReadQueue, m_serialFrame)) - break; - } - if (!totalCharsRead) - return; -#endif // USE_NATIVE - } - - if (m_trkReadQueue.size() < 9) { - logMessage("ERROR READBUFFER INVALID (1): " - + stringFromArray(m_trkReadQueue)); - m_trkReadQueue.clear(); + if (result.isDebugOutput) { + logMessage(QLatin1String("APPLICATION OUTPUT: ") + QString::fromAscii(result.data)); return; } - - TrkResult r; - while (extractResult(&m_trkReadQueue, m_serialFrame, &r)) - handleResult(r); - - m_trkWriteBusy = false; -} - - -void Adapter::handleResult(const TrkResult &result) -{ QByteArray prefix = "READ BUF: "; QByteArray str = result.toString().toUtf8(); switch (result.code) { - case 0x80: { // ACK - //logMessage(prefix + "ACK: " + str); - if (const int ec = result.errorCode()) - logMessage(QString::fromLatin1("READ BUF ACK/ERR %1 for token=%2").arg(ec).arg(result.token), true); - if (!m_writtenTrkMessages.contains(result.token)) { - logMessage("NO ENTRY FOUND!"); - } - TrkMessage msg = m_writtenTrkMessages.take(result.token); - TrkResult result1 = result; - result1.cookie = msg.cookie; - TrkCallBack cb = msg.callBack; - if (cb) { - (this->*cb)(result1); - } else { - QString msg = result.cookie.toString(); - if (!msg.isEmpty()) - logMessage("HANDLE: " + msg + stringFromArray(result.data)); - } + case 0x80: // ACK break; - } case 0xff: { // NAK. This mostly means transmission error, not command failed. - const TrkMessage writtenMsg = m_writtenTrkMessages.take(result.token); QString logMsg; - QTextStream(&logMsg) << prefix << "NAK: for 0x" << QString::number(int(writtenMsg.code), 16) - << " token=" << result.token << " ERROR: " << errorMessage(result.data.at(0)) << ' ' << str; + QTextStream(&logMsg) << prefix << "NAK: for token=" << result.token << " ERROR: " << errorMessage(result.data.at(0)) << ' ' << str; logMessage(logMsg, true); - // Invoke failure if desired - if (writtenMsg.callBack && writtenMsg.invokeOnFailure) { - TrkResult result1 = result; - result1.cookie = writtenMsg.cookie; - (this->*writtenMsg.callBack)(result1); - } break; } case 0x90: { // Notified Stopped @@ -1258,10 +999,10 @@ void Adapter::handleCreateProcess(const TrkResult &result) // Command: 0x42 Read Info // [42 0C 00 06 00 00 00 00 00 14 50 6F 6C 79 6D 6F // 72 70 68 69 63 44 4C 4C 32 2E 64 6C 6C 00] - sendTrkMessage(0x42, CB(handleReadInfo), + sendTrkMessage(0x42, Callback(this, &Adapter::handleReadInfo), "00 06 00 00 00 00 00 14 50 6F 6C 79 6D 6F " "72 70 68 69 63 44 4C 4C 32 2E 64 6C 6C 00"); - //sendTrkMessage(0x42, CB(handleReadInfo), + //sendTrkMessage(0x42, Callback(this, &Adapter::handleReadInfo), // "00 01 00 00 00 00"); //---TRK------------------------------------------------------ // Command: 0x80 Acknowledge @@ -1273,7 +1014,7 @@ void Adapter::handleCreateProcess(const TrkResult &result) // Command: 0x42 Read Info // [42 0D 00 06 00 00 00 00 00 14 50 6F 6C 79 6D 6F // 72 70 68 69 63 44 4C 4C 31 2E 64 6C 6C 00] - sendTrkMessage(0x42, CB(handleReadInfo), + sendTrkMessage(0x42, Callback(this, &Adapter::handleReadInfo), "00 06 00 00 00 00 00 14 50 6F 6C 79 6D 6F " "72 70 68 69 63 44 4C 4C 31 2E 64 6C 6C 00"); //---TRK------------------------------------------------------ @@ -1282,7 +1023,7 @@ void Adapter::handleCreateProcess(const TrkResult &result) // [80 0D 20] #endif - //sendTrkMessage(0x18, CB(handleStop), + //sendTrkMessage(0x18, Callback(this, &Adapter::handleStop), // "01 " + formatInt(m_session.pid) + formatInt(m_session.tid)); //---IDE------------------------------------------------------ @@ -1293,8 +1034,8 @@ void Adapter::handleCreateProcess(const TrkResult &result) QByteArray ba; appendInt(&ba, m_session.pid); appendInt(&ba, m_session.tid); - sendTrkMessage(0x18, CB(handleContinue), ba); - //sendTrkMessage(0x18, CB(handleContinue), + sendTrkMessage(0x18, Callback(this, &Adapter::handleContinue), ba); + //sendTrkMessage(0x18, Callback(this, &Adapter::handleContinue), // formatInt(m_session.pid) + "ff ff ff ff"); //---TRK------------------------------------------------------ // Command: 0x80 Acknowledge @@ -1423,7 +1164,7 @@ void Adapter::clearTrkBreakpoint(const Breakpoint &bp) appendByte(&ba, 0x00); appendShort(&ba, bp.number); appendInt(&ba, m_session.codeseg + bp.offset); - sendTrkMessage(0x1C, CB(handleClearBreakpoint), ba); + sendTrkMessage(0x1C, Callback(this, &Adapter::handleClearBreakpoint), ba); } void Adapter::handleClearBreakpoint(const TrkResult &result) @@ -1497,7 +1238,7 @@ void Adapter::cleanUp() appendByte(&ba, 0x00); appendByte(&ba, 0x00); appendInt(&ba, m_session.pid); - sendTrkMessage(0x41, 0, ba, "Delete process"); + sendTrkMessage(0x41, Callback(), ba, "Delete process"); //---TRK------------------------------------------------------ // Command: 0x80 Acknowledge @@ -1507,7 +1248,7 @@ void Adapter::cleanUp() foreach (const Breakpoint &bp, m_breakpoints) clearTrkBreakpoint(bp); - sendTrkMessage(0x02, CB(handleDisconnect)); + sendTrkMessage(0x02, Callback(this, &Adapter::handleDisconnect)); m_startInferiorTriggered = false; //---IDE------------------------------------------------------ // Command: 0x1C Clear Break @@ -1538,7 +1279,7 @@ void Adapter::cleanUp() //---IDE------------------------------------------------------ // Command: 0x02 Disconnect // [02 27] -// sendTrkMessage(0x02, CB(handleDisconnect)); +// sendTrkMessage(0x02, Callback(this, &Adapter::handleDisconnect)); //---TRK------------------------------------------------------ // Command: 0x80 Acknowledge // Error: 0x00 @@ -1565,11 +1306,11 @@ void Adapter::readMemory(uint addr, uint len) appendInt(&ba, m_session.pid); appendInt(&ba, m_session.tid); // Read Memory - sendTrkMessage(0x10, CB(handleReadMemory), ba, QVariant(blockaddr), true); + sendTrkMessage(0x10, Callback(this, &Adapter::handleReadMemory), ba, QVariant(blockaddr), true); } } qulonglong cookie = (qulonglong(addr) << 32) + len; - sendTrkMessage(TRK_SYNC, CB(reportReadMemory), QByteArray(), cookie); + sendTrkMessage(TRK_WRITE_QUEUE_NOOP_CODE, Callback(this, &Adapter::reportReadMemory), QByteArray(), cookie); } void Adapter::startInferiorIfNeeded() @@ -1589,7 +1330,7 @@ void Adapter::startInferiorIfNeeded() QByteArray file("C:\\sys\\bin\\filebrowseapp.exe"); appendString(&ba, file, TargetByteOrder); - sendTrkMessage(0x40, CB(handleCreateProcess), ba); // Create Item + sendTrkMessage(0x40, Callback(this, &Adapter::handleCreateProcess), ba); // Create Item } void Adapter::interruptInferior() @@ -1599,7 +1340,7 @@ void Adapter::interruptInferior() appendByte(&ba, 1); appendInt(&ba, m_session.pid); appendInt(&ba, m_session.tid); // threadID: 4 bytes Variable number of bytes. - sendTrkMessage(0x1A, 0, ba, "Interrupting..."); + sendTrkMessage(0x1A, Callback(), ba, "Interrupting..."); } static bool readAdapterArgs(const QStringList &args, AdapterOptions *o) diff --git a/tests/manual/trk/adapter.pro b/tests/manual/trk/adapter.pro index 48ebd9bec67aefddfd82f670d3769b3b59733b00..22f4aa3cddb1785127a452b4ee5ee116deebe501 100644 --- a/tests/manual/trk/adapter.pro +++ b/tests/manual/trk/adapter.pro @@ -4,8 +4,11 @@ TEMPLATE = app QT = core network win32:CONFIG+=console -HEADERS += trkutils.h +HEADERS += trkutils.h \ +trkfunctor.h \ +trkdevice.h \ SOURCES += \ adapter.cpp \ - trkutils.cpp + trkutils.cpp \ + trkdevice.cpp diff --git a/tests/manual/trk/launcher.cpp b/tests/manual/trk/launcher.cpp index a3d3d06e0359427d3e888c2034b5fa25fcf559b3..474b965a236f478526896e759a6e2e64e6570b8d 100644 --- a/tests/manual/trk/launcher.cpp +++ b/tests/manual/trk/launcher.cpp @@ -67,7 +67,7 @@ LauncherPrivate::LauncherPrivate() : Launcher::Launcher() : d(new LauncherPrivate) { - connect(&d->m_device, SIGNAL(messageReceived(TrkResult)), this, SLOT(handleResult(TrkResult))); + connect(&d->m_device, SIGNAL(messageReceived(trk::TrkResult)), this, SLOT(handleResult(trk::TrkResult))); } Launcher::~Launcher() @@ -147,7 +147,7 @@ void Launcher::installAndRun() void Launcher::logMessage(const QString &msg) { if (d->m_verbose) - qDebug() << "ADAPTER: " << qPrintable(msg); + qDebug() << "LAUNCHER: " << qPrintable(msg); } void Launcher::waitForTrkFinished(const TrkResult &result) @@ -239,7 +239,7 @@ void Launcher::handleResult(const TrkResult &result) case TrkNotifyDeleted: { // NotifyDeleted const ushort itemType = (unsigned char)result.data.at(1); const ushort len = result.data.size() > 12 ? extractShort(result.data.data() + 10) : ushort(0); - const QString name = len ? QString::fromAscii(result.data.mid(13, len)) : QString(); + const QString name = len ? QString::fromAscii(result.data.mid(12, len)) : QString(); logMessage(QString::fromLatin1("%1 %2 UNLOAD: %3"). arg(QString::fromAscii(prefix)).arg(itemType ? QLatin1String("LIB") : QLatin1String("PROCESS")). arg(name)); diff --git a/tests/manual/trk/launcher.h b/tests/manual/trk/launcher.h index c2527d57d5cee2f531be99aaece1d82c4db832ca..cb9881b5511b58d37683de9e8fac58cbf1bdecdd 100644 --- a/tests/manual/trk/launcher.h +++ b/tests/manual/trk/launcher.h @@ -67,7 +67,7 @@ public slots: void terminate(); private slots: - void handleResult(const TrkResult &data); + void handleResult(const trk::TrkResult &data); private: void tryTrkRead(); diff --git a/tests/manual/trk/run.pl b/tests/manual/trk/run.pl index 2644c30497059986c7fe767f1020f712a7f71575..9f4e31097c6d55aa38a884f23fb31e4a8511f069 100755 --- a/tests/manual/trk/run.pl +++ b/tests/manual/trk/run.pl @@ -21,10 +21,15 @@ Usage: run.pl -av -aq -tv -tq -l [COM] Options: -av Adapter verbose -aq Adapter quiet + -af Adapter turn off serial frame -tv TrkServer verbose -tq TrkServer quiet trkserver simulator will be run unless COM is specified + +Bluetooth: + rfcomm listen /dev/rfcomm0 1 \$PWD/run.pl -av -af {} + EOF # ------- Parse arguments @@ -36,11 +41,16 @@ for (my $i = 0; $i < $argCount; $i++) { push(@ADAPTER_OPTIONS, '-v'); } elsif ($a eq '-aq') { push(@ADAPTER_OPTIONS, '-q'); + } elsif ($a eq '-af') { + push(@ADAPTER_OPTIONS, '-f'); } elsif ($a eq '-tv') { push(@TRKSERVEROPTIONS, '-v'); } elsif ($a eq '-tq') { push(@TRKSERVEROPTIONS, '-q'); } elsif ($a eq '-h') { + print $usage; + exit(0); + } else { print $usage; exit(1); } diff --git a/tests/manual/trk/trkdevice.cpp b/tests/manual/trk/trkdevice.cpp index 13608cb2da8eb8312c5e0992b88139247d8e9280..6746e5b1f64e5d286af6a14a284381aa15c02890 100644 --- a/tests/manual/trk/trkdevice.cpp +++ b/tests/manual/trk/trkdevice.cpp @@ -35,6 +35,7 @@ #include <QtCore/QQueue> #include <QtCore/QHash> #include <QtCore/QMap> +#include <QtCore/QSharedPointer> #ifdef Q_OS_WIN # include <windows.h> @@ -312,10 +313,13 @@ void TrkDevice::tryTrkRead() } #endif // Q_OS_WIN TrkResult r; - while (extractResult(&d->trkReadBuffer, d->serialFrame, &r)) { + QByteArray rawData; + while (extractResult(&d->trkReadBuffer, d->serialFrame, &r, &rawData)) { if (d->verbose) qDebug() << "Read TrkResult " << r.data.toHex(); emit messageReceived(r); + if (!rawData.isEmpty()) + emit rawDataReceived(rawData); } } @@ -359,28 +363,149 @@ TrkMessage::TrkMessage(unsigned char c, } // ------- TrkWriteQueueDevice +typedef QSharedPointer<TrkMessage> SharedPointerTrkMessage; -struct TrkWriteQueueDevicePrivate { - typedef QMap<unsigned char, TrkMessage> TokenMessageMap; - TrkWriteQueueDevicePrivate(); +/* Mixin class that manages a write queue of Trk messages. */ + +class TrkWriteQueue { +public: + typedef TrkWriteQueueDevice::Callback Callback; + + TrkWriteQueue(); + + // Enqueue messages. + void queueTrkMessage(unsigned char code, Callback callback, + const QByteArray &data, const QVariant &cookie, + bool invokeOnNAK); + void queueTrkInitialPing(); + + // Call this from the device read notification with the results. + void slotHandleResult(const TrkResult &result); + + // This can be called periodically in a timer to retrieve + // the pending messages to be sent. + bool pendingMessage(SharedPointerTrkMessage *message = 0); + // Notify the queue about the success of the write operation + // after taking the pendingMessage off. + void notifyWriteResult(bool ok); + + // Factory function for ack message + static SharedPointerTrkMessage trkAck(unsigned char token); + +private: + typedef QMap<unsigned char, SharedPointerTrkMessage> TokenMessageMap; + + unsigned char nextTrkWriteToken(); unsigned char trkWriteToken; - QQueue<TrkMessage> trkWriteQueue; + QQueue<SharedPointerTrkMessage> trkWriteQueue; TokenMessageMap writtenTrkMessages; bool trkWriteBusy; }; -TrkWriteQueueDevicePrivate::TrkWriteQueueDevicePrivate() : +TrkWriteQueue::TrkWriteQueue() : trkWriteToken(0), trkWriteBusy(false) { } +unsigned char TrkWriteQueue::nextTrkWriteToken() +{ + ++trkWriteToken; + if (trkWriteToken == 0) + ++trkWriteToken; + return trkWriteToken; +} + +void TrkWriteQueue::queueTrkMessage(unsigned char code, Callback callback, + const QByteArray &data, const QVariant &cookie, + bool invokeOnNAK) +{ + const unsigned char token = code == TRK_WRITE_QUEUE_NOOP_CODE ? + (unsigned char)(0) : nextTrkWriteToken(); + SharedPointerTrkMessage msg(new TrkMessage(code, token, callback)); + msg->data = data; + msg->cookie = cookie; + msg->invokeOnNAK = invokeOnNAK; + trkWriteQueue.append(msg); +} + +bool TrkWriteQueue::pendingMessage(SharedPointerTrkMessage *message) +{ + // Invoked from timer, try to flush out message queue + if (trkWriteBusy || trkWriteQueue.isEmpty()) + return false; + // Handle the noop message, just invoke CB + if (trkWriteQueue.front()->code == TRK_WRITE_QUEUE_NOOP_CODE) { + const SharedPointerTrkMessage noopMessage = trkWriteQueue.dequeue(); + if (noopMessage->callback) { + TrkResult result; + result.code = noopMessage->code; + result.token = noopMessage->token; + result.data = noopMessage->data; + result.cookie = noopMessage->cookie; + noopMessage->callback(result); + } + } + // Check again for real messages + if (trkWriteQueue.isEmpty()) + return false; + if (message) + *message = trkWriteQueue.front(); + return true; +} + +void TrkWriteQueue::notifyWriteResult(bool ok) +{ + // On success, dequeue message and await result + if (ok) { + const SharedPointerTrkMessage firstMsg = trkWriteQueue.dequeue(); + writtenTrkMessages.insert(firstMsg->token, firstMsg); + trkWriteBusy = true; + } +} + +void TrkWriteQueue::slotHandleResult(const TrkResult &result) +{ + trkWriteBusy = false; + if (result.code != TrkNotifyAck && result.code != TrkNotifyNak) + return; + // Find which request the message belongs to and invoke callback + // if ACK or on NAK if desired. + const TokenMessageMap::iterator it = writtenTrkMessages.find(result.token); + if (it == writtenTrkMessages.end()) + return; + const bool invokeCB = it.value()->callback + && (result.code == TrkNotifyAck || it.value()->invokeOnNAK); + + if (invokeCB) { + TrkResult result1 = result; + result1.cookie = it.value()->cookie; + it.value()->callback(result1); + } + writtenTrkMessages.erase(it); +} + +SharedPointerTrkMessage TrkWriteQueue::trkAck(unsigned char token) +{ + SharedPointerTrkMessage msg(new TrkMessage(0x80, token)); + msg->token = token; + msg->data.append('\0'); + return msg; +} + +void TrkWriteQueue::queueTrkInitialPing() +{ + const SharedPointerTrkMessage msg(new TrkMessage(0, 0)); // Ping, reset sequence count + trkWriteQueue.append(msg); +} + +// ----------------------- TrkWriteQueueDevice::TrkWriteQueueDevice(QObject *parent) : TrkDevice(parent), - qd(new TrkWriteQueueDevicePrivate) + qd(new TrkWriteQueue) { - connect(this, SIGNAL(messageReceived(TrkResult)), this, SLOT(slotHandleResult(TrkResult))); + connect(this, SIGNAL(messageReceived(trk::TrkResult)), this, SLOT(slotHandleResult(trk::TrkResult))); } TrkWriteQueueDevice::~TrkWriteQueueDevice() @@ -388,56 +513,34 @@ TrkWriteQueueDevice::~TrkWriteQueueDevice() delete qd; } -unsigned char TrkWriteQueueDevice::nextTrkWriteToken() -{ - ++qd->trkWriteToken; - if (qd->trkWriteToken == 0) - ++qd->trkWriteToken; - return qd->trkWriteToken; -} - void TrkWriteQueueDevice::sendTrkMessage(unsigned char code, Callback callback, const QByteArray &data, const QVariant &cookie, bool invokeOnNAK) { - TrkMessage msg(code, nextTrkWriteToken(), callback); - msg.data = data; - msg.cookie = cookie; - msg.invokeOnNAK = invokeOnNAK; - queueTrkMessage(msg); + qd->queueTrkMessage(code, callback, data, cookie, invokeOnNAK); } void TrkWriteQueueDevice::sendTrkInitialPing() { - const TrkMessage msg(0, 0); // Ping, reset sequence count - queueTrkMessage(msg); + qd->queueTrkInitialPing(); } bool TrkWriteQueueDevice::sendTrkAck(unsigned char token) { - TrkMessage msg(0x80, token); - msg.token = token; - msg.data.append('\0'); // The acknowledgement must not be queued! - return trkWriteRawMessage(msg); + const SharedPointerTrkMessage ack = TrkWriteQueue::trkAck(token); + return trkWriteRawMessage(*ack); // 01 90 00 07 7e 80 01 00 7d 5e 7e } -void TrkWriteQueueDevice::queueTrkMessage(const TrkMessage &msg) -{ - qd->trkWriteQueue.append(msg); -} - void TrkWriteQueueDevice::tryTrkWrite() { - // Invoked from timer, try to flush out message queue - if (qd->trkWriteBusy) + if (!qd->pendingMessage()) return; - if (qd->trkWriteQueue.isEmpty()) - return; - - const TrkMessage msg = qd->trkWriteQueue.dequeue(); - trkWrite(msg); + SharedPointerTrkMessage message; + qd->pendingMessage(&message); + const bool success = trkWriteRawMessage(*message); + qd->notifyWriteResult(success); } bool TrkWriteQueueDevice::trkWriteRawMessage(const TrkMessage &msg) @@ -452,13 +555,6 @@ bool TrkWriteQueueDevice::trkWriteRawMessage(const TrkMessage &msg) return rc; } -bool TrkWriteQueueDevice::trkWrite(const TrkMessage &msg) -{ - qd->writtenTrkMessages.insert(msg.token, msg); - qd->trkWriteBusy = true; - return trkWriteRawMessage(msg); -} - void TrkWriteQueueDevice::timerEvent(QTimerEvent *ev) { tryTrkWrite(); @@ -467,23 +563,128 @@ void TrkWriteQueueDevice::timerEvent(QTimerEvent *ev) void TrkWriteQueueDevice::slotHandleResult(const TrkResult &result) { - qd->trkWriteBusy = false; - if (result.code != TrkNotifyAck && result.code != TrkNotifyNak) - return; - // Find which request the message belongs to and invoke callback - // if ACK or on NAK if desired. - const TrkWriteQueueDevicePrivate::TokenMessageMap::iterator it = qd->writtenTrkMessages.find(result.token); - if (it == qd->writtenTrkMessages.end()) + qd->slotHandleResult(result); +} + +// ----------- TrkWriteQueueDevice + +struct TrkWriteQueueIODevicePrivate { + TrkWriteQueueIODevicePrivate(const QSharedPointer<QIODevice> &d); + + const QSharedPointer<QIODevice> device; + TrkWriteQueue queue; + QByteArray readBuffer; + bool serialFrame; + bool verbose; +}; + +TrkWriteQueueIODevicePrivate::TrkWriteQueueIODevicePrivate(const QSharedPointer<QIODevice> &d) : + device(d), + serialFrame(true), + verbose(false) +{ +} + +TrkWriteQueueIODevice::TrkWriteQueueIODevice(const QSharedPointer<QIODevice> &device, + QObject *parent) : + QObject(parent), + d(new TrkWriteQueueIODevicePrivate(device)) +{ + startTimer(TimerInterval); +} + +TrkWriteQueueIODevice::~TrkWriteQueueIODevice() +{ + delete d; +} + +bool TrkWriteQueueIODevice::serialFrame() const +{ + return d->serialFrame; +} + +void TrkWriteQueueIODevice::setSerialFrame(bool f) +{ + d->serialFrame = f; +} + +bool TrkWriteQueueIODevice::verbose() const +{ + return d->verbose; +} + +void TrkWriteQueueIODevice::setVerbose(bool b) +{ + d->verbose = b; +} + +void TrkWriteQueueIODevice::sendTrkMessage(unsigned char code, Callback callback, + const QByteArray &data, const QVariant &cookie, + bool invokeOnNAK) +{ + d->queue.queueTrkMessage(code, callback, data, cookie, invokeOnNAK); +} + +void TrkWriteQueueIODevice::sendTrkInitialPing() +{ + d->queue.queueTrkInitialPing(); +} + +bool TrkWriteQueueIODevice::sendTrkAck(unsigned char token) +{ + // The acknowledgement must not be queued! + const SharedPointerTrkMessage ack = TrkWriteQueue::trkAck(token); + return trkWriteRawMessage(*ack); + // 01 90 00 07 7e 80 01 00 7d 5e 7e +} + + +void TrkWriteQueueIODevice::timerEvent(QTimerEvent *) +{ + tryTrkWrite(); + tryTrkRead(); +} + +void TrkWriteQueueIODevice::tryTrkWrite() +{ + if (!d->queue.pendingMessage()) return; - const bool invokeCB = it.value().callback - && (result.code == TrkNotifyAck || it.value().invokeOnNAK); + SharedPointerTrkMessage message; + d->queue.pendingMessage(&message); + const bool success = trkWriteRawMessage(*message); + d->queue.notifyWriteResult(success); +} - if (invokeCB) { - TrkResult result1 = result; - result1.cookie = it.value().cookie; - it.value().callback(result1); +bool TrkWriteQueueIODevice::trkWriteRawMessage(const TrkMessage &msg) +{ + const QByteArray ba = frameMessage(msg.code, msg.token, msg.data, serialFrame()); + if (verbose()) + qDebug() << ("WRITE: " + stringFromArray(ba)); + const bool ok = d->device->write(ba) != -1; + if (!ok) { + const QString msg = QString::fromLatin1("Unable to write %1 bytes: %2:").arg(ba.size()).arg(d->device->errorString()); + qWarning("%s\n", qPrintable(msg)); + } + return ok; +} + +void TrkWriteQueueIODevice::tryTrkRead() +{ + const quint64 bytesAvailable = d->device->bytesAvailable(); + if (!bytesAvailable) + return; + const QByteArray newData = d->device->read(bytesAvailable); + if (d->verbose) + qDebug() << "READ " << newData.toHex(); + d->readBuffer.append(newData); + TrkResult r; + QByteArray rawData; + while (extractResult(&(d->readBuffer), d->serialFrame, &r, &rawData)) { + d->queue.slotHandleResult(r); + emit messageReceived(r); + if (!rawData.isEmpty()) + emit rawDataReceived(rawData); } - qd->writtenTrkMessages.erase(it); } } // namespace tr diff --git a/tests/manual/trk/trkdevice.h b/tests/manual/trk/trkdevice.h index 53047bfb8995f59359b5848d68c7fc4bda8781f6..61b382ad5766bdc0a89d2dbfe2fc7a8160169f5d 100644 --- a/tests/manual/trk/trkdevice.h +++ b/tests/manual/trk/trkdevice.h @@ -35,13 +35,19 @@ #include <QtCore/QObject> #include <QtCore/QVariant> #include <QtCore/QByteArray> +#include <QtCore/QSharedPointer> + +QT_BEGIN_NAMESPACE +class QIODevice; +QT_END_NAMESPACE namespace trk { struct TrkResult; struct TrkMessage; struct TrkDevicePrivate; -struct TrkWriteQueueDevicePrivate; +class TrkWriteQueue; +struct TrkWriteQueueIODevicePrivate; /* TrkDevice: Implements a Windows COM or Linux device for * Trk communications. Provides synchronous write and asynchronous @@ -74,7 +80,9 @@ public: bool write(const QByteArray &data, QString *errorMessage); signals: - void messageReceived(const TrkResult&); + void messageReceived(const trk::TrkResult&); + // Emitted with the contents of messages enclosed in 07e, not for log output + void rawDataReceived(const QByteArray &data); void error(const QString &s); protected: @@ -89,7 +97,12 @@ private: /* TrkWriteQueueDevice: Extends TrkDevice by write message queue allowing * for queueing messages with a notification callback. If the message receives - * an ACK, the callback is invoked. */ + * an ACK, the callback is invoked. + * The special message TRK_WRITE_QUEUE_NOOP_CODE code can be used for synchronisation. + * The respective message will not be sent, the callback is just invoked. */ + +enum { TRK_WRITE_QUEUE_NOOP_CODE = 0x7f }; + class TrkWriteQueueDevice : public TrkDevice { Q_OBJECT @@ -115,21 +128,64 @@ public: bool sendTrkAck(unsigned char token); private slots: - void slotHandleResult(const TrkResult &); + void slotHandleResult(const trk::TrkResult &); protected: virtual void timerEvent(QTimerEvent *ev); private: - unsigned char nextTrkWriteToken(); - void queueTrkMessage(const TrkMessage &msg); void tryTrkWrite(); bool trkWriteRawMessage(const TrkMessage &msg); - bool trkWrite(const TrkMessage &msg); - TrkWriteQueueDevicePrivate *qd; + TrkWriteQueue *qd; }; +/* A Trk queueing device wrapping around a QIODevice (otherwise + * mimicking TrkWriteQueueDevice). + * Can be used to forward Trk over a network or to simulate things. */ + +class TrkWriteQueueIODevice : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(TrkWriteQueueIODevice) + Q_PROPERTY(bool serialFrame READ serialFrame WRITE setSerialFrame) + Q_PROPERTY(bool verbose READ verbose WRITE setVerbose) +public: + typedef TrkFunctor1<const TrkResult &> Callback; + + explicit TrkWriteQueueIODevice(const QSharedPointer<QIODevice> &device, + QObject *parent = 0); + virtual ~TrkWriteQueueIODevice(); + + bool serialFrame() const; + void setSerialFrame(bool f); + + bool verbose() const; + void setVerbose(bool b); + + void sendTrkMessage(unsigned char code, + Callback callback = Callback(), + const QByteArray &data = QByteArray(), + const QVariant &cookie = QVariant(), + bool invokeOnNAK = false); + void sendTrkInitialPing(); + bool sendTrkAck(unsigned char token); + +signals: + void messageReceived(const trk::TrkResult&); + // Emitted with the contents of messages enclosed in 07e, not for log output + void rawDataReceived(const QByteArray &data); + +protected: + virtual void timerEvent(QTimerEvent *ev); + +private: + void tryTrkRead(); + void tryTrkWrite(); + bool trkWriteRawMessage(const TrkMessage &msg); + + TrkWriteQueueIODevicePrivate *d; +}; } // namespace trk diff --git a/tests/manual/trk/trkutils.cpp b/tests/manual/trk/trkutils.cpp index 8b5622ed9faa77d2cc1ec7d34ea6b3b0989de4f2..81925c07d674e0efb19afb836f658c193eaf47a3 100644 --- a/tests/manual/trk/trkutils.cpp +++ b/tests/manual/trk/trkutils.cpp @@ -122,9 +122,11 @@ ushort isValidTrkResult(const QByteArray &buffer, bool serialFrame) return firstDelimiterPos != -1 ? firstDelimiterPos : buffer.size(); } -bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result) +bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result, QByteArray *rawData) { result->clear(); + if(rawData) + rawData->clear(); const ushort len = isValidTrkResult(*buffer, serialFrame); if (!len) return false; @@ -140,6 +142,8 @@ bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result) // FIXME: what happens if the length contains 0xfe? // Assume for now that it passes unencoded! const QByteArray data = decode7d(buffer->mid(delimiterPos + 1, len - 2)); + if(rawData) + *rawData = data; *buffer->remove(0, delimiterPos + len); byte sum = 0; diff --git a/tests/manual/trk/trkutils.h b/tests/manual/trk/trkutils.h index 77e469b8068e6f2bd9cf207fe84396e2157d6b56..e54efc6f6e915c214f1b7ae2f4e575da754b6869 100644 --- a/tests/manual/trk/trkutils.h +++ b/tests/manual/trk/trkutils.h @@ -188,7 +188,7 @@ struct TrkResult // the serial frame [0x01 0x90 <len>] and 0x7e encoded7d(ba) 0x7e QByteArray frameMessage(byte command, byte token, const QByteArray &data, bool serialFrame); ushort isValidTrkResult(const QByteArray &buffer, bool serialFrame); -bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *r); +bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *r, QByteArray *rawData = 0); QByteArray errorMessage(byte code); QByteArray hexNumber(uint n, int digits = 0); uint swapEndian(uint in);