1 /* This file is part of the KDE project
2 Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
3 Copyright (C) 2004-2011 Jarosław Staniek <staniek@kde.org>
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this program; see the file COPYING. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include "kexidbimagebox.h"
22 #include <KexiIcon.h>
23 #include <widget/utils/kexidropdownbutton.h>
24 #include <kexiutils/utils.h>
25 #include <formeditor/widgetlibrary.h>
26 #include <formeditor/utils.h>
27 #include <kexi_global.h>
28 #include "kexidbutils.h"
29 #include "kexiformpart.h"
30 #include "kexiformmanager.h"
31
32 #include <KDbField>
33 #include <KDbUtils>
34 #include <KDbQuerySchema>
35
36 #include <KIconLoader>
37 #include <KLocalizedString>
38 #include <KIconEffect>
39
40 #include <QApplication>
41 #include <QPixmap>
42 #include <QStyle>
43 #include <QStyleOptionFocusRect>
44 #include <QStyleOptionFrame>
45 #include <QClipboard>
46 #include <QFile>
47 #include <QBuffer>
48 #include <QPainter>
49 #include <QMimeDatabase>
50 #include <QStandardPaths>
51 #include <QMimeType>
52 #include <QImageReader>
53 #include <QDebug>
54
55 //! @internal
56 struct KexiDBImageBox_Static {
KexiDBImageBox_StaticKexiDBImageBox_Static57 KexiDBImageBox_Static() : pixmap(0), small(0) {}
~KexiDBImageBox_StaticKexiDBImageBox_Static58 ~KexiDBImageBox_Static() { delete pixmap; delete small; }
59 QPixmap *pixmap;
60 QPixmap *small;
61 };
62
Q_GLOBAL_STATIC(KexiDBImageBox_Static,KexiDBImageBox_static)63 Q_GLOBAL_STATIC(KexiDBImageBox_Static, KexiDBImageBox_static)
64
65 KexiDBImageBox::KexiDBImageBox(bool designMode, QWidget *parent)
66 : KexiFrame(parent)
67 , KexiFormDataItemInterface()
68 , m_alignment(Qt::AlignLeft | Qt::AlignTop)
69 , m_readOnly(false)
70 , m_scaledContents(false)
71 , m_smoothTransformation(true)
72 , m_keepAspectRatio(true)
73 , m_insideSetData(false)
74 , m_setFocusOnButtonAfterClosingPopup(false)
75 , m_paintEventEnabled(true)
76 , m_dropDownButtonVisible(true)
77 , m_insideSetPalette(false)
78 {
79 setDesignMode(designMode);
80 installEventFilter(this);
81 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
82 QPalette pal(palette());
83 pal.setBrush(backgroundRole(), QBrush(Qt::transparent));
84 KexiFrame::setPalette(pal);
85
86 m_contextMenu = new KexiImageContextMenu(this);
87 m_contextMenu->installEventFilter(this);
88
89 if (designMode) {
90 m_chooser = 0;
91 } else {
92 m_chooser = new KexiDropDownButton(this);
93 m_chooser->setFocusPolicy(Qt::StrongFocus);
94 m_chooser->setMenu(m_contextMenu);
95 setFocusProxy(m_chooser);
96 m_chooser->installEventFilter(this);
97 }
98
99 setFrameShape(QFrame::Box);
100 setFrameShadow(QFrame::Plain);
101 setFrameColor(palette().color(QPalette::Foreground));
102
103 m_paletteBackgroundColorChanged = false; //set this here, not before
104
105 connect(m_contextMenu, SIGNAL(updateActionsAvailabilityRequested(bool*,bool*)),
106 this, SLOT(slotUpdateActionsAvailabilityRequested(bool*,bool*)));
107 connect(m_contextMenu, SIGNAL(insertFromFileRequested(QUrl)),
108 this, SLOT(handleInsertFromFileAction(QUrl)));
109 connect(m_contextMenu, SIGNAL(saveAsRequested(QUrl)),
110 this, SLOT(handleSaveAsAction(QUrl)));
111 connect(m_contextMenu, SIGNAL(cutRequested()),
112 this, SLOT(handleCutAction()));
113 connect(m_contextMenu, SIGNAL(copyRequested()),
114 this, SLOT(handleCopyAction()));
115 connect(m_contextMenu, SIGNAL(pasteRequested()),
116 this, SLOT(handlePasteAction()));
117 connect(m_contextMenu, SIGNAL(clearRequested()),
118 this, SLOT(clear()));
119 connect(m_contextMenu, SIGNAL(showPropertiesRequested()),
120 this, SLOT(handleShowPropertiesAction()));
121
122 KexiFrame::setLineWidth(0);
123 setDataSource(QString()); //to initialize popup menu and actions availability
124 }
125
~KexiDBImageBox()126 KexiDBImageBox::~KexiDBImageBox()
127 {
128 }
129
contextMenu() const130 KexiImageContextMenu* KexiDBImageBox::contextMenu() const
131 {
132 return m_contextMenu;
133 }
134
value()135 QVariant KexiDBImageBox::value()
136 {
137 if (dataSource().isEmpty()) {
138 //not db-aware
139 return QVariant();
140 }
141 //db-aware mode
142 return m_value; //!< @todo
143 }
144
setValueInternal(const QVariant & add,bool removeOld,bool loadPixmap)145 void KexiDBImageBox::setValueInternal(const QVariant& add, bool removeOld, bool loadPixmap)
146 {
147 if (isReadOnly())
148 return;
149 m_contextMenu->hide();
150 if (removeOld)
151 m_value = add.toByteArray();
152 else //do not add "m_origValue" to "add" as this is QByteArray
153 m_value = KexiDataItemInterface::originalValue().toByteArray();
154 bool ok = !m_value.isEmpty();
155 if (ok) {
156 if (loadPixmap) {
157 ok = KexiUtils::loadPixmapFromData(&m_pixmap, m_value);
158 m_currentScaledPixmap = QPixmap(); // clear cache
159 }
160 if (!ok) {
161 //! @todo inform about error?
162 }
163 }
164 if (!ok) {
165 m_valueMimeType.clear();
166 m_pixmap = QPixmap();
167 m_currentScaledPixmap = QPixmap(); // clear cache
168 }
169 repaint();
170 }
171
setInvalidState(const QString & displayText)172 void KexiDBImageBox::setInvalidState(const QString& displayText)
173 {
174 Q_UNUSED(displayText);
175 if (!dataSource().isEmpty()) {
176 m_value = QByteArray();
177 }
178
179 //! @todo m_pixmapLabel->setText( displayText );
180
181 if (m_chooser)
182 m_chooser->hide();
183 setReadOnly(true);
184 }
185
valueIsNull()186 bool KexiDBImageBox::valueIsNull()
187 {
188 return m_value.isEmpty();
189 }
190
valueIsEmpty()191 bool KexiDBImageBox::valueIsEmpty()
192 {
193 return false;
194 }
195
isReadOnly() const196 bool KexiDBImageBox::isReadOnly() const
197 {
198 return m_readOnly;
199 }
200
setReadOnly(bool set)201 void KexiDBImageBox::setReadOnly(bool set)
202 {
203 m_readOnly = set;
204 }
205
pixmap() const206 QPixmap KexiDBImageBox::pixmap() const
207 {
208 if (dataSource().isEmpty()) {
209 //not db-aware
210 return m_data.pixmap();
211 }
212 //db-aware mode
213 return m_pixmap;
214 }
215
pixmapId() const216 int KexiDBImageBox::pixmapId() const
217 {
218 if (dataSource().isEmpty()) {
219 //not db-aware
220 return m_data.id();
221 }
222 return 0;
223 }
224
setPixmapId(int id)225 void KexiDBImageBox::setPixmapId(int id)
226 {
227 if (m_insideSetData) //avoid recursion
228 return;
229 setData(KexiBLOBBuffer::self()->objectForId(id, /*unstored*/false));
230 repaint();
231 }
232
storedPixmapId() const233 int KexiDBImageBox::storedPixmapId() const
234 {
235 if (dataSource().isEmpty() && m_data.stored()) {
236 //not db-aware
237 return m_data.id();
238 }
239 return 0;
240 }
241
setStoredPixmapId(int id)242 void KexiDBImageBox::setStoredPixmapId(int id)
243 {
244 setData(KexiBLOBBuffer::self()->objectForId(id, /*stored*/true));
245 repaint();
246 }
247
hasScaledContents() const248 bool KexiDBImageBox::hasScaledContents() const
249 {
250 return m_scaledContents;
251 }
252
setScaledContents(bool set)253 void KexiDBImageBox::setScaledContents(bool set)
254 {
255 //! @todo m_pixmapLabel->setScaledContents(set);
256 m_scaledContents = set;
257 m_currentScaledPixmap = QPixmap();
258 repaint();
259 }
260
smoothTransformation() const261 bool KexiDBImageBox::smoothTransformation() const
262 {
263 return m_smoothTransformation;
264 }
265
alignment() const266 Qt::Alignment KexiDBImageBox::alignment() const
267 {
268 return m_alignment;
269 }
270
keepAspectRatio() const271 bool KexiDBImageBox::keepAspectRatio() const
272 {
273 return m_keepAspectRatio;
274 }
275
setSmoothTransformation(bool set)276 void KexiDBImageBox::setSmoothTransformation(bool set)
277 {
278 m_smoothTransformation = set;
279 m_currentScaledPixmap = QPixmap();
280 repaint();
281 }
282
setKeepAspectRatio(bool set)283 void KexiDBImageBox::setKeepAspectRatio(bool set)
284 {
285 m_keepAspectRatio = set;
286 m_currentScaledPixmap = QPixmap();
287 if (m_scaledContents) {
288 repaint();
289 }
290 }
291
widget()292 QWidget* KexiDBImageBox::widget()
293 {
294 //! @todo return m_pixmapLabel;
295 return this;
296 }
297
cursorAtStart()298 bool KexiDBImageBox::cursorAtStart()
299 {
300 return true;
301 }
302
cursorAtEnd()303 bool KexiDBImageBox::cursorAtEnd()
304 {
305 return true;
306 }
307
data() const308 QByteArray KexiDBImageBox::data() const
309 {
310 if (dataSource().isEmpty()) {
311 //static mode
312 return m_data.data();
313 } else {
314 //db-aware mode
315 return m_value;
316 }
317 }
318
insertFromFile()319 void KexiDBImageBox::insertFromFile()
320 {
321 m_contextMenu->insertFromFile();
322 }
323
handleInsertFromFileAction(const QUrl & url)324 void KexiDBImageBox::handleInsertFromFileAction(const QUrl &url)
325 {
326 if (!dataSource().isEmpty() && isReadOnly())
327 return;
328
329 if (dataSource().isEmpty()) {
330 //static mode
331 KexiBLOBBuffer::Handle h = KexiBLOBBuffer::self()->insertPixmap(url);
332 if (!h)
333 return;
334 setData(h);
335 repaint();
336 } else {
337 //db-aware
338 QString fileName(url.isLocalFile() ? url.toLocalFile() : url.toDisplayString());
339
340 //! @todo download the file if remote, then set fileName properly
341 QFile f(fileName);
342 if (!f.open(QIODevice::ReadOnly)) {
343 //! @todo err msg
344 return;
345 }
346 QByteArray ba = f.readAll();
347 if (f.error() != QFile::NoError) {
348 //! @todo err msg
349 f.close();
350 return;
351 }
352 QMimeDatabase db;
353 m_valueMimeType = db.mimeTypeForFile(fileName, QMimeDatabase::MatchExtension).name();
354 setValueInternal(ba, true);
355 }
356
357 //! @todo emit signal for setting "dirty" flag within the design
358 if (!dataSource().isEmpty()) {
359 signalValueChanged();
360 }
361 }
362
handleAboutToSaveAsAction(QString * origFilename,QString * mimeType,bool * dataIsEmpty)363 void KexiDBImageBox::handleAboutToSaveAsAction(
364 QString* origFilename, QString* mimeType, bool *dataIsEmpty)
365 {
366 Q_ASSERT(origFilename);
367 Q_ASSERT(mimeType);
368 Q_ASSERT(dataIsEmpty);
369 if (data().isEmpty()) {
370 qWarning() << "no pixmap!";
371 *dataIsEmpty = false;
372 return;
373 }
374 if (dataSource().isEmpty()) { //for static images filename and mimetype can be available
375 *origFilename = m_data.originalFileName();
376 if (!origFilename->isEmpty()) {
377 *origFilename = QLatin1String("/") + *origFilename;
378 }
379 const QMimeDatabase db;
380 const QMimeType mime(db.mimeTypeForName(m_data.mimeType()));
381 if (!m_data.mimeType().isEmpty() && QImageReader::supportedMimeTypes().contains(mime.name().toLatin1())) {
382 *mimeType = mime.name();
383 }
384 }
385 }
386
handleSaveAsAction(const QUrl & url)387 bool KexiDBImageBox::handleSaveAsAction(const QUrl &url)
388 {
389 //! @todo handle remote URLs
390 QFile f(url.toLocalFile());
391 if (!f.open(QIODevice::WriteOnly)) {
392 //! @todo err msg
393 return false;
394 }
395 f.write(data());
396 if (f.error() != QFile::NoError) {
397 //! @todo err msg
398 f.close();
399 return false;
400 }
401 f.close();
402 return true;
403 }
404
handleCutAction()405 void KexiDBImageBox::handleCutAction()
406 {
407 if (!dataSource().isEmpty() && isReadOnly())
408 return;
409 handleCopyAction();
410 clear();
411 }
412
handleCopyAction()413 void KexiDBImageBox::handleCopyAction()
414 {
415 qApp->clipboard()->setPixmap(pixmap(), QClipboard::Clipboard);
416 }
417
handlePasteAction()418 void KexiDBImageBox::handlePasteAction()
419 {
420 if (isReadOnly() || (!designMode() && !hasFocus()))
421 return;
422 QPixmap pm(qApp->clipboard()->pixmap(QClipboard::Clipboard));
423 if (dataSource().isEmpty()) {
424 //static mode
425 KexiBLOBBuffer::Handle h = KexiBLOBBuffer::self()->insertPixmap(pm);
426 if (!h)
427 return;
428 setData(h);
429 } else {
430 //db-aware mode
431 m_pixmap = pm;
432 QByteArray ba;
433 QBuffer buffer(&ba);
434 buffer.open(QIODevice::WriteOnly);
435 if (m_pixmap.save(&buffer, "PNG")) { // write pixmap into ba in PNG format
436 setValueInternal(ba, true, false/* !loadPixmap */);
437 m_currentScaledPixmap = QPixmap(); // clear cache
438 } else {
439 setValueInternal(QByteArray(), true);
440 }
441 }
442
443 repaint();
444 if (!dataSource().isEmpty()) {
445 signalValueChanged();
446 }
447 }
448
clear()449 void KexiDBImageBox::clear()
450 {
451 if (dataSource().isEmpty()) {
452 //static mode
453 setData(KexiBLOBBuffer::Handle());
454 } else {
455 if (isReadOnly())
456 return;
457 //db-aware mode
458 setValueInternal(QByteArray(), true);
459 }
460
461 //! @todo emit signal for setting "dirty" flag within the design
462
463 repaint();
464 if (!dataSource().isEmpty()) {
465 signalValueChanged();
466 }
467 }
468
handleShowPropertiesAction()469 void KexiDBImageBox::handleShowPropertiesAction()
470 {
471 //! @todo
472 }
473
slotUpdateActionsAvailabilityRequested(bool * valueIsNull,bool * valueIsReadOnly)474 void KexiDBImageBox::slotUpdateActionsAvailabilityRequested(bool* valueIsNull, bool* valueIsReadOnly)
475 {
476 Q_ASSERT(valueIsNull);
477 Q_ASSERT(valueIsReadOnly);
478 *valueIsNull = !(
479 (dataSource().isEmpty() && !pixmap().isNull()) /*static pixmap available*/
480 || (!dataSource().isEmpty() && !this->valueIsNull()) /*db-aware pixmap available*/
481 );
482 // read-only if static pixmap or db-aware pixmap for read-only widget:
483 *valueIsReadOnly =
484 (!designMode() && dataSource().isEmpty())
485 || (!dataSource().isEmpty() && isReadOnly())
486 || (designMode() && !dataSource().isEmpty());
487 }
488
contextMenuEvent(QContextMenuEvent * e)489 void KexiDBImageBox::contextMenuEvent(QContextMenuEvent * e)
490 {
491 if (popupMenuAvailable())
492 m_contextMenu->exec(e->globalPos());
493 }
494
updateActionStrings()495 void KexiDBImageBox::updateActionStrings()
496 {
497 if (!m_contextMenu)
498 return;
499 if (designMode()) {
500 }
501 else {
502 //update title in data view mode, based on the data source
503 if (columnInfo()) {
504 KexiImageContextMenu::updateTitle(m_contextMenu, columnInfo()->captionOrAliasOrName(),
505 KexiFormManager::self()->library()->iconName(metaObject()->className()));
506 }
507 }
508
509 if (m_chooser) {
510 if (popupMenuAvailable() && dataSource().isEmpty()) { //this may work in the future (see @todo below)
511 m_chooser->setToolTip(xi18n("Click to show actions for this image box"));
512 } else {
513 QString beautifiedImageBoxName;
514 if (designMode()) {
515 beautifiedImageBoxName = dataSource();
516 } else {
517 beautifiedImageBoxName = columnInfo() ? columnInfo()->captionOrAliasOrName() : QString();
518 /*! @todo look at makeFirstCharacterUpperCaseInCaptions setting [bool]
519 (see doc/dev/settings.txt) */
520 beautifiedImageBoxName = beautifiedImageBoxName[0].toUpper() + beautifiedImageBoxName.mid(1);
521 }
522 m_chooser->setToolTip(
523 xi18n("Click to show actions for <interface>%1</interface> image box", beautifiedImageBoxName));
524 }
525 }
526 }
527
popupMenuAvailable()528 bool KexiDBImageBox::popupMenuAvailable()
529 {
530 /*! @todo add kexi-global setting which anyway, allows to show this button
531 (read-only actions like copy/save as/print can be available) */
532 //chooser button can be only visible when data source is specified
533 return !dataSource().isEmpty();
534 }
535
setDataSource(const QString & ds)536 void KexiDBImageBox::setDataSource(const QString &ds)
537 {
538 KexiFormDataItemInterface::setDataSource(ds);
539 setData(KexiBLOBBuffer::Handle());
540 updateActionStrings();
541 KexiFrame::setFocusPolicy(focusPolicy()); //set modified policy
542
543 if (m_chooser) {
544 m_chooser->setEnabled(popupMenuAvailable());
545 if (m_dropDownButtonVisible && popupMenuAvailable()) {
546 m_chooser->show();
547 } else {
548 m_chooser->hide();
549 }
550 }
551
552 // update some properties not changed by user
553 //! @todo get default line width from global style settings
554 // if (!m_lineWidthChanged) {
555 // KexiFrame::setLineWidth(ds.isEmpty() ? 0 : 1);
556 // }
557 if (!m_paletteBackgroundColorChanged && parentWidget()) {
558 QPalette p = palette();
559 p.setColor(backgroundRole(),
560 dataSource().isEmpty()
561 ? parentWidget()->palette().color(parentWidget()->backgroundRole())
562 : palette().color(QPalette::Active, QPalette::Base)
563 );
564 KexiFrame::setPalette(p);
565 }
566 }
567
sizeHint() const568 QSize KexiDBImageBox::sizeHint() const
569 {
570 if (pixmap().isNull())
571 return QSize(80, 80);
572 return pixmap().size();
573 }
574
realLineWidth() const575 int KexiDBImageBox::realLineWidth() const
576 {
577 switch (frameShape()) {
578 case QFrame::NoFrame:
579 // shadow, line, midline unused
580 return 0;
581 case QFrame::Box:
582 switch (frameShadow()) {
583 case QFrame::Plain:
584 // midline unused
585 return lineWidth();
586 default: // sunken, raised:
587 return 2 * lineWidth() + midLineWidth();
588 }
589 break;
590 case QFrame::Panel:
591 // shadow, midline unused
592 return lineWidth();
593 case QFrame::WinPanel:
594 // shadow, line, midline unused
595 return 2;
596 case QFrame::StyledPanel: {
597 // shadow, line, midline unused
598 QStyleOptionFrame option;
599 option.initFrom(this);
600 return style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &option, this);
601 }
602 default:
603 return lineWidth();
604 }
605 }
606
scaledImageBoxIcon(const QMargins & margins,const QSize & size)607 static QPixmap *scaledImageBoxIcon(const QMargins& margins, const QSize& size)
608 {
609 const int realHeight = size.height() - margins.top() - margins.bottom();
610 const int realWidth = size.width() - margins.left() - margins.right();
611 if ( realHeight <= KexiDBImageBox_static->pixmap->height()
612 || realWidth <= KexiDBImageBox_static->pixmap->width())
613 {
614 if ( realHeight <= KexiDBImageBox_static->small->height()
615 || realWidth <= KexiDBImageBox_static->small->width())
616 {
617 return 0;
618 }
619 return KexiDBImageBox_static->small;
620 }
621 return KexiDBImageBox_static->pixmap;
622 }
623
paintEvent(QPaintEvent * pe)624 void KexiDBImageBox::paintEvent(QPaintEvent *pe)
625 {
626 if (!m_paintEventEnabled)
627 return;
628 QPainter p(this);
629 p.setClipRect(pe->rect());
630 QMargins margins(contentsMargins());
631 margins += realLineWidth();
632 if (designMode() && pixmap().isNull()) {
633 QRect r(
634 QPoint(margins.left(), margins.top()),
635 size() - QSize(margins.left() + margins.right(), margins.top() + margins.bottom()));
636
637 updatePixmap();
638 QPixmap *imagBoxPm = scaledImageBoxIcon(margins, size());
639 if (imagBoxPm) {
640 p.drawPixmap(2, height() - margins.top() - margins.bottom() - imagBoxPm->height() - 2,
641 *imagBoxPm);
642 }
643 QFont f(qApp->font());
644 p.setFont(f);
645 const QFontMetrics fm(fontMetrics());
646 QString text;
647 if (dataSource().isEmpty()) {
648 if ((fm.height() * 2) > height()) {
649 text = xi18nc("Unbound Image Box", "%1 (unbound)", objectName());
650 } else {
651 text = xi18nc("Unbound Image Box", "%1\n(unbound)", objectName());
652 }
653 }
654 else {
655 text = dataSource();
656 const QPixmap dataSourceTagIcon(KexiFormUtils::dataSourceTagIcon());
657 if (width() >= (dataSourceTagIcon.width() + 2 + fm.boundingRect(r, Qt::AlignCenter, text).width())) {
658 r.setLeft( r.left() + dataSourceTagIcon.width() + 2 ); // make some room for the [>] icon
659 QRect bounding = fm.boundingRect(r, Qt::AlignCenter, text);
660 p.drawPixmap(
661 bounding.left() - dataSourceTagIcon.width() - 2,
662 bounding.top() + bounding.height() / 2 - dataSourceTagIcon.height() / 2,
663 dataSourceTagIcon);
664 }
665 }
666 p.drawText(r, Qt::AlignCenter, text);
667 }
668 else {
669 QSize internalSize(size());
670 if (m_chooser && m_dropDownButtonVisible && !dataSource().isEmpty())
671 internalSize.setWidth(internalSize.width() - m_chooser->width());
672
673 const QRect internalRect(QPoint(0, 0), internalSize);
674 if (m_currentScaledPixmap.isNull() || internalRect != m_currentRect) {
675 m_currentRect = internalRect;
676 m_currentPixmapPos = QPoint(0, 0);
677 m_currentScaledPixmap = KexiUtils::scaledPixmap(
678 margins, m_currentRect, pixmap(), &m_currentPixmapPos, m_alignment,
679 m_scaledContents, m_keepAspectRatio,
680 m_smoothTransformation ? Qt::SmoothTransformation : Qt::FastTransformation);
681 }
682 p.drawPixmap(m_currentPixmapPos, m_currentScaledPixmap);
683 }
684 KexiFrame::drawFrame(&p);
685
686 if (designMode()) {
687 const bool hasFrame = frameWidth() >= 1 && frameShape() != QFrame::NoFrame;
688 if (!hasFrame) {
689 KFormDesigner::paintWidgetFrame(p, rect());
690 }
691 }
692 else { // data mode
693 // if the widget is focused, draw focus indicator rect _if_ there is no chooser button
694 if ( !dataSource().isEmpty()
695 && hasFocus()
696 && (!m_chooser || !m_chooser->isVisible()))
697 {
698 QStyleOptionFocusRect option;
699 option.initFrom(this);
700 style()->drawPrimitive(
701 QStyle::PE_FrameFocusRect, &option, &p, this);
702 }
703 }
704 }
705
updatePixmap()706 void KexiDBImageBox::updatePixmap()
707 {
708 if (!(designMode() && pixmap().isNull()))
709 return;
710
711 if (!KexiDBImageBox_static->pixmap) {
712 QPixmap pm( KIconLoader::global()->loadMimeTypeIcon(
713 koIconNameCStr("image-x-generic"), KIconLoader::NoGroup, KIconLoader::SizeLarge, KIconLoader::DisabledState) );
714 if (!pm.isNull()) {
715 KIconEffect::semiTransparent(pm);
716 KIconEffect::semiTransparent(pm);
717 }
718 KexiDBImageBox_static->pixmap = new QPixmap(pm);
719 KexiDBImageBox_static->small = new QPixmap(
720 KexiDBImageBox_static->pixmap->scaled(
721 KexiDBImageBox_static->pixmap->width() / 2, KexiDBImageBox_static->pixmap->height() / 2,
722 Qt::KeepAspectRatio, Qt::SmoothTransformation) );
723 }
724 }
725
setAlignment(Qt::Alignment alignment)726 void KexiDBImageBox::setAlignment(Qt::Alignment alignment)
727 {
728 m_alignment = alignment;
729 m_currentScaledPixmap = QPixmap(); // clear cache
730 repaint();
731 }
732
setData(const KexiBLOBBuffer::Handle & handle)733 void KexiDBImageBox::setData(const KexiBLOBBuffer::Handle& handle)
734 {
735 if (m_insideSetData) //avoid recursion
736 return;
737 m_insideSetData = true;
738 m_data = handle;
739 m_currentScaledPixmap = QPixmap(); // clear cache
740 emit idChanged(handle.id());
741 m_insideSetData = false;
742 update();
743 }
744
resizeEvent(QResizeEvent * e)745 void KexiDBImageBox::resizeEvent(QResizeEvent * e)
746 {
747 KexiFrame::resizeEvent(e);
748 if (m_chooser) {
749 QSize s(m_chooser->sizeHint());
750 const int _realLineWidth = realLineWidth();
751 QSize margin(_realLineWidth, _realLineWidth);
752 s.setHeight(height() - 2*margin.height());
753 s = s.boundedTo(size() - 2 * margin);
754 m_chooser->resize(s);
755 m_chooser->move(QRect(QPoint(0, 0), e->size() - m_chooser->size() - margin + QSize(1, 1)).bottomRight());
756 }
757 }
758
setColumnInfo(KDbConnection * conn,KDbQueryColumnInfo * cinfo)759 void KexiDBImageBox::setColumnInfo(KDbConnection *conn,KDbQueryColumnInfo* cinfo)
760 {
761 KexiFormDataItemInterface::setColumnInfo(conn, cinfo);
762 //updating strings and title is needed
763 updateActionStrings();
764 }
765
keyPressed(QKeyEvent * ke)766 bool KexiDBImageBox::keyPressed(QKeyEvent *ke)
767 {
768 // Esc key should close the popup
769 if (ke->modifiers() == Qt::NoModifier && ke->key() == Qt::Key_Escape) {
770 if (m_contextMenu->isVisible()) {
771 m_setFocusOnButtonAfterClosingPopup = true;
772 return true;
773 }
774 }
775 return false;
776 }
777
setPalette(const QPalette & pal)778 void KexiDBImageBox::setPalette(const QPalette &pal)
779 {
780 KexiFrame::setPalette(pal);
781 if (m_insideSetPalette)
782 return;
783 m_insideSetPalette = true;
784 setPaletteBackgroundColor(pal.color(QPalette::Active, QPalette::Base));
785 QPalette p(palette());
786 p.setColor(foregroundRole(), pal.color(foregroundRole()));
787 setPalette(p);
788 m_insideSetPalette = false;
789 }
790
setPaletteBackgroundColor(const QColor & color)791 void KexiDBImageBox::setPaletteBackgroundColor(const QColor & color)
792 {
793 m_paletteBackgroundColorChanged = true;
794 QPalette pal(palette());
795 pal.setColor(backgroundRole(), color);
796 setPalette(pal);
797 if (m_chooser)
798 m_chooser->setPalette(qApp->palette());
799 }
800
dropDownButtonVisible() const801 bool KexiDBImageBox::dropDownButtonVisible() const
802 {
803 return m_dropDownButtonVisible;
804 }
805
lineWidth() const806 int KexiDBImageBox::lineWidth() const
807 {
808 return KexiFrame::lineWidth();
809 }
810
setDropDownButtonVisible(bool set)811 void KexiDBImageBox::setDropDownButtonVisible(bool set)
812 {
813 //! @todo use global default setting for this property
814 if (m_dropDownButtonVisible == set)
815 return;
816 m_dropDownButtonVisible = set;
817 if (m_chooser) {
818 if (m_dropDownButtonVisible)
819 m_chooser->show();
820 else
821 m_chooser->hide();
822 }
823 }
824
subwidgetStretchRequired(KexiDBAutoField * autoField) const825 bool KexiDBImageBox::subwidgetStretchRequired(KexiDBAutoField* autoField) const
826 {
827 Q_UNUSED(autoField);
828 return true;
829 }
830
eventFilter(QObject * watched,QEvent * e)831 bool KexiDBImageBox::eventFilter(QObject * watched, QEvent * e)
832 {
833 if (watched == this || watched == m_chooser) { //we're watching chooser as well because it's a focus proxy even if invisible
834 if (e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut || e->type() == QEvent::MouseButtonPress) {
835 update(); //to repaint focus rect
836 }
837 }
838 // hide popup menu as soon as it loses focus
839 if (watched == m_contextMenu && e->type() == QEvent::FocusOut) {
840 m_contextMenu->hide();
841 }
842 return KexiFrame::eventFilter(watched, e);
843 }
844
setValueInternal(const QVariant & add,bool removeOld)845 void KexiDBImageBox::setValueInternal(const QVariant& add, bool removeOld)
846 {
847 setValueInternal(add, removeOld, true /*loadPixmap*/);
848 }
849
focusPolicy() const850 Qt::FocusPolicy KexiDBImageBox::focusPolicy() const
851 {
852 if (dataSource().isEmpty())
853 return Qt::NoFocus;
854 return m_focusPolicyInternal;
855 }
856
focusPolicyInternal() const857 Qt::FocusPolicy KexiDBImageBox::focusPolicyInternal() const
858 {
859 return m_focusPolicyInternal;
860 }
861
setFocusPolicy(Qt::FocusPolicy policy)862 void KexiDBImageBox::setFocusPolicy(Qt::FocusPolicy policy)
863 {
864 m_focusPolicyInternal = policy;
865 KexiFrame::setFocusPolicy(focusPolicy()); //set modified policy
866 }
867
setFrameShape(QFrame::Shape s)868 void KexiDBImageBox::setFrameShape(QFrame::Shape s)
869 {
870 KexiFrame::setFrameShape(s);
871 m_currentScaledPixmap = QPixmap(); // clear cache
872 update();
873 }
874
setFrameShadow(QFrame::Shadow s)875 void KexiDBImageBox::setFrameShadow(QFrame::Shadow s)
876 {
877 KexiFrame::setFrameShadow(s);
878 m_currentScaledPixmap = QPixmap(); // clear cache
879 update();
880 }
881
setLineWidth(int w)882 void KexiDBImageBox::setLineWidth(int w)
883 {
884 KexiFrame::setLineWidth(w);
885 m_currentScaledPixmap = QPixmap(); // clear cache
886 update();
887 }
888
setMidLineWidth(int w)889 void KexiDBImageBox::setMidLineWidth(int w)
890 {
891 KexiFrame::setMidLineWidth(w);
892 m_currentScaledPixmap = QPixmap(); // clear cache
893 update();
894 }
895
896