1 /* This file is part of the KDE project
2    Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
3    Copyright (C) 2004-2012 Jarosław Staniek <staniek@kde.org>
4    Copyright (C) 2014 Wojciech Kosowicz <pcellix@gmail.com>
5 
6    This program is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public
8    License as published by the Free Software Foundation; either
9    version 2 of the License, or (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15 
16    You should have received a copy of the GNU Library General Public License
17    along with this program; see the file COPYING.  If not, write to
18    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20 */
21 
22 #include "kexidbtextedit.h"
23 #include "kexidblineedit.h"
24 #include <kexiutils/utils.h>
25 
26 #include <KDbQuerySchema>
27 
28 #include <KStandardShortcut>
29 
30 #include <QApplication>
31 #include <QPaintEvent>
32 #include <QPainter>
33 #include <QLabel>
34 #include <QMenu>
35 #include <QFontDatabase>
36 
37 class DataSourceLabel : public QLabel
38 {
39     Q_OBJECT
40 public:
DataSourceLabel(QWidget * parent)41     explicit DataSourceLabel(QWidget *parent) : QLabel(parent)
42     {
43     }
44 protected:
paintEvent(QPaintEvent * pe)45     void paintEvent(QPaintEvent *pe)
46     {
47         QLabel::paintEvent(pe);
48         QPainter p(this);
49         int leftMargin, topMargin, rightMargin, bottomMargin;
50         getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
51         QRect r( rect() );
52         r.setX(r.x() + leftMargin);
53         r.setY(r.y() + topMargin);
54         r.setRight(r.right() - rightMargin);
55         r.setBottom(r.bottom() - bottomMargin);
56         QPixmap dataSourceTagIcon;
57         int x;
58         if (layoutDirection() == Qt::LeftToRight) {
59             dataSourceTagIcon = KexiFormUtils::dataSourceTagIcon();
60             x = r.left() - 1;
61         }
62         else {
63             dataSourceTagIcon = KexiFormUtils::dataSourceRTLTagIcon();
64             x = r.right() - dataSourceTagIcon.width() - 5;
65         }
66         p.drawPixmap(
67             x, r.top() + (r.height() - dataSourceTagIcon.height()) / 2,
68             dataSourceTagIcon
69         );
70     }
71 };
72 
73 // --------------
74 
KexiDBTextEdit(QWidget * parent)75 KexiDBTextEdit::KexiDBTextEdit(QWidget *parent)
76         : KTextEdit(parent)
77         , KexiDBTextWidgetInterface()
78         , KexiFormDataItemInterface()
79         , m_menuExtender(this, this)
80         , m_slotTextChanged_enabled(true)
81         , m_dataSourceLabel(0)
82         , m_length(0)
83         , m_paletteChangeEvent_enabled(true)
84 {
85     QFont tmpFont;
86     tmpFont.setPointSize(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont).pointSize());
87     setMinimumHeight(QFontMetrics(tmpFont).height() + 6);
88     connect(this, SIGNAL(textChanged()), this, SLOT(slotTextChanged()));
89 //hmm disabled again because this makes the widget disappear entirely
90 //    setAutoFillBackground(true); // otherwise we get transparent background...
91 //    installEventFilter(this);
92     setBackgroundRole(QPalette::Base);
93     setAcceptRichText(false);
94 }
95 
~KexiDBTextEdit()96 KexiDBTextEdit::~KexiDBTextEdit()
97 {
98 }
99 
setInvalidState(const QString & displayText)100 void KexiDBTextEdit::setInvalidState(const QString& displayText)
101 {
102     setReadOnly(true);
103 //! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ?
104     if (focusPolicy() & Qt::TabFocus)
105         setFocusPolicy(Qt::ClickFocus);
106     KTextEdit::setPlainText(displayText);
107 }
108 
setValueInternal(const QVariant & add,bool removeOld)109 void KexiDBTextEdit::setValueInternal(const QVariant& add, bool removeOld)
110 {
111 //! @todo how about rich text?
112     if (m_columnInfo && m_columnInfo->field()->type() == KDbField::Boolean) {
113 //! @todo temporary solution for booleans!
114         KTextEdit::setHtml(add.toBool() ? "1" : "0");
115     } else {
116         QString t;
117         if (removeOld) {
118             t = add.toString();
119         }
120         else {
121             t = KexiDataItemInterface::originalValue().toString() + add.toString();
122         }
123 
124         if (acceptRichText()) {
125             KTextEdit::setHtml(t);
126         }
127         else {
128             KTextEdit::setPlainText(t);
129         }
130     }
131 }
132 
value()133 QVariant KexiDBTextEdit::value()
134 {
135     return acceptRichText() ? toHtml() : toPlainText();
136 }
137 
slotTextChanged()138 void KexiDBTextEdit::slotTextChanged()
139 {
140     if (!m_slotTextChanged_enabled)
141         return;
142 
143     if (m_length > 0) {
144         QString t;
145         if (acceptRichText()) {
146             t = toHtml();
147         }
148         else {
149             t = toPlainText();
150         }
151         if (t.length() > (int)m_length) {
152             m_slotTextChanged_enabled = false;
153             if (acceptRichText()) {
154 //! @todo KEXI3 setHtml(t.left(m_length));
155             }
156             else {
157                 setPlainText(t.left(m_length));
158             }
159             m_slotTextChanged_enabled = true;
160             moveCursorToEnd();
161         }
162     }
163 
164     signalValueChanged();
165 }
166 
valueIsNull()167 bool KexiDBTextEdit::valueIsNull()
168 {
169     return (acceptRichText() ? toHtml() : toPlainText()).isNull();
170 }
171 
valueIsEmpty()172 bool KexiDBTextEdit::valueIsEmpty()
173 {
174     return (acceptRichText() ? toHtml() : toPlainText()).isEmpty();
175 }
176 
isReadOnly() const177 bool KexiDBTextEdit::isReadOnly() const
178 {
179     return KTextEdit::isReadOnly();
180 }
181 
setReadOnly(bool readOnly)182 void KexiDBTextEdit::setReadOnly(bool readOnly)
183 {
184     KTextEdit::setReadOnly(readOnly);
185 //! @todo KEXI3 KexiDBTextEdit::setReadOnly() - bg color
186 #if 0//TODO
187     QPalette p = palette();
188     QColor c(readOnly
189              ? KexiFormUtils::lighterGrayBackgroundColor(kapp->palette()) : p.color(QPalette::Active, QPalette::Base));
190     setPaper(c);
191     p.setColor(QPalette::Base, c);
192     p.setColor(QPalette::Background, c);
193     setPalette(p);
194 #endif
195 }
196 
widget()197 QWidget* KexiDBTextEdit::widget()
198 {
199     return this;
200 }
201 
cursorAtStart()202 bool KexiDBTextEdit::cursorAtStart()
203 {
204     return textCursor().atStart();
205 }
206 
cursorAtEnd()207 bool KexiDBTextEdit::cursorAtEnd()
208 {
209     return textCursor().atEnd();
210 }
211 
clear()212 void KexiDBTextEdit::clear()
213 {
214     document()->clear();
215 }
216 
setColumnInfo(KDbConnection * conn,KDbQueryColumnInfo * cinfo)217 void KexiDBTextEdit::setColumnInfo(KDbConnection *conn, KDbQueryColumnInfo* cinfo)
218 {
219     KexiFormDataItemInterface::setColumnInfo(conn, cinfo);
220     if (!cinfo) {
221         m_length = 0;
222         return;
223     }
224 
225     if (cinfo->field()->type() == KDbField::Text) {
226         if (!designMode()) {
227             if (cinfo->field()->maxLength() > 0) {
228                 m_length = cinfo->field()->maxLength();
229             }
230         }
231     }
232 
233     KexiDBTextWidgetInterface::setColumnInfo(m_columnInfo, this);
234 }
235 
paintEvent(QPaintEvent * pe)236 void KexiDBTextEdit::paintEvent(QPaintEvent *pe)
237 {
238     KTextEdit::paintEvent(pe);
239     QPainter p(viewport());
240     //! @todo how about rich text?
241     KexiDBTextWidgetInterface::paint(this, &p, toPlainText().isEmpty(), alignment(), hasFocus());
242 }
243 
contextMenuEvent(QContextMenuEvent * e)244 void KexiDBTextEdit::contextMenuEvent(QContextMenuEvent *e)
245 {
246     QMenu *menu = createStandardContextMenu();
247     m_menuExtender.exec(menu, e->globalPos());
248     delete menu;
249 }
250 
undo()251 void KexiDBTextEdit::undo()
252 {
253     cancelEditor();
254 }
255 
setDisplayDefaultValue(QWidget * widget,bool displayDefaultValue)256 void KexiDBTextEdit::setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue)
257 {
258     KexiFormDataItemInterface::setDisplayDefaultValue(widget, displayDefaultValue);
259     // initialize display parameters for default / entered value
260     KexiDisplayUtils::DisplayParameters * const params
261         = displayDefaultValue ? m_displayParametersForDefaultValue : m_displayParametersForEnteredValue;
262     QPalette pal(palette());
263     pal.setColor(QPalette::Active, QPalette::Text, params->textColor);
264     setPalette(pal);
265     setFont(params->font);
266 //! @todo support rich text...
267     /* m_slotTextChanged_enabled = false;
268         //for rich text...
269         const QString origText( text() );
270         KTextEdit::setText(QString());
271         setCurrentFont(params->font);
272         setColor(params->textColor);
273         KTextEdit::setText(origText);
274       m_slotTextChanged_enabled = true;*/
275 }
276 
moveCursorToEnd()277 void KexiDBTextEdit::moveCursorToEnd()
278 {
279     moveCursor(QTextCursor::End);
280 }
281 
moveCursorToStart()282 void KexiDBTextEdit::moveCursorToStart()
283 {
284     moveCursor(QTextCursor::Start);
285 }
286 
selectAll()287 void KexiDBTextEdit::selectAll()
288 {
289     KTextEdit::selectAll();
290 }
291 
keyPressEvent(QKeyEvent * ke)292 void KexiDBTextEdit::keyPressEvent(QKeyEvent *ke)
293 {
294     // for instance, Windows uses Ctrl+Tab for moving between tabs, so do not steal this shortcut
295     if (KStandardShortcut::tabNext().contains(QKeySequence(ke->key() | ke->modifiers()))
296             || KStandardShortcut::tabPrev().contains(QKeySequence(ke->key() | ke->modifiers()))) {
297         ke->ignore();
298         return;
299     }
300     KTextEdit::keyPressEvent(ke);
301 }
302 
event(QEvent * e)303 bool KexiDBTextEdit::event(QEvent *e)
304 {
305     bool res = KTextEdit::event(e);
306 
307     if (e->type() == QEvent::LayoutDirectionChange) {
308         if (m_dataSourceLabel) {
309             m_dataSourceLabel->setLayoutDirection( layoutDirection() );
310         }
311         updateTextForDataSource();
312     }
313     else if (e->type() == QEvent::Resize) {
314         if (m_dataSourceLabel) {
315             m_dataSourceLabel->setFixedWidth(width());
316         }
317     }
318     return res;
319 }
320 
updateTextForDataSource()321 void KexiDBTextEdit::updateTextForDataSource()
322 {
323     if (!designMode()) {
324         if (m_dataSourceLabel) {
325             m_dataSourceLabel->hide();
326         }
327         return;
328     }
329     setPlainText(QString());
330     if (!m_dataSourceLabel && !dataSource().isEmpty()) {
331         createDataSourceLabel();
332     }
333     if (m_dataSourceLabel) {
334         m_dataSourceLabel->setText(dataSource());
335         m_dataSourceLabel->setIndent( KexiFormUtils::dataSourceTagIcon().width()
336             + (layoutDirection() == Qt::LeftToRight ? 0 : 7) );
337         m_dataSourceLabel->setVisible(!dataSource().isEmpty());
338     }
339 }
340 
setDataSource(const QString & ds)341 void KexiDBTextEdit::setDataSource(const QString &ds)
342 {
343     KexiFormDataItemInterface::setDataSource(ds);
344     updateTextForDataSource();
345 }
346 
setDataSourcePluginId(const QString & pluginId)347 void KexiDBTextEdit::setDataSourcePluginId(const QString &pluginId)
348 {
349     KexiFormDataItemInterface::setDataSourcePluginId(pluginId);
350     updateTextForDataSource();
351 }
352 
createDataSourceLabel()353 void KexiDBTextEdit::createDataSourceLabel()
354 {
355     if (m_dataSourceLabel)
356         return;
357     m_dataSourceLabel = new DataSourceLabel(viewport());
358     m_dataSourceLabel->hide();
359     m_dataSourceLabel->move(0, 0);
360     int leftMargin, topMargin, rightMargin, bottomMargin;
361     getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
362     m_dataSourceLabel->setContentsMargins(leftMargin, topMargin, rightMargin, bottomMargin);
363 }
364 
selectAllOnFocusIfNeeded()365 void KexiDBTextEdit::selectAllOnFocusIfNeeded()
366 {
367 }
368 
focusOutEvent(QFocusEvent * e)369 void KexiDBTextEdit::focusOutEvent(QFocusEvent *e)
370 {
371     KTextEdit::focusOutEvent(e);
372     if (textCursor().hasSelection()) {
373         moveCursorToEnd();
374     }
375 }
376 
updatePalette()377 void KexiDBTextEdit::updatePalette()
378 {
379     m_paletteChangeEvent_enabled = false;
380     setPalette(isReadOnly() ?
381                KexiUtils::paletteForReadOnly(m_originalPalette)
382               : m_originalPalette);
383     m_paletteChangeEvent_enabled = true;
384 }
385 
changeEvent(QEvent * e)386 void KexiDBTextEdit::changeEvent(QEvent *e)
387 {
388     if (e->type() == QEvent::PaletteChange && m_paletteChangeEvent_enabled) {
389         m_originalPalette = palette();
390         updatePalette();
391     }
392     KTextEdit::changeEvent(e);
393 }
394 
395 #include "kexidbtextedit.moc"
396