diff options
Diffstat (limited to 'src/kernel/tqpainter_x11.cpp')
-rw-r--r-- | src/kernel/tqpainter_x11.cpp | 3183 |
1 files changed, 3183 insertions, 0 deletions
diff --git a/src/kernel/tqpainter_x11.cpp b/src/kernel/tqpainter_x11.cpp new file mode 100644 index 000000000..e61b2fab2 --- /dev/null +++ b/src/kernel/tqpainter_x11.cpp @@ -0,0 +1,3183 @@ +/**************************************************************************** +** +** Implementation of TQPainter class for X11 +** +** Created : 940112 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the kernel module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qplatformdefs.h" + +#include "tqfont.h" +#include "tqpainter.h" +#include "tqwidget.h" +#include "tqbitmap.h" +#include "ntqpixmapcache.h" +#include "tqtextcodec.h" +#include "tqpaintdevicemetrics.h" + +#include "qt_x11_p.h" + +#include "tqtextlayout_p.h" +#include "tqfontdata_p.h" +#include "tqfontengine_p.h" +#include "tqtextengine_p.h" + +#include <math.h> + +// paintevent magic to provide Windows semantics on X11 +static TQRegion* paintEventClipRegion = 0; +static TQPaintDevice* paintEventDevice = 0; + +void qt_set_paintevent_clipping( TQPaintDevice* dev, const TQRegion& region) +{ + if ( !paintEventClipRegion ) + paintEventClipRegion = new TQRegion( region ); + else + *paintEventClipRegion = region; + paintEventDevice = dev; +} + +void qt_clear_paintevent_clipping() +{ + delete paintEventClipRegion; + paintEventClipRegion = 0; + paintEventDevice = 0; +} + +class TQWFlagWidget : public TQWidget +{ +public: + void setWState( WFlags f ) { TQWidget::setWState(f); } + void clearWState( WFlags f ) { TQWidget::clearWState(f); } + void setWFlags( WFlags f ) { TQWidget::setWFlags(f); } + void clearWFlags( WFlags f ) { TQWidget::clearWFlags(f); } +}; + +void qt_erase_region( TQWidget* w, const TQRegion& region) +{ + TQRegion reg = region; + + if ( TQPainter::redirect(w) || (!w->isTopLevel() && w->backgroundPixmap() + && w->backgroundOrigin() != TQWidget::WidgetOrigin) ) { + TQPoint offset = w->backgroundOffset(); + int ox = offset.x(); + int oy = offset.y(); + + bool unclipped = w->testWFlags( TQt::WPaintUnclipped ); + if ( unclipped ) + ((TQWFlagWidget*)w)->clearWFlags( TQt::WPaintUnclipped ); + TQPainter p( w ); + p.setClipRegion( region ); // automatically includes paintEventDevice if required + if ( w->backgroundPixmap() ) + p.drawTiledPixmap( 0, 0, w->width(), w->height(), + *w->backgroundPixmap(), ox, oy ); + else + p.fillRect( w->rect(), w->eraseColor() ); + if ( unclipped ) + ((TQWFlagWidget*)w)->setWFlags( TQt::WPaintUnclipped ); + return; + } + + if ( w == paintEventDevice && paintEventClipRegion ) + reg = paintEventClipRegion->intersect( reg ); + + TQMemArray<TQRect> r = reg.rects(); + for (uint i=0; i<r.size(); i++) { + const TQRect& rr = r[(int)i]; + XClearArea( w->x11Display(), w->winId(), + rr.x(), rr.y(), rr.width(), rr.height(), False ); + } +} + +void qt_erase_rect( TQWidget* w, const TQRect& r) +{ + if ( TQPainter::redirect(w) || w == paintEventDevice + || w->backgroundOrigin() != TQWidget::WidgetOrigin ) + qt_erase_region( w, r ); + else + XClearArea( w->x11Display(), w->winId(), r.x(), r.y(), r.width(), r.height(), False ); + +} + +#ifdef TQT_NO_XFTFREETYPE +static const TQt::HANDLE rendhd = 0; +#endif + +// hack, so we don't have to make TQRegion::clipRectangles() public or include +// X11 headers in ntqregion.h +inline void *qt_getClipRects( const TQRegion &r, int &num ) +{ + return r.clipRectangles( num ); +} + +static inline void x11SetClipRegion(Display *dpy, GC gc, GC gc2, TQt::HANDLE draw, const TQRegion &r) +{ + int num; + XRectangle *rects = (XRectangle *)qt_getClipRects( r, num ); + + if (gc) + XSetClipRectangles( dpy, gc, 0, 0, rects, num, YXBanded ); + if (gc2) + XSetClipRectangles( dpy, gc2, 0, 0, rects, num, YXBanded ); + +#ifndef TQT_NO_XFTFREETYPE + if (draw) + XftDrawSetClipRectangles((XftDraw *) draw, 0, 0, rects, num); +#else + Q_UNUSED(draw); +#endif // TQT_NO_XFTFREETYPE +} + +static inline void x11ClearClipRegion(Display *dpy, GC gc, GC gc2, TQt::HANDLE draw) +{ + if (gc) + XSetClipMask(dpy, gc, None); + if (gc2) + XSetClipMask(dpy, gc2, None); + +#ifndef TQT_NO_XFTFREETYPE + if (draw) { +# ifdef QT_XFT2 + XftDrawSetClip((XftDraw *) draw, None); +# else + // stupid Xft1 + Picture pict = XftDrawPicture((XftDraw *) draw); + XRenderPictureAttributes pattr; + pattr.clip_mask = None; + XRenderChangePicture(dpy, pict, CPClipMask, &pattr); +# endif // QT_XFT2 + } +#else + Q_UNUSED(draw); +#endif // TQT_NO_XFTFREETYPE +} + + +/***************************************************************************** + Trigonometric function for TQPainter + + We have implemented simple sine and cosine function that are called from + TQPainter::drawPie() and TQPainter::drawChord() when drawing the outline of + pies and chords. + These functions are slower and less accurate than math.h sin() and cos(), + but with still around 1/70000th sec. execution time (on a 486DX2-66) and + 8 digits accuracy, it should not be the bottleneck in drawing these shapes. + The advantage is that you don't have to link in the math library. + *****************************************************************************/ + +const double Q_PI = 3.14159265358979323846; // pi +const double Q_2PI = 6.28318530717958647693; // 2*pi +const double Q_PI2 = 1.57079632679489661923; // pi/2 + + +#if defined(Q_CC_GNU) && defined(Q_OS_AIX) +// AIX 4.2 gcc 2.7.2.3 gets internal error. +static int tqRoundAIX( double d ) +{ + return tqRound(d); +} +#define tqRound tqRoundAIX +#endif + + +#if defined(Q_CC_GNU) && defined(__i386__) + +inline double qcos( double a ) +{ + double r; + __asm__ ( + "fcos" + : "=t" (r) : "0" (a) ); + return(r); +} + +inline double qsin( double a ) +{ + double r; + __asm__ ( + "fsin" + : "=t" (r) : "0" (a) ); + return(r); +} + +double qsincos( double a, bool calcCos=FALSE ) +{ + return calcCos ? qcos(a) : qsin(a); +} + +#else + +double qsincos( double a, bool calcCos=FALSE ) +{ + if ( calcCos ) // calculate cosine + a -= Q_PI2; + if ( a >= Q_2PI || a <= -Q_2PI ) { // fix range: -2*pi < a < 2*pi + int m = (int)(a/Q_2PI); + a -= Q_2PI*m; + } + if ( a < 0.0 ) // 0 <= a < 2*pi + a += Q_2PI; + int sign = a > Q_PI ? -1 : 1; + if ( a >= Q_PI ) + a = Q_2PI - a; + if ( a >= Q_PI2 ) + a = Q_PI - a; + if ( calcCos ) + sign = -sign; + double a2 = a*a; // here: 0 <= a < pi/4 + double a3 = a2*a; // make taylor sin sum + double a5 = a3*a2; + double a7 = a5*a2; + double a9 = a7*a2; + double a11 = a9*a2; + return (a-a3/6+a5/120-a7/5040+a9/362880-a11/39916800)*sign; +} + +inline double qsin( double a ) { return qsincos(a, FALSE); } +inline double qcos( double a ) { return qsincos(a, TRUE); } + +#endif + + +/***************************************************************************** + TQPainter internal GC (Graphics Context) allocator. + + The GC allocator offers two functions; alloc_gc() and free_gc() that + reuse GC objects instead of calling XCreateGC() and XFreeGC(), which + are a whole lot slower. + *****************************************************************************/ + +struct TQGC +{ + GC gc; + char in_use; + bool mono; + int scrn; +}; + +const int gc_array_size = 256; +static TQGC gc_array[gc_array_size]; // array of GCs +static bool gc_array_init = FALSE; + + +static void init_gc_array() +{ + if ( !gc_array_init ) { + memset( gc_array, 0, gc_array_size*sizeof(TQGC) ); + gc_array_init = TRUE; + } +} + +static void cleanup_gc_array( Display *dpy ) +{ + TQGC *p = gc_array; + int i = gc_array_size; + if ( gc_array_init ) { + while ( i-- ) { + if ( p->gc ) // destroy GC + XFreeGC( dpy, p->gc ); + p++; + } + gc_array_init = FALSE; + } +} + +// #define DONT_USE_GC_ARRAY + +static GC alloc_gc( Display *dpy, int scrn, Drawable hd, bool monochrome=FALSE, + bool privateGC = FALSE ) +{ +#if defined(DONT_USE_GC_ARRAY) + privateGC = TRUE; // will be slower +#endif + if ( privateGC ) { + GC gc = XCreateGC( dpy, hd, 0, 0 ); + XSetGraphicsExposures( dpy, gc, False ); + return gc; + } + TQGC *p = gc_array; + int i = gc_array_size; + if ( !gc_array_init ) // not initialized + init_gc_array(); + while ( i-- ) { + if ( !p->gc ) { // create GC (once) + p->gc = XCreateGC( dpy, hd, 0, 0 ); + p->scrn = scrn; + XSetGraphicsExposures( dpy, p->gc, False ); + p->in_use = FALSE; + p->mono = monochrome; + } + if ( !p->in_use && p->mono == monochrome && p->scrn == scrn ) { + p->in_use = TRUE; // available/compatible GC + return p->gc; + } + p++; + } +#if defined(QT_CHECK_NULL) + tqWarning( "TQPainter: Internal error; no available GC" ); +#endif + GC gc = XCreateGC( dpy, hd, 0, 0 ); + XSetGraphicsExposures( dpy, gc, False ); + return gc; +} + +static void free_gc( Display *dpy, GC gc, bool privateGC = FALSE ) +{ +#if defined(DONT_USE_GC_ARRAY) + privateGC = TRUE; // will be slower +#endif + if ( privateGC ) { + Q_ASSERT( dpy != 0 ); + XFreeGC( dpy, gc ); + return; + } + TQGC *p = gc_array; + int i = gc_array_size; + if ( gc_array_init ) { + while ( i-- ) { + if ( p->gc == gc ) { + p->in_use = FALSE; // set available + XSetClipMask( dpy, gc, None ); // make it reusable + XSetFunction( dpy, gc, GXcopy ); + XSetFillStyle( dpy, gc, FillSolid ); + XSetTSOrigin( dpy, gc, 0, 0 ); + return; + } + p++; + } + } + + // not found in gc_array + XFreeGC(dpy, gc); +} + + +/***************************************************************************** + TQPainter internal GC (Graphics Context) cache for solid pens and + brushes. + + The GC cache makes a significant contribution to speeding up + drawing. Setting new pen and brush colors will make the painter + look for another GC with the same color instead of changing the + color value of the GC currently in use. The cache structure is + optimized for fast lookup. Only solid line pens with line width 0 + and solid brushes are cached. + + In addition, stored GCs may have an implicit clipping region + set. This prevents any drawing outside paint events. Both + updatePen() and updateBrush() keep track of the validity of this + clipping region by storing the clip_serial number in the cache. + +*****************************************************************************/ + +struct TQGCC // cached GC +{ + GC gc; + uint pix; + int count; + int hits; + uint clip_serial; + int scrn; +}; + +const int gc_cache_size = 29; // multiply by 4 +static TQGCC *gc_cache_buf; +static TQGCC *gc_cache[4*gc_cache_size]; +static bool gc_cache_init = FALSE; +static uint gc_cache_clip_serial = 0; + + +static void init_gc_cache() +{ + if ( !gc_cache_init ) { + gc_cache_init = TRUE; + gc_cache_clip_serial = 0; + TQGCC *g = gc_cache_buf = new TQGCC[4*gc_cache_size]; + memset( g, 0, 4*gc_cache_size*sizeof(TQGCC) ); + for ( int i=0; i<4*gc_cache_size; i++ ) + gc_cache[i] = g++; + } +} + + +// #define GC_CACHE_STAT +#if defined(GC_CACHE_STAT) +#include "tqtextstream.h" +#include "tqbuffer.h" + +static int g_numhits = 0; +static int g_numcreates = 0; +static int g_numfaults = 0; +#endif + + +static void cleanup_gc_cache() +{ + if ( !gc_cache_init ) + return; +#if defined(GC_CACHE_STAT) + tqDebug( "Number of cache hits = %d", g_numhits ); + tqDebug( "Number of cache creates = %d", g_numcreates ); + tqDebug( "Number of cache faults = %d", g_numfaults ); + for ( int i=0; i<gc_cache_size; i++ ) { + TQCString str; + TQBuffer buf( str ); + buf.open(IO_ReadWrite); + TQTextStream s(&buf); + s << i << ": "; + for ( int j=0; j<4; j++ ) { + TQGCC *g = gc_cache[i*4+j]; + s << (g->gc ? 'X' : '-') << ',' << g->hits << ',' + << g->count << '\t'; + } + s << '\0'; + tqDebug( str ); + buf.close(); + } +#endif + delete [] gc_cache_buf; + gc_cache_init = FALSE; +} + + +static bool obtain_gc( void **ref, GC *gc, uint pix, Display *dpy, int scrn, + TQt::HANDLE hd, uint painter_clip_serial ) +{ + if ( !gc_cache_init ) + init_gc_cache(); + + int k = (pix % gc_cache_size) * 4; + TQGCC *g = gc_cache[k]; + TQGCC *prev = 0; + +#define NOMATCH (g->gc && (g->pix != pix || g->scrn != scrn || \ + (g->clip_serial > 0 && g->clip_serial != painter_clip_serial))) + + if ( NOMATCH ) { + prev = g; + g = gc_cache[++k]; + if ( NOMATCH ) { + prev = g; + g = gc_cache[++k]; + if ( NOMATCH ) { + prev = g; + g = gc_cache[++k]; + if ( NOMATCH ) { + if ( g->count == 0 && g->scrn == scrn) { // steal this GC + g->pix = pix; + g->count = 1; + g->hits = 1; + g->clip_serial = 0; + XSetForeground( dpy, g->gc, pix ); + XSetClipMask(dpy, g->gc, None); + gc_cache[k] = prev; + gc_cache[k-1] = g; + *ref = (void *)g; + *gc = g->gc; + return TRUE; + } else { // all GCs in use +#if defined(GC_CACHE_STAT) + g_numfaults++; +#endif + *ref = 0; + return FALSE; + } + } + } + } + } + +#undef NOMATCH + + *ref = (void *)g; + + if ( g->gc ) { // reuse existing GC +#if defined(GC_CACHE_STAT) + g_numhits++; +#endif + *gc = g->gc; + g->count++; + g->hits++; + if ( prev && g->hits > prev->hits ) { // maintain LRU order + gc_cache[k] = prev; + gc_cache[k-1] = g; + } + return TRUE; + } else { // create new GC +#if defined(GC_CACHE_STAT) + g_numcreates++; +#endif + g->gc = alloc_gc( dpy, scrn, hd, FALSE ); + g->scrn = scrn; + g->pix = pix; + g->count = 1; + g->hits = 1; + g->clip_serial = 0; + *gc = g->gc; + return FALSE; + } +} + +static inline void release_gc( void *ref ) +{ + ((TQGCC*)ref)->count--; +} + +/***************************************************************************** + TQPainter member functions + *****************************************************************************/ + +/*! + \internal + + Internal function that initializes the painter. +*/ + +void TQPainter::initialize() +{ + init_gc_array(); + init_gc_cache(); +} + +/*! + \internal + + Internal function that cleans up the painter. +*/ + +void TQPainter::cleanup() +{ + cleanup_gc_cache(); + cleanup_gc_array( TQPaintDevice::x11AppDisplay() ); + TQPointArray::cleanBuffers(); +} + +/*! + \internal + + Internal function that destroys up the painter. +*/ + +void TQPainter::destroy() +{ + +} + +void TQPainter::init() +{ + d = 0; + flags = IsStartingUp; + bg_col = white; // default background color + bg_mode = TransparentMode; // default background mode + rop = CopyROP; // default ROP + tabstops = 0; // default tabbing + tabarray = 0; + tabarraylen = 0; + ps_stack = 0; + wm_stack = 0; + gc = gc_brush = 0; + pdev = 0; + dpy = 0; + txop = txinv = 0; + penRef = brushRef = 0; + clip_serial = 0; + pfont = 0; + block_ext = FALSE; +} + + +/*! + \fn const TQFont &TQPainter::font() const + + Returns the currently set painter font. + + \sa setFont(), TQFont +*/ + +/*! + Sets the painter's font to \a font. + + This font is used by subsequent drawText() functions. The text + color is the same as the pen color. + + \sa font(), drawText() +*/ + +void TQPainter::setFont( const TQFont &font ) +{ +#if defined(QT_CHECK_STATE) + if ( !isActive() ) + tqWarning( "TQPainter::setFont: Will be reset by begin()" ); +#endif + if ( cfont.d != font.d ) { + cfont = font; + cfont.x11SetScreen( scrn ); + setf(DirtyFont); + } +} + + +void TQPainter::updateFont() +{ + if (!isActive()) + return; + + clearf(DirtyFont); + if ( testf(ExtDev) ) { + if (pdev->devType() == TQInternal::Printer) { + if ( pfont ) delete pfont; + pfont = new TQFont( cfont.d, pdev ); + } + TQPDevCmdParam param[1]; + param[0].font = &cfont; + if ( !pdev->cmd( TQPaintDevice::PdcSetFont, this, param ) || !hd ) + return; + } + setf(NoCache); + if ( penRef ) + updatePen(); // force a non-cached GC +} + + +void TQPainter::updatePen() +{ + if (!isActive()) + return; + + if ( testf(ExtDev) ) { + TQPDevCmdParam param[1]; + param[0].pen = &cpen; + if ( !pdev->cmd( TQPaintDevice::PdcSetPen, this, param ) || !hd ) + return; + } + + int ps = cpen.style(); + bool cacheIt = !testf(ClipOn|MonoDev|NoCache) && + (ps == NoPen || ps == SolidLine) && + cpen.width() == 0 && rop == CopyROP; + + bool obtained = FALSE; + bool internclipok = hasClipping(); + if ( cacheIt ) { + if ( gc ) { + if ( penRef ) + release_gc( penRef ); + else + free_gc( dpy, gc ); + } + obtained = obtain_gc(&penRef, &gc, cpen.color().pixel(scrn), dpy, scrn, + hd, clip_serial); + if ( !obtained && !penRef ) + gc = alloc_gc( dpy, scrn, hd, FALSE ); + } else { + if ( gc ) { + if ( penRef ) { + release_gc( penRef ); + penRef = 0; + gc = alloc_gc( dpy, scrn, hd, testf(MonoDev) ); + } else { + internclipok = TRUE; + } + } else { + gc = alloc_gc( dpy, scrn, hd, testf(MonoDev), testf(UsePrivateCx) ); + } + } + + if ( !internclipok ) { + if ( pdev == paintEventDevice && paintEventClipRegion ) { + if ( penRef &&((TQGCC*)penRef)->clip_serial < gc_cache_clip_serial ) { + x11SetClipRegion( dpy, gc, 0, rendhd, *paintEventClipRegion ); + ((TQGCC*)penRef)->clip_serial = gc_cache_clip_serial; + } else if ( !penRef ) { + x11SetClipRegion( dpy, gc, 0, rendhd, *paintEventClipRegion ); + } + } else if (penRef && ((TQGCC*)penRef)->clip_serial ) { + x11ClearClipRegion(dpy, gc, 0, rendhd); + ((TQGCC*)penRef)->clip_serial = 0; + } + } + + if ( obtained ) + return; + + char dashes[10]; // custom pen dashes + int dash_len = 0; // length of dash list + int s = LineSolid; + int cp = CapButt; + int jn = JoinMiter; + + /* + We are emulating Windows here. Windows treats cpen.width() == 1 + (or 0) as a very special case. The fudge variable unifies this + case with the general case. + */ + int dot = cpen.width(); // width of a dot + int fudge = 1; + bool allow_zero_lw = TRUE; + if ( dot <= 1 ) { + dot = 3; + fudge = 2; + } + + switch( ps ) { + case NoPen: + case SolidLine: + s = LineSolid; + break; + case DashLine: + dashes[0] = fudge * 3 * dot; + dashes[1] = fudge * dot; + dash_len = 2; + allow_zero_lw = FALSE; + break; + case DotLine: + dashes[0] = dot; + dashes[1] = dot; + dash_len = 2; + allow_zero_lw = FALSE; + break; + case DashDotLine: + dashes[0] = 3 * dot; + dashes[1] = fudge * dot; + dashes[2] = dot; + dashes[3] = fudge * dot; + dash_len = 4; + allow_zero_lw = FALSE; + break; + case DashDotDotLine: + dashes[0] = 3 * dot; + dashes[1] = dot; + dashes[2] = dot; + dashes[3] = dot; + dashes[4] = dot; + dashes[5] = dot; + dash_len = 6; + allow_zero_lw = FALSE; + break; + case FineDotLine: + dot = 1; + dashes[0] = dot; + dashes[1] = dot; + dash_len = 2; + allow_zero_lw = FALSE; + } + Q_ASSERT( dash_len <= (int) sizeof(dashes) ); + + switch ( cpen.capStyle() ) { + case SquareCap: + cp = CapProjecting; + break; + case RoundCap: + cp = CapRound; + break; + case FlatCap: + default: + cp = CapButt; + break; + } + switch ( cpen.joinStyle() ) { + case BevelJoin: + jn = JoinBevel; + break; + case RoundJoin: + jn = JoinRound; + break; + case MiterJoin: + default: + jn = JoinMiter; + break; + } + + XSetForeground( dpy, gc, cpen.color().pixel(scrn) ); + XSetBackground( dpy, gc, bg_col.pixel(scrn) ); + + if ( dash_len ) { // make dash list + XSetDashes( dpy, gc, 0, dashes, dash_len ); + s = bg_mode == TransparentMode ? LineOnOffDash : LineDoubleDash; + } + XSetLineAttributes( dpy, gc, + (! allow_zero_lw && cpen.width() == 0) ? 1 : cpen.width(), + s, cp, jn ); +} + + +void TQPainter::updateBrush() +{ + if (!isActive()) + return; + + static const uchar dense1_pat[] = { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff }; + static const uchar dense2_pat[] = { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff }; + static const uchar dense3_pat[] = { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee }; + static const uchar dense4_pat[] = { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa }; + static const uchar dense5_pat[] = { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 }; + static const uchar dense6_pat[] = { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 }; + static const uchar dense7_pat[] = { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 }; + static const uchar hor_pat[] = { // horizontal pattern + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar ver_pat[] = { // vertical pattern + 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, + 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, + 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, + 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, + 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, + 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20 }; + static const uchar cross_pat[] = { // cross pattern + 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0xff, 0xff, 0xff, + 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, + 0x08, 0x82, 0x20, 0xff, 0xff, 0xff, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, + 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0xff, 0xff, 0xff, + 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, + 0x08, 0x82, 0x20, 0xff, 0xff, 0xff, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20 }; + static const uchar bdiag_pat[] = { // backward diagonal pattern + 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x01, 0x01, + 0x80, 0x80, 0x40, 0x40, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, + 0x02, 0x02, 0x01, 0x01, 0x80, 0x80, 0x40, 0x40 }; + static const uchar fdiag_pat[] = { // forward diagonal pattern + 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x40, 0x40, + 0x80, 0x80, 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, + 0x20, 0x20, 0x40, 0x40, 0x80, 0x80, 0x01, 0x01 }; + static const uchar dcross_pat[] = { // diagonal cross pattern + 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x14, 0x14, 0x22, 0x22, 0x41, 0x41, + 0x80, 0x80, 0x41, 0x41, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x14, 0x14, + 0x22, 0x22, 0x41, 0x41, 0x80, 0x80, 0x41, 0x41 }; + static const uchar * const pat_tbl[] = { + dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat, + dense6_pat, dense7_pat, + hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat }; + + if ( testf(ExtDev) ) { + TQPDevCmdParam param[1]; + param[0].brush = &cbrush; + if ( !pdev->cmd( TQPaintDevice::PdcSetBrush, this, param ) || !hd ) + return; + } + + int bs = cbrush.style(); + bool cacheIt = !testf(ClipOn|MonoDev|NoCache) && + (bs == NoBrush || bs == SolidPattern) && + bro.x() == 0 && bro.y() == 0 && rop == CopyROP; + + bool obtained = FALSE; + bool internclipok = hasClipping(); + if ( cacheIt ) { + if ( gc_brush ) { + if ( brushRef ) + release_gc( brushRef ); + else + free_gc( dpy, gc_brush ); + } + obtained = obtain_gc(&brushRef, &gc_brush, cbrush.color().pixel(scrn), dpy, + scrn, hd, clip_serial); + if ( !obtained && !brushRef ) + gc_brush = alloc_gc( dpy, scrn, hd, FALSE ); + } else { + if ( gc_brush ) { + if ( brushRef ) { + release_gc( brushRef ); + brushRef = 0; + gc_brush = alloc_gc( dpy, scrn, hd, testf(MonoDev) ); + } else { + internclipok = TRUE; + } + } else { + gc_brush = alloc_gc( dpy, scrn, hd, testf(MonoDev), testf(UsePrivateCx)); + } + } + + if ( !internclipok ) { + if ( pdev == paintEventDevice && paintEventClipRegion ) { + if ( brushRef &&((TQGCC*)brushRef)->clip_serial < gc_cache_clip_serial ) { + x11SetClipRegion( dpy, gc_brush, 0, rendhd, *paintEventClipRegion ); + ((TQGCC*)brushRef)->clip_serial = gc_cache_clip_serial; + } else if ( !brushRef ){ + x11SetClipRegion( dpy, gc_brush, 0, rendhd, *paintEventClipRegion ); + } + } else if (brushRef && ((TQGCC*)brushRef)->clip_serial ) { + x11ClearClipRegion(dpy, gc_brush, 0, rendhd); + ((TQGCC*)brushRef)->clip_serial = 0; + } + } + + if ( obtained ) + return; + + const uchar *pat = 0; // pattern + int d = 0; // defalt pattern size: d*d + int s = FillSolid; + if ( bs >= Dense1Pattern && bs <= DiagCrossPattern ) { + pat = pat_tbl[ bs-Dense1Pattern ]; + if ( bs <= Dense7Pattern ) + d = 8; + else if ( bs <= CrossPattern ) + d = 24; + else + d = 16; + } + + XSetLineAttributes( dpy, gc_brush, 0, LineSolid, CapButt, JoinMiter ); + XSetForeground( dpy, gc_brush, cbrush.color().pixel(scrn) ); + XSetBackground( dpy, gc_brush, bg_col.pixel(scrn) ); + + if ( bs == CustomPattern || pat ) { + TQPixmap *pm; + if ( pat ) { + TQString key; + key.sprintf( "$qt-brush$%d", bs ); + pm = TQPixmapCache::find( key ); + bool del = FALSE; + if ( !pm ) { // not already in pm dict + pm = new TQBitmap( d, d, pat, TRUE ); + TQ_CHECK_PTR( pm ); + del = !TQPixmapCache::insert( key, pm ); + } + if ( cbrush.data->pixmap ) + delete cbrush.data->pixmap; + cbrush.data->pixmap = new TQPixmap( *pm ); + if (del) delete pm; + } + pm = cbrush.data->pixmap; + pm->x11SetScreen( scrn ); + if ( pm->depth() == 1 ) { + XSetStipple( dpy, gc_brush, pm->handle() ); + s = bg_mode == TransparentMode ? FillStippled : FillOpaqueStippled; + } else { + XSetTile( dpy, gc_brush, pm->handle() ); + s = FillTiled; + } + } + XSetFillStyle( dpy, gc_brush, s ); +} + + +/*! + Begins painting the paint device \a pd and returns TRUE if + successful; otherwise returns FALSE. If \a unclipped is TRUE, the + painting will not be clipped at the paint device's boundaries, + (although this is not supported by all platforms). + + The errors that can occur are serious problems, such as these: + + \code + p->begin( 0 ); // impossible - paint device cannot be 0 + + TQPixmap pm( 0, 0 ); + p->begin( pm ); // impossible - pm.isNull(); + + p->begin( myWidget ); + p2->begin( myWidget ); // impossible - only one painter at a time + \endcode + + Note that most of the time, you can use one of the constructors + instead of begin(), and that end() is automatically done at + destruction. + + \warning A paint device can only be painted by one painter at a + time. + + \sa end(), flush() +*/ + +bool TQPainter::begin( const TQPaintDevice *pd, bool unclipped ) +{ + if ( isActive() ) { // already active painting +#if defined(QT_CHECK_STATE) + tqWarning( "TQPainter::begin: Painter is already active." + "\n\tYou must end() the painter before a second begin()" ); +#endif + return FALSE; + } + if ( pd == 0 ) { +#if defined(QT_CHECK_NULL) + tqWarning( "TQPainter::begin: Paint device cannot be null" ); +#endif + return FALSE; + } + + TQPixmap::x11SetDefaultScreen( pd->x11Screen() ); + + const TQWidget *copyFrom = 0; + pdev = redirect( (TQPaintDevice*)pd ); + if ( pdev ) { // redirected paint device? + if ( pd->devType() == TQInternal::Widget ) + copyFrom = (const TQWidget *)pd; // copy widget settings + } else { + pdev = (TQPaintDevice*)pd; + } + + if ( pdev->isExtDev() && pdev->paintingActive() ) { + // somebody else is already painting +#if defined(QT_CHECK_STATE) + tqWarning( "TQPainter::begin: Another TQPainter is already painting " + "this device;\n\tAn extended paint device can only be " + "painted by one TQPainter at a time." ); +#endif + return FALSE; + } + + bool reinit = flags != IsStartingUp; // 2nd or 3rd etc. time called + flags = IsActive | DirtyFont; // init flags + int dt = pdev->devType(); // get the device type + + if ( (pdev->devFlags & TQInternal::ExternalDevice) != 0 ) + setf(ExtDev); + else if ( dt == TQInternal::Pixmap ) // device is a pixmap + ((TQPixmap*)pdev)->detach(); // will modify it + + dpy = pdev->x11Display(); // get display variable + scrn = pdev->x11Screen(); // get screen variable + hd = pdev->handle(); // get handle to drawable + rendhd = pdev->rendhd; + + if ( testf(ExtDev) ) { // external device + if ( !pdev->cmd( TQPaintDevice::PdcBegin, this, 0 ) ) { + // could not begin painting + if ( reinit ) + clearf( IsActive | DirtyFont ); + else + flags = IsStartingUp; + pdev = 0; + return FALSE; + } + if ( tabstops ) // update tabstops for device + setTabStops( tabstops ); + if ( tabarray ) // update tabarray for device + setTabArray( tabarray ); + } + + if ( pdev->x11Depth() != pdev->x11AppDepth( scrn ) ) { // non-standard depth + setf(NoCache); + setf(UsePrivateCx); + } + + pdev->painters++; // also tell paint device + bro = curPt = TQPoint( 0, 0 ); + if ( reinit ) { + bg_mode = TransparentMode; // default background mode + rop = CopyROP; // default ROP + wxmat.reset(); // reset world xform matrix + xmat.reset(); + ixmat.reset(); + txop = txinv = 0; + if ( dt != TQInternal::Widget ) { + TQFont defaultFont; // default drawing tools + TQPen defaultPen; + TQBrush defaultBrush; + cfont = defaultFont; // set these drawing tools + cpen = defaultPen; + cbrush = defaultBrush; + bg_col = white; // default background color + } + } + wx = wy = vx = vy = 0; // default view origins + + if ( dt == TQInternal::Widget ) { // device is a widget + TQWidget *w = (TQWidget*)pdev; + cfont = w->font(); // use widget font + cpen = TQPen( w->foregroundColor() ); // use widget fg color + if ( reinit ) { + TQBrush defaultBrush; + cbrush = defaultBrush; + } + bg_col = w->backgroundColor(); // use widget bg color + ww = vw = w->width(); // default view size + wh = vh = w->height(); + if ( unclipped || w->testWFlags( WPaintUnclipped ) ) { // paint direct on device + setf( NoCache ); + setf(UsePrivateCx); + updatePen(); + updateBrush(); + XSetSubwindowMode( dpy, gc, IncludeInferiors ); + XSetSubwindowMode( dpy, gc_brush, IncludeInferiors ); +#ifndef TQT_NO_XFTFREETYPE + if (rendhd) + XftDrawSetSubwindowMode((XftDraw *) rendhd, IncludeInferiors); +#endif + } + } else if ( dt == TQInternal::Pixmap ) { // device is a pixmap + TQPixmap *pm = (TQPixmap*)pdev; + if ( pm->isNull() ) { +#if defined(QT_CHECK_NULL) + tqWarning( "TQPainter::begin: Cannot paint null pixmap" ); +#endif + end(); + return FALSE; + } + bool mono = pm->depth() == 1; // monochrome bitmap + if ( mono ) { + setf( MonoDev ); + bg_col = color0; + cpen.setColor( color1 ); + } + ww = vw = pm->width(); // default view size + wh = vh = pm->height(); + } else if ( testf(ExtDev) ) { // external device + ww = vw = pdev->metric( TQPaintDeviceMetrics::PdmWidth ); + wh = vh = pdev->metric( TQPaintDeviceMetrics::PdmHeight ); + } + if ( ww == 0 ) + ww = wh = vw = vh = 1024; + if ( copyFrom ) { // copy redirected widget + cfont = copyFrom->font(); + cpen = TQPen( copyFrom->foregroundColor() ); + bg_col = copyFrom->backgroundColor(); + } + if ( testf(ExtDev) ) { // external device + setBackgroundColor( bg_col ); // default background color + setBackgroundMode( TransparentMode ); // default background mode + setRasterOp( CopyROP ); // default raster operation + } + clip_serial = gc_cache_clip_serial++; + updateBrush(); + updatePen(); + return TRUE; +} + +/*! + Ends painting. Any resources used while painting are released. + + Note that while you mostly don't need to call end(), the + destructor will do it, there is at least one common case when it + is needed, namely double buffering. + + \code + TQPainter p( myPixmap, this ) + // ... + p.end(); // stops drawing on myPixmap + p.begin( this ); + p.drawPixmap( 0, 0, myPixmap ); + \endcode + + Since you can't draw a TQPixmap while it is being painted, it is + necessary to close the active painter. + + \sa begin(), isActive() +*/ + +bool TQPainter::end() // end painting +{ + if ( !isActive() ) { +#if defined(QT_CHECK_STATE) + tqWarning( "TQPainter::end: Missing begin() or begin() failed" ); +#endif + return FALSE; + } + killPStack(); + + //#### This should not be necessary: + if ( pdev->devType() == TQInternal::Widget && // ##### + ((TQWidget*)pdev)->testWFlags(WPaintUnclipped) ) { + if ( gc ) + XSetSubwindowMode( dpy, gc, ClipByChildren ); + if ( gc_brush ) + XSetSubwindowMode( dpy, gc_brush, ClipByChildren ); + } + + if ( gc_brush ) { // restore brush gc + if ( brushRef ) { + release_gc( brushRef ); + brushRef = 0; + } else { + free_gc( dpy, gc_brush, testf(UsePrivateCx) ); + } + gc_brush = 0; + + } + if ( gc ) { // restore pen gc + if ( penRef ) { + release_gc( penRef ); + penRef = 0; + } else { + free_gc( dpy, gc, testf(UsePrivateCx) ); + } + gc = 0; + } + + if ( testf(ExtDev) ) + pdev->cmd( TQPaintDevice::PdcEnd, this, 0 ); + +#ifndef TQT_NO_XFTFREETYPE + if (rendhd) { + // reset clipping/subwindow mode on our render picture + XftDrawSetClip((XftDraw *) rendhd, None); + XftDrawSetSubwindowMode((XftDraw *) rendhd, ClipByChildren); + } +#endif // TQT_NO_XFTFREETYPE + + if ( pfont ) { + delete pfont; + pfont = 0; + } + + flags = 0; + pdev->painters--; + pdev = 0; + dpy = 0; + return TRUE; +} + +/*! + Flushes any buffered drawing operations inside the region \a + region using clipping mode \a cm. + + The flush may update the whole device if the platform does not + support flushing to a specified region. + + \sa flush() CoordinateMode +*/ + +void TQPainter::flush(const TQRegion &rgn, CoordinateMode m) +{ + if ( testf(ExtDev) ) { + TQPDevCmdParam param[2]; + param[0].rgn = &rgn; + param[1].ival = m; + pdev->cmd( TQPaintDevice::PdcFlushRegion, this, param ); + return; + } + + flush(); +} + + +/*! + \overload + + Flushes any buffered drawing operations. +*/ + +void TQPainter::flush() +{ + if ( testf(ExtDev) ) { + pdev->cmd( TQPaintDevice::PdcFlush, this, 0 ); + return; + } + + if ( isActive() && dpy ) + XFlush( dpy ); +} + + +/*! + Sets the background color of the painter to \a c. + + The background color is the color that is filled in when drawing + opaque text, stippled lines and bitmaps. The background color has + no effect in transparent background mode (which is the default). + + \sa backgroundColor() setBackgroundMode() BackgroundMode +*/ + +void TQPainter::setBackgroundColor( const TQColor &c ) +{ + if ( !isActive() ) { +#if defined(QT_CHECK_STATE) + tqWarning( "TQPainter::setBackgroundColor: Call begin() first" ); +#endif + return; + } + bg_col = c; + if ( testf(ExtDev) ) { + TQPDevCmdParam param[1]; + param[0].color = &bg_col; + if ( !pdev->cmd( TQPaintDevice::PdcSetBkColor, this, param ) || !hd ) + return; + } + if ( !penRef ) + updatePen(); // update pen setting + if ( !brushRef ) + updateBrush(); // update brush setting +} + +/*! + Sets the background mode of the painter to \a m, which must be + either \c TransparentMode (the default) or \c OpaqueMode. + + Transparent mode draws stippled lines and text without setting the + background pixels. Opaque mode fills these space with the current + background color. + + Note that in order to draw a bitmap or pixmap transparently, you + must use TQPixmap::setMask(). + + \sa backgroundMode(), setBackgroundColor() +*/ + +void TQPainter::setBackgroundMode( BGMode m ) +{ + if ( !isActive() ) { +#if defined(QT_CHECK_STATE) + tqWarning( "TQPainter::setBackgroundMode: Call begin() first" ); +#endif + return; + } + if ( m != TransparentMode && m != OpaqueMode ) { +#if defined(QT_CHECK_RANGE) + tqWarning( "TQPainter::setBackgroundMode: Invalid mode" ); +#endif + return; + } + bg_mode = m; + if ( testf(ExtDev) ) { + TQPDevCmdParam param[1]; + param[0].ival = m; + if ( !pdev->cmd( TQPaintDevice::PdcSetBkMode, this, param ) || !hd ) + return; + } + if ( !penRef ) + updatePen(); // update pen setting + if ( !brushRef ) + updateBrush(); // update brush setting +} + +static const short ropCodes[] = { // ROP translation table + GXcopy, // CopyROP + GXor, // OrROP + GXxor, // XorROP + GXandInverted, // NotAndROP EraseROP + GXcopyInverted, // NotCopyROP + GXorInverted, // NotOrROP + GXequiv, // NotXorROP + GXand, // AndROP + GXinvert, // NotROP + GXclear, // ClearROP + GXset, // SetROP + GXnoop, // NopROP + GXandReverse, // AndNotROP + GXorReverse, // OrNotROP + GXnand, // NandROP + GXnor // NorROP +}; + + +/*! + Sets the \link TQt::RasterOp raster operation \endlink to \a r. + The default is \c CopyROP. + + \sa rasterOp() TQt::RasterOp +*/ + +void TQPainter::setRasterOp( RasterOp r ) +{ + if ( !isActive() ) { +#if defined(QT_CHECK_STATE) + tqWarning( "TQPainter::setRasterOp: Call begin() first" ); +#endif + return; + } + if ( (uint)r > LastROP ) { +#if defined(QT_CHECK_RANGE) + tqWarning( "TQPainter::setRasterOp: Invalid ROP code" ); +#endif + return; + } + rop = r; + if ( testf(ExtDev) ) { + TQPDevCmdParam param[1]; + param[0].ival = r; + if ( !pdev->cmd( TQPaintDevice::PdcSetROP, this, param ) || !hd ) + return; + } + if ( penRef ) + updatePen(); // get non-cached pen GC + if ( brushRef ) + updateBrush(); // get non-cached brush GC + XSetFunction( dpy, gc, ropCodes[rop] ); + XSetFunction( dpy, gc_brush, ropCodes[rop] ); +} + +// ### matthias - true? + +/*! + Sets the brush origin to \a (x, y). + + The brush origin specifies the (0, 0) coordinate of the painter's + brush. This setting only applies to pattern brushes and pixmap + brushes. + + \sa brushOrigin() +*/ + +void TQPainter::setBrushOrigin( int x, int y ) +{ + if ( !isActive() ) { +#if defined(QT_CHECK_STATE) + tqWarning( "TQPainter::setBrushOrigin: Call begin() first" ); +#endif + return; + } + bro = TQPoint(x, y); + if ( testf(ExtDev) ) { + TQPDevCmdParam param[1]; + param[0].point = &bro; + if ( !pdev->cmd( TQPaintDevice::PdcSetBrushOrigin, this, param ) || + !hd ) + return; + } + if ( brushRef ) + updateBrush(); // get non-cached brush GC + XSetTSOrigin( dpy, gc_brush, x, y ); +} + + +/*! + Enables clipping if \a enable is TRUE, or disables clipping if \a + enable is FALSE. + + \sa hasClipping(), setClipRect(), setClipRegion() +*/ + +void TQPainter::setClipping( bool enable ) +{ + if ( !isActive() ) { +#if defined(QT_CHECK_STATE) + tqWarning( "TQPainter::setClipping: Will be reset by begin()" ); +#endif + return; + } + + if ( enable == testf(ClipOn) ) + return; + + setf( ClipOn, enable ); + if ( testf(ExtDev) ) { + if ( block_ext ) + return; + TQPDevCmdParam param[1]; + param[0].ival = enable; + if ( !pdev->cmd( TQPaintDevice::PdcSetClip, this, param ) || !hd ) + return; + } + if ( enable ) { + TQRegion rgn = crgn; + if ( pdev == paintEventDevice && paintEventClipRegion ) + rgn = rgn.intersect( *paintEventClipRegion ); + if ( penRef ) + updatePen(); + if ( brushRef ) + updateBrush(); + x11SetClipRegion( dpy, gc, gc_brush, rendhd, rgn ); + } else { + if ( pdev == paintEventDevice && paintEventClipRegion ) { + x11SetClipRegion( dpy, gc, gc_brush , rendhd, *paintEventClipRegion ); + } else { + x11ClearClipRegion(dpy, gc, gc_brush, rendhd); + } + } +} + + +/*! + \overload + + Sets the clip region to the rectangle \a r and enables clipping. + The clip mode is set to \a m. + + \sa CoordinateMode +*/ + +void TQPainter::setClipRect( const TQRect &r, CoordinateMode m ) +{ + setClipRegion( TQRegion( r ), m ); +} + +/*! + Sets the clip region to \a rgn and enables clipping. The clip mode + is set to \a m. + + Note that the clip region is given in physical device coordinates + and \e not subject to any \link coordsys.html coordinate + transformation.\endlink + + \sa setClipRect(), clipRegion(), setClipping() CoordinateMode +*/ + +void TQPainter::setClipRegion( const TQRegion &rgn, CoordinateMode m ) +{ +#if defined(QT_CHECK_STATE) + if ( !isActive() ) + tqWarning( "TQPainter::setClipRegion: Will be reset by begin()" ); +#endif + if ( m == CoordDevice ) + crgn = rgn; + else + crgn = xmat * rgn; + + if ( testf(ExtDev) ) { + if ( block_ext ) + return; + TQPDevCmdParam param[2]; + param[0].rgn = &rgn; + param[1].ival = m; + if ( !pdev->cmd( TQPaintDevice::PdcSetClipRegion, this, param ) ) + return; // device cannot clip + } + clearf( ClipOn ); // be sure to update clip rgn + setClipping( TRUE ); +} + + +/*! + \internal + + Internal function for drawing a polygon. +*/ + +void TQPainter::drawPolyInternal( const TQPointArray &a, bool close ) +{ + if ( a.size() < 2 ) + return; + + int x1, y1, x2, y2; // connect last to first point + a.point( a.size()-1, &x1, &y1 ); + a.point( 0, &x2, &y2 ); + bool do_close = close && !(x1 == x2 && y1 == y2); + + if ( close && cbrush.style() != NoBrush ) { // draw filled polygon + XFillPolygon( dpy, hd, gc_brush, (XPoint*)a.shortPoints(), a.size(), + Nonconvex, CoordModeOrigin ); + if ( cpen.style() == NoPen ) { // draw fake outline + XDrawLines( dpy, hd, gc_brush, (XPoint*)a.shortPoints(), a.size(), + CoordModeOrigin ); + if ( do_close ) + XDrawLine( dpy, hd, gc_brush, x1, y1, x2, y2 ); + } + } + if ( cpen.style() != NoPen ) { // draw outline + XDrawLines( dpy, hd, gc, (XPoint*)a.shortPoints(), a.size(), + CoordModeOrigin); + if ( do_close ) + XDrawLine( dpy, hd, gc, x1, y1, x2, y2 ); + } +} + + +/*! + Draws/plots a single point at \a (x, y) using the current pen. + + \sa TQPen +*/ + +void TQPainter::drawPoint( int x, int y ) +{ + if ( !isActive() ) + return; + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + TQPDevCmdParam param[1]; + TQPoint p( x, y ); + param[0].point = &p; + if ( !pdev->cmd( TQPaintDevice::PdcDrawPoint, this, param ) || + !hd ) + return; + } + map( x, y, &x, &y ); + } + if ( cpen.style() != NoPen ) + XDrawPoint( dpy, hd, gc, x, y ); +} + + +/*! + Draws/plots an array of points, \a a, using the current pen. + + If \a index is non-zero (the default is zero) only points from \a + index are drawn. If \a npoints is negative (the default) the rest + of the points from \a index are drawn. If \a npoints is zero or + greater, \a npoints points are drawn. + + \warning On X11, coordinates that do not fit into 16-bit signed + values are truncated. This limitation is expected to go away in + TQt 4. +*/ + +void TQPainter::drawPoints( const TQPointArray& a, int index, int npoints ) +{ + if ( npoints < 0 ) + npoints = a.size() - index; + if ( index + npoints > (int)a.size() ) + npoints = a.size() - index; + if ( !isActive() || npoints < 1 || index < 0 ) + return; + TQPointArray pa = a; + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + TQPDevCmdParam param[1]; + for (int i=0; i<npoints; i++) { + TQPoint p( pa[index+i].x(), pa[index+i].y() ); + param[0].point = &p; + if ( !pdev->cmd( TQPaintDevice::PdcDrawPoint, this, param )) + return; + } + if ( !hd ) return; + } + if ( txop != TxNone ) { + pa = xForm( a, index, npoints ); + if ( pa.size() != a.size() ) { + index = 0; + npoints = pa.size(); + } + } + } + if ( cpen.style() != NoPen ) + XDrawPoints( dpy, hd, gc, (XPoint*)(pa.shortPoints( index, npoints )), + npoints, CoordModeOrigin ); +} + + +/*! \obsolete + Sets the current pen position to \a (x, y) + + \sa lineTo(), pos() +*/ + +void TQPainter::moveTo( int x, int y ) +{ + if ( !isActive() ) + return; + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + TQPDevCmdParam param[1]; + TQPoint p( x, y ); + param[0].point = &p; + if ( !pdev->cmd( TQPaintDevice::PdcMoveTo, this, param ) || !hd ) + return; + } + } + curPt = TQPoint( x, y ); +} + +/*! \obsolete + Use drawLine() instead. + + Draws a line from the current pen position to \a (x, y) and sets + \a (x, y) to be the new current pen position. + + \sa TQPen moveTo(), drawLine(), pos() +*/ + +void TQPainter::lineTo( int x, int y ) +{ + if ( !isActive() ) + return; + int cx = curPt.x(), cy = curPt.y(); + curPt = TQPoint( x, y ); + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + TQPDevCmdParam param[1]; + TQPoint p( x, y ); + param[0].point = &p; + if ( !pdev->cmd( TQPaintDevice::PdcLineTo, this, param ) || !hd ) + return; + } + map( x, y, &x, &y ); + map( cx, cy, &cx, &cy ); + } + if ( cpen.style() != NoPen ) + XDrawLine( dpy, hd, gc, cx, cy, x, y ); +} + +/*! + Draws a line from (\a x1, \a y1) to (\a x2, \a y2) and sets the + current pen position to (\a x2, \a y2). + + \sa pen() +*/ + +void TQPainter::drawLine( int x1, int y1, int x2, int y2 ) +{ + if ( !isActive() ) + return; + curPt = TQPoint( x2, y2 ); + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + TQPDevCmdParam param[2]; + TQPoint p1(x1, y1), p2(x2, y2); + param[0].point = &p1; + param[1].point = &p2; + if ( !pdev->cmd( TQPaintDevice::PdcDrawLine, this, param ) || !hd ) + return; + } + map( x1, y1, &x1, &y1 ); + map( x2, y2, &x2, &y2 ); + } + if ( cpen.style() != NoPen ) + XDrawLine( dpy, hd, gc, x1, y1, x2, y2 ); +} + + + +/*! + Draws a rectangle with upper left corner at \a (x, y) and with + width \a w and height \a h. + + \sa TQPen, drawRoundRect() +*/ + +void TQPainter::drawRect( int x, int y, int w, int h ) +{ + if ( !isActive() ) + return; + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + TQPDevCmdParam param[1]; + TQRect r( x, y, w, h ); + param[0].rect = &r; + if ( !pdev->cmd( TQPaintDevice::PdcDrawRect, this, param ) || !hd ) + return; + } + if ( txop == TxRotShear ) { // rotate/shear polygon + TQPointArray pa = xmat.mapToPolygon( TQRect(x, y, w, h) ); + pa.resize( 5 ); + pa.setPoint( 4, pa.point( 0 ) ); + drawPolyInternal( pa ); + return; + } + map( x, y, w, h, &x, &y, &w, &h ); + } + if ( w <= 0 || h <= 0 ) { + if ( w == 0 || h == 0 ) + return; + fix_neg_rect( &x, &y, &w, &h ); + } + if ( cbrush.style() != NoBrush ) { + if ( cpen.style() == NoPen ) { + XFillRectangle( dpy, hd, gc_brush, x, y, w, h ); + return; + } + int lw = cpen.width(); + int lw2 = (lw+1)/2; + if ( w > lw && h > lw ) + XFillRectangle( dpy, hd, gc_brush, x+lw2, y+lw2, w-lw-1, h-lw-1 ); + } + if ( cpen.style() != NoPen ) + XDrawRectangle( dpy, hd, gc, x, y, w-1, h-1 ); +} + +/*! + \overload + + Draws a Windows focus rectangle with upper left corner at (\a x, + \a y) and with width \a w and height \a h. + + This function draws a stippled XOR rectangle that is used to + indicate keyboard focus (when TQApplication::style() is \c + WindowStyle). + + \warning This function draws nothing if the coordinate system has + been \link rotate() rotated\endlink or \link shear() + sheared\endlink. + + \sa drawRect(), TQApplication::style() +*/ + +void TQPainter::drawWinFocusRect( int x, int y, int w, int h ) +{ + drawWinFocusRect( x, y, w, h, TRUE, color0 ); +} + +/*! + Draws a Windows focus rectangle with upper left corner at (\a x, + \a y) and with width \a w and height \a h using a pen color that + contrasts with \a bgColor. + + This function draws a stippled rectangle (XOR is not used) that is + used to indicate keyboard focus (when the TQApplication::style() is + \c WindowStyle). + + The pen color used to draw the rectangle is either white or black + depending on the color of \a bgColor (see TQColor::gray()). + + \warning This function draws nothing if the coordinate system has + been \link rotate() rotated\endlink or \link shear() + sheared\endlink. + + \sa drawRect(), TQApplication::style() +*/ + +void TQPainter::drawWinFocusRect( int x, int y, int w, int h, + const TQColor &bgColor ) +{ + drawWinFocusRect( x, y, w, h, FALSE, bgColor ); +} + + +/*! + \internal +*/ + +void TQPainter::drawWinFocusRect( int x, int y, int w, int h, + bool xorPaint, const TQColor &bgColor ) +{ + if ( !isActive() || txop == TxRotShear ) + return; + static char winfocus_line[] = { 1, 1 }; + + TQPen old_pen = cpen; + TQBrush old_brush = cbrush; + RasterOp old_rop = (RasterOp)rop; + + if ( xorPaint ) { + if ( TQColor::numBitPlanes() <= 8 ) { + setPen( TQPen(color1, 0, TQt::FineDotLine) ); + } + else if ( TQColor::numBitPlanes() <= 8 ) { + setPen( TQPen(white, 0, TQt::FineDotLine) ); + } + else { + setPen( TQPen(TQColor(tqRgba(255,255,255,0)), 0, TQt::FineDotLine) ); + } + setRasterOp( XorROP ); + } + else { + if ( tqGray( bgColor.rgb() ) < 128 ) { + setPen( TQPen(white, 0, TQt::FineDotLine) ); + } + else { + setPen( TQPen(black, 0, TQt::FineDotLine) ); + } + } + + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + TQPDevCmdParam param[1]; + TQRect r( x, y, w-1, h-1 ); + TQBrush noBrush; + setBrush( noBrush ); + param[0].rect = &r; + if ( !pdev->cmd( TQPaintDevice::PdcDrawRect, this, param ) || !hd) { + setRasterOp( old_rop ); + setPen( old_pen ); + setBrush( old_brush ); + return; + } + } + map( x, y, w, h, &x, &y, &w, &h ); + } + if ( w <= 0 || h <= 0 ) { + if ( w == 0 || h == 0 ) + return; + fix_neg_rect( &x, &y, &w, &h ); + } + XSetDashes( dpy, gc, 0, winfocus_line, 2 ); + XSetLineAttributes( dpy, gc, 1, LineOnOffDash, CapButt, JoinMiter ); + + XDrawRectangle( dpy, hd, gc, x, y, w-1, h-1 ); + + XSetLineAttributes( dpy, gc, 0, LineSolid, CapButt, JoinMiter ); + setRasterOp( old_rop ); + setPen( old_pen ); +} + + +/*! + Draws a rectangle with rounded corners at \a (x, y), with width \a + w and height \a h. + + The \a xRnd and \a yRnd arguments specify how rounded the corners + should be. 0 is angled corners, 99 is maximum roundedness. + + The width and height include all of the drawn lines. + + \sa drawRect(), TQPen +*/ + +void TQPainter::drawRoundRect( int x, int y, int w, int h, int xRnd, int yRnd ) +{ + if ( !isActive() ) + return; + if ( xRnd <= 0 || yRnd <= 0 ) { + drawRect( x, y, w, h ); // draw normal rectangle + return; + } + if ( xRnd >= 100 ) // fix ranges + xRnd = 99; + if ( yRnd >= 100 ) + yRnd = 99; + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + TQPDevCmdParam param[3]; + TQRect r( x, y, w, h ); + param[0].rect = &r; + param[1].ival = xRnd; + param[2].ival = yRnd; + if ( !pdev->cmd( TQPaintDevice::PdcDrawRoundRect, this, param ) || + !hd ) + return; + } + if ( txop == TxRotShear ) { // rotate/shear polygon + if ( w <= 0 || h <= 0 ) + fix_neg_rect( &x, &y, &w, &h ); + w--; + h--; + int rxx = w*xRnd/200; + int ryy = h*yRnd/200; + // were there overflows? + if ( rxx < 0 ) + rxx = w/200*xRnd; + if ( ryy < 0 ) + ryy = h/200*yRnd; + int rxx2 = 2*rxx; + int ryy2 = 2*ryy; + TQPointArray a[4]; + a[0].makeArc( x, y, rxx2, ryy2, 1*16*90, 16*90, xmat ); + a[1].makeArc( x, y+h-ryy2, rxx2, ryy2, 2*16*90, 16*90, xmat ); + a[2].makeArc( x+w-rxx2, y+h-ryy2, rxx2, ryy2, 3*16*90, 16*90, xmat ); + a[3].makeArc( x+w-rxx2, y, rxx2, ryy2, 0*16*90, 16*90, xmat ); + // ### is there a better way to join TQPointArrays? + TQPointArray aa; + aa.resize( a[0].size() + a[1].size() + a[2].size() + a[3].size() ); + uint j = 0; + for ( int k=0; k<4; k++ ) { + for ( uint i=0; i<a[k].size(); i++ ) { + aa.setPoint( j, a[k].point(i) ); + j++; + } + } + drawPolyInternal( aa ); + return; + } + map( x, y, w, h, &x, &y, &w, &h ); + } + w--; + h--; + if ( w <= 0 || h <= 0 ) { + if ( w == 0 || h == 0 ) + return; + fix_neg_rect( &x, &y, &w, &h ); + } + int rx = (w*xRnd)/200; + int ry = (h*yRnd)/200; + int rx2 = 2*rx; + int ry2 = 2*ry; + if ( cbrush.style() != NoBrush ) { // draw filled round rect + int dp, ds; + if ( cpen.style() == NoPen ) { + dp = 0; + ds = 1; + } + else { + dp = 1; + ds = 0; + } +#define SET_ARC(px, py, w, h, a1, a2) \ + a->x=px; a->y=py; a->width=w; a->height=h; a->angle1=a1; a->angle2=a2; a++ + XArc arcs[4]; + XArc *a = arcs; + SET_ARC( x+w-rx2, y, rx2, ry2, 0, 90*64 ); + SET_ARC( x, y, rx2, ry2, 90*64, 90*64 ); + SET_ARC( x, y+h-ry2, rx2, ry2, 180*64, 90*64 ); + SET_ARC( x+w-rx2, y+h-ry2, rx2, ry2, 270*64, 90*64 ); + XFillArcs( dpy, hd, gc_brush, arcs, 4 ); +#undef SET_ARC +#define SET_RCT(px, py, w, h) \ + r->x=px; r->y=py; r->width=w; r->height=h; r++ + XRectangle rects[3]; + XRectangle *r = rects; + SET_RCT( x+rx, y+dp, w-rx2, ry ); + SET_RCT( x+dp, y+ry, w+ds, h-ry2 ); + SET_RCT( x+rx, y+h-ry, w-rx2, ry+ds ); + XFillRectangles( dpy, hd, gc_brush, rects, 3 ); +#undef SET_RCT + } + if ( cpen.style() != NoPen ) { // draw outline +#define SET_ARC(px, py, w, h, a1, a2) \ + a->x=px; a->y=py; a->width=w; a->height=h; a->angle1=a1; a->angle2=a2; a++ + XArc arcs[4]; + XArc *a = arcs; + SET_ARC( x+w-rx2, y, rx2, ry2, 0, 90*64 ); + SET_ARC( x, y, rx2, ry2, 90*64, 90*64 ); + SET_ARC( x, y+h-ry2, rx2, ry2, 180*64, 90*64 ); + SET_ARC( x+w-rx2, y+h-ry2, rx2, ry2, 270*64, 90*64 ); + XDrawArcs( dpy, hd, gc, arcs, 4 ); +#undef SET_ARC +#define SET_SEG(xp1, yp1, xp2, yp2) \ + s->x1=xp1; s->y1=yp1; s->x2=xp2; s->y2=yp2; s++ + XSegment segs[4]; + XSegment *s = segs; + SET_SEG( x+rx, y, x+w-rx, y ); + SET_SEG( x+rx, y+h, x+w-rx, y+h ); + SET_SEG( x, y+ry, x, y+h-ry ); + SET_SEG( x+w, y+ry, x+w, y+h-ry ); + XDrawSegments( dpy, hd, gc, segs, 4 ); +#undef SET_SET + } +} + +/*! + Draws an ellipse with center at \a (x + w/2, y + h/2) and size \a + (w, h). +*/ + +void TQPainter::drawEllipse( int x, int y, int w, int h ) +{ + if ( !isActive() ) + return; + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + TQPDevCmdParam param[1]; + TQRect r( x, y, w, h ); + param[0].rect = &r; + if ( !pdev->cmd( TQPaintDevice::PdcDrawEllipse, this, param ) || + !hd ) + return; + } + if ( txop == TxRotShear ) { // rotate/shear polygon + TQPointArray a; + a.makeArc( x, y, w, h, 0, 360*16, xmat ); + drawPolyInternal( a ); + return; + } + map( x, y, w, h, &x, &y, &w, &h ); + } + if ( w <= 0 || h <= 0 ) { + if ( w == 0 || h == 0 ) + return; + fix_neg_rect( &x, &y, &w, &h ); + } + if ( w == 1 && h == 1 ) { + XDrawPoint( dpy, hd, (cpen.style() == NoPen)?gc_brush:gc, x, y ); + return; + } + w--; + h--; + if ( cbrush.style() != NoBrush ) { // draw filled ellipse + XFillArc( dpy, hd, gc_brush, x, y, w, h, 0, 360*64 ); + if ( cpen.style() == NoPen ) { + XDrawArc( dpy, hd, gc_brush, x, y, w, h, 0, 360*64 ); + return; + } + } + if ( cpen.style() != NoPen ) // draw outline + XDrawArc( dpy, hd, gc, x, y, w, h, 0, 360*64 ); +} + + +/*! + Draws an arc defined by the rectangle \a (x, y, w, h), the start + angle \a a and the arc length \a alen. + + The angles \a a and \a alen are 1/16th of a degree, i.e. a full + circle equals 5760 (16*360). Positive values of \a a and \a alen + mean counter-clockwise while negative values mean the clockwise + direction. Zero degrees is at the 3 o'clock position. + + Example: + \code + TQPainter p( myWidget ); + p.drawArc( 10,10, 70,100, 100*16, 160*16 ); // draws a "(" arc + \endcode + + \sa drawPie(), drawChord() +*/ + +void TQPainter::drawArc( int x, int y, int w, int h, int a, int alen ) +{ + if ( !isActive() ) + return; + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + TQPDevCmdParam param[3]; + TQRect r( x, y, w, h ); + param[0].rect = &r; + param[1].ival = a; + param[2].ival = alen; + if ( !pdev->cmd( TQPaintDevice::PdcDrawArc, this, param ) || + !hd ) + return; + } + if ( txop == TxRotShear ) { // rotate/shear + TQPointArray pa; + pa.makeArc( x, y, w, h, a, alen, xmat ); // arc polyline + drawPolyInternal( pa, FALSE ); + return; + } + map( x, y, w, h, &x, &y, &w, &h ); + } + w--; + h--; + if ( w <= 0 || h <= 0 ) { + if ( w == 0 || h == 0 ) + return; + fix_neg_rect( &x, &y, &w, &h ); + } + if ( cpen.style() != NoPen ) + XDrawArc( dpy, hd, gc, x, y, w, h, a*4, alen*4 ); +} + + +/*! + Draws a pie defined by the rectangle \a (x, y, w, h), the start + angle \a a and the arc length \a alen. + + The pie is filled with the current brush(). + + The angles \a a and \a alen are 1/16th of a degree, i.e. a full + circle equals 5760 (16*360). Positive values of \a a and \a alen + mean counter-clockwise while negative values mean the clockwise + direction. Zero degrees is at the 3 o'clock position. + + \sa drawArc(), drawChord() +*/ + +void TQPainter::drawPie( int x, int y, int w, int h, int a, int alen ) +{ + // Make sure "a" is 0..360*16, as otherwise a*4 may overflow 16 bits. + if ( a > (360*16) ) { + a = a % (360*16); + } else if ( a < 0 ) { + a = a % (360*16); + if ( a < 0 ) a += (360*16); + } + + if ( !isActive() ) + return; + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + TQPDevCmdParam param[3]; + TQRect r( x, y, w, h ); + param[0].rect = &r; + param[1].ival = a; + param[2].ival = alen; + if ( !pdev->cmd( TQPaintDevice::PdcDrawPie, this, param ) || !hd ) + return; + } + if ( txop == TxRotShear ) { // rotate/shear + TQPointArray pa; + pa.makeArc( x, y, w, h, a, alen, xmat ); // arc polyline + int n = pa.size(); + int cx, cy; + xmat.map(x+w/2, y+h/2, &cx, &cy); + pa.resize( n+2 ); + pa.setPoint( n, cx, cy ); // add legs + pa.setPoint( n+1, pa.at(0) ); + drawPolyInternal( pa ); + return; + } + map( x, y, w, h, &x, &y, &w, &h ); + } + XSetArcMode( dpy, gc_brush, ArcPieSlice ); + w--; + h--; + if ( w <= 0 || h <= 0 ) { + if ( w == 0 || h == 0 ) + return; + fix_neg_rect( &x, &y, &w, &h ); + } + + GC g = gc; + bool nopen = cpen.style() == NoPen; + + if ( cbrush.style() != NoBrush ) { // draw filled pie + XFillArc( dpy, hd, gc_brush, x, y, w, h, a*4, alen*4 ); + if ( nopen ) { + g = gc_brush; + nopen = FALSE; + } + } + if ( !nopen ) { // draw pie outline + double w2 = 0.5*w; // with, height in ellipsis + double h2 = 0.5*h; + double xc = (double)x+w2; + double yc = (double)y+h2; + double ra1 = Q_PI/2880.0*a; // convert a, alen to radians + double ra2 = ra1 + Q_PI/2880.0*alen; + int xic = tqRound(xc); + int yic = tqRound(yc); + XDrawLine( dpy, hd, g, xic, yic, + tqRound(xc + qcos(ra1)*w2), tqRound(yc - qsin(ra1)*h2)); + XDrawLine( dpy, hd, g, xic, yic, + tqRound(xc + qcos(ra2)*w2), tqRound(yc - qsin(ra2)*h2)); + XDrawArc( dpy, hd, g, x, y, w, h, a*4, alen*4 ); + } +} + + +/*! + Draws a chord defined by the rectangle \a (x, y, w, h), the start + angle \a a and the arc length \a alen. + + The chord is filled with the current brush(). + + The angles \a a and \a alen are 1/16th of a degree, i.e. a full + circle equals 5760 (16*360). Positive values of \a a and \a alen + mean counter-clockwise while negative values mean the clockwise + direction. Zero degrees is at the 3 o'clock position. + + \sa drawArc(), drawPie() +*/ + +void TQPainter::drawChord( int x, int y, int w, int h, int a, int alen ) +{ + if ( !isActive() ) + return; + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + TQPDevCmdParam param[3]; + TQRect r( x, y, w, h ); + param[0].rect = &r; + param[1].ival = a; + param[2].ival = alen; + if ( !pdev->cmd(TQPaintDevice::PdcDrawChord, this, param) || !hd ) + return; + } + if ( txop == TxRotShear ) { // rotate/shear + TQPointArray pa; + pa.makeArc( x, y, w-1, h-1, a, alen, xmat ); // arc polygon + int n = pa.size(); + pa.resize( n+1 ); + pa.setPoint( n, pa.at(0) ); // connect endpoints + drawPolyInternal( pa ); + return; + } + map( x, y, w, h, &x, &y, &w, &h ); + } + XSetArcMode( dpy, gc_brush, ArcChord ); + w--; + h--; + if ( w <= 0 || h <= 0 ) { + if ( w == 0 || h == 0 ) + return; + fix_neg_rect( &x, &y, &w, &h ); + } + + GC g = gc; + bool nopen = cpen.style() == NoPen; + + if ( cbrush.style() != NoBrush ) { // draw filled chord + XFillArc( dpy, hd, gc_brush, x, y, w, h, a*4, alen*4 ); + if ( nopen ) { + g = gc_brush; + nopen = FALSE; + } + } + if ( !nopen ) { // draw chord outline + double w2 = 0.5*w; // with, height in ellipsis + double h2 = 0.5*h; + double xc = (double)x+w2; + double yc = (double)y+h2; + double ra1 = Q_PI/2880.0*a; // convert a, alen to radians + double ra2 = ra1 + Q_PI/2880.0*alen; + XDrawLine( dpy, hd, g, + tqRound(xc + qcos(ra1)*w2), tqRound(yc - qsin(ra1)*h2), + tqRound(xc + qcos(ra2)*w2), tqRound(yc - qsin(ra2)*h2)); + XDrawArc( dpy, hd, g, x, y, w, h, a*4, alen*4 ); + } + XSetArcMode( dpy, gc_brush, ArcPieSlice ); +} + + +/*! + Draws \a nlines separate lines from points defined in \a a, + starting at \a a[index] (\a index defaults to 0). If \a nlines is + -1 (the default) all points until the end of the array are used + (i.e. (a.size()-index)/2 lines are drawn). + + Draws the 1st line from \a a[index] to \a a[index+1]. Draws the + 2nd line from \a a[index+2] to \a a[index+3] etc. + + \warning On X11, coordinates that do not fit into 16-bit signed + values are truncated. This limitation is expected to go away in + TQt 4. + + \sa drawPolyline(), drawPolygon(), TQPen +*/ + +void TQPainter::drawLineSegments( const TQPointArray &a, int index, int nlines ) +{ + if ( nlines < 0 ) + nlines = a.size()/2 - index/2; + if ( index + nlines*2 > (int)a.size() ) + nlines = (a.size() - index)/2; + if ( !isActive() || nlines < 1 || index < 0 ) + return; + TQPointArray pa = a; + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + if ( 2*nlines != (int)pa.size() ) { + pa = TQPointArray( nlines*2 ); + for ( int i=0; i<nlines*2; i++ ) + pa.setPoint( i, a.point(index+i) ); + index = 0; + } + TQPDevCmdParam param[1]; + param[0].ptarr = (TQPointArray*)&pa; + if ( !pdev->cmd(TQPaintDevice::PdcDrawLineSegments, this, param) || + !hd ) + return; + } + if ( txop != TxNone ) { + pa = xForm( a, index, nlines*2 ); + if ( pa.size() != a.size() ) { + index = 0; + nlines = pa.size()/2; + } + } + } + if ( cpen.style() != NoPen ) + XDrawSegments( dpy, hd, gc, + (XSegment*)(pa.shortPoints( index, nlines*2 )), nlines ); +} + + +/*! + Draws the polyline defined by the \a npoints points in \a a + starting at \a a[index]. (\a index defaults to 0.) + + If \a npoints is -1 (the default) all points until the end of the + array are used (i.e. a.size()-index-1 line segments are drawn). + + \warning On X11, coordinates that do not fit into 16-bit signed + values are truncated. This limitation is expected to go away in + TQt 4. + + \sa drawLineSegments(), drawPolygon(), TQPen +*/ + +void TQPainter::drawPolyline( const TQPointArray &a, int index, int npoints ) +{ + if ( npoints < 0 ) + npoints = a.size() - index; + if ( index + npoints > (int)a.size() ) + npoints = a.size() - index; + if ( !isActive() || npoints < 2 || index < 0 ) + return; + TQPointArray pa = a; + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + if ( npoints != (int)pa.size() ) { + pa = TQPointArray( npoints ); + for ( int i=0; i<npoints; i++ ) + pa.setPoint( i, a.point(index+i) ); + index = 0; + } + TQPDevCmdParam param[1]; + param[0].ptarr = (TQPointArray*)&pa; + if ( !pdev->cmd(TQPaintDevice::PdcDrawPolyline, this, param) || !hd ) + return; + } + if ( txop != TxNone ) { + pa = xForm( pa, index, npoints ); + if ( pa.size() != a.size() ) { + index = 0; + npoints = pa.size(); + } + } + } + if ( cpen.style() != NoPen ) { + while(npoints>65535) { + XDrawLines( dpy, hd, gc, (XPoint*)(pa.shortPoints( index, 65535 )), + 65535, CoordModeOrigin ); + npoints-=65535; + index+=65535; + } + XDrawLines( dpy, hd, gc, (XPoint*)(pa.shortPoints( index, npoints )), + npoints, CoordModeOrigin ); + } +} + +static int global_polygon_shape = Complex; + +/*! + Draws the polygon defined by the \a npoints points in \a a + starting at \a a[index]. (\a index defaults to 0.) + + If \a npoints is -1 (the default) all points until the end of the + array are used (i.e. a.size()-index line segments define the + polygon). + + The first point is always connected to the last point. + + The polygon is filled with the current brush(). If \a winding is + TRUE, the polygon is filled using the winding fill algorithm. If + \a winding is FALSE, the polygon is filled using the even-odd + (alternative) fill algorithm. + + \warning On X11, coordinates that do not fit into 16-bit signed + values are truncated. This limitation is expected to go away in + TQt 4. + + \sa drawLineSegments(), drawPolyline(), TQPen +*/ + +void TQPainter::drawPolygon( const TQPointArray &a, bool winding, + int index, int npoints ) +{ + if ( npoints < 0 ) + npoints = a.size() - index; + if ( index + npoints > (int)a.size() ) + npoints = a.size() - index; + if ( !isActive() || npoints < 2 || index < 0 ) + return; + TQPointArray pa = a; + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + if ( npoints != (int)a.size() ) { + pa = TQPointArray( npoints ); + for ( int i=0; i<npoints; i++ ) + pa.setPoint( i, a.point(index+i) ); + index = 0; + } + TQPDevCmdParam param[2]; + param[0].ptarr = (TQPointArray*)&pa; + param[1].ival = winding; + if ( !pdev->cmd(TQPaintDevice::PdcDrawPolygon, this, param) || !hd ) + return; + } + if ( txop != TxNone ) { + pa = xForm( a, index, npoints ); + if ( pa.size() != a.size() ) { + index = 0; + npoints = pa.size(); + } + } + } + if ( winding ) // set to winding fill rule + XSetFillRule( dpy, gc_brush, WindingRule ); + + if ( pa[index] != pa[index+npoints-1] ){ // close open pointarray + pa.detach(); + pa.resize( index+npoints+1 ); + pa.setPoint( index+npoints, pa[index] ); + npoints++; + } + + if ( cbrush.style() != NoBrush ) { // draw filled polygon + XFillPolygon( dpy, hd, gc_brush, + (XPoint*)(pa.shortPoints( index, npoints )), + npoints, global_polygon_shape, CoordModeOrigin ); + } + if ( cpen.style() != NoPen ) { // draw outline + XDrawLines( dpy, hd, gc, (XPoint*)(pa.shortPoints( index, npoints )), + npoints, CoordModeOrigin ); + } + if ( winding ) // set to normal fill rule + XSetFillRule( dpy, gc_brush, EvenOddRule ); +} + +/*! + Draws the convex polygon defined by the \a npoints points in \a pa + starting at \a pa[index] (\a index defaults to 0). + + If the supplied polygon is not convex, the results are undefined. + + On some platforms (e.g. X Window), this is faster than + drawPolygon(). + + \warning On X11, coordinates that do not fit into 16-bit signed + values are truncated. This limitation is expected to go away in + TQt 4. +*/ +void TQPainter::drawConvexPolygon( const TQPointArray &pa, + int index, int npoints ) +{ + global_polygon_shape = Convex; + drawPolygon(pa, FALSE, index, npoints); + global_polygon_shape = Complex; +} + + + +/*! + Draws a cubic Bezier curve defined by the control points in \a a, + starting at \a a[index] (\a index defaults to 0). + + Control points after \a a[index + 3] are ignored. Nothing happens + if there aren't enough control points. + + \warning On X11, coordinates that do not fit into 16-bit signed + values are truncated. This limitation is expected to go away in + TQt 4. +*/ + +void TQPainter::drawCubicBezier( const TQPointArray &a, int index ) +{ + if ( !isActive() ) + return; + if ( a.size() - index < 4 ) { +#if defined(QT_CHECK_RANGE) + tqWarning( "TQPainter::drawCubicBezier: Cubic Bezier needs 4 control " + "points" ); +#endif + return; + } + TQPointArray pa( a ); + if ( index != 0 || a.size() > 4 ) { + pa = TQPointArray( 4 ); + for ( int i=0; i<4; i++ ) + pa.setPoint( i, a.point(index+i) ); + } + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) ) { + TQPDevCmdParam param[1]; + param[0].ptarr = (TQPointArray*)&pa; + if ( !pdev->cmd(TQPaintDevice::PdcDrawCubicBezier, this, param) || + !hd ) + return; + } + if ( txop != TxNone ) + pa = xForm( pa ); + } + if ( cpen.style() != NoPen ) { + pa = pa.cubicBezier(); + XDrawLines( dpy, hd, gc, (XPoint*)pa.shortPoints(), pa.size(), + CoordModeOrigin ); + } +} + + +/*! + Draws a pixmap at \a (x, y) by copying a part of \a pixmap into + the paint device. + + \a (x, y) specifies the top-left point in the paint device that is + to be drawn onto. \a (sx, sy) specifies the top-left point in \a + pixmap that is to be drawn. The default is (0, 0). + + \a (sw, sh) specifies the size of the pixmap that is to be drawn. + The default, (-1, -1), means all the way to the bottom right of + the pixmap. + + Currently the mask of the pixmap or it's alpha channel are ignored + when painting on a TQPrinter. + + \sa bitBlt(), TQPixmap::setMask() +*/ + +void TQPainter::drawPixmap( int x, int y, const TQPixmap &pixmap, + int sx, int sy, int sw, int sh ) +{ + if ( !isActive() || pixmap.isNull() ) + return; + + // right/bottom + if ( sw < 0 ) + sw = pixmap.width() - sx; + if ( sh < 0 ) + sh = pixmap.height() - sy; + + // Sanity-check clipping + if ( sx < 0 ) { + x -= sx; + sw += sx; + sx = 0; + } + if ( sw + sx > pixmap.width() ) + sw = pixmap.width() - sx; + if ( sy < 0 ) { + y -= sy; + sh += sy; + sy = 0; + } + if ( sh + sy > pixmap.height() ) + sh = pixmap.height() - sy; + + if ( sw <= 0 || sh <= 0 ) + return; + + if ( pdev->x11Screen() != pixmap.x11Screen() ) { + TQPixmap* p = (TQPixmap*) &pixmap; + p->x11SetScreen( pdev->x11Screen() ); + } + + TQPixmap::x11SetDefaultScreen( pixmap.x11Screen() ); + + if ( testf(ExtDev|VxF|WxF) ) { + if ( testf(ExtDev) || txop == TxScale || txop == TxRotShear ) { + if ( sx != 0 || sy != 0 || + sw != pixmap.width() || sh != pixmap.height() ) { + TQPixmap tmp( sw, sh, pixmap.depth() ); + bitBlt( &tmp, 0, 0, &pixmap, sx, sy, sw, sh, CopyROP, TRUE ); + if ( pixmap.mask() ) { + TQBitmap mask( sw, sh ); + bitBlt( &mask, 0, 0, pixmap.mask(), sx, sy, sw, sh, + CopyROP, TRUE ); + tmp.setMask( mask ); + } + drawPixmap( x, y, tmp ); + return; + } + if ( testf(ExtDev) ) { + TQPDevCmdParam param[2]; + TQRect r(x, y, pixmap.width(), pixmap.height()); + param[0].rect = &r; + param[1].pixmap = &pixmap; + if ( !pdev->cmd(TQPaintDevice::PdcDrawPixmap, this, param) || !hd ) + return; + } + if ( txop == TxScale || txop == TxRotShear ) { + TQWMatrix mat( m11(), m12(), + m21(), m22(), + dx(), dy() ); + mat = TQPixmap::trueMatrix( mat, sw, sh ); + TQPixmap pm = pixmap.xForm( mat ); + if ( !pm.mask() && txop == TxRotShear ) { + TQBitmap bm_clip( sw, sh, 1 ); + bm_clip.fill( color1 ); + pm.setMask( bm_clip.xForm(mat) ); + } + map( x, y, &x, &y ); // compute position of pixmap + int dx, dy; + mat.map( 0, 0, &dx, &dy ); + uint save_flags = flags; + flags = IsActive | (save_flags & ClipOn); + drawPixmap( x-dx, y-dy, pm ); + flags = save_flags; + return; + } + } + map( x, y, &x, &y ); + } + + TQBitmap *mask = (TQBitmap *)pixmap.mask(); + bool mono = pixmap.depth() == 1; + + if ( mask && !hasClipping() && pdev != paintEventDevice ) { + if ( mono ) { // needs GCs pen color + bool selfmask = pixmap.data->selfmask; + if ( selfmask ) { + XSetFillStyle( dpy, gc, FillStippled ); + XSetStipple( dpy, gc, pixmap.handle() ); + } else { + XSetFillStyle( dpy, gc, FillOpaqueStippled ); + XSetStipple( dpy, gc, pixmap.handle() ); + XSetClipMask( dpy, gc, mask->handle() ); + XSetClipOrigin( dpy, gc, x-sx, y-sy ); + } + XSetTSOrigin( dpy, gc, x-sx, y-sy ); + XFillRectangle( dpy, hd, gc, x, y, sw, sh ); + XSetTSOrigin( dpy, gc, 0, 0 ); + XSetFillStyle( dpy, gc, FillSolid ); + if ( !selfmask ) { + if ( pdev == paintEventDevice && paintEventClipRegion ) { + x11SetClipRegion( dpy, gc, 0, rendhd, *paintEventClipRegion ); + } else { + x11ClearClipRegion(dpy, gc, 0, rendhd); + } + } + } else { + bitBlt( pdev, x, y, &pixmap, sx, sy, sw, sh, (RasterOp)rop ); + } + return; + } + + TQRegion rgn = crgn; + + if ( mask ) { // pixmap has clip mask + // Implies that clipping is on, either explicit or implicit + // Create a new mask that combines the mask with the clip region + + if ( pdev == paintEventDevice && paintEventClipRegion ) { + if ( hasClipping() ) + rgn = rgn.intersect( *paintEventClipRegion ); + else + rgn = *paintEventClipRegion; + } + + TQBitmap *comb = new TQBitmap( sw, sh ); + comb->detach(); + GC cgc = tqt_xget_temp_gc( pixmap.x11Screen(), TRUE ); // get temporary mono GC + XSetForeground( dpy, cgc, 0 ); + XFillRectangle( dpy, comb->handle(), cgc, 0, 0, sw, sh ); + XSetBackground( dpy, cgc, 0 ); + XSetForeground( dpy, cgc, 1 ); + int num; + XRectangle *rects = (XRectangle *)qt_getClipRects( rgn, num ); + XSetClipRectangles( dpy, cgc, -x, -y, rects, num, YXBanded ); + XSetFillStyle( dpy, cgc, FillOpaqueStippled ); + XSetStipple( dpy, cgc, mask->handle() ); + XSetTSOrigin( dpy, cgc, -sx, -sy ); + XFillRectangle( dpy, comb->handle(), cgc, 0, 0, sw, sh ); + XSetTSOrigin( dpy, cgc, 0, 0 ); // restore cgc + XSetFillStyle( dpy, cgc, FillSolid ); + XSetClipMask( dpy, cgc, None ); + mask = comb; // it's deleted below + + XSetClipMask( dpy, gc, mask->handle() ); + XSetClipOrigin( dpy, gc, x, y ); + } + + if ( mono ) { + XSetBackground( dpy, gc, bg_col.pixel(scrn) ); + XSetFillStyle( dpy, gc, FillOpaqueStippled ); + XSetStipple( dpy, gc, pixmap.handle() ); + XSetTSOrigin( dpy, gc, x-sx, y-sy ); + XFillRectangle( dpy, hd, gc, x, y, sw, sh ); + XSetTSOrigin( dpy, gc, 0, 0 ); + XSetFillStyle( dpy, gc, FillSolid ); + } else { +#if !defined(TQT_NO_XFTFREETYPE) && !defined(TQT_NO_XRENDER) + Picture pict = rendhd ? XftDrawPicture((XftDraw *) rendhd) : None; + TQPixmap *alpha = pixmap.data->alphapm; + + if ( pict && pixmap.x11RenderHandle() && + alpha && alpha->x11RenderHandle()) { + XRenderComposite(dpy, PictOpOver, pixmap.x11RenderHandle(), + alpha->x11RenderHandle(), pict, + sx, sy, sx, sy, x, y, sw, sh); + } else +#endif // !TQT_NO_XFTFREETYPE && !TQT_NO_XRENDER + { + XCopyArea( dpy, pixmap.handle(), hd, gc, sx, sy, sw, sh, x, y ); + } + } + + if ( mask ) { // restore clipping + XSetClipOrigin( dpy, gc, 0, 0 ); + XSetRegion( dpy, gc, rgn.handle() ); + delete mask; // delete comb, created above + } +} + + +/* Internal, used by drawTiledPixmap */ + +static void drawTile( TQPainter *p, int x, int y, int w, int h, + const TQPixmap &pixmap, int xOffset, int yOffset ) +{ + int yPos, xPos, drawH, drawW, yOff, xOff; + yPos = y; + yOff = yOffset; + while( yPos < y + h ) { + drawH = pixmap.height() - yOff; // Cropping first row + if ( yPos + drawH > y + h ) // Cropping last row + drawH = y + h - yPos; + xPos = x; + xOff = xOffset; + while( xPos < x + w ) { + drawW = pixmap.width() - xOff; // Cropping first column + if ( xPos + drawW > x + w ) // Cropping last column + drawW = x + w - xPos; + p->drawPixmap( xPos, yPos, pixmap, xOff, yOff, drawW, drawH ); + xPos += drawW; + xOff = 0; + } + yPos += drawH; + yOff = 0; + } +} + +#if 0 // see comment in drawTiledPixmap +/* Internal, used by drawTiledPixmap */ + +static void fillTile( TQPixmap *tile, const TQPixmap &pixmap ) +{ + bitBlt( tile, 0, 0, &pixmap, 0, 0, -1, -1, TQt::CopyROP, TRUE ); + int x = pixmap.width(); + while ( x < tile->width() ) { + bitBlt( tile, x,0, tile, 0,0, x,pixmap.height(), TQt::CopyROP, TRUE ); + x *= 2; + } + int y = pixmap.height(); + while ( y < tile->height() ) { + bitBlt( tile, 0,y, tile, 0,0, tile->width(),y, TQt::CopyROP, TRUE ); + y *= 2; + } +} +#endif + +/*! + Draws a tiled \a pixmap in the specified rectangle. + + \a (x, y) specifies the top-left point in the paint device that is + to be drawn onto; with the width and height given by \a w and \a + h. \a (sx, sy) specifies the top-left point in \a pixmap that is + to be drawn. The default is (0, 0). + + Calling drawTiledPixmap() is similar to calling drawPixmap() + several times to fill (tile) an area with a pixmap, but is + potentially much more efficient depending on the underlying window + system. + + \sa drawPixmap() +*/ + +void TQPainter::drawTiledPixmap( int x, int y, int w, int h, + const TQPixmap &pixmap, int sx, int sy ) +{ + int sw = pixmap.width(); + int sh = pixmap.height(); + if (!sw || !sh ) + return; + if ( sx < 0 ) + sx = sw - -sx % sw; + else + sx = sx % sw; + if ( sy < 0 ) + sy = sh - -sy % sh; + else + sy = sy % sh; + /* + Requirements for optimizing tiled pixmaps: + - not an external device + - not scale or rotshear + - not mono pixmap + - no mask + */ + TQBitmap *mask = (TQBitmap *)pixmap.mask(); + if ( !testf(ExtDev) && txop <= TxTranslate && pixmap.depth() > 1 && + mask == 0 ) { + if ( txop == TxTranslate ) + map( x, y, &x, &y ); + +#if !defined(TQT_NO_XFTFREETYPE) && !defined(TQT_NO_XRENDER) + Picture pict = rendhd ? XftDrawPicture((XftDraw *) rendhd) : None; + TQPixmap *alpha = pixmap.data->alphapm; + + if (pict && pixmap.x11RenderHandle() && alpha && alpha->x11RenderHandle()) { + // this is essentially drawTile() from above, inlined for + // the XRenderComposite call + int yPos, xPos, drawH, drawW, yOff, xOff; + yPos = y; + yOff = sy; + while( yPos < y + h ) { + drawH = pixmap.height() - yOff; // Cropping first row + if ( yPos + drawH > y + h ) // Cropping last row + drawH = y + h - yPos; + xPos = x; + xOff = sx; + while( xPos < x + w ) { + drawW = pixmap.width() - xOff; // Cropping first column + if ( xPos + drawW > x + w ) // Cropping last column + drawW = x + w - xPos; + XRenderComposite(dpy, PictOpOver, pixmap.x11RenderHandle(), + alpha->x11RenderHandle(), pict, + xOff, yOff, xOff, yOff, xPos, yPos, drawW, drawH); + xPos += drawW; + xOff = 0; + } + yPos += drawH; + yOff = 0; + } + return; + } +#endif // !TQT_NO_XFTFREETYPE && !TQT_NO_XRENDER + + XSetTile( dpy, gc, pixmap.handle() ); + XSetFillStyle( dpy, gc, FillTiled ); + XSetTSOrigin( dpy, gc, x-sx, y-sy ); + XFillRectangle( dpy, hd, gc, x, y, w, h ); + XSetTSOrigin( dpy, gc, 0, 0 ); + XSetFillStyle( dpy, gc, FillSolid ); + return; + } + +#if 0 + // maybe there'll be point in this again, but for the time all it + // does is make trouble for the postscript code. + if ( sw*sh < 8192 && sw*sh < 16*w*h ) { + int tw = sw; + int th = sh; + while( th * tw < 4096 && ( th < h || tw < w ) ) { + if ( h/th > w/tw ) + th *= 2; + else + tw *= 2; + } + TQPixmap tile( tw, th, pixmap.depth(), TQPixmap::NormalOptim ); + fillTile( &tile, pixmap ); + if ( mask ) { + TQBitmap tilemask( tw, th, TQPixmap::NormalOptim ); + fillTile( &tilemask, *mask ); + tile.setMask( tilemask ); + } + drawTile( this, x, y, w, h, tile, sx, sy ); + } else { + drawTile( this, x, y, w, h, pixmap, sx, sy ); + } +#else + // for now we'll just output the original and let the postscript + // code make what it can of it. qpicture will be unhappy. + drawTile( this, x, y, w, h, pixmap, sx, sy ); +#endif +} + +#if 0 +// +// Generate a string that describes a transformed bitmap. This string is used +// to insert and find bitmaps in the global pixmap cache. +// + +static TQString gen_text_bitmap_key( const TQWMatrix &m, const TQFont &font, + const TQString &str, int pos, int len ) +{ + TQString fk = font.key(); + int sz = 4*2 + len*2 + fk.length()*2 + sizeof(double)*6; + TQByteArray buf(sz); + uchar *p = (uchar *)buf.data(); + *((double*)p)=m.m11(); p+=sizeof(double); + *((double*)p)=m.m12(); p+=sizeof(double); + *((double*)p)=m.m21(); p+=sizeof(double); + *((double*)p)=m.m22(); p+=sizeof(double); + *((double*)p)=m.dx(); p+=sizeof(double); + *((double*)p)=m.dy(); p+=sizeof(double); + TQChar h1( '$' ); + TQChar h2( 'q' ); + TQChar h3( 't' ); + TQChar h4( '$' ); + *((TQChar*)p)=h1; p+=2; + *((TQChar*)p)=h2; p+=2; + *((TQChar*)p)=h3; p+=2; + *((TQChar*)p)=h4; p+=2; + memcpy( (char*)p, (char*)(str.unicode()+pos), len*2 ); p += len*2; + memcpy( (char*)p, (char*)fk.unicode(), fk.length()*2 ); p += fk.length()*2; + return TQString( (TQChar*)buf.data(), buf.size()/2 ); +} + +static TQBitmap *get_text_bitmap( const TQString &key ) +{ + return (TQBitmap*)TQPixmapCache::find( key ); +} + +static void ins_text_bitmap( const TQString &key, TQBitmap *bm ) +{ + if ( !TQPixmapCache::insert(key, bm) ) // cannot insert pixmap + delete bm; +} +#endif + +void qt_draw_transformed_rect( TQPainter *p, int x, int y, int w, int h, bool fill ) +{ + XPoint points[5]; + int xp = x, yp = y; + p->map( xp, yp, &xp, &yp ); + points[0].x = xp; + points[0].y = yp; + xp = x + w; yp = y; + p->map( xp, yp, &xp, &yp ); + points[1].x = xp; + points[1].y = yp; + xp = x + w; yp = y + h; + p->map( xp, yp, &xp, &yp ); + points[2].x = xp; + points[2].y = yp; + xp = x; yp = y + h; + p->map( xp, yp, &xp, &yp ); + points[3].x = xp; + points[3].y = yp; + points[4] = points[0]; + + if ( fill ) + XFillPolygon( p->dpy, p->hd, p->gc, points, 4, Convex, CoordModeOrigin ); + else + XDrawLines( p->dpy, p->hd, p->gc, points, 5, CoordModeOrigin ); +} + +void qt_draw_background( TQPainter *p, int x, int y, int w, int h ) +{ + if (p->testf(TQPainter::ExtDev)) { + if (p->pdev->devType() == TQInternal::Printer) + p->fillRect(x, y, w, h, p->bg_col); + return; + } + XSetForeground( p->dpy, p->gc, p->bg_col.pixel(p->scrn) ); + qt_draw_transformed_rect( p, x, y, w, h, TRUE); + XSetForeground( p->dpy, p->gc, p->cpen.color().pixel(p->scrn) ); +} + +/*! + Draws at most \a len characters of the string \a str at position + \a (x, y). + + \a (x, y) is the base line position. Note that the meaning of \a y + is not the same for the two drawText() varieties. +*/ +void TQPainter::drawText( int x, int y, const TQString &str, int len, TQPainter::TextDirection dir ) +{ + drawText( x, y, str, 0, len, dir ); +} + +/*! + Draws at most \a len characters starting at position \a pos from the + string \a str to position \a (x, y). + + \a (x, y) is the base line position. Note that the meaning of \a y + is not the same for the two drawText() varieties. +*/ +void TQPainter::drawText( int x, int y, const TQString &str, int pos, int len, TQPainter::TextDirection dir ) +{ + if ( !isActive() ) + return; + if (len < 0) + len = str.length() - pos; + if ( len <= 0 || pos >= (int)str.length() ) // empty string + return; + if ( pos + len > (int)str.length() ) + len = str.length() - pos; + + if ( testf(DirtyFont) ) { + updateFont(); + } + + if ( testf(ExtDev) && pdev->devType() != TQInternal::Printer ) { + TQPDevCmdParam param[3]; + TQPoint p(x, y); + TQString string = str.mid( pos, len ); + param[0].point = &p; + param[1].str = &string; + param[2].ival = TQFont::Latin; + if ( !pdev->cmd(TQPaintDevice::PdcDrawText2, this, param) || !hd ) + return; + } + + bool simple = (dir == TQPainter::Auto) && str.simpleText(); + // we can't take the complete string here as we would otherwise + // get quadratic behaviour when drawing long strings in parts. + // we do however need some chars around the part we paint to get arabic shaping correct. + // ### maybe possible to remove after cursor restrictions work in TQRT + int start; + int end; + if ( simple ) { + start = pos; + end = pos+len; + } else { + start = TQMAX( 0, pos - 8 ); + end = TQMIN( (int)str.length(), pos + len + 8 ); + } + TQConstString cstr( str.unicode() + start, end - start ); + pos -= start; + + TQTextEngine engine( cstr.string(), pfont ? pfont->d : cfont.d ); + TQTextLayout layout( &engine ); + + // this is actually what beginLayout does. Inlined here, so we can + // avoid the bidi algorithm if we don't need it. + engine.itemize( simple ? TQTextEngine::NoBidi|TQTextEngine::SingleLine : TQTextEngine::Full|TQTextEngine::SingleLine ); + engine.currentItem = 0; + engine.firstItemInLine = -1; + + if ( dir != Auto ) { + int level = dir == RTL ? 1 : 0; + for ( int i = engine.items.size(); i >= 0; i-- ) + engine.items[i].analysis.bidiLevel = level; + } + + if ( !simple ) { + layout.setBoundary( pos ); + layout.setBoundary( pos + len ); + } + + // small hack to force skipping of unneeded items + start = 0; + while ( engine.items[start].position < pos ) + ++start; + engine.currentItem = start; + layout.beginLine( 0xfffffff ); + end = start; + while ( !layout.atEnd() && layout.currentItem().from() < pos + len ) { + layout.addCurrentItem(); + end++; + } + TQFontMetrics fm(fontMetrics()); + int ascent = fm.ascent(), descent = fm.descent(); + int left, right; + layout.endLine( 0, 0, TQt::SingleLine|TQt::AlignLeft, &ascent, &descent, &left, &right ); + + // do _not_ call endLayout() here, as it would clean up the shaped items and we would do shaping another time + // for painting. + + int textFlags = 0; + if ( cfont.d->underline ) textFlags |= TQt::Underline; + if ( cfont.d->overline ) textFlags |= TQt::Overline; + if ( cfont.d->strikeOut ) textFlags |= TQt::StrikeOut; + + if ( bg_mode == OpaqueMode ) + qt_draw_background( this, x, y-ascent, right-left, ascent+descent+1); + + for ( int i = start; i < end; i++ ) { + TQTextItem ti; + ti.item = i; + ti.engine = &engine; + + drawTextItem( x, y - ascent, ti, textFlags ); + } + layout.d = 0; +} + + +/*! \internal + Draws the text item \a ti at position \a (x, y ). + + This method ignores the painters background mode and + color. drawText and qt_format_text have to do it themselves, as + only they know the extents of the complete string. + + It ignores the font set on the painter as the text item has one of its own. + + The underline and strikeout parameters of the text items font are + ignored aswell. You'll need to pass in the correct flags to get + underlining and strikeout. +*/ +void TQPainter::drawTextItem( int x, int y, const TQTextItem &ti, int textFlags ) +{ + if ( testf(ExtDev) ) { + TQPDevCmdParam param[2]; + TQPoint p(x, y); + param[0].point = &p; + param[1].textItem = &ti; + bool retval = pdev->cmd(TQPaintDevice::PdcDrawTextItem, this, param); + if ( !retval || !hd ) + return; + } + + TQTextEngine *engine = ti.engine; + TQScriptItem *si = &engine->items[ti.item]; + + engine->shape( ti.item ); + TQFontEngine *fe = si->fontEngine; + assert( fe != 0 ); + + x += si->x; + y += si->y; + + fe->draw( this, x, y, engine, si, textFlags ); +} + +/*! + \obsolete + Returns the current position of the pen. + + \sa moveTo() + */ +TQPoint TQPainter::pos() const +{ + return curPt; +} |