5#include "screenshotdialog.h"
7#include "katedocument.h"
9#include "katelinelayout.h"
10#include "katerenderer.h"
13#include <QActionGroup>
14#include <QApplication>
18#include <QColorDialog>
22#include <QGraphicsDropShadowEffect>
23#include <QImageWriter>
30#include <QPainterPath>
38#include <KConfigGroup>
39#include <KLocalizedString>
40#include <KSyntaxHighlighting/Theme>
44static constexpr QPoint noDragStartCandidatePos = {-1, -1};
46class BaseWidget :
public QWidget
51 , m_screenshot(new QLabel(this))
55 auto layout =
new QHBoxLayout(
this);
59 layout->addWidget(m_screenshot);
62 m_renableEffects.setInterval(500);
63 m_renableEffects.setSingleShot(
true);
64 m_renableEffects.callOnTimeout(
this, &BaseWidget::enableDropShadow);
67 void setColor(QColor c)
75 void setPixmap(
const QPixmap &p)
77 temporarilyDisableDropShadow();
79 m_screenshot->setPixmap(p);
80 m_screenshotSize = p.
size();
85 const int h = m_screenshotSize.
height();
86 const int y = std::max(((
height() - h) / 2), 0);
87 const int x = m_screenshot->geometry().x();
88 QRect r(
x,
y, m_screenshotSize.width(), m_screenshotSize.height());
89 r.adjust(-6, -6, 6, 6);
93 void mousePressEvent(QMouseEvent *
event)
override
97 m_dragStartCandidatePos = noDragStartCandidatePos;
102 m_dragStartCandidatePos =
event->pos();
105 void mouseMoveEvent(QMouseEvent *
event)
override
107 if ((m_dragStartCandidatePos == noDragStartCandidatePos) || (
event->buttons() !=
Qt::LeftButton)
113 const QPixmap pixmap = grabPixmap();
115 auto *mimeData =
new QMimeData;
116 mimeData->setImageData(pixmap);
118 auto *drag =
new QDrag(
this);
119 drag->setMimeData(mimeData);
127 void temporarilyDisableDropShadow()
131 m_screenshot->setGraphicsEffect(
nullptr);
132 m_renableEffects.start();
136 void enableDropShadow()
138 QGraphicsDropShadowEffect *e =
new QGraphicsDropShadowEffect(m_screenshot);
142 m_screenshot->setGraphicsEffect(e);
145 QLabel *
const m_screenshot;
146 QSize m_screenshotSize;
147 QTimer m_renableEffects;
148 QPoint m_dragStartCandidatePos;
150 friend class ScrollArea;
156 explicit ScrollArea(BaseWidget *contents,
QWidget *
parent =
nullptr)
163 void scrollContentsBy(
int dx,
int dy)
override
165 m_base->temporarilyDisableDropShadow();
170 BaseWidget *
const m_base;
173ScreenshotDialog::ScreenshotDialog(
KTextEditor::Range selRange, KTextEditor::ViewPrivate *parent)
175 , m_base(new BaseWidget(this))
176 , m_selRange(selRange)
177 , m_scrollArea(new ScrollArea(m_base, this))
178 , m_saveButton(new
QPushButton(
QIcon::fromTheme(QStringLiteral(
"document-save")),
i18n(
"Save")))
180 , m_changeBGColor(new
QPushButton(
QIcon::fromTheme(QStringLiteral(
"color-fill")),
i18nc(
"@action:button",
"Background Color…")))
182 , m_extraDecorations(new
QCheckBox(
i18n(
"Show Extra Decorations"), this))
183 , m_windowDecorations(new
QCheckBox(
i18n(
"Show Window Decorations"), this))
184 , m_lineNumMenu(new
QMenu(this))
185 , m_resizeTimer(new
QTimer(this))
188 setWindowTitle(
i18nc(
"@title:window",
"Take Screenshot of Selection"));
190 m_scrollArea->setWidget(m_base);
191 m_scrollArea->setWidgetResizable(
true);
192 m_scrollArea->setAutoFillBackground(
true);
197 baseLayout->setContentsMargins(0, 0, 0, 4);
198 baseLayout->addWidget(m_scrollArea);
204 m_scrollArea->setPalette(m_base->palette());
207 baseLayout->addLayout(bottomBar);
208 bottomBar->setContentsMargins(0, 0, 4, 0);
209 bottomBar->addStretch();
210 bottomBar->addWidget(m_windowDecorations);
211 bottomBar->addWidget(m_extraDecorations);
212 bottomBar->addWidget(m_lineNumButton);
213 bottomBar->addWidget(m_changeBGColor);
214 bottomBar->addWidget(m_saveButton);
215 bottomBar->addWidget(m_copyButton);
222 QColor c = dlg.selectedColor();
224 m_scrollArea->setPalette(m_base->palette());
227 cg.writeEntry(
"BackgroundColor", c.
rgba());
232 renderScreenshot(
static_cast<KTextEditor::ViewPrivate *
>(parentWidget())->renderer());
234 cg.writeEntry<
bool>(
"ShowExtraDecorations", m_extraDecorations->isChecked());
236 m_extraDecorations->setChecked(cg.readEntry<
bool>(
"ShowExtraDecorations",
true));
239 renderScreenshot(
static_cast<KTextEditor::ViewPrivate *
>(parentWidget())->renderer());
241 cg.writeEntry<
bool>(
"ShowWindowDecorations", m_windowDecorations->isChecked());
243 m_windowDecorations->setChecked(cg.readEntry<
bool>(
"ShowWindowDecorations",
true));
247 int i = cg.readEntry(
"LineNumbers", (
int)ShowAbsoluteLineNums);
250 auto addMenuAction = [
this, gp](
const QString &text,
int data) {
251 auto a =
new QAction(text, m_lineNumMenu);
252 a->setCheckable(
true);
253 a->setActionGroup(gp);
254 m_lineNumMenu->addAction(a);
256 onLineNumChangedClicked(data);
260 addMenuAction(
i18n(
"Don't Show Line Numbers"), DontShowLineNums)->setChecked(i == DontShowLineNums);
261 addMenuAction(
i18n(
"Show Line Numbers From 1"), ShowAbsoluteLineNums)->setChecked(i == ShowAbsoluteLineNums);
262 addMenuAction(
i18n(
"Show Actual Line Numbers"), ShowActualLineNums)->setChecked(i == ShowActualLineNums);
264 m_showLineNumbers = i != DontShowLineNums;
265 m_absoluteLineNumbers = i == ShowAbsoluteLineNums;
268 m_lineNumButton->setText(
i18n(
"Line Numbers"));
270 m_lineNumButton->setMenu(m_lineNumMenu);
272 m_resizeTimer->setSingleShot(
true);
273 m_resizeTimer->setInterval(500);
274 m_resizeTimer->callOnTimeout(
this, [
this] {
275 renderScreenshot(
static_cast<KTextEditor::ViewPrivate *
>(parentWidget())->renderer());
277 cg.writeEntry(
"Geometry", saveGeometry());
282 restoreGeometry(geometry);
286ScreenshotDialog::~ScreenshotDialog()
288 m_resizeTimer->stop();
293 if (m_selRange.isEmpty()) {
297 constexpr int leftMargin = 16;
298 constexpr int rightMargin = 16;
299 constexpr int topMargin = 8;
300 constexpr int bottomMargin = 8;
301 constexpr int lnNoAreaSpacing = 8;
304 renderer.setPrinterFriendly(!m_extraDecorations->isChecked());
306 int startLine = m_selRange.start().line();
307 int endLine = m_selRange.end().line();
309 int width = std::min(1024, std::max(400, this->
width() - (m_scrollArea->horizontalScrollBar()->height())));
312 const bool fixedWidth = QFontInfo(renderer.currentFont()).fixedPitch();
314 int maxLineWidth = 0;
315 auto doc = renderer.view()->doc();
316 int w = renderer.currentFontMetrics().averageCharWidth();
317 for (
int line = startLine; line <= endLine; ++line) {
318 maxLineWidth = std::max(maxLineWidth, (doc->lineLength(line) * w));
321 const int windowWidth =
width;
322 if (maxLineWidth > windowWidth) {
323 maxLineWidth = windowWidth;
326 width = std::min(1024, maxLineWidth);
331 const int xEnd =
width;
333 std::vector<std::unique_ptr<KateLineLayout>> lineLayouts;
334 for (
int line = startLine; line <= endLine; ++line) {
335 auto lineLayout = std::make_unique<KateLineLayout>(renderer);
336 lineLayout->setLine(line, -1);
337 renderer.layoutLine(lineLayout.get(), xEnd,
false );
338 height += lineLayout->viewLineCount() * renderer.lineHeight();
339 lineLayouts.push_back(std::move(lineLayout));
342 if (m_windowDecorations->isChecked()) {
343 height += renderer.lineHeight() + topMargin + bottomMargin;
345 height += topMargin + bottomMargin;
348 int xStart = -leftMargin;
349 int lineNoAreaWidth = 0;
350 if (m_showLineNumbers) {
351 int lastLine = m_absoluteLineNumbers ? (endLine - startLine) + 1 : endLine;
352 const int lnNoWidth = renderer.currentFontMetrics().horizontalAdvance(
QString::number(lastLine));
353 lineNoAreaWidth = lnNoWidth + lnNoAreaSpacing;
354 width += lineNoAreaWidth;
355 xStart += -lineNoAreaWidth;
358 width += leftMargin + rightMargin;
360 pix.fill(renderer.view()->rendererConfig()->backgroundColor());
362 QPainter paint(&pix);
364 paint.translate(0, topMargin);
366 if (m_windowDecorations->isChecked()) {
367 int midY = (renderer.lineHeight() + 4) / 2;
373 QBrush b(QColor(0xff5f5a));
375 paint.drawEllipse(QPoint(
x, midY), 8, 8);
378 b = QColor(0xffbe2e);
380 paint.drawEllipse(QPoint(
x, midY), 8, 8);
383 b = QColor(0x2aca44);
385 paint.drawEllipse(QPoint(
x, midY), 8, 8);
390 paint.translate(0, renderer.lineHeight() + 4);
393 KateRenderer::PaintTextLineFlags flags;
396 int lineNo = m_absoluteLineNumbers ? 1 : startLine + 1;
397 paint.setFont(renderer.currentFont());
398 for (
auto &lineLayout : lineLayouts) {
399 renderer.paintTextLine(paint, lineLayout.get(), xStart, xEnd, QRectF{},
nullptr, flags);
401 if (lineNoAreaWidth != 0) {
402 paint.drawText(QRect(leftMargin - lnNoAreaSpacing, 0, lineNoAreaWidth, renderer.lineHeight()),
407 paint.translate(0, lineLayout->viewLineCount() * renderer.lineHeight());
410 m_base->setPixmap(pix);
413void ScreenshotDialog::onSaveClicked()
420 QImageWriter writer(name);
421 writer.write(m_base->grabPixmap().toImage());
422 if (!writer.errorString().isEmpty()) {
427void ScreenshotDialog::onCopyClicked()
429 if (
auto clip = qApp->clipboard()) {
438 m_resizeTimer->start();
443void ScreenshotDialog::onLineNumChangedClicked(
int i)
445 m_showLineNumbers = i != DontShowLineNums;
446 m_absoluteLineNumbers = i == ShowAbsoluteLineNums;
449 cg.writeEntry(
"LineNumbers", i);
451 renderScreenshot(
static_cast<KTextEditor::ViewPrivate *
>(
parentWidget())->renderer());
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
static KTextEditor::EditorPrivate * self()
Kate Part Internal stuff ;)
An object representing a section of text, from one Cursor to another.
Handles all of the work of rendering the text (used for the views and printing)
Kate::TextFolding & folding() const
Returns the folding info to which this renderer is bound.
KTextEditor::ViewPrivate * view() const
Returns the view to which this renderer is bound.
@ SkipDrawLineSelection
Skip drawing the line selection This is useful when we are drawing the draggable pixmap for drag even...
@ SkipDrawFirstInvisibleLineUnderlined
Skip drawing the dashed underline at the start of a folded block of text?
KTextEditor::DocumentPrivate * doc() const
Returns the document to which this renderer is bound.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QString name(const QVariant &location)
The KTextEditor namespace contains all the public API that is required to use the KTextEditor compone...
void triggered(bool checked)
bool isEmpty() const const
QColor fromRgba(QRgb rgba)
virtual void resizeEvent(QResizeEvent *) override
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QFlags< T > & setFlag(Enum flag, bool on)
void setBlurRadius(qreal blurRadius)
void setColor(const QColor &color)
void setOffset(const QPointF &ofs)
StandardButton warning(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons, StandardButton defaultButton)
virtual bool event(QEvent *e)
QObject * parent() const const
QPixmap scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
virtual void scrollContentsBy(int dx, int dy) override
bool isEmpty() const const
QString number(double n, char format, int precision)
void setContentsMargins(const QMargins &margins)