00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
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
00103 const int w = textImage.width();
00104 const int h = textImage.height();
00105
00106
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
00116
00117 double alphaShadow;
00118
00119
00120
00121
00122 QImage img = textImage.convertDepth( 32 );
00123
00124
00125
00126
00127 if ( ( result.width() != w ) || ( result.height() != h ) ) {
00128 result.create( w, h, 32 );
00129 }
00130
00131
00132 double realOpacity = SHADOW_OPACITY + QMIN(50.0/double(256.0-qGray(bgColor.rgb())), 50.0);
00133
00134
00135 if (colorGroup().background()==Qt::red)
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
00162 if (alphaShadow > 0)
00163 result.setPixel( i, j, qRgba( bgRed, bgGreen , bgBlue,
00164 ( int ) (( alphaShadow > realOpacity ) ? realOpacity : alphaShadow)
00165 ) );
00166 }
00167
00168
00169
00170
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
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
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
00609
00610
00611
00612
00613
00614
00615
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
00633 valueChanged();
00634 repaint();
00635 }
00636
00637 bool KexiDBLabel::shadowEnabled() const
00638 {
00639 return d->shadowEnabled;
00640 }
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651 #define ClassName KexiDBLabel
00652 #define SuperClassName QLabel
00653 #include "kexiframeutils_p.cpp"
00654 #include "kexidblabel.moc"