forked from qt-creator/qt-creator
make terminal emulator configurable centrally
this includes changing the runInTerminal.command command line. the terminal setting mock from the debugger plugin is gone again.
This commit is contained in:
@@ -1,11 +1,50 @@
|
|||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
osascript >/dev/null 2>&1 <<EOF
|
|
||||||
|
### FIXME:
|
||||||
|
# - currentTab and geometry stuff does not work with macX 10.4 (tiger)
|
||||||
|
# - -async is always in effect, i.e., synchronous execution is not implemented
|
||||||
|
|
||||||
|
geom=
|
||||||
|
async=
|
||||||
|
while test -n "$1"; do
|
||||||
|
case $1 in
|
||||||
|
-async)
|
||||||
|
async=1
|
||||||
|
shift;;
|
||||||
|
-geom)
|
||||||
|
shift
|
||||||
|
w=${1%%x*}
|
||||||
|
y=${1#*x}
|
||||||
|
h=${y%%+*}
|
||||||
|
y=${y#*+}
|
||||||
|
x=${y%%+*}
|
||||||
|
y=${y#*+}
|
||||||
|
geom="\
|
||||||
|
set number of columns of currentTab to $w
|
||||||
|
set number of rows of currentTab to $h
|
||||||
|
set position of windows whose tabs contains currentTab to {$x, $y}"
|
||||||
|
shift;;
|
||||||
|
-e)
|
||||||
|
shift
|
||||||
|
break;;
|
||||||
|
*)
|
||||||
|
echo "Invalid call" >&2
|
||||||
|
exit 1;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
args=
|
||||||
|
for i in "$@"; do
|
||||||
|
i=${i//\\/\\\\\\\\}
|
||||||
|
i=${i//\"/\\\\\\\"}
|
||||||
|
i=${i//\$/\\\\\\\$}
|
||||||
|
i=${i//\`/\\\\\\\`}
|
||||||
|
args="$args \\\"$i\\\""
|
||||||
|
done
|
||||||
|
osascript <<EOF
|
||||||
tell application "Terminal"
|
tell application "Terminal"
|
||||||
do script "$1 $2 +$3 +\"normal $4|\"; exit"
|
do script "$args; exit"
|
||||||
set currentTab to the result
|
set currentTab to the result
|
||||||
set number of columns of currentTab to $5
|
$geom
|
||||||
set number of rows of currentTab to $6
|
|
||||||
set position of windows whose tabs contains currentTab to {$7, $8}
|
|
||||||
activate
|
activate
|
||||||
end tell
|
end tell
|
||||||
EOF
|
EOF
|
||||||
|
@@ -47,6 +47,7 @@ QT_END_NAMESPACE
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QSettings;
|
||||||
class QTemporaryFile;
|
class QTemporaryFile;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
@@ -72,6 +73,13 @@ public:
|
|||||||
int exitCode() const { return m_appCode; } // This will be the signal number if exitStatus == CrashExit
|
int exitCode() const { return m_appCode; } // This will be the signal number if exitStatus == CrashExit
|
||||||
QProcess::ExitStatus exitStatus() const { return m_appStatus; }
|
QProcess::ExitStatus exitStatus() const { return m_appStatus; }
|
||||||
|
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
void setSettings(QSettings *settings) { m_settings = settings; }
|
||||||
|
static QString defaultTerminalEmulator();
|
||||||
|
static QString terminalEmulator(const QSettings *settings);
|
||||||
|
static void setTerminalEmulator(QSettings *settings, const QString &term);
|
||||||
|
#endif
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void processError(const QString &error);
|
void processError(const QString &error);
|
||||||
// These reflect the state of the actual client process
|
// These reflect the state of the actual client process
|
||||||
@@ -114,6 +122,7 @@ private:
|
|||||||
#else
|
#else
|
||||||
QProcess m_process;
|
QProcess m_process;
|
||||||
QByteArray m_stubServerDir;
|
QByteArray m_stubServerDir;
|
||||||
|
QSettings *m_settings;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@@ -30,6 +30,8 @@
|
|||||||
#include "consoleprocess.h"
|
#include "consoleprocess.h"
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
|
#include <QtCore/QDir>
|
||||||
|
#include <QtCore/QSettings>
|
||||||
#include <QtCore/QTemporaryFile>
|
#include <QtCore/QTemporaryFile>
|
||||||
|
|
||||||
#include <QtNetwork/QLocalSocket>
|
#include <QtNetwork/QLocalSocket>
|
||||||
@@ -48,6 +50,7 @@ ConsoleProcess::ConsoleProcess(QObject *parent)
|
|||||||
m_debug = false;
|
m_debug = false;
|
||||||
m_appPid = 0;
|
m_appPid = 0;
|
||||||
m_stubSocket = 0;
|
m_stubSocket = 0;
|
||||||
|
m_settings = 0;
|
||||||
|
|
||||||
connect(&m_stubServer, SIGNAL(newConnection()), SLOT(stubConnectionAvailable()));
|
connect(&m_stubServer, SIGNAL(newConnection()), SLOT(stubConnectionAvailable()));
|
||||||
|
|
||||||
@@ -88,8 +91,8 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args)
|
|||||||
m_tempFile->flush();
|
m_tempFile->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList xtermArgs;
|
QStringList xtermArgs = terminalEmulator(m_settings).split(QLatin1Char(' ')); // FIXME: quoting
|
||||||
xtermArgs << "-e"
|
xtermArgs
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
<< (QCoreApplication::applicationDirPath() + "/../Resources/qtcreator_process_stub")
|
<< (QCoreApplication::applicationDirPath() + "/../Resources/qtcreator_process_stub")
|
||||||
#else
|
#else
|
||||||
@@ -102,10 +105,11 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args)
|
|||||||
<< (m_tempFile ? m_tempFile->fileName() : 0)
|
<< (m_tempFile ? m_tempFile->fileName() : 0)
|
||||||
<< program << args;
|
<< program << args;
|
||||||
|
|
||||||
m_process.start(QLatin1String("xterm"), xtermArgs);
|
QString xterm = xtermArgs.takeFirst();
|
||||||
|
m_process.start(xterm, xtermArgs);
|
||||||
if (!m_process.waitForStarted()) {
|
if (!m_process.waitForStarted()) {
|
||||||
stubServerShutdown();
|
stubServerShutdown();
|
||||||
emit processError(tr("Cannot start console emulator xterm."));
|
emit processError(tr("Cannot start terminal emulator %1.").arg(xterm));
|
||||||
delete m_tempFile;
|
delete m_tempFile;
|
||||||
m_tempFile = 0;
|
m_tempFile = 0;
|
||||||
return false;
|
return false;
|
||||||
@@ -232,3 +236,27 @@ void ConsoleProcess::stubExited()
|
|||||||
}
|
}
|
||||||
emit wrapperStopped();
|
emit wrapperStopped();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ConsoleProcess::defaultTerminalEmulator()
|
||||||
|
{
|
||||||
|
// FIXME: enable this once runInTerminal works nicely
|
||||||
|
#if 0 //def Q_OS_MAC
|
||||||
|
return QDir::cleanPath(QCoreApplication::applicationDirPath()
|
||||||
|
+ QLatin1String("/../Resources/runInTerminal.command"));
|
||||||
|
#else
|
||||||
|
return QLatin1String("xterm");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ConsoleProcess::terminalEmulator(const QSettings *settings)
|
||||||
|
{
|
||||||
|
QString dflt = defaultTerminalEmulator() + QLatin1String(" -e");
|
||||||
|
if (!settings)
|
||||||
|
return dflt;
|
||||||
|
return settings->value(QLatin1String("General/TerminalEmulator"), dflt).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConsoleProcess::setTerminalEmulator(QSettings *settings, const QString &term)
|
||||||
|
{
|
||||||
|
return settings->setValue(QLatin1String("General/TerminalEmulator"), term);
|
||||||
|
}
|
||||||
|
@@ -49,6 +49,7 @@
|
|||||||
|
|
||||||
#include <extensionsystem/pluginmanager.h>
|
#include <extensionsystem/pluginmanager.h>
|
||||||
|
|
||||||
|
#include <utils/consoleprocess.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
@@ -73,6 +74,7 @@ Q_DECLARE_METATYPE(Core::IEditor*)
|
|||||||
|
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
using namespace Core::Internal;
|
using namespace Core::Internal;
|
||||||
|
using namespace Core::Utils;
|
||||||
|
|
||||||
enum { debugEditorManager=0 };
|
enum { debugEditorManager=0 };
|
||||||
|
|
||||||
@@ -429,15 +431,14 @@ void EditorManager::init()
|
|||||||
|
|
||||||
QString EditorManager::defaultExternalEditor() const
|
QString EditorManager::defaultExternalEditor() const
|
||||||
{
|
{
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
return ConsoleProcess::defaultTerminalEmulator() + QLatin1String(
|
||||||
# ifdef Q_OS_MAC
|
# ifdef Q_OS_MAC
|
||||||
return m_d->m_core->resourcePath()
|
" -async"
|
||||||
+QLatin1String("/runInTerminal.command vi %f %l %c %W %H %x %y");
|
# endif
|
||||||
#elif defined(Q_OS_UNIX)
|
" -geom %Wx%H+%x+%y -e vi %f +%l +\"normal %c|\"");
|
||||||
return QLatin1String("xterm -geom %Wx%H+%x+%y -e vi %f +%l +\"normal %c|\"");
|
|
||||||
#elif defined (Q_OS_WIN)
|
|
||||||
return QLatin1String("notepad %f");
|
|
||||||
#else
|
#else
|
||||||
return QString();
|
return QLatin1String("notepad %f");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,11 +31,14 @@
|
|||||||
|
|
||||||
#include "stylehelper.h"
|
#include "stylehelper.h"
|
||||||
#include "utils/qtcolorbutton.h"
|
#include "utils/qtcolorbutton.h"
|
||||||
|
#include <utils/consoleprocess.h>
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
#include <QtGui/QMessageBox>
|
#include <QtGui/QMessageBox>
|
||||||
|
|
||||||
#include "ui_generalsettings.h"
|
#include "ui_generalsettings.h"
|
||||||
|
|
||||||
|
using namespace Core::Utils;
|
||||||
using namespace Core::Internal;
|
using namespace Core::Internal;
|
||||||
|
|
||||||
GeneralSettings::GeneralSettings():
|
GeneralSettings::GeneralSettings():
|
||||||
@@ -71,6 +74,13 @@ QWidget *GeneralSettings::createPage(QWidget *parent)
|
|||||||
|
|
||||||
m_page->colorButton->setColor(StyleHelper::baseColor());
|
m_page->colorButton->setColor(StyleHelper::baseColor());
|
||||||
m_page->externalEditorEdit->setText(EditorManager::instance()->externalEditor());
|
m_page->externalEditorEdit->setText(EditorManager::instance()->externalEditor());
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
m_page->terminalEdit->setText(ConsoleProcess::terminalEmulator(Core::ICore::instance()->settings()));
|
||||||
|
#else
|
||||||
|
m_page->terminalLabel->hide();
|
||||||
|
m_page->terminalEdit->hide();
|
||||||
|
m_page->resetTerminalButton->hide();
|
||||||
|
#endif
|
||||||
|
|
||||||
connect(m_page->resetButton, SIGNAL(clicked()),
|
connect(m_page->resetButton, SIGNAL(clicked()),
|
||||||
this, SLOT(resetInterfaceColor()));
|
this, SLOT(resetInterfaceColor()));
|
||||||
@@ -78,6 +88,10 @@ QWidget *GeneralSettings::createPage(QWidget *parent)
|
|||||||
this, SLOT(resetExternalEditor()));
|
this, SLOT(resetExternalEditor()));
|
||||||
connect(m_page->helpExternalEditorButton, SIGNAL(clicked()),
|
connect(m_page->helpExternalEditorButton, SIGNAL(clicked()),
|
||||||
this, SLOT(showHelpForExternalEditor()));
|
this, SLOT(showHelpForExternalEditor()));
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
connect(m_page->resetTerminalButton, SIGNAL(clicked()),
|
||||||
|
this, SLOT(resetTerminal()));
|
||||||
|
#endif
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
@@ -87,6 +101,10 @@ void GeneralSettings::apply()
|
|||||||
// Apply the new base color if accepted
|
// Apply the new base color if accepted
|
||||||
StyleHelper::setBaseColor(m_page->colorButton->color());
|
StyleHelper::setBaseColor(m_page->colorButton->color());
|
||||||
EditorManager::instance()->setExternalEditor(m_page->externalEditorEdit->text());
|
EditorManager::instance()->setExternalEditor(m_page->externalEditorEdit->text());
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
ConsoleProcess::setTerminalEmulator(Core::ICore::instance()->settings(),
|
||||||
|
m_page->terminalEdit->text());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeneralSettings::finish()
|
void GeneralSettings::finish()
|
||||||
@@ -104,6 +122,13 @@ void GeneralSettings::resetExternalEditor()
|
|||||||
m_page->externalEditorEdit->setText(EditorManager::instance()->defaultExternalEditor());
|
m_page->externalEditorEdit->setText(EditorManager::instance()->defaultExternalEditor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
void GeneralSettings::resetTerminal()
|
||||||
|
{
|
||||||
|
m_page->terminalEdit->setText(ConsoleProcess::defaultTerminalEmulator() + QLatin1String(" -e"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void GeneralSettings::showHelpForExternalEditor()
|
void GeneralSettings::showHelpForExternalEditor()
|
||||||
{
|
{
|
||||||
if (m_dialog) {
|
if (m_dialog) {
|
||||||
|
@@ -59,6 +59,9 @@ private slots:
|
|||||||
void resetInterfaceColor();
|
void resetInterfaceColor();
|
||||||
void resetExternalEditor();
|
void resetExternalEditor();
|
||||||
void showHelpForExternalEditor();
|
void showHelpForExternalEditor();
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
void resetTerminal();
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui_GeneralSettings *m_page;
|
Ui_GeneralSettings *m_page;
|
||||||
|
@@ -96,6 +96,34 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="terminalLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Terminal:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="terminalEdit"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="resetTerminalButton">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Reset to default</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>R</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="core.qrc">
|
||||||
|
<normaloff>:/core/images/reset.png</normaloff>:/core/images/reset.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||||
<item>
|
<item>
|
||||||
|
@@ -267,11 +267,6 @@ DebuggerSettings *DebuggerSettings::instance()
|
|||||||
item->setCheckable(true);
|
item->setCheckable(true);
|
||||||
instance->insertItem(UseToolTips, item);
|
instance->insertItem(UseToolTips, item);
|
||||||
|
|
||||||
item = new SavedAction(instance);
|
|
||||||
item->setDefaultValue("xterm");
|
|
||||||
item->setSettingsKey("DebugMode", "Terminal");
|
|
||||||
instance->insertItem(TerminalApplication, item);
|
|
||||||
|
|
||||||
item = new SavedAction(instance);
|
item = new SavedAction(instance);
|
||||||
item->setSettingsKey("DebugMode", "ListSourceFiles");
|
item->setSettingsKey("DebugMode", "ListSourceFiles");
|
||||||
item->setText(tr("List source files"));
|
item->setText(tr("List source files"));
|
||||||
|
@@ -70,7 +70,6 @@ enum DebuggerActionCode
|
|||||||
AdjustColumnWidths,
|
AdjustColumnWidths,
|
||||||
AlwaysAdjustColumnWidths,
|
AlwaysAdjustColumnWidths,
|
||||||
AutoQuit,
|
AutoQuit,
|
||||||
TerminalApplication,
|
|
||||||
LockView,
|
LockView,
|
||||||
|
|
||||||
// Gdb
|
// Gdb
|
||||||
|
@@ -269,8 +269,6 @@ QWidget *GdbOptionPage::createPage(QWidget *parent)
|
|||||||
m_ui.gdbLocationChooser->setPromptDialogTitle(tr("Choose Gdb Location"));
|
m_ui.gdbLocationChooser->setPromptDialogTitle(tr("Choose Gdb Location"));
|
||||||
m_ui.scriptFileChooser->setExpectedKind(Core::Utils::PathChooser::File);
|
m_ui.scriptFileChooser->setExpectedKind(Core::Utils::PathChooser::File);
|
||||||
m_ui.scriptFileChooser->setPromptDialogTitle(tr("Choose Location of Startup Script File"));
|
m_ui.scriptFileChooser->setPromptDialogTitle(tr("Choose Location of Startup Script File"));
|
||||||
m_ui.terminalChooser->setExpectedKind(Core::Utils::PathChooser::Command);
|
|
||||||
m_ui.terminalChooser->setPromptDialogTitle(tr("Choose Location of Terminal Application"));
|
|
||||||
|
|
||||||
m_group.clear();
|
m_group.clear();
|
||||||
m_group.insert(theDebuggerAction(GdbLocation),
|
m_group.insert(theDebuggerAction(GdbLocation),
|
||||||
@@ -279,8 +277,6 @@ QWidget *GdbOptionPage::createPage(QWidget *parent)
|
|||||||
m_ui.scriptFileChooser);
|
m_ui.scriptFileChooser);
|
||||||
m_group.insert(theDebuggerAction(GdbEnvironment),
|
m_group.insert(theDebuggerAction(GdbEnvironment),
|
||||||
m_ui.environmentEdit);
|
m_ui.environmentEdit);
|
||||||
m_group.insert(theDebuggerAction(TerminalApplication),
|
|
||||||
m_ui.terminalChooser);
|
|
||||||
|
|
||||||
m_group.insert(theDebuggerAction(AllPluginBreakpoints),
|
m_group.insert(theDebuggerAction(AllPluginBreakpoints),
|
||||||
m_ui.radioButtonAllPluginBreakpoints);
|
m_ui.radioButtonAllPluginBreakpoints);
|
||||||
|
@@ -39,17 +39,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="terminalLocation">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>This is either a full abolute path leading to the terminal
|
|
||||||
you indent to use or the name of a terminal that will be searched in your PATH.</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Terminal:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="labelEnvironment">
|
<widget class="QLabel" name="labelEnvironment">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@@ -76,9 +65,6 @@ you indent to use or the name of a terminal that will be searched in your PATH.<
|
|||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="Core::Utils::PathChooser" name="gdbLocationChooser" native="true"/>
|
<widget class="Core::Utils::PathChooser" name="gdbLocationChooser" native="true"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="Core::Utils::PathChooser" name="terminalChooser" native="true"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@@ -30,6 +30,8 @@
|
|||||||
#include "applicationlauncher.h"
|
#include "applicationlauncher.h"
|
||||||
#include "consoleprocess.h"
|
#include "consoleprocess.h"
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
|
||||||
using namespace ProjectExplorer::Internal;
|
using namespace ProjectExplorer::Internal;
|
||||||
@@ -52,6 +54,7 @@ ApplicationLauncher::ApplicationLauncher(QObject *parent)
|
|||||||
this, SLOT(bringToForeground()));
|
this, SLOT(bringToForeground()));
|
||||||
|
|
||||||
m_consoleProcess = new ConsoleProcess(this);
|
m_consoleProcess = new ConsoleProcess(this);
|
||||||
|
m_consoleProcess->setSettings(Core::ICore::instance()->settings());
|
||||||
connect(m_consoleProcess, SIGNAL(processError(const QString&)),
|
connect(m_consoleProcess, SIGNAL(processError(const QString&)),
|
||||||
this, SIGNAL(applicationError(const QString&)));
|
this, SIGNAL(applicationError(const QString&)));
|
||||||
connect(m_consoleProcess, SIGNAL(processStopped()),
|
connect(m_consoleProcess, SIGNAL(processStopped()),
|
||||||
|
Reference in New Issue
Block a user