diff options
Diffstat (limited to 'src/plugins/snippets/snippetswindow.cpp')
-rw-r--r-- | src/plugins/snippets/snippetswindow.cpp | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/src/plugins/snippets/snippetswindow.cpp b/src/plugins/snippets/snippetswindow.cpp new file mode 100644 index 0000000000..e44f21182d --- /dev/null +++ b/src/plugins/snippets/snippetswindow.cpp @@ -0,0 +1,434 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "snippetswindow.h" +#include "snippetspec.h" +#include "inputwidget.h" +#include "snippetsplugin.h" + +#include <coreplugin/icore.h> +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/itexteditable.h> +#include <texteditor/itexteditor.h> + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtGui/QDragEnterEvent> +#include <QtGui/QApplication> +#include <QtGui/QLabel> +#include <QtCore/QMimeData> +#include <QtGui/QHeaderView> + +using namespace Snippets::Internal; + +const QIcon SnippetsWindow::m_fileIcon = QIcon(":/snippets/images/file.png"); +const QIcon SnippetsWindow::m_dirIcon = QIcon(":/snippets/images/dir.png"); +const QIcon SnippetsWindow::m_dirOpenIcon = QIcon(":/snippets/images/diropen.png"); + +Q_DECLARE_METATYPE(Snippets::Internal::SnippetSpec *) + +SnippetsWindow::SnippetsWindow() +{ + m_core = SnippetsPlugin::core(); + + setWindowTitle(tr("Snippets")); + setWindowIcon(QIcon(":/snippets/images/snippets.png")); + setOrientation(Qt::Vertical); + + m_snippetsTree = new SnippetsTree(this); + addWidget(m_snippetsTree); + + m_descLabel = new QLabel(this); + m_descLabel->setAlignment(Qt::AlignTop|Qt::AlignLeft); + m_descLabel->setFrameShape(QFrame::Panel); + m_descLabel->setFrameShadow(QFrame::Raised); + m_descLabel->setWordWrap(true); + addWidget(m_descLabel); + + m_snippetsDir = QDir::home(); + if (!initSnippetsDir()) + setDisabled(true); + else { + QDir defaultDir(m_core->resourcePath() + QLatin1String("/snippets")); + if (defaultDir.exists()) + initSnippets(defaultDir); + initSnippets(m_snippetsDir); + } + + connect(m_snippetsTree, SIGNAL(itemCollapsed(QTreeWidgetItem *)), + this, SLOT(setClosedIcon(QTreeWidgetItem *))); + + connect(m_snippetsTree, SIGNAL(itemExpanded(QTreeWidgetItem *)), + this, SLOT(setOpenIcon(QTreeWidgetItem *))); + + connect(m_snippetsTree, SIGNAL(itemActivated(QTreeWidgetItem *, int)), + this, SLOT(activateSnippet(QTreeWidgetItem *, int))); + + connect(m_snippetsTree, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + this, SLOT(updateDescription(QTreeWidgetItem *))); +} + +SnippetsWindow::~SnippetsWindow() +{ + qDeleteAll(m_snippets); +} + + +void SnippetsWindow::activateSnippet(QTreeWidgetItem *item, int column) +{ + if (!item->parent()) + return; + + TextEditor::ITextEditable *editor = 0; + if (m_core->editorManager()->currentEditor()) + editor = qobject_cast<TextEditor::ITextEditable *>( + m_core->editorManager()->currentEditor()); + if (editor) { + SnippetSpec* spec = qVariantValue<SnippetSpec*>(item->data(0, Qt::UserRole)); + insertSnippet(editor, spec); + } + + Q_UNUSED(column); +} + +const QList<SnippetSpec *> &SnippetsWindow::snippets() const +{ + return m_snippets; +} + +void SnippetsWindow::initSnippets(const QDir &dir) +{ + QString name; + QString category; + + QMap<QString, QTreeWidgetItem *> categories; + for (int i = 0; i < m_snippetsTree->topLevelItemCount(); ++i) { + categories.insert(m_snippetsTree->topLevelItem(i)->text(0), + m_snippetsTree->topLevelItem(i)); + } + + foreach (const QString &snippet, dir.entryList(QStringList("*.snp"))) { + SnippetSpec *spec = new SnippetSpec(); + if (spec->load(dir.filePath(snippet))) { + if (!categories.contains(spec->category())) { + QTreeWidgetItem *citem = new QTreeWidgetItem(m_snippetsTree); + citem->setText(0, spec->category()); + citem->setIcon(0, m_dirIcon); + categories.insert(spec->category(), citem); + } + + QTreeWidgetItem *item = new QTreeWidgetItem( + categories.value(spec->category())); + item->setText(0, spec->name()); + item->setIcon(0, m_fileIcon); + QVariant v; + qVariantSetValue<SnippetSpec *>(v, spec); + item->setData(0, Qt::UserRole, v); + + m_snippets.append(spec); + } + } +} + +QString SnippetsWindow::createUniqueFileName() +{ + int fileNumber = 0; + QString baseName = "snippet"; + while (m_snippetsDir.exists(baseName + QString::number(fileNumber) + ".snp")) { + ++fileNumber; + } + return baseName + QString::number(fileNumber) + ".snp"; +} + +void SnippetsWindow::writeSnippet(const QMimeData *) +{ +} + +bool SnippetsWindow::initSnippetsDir() +{ + if (!m_snippetsDir.exists(".qworkbench")) + m_snippetsDir.mkdir(".qworkbench"); + if (!m_snippetsDir.cd(".qworkbench")) + return false; + + if (!m_snippetsDir.exists("snippets")) + m_snippetsDir.mkdir("snippets"); + return m_snippetsDir.cd("snippets"); +} + +void SnippetsWindow::getArguments() +{ + QString contents = m_currentSnippet->contents(); + int index = 0; + bool pc = false; + QString nrstr; + + QSet<int> requiredArgs; + m_requiredArgs.clear(); + m_args.clear(); + + while (index < contents.length()) { + QChar c = contents.at(index); + if (c == QLatin1Char('%')) { + pc = !pc; + } else if (pc) { + if (c.isNumber()) { + nrstr += c; + } else { + pc = false; + } + } + + if (!pc && !nrstr.isEmpty()) { + requiredArgs << nrstr.toInt(); + nrstr.clear(); + } + + ++index; + } + + m_requiredArgs = requiredArgs.toList(); + m_requiredArgs.prepend(-1); + + showInputWidget(false, QString()); +} + +void SnippetsWindow::showInputWidget(bool canceled, const QString &value) +{ + if (canceled) + return; + + TextEditor::ITextEditor *te = 0; + if (m_core->editorManager()->currentEditor()) + te = qobject_cast<TextEditor::ITextEditor*>( + m_core->editorManager()->currentEditor()); + + int arg = m_requiredArgs.takeFirst(); + if (arg != -1) + m_args << value; + + if (!te || m_requiredArgs.isEmpty()) { + qDebug("replaceAndInsert"); + replaceAndInsert(); + } else { + QString desc = m_currentSnippet->argumentDescription(m_requiredArgs.first()); + QString def = m_currentSnippet->argumentDefault(m_requiredArgs.first()); + foreach(QString arg, m_args) { + desc = desc.arg(arg); + def = def.arg(arg); + } + + InputWidget *iw = new InputWidget(desc, def); + connect(iw, SIGNAL(finished(bool, const QString &)), + this, SLOT(showInputWidget(bool, const QString &))); + iw->showInputWidget(te->cursorRect().bottomRight()); + } +} + +void SnippetsWindow::replaceAndInsert() +{ + QString result; + QString keyWord; + int setAnchor = -1; + int setCursor = -1; + int selLength = 0; + + //clean up selection + int startPos = m_currentEditor->position(TextEditor::ITextEditable::Anchor); + int endPos = m_currentEditor->position(); + + if (startPos < 0) { + startPos = endPos; + } else { + if (startPos > endPos) { + int tmp = startPos; + startPos = endPos; + endPos = tmp; + } + selLength = endPos - startPos; + } + + //parse the contents + m_currentEditor->setCurPos(startPos); + QString editorIndent = getCurrentIndent(m_currentEditor); + QString content = m_currentSnippet->contents(); + foreach (const QString &arg, m_args) { + content = content.arg(arg); + } + + int startOfKey = -1; + for (int i = 0; i<content.length(); ++i) { + //handle windows,mac and linux new lines... + if (content.at(i) == QLatin1Char('\n')) { + if ((i <= 0) || content.at(i-1) != QLatin1Char('\r')) + result += QLatin1Char('\n') + editorIndent; + continue; + } else if (content.at(i) == QLatin1Char('\r')) { + result += QLatin1Char('\n') + editorIndent; + continue; + } + + if (content.at(i) == QChar('$')) { + if (startOfKey != -1) { + m_currentEditor->insert(result); + if (keyWord == QLatin1String("selection")) { + const QString &indent = indentOfString(content, i); + int selStartPos = m_currentEditor->position(); + m_currentEditor->setCurPos(selStartPos + selLength); + insertIdents(m_currentEditor, indent, selStartPos, m_currentEditor->position()); + } else if (keyWord == QLatin1String("anchor")) { + setAnchor = m_currentEditor->position(); + } else if (keyWord == QLatin1String("cursor")) { + setCursor = m_currentEditor->position(); + } + result.clear(); + keyWord.clear(); + startOfKey = -1; + } else { + startOfKey = i; + } + } else { + if (startOfKey != -1) + keyWord += content.at(i).toLower(); + else + result += content.at(i); + } + } + + m_currentEditor->insert(result); + + if (setAnchor != -1) { + m_currentEditor->setCurPos(setAnchor); + m_currentEditor->select(setCursor); + } else if (setCursor != -1) { + m_currentEditor->setCurPos(setCursor); + } +} + +void SnippetsWindow::insertSnippet(TextEditor::ITextEditable *editor, SnippetSpec *snippet) +{ + m_currentEditor = editor; + m_currentSnippet = snippet; + getArguments(); +} + +QString SnippetsWindow::getCurrentIndent(TextEditor::ITextEditor *editor) +{ + const int startPos = editor->position(TextEditor::ITextEditor::StartOfLine); + const int endPos = editor->position(TextEditor::ITextEditor::EndOfLine); + if (startPos < endPos) + return indentOfString(editor->textAt(startPos, endPos - startPos)); + return QString(); +} + +void SnippetsWindow::insertIdents(TextEditor::ITextEditable *editor, + const QString &indent, int fromPos, int toPos) +{ + int offset = 0; + const int startPos = editor->position(); + editor->setCurPos(toPos); + int currentLinePos = editor->position(TextEditor::ITextEditor::StartOfLine); + while (currentLinePos > fromPos) { + editor->setCurPos(currentLinePos); + editor->insert(indent); + offset += indent.length(); + editor->setCurPos(currentLinePos-1); + currentLinePos = editor->position(TextEditor::ITextEditor::StartOfLine); + } + editor->setCurPos(startPos + offset); +} + +QString SnippetsWindow::indentOfString(const QString &str, int at) +{ + QString result; + int startAt = at; + if (startAt < 0) + startAt = str.length() - 1; + + // find start position + while (startAt >= 0 && str.at(startAt) != QChar('\n') + && str.at(startAt) != QChar('\r')) --startAt; + + for (int i = (startAt + 1); i < str.length(); ++i) { + if (str.at(i) == QChar(' ') || str.at(i) == QChar('\t')) + result += str.at(i); + else + break; + } + + return result; +} + +void SnippetsWindow::setOpenIcon(QTreeWidgetItem *item) +{ + item->setIcon(0, m_dirOpenIcon); +} + +void SnippetsWindow::setClosedIcon(QTreeWidgetItem *item) +{ + item->setIcon(0, m_dirIcon); +} + +void SnippetsWindow::updateDescription(QTreeWidgetItem *item) +{ + const SnippetSpec* spec = qVariantValue<SnippetSpec*>(item->data(0, Qt::UserRole)); + if (spec) { + m_descLabel->setText(QLatin1String("<b>") + spec->name() + QLatin1String("</b><br>") + + spec->description()); + } else { + m_descLabel->setText(QLatin1String("<b>") + item->text(0) + QLatin1String("</b><br>")); + } +} + +SnippetsTree::SnippetsTree(QWidget *parent) + : QTreeWidget(parent) +{ + setColumnCount(1); + header()->setVisible(false); + setAlternatingRowColors(true); + setAcceptDrops(true); +} + +void SnippetsTree::dropEvent(QDropEvent *) +{ + //writeSnippet(event->mimeData()); +} + +void SnippetsTree::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasText()) + event->acceptProposedAction(); +} + +void SnippetsTree::dragMoveEvent(QDragMoveEvent *) +{ +} |