kexi

kexidblabel.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2005 Christian Nitschkowski <segfault_ii@web.de>
3 Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
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 "kexidblabel.h"
22
23#include <tqbitmap.h>
24#include <tqpainter.h>
25#include <tqdrawutil.h>
26#include <tqapplication.h>
27#include <tqtimer.h>
28
29#include <kdebug.h>
30#include <kimageeffect.h>
31
32#include <kexidb/field.h>
33#include <kexiutils/utils.h>
34
35#define SHADOW_OFFSET_X 3
36#define SHADOW_OFFSET_Y 3
37#define SHADOW_FACTOR 16.0
38#define SHADOW_OPACITY 50.0
39#define SHADOW_AXIS_FACTOR 2.0
40#define SHADOW_DIAGONAL_FACTOR 1.0
41#define SHADOW_THICKNESS 1
42
44class KexiDBInternalLabel : public TQLabel {
45 friend class KexiDBLabel;
46 public:
47 KexiDBInternalLabel( KexiDBLabel* );
48 virtual ~KexiDBInternalLabel();
49
50 protected:
51 void updateFrame();
52
53 TQImage makeShadow( const TQImage& textImage, const TQColor &bgColor, const TQRect& boundingRect );
54 TQRect getBounding( const TQImage &image, const TQRect& startRect );
55// double defaultDecay( TQImage& source, int i, int j );
56 KPixmap getShadowPixmap();
57
58 TQRect m_shadowRect;
59 KexiDBLabel *m_parentLabel;
60};
61
62KexiDBInternalLabel::KexiDBInternalLabel( KexiDBLabel* parent )
63 : TQLabel( parent )
64 , m_parentLabel(parent)
65{
66 int a = alignment() | TQt::WordBreak;
67 a &= (0xffffff ^ TQt::AlignVertical_Mask);
68 a |= TQt::AlignTop;
69 setAlignment( a );
70 updateFrame();
71}
72
73void KexiDBInternalLabel::updateFrame()
74{
75 setIndent(m_parentLabel->indent());
76 setMargin(m_parentLabel->margin());
77 setFont(m_parentLabel->font());
78
79 setFrameShadow(m_parentLabel->frameShadow());
80 setFrameShape(m_parentLabel->frameShape());
81 setFrameStyle(m_parentLabel->frameStyle());
82 setMidLineWidth(m_parentLabel->midLineWidth());
83 setLineWidth(m_parentLabel->lineWidth());
84}
85
86KexiDBInternalLabel::~KexiDBInternalLabel()
87{
88}
89
96TQImage KexiDBInternalLabel::makeShadow( const TQImage& textImage,
97 const TQColor &bgColor, const TQRect& boundingRect )
98{
99 TQImage result;
100 TQString origText( text() );
101
102 // create a new image for for the shaddow
103 const int w = textImage.width();
104 const int h = textImage.height();
105
106 // avoid calling these methods for every pixel
107 const int bgRed = bgColor.red();
108 const int bgGreen = bgColor.green();
109 const int bgBlue = bgColor.blue();
110
111 const int startX = boundingRect.x() + SHADOW_THICKNESS;
112 const int startY = boundingRect.y() + SHADOW_THICKNESS;
113 const int effectWidth = boundingRect.bottomRight().x() - SHADOW_THICKNESS;
114 const int effectHeight = boundingRect.bottomRight().y() - SHADOW_THICKNESS;
115// const int period = (effectWidth - startX) / 10;
116
117 double alphaShadow;
118
119 /*
120 * This is the source pixmap
121 */
122 TQImage img = textImage.convertDepth( 32 );
123
124 /*
125 * Resize the image if necessary
126 */
127 if ( ( result.width() != w ) || ( result.height() != h ) ) {
128 result.create( w, h, 32 );
129 }
130
131// result.fill( 0 ); // all black
132 double realOpacity = SHADOW_OPACITY + TQMIN(50.0/double(256.0-tqGray(bgColor.rgb())), 50.0);
133 //int _h, _s, _v;
134 //.getHsv( &_h, &_s, &_v );
135 if (colorGroup().background()==TQt::red)//_s>=250 && _v>=250) //for colors like cyan or red, make the result more white
136 realOpacity += 50.0;
137 result.fill( (int)realOpacity );
138 result.setAlphaBuffer( true );
139
140 for ( int i = startX; i < effectWidth; i++ ) {
141 for ( int j = startY; j < effectHeight; j++ ) {
148 if ( ( i < 1 ) || ( j < 1 ) || ( i > img.width() - 2 ) || ( j > img.height() - 2 ) )
149 continue;
150 else
151 alphaShadow = ( tqGray( img.pixel( i - 1, j - 1 ) ) * SHADOW_DIAGONAL_FACTOR +
152 tqGray( img.pixel( i - 1, j ) ) * SHADOW_AXIS_FACTOR +
153 tqGray( img.pixel( i - 1, j + 1 ) ) * SHADOW_DIAGONAL_FACTOR +
154 tqGray( img.pixel( i , j - 1 ) ) * SHADOW_AXIS_FACTOR +
155 0 +
156 tqGray( img.pixel( i , j + 1 ) ) * SHADOW_AXIS_FACTOR +
157 tqGray( img.pixel( i + 1, j - 1 ) ) * SHADOW_DIAGONAL_FACTOR +
158 tqGray( img.pixel( i + 1, j ) ) * SHADOW_AXIS_FACTOR +
159 tqGray( img.pixel( i + 1, j + 1 ) ) * SHADOW_DIAGONAL_FACTOR ) / SHADOW_FACTOR;
160
161 // update the shadow's i,j pixel.
162 if (alphaShadow > 0)
163 result.setPixel( i, j, tqRgba( bgRed, bgGreen , bgBlue,
164 ( int ) (( alphaShadow > realOpacity ) ? realOpacity : alphaShadow)
165 ) );
166 }
167/*caused too much redraw problems if (period && i % period) {
168 tqApp->processEvents();
169 if (text() != origText) //text has been changed in the meantime: abort
170 return TQImage();
171 }*/
172 }
173 return result;
174}
175
176KPixmap KexiDBInternalLabel::getShadowPixmap() {
180 const TQColor textColor = colorGroup().foreground();
181
185 KPixmap finalPixmap, tempPixmap;
186 TQImage shadowImage, tempImage;
187 TQPainter painter;
188
189 m_shadowRect = TQRect();
190
191 tempPixmap.resize( size() );
192 tempPixmap.fill( TQt::black );
193 tempPixmap.setMask( tempPixmap.createHeuristicMask( true ) );
194
198 setPaletteForegroundColor( TQt::white );
199
203 painter.begin( &tempPixmap );
204 painter.setFont( font() );
205 drawContents( &painter );
206 painter.end();
207 setPaletteForegroundColor( textColor );
208
213 shadowImage = tempPixmap;
214 tempPixmap.setMask( TQBitmap() );
215
220 m_shadowRect = getBounding( shadowImage, m_shadowRect );
221
228 m_shadowRect.setX( TQMAX( m_shadowRect.x() - ( m_shadowRect.width() / 4 ), 0 ) );
229 m_shadowRect.setY( TQMAX( m_shadowRect.y() - ( m_shadowRect.height() / 4 ), 0 ) );
230 m_shadowRect.setBottomRight( TQPoint(
231 TQMIN( m_shadowRect.x() + ( m_shadowRect.width() * 3 / 2 ), shadowImage.width() ),
232 TQMIN( m_shadowRect.y() + ( m_shadowRect.height() * 3 / 2 ), shadowImage.height() ) ) );
233
234 shadowImage = makeShadow( shadowImage,
235 tqGray( colorGroup().background().rgb() ) < 127 ? TQt::white : TQt::black,
236 m_shadowRect );
237 if (shadowImage.isNull())
238 return KPixmap();
239
243 m_shadowRect = getBounding( shadowImage, m_shadowRect );
244
248 finalPixmap.resize( size() );
249 painter.begin( &finalPixmap );
250 painter.fillRect( 0, 0, finalPixmap.width(), finalPixmap.height(),
251 palette().brush(
252 isEnabled() ? TQPalette::Active : TQPalette::Disabled,
253 TQColorGroup::Background ) );
254 painter.end();
255
260 tempPixmap.resize( m_shadowRect.size() );
261 if (!finalPixmap.isNull()) {
262 bitBlt( &tempPixmap, 0, 0, &finalPixmap,
263 m_shadowRect.x() + SHADOW_OFFSET_X,
264 m_shadowRect.y() + SHADOW_OFFSET_Y,
265 m_shadowRect.width(),
266 m_shadowRect.height() );
267 }
272 finalPixmap = tempPixmap;
273
281 tempImage = shadowImage.copy( m_shadowRect );
282 tempPixmap.convertFromImage( tempImage );
286 if (!tempPixmap.isNull()) {
287 bitBlt( &finalPixmap, 0, 0, &tempPixmap );
288 }
289
294 m_shadowRect.moveBy( SHADOW_OFFSET_X, SHADOW_OFFSET_Y );
295
296 return finalPixmap;
297}
298
299TQRect KexiDBInternalLabel::getBounding( const TQImage &image, const TQRect& startRect ) {
300 TQPoint topLeft;
301 TQPoint bottomRight;
302
303 const int startX = startRect.x();
304 const int startY = startRect.y();
308 const int width = TQMIN( ( startRect.bottomRight().x() > 0
309 ? startRect.bottomRight().x() : TQCOORD_MAX ),
310 image.width() );
311 const int height = TQMIN( ( startRect.bottomRight().y() > 0
312 ? startRect.bottomRight().y() : TQCOORD_MAX ),
313 image.height() );
314
322 TQRgb trans = image.pixel( 0, 0 );
323
324 for ( int y = startY; y < height; y++ ) {
325 for ( int x = startX; x < width; x++ ) {
326 if ( image.pixel( x, y ) != trans ) {
327 topLeft.setY( y );
328 y = height;
329 break;
330 }
331 }
332 }
333
334 for ( int x = startX; x < width; x++ ) {
335 for ( int y = startY; y < height; y++ ) {
336 if ( image.pixel( x, y ) != trans ) {
337 topLeft.setX( x );
338 x = width;
339 break;
340 }
341 }
342 }
343
344 for ( int y = height - 1; y > topLeft.y(); y-- ) {
345 for ( int x = width - 1; x > topLeft.x(); x-- ) {
346 if ( image.pixel( x, y ) != trans ) {
347 bottomRight.setY( y + 1 );
348 y = 0;
349 break;
350 }
351 }
352 }
353
354 for ( int x = width - 1; x > topLeft.x(); x-- ) {
355 for ( int y = height - 1; y > topLeft.y(); y-- ) {
356 if ( image.pixel( x, y ) != trans ) {
357 bottomRight.setX( x + 1 );
358 x = 0;
359 break;
360 }
361 }
362 }
363
364 return TQRect(
365 topLeft.x(),
366 topLeft.y(),
367 bottomRight.x() - topLeft.x(),
368 bottomRight.y() - topLeft.y() );
369}
370
371//=========================================================
372
374class KexiDBLabel::Private
375{
376 public:
377 Private()
378 : timer(0)
379// , autonumberDisplayParameters(0)
380 , pixmapDirty( true )
381 , shadowEnabled( false )
382 , resizeEvent( false )
383 {
384 }
385 ~Private() {}
386 KPixmap shadowPixmap;
387 TQPoint shadowPosition;
388 KexiDBInternalLabel* internalLabel;
389 TQTimer* timer;
390 TQColor frameColor;
391 bool pixmapDirty : 1;
392 bool shadowEnabled : 1;
393 bool resizeEvent : 1;
394};
395
396//=========================================================
397
398KexiDBLabel::KexiDBLabel( TQWidget *parent, const char *name, WFlags f )
399 : TQLabel( parent, name, f )
402 , d( new Private() )
403{
404 init();
405}
406
407KexiDBLabel::KexiDBLabel( const TQString& text, TQWidget *parent, const char *name, WFlags f )
408 : TQLabel( parent, name, f )
411 , d( new Private() )
412{
413 init();
414 setText( text );
415}
416
417KexiDBLabel::~KexiDBLabel()
418{
419 delete d;
420}
421
422void KexiDBLabel::init()
423{
424 m_hasFocusableWidget = false;
425 d->internalLabel = new KexiDBInternalLabel( this );
426 d->internalLabel->hide();
427 d->frameColor = palette().active().foreground();
428
429 setAlignment( d->internalLabel->alignment() );
430}
431
432void KexiDBLabel::updatePixmapLater() {
433 if (d->resizeEvent) {
434 if (!d->timer) {
435 d->timer = new TQTimer(this, "KexiDBLabelTimer");
436 connect(d->timer, TQ_SIGNAL(timeout()), this, TQ_SLOT(updatePixmap()));
437 }
438 d->timer->start(100, true);
439 d->resizeEvent = false;
440 return;
441 }
442 if (d->timer && d->timer->isActive())
443 return;
444 updatePixmap();
445}
446
453 d->internalLabel->setText( text() );
454 d->internalLabel->setFixedSize( size() );
455 d->internalLabel->setPalette( palette() );
456 d->internalLabel->setAlignment( alignment() );
457// d->shadowPixmap = KPixmap(); //parallel repaints won't hurt us cause incomplete pixmap
458 KPixmap shadowPixmap = d->internalLabel->getShadowPixmap();
459 if (shadowPixmap.isNull())
460 return;
461 d->shadowPixmap = shadowPixmap;
462 d->shadowPosition = d->internalLabel->m_shadowRect.topLeft();
463 d->pixmapDirty = false;
464 repaint();
465}
466
467void KexiDBLabel::paintEvent( TQPaintEvent* e )
468{
469 TQPainter p( this );
470 if ( d->shadowEnabled ) {
474 if ( d->pixmapDirty ) {
475 updatePixmapLater();
476 }
477
485 if ( !d->pixmapDirty && e->rect().contains( d->shadowPosition ) && !d->shadowPixmap.isNull()) {
486 TQRect clipRect = TQRect(
487 TQMAX( e->rect().x() - d->shadowPosition.x(), 0 ),
488 TQMAX( e->rect().y() - d->shadowPosition.y(), 0 ),
489 TQMIN( e->rect().width() + d->shadowPosition.x(), d->shadowPixmap.width() ),
490 TQMIN( e->rect().height() + d->shadowPosition.y(), d->shadowPixmap.height() ) );
491 p.drawPixmap( d->internalLabel->m_shadowRect.topLeft(), d->shadowPixmap, clipRect );
492 }
493 }
494 KexiDBTextWidgetInterface::paint( this, &p, text().isEmpty(), alignment(), false );
495 TQLabel::paintEvent( e );
496}
497
498void KexiDBLabel::setValueInternal( const TQVariant& add, bool removeOld ) {
499 if (removeOld)
500 setText(add.toString());
501 else
502 setText( m_origValue.toString() + add.toString() );
503}
504
506 return text();
507}
508
509void KexiDBLabel::setInvalidState( const TQString& displayText )
510{
511 setText( displayText );
512}
513
515{
516 return text().isNull();
517}
518
520{
521 return text().isEmpty();
522}
523
525{
526 return true;
527}
528
529void KexiDBLabel::setReadOnly( bool readOnly )
530{
531 Q_UNUSED(readOnly);
532}
533
535{
536 return this;
537}
538
540{
541 return false;
542}
543
545{
546 return false;
547}
548
550{
551 setText(TQString());
552}
553
554bool KexiDBLabel::setProperty( const char * name, const TQVariant & value )
555{
556 const bool ret = TQLabel::setProperty(name, value);
557 if (d->shadowEnabled) {
558 if (0==qstrcmp("indent", name) || 0==qstrcmp("font", name) || 0==qstrcmp("margin", name)
559 || 0==qstrcmp("frameShadow", name) || 0==qstrcmp("frameShape", name)
560 || 0==qstrcmp("frameStyle", name) || 0==qstrcmp("midLineWidth", name)
561 || 0==qstrcmp("lineWidth", name)) {
562 d->internalLabel->setProperty(name, value);
563 updatePixmap();
564 }
565 }
566 return ret;
567}
568
574
576 d->shadowEnabled = state;
577 d->pixmapDirty = true;
578 if (state)
579 d->internalLabel->updateFrame();
580 repaint();
581}
582
583void KexiDBLabel::resizeEvent( TQResizeEvent* e ) {
584 if (isVisible())
585 d->resizeEvent = true;
586 d->pixmapDirty = true;
587 TQLabel::resizeEvent( e );
588}
589
590void KexiDBLabel::fontChange( const TQFont& font ) {
591 d->pixmapDirty = true;
592 d->internalLabel->setFont( font );
593 TQLabel::fontChange( font );
594}
595
596void KexiDBLabel::styleChange( TQStyle& style ) {
597 d->pixmapDirty = true;
598 TQLabel::styleChange( style );
599}
600
601void KexiDBLabel::enabledChange( bool enabled ) {
602 d->pixmapDirty = true;
603 d->internalLabel->setEnabled( enabled );
604 TQLabel::enabledChange( enabled );
605}
606
607void KexiDBLabel::paletteChange( const TQPalette& oldPal ) {
608 Q_UNUSED(oldPal);
609 d->pixmapDirty = true;
610 d->internalLabel->setPalette( palette() );
611}
612
613/*const TQColor & KexiDBLabel::paletteForegroundColor () const
614{
615 return d->foregroundColor;
616}
617
618void KexiDBLabel::setPaletteForegroundColor ( const TQColor& color )
619{
620 d->foregroundColor = color;
621}*/
622
623void KexiDBLabel::frameChanged() {
624 d->pixmapDirty = true;
625 d->internalLabel->updateFrame();
626 TQFrame::frameChanged();
627}
628
629void KexiDBLabel::showEvent( TQShowEvent* e ) {
630 d->pixmapDirty = true;
631 TQLabel::showEvent( e );
632}
633
634void KexiDBLabel::setText( const TQString& text ) {
635 d->pixmapDirty = true;
636 TQLabel::setText( text );
637 //This is necessary for KexiFormDataItemInterface
638 valueChanged();
639 repaint();
640}
641
642bool KexiDBLabel::shadowEnabled() const
643{
644 return d->shadowEnabled;
645}
646
647#define ClassName KexiDBLabel
648#define SuperClassName TQLabel
649#include "kexiframeutils_p.cpp"
650#include "kexidblabel.moc"
An extended, data-aware, read-only text label.
Definition kexidblabel.h:42
virtual void setInvalidState(const TQString &displayText)
virtual bool isReadOnly() const
always true
virtual void clear()
clears item's data, so the data will contain NULL data
virtual TQWidget * widget()
virtual void setValueInternal(const TQVariant &add, bool removeOld)
Sets value value for a widget.
virtual bool valueIsEmpty()
virtual bool setProperty(const char *name, const TQVariant &value)
used to catch setIndent(), etc.
virtual void setReadOnly(bool readOnly)
empty
void updatePixmap()
virtual bool cursorAtStart()
always false
virtual void setColumnInfo(KexiDB::QueryColumnInfo *cinfo)
virtual TQVariant value()
void setShadowEnabled(bool state)
virtual bool cursorAtEnd()
always false
virtual bool valueIsNull()
virtual void paintEvent(TQPaintEvent *)
An interface providing common text editor's functionality.
void setColumnInfo(KexiDB::QueryColumnInfo *cinfo, TQWidget *w)
Called from KexiFormDataItemInterface::setColumnInfo(KexiDB::QueryColumnInfo* cinfo) implementation.
void paint(TQFrame *w, TQPainter *p, bool textIsEmpty, int alignment, bool hasFocus)
Called from paintEvent( TQPaintEvent *pe ) method of the data aware widget.
Helper class that assigns additional information for the column in a query.
Definition queryschema.h:49
An interface for declaring form widgets to be data-aware.
virtual void setColumnInfo(KexiDB::QueryColumnInfo *cinfo)