00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "kexidbimagebox.h"
00022
00023 #include <qapplication.h>
00024 #include <qpixmap.h>
00025 #include <qstyle.h>
00026 #include <qtoolbutton.h>
00027 #include <qclipboard.h>
00028 #include <qtooltip.h>
00029 #include <qimage.h>
00030 #include <qbuffer.h>
00031 #include <qfiledialog.h>
00032 #include <qpainter.h>
00033
00034 #include <kdebug.h>
00035 #include <kpopupmenu.h>
00036 #include <klocale.h>
00037 #include <kiconloader.h>
00038 #include <kfiledialog.h>
00039 #include <kimageio.h>
00040 #include <kstandarddirs.h>
00041 #include <kstaticdeleter.h>
00042 #include <kimageeffect.h>
00043
00044 #include <kexiutils/utils.h>
00045 #include <kexidb/field.h>
00046 #include <kexidb/queryschema.h>
00047
00048 #ifdef Q_WS_WIN
00049 #include <win32_utils.h>
00050 #include <krecentdirs.h>
00051 #endif
00052
00053 static KStaticDeleter<QPixmap> KexiDBImageBox_pmDeleter;
00054 static QPixmap* KexiDBImageBox_pm = 0;
00055
00057 class KexiDBImageBox::Button : public QToolButton
00058 {
00059 public:
00060 Button(QWidget *parent) : QToolButton(parent, "KexiDBImageBox::Button")
00061 {
00062 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
00063 setFixedWidth(QMAX(15, qApp->globalStrut().width()));
00064
00065 setAutoRaise(true);
00066 }
00067 ~Button() {}
00068 virtual void drawButton( QPainter *p ) {
00069 QToolButton::drawButton(p);
00070 QStyle::SFlags arrowFlags = QStyle::Style_Default;
00071 if (isDown())
00072 arrowFlags |= QStyle::Style_Down;
00073 if (isEnabled())
00074 arrowFlags |= QStyle::Style_Enabled;
00075 style().drawPrimitive(QStyle::PE_ArrowDown, p,
00076 QRect((width()-7)/2, height()-9, 7, 7), colorGroup(),
00077 arrowFlags, QStyleOption() );
00078 }
00079 };
00080
00082
00083 KexiDBImageBox::KexiDBImageBox( bool designMode, QWidget *parent, const char *name )
00084 : QWidget( parent, name, WNoAutoErase )
00085 , KexiFormDataItemInterface()
00086 , m_actionCollection(this)
00087 , m_alignment(Qt::AlignAuto|Qt::AlignTop)
00088 , m_designMode(designMode)
00089 , m_readOnly(false)
00090 , m_scaledContents(false)
00091 , m_keepAspectRatio(true)
00092 , m_insideSetData(false)
00093 {
00094 setBackgroundMode(Qt::NoBackground);
00095
00096
00097
00098
00099
00100 if (m_designMode) {
00101 m_chooser = 0;
00102 }
00103 else {
00104 m_chooser = new Button(this);
00105
00106 }
00107
00108 m_popup = new KPopupMenu(this);
00109 QString titleString = i18n("Image Box");
00110 m_titleID = m_popup->insertTitle(SmallIcon("pixmaplabel"), titleString);
00111 m_insertFromFileAction = new KAction(i18n("Insert From &File..."), SmallIconSet("fileopen"), 0,
00112 this, SLOT(insertFromFile()), &m_actionCollection, "insert");
00113 m_insertFromFileAction->plug(m_popup);
00114 m_saveAsAction = KStdAction::saveAs(this, SLOT(saveAs()), &m_actionCollection);
00115
00116 m_saveAsAction->plug(m_popup);
00117 m_popup->insertSeparator();
00118 m_cutAction = KStdAction::cut(this, SLOT(cut()), &m_actionCollection);
00119 m_cutAction->plug(m_popup);
00120 m_copyAction = KStdAction::copy(this, SLOT(copy()), &m_actionCollection);
00121 m_copyAction->plug(m_popup);
00122 m_pasteAction = KStdAction::paste(this, SLOT(paste()), &m_actionCollection);
00123 m_pasteAction->plug(m_popup);
00124 m_deleteAction = new KAction(i18n("&Clear"), SmallIconSet("editdelete"), 0,
00125 this, SLOT(clear()), &m_actionCollection, "delete");
00126 m_deleteAction->plug(m_popup);
00127 #ifdef KEXI_NO_UNFINISHED
00128 m_propertiesAction = 0;
00129 #else
00130 m_popup->insertSeparator();
00131 m_propertiesAction = new KAction(i18n("Properties"), 0, 0,
00132 this, SLOT(showProperties()), &m_actionCollection, "properties");
00133 m_propertiesAction->plug(m_popup);
00134 #endif
00135 connect(m_popup, SIGNAL(aboutToShow()), this, SLOT(updateActionsAvailability()));
00136 connect(m_popup, SIGNAL(aboutToHide()), this, SLOT(slotAboutToHidePopupMenu()));
00137 if (m_chooser) {
00138
00139 connect(m_chooser, SIGNAL(pressed()), this, SLOT(slotChooserPressed()));
00140 }
00141
00142 setDataSource( QString::null );
00143
00144
00145
00146 }
00147
00148 KexiDBImageBox::~KexiDBImageBox()
00149 {
00150 }
00151
00152 QVariant KexiDBImageBox::value()
00153 {
00154 if (dataSource().isEmpty()) {
00155
00156 return QVariant();
00157 }
00158
00159 return QVariant();
00160 }
00161
00162 void KexiDBImageBox::setValueInternal( const QVariant& add, bool )
00163 {
00164 if (isReadOnly())
00165 return;
00166 m_value = add.toByteArray();
00167
00168 repaint();
00169 emit valueChanged(m_value);
00170 }
00171
00172 void KexiDBImageBox::setInvalidState( const QString& displayText )
00173 {
00174 Q_UNUSED( displayText );
00175
00176
00177 if (!dataSource().isEmpty()) {
00178 m_value = QByteArray();
00179 }
00180
00181
00182
00184
00185 if (m_chooser)
00186 m_chooser->hide();
00187 setReadOnly(true);
00188 }
00189
00190 bool KexiDBImageBox::valueIsNull()
00191 {
00192 return m_value.isEmpty();
00193
00194 }
00195
00196 bool KexiDBImageBox::valueIsEmpty()
00197 {
00198 return false;
00199 }
00200
00201 bool KexiDBImageBox::isReadOnly() const
00202 {
00203 return m_readOnly;
00204 }
00205
00206 void KexiDBImageBox::setReadOnly(bool set)
00207 {
00208 m_readOnly = set;
00209 }
00210
00211 QPixmap KexiDBImageBox::pixmap() const
00212 {
00213 if (dataSource().isEmpty()) {
00214
00215 return m_data.pixmap();
00216 }
00217
00218 return m_pixmap;
00219 }
00220
00221 uint KexiDBImageBox::pixmapId() const
00222 {
00223 if (dataSource().isEmpty()) {
00224
00225 return m_data.id();
00226 }
00227 return 0;
00228 }
00229
00230 void KexiDBImageBox::setPixmapId(uint id)
00231 {
00232 if (m_insideSetData)
00233 return;
00234 setData(KexiBLOBBuffer::self()->objectForId( id, false ));
00235 repaint();
00236 }
00237
00238 uint KexiDBImageBox::storedPixmapId() const
00239 {
00240 if (dataSource().isEmpty() && m_data.stored()) {
00241
00242 return m_data.id();
00243 }
00244 return 0;
00245 }
00246
00247 void KexiDBImageBox::setStoredPixmapId(uint id)
00248 {
00249 setData(KexiBLOBBuffer::self()->objectForId( id, true ));
00250 repaint();
00251 }
00252
00253 bool KexiDBImageBox::hasScaledContents() const
00254 {
00255 return m_scaledContents;
00256
00257 }
00258
00259
00260
00261
00262
00263
00264
00265 void KexiDBImageBox::setScaledContents(bool set)
00266 {
00267
00268 m_scaledContents = set;
00269 repaint();
00270 }
00271
00272 void KexiDBImageBox::setKeepAspectRatio(bool set)
00273 {
00274 m_keepAspectRatio = set;
00275 if (m_scaledContents)
00276 repaint();
00277 }
00278
00279 QWidget* KexiDBImageBox::widget()
00280 {
00282
00283 return this;
00284 }
00285
00286 bool KexiDBImageBox::cursorAtStart()
00287 {
00288 return true;
00289 }
00290
00291 bool KexiDBImageBox::cursorAtEnd()
00292 {
00293 return true;
00294 }
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307 void KexiDBImageBox::insertFromFile()
00308 {
00309 if (!dataSource().isEmpty() && isReadOnly())
00310 return;
00311
00312 #ifdef Q_WS_WIN
00313 QString recentDir;
00314 QString fileName = QFileDialog::getOpenFileName(
00315 KFileDialog::getStartURL(":LastVisitedImagePath", recentDir).path(),
00316 convertKFileDialogFilterToQFileDialogFilter(KImageIO::pattern(KImageIO::Reading)),
00317 this, 0, i18n("Insert Image From File"));
00318 KURL url;
00319 url.setPath( fileName );
00320 #else
00321 KURL url( KFileDialog::getImageOpenURL(
00322 ":LastVisitedImagePath", this, i18n("Insert Image From File")) );
00323
00324
00326 #endif
00327 if (!url.isValid())
00328 return;
00329 kexipluginsdbg << "fname=" << url.prettyURL() << endl;
00330
00331 if (dataSource().isEmpty()) {
00332
00333 KexiBLOBBuffer::Handle h = KexiBLOBBuffer::self()->insertPixmap( url );
00334 if (!h)
00335 return;
00336 setData(h);
00337 }
00338 else {
00339
00340 #ifndef Q_WS_WIN
00341 QString fileName = url.isLocalFile() ? url.path() : url.prettyURL();
00342 #endif
00343
00344 QFile f(fileName);
00345 if (!f.open(IO_ReadOnly)) {
00347 return;
00348 }
00349 m_value = f.readAll();
00350 if (f.status()!=IO_Ok) {
00352 f.close();
00353 return;
00354 }
00355 }
00356 repaint();
00357
00359
00360 #ifdef Q_WS_WIN
00361
00362
00363 if (url.isLocalFile())
00364 KRecentDirs::add(":LastVisitedImagePath", url.directory());
00365 #endif
00366 }
00367
00368 QByteArray KexiDBImageBox::data() const
00369 {
00370 if (dataSource().isEmpty()) {
00371
00372 return m_data.data();
00373 }
00374 else {
00375
00376 return m_value;
00377 }
00378 }
00379
00380 void KexiDBImageBox::saveAs()
00381 {
00382
00383 if (data().isEmpty()) {
00384 kdWarning() << "KexiDBImageBox::saveAs(): no pixmap!" << endl;
00385 return;
00386 }
00387 #ifdef Q_WS_WIN
00388 QString recentDir;
00389
00390 QString fileName = QFileDialog::getSaveFileName(
00391 KFileDialog::getStartURL(":LastVisitedImagePath", recentDir).path()
00392 +"/"+m_data.originalFileName(),
00393 convertKFileDialogFilterToQFileDialogFilter(KImageIO::pattern(KImageIO::Writing)),
00394 this, 0, i18n("Save Image to File"));
00395 #else
00396
00397 QString fileName = KFileDialog::getSaveFileName(
00398 ":LastVisitedImagePath", KImageIO::pattern(KImageIO::Writing), this, i18n("Save Image to File"));
00399 #endif
00400 if (fileName.isEmpty())
00401 return;
00402 kexipluginsdbg << fileName << endl;
00403 KURL url;
00404 url.setPath( fileName );
00405
00406 QFile f(fileName);
00407 if (!f.open(IO_WriteOnly)) {
00409 return;
00410 }
00411 f.writeBlock( data() );
00412 if (f.status()!=IO_Ok) {
00414 f.close();
00415 return;
00416 }
00417 f.close();
00418
00419
00420
00421
00422
00423
00424 #ifdef Q_WS_WIN
00425
00426
00427 if (url.isLocalFile())
00428 KRecentDirs::add(":LastVisitedImagePath", url.directory());
00429 #endif
00430 }
00431
00432 void KexiDBImageBox::cut()
00433 {
00434 if (!dataSource().isEmpty() && isReadOnly())
00435 return;
00436 copy();
00437 clear();
00438 }
00439
00440 void KexiDBImageBox::copy()
00441 {
00442
00443 qApp->clipboard()->setPixmap(pixmap(), QClipboard::Clipboard);
00444 }
00445
00446 void KexiDBImageBox::paste()
00447 {
00448 if (isReadOnly())
00449 return;
00450 QPixmap pm( qApp->clipboard()->pixmap(QClipboard::Clipboard) );
00451
00452
00453 if (dataSource().isEmpty()) {
00454
00455 setData(KexiBLOBBuffer::self()->insertPixmap( pm ));
00456 }
00457 else {
00458
00459 m_pixmap = pm;
00460
00461 }
00462
00463 repaint();
00464 emit valueChanged(data());
00465 }
00466
00467 void KexiDBImageBox::clear()
00468 {
00469 if (dataSource().isEmpty()) {
00470
00471 setData(KexiBLOBBuffer::Handle());
00472 }
00473 else {
00474 if (isReadOnly())
00475 return;
00476
00477 m_pixmap = QPixmap();
00478 m_value = QByteArray();
00479 }
00480
00481
00482
00483
00485
00486
00487 repaint();
00488 emit valueChanged(data());
00489 }
00490
00491 void KexiDBImageBox::showProperties()
00492 {
00494 }
00495
00496 void KexiDBImageBox::updateActionsAvailability()
00497 {
00498 const bool notNull
00499 = (dataSource().isEmpty() && !pixmap().isNull())
00500 || (!dataSource().isEmpty() && !valueIsNull());
00501 const bool readOnly = !dataSource().isEmpty() && !isReadOnly();
00502
00503 m_insertFromFileAction->setEnabled( !readOnly );
00504 m_saveAsAction->setEnabled( notNull );
00505 m_cutAction->setEnabled( notNull && !readOnly );
00506 m_copyAction->setEnabled( notNull );
00507 m_pasteAction->setEnabled( !readOnly );
00508 m_deleteAction->setEnabled( notNull && !readOnly );
00509 if (m_propertiesAction)
00510 m_propertiesAction->setEnabled( notNull );
00511 }
00512
00513 void KexiDBImageBox::slotAboutToHidePopupMenu()
00514 {
00515 m_clickTimer.start(50, true);
00516 }
00517
00518 void KexiDBImageBox::contextMenuEvent( QContextMenuEvent * e )
00519 {
00520 if (popupMenuAvailable())
00521 m_popup->exec( e->globalPos(), -1 );
00522 }
00523
00524 void KexiDBImageBox::slotChooserPressed()
00525 {
00526 if (m_clickTimer.isActive())
00527 return;
00528 QRect screen = qApp->desktop()->availableGeometry( m_chooser );
00529 QPoint p;
00530 if ( QApplication::reverseLayout() ) {
00531 if ( mapToGlobal( m_chooser->rect().bottomLeft() ).y() + m_popup->sizeHint().height() <= screen.height() )
00532 p = m_chooser->mapToGlobal( m_chooser->rect().bottomRight() );
00533 else
00534 p = m_chooser->mapToGlobal( m_chooser->rect().topRight() - QPoint( 0, m_popup->sizeHint().height() ) );
00535 p.rx() -= m_popup->sizeHint().width();
00536 }
00537 else {
00538 if ( m_chooser->mapToGlobal( m_chooser->rect().bottomLeft() ).y() + m_popup->sizeHint().height() <= screen.height() )
00539 p = m_chooser->mapToGlobal( m_chooser->rect().bottomLeft() );
00540 else
00541 p = m_chooser->mapToGlobal( m_chooser->rect().topLeft() - QPoint( 0, m_popup->sizeHint().height() ) );
00542 }
00543 if (!m_popup->isVisible()) {
00544 m_popup->exec( p, -1 );
00545 }
00546 m_chooser->setDown( false );
00547 }
00548
00549 void KexiDBImageBox::updateActionStrings()
00550 {
00551 if (!m_popup)
00552 return;
00553 QString titleString = i18n("Image Box");
00554 if (!dataSource().isEmpty())
00555 titleString += (": " + dataSource());
00556 m_popup->changeTitle(m_titleID, m_popup->titlePixmap(m_titleID), titleString);
00557
00558 if (m_chooser) {
00559 if (popupMenuAvailable() && dataSource().isEmpty())
00560 QToolTip::add(m_chooser, i18n("Click to show actions for this image box"));
00561 else
00562 QToolTip::add(m_chooser, i18n("Click to show actions for \"%1\" image box").arg(dataSource()));
00563 }
00564 }
00565
00566 bool KexiDBImageBox::popupMenuAvailable()
00567 {
00570
00571 return !dataSource().isEmpty();
00572 }
00573
00574 void KexiDBImageBox::setDataSource( const QString &ds )
00575 {
00576 KexiFormDataItemInterface::setDataSource( ds );
00577 setData(KexiBLOBBuffer::Handle());
00578 updateActionStrings();
00579
00580 if (m_chooser) {
00581 if (popupMenuAvailable()) {
00582 m_chooser->show();
00583 }
00584 else {
00585 m_chooser->hide();
00586 }
00587 }
00588 }
00589
00590 QSize KexiDBImageBox::sizeHint() const
00591 {
00592 if (pixmap().isNull())
00593 return QSize(80, 80);
00594 return pixmap().size();
00595 }
00596
00597
00598 void KexiDBImageBox::paintEvent( QPaintEvent*pe )
00599 {
00600 QPainter p(this);
00601 p.setClipRect(pe->rect());
00602 const int m = 0;
00603
00604
00605
00606
00607
00608
00609 QColor bg(eraseColor());
00610 if (m_designMode && pixmap().isNull()) {
00611 QPixmap pm(size());
00612 QPainter p2;
00613 p2.begin(&pm, this);
00614
00615 p2.fillRect(0,0,width(),height(), bg);
00616
00617 updatePixmap();
00618 QImage img(KexiDBImageBox_pm->convertToImage());
00619 img = KImageEffect::flatten(img, bg.dark(150),
00620 qGray( bg.rgb() ) <= 20 ? Qt::darkGray : bg.light(105));
00621
00622
00623
00624
00625
00626
00627
00628 QPixmap converted;
00629 converted.convertFromImage(img);
00630 p2.drawPixmap(m+2, height()-m-KexiDBImageBox_pm->height()-2, converted);
00631 QFont f(qApp->font());
00632
00633 p2.setFont(f);
00634 p2.setPen( KexiUtils::contrastColor( bg ) );
00635 p2.drawText(pm.rect(), Qt::AlignCenter|Qt::WordBreak, i18n("No Image"));
00636 p2.end();
00637 bitBlt(this, 0, 0, &pm);
00638 }
00639 else {
00640
00641 if (pixmap().isNull())
00642 p.fillRect(0,0,width(),height(), bg);
00643 else {
00644 const bool fast = pixmap().width()>1000 && pixmap().height()>800;
00647 QPixmap pm;
00648 QPainter p2;
00649 QPainter *target;
00650 if (fast) {
00651 target = &p;
00652 }
00653 else {
00654 pm.resize(size());
00655 p2.begin(&pm, this);
00656 target = &p2;
00657 }
00658
00659 target->fillRect(0,0,width(),height(), bg);
00660 if (m_scaledContents) {
00661 if (m_keepAspectRatio) {
00662 QImage img(pixmap().convertToImage());
00663 img = img.smoothScale(width(), height(), QImage::ScaleMin);
00664 QPoint pos(0,0);
00665 if (img.width()<width()) {
00666 int hAlign = QApplication::horizontalAlignment( m_alignment );
00667 if ( hAlign & Qt::AlignRight )
00668 pos.setX(width()-img.width());
00669 else if ( hAlign & Qt::AlignHCenter )
00670 pos.setX(width()/2-img.width()/2);
00671 }
00672 else if (img.height()<height()) {
00673 if ( m_alignment & Qt::AlignBottom )
00674 pos.setY(height()-img.height());
00675 else if ( m_alignment & Qt::AlignVCenter )
00676 pos.setY(height()/2-img.height()/2);
00677 }
00678 QPixmap px;
00679 px.convertFromImage(img);
00680 target->drawPixmap(pos, px);
00681 }
00682 else {
00683 target->drawPixmap(QRect(m, m, width()-m*2, height()-m*2), pixmap());
00684 }
00685 }
00686 else {
00687 int hAlign = QApplication::horizontalAlignment( m_alignment );
00688 QPoint pos;
00689 if ( hAlign & Qt::AlignRight )
00690 pos.setX(width()-pixmap().width()-m);
00691 else if ( hAlign & Qt::AlignHCenter )
00692 pos.setX(width()/2-pixmap().width()/2);
00693 else
00694 pos.setX(m);
00695
00696 if ( m_alignment & Qt::AlignBottom )
00697 pos.setY(height()-pixmap().height()-m);
00698 else if ( m_alignment & Qt::AlignVCenter )
00699 pos.setY(height()/2-pixmap().height()/2);
00700 else
00701 pos.setY(m);
00702 target->drawPixmap(pos, pixmap());
00703 }
00704 if (!fast) {
00705 p2.end();
00706 bitBlt(this, 0, 0, &pm);
00707 }
00708 }
00709 }
00710 }
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720 void KexiDBImageBox::updatePixmap() {
00721 if (! (m_designMode && pixmap().isNull()) )
00722 return;
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733 if (!KexiDBImageBox_pm) {
00734 QString fname( locate("data", QString("kexi/pics/imagebox.png")) );
00735 KexiDBImageBox_pmDeleter.setObject( KexiDBImageBox_pm, new QPixmap(fname, "PNG") );
00736 }
00737 }
00738
00739 void KexiDBImageBox::setAlignment(int alignment)
00740 {
00741 m_alignment = alignment;
00742 if (!m_scaledContents || m_keepAspectRatio)
00743 repaint();
00744 }
00745
00746 void KexiDBImageBox::setData(const KexiBLOBBuffer::Handle& handle)
00747 {
00748 if (m_insideSetData)
00749 return;
00750 m_insideSetData = true;
00751 m_data = handle;
00752 emit idChanged(handle.id());
00753 m_insideSetData = false;
00754 }
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788 #include "kexidbimagebox.moc"