2008-12-02 12:01:29 +01:00
|
|
|
/***************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** 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)
|
|
|
|
|
**
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
|
|
|
|
** Non-Open Source Usage
|
|
|
|
|
**
|
2008-12-02 12:01:29 +01:00
|
|
|
** 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
|
2008-12-02 14:17:16 +01:00
|
|
|
** agreement between you and Nokia.
|
|
|
|
|
**
|
|
|
|
|
** GNU General Public License Usage
|
|
|
|
|
**
|
2008-12-02 12:01:29 +01:00
|
|
|
** 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
|
2008-12-02 14:17:16 +01:00
|
|
|
** rights. These rights are described in the Nokia Qt GPL Exception
|
|
|
|
|
** version 1.2, included in the file GPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
|
|
|
|
***************************************************************************/
|
2008-12-02 12:01:29 +01:00
|
|
|
***************************************************************************/
|
|
|
|
|
#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 *)
|
|
|
|
|
{
|
|
|
|
|
}
|