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(StandardAction id)
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)