kexi

kexidblabel.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2005 Christian Nitschkowski <segfault_ii@web.de>
00003    Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
00004 
00005    This program is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this program; see the file COPYING.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "kexidblabel.h"
00022 
00023 #include <qbitmap.h>
00024 #include <qpainter.h>
00025 #include <qdrawutil.h>
00026 #include <qapplication.h>
00027 #include <qtimer.h>
00028 
00029 #include <kdebug.h>
00030 #include <kimageeffect.h>
00031 
00032 #include <kexidb/field.h>
00033 #include <kexiutils/utils.h>
00034 
00035 #define SHADOW_OFFSET_X 3
00036 #define SHADOW_OFFSET_Y 3
00037 #define SHADOW_FACTOR 16.0
00038 #define SHADOW_OPACITY 50.0
00039 #define SHADOW_AXIS_FACTOR 2.0
00040 #define SHADOW_DIAGONAL_FACTOR 1.0
00041 #define SHADOW_THICKNESS 1
00042 
00044 class KexiDBInternalLabel : public QLabel {
00045         friend class KexiDBLabel;
00046     public:
00047         KexiDBInternalLabel( KexiDBLabel* );
00048         virtual ~KexiDBInternalLabel();
00049 
00050     protected:
00051         void updateFrame();
00052 
00053         QImage makeShadow( const QImage& textImage, const QColor &bgColor, const QRect& boundingRect );
00054         QRect getBounding( const QImage &image, const QRect& startRect );
00055 //      double defaultDecay( QImage& source, int i, int j );
00056         KPixmap getShadowPixmap();
00057 
00058         QRect m_shadowRect;
00059         KexiDBLabel *m_parentLabel;
00060 };
00061 
00062 KexiDBInternalLabel::KexiDBInternalLabel( KexiDBLabel* parent )
00063     : QLabel( parent )
00064     , m_parentLabel(parent)
00065 {
00066     int a = alignment() | Qt::WordBreak;
00067     a &= (0xffffff ^ Qt::AlignVertical_Mask);
00068     a |= Qt::AlignTop;
00069     setAlignment( a );
00070     updateFrame();
00071 }
00072 
00073 void KexiDBInternalLabel::updateFrame()
00074 {
00075     setIndent(m_parentLabel->indent());
00076     setMargin(m_parentLabel->margin());
00077     setFont(m_parentLabel->font());
00078 
00079     setFrameShadow(m_parentLabel->frameShadow());
00080     setFrameShape(m_parentLabel->frameShape());
00081     setFrameStyle(m_parentLabel->frameStyle());
00082     setMidLineWidth(m_parentLabel->midLineWidth());
00083     setLineWidth(m_parentLabel->lineWidth());
00084 }
00085 
00086 KexiDBInternalLabel::~KexiDBInternalLabel()
00087 {
00088 }
00089 
00096 QImage KexiDBInternalLabel::makeShadow( const QImage& textImage, 
00097     const QColor &bgColor, const QRect& boundingRect )
00098 {
00099     QImage result;
00100     QString origText( text() );
00101 
00102     // create a new image for for the shaddow
00103     const int w = textImage.width();
00104     const int h = textImage.height();
00105 
00106     // avoid calling these methods for every pixel
00107     const int bgRed = bgColor.red();
00108     const int bgGreen = bgColor.green();
00109     const int bgBlue = bgColor.blue();
00110 
00111     const int startX = boundingRect.x() + SHADOW_THICKNESS;
00112     const int startY = boundingRect.y() + SHADOW_THICKNESS;
00113     const int effectWidth = boundingRect.bottomRight().x() - SHADOW_THICKNESS;
00114     const int effectHeight = boundingRect.bottomRight().y() - SHADOW_THICKNESS;
00115 //  const int period = (effectWidth - startX) / 10;
00116 
00117     double alphaShadow;
00118 
00119     /*
00120      *  This is the source pixmap
00121      */
00122     QImage img = textImage.convertDepth( 32 );
00123 
00124     /*
00125      *  Resize the image if necessary
00126      */
00127     if ( ( result.width() != w ) || ( result.height() != h ) ) {
00128         result.create( w, h, 32 );
00129     }
00130 
00131 //  result.fill( 0 ); // all black
00132     double realOpacity = SHADOW_OPACITY + QMIN(50.0/double(256.0-qGray(bgColor.rgb())), 50.0);
00133     //int _h, _s, _v;
00134     //.getHsv( &_h, &_s, &_v );
00135     if (colorGroup().background()==Qt::red)//_s>=250 && _v>=250) //for colors like cyan or red, make the result more white
00136         realOpacity += 50.0;
00137     result.fill( (int)realOpacity );
00138     result.setAlphaBuffer( true );
00139 
00140     for ( int i = startX; i < effectWidth; i++ ) {
00141         for ( int j = startY; j < effectHeight; j++ ) {
00148                 if ( ( i < 1 ) || ( j < 1 ) || ( i > img.width() - 2 ) || ( j > img.height() - 2 ) )
00149                     continue;
00150                 else
00151                     alphaShadow = ( qGray( img.pixel( i - 1, j - 1 ) ) * SHADOW_DIAGONAL_FACTOR +
00152                         qGray( img.pixel( i - 1, j ) ) * SHADOW_AXIS_FACTOR +
00153                         qGray( img.pixel( i - 1, j + 1 ) ) * SHADOW_DIAGONAL_FACTOR +
00154                         qGray( img.pixel( i , j - 1 ) ) * SHADOW_AXIS_FACTOR +
00155                         0 +
00156                         qGray( img.pixel( i , j + 1 ) ) * SHADOW_AXIS_FACTOR +
00157                         qGray( img.pixel( i + 1, j - 1 ) ) * SHADOW_DIAGONAL_FACTOR +
00158                         qGray( img.pixel( i + 1, j ) ) * SHADOW_AXIS_FACTOR +
00159                         qGray( img.pixel( i + 1, j + 1 ) ) * SHADOW_DIAGONAL_FACTOR ) / SHADOW_FACTOR;
00160 
00161             // update the shadow's i,j pixel.
00162             if (alphaShadow > 0)
00163                 result.setPixel( i, j, qRgba( bgRed, bgGreen , bgBlue, 
00164                     ( int ) (( alphaShadow > realOpacity ) ? realOpacity : alphaShadow)
00165                 ) );
00166         }
00167 /*caused too much redraw problems       if (period && i % period) {
00168             qApp->processEvents();
00169             if (text() != origText) //text has been changed in the meantime: abort
00170                 return QImage();
00171         }*/
00172     }
00173     return result;
00174 }
00175 
00176 KPixmap KexiDBInternalLabel::getShadowPixmap() {
00180     const QColor textColor = colorGroup().foreground();
00181 
00185     KPixmap finalPixmap, tempPixmap;
00186     QImage shadowImage, tempImage;
00187     QPainter painter;
00188 
00189     m_shadowRect = QRect();
00190 
00191     tempPixmap.resize( size() );
00192     tempPixmap.fill( Qt::black );
00193     tempPixmap.setMask( tempPixmap.createHeuristicMask( true ) );
00194 
00198     setPaletteForegroundColor( Qt::white );
00199 
00203     painter.begin( &tempPixmap );
00204     painter.setFont( font() );
00205     drawContents( &painter );
00206     painter.end();
00207     setPaletteForegroundColor( textColor );
00208 
00213     shadowImage = tempPixmap;
00214     tempPixmap.setMask( QBitmap() );
00215 
00220     m_shadowRect = getBounding( shadowImage, m_shadowRect );
00221 
00228     m_shadowRect.setX( QMAX( m_shadowRect.x() - ( m_shadowRect.width() / 4 ), 0 ) );
00229     m_shadowRect.setY( QMAX( m_shadowRect.y() - ( m_shadowRect.height() / 4 ), 0 ) );
00230     m_shadowRect.setBottomRight( QPoint(
00231         QMIN( m_shadowRect.x() + ( m_shadowRect.width() * 3 / 2 ), shadowImage.width() ),
00232         QMIN( m_shadowRect.y() + ( m_shadowRect.height() * 3 / 2 ), shadowImage.height() ) ) );
00233 
00234     shadowImage = makeShadow( shadowImage,
00235         qGray( colorGroup().background().rgb() ) < 127 ? Qt::white : Qt::black,
00236         m_shadowRect );
00237     if (shadowImage.isNull())
00238         return KPixmap();
00239 
00243     m_shadowRect = getBounding( shadowImage, m_shadowRect );
00244 
00248     finalPixmap.resize( size() );
00249     painter.begin( &finalPixmap );
00250     painter.fillRect( 0, 0, finalPixmap.width(), finalPixmap.height(),
00251         palette().brush(
00252         isEnabled() ? QPalette::Active : QPalette::Disabled,
00253         QColorGroup::Background ) );
00254     painter.end();
00255 
00260     tempPixmap.resize( m_shadowRect.size() );
00261     if (!finalPixmap.isNull()) {
00262         bitBlt( &tempPixmap, 0, 0, &finalPixmap,
00263             m_shadowRect.x() + SHADOW_OFFSET_X,
00264             m_shadowRect.y() + SHADOW_OFFSET_Y,
00265             m_shadowRect.width(),
00266             m_shadowRect.height() );
00267     }
00272     finalPixmap = tempPixmap;
00273 
00281     tempImage = shadowImage.copy( m_shadowRect );
00282     tempPixmap.convertFromImage( tempImage );
00286     if (!tempPixmap.isNull()) {
00287         bitBlt( &finalPixmap, 0, 0, &tempPixmap );
00288     }
00289 
00294     m_shadowRect.moveBy( SHADOW_OFFSET_X, SHADOW_OFFSET_Y );
00295 
00296     return finalPixmap;
00297 }
00298 
00299 QRect KexiDBInternalLabel::getBounding( const QImage &image, const QRect& startRect ) {
00300     QPoint topLeft;
00301     QPoint bottomRight;
00302 
00303     const int startX = startRect.x();
00304     const int startY = startRect.y();
00308     const int width = QMIN( ( startRect.bottomRight().x() > 0
00309         ? startRect.bottomRight().x() : QCOORD_MAX ),
00310         image.width() );
00311     const int height = QMIN( ( startRect.bottomRight().y() > 0
00312         ? startRect.bottomRight().y() : QCOORD_MAX ),
00313         image.height() );
00314 
00322     QRgb trans = image.pixel( 0, 0 );
00323 
00324     for ( int y = startY; y < height; y++ ) {
00325         for ( int x = startX; x < width; x++ ) {
00326             if ( image.pixel( x, y ) != trans ) {
00327                 topLeft.setY( y );
00328                 y = height;
00329                 break;
00330             }
00331         }
00332     }
00333 
00334     for ( int x = startX; x < width; x++ ) {
00335         for ( int y = startY; y < height; y++ ) {
00336             if ( image.pixel( x, y ) != trans ) {
00337                 topLeft.setX( x );
00338                 x = width;
00339                 break;
00340             }
00341         }
00342     }
00343 
00344     for ( int y = height - 1; y > topLeft.y(); y-- ) {
00345         for ( int x = width - 1; x > topLeft.x(); x-- ) {
00346             if ( image.pixel( x, y ) != trans ) {
00347                 bottomRight.setY( y + 1 );
00348                 y = 0;
00349                 break;
00350             }
00351         }
00352     }
00353 
00354     for ( int x = width - 1; x > topLeft.x(); x-- ) {
00355         for ( int y = height - 1; y > topLeft.y(); y-- ) {
00356             if ( image.pixel( x, y ) != trans ) {
00357                 bottomRight.setX( x + 1 );
00358                 x = 0;
00359                 break;
00360             }
00361         }
00362     }
00363 
00364     return QRect(
00365         topLeft.x(),
00366         topLeft.y(),
00367         bottomRight.x() - topLeft.x(),
00368         bottomRight.y() - topLeft.y() );
00369 }
00370 
00371 //=========================================================
00372 
00374 class KexiDBLabel::Private
00375 {
00376     public:
00377         Private()
00378         : timer(0)
00379 //      , autonumberDisplayParameters(0)
00380         , pixmapDirty( true )
00381         , shadowEnabled( false )
00382         , resizeEvent( false )
00383         {
00384         }
00385         ~Private() {}
00386         KPixmap shadowPixmap;
00387         QPoint shadowPosition;
00388         KexiDBInternalLabel* internalLabel;
00389         QTimer* timer;
00390         QColor frameColor;
00391         bool pixmapDirty : 1;
00392         bool shadowEnabled : 1;
00393         bool resizeEvent : 1;
00394 };
00395 
00396 //=========================================================
00397 
00398 KexiDBLabel::KexiDBLabel( QWidget *parent, const char *name, WFlags f )
00399     : QLabel( parent, name, f )
00400     , KexiDBTextWidgetInterface()
00401     , KexiFormDataItemInterface()
00402     , d( new Private() )
00403 {
00404     init();
00405 }
00406 
00407 KexiDBLabel::KexiDBLabel( const QString& text, QWidget *parent, const char *name, WFlags f )
00408     : QLabel( parent, name, f )
00409     , KexiDBTextWidgetInterface()
00410     , KexiFormDataItemInterface()
00411     , d( new Private() )
00412 {
00413     init();
00414     setText( text );
00415 }
00416 
00417 KexiDBLabel::~KexiDBLabel()
00418 {
00419     delete d;
00420 }
00421 
00422 void KexiDBLabel::init()
00423 {
00424     m_hasFocusableWidget = false;
00425     d->internalLabel = new KexiDBInternalLabel( this );
00426     d->internalLabel->hide();
00427     d->frameColor = palette().active().foreground();
00428 
00429     setAlignment( d->internalLabel->alignment() );
00430 }
00431 
00432 void KexiDBLabel::updatePixmapLater() {
00433     if (d->resizeEvent) {
00434         if (!d->timer) {
00435             d->timer = new QTimer(this, "KexiDBLabelTimer");
00436             connect(d->timer, SIGNAL(timeout()), this, SLOT(updatePixmap()));
00437         }
00438         d->timer->start(100, true);
00439         d->resizeEvent = false;
00440         return;
00441     }
00442     if (d->timer && d->timer->isActive())
00443         return;
00444     updatePixmap();
00445 }
00446 
00447 void KexiDBLabel::updatePixmap() {
00453     d->internalLabel->setText( text() );
00454     d->internalLabel->setFixedSize( size() );
00455     d->internalLabel->setPalette( palette() );
00456     d->internalLabel->setAlignment( alignment() );
00457 //  d->shadowPixmap = KPixmap(); //parallel repaints won't hurt us cause incomplete pixmap
00458     KPixmap shadowPixmap = d->internalLabel->getShadowPixmap();
00459     if (shadowPixmap.isNull())
00460         return;
00461     d->shadowPixmap = shadowPixmap;
00462     d->shadowPosition = d->internalLabel->m_shadowRect.topLeft();
00463     d->pixmapDirty = false;
00464     repaint();
00465 }
00466 
00467 void KexiDBLabel::paintEvent( QPaintEvent* e ) {
00468     if ( d->shadowEnabled ) {
00472         if ( d->pixmapDirty ) {
00473             updatePixmapLater();
00474         }
00475 
00483         if ( !d->pixmapDirty && e->rect().contains( d->shadowPosition ) && !d->shadowPixmap.isNull()) {
00484             QPainter p( this );
00485             QRect clipRect = QRect(
00486                 QMAX( e->rect().x() - d->shadowPosition.x(), 0 ),
00487                 QMAX( e->rect().y() - d->shadowPosition.y(), 0 ),
00488                 QMIN( e->rect().width() + d->shadowPosition.x(), d->shadowPixmap.width() ),
00489                 QMIN( e->rect().height() + d->shadowPosition.y(), d->shadowPixmap.height() ) );
00490             p.drawPixmap( d->internalLabel->m_shadowRect.topLeft(), d->shadowPixmap, clipRect );
00491         }
00492     }
00493 
00494     KexiDBTextWidgetInterface::paintEvent( this, text().isEmpty(), alignment(), false );
00495     QLabel::paintEvent( e );
00496 }
00497 
00498 void KexiDBLabel::setValueInternal( const QVariant& add, bool removeOld ) {
00499     if (removeOld) 
00500         setText(add.toString());
00501     else
00502         setText( m_origValue.toString() + add.toString() );
00503 }
00504 
00505 QVariant KexiDBLabel::value() {
00506     return text();
00507 }
00508 
00509 void KexiDBLabel::setInvalidState( const QString& displayText )
00510 {
00511     setText( displayText );
00512 }
00513 
00514 bool KexiDBLabel::valueIsNull()
00515 {
00516     return text().isNull();
00517 }
00518 
00519 bool KexiDBLabel::valueIsEmpty()
00520 {
00521     return text().isEmpty();
00522 }
00523 
00524 bool KexiDBLabel::isReadOnly() const
00525 {
00526     return true;
00527 }
00528 
00529 QWidget* KexiDBLabel::widget()
00530 {
00531     return this;
00532 }
00533 
00534 bool KexiDBLabel::cursorAtStart()
00535 {
00536     return false;
00537 }
00538 
00539 bool KexiDBLabel::cursorAtEnd()
00540 {
00541     return false;
00542 }
00543 
00544 void KexiDBLabel::clear()
00545 {
00546     setText(QString::null);
00547 }
00548 
00549 bool KexiDBLabel::setProperty( const char * name, const QVariant & value )
00550 {
00551     const bool ret = QLabel::setProperty(name, value);
00552     if (d->shadowEnabled) {
00553         if (0==qstrcmp("indent", name) || 0==qstrcmp("font", name) || 0==qstrcmp("margin", name)
00554             || 0==qstrcmp("frameShadow", name) || 0==qstrcmp("frameShape", name)
00555             || 0==qstrcmp("frameStyle", name) || 0==qstrcmp("midLineWidth", name)
00556             || 0==qstrcmp("lineWidth", name)) {
00557             d->internalLabel->setProperty(name, value);
00558             updatePixmap();
00559         }
00560     }
00561     return ret;
00562 }
00563 
00564 void KexiDBLabel::setColumnInfo(KexiDB::QueryColumnInfo* cinfo)
00565 {
00566     KexiFormDataItemInterface::setColumnInfo(cinfo);
00567     KexiDBTextWidgetInterface::setColumnInfo(cinfo, this);
00568 }
00569 
00570 void KexiDBLabel::setShadowEnabled( bool state ) {
00571     d->shadowEnabled = state;
00572     d->pixmapDirty = true;
00573     if (state)
00574         d->internalLabel->updateFrame();
00575     repaint();
00576 }
00577 
00578 void KexiDBLabel::resizeEvent( QResizeEvent* e ) {
00579     if (isVisible())
00580         d->resizeEvent = true;
00581     d->pixmapDirty = true;
00582     QLabel::resizeEvent( e );
00583 }
00584 
00585 void KexiDBLabel::fontChange( const QFont& font ) {
00586     d->pixmapDirty = true;
00587     d->internalLabel->setFont( font );
00588     QLabel::fontChange( font );
00589 }
00590 
00591 void KexiDBLabel::styleChange( QStyle& style ) {
00592     d->pixmapDirty = true;
00593     QLabel::styleChange( style );
00594 }
00595 
00596 void KexiDBLabel::enabledChange( bool enabled ) {
00597     d->pixmapDirty = true;
00598     d->internalLabel->setEnabled( enabled );
00599     QLabel::enabledChange( enabled );
00600 }
00601 
00602 void KexiDBLabel::paletteChange( const QPalette& oldPal ) {
00603     Q_UNUSED(oldPal);
00604     d->pixmapDirty = true;
00605     d->internalLabel->setPalette( palette() );
00606 }
00607 
00608 /*const QColor & KexiDBLabel::paletteForegroundColor () const 
00609 {
00610     return d->foregroundColor;
00611 }
00612 
00613 void KexiDBLabel::setPaletteForegroundColor ( const QColor& color )
00614 {
00615     d->foregroundColor = color;
00616 }*/
00617 
00618 void KexiDBLabel::frameChanged() {
00619     d->pixmapDirty = true;
00620     d->internalLabel->updateFrame();
00621     QFrame::frameChanged();
00622 }
00623 
00624 void KexiDBLabel::showEvent( QShowEvent* e ) {
00625     d->pixmapDirty = true;
00626     QLabel::showEvent( e );
00627 }
00628 
00629 void KexiDBLabel::setText( const QString& text ) {
00630     d->pixmapDirty = true;
00631     QLabel::setText( text );
00632     //This is necessary for KexiFormDataItemInterface
00633     valueChanged();
00634     repaint();
00635 }
00636 
00637 bool KexiDBLabel::shadowEnabled() const
00638 {
00639     return d->shadowEnabled;
00640 }
00641 
00642 /*bool KexiDBLabel::event( QEvent* e )
00643 {
00644     if (e->type()==QEvent::FocusOut) {
00645         repaint();
00646         kdDebug() << "FFFFFFFFFFFFFFFFFF!" << endl;
00647     }
00648     return QLabel::event(e);
00649 }*/
00650 
00651 #define ClassName KexiDBLabel
00652 #define SuperClassName QLabel
00653 #include "kexiframeutils_p.cpp"
00654 #include "kexidblabel.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys