You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tqt3/src/kernel/qpainter_x11.cpp

3184 lines
96 KiB

/****************************************************************************
**
** 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 "ntqfont.h"
#include "ntqpainter.h"
#include "ntqwidget.h"
#include "ntqbitmap.h"
#include "ntqpixmapcache.h"
#include "ntqtextcodec.h"
#include "ntqpaintdevicemetrics.h"
#include "qt_x11_p.h"
#include "qtextlayout_p.h"
#include "qfontdata_p.h"
#include "qfontengine_p.h"
#include "qtextengine_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 "ntqtextstream.h"
#include "ntqbuffer.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;
}