annotwindow.cpp 15.4 KB
Newer Older
1
2
/***************************************************************************
 *   Copyright (C) 2006 by Chu Xiaodong <xiaodongchu@gmail.com>            *
3
 *   Copyright (C) 2006 by Pino Toscano <pino@kde.org>                     *
4
5
6
 *   Copyright (C) 2017    Klarälvdalens Datakonsult AB, a KDAB Group      *
 *                         company, info@kdab.com. Work sponsored by the   *
 *                         LiMux project of the city of Munich             *
7
8
9
10
11
12
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/
13

Albert Astals Cid's avatar
Albert Astals Cid committed
14
15
#include "annotwindow.h"

16
// qt/kde includes
17
18
19
#include <qapplication.h>
#include <qevent.h>
#include <qfont.h>
20
21
#include <qfontinfo.h>
#include <qfontmetrics.h>
22
#include <qframe.h>
23
24
#include <qlabel.h>
#include <qlayout.h>
25
#include <qpushbutton.h>
26
#include <qsizegrip.h>
27
#include <qstyle.h>
28
#include <qtoolbutton.h>
Laurent Montel's avatar
Laurent Montel committed
29
#include <KLocalizedString>
Albert Astals Cid's avatar
Albert Astals Cid committed
30
#include <ktextedit.h>
Yuri Chornoivan's avatar
Yuri Chornoivan committed
31
#include <QDebug>
Laurent Montel's avatar
Laurent Montel committed
32
#include <qaction.h>
33
34
#include <kstandardaction.h>
#include <qmenu.h>
35

36
// local includes
37
#include "core/annotations.h"
38
#include "core/document.h"
39
40
41
#include "latexrenderer.h"
#include <core/utils.h>
#include <KMessageBox>
42
43
44
45

class CloseButton
  : public QPushButton
{
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
46
47
    Q_OBJECT

48
public:
Laurent Montel's avatar
Laurent Montel committed
49
    CloseButton( QWidget * parent = Q_NULLPTR )
50
51
52
      : QPushButton( parent )
    {
        setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
53
54
        QSize size = QSize( 14, 14 ).expandedTo( QApplication::globalStrut() );
        setFixedSize( size );
55
        setIcon( style()->standardIcon( QStyle::SP_DockWidgetCloseButton ) );
56
        setIconSize( size );
57
        setToolTip( i18n( "Close this note" ) );
58
        setCursor( Qt::ArrowCursor );
59
60
    }
};
61
62


63
64
class MovableTitle
  : public QWidget
65
{
Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
66
67
    Q_OBJECT

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public:
    MovableTitle( QWidget * parent )
      : QWidget( parent )
    {
        QVBoxLayout * mainlay = new QVBoxLayout( this );
        mainlay->setMargin( 0 );
        mainlay->setSpacing( 0 );
        // close button row
        QHBoxLayout * buttonlay = new QHBoxLayout();
        mainlay->addLayout( buttonlay );
        titleLabel = new QLabel( this );
        QFont f = titleLabel->font();
        f.setBold( true );
        titleLabel->setFont( f );
        titleLabel->setCursor( Qt::SizeAllCursor );
        buttonlay->addWidget( titleLabel );
        dateLabel = new QLabel( this );
        dateLabel->setAlignment( Qt::AlignTop | Qt::AlignRight );
86
87
88
        f = dateLabel->font();
        f.setPointSize( QFontInfo( f ).pointSize() - 2 );
        dateLabel->setFont( f );
89
90
91
        dateLabel->setCursor( Qt::SizeAllCursor );
        buttonlay->addWidget( dateLabel );
        CloseButton * close = new CloseButton( this );
Laurent Montel's avatar
Laurent Montel committed
92
        connect( close, &QAbstractButton::clicked, parent, &QWidget::close );
93
94
95
96
97
98
99
100
101
        buttonlay->addWidget( close );
        // option button row
        QHBoxLayout * optionlay = new QHBoxLayout();
        mainlay->addLayout( optionlay );
        authorLabel = new QLabel( this );
        authorLabel->setCursor( Qt::SizeAllCursor );
        authorLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
        optionlay->addWidget( authorLabel );
        optionButton = new QToolButton( this );
102
103
        QString opttext = i18n( "Options" );
        optionButton->setText( opttext );
104
        optionButton->setAutoRaise( true );
105
106
107
        QSize s = QFontMetrics( optionButton->font() ).boundingRect( opttext ).size() + QSize( 8, 8 );
        optionButton->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
        optionButton->setFixedSize( s );
108
        optionlay->addWidget( optionButton );
109
110
        // ### disabled for now
        optionButton->hide();
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
        latexButton = new QToolButton( this );
        QHBoxLayout * latexlay = new QHBoxLayout();
        QString latextext = i18n ( "This annotation may contain LaTeX code.\nClick here to render." );
        latexButton->setText( latextext );
        latexButton->setAutoRaise( true );
        s = QFontMetrics( latexButton->font() ).boundingRect(0, 0, this->width(), this->height(), 0, latextext ).size() + QSize( 8, 8 );
        latexButton->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
        latexButton->setFixedSize( s );
        latexButton->setCheckable( true );
        latexButton->setVisible( false );
        latexlay->addSpacing( 1 );
        latexlay->addWidget( latexButton );
        latexlay->addSpacing( 1 );
        mainlay->addLayout( latexlay );
        connect(latexButton, SIGNAL(clicked(bool)), parent, SLOT(renderLatex(bool)));
        connect(parent, SIGNAL(containsLatex(bool)), latexButton, SLOT(setVisible(bool)));
127
128
129
130
131
132

        titleLabel->installEventFilter( this );
        dateLabel->installEventFilter( this );
        authorLabel->installEventFilter( this );
    }

133
    bool eventFilter( QObject * obj, QEvent * e ) override
134
135
136
137
    {
        if ( obj != titleLabel && obj != authorLabel && obj != dateLabel )
            return false;

138
        QMouseEvent * me = nullptr;
139
140
141
142
143
        switch ( e->type() )
        {
            case QEvent::MouseButtonPress:
                me = (QMouseEvent*)e;
                mousePressPos = me->pos();
144
                parentWidget()->raise();
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
                break;
            case QEvent::MouseButtonRelease:
                mousePressPos = QPoint();
                break;
            case QEvent::MouseMove:
                me = (QMouseEvent*)e;
                parentWidget()->move( me->pos() - mousePressPos + parentWidget()->pos() );
                break;
            default:
                return false;
        }
        return true;
    }

    void setTitle( const QString& title )
    {
Laurent Montel's avatar
Laurent Montel committed
161
        titleLabel->setText( QStringLiteral( " " ) + title );
162
163
164
165
    }

    void setDate( const QDateTime& dt )
    {
166
        dateLabel->setText( QLocale().toString( dt.toTimeSpec(Qt::LocalTime), QLocale::ShortFormat ) + QLatin1Char(' ') );
167
168
169
170
    }

    void setAuthor( const QString& author )
    {
Laurent Montel's avatar
Laurent Montel committed
171
        authorLabel->setText( QStringLiteral( " " ) + author );
172
173
174
175
    }

    void connectOptionButton( QObject * recv, const char* method )
    {
Laurent Montel's avatar
Laurent Montel committed
176
        connect( optionButton, SIGNAL(clicked()), recv, method );
177
178
    }

179
180
181
182
183
    void uncheckLatexButton()
    {
        latexButton->setChecked( false );
    }

184
185
186
187
188
189
private:
    QLabel * titleLabel;
    QLabel * dateLabel;
    QLabel * authorLabel;
    QPoint mousePressPos;
    QToolButton * optionButton;
190
    QToolButton * latexButton;
191
192
193
};


194
// Qt::SubWindow is needed to make QSizeGrip work
195
196
AnnotWindow::AnnotWindow( QWidget * parent, Okular::Annotation * annot, Okular::Document *document, int page )
    : QFrame( parent, Qt::SubWindow ), m_annot( annot ), m_document( document ), m_page( page )
197
{
198
199
    setAutoFillBackground( true );
    setFrameStyle( Panel | Raised );
200
    setAttribute( Qt::WA_DeleteOnClose );
Laurent Montel's avatar
Laurent Montel committed
201
    setObjectName(QStringLiteral("AnnotWindow"));
202

203
204
    const bool canEditAnnotation = m_document->canModifyPageAnnotation( annot );

Pino Toscano's avatar
Pino Toscano committed
205
206
    textEdit = new KTextEdit( this );
    textEdit->setAcceptRichText( false );
207
    textEdit->setPlainText( m_annot->contents() );
208
    textEdit->installEventFilter( this );
209
210
211
212
213
    textEdit->setUndoRedoEnabled( false );

    m_prevCursorPos = textEdit->textCursor().position();
    m_prevAnchorPos = textEdit->textCursor().anchor();

Laurent Montel's avatar
Laurent Montel committed
214
215
216
217
    connect(textEdit, &KTextEdit::textChanged, this, &AnnotWindow::slotsaveWindowText);
    connect(textEdit, &KTextEdit::cursorPositionChanged, this, &AnnotWindow::slotsaveWindowText);
    connect(textEdit, &KTextEdit::aboutToShowContextMenu, this, &AnnotWindow::slotUpdateUndoAndRedoInContextMenu);
    connect(m_document, &Okular::Document::annotationContentsChangedByUndoRedo, this, &AnnotWindow::slotHandleContentsChangedByUndoRedo);
218

219
220
221
    if (!canEditAnnotation)
        textEdit->setReadOnly(true);

222
223
224
225
226
227
228
229
230
    QVBoxLayout * mainlay = new QVBoxLayout( this );
    mainlay->setMargin( 2 );
    mainlay->setSpacing( 0 );
    m_title = new MovableTitle( this );
    mainlay->addWidget( m_title );
    mainlay->addWidget( textEdit );
    QHBoxLayout * lowerlay = new QHBoxLayout();
    mainlay->addLayout( lowerlay );
    lowerlay->addItem( new QSpacerItem( 5, 5, QSizePolicy::Expanding, QSizePolicy::Fixed ) );
231
    QSizeGrip * sb = new QSizeGrip( this );
232
233
    lowerlay->addWidget( sb );

234
    m_latexRenderer = new GuiUtils::LatexRenderer();
235
    emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( m_annot->contents() ) );
236

237
    m_title->setTitle( m_annot->window().summary() );
Laurent Montel's avatar
Laurent Montel committed
238
    m_title->connectOptionButton( this, SLOT(slotOptionBtn()) );
239

240
    setGeometry(10,10,300,300 );
241
242
243
244

    reloadInfo();
}

245
246
247
248
249
AnnotWindow::~AnnotWindow()
{
    delete m_latexRenderer;
}

250
251
252
253
254
Okular::Annotation * AnnotWindow::annotation() const
{
    return m_annot;
}

255
256
257
258
259
void AnnotWindow::updateAnnotation( Okular::Annotation * a )
{
    m_annot = a;
}

260
261
void AnnotWindow::reloadInfo()
{
262
263
264
265
266
267
268
269
270
    QColor newcolor;
    if ( m_annot->subType() == Okular::Annotation::AText )
    {
        Okular::TextAnnotation * textAnn = static_cast< Okular::TextAnnotation * >( m_annot );
        if ( textAnn->textType() == Okular::TextAnnotation::InPlace && textAnn->inplaceIntent() == Okular::TextAnnotation::TypeWriter )
            newcolor = QColor("#fdfd96");
    }
    if ( !newcolor.isValid() )
        newcolor = m_annot->style().color().isValid() ? QColor(m_annot->style().color().red(), m_annot->style().color().green(), m_annot->style().color().blue(), 255) : Qt::yellow;
271
272
273
274
275
276
277
278
    if ( newcolor != m_color )
    {
        m_color = newcolor;
        setPalette( QPalette( m_color ) );
        QPalette pl = textEdit->palette();
        pl.setColor( QPalette::Base, m_color );
        textEdit->setPalette( pl );
    }
Pino Toscano's avatar
Pino Toscano committed
279
    m_title->setAuthor( m_annot->author() );
280
    m_title->setDate( m_annot->modificationDate() );
281
282
}

283
284
285
286
287
int AnnotWindow::pageNumber() const
{
    return m_page;
}

288
289
290
291
292
293
294
295
void AnnotWindow::showEvent( QShowEvent * event )
{
    QFrame::showEvent( event );

    // focus the content area by default
    textEdit->setFocus();
}

296
297
298
299
300
301
302
303
304
305
306
bool AnnotWindow::eventFilter(QObject *, QEvent *e)
{
    if ( e->type () == QEvent::ShortcutOverride )
    {
        QKeyEvent * keyEvent = static_cast< QKeyEvent * >( e );
        if ( keyEvent->key() == Qt::Key_Escape )
        {
            close();
            return true;
        }
    }
307
308
309
310
311
312
313
314
315
316
317
318
319
320
    else if (e->type() == QEvent::KeyPress)
    {
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(e);
        if (keyEvent == QKeySequence::Undo)
        {
            m_document->undo();
            return true;
        }
        else if (keyEvent == QKeySequence::Redo)
        {
            m_document->redo();
            return true;
        }
    }
321
322
323
324
    else if (e->type() == QEvent::FocusIn)
    {
        raise();
    }
325
326
327
    return false;
}

328
329
330
331
332
333
334
void AnnotWindow::slotUpdateUndoAndRedoInContextMenu(QMenu* menu)
{
    if (!menu) return;

    QList<QAction *> actionList = menu->actions();
    enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, ClearAct, SelectAllAct, NCountActs };

335
336
    QAction *kundo = KStandardAction::create( KStandardAction::Undo, m_document, SLOT(undo()), menu);
    QAction *kredo = KStandardAction::create( KStandardAction::Redo, m_document, SLOT(redo()), menu);
Laurent Montel's avatar
Laurent Montel committed
337
338
    connect(m_document, &Okular::Document::canUndoChanged, kundo, &QAction::setEnabled);
    connect(m_document, &Okular::Document::canRedoChanged, kredo, &QAction::setEnabled);
339
340
341
342
343
344
345
346
347
348
349
350
351
352
    kundo->setEnabled(m_document->canUndo());
    kredo->setEnabled(m_document->canRedo());

    QAction *oldUndo, *oldRedo;
    oldUndo = actionList[UndoAct];
    oldRedo = actionList[RedoAct];

    menu->insertAction(oldUndo, kundo);
    menu->insertAction(oldRedo, kredo);

    menu->removeAction(oldUndo);
    menu->removeAction(oldRedo);
}

353
void AnnotWindow::slotOptionBtn()
354
355
356
357
{
    //TODO: call context menu in pageview
    //emit sig...
}
358

359
void AnnotWindow::slotsaveWindowText()
360
{
361
362
    const QString contents = textEdit->toPlainText();
    const int cursorPos = textEdit->textCursor().position();
363
    if (contents != m_annot->contents())
364
    {
365
366
        m_document->editPageAnnotationContents( m_page, m_annot, contents, cursorPos, m_prevCursorPos, m_prevAnchorPos);
        emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( textEdit->toPlainText() ) );
367
    }
368
369
    m_prevCursorPos = cursorPos;
    m_prevAnchorPos = textEdit->textCursor().anchor();
370
371
372
373
374
375
376
}

void AnnotWindow::renderLatex( bool render )
{
    if (render)
    {
        textEdit->setReadOnly( true );
Laurent Montel's avatar
Laurent Montel committed
377
378
        disconnect(textEdit, &KTextEdit::textChanged, this, &AnnotWindow::slotsaveWindowText);
        disconnect(textEdit, &KTextEdit::cursorPositionChanged, this, &AnnotWindow::slotsaveWindowText);
379
        textEdit->setAcceptRichText( true );
380
        QString contents = m_annot->contents();
381
382
383
384
        contents = Qt::convertFromPlainText( contents );
        QColor fontColor = textEdit->textColor();
        int fontSize = textEdit->fontPointSize();
        QString latexOutput;
385
        GuiUtils::LatexRenderer::Error errorCode = m_latexRenderer->renderLatexInHtml( contents, fontColor, fontSize, Okular::Utils::realDpi(nullptr).width(), latexOutput );
386
387
388
389
390
391
392
393
394
395
396
397
398
        switch ( errorCode )
        {
            case GuiUtils::LatexRenderer::LatexNotFound:
                KMessageBox::sorry( this, i18n( "Cannot find latex executable." ), i18n( "LaTeX rendering failed" ) );
                m_title->uncheckLatexButton();
                renderLatex( false );
                break;
            case GuiUtils::LatexRenderer::DvipngNotFound:
                KMessageBox::sorry( this, i18n( "Cannot find dvipng executable." ), i18n( "LaTeX rendering failed" ) );
                m_title->uncheckLatexButton();
                renderLatex( false );
                break;
            case GuiUtils::LatexRenderer::LatexFailed:
399
                KMessageBox::detailedSorry( this, i18n( "A problem occurred during the execution of the 'latex' command." ), latexOutput, i18n( "LaTeX rendering failed" ) );
400
401
402
403
                m_title->uncheckLatexButton();
                renderLatex( false );
                break;
            case GuiUtils::LatexRenderer::DvipngFailed:
404
                KMessageBox::sorry( this, i18n( "A problem occurred during the execution of the 'dvipng' command." ), i18n( "LaTeX rendering failed" ) );
405
406
407
408
409
410
411
412
413
414
415
416
                m_title->uncheckLatexButton();
                renderLatex( false );
                break;
            case GuiUtils::LatexRenderer::NoError:
            default:
                textEdit->setHtml( contents );
                break;
        }
    }
    else
    {
        textEdit->setAcceptRichText( false );
417
        textEdit->setPlainText( m_annot->contents() );
Laurent Montel's avatar
Laurent Montel committed
418
419
        connect(textEdit, &KTextEdit::textChanged, this, &AnnotWindow::slotsaveWindowText);
        connect(textEdit, &KTextEdit::cursorPositionChanged, this, &AnnotWindow::slotsaveWindowText);
420
421
        textEdit->setReadOnly( false );
    }
422
}
423

Laurent Montel's avatar
Laurent Montel committed
424
void AnnotWindow::slotHandleContentsChangedByUndoRedo(Okular::Annotation* annot, const QString &contents, int cursorPos, int anchorPos)
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
{
    if ( annot != m_annot )
    {
        return;
    }

    textEdit->setPlainText(contents);
    QTextCursor c = textEdit->textCursor();
    c.setPosition(anchorPos);
    c.setPosition(cursorPos,QTextCursor::KeepAnchor);
    m_prevCursorPos = cursorPos;
    m_prevAnchorPos = anchorPos;
    textEdit->setTextCursor(c);
    textEdit->setFocus();
    emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( m_annot->contents() ) );
}

Albert Vaca Cintora's avatar
Albert Vaca Cintora committed
442
#include "annotwindow.moc"