Qt logo

Transformed Graphics Demo


This example lets the user rotate, shear and scale text and graphics arbitrarily.
/****************************************************************************
** $Id: xform.cpp,v 2.9.2.1 1998/10/05 13:28:02 warwick Exp $
**
** Copyright (C) 1992-1998 Troll Tech AS.  All rights reserved.
**
** This file is part of an example program for Qt.  This example
** program may be used, distributed and modified without limitation.
**
*****************************************************************************/

#include <qapplication.h>

#include <qdialog.h>
#include <qlabel.h>
#include <qlineedit.h>
#include <qpushbutton.h>
#include <qcheckbox.h>
#include <qradiobutton.h>
#include <qbuttongroup.h>
#include <qlcdnumber.h>
#include <qslider.h>
#include <qmenubar.h>

#include <qpainter.h>
#include <qpixmap.h>

#include <stdlib.h>

class FontSelect;

class XFormControl : public QFrame
{
    Q_OBJECT
public:
    XFormControl( QWidget *parent=0, const char *name=0 );
   ~XFormControl() {}
signals:
    void newMatrix( QWMatrix );
    void newText( const char * );
    void newFont( const QFont & );
    void newMode( bool image );
private slots:
    void newMtx();
    void selectFont();
    void selectFontDestroyed();
    void fontSelected( const QFont & );
    void toggleMode();
private:
    QSlider     *rotS;                 // Rotation angle scroll bar
    QSlider     *shearS;               // Shear value scroll bar
    QSlider     *magS;                 // Magnification value scroll bar
    QLCDNumber  *rotLCD;               // Rotation angle LCD display
    QLCDNumber  *shearLCD;             // Shear value LCD display
    QLCDNumber  *magLCD;               // Magnification value LCD display
    QCheckBox   *mirror;               // Checkbox for mirror image on/of
    QLineEdit   *textEd;               // Inp[ut field for xForm text
    QPushButton *f;                    // Select font push button
    QCheckBox   *imgCheckBox;          // Check box for image on/off
    FontSelect  *fs;                   // font select dialog box
    bool         fsUp;                 // TRUE if font select is visible
};

/*
  ShowXForm displays a text or a pixmap (QPixmap) using a coordinate
  transformation matrix (QWMatrix)
*/

class ShowXForm : public QWidget
{
    Q_OBJECT
public:
    ShowXForm( QWidget *parent=0, const char *name=0 );
   ~ShowXForm() {}
    void showIt();                      // (Re)displays text or pixmap
    bool pixmapMode() const { return usingPixmap; }
public slots:
    void setText( const char* );
    void setMatrix( QWMatrix );
    void setFont( const QFont &f );
    void setPixmap( QPixmap );
    void setPixmapMode( bool b );
private:
    void paintEvent( QPaintEvent * );
    void resizeEvent( QResizeEvent * );
    QString   text;                     // text to be displayed
    QWMatrix  mtx;                      // coordinate transform matrix
    QPixmap   pix;                      // pixmap to be displayed
    bool      usingPixmap;              // TRUE if pix is being displayed
    QRect     eraseRect;                // covers last displayed text/pixmap
};

class FontSelect : public QDialog
{
    Q_OBJECT
public:
    FontSelect( QWidget *parent=0, const char *name=0 );
private slots:
    void         newStrikeOut();
    void         doFont();
    void         newUnderline();
    void         newItalic();
    void         newWeight( int id );
    void         newFamily();
    void         newSize( int );
    void         slidingSize( int );
    void         doApply();
    void         familyActivated( int );
    void         pSizeActivated( int );
signals:
    void         newFont( const QFont & );
protected:
    void         updateMetrics();
private:
    QLabel       *familyL;
    QLabel       *sizeL;
    QLineEdit    *family;
    QLineEdit    *sizeE;
    QCheckBox    *italic;
    QCheckBox    *underline;
    QCheckBox    *strikeOut;
    QPushButton  *apply;
    QPushButton  *stat;
    QSlider      *sizeS;
    QRadioButton *rb[5];
    QButtonGroup *weight;
    QGroupBox    *mGroup;
    QFont         f;
    QMenuBar     *menu;
    QLabel       *metrics[4][2];
    QWidget      *fontInternal;
};

XFormControl::XFormControl( QWidget *parent, const char *name )
        : QFrame( parent, name )
{
    rotLCD      = new QLCDNumber( 4, this, "rotateLCD" );
    rotS        = new QSlider( QSlider::Horizontal, this,
                                  "rotateSlider" );
    shearLCD    = new QLCDNumber( 5,this, "shearLCD" );
    shearS      = new QSlider( QSlider::Horizontal, this,
                                  "shearSlider" );
    mirror      = new QCheckBox( this, "mirrorCheckBox" );
    textEd      = new QLineEdit( this, "text" );
    f           = new QPushButton( this, "text" );
    imgCheckBox = new QCheckBox( this, "fontOrImage" );

    rotLCD->setGeometry( 10, 10, 100, 60 );
    rotLCD->display( "  0'" );

    rotS->setGeometry( 10, 80, 100, 15 );
    rotS->setRange( -180, 180 );
    rotS->setValue( 0 );
    connect( rotS, SIGNAL(valueChanged(int)), SLOT(newMtx()) );

    shearLCD->setGeometry( 10, 105, 100, 60 );
    shearLCD->display( "0.00" );

    shearS->setGeometry( 10, 175, 100, 15 );
    shearS->setRange( -25, 25 );
    shearS->setValue( 0 );
    connect( shearS, SIGNAL(valueChanged(int)), SLOT(newMtx()) );

    mirror->setGeometry( 10, 200, 100, 15 );
    mirror->setText( "Mirror" );
    connect( mirror, SIGNAL(clicked()), SLOT(newMtx()) );

    imgCheckBox->setGeometry( 10, 220, 100, 15 );
    imgCheckBox->setText( "Image" );
    connect( imgCheckBox, SIGNAL(clicked()), SLOT(toggleMode()) );

    textEd->setGeometry( 10, 245, 100, 20 );
    textEd->setText( "Troll" );
    connect( textEd, SIGNAL(textChanged(const char*)),
                     SIGNAL(newText(const char*)) );

    f->setGeometry( 10, 275, 100, 20 );
    f->setText( "Select font..." );
    connect( f, SIGNAL(clicked()), SLOT(selectFont()) );

    magS    = 0;
    fs      = 0;
}

/*
    Called whenever the user has changed one of the matrix parameters
    (i.e. rotate, shear or magnification)
*/
void XFormControl::newMtx()
{
    QWMatrix m;
    if ( imgCheckBox->isChecked() ) {
        double magVal = 1.0*magS->value()/100;
        m.scale( magVal, magVal );
    }
    double shearVal = 1.0*shearS->value()/25;
    m.shear( shearVal, shearVal );
    m.rotate( rotS->value() );
    if ( mirror->isChecked() ) {
        m.scale( 1, -1 );
        m.rotate( 180 );
    }

    QString tmp;
    tmp.sprintf( "%1.2f", shearVal  );
    if ( shearVal >= 0 )
        tmp.insert( 0, " " );
    shearLCD->display( tmp );

    int rot = rotS->value();
    if ( rot < 0 )
        rot = rot + 360;
    tmp.sprintf( "%3i'", rot );
    rotLCD->display( tmp );
    emit newMatrix( m );
}

void XFormControl::selectFont()
{
    if (!fs) {
        fs   = new FontSelect;
        connect( fs, SIGNAL(destroyed()),    SLOT(selectFontDestroyed()) );
        connect( fs, SIGNAL(newFont(const QFont&)),
                     SLOT(fontSelected(const QFont&)) );
        fs->setGeometry( QRect( 100, 200, 380, 260 ) );
        fsUp = FALSE;
    }
    fsUp = !fsUp;
    if ( fsUp )
        fs->show();
    else
        fs->hide();
}

/*
    Called when the user has closed the SelectFont dialog via the
    window manager.
*/
void XFormControl::selectFontDestroyed()
{
    fs = 0;
}

void XFormControl::fontSelected( const QFont &font )
{
    imgCheckBox->setChecked( FALSE );
    emit newFont( font );
    toggleMode();
}

/*
    Toggles between image and text mode.
*/

void XFormControl::toggleMode()
{
    if ( magS == 0 ) {
        magS = new QSlider( QSlider::Horizontal, this,
                               "magnifySlider" );
        magS->setGeometry( 10, 375, 100, 15 );
        magS->setRange( 0, 400 );
        magS->setValue( 100 );
        connect( magS, SIGNAL(valueChanged(int)), SLOT(newMtx()) );
        magLCD = new QLCDNumber( 4,this, "shearLCD" );
        magLCD->setGeometry( 10, 305, 100, 60 );
        magLCD->display( "100" );
        connect( magS, SIGNAL(valueChanged(int)), magLCD, SLOT(display(int)));
    }
    emit newMode( imgCheckBox->isChecked() );
    newMtx();
    if ( imgCheckBox->isChecked() ) {
        magS->show();
        magLCD->show();
    } else {
        magS->hide();
        magLCD->hide();
    }
    qApp->flushX();
}

const int yOff = 35;

ShowXForm::ShowXForm( QWidget *parent, const char *name )
        : QWidget( parent, name )
{
    setFont( QFont( "Charter", 48, QFont::Bold ) );
    setBackgroundColor( white );
    usingPixmap = FALSE;
    eraseRect = QRect( 0, 0, 0, 0 );
}

void ShowXForm::paintEvent( QPaintEvent * )
{
    showIt();
}

void ShowXForm::resizeEvent( QResizeEvent * )
{
    eraseRect = QRect( width()/2, height()/2, 0, 0 );
}

void ShowXForm::setText( const char *s )
{
    text = s;
    showIt();
}

void ShowXForm::setMatrix( QWMatrix w )
{
    mtx = w;
    showIt();
}

void ShowXForm::setFont( const QFont &f )
{
    usingPixmap = FALSE;
    QWidget::setFont( f );
}

void ShowXForm::setPixmap( QPixmap pm )
{
    pix         = pm;
    usingPixmap = TRUE;
    showIt();
}

void ShowXForm::setPixmapMode( bool enable )
{
    usingPixmap = enable;
}

void ShowXForm::showIt()
{
    QPainter p;
    QRect r;      // rectangle covering new text/pixmap in virtual coordinates
    QWMatrix m;   // copy user specified transform
    int textYPos = 0; // distance from boundingRect y pos to baseline
    int textXPos = 0; // distance from boundingRect x pos to text start
    QRect br;
    QFontMetrics fm( fontMetrics() );   // get widget font metrics
    if ( pixmapMode() ) {
        r = pix.rect();
    } else {
        br = fm.boundingRect( text );   // rectangle covering text
        r  = br;
        textYPos = -r.y();
        textXPos = -r.x();
        br.moveTopLeft( QPoint( -br.width()/2, -br.height()/2 ) );
    }
    r.moveTopLeft( QPoint(-r.width()/2, -r.height()/2) );
          // compute union of new and old rect
          // the resulting rectangle will cover what is already displayed
          // and have room for the new text/pixmap
    eraseRect = eraseRect.unite( mtx.map(r) );
    eraseRect.moveBy( -1, -1 ); // add border for matrix round off
    eraseRect.setSize( QSize( eraseRect.width() + 2,eraseRect.height() + 2 ) );
    int pw = QMIN(eraseRect.width(),width());
    int ph = QMIN(eraseRect.height(),height());
    QPixmap pm( pw, ph );               // off-screen drawing pixmap
    pm.fill( backgroundColor() );

    p.begin( &pm );
    m.translate( pw/2, ph/2 );  // 0,0 is center
    m = mtx * m;
    p.setWorldMatrix( m );
    if ( pixmapMode() ) {
        p.drawPixmap( -pix.width()/2, -pix.height()/2, pix );
    } else {
        p.setFont( font() );            // use widget font
        p.drawText( r.left() + textXPos, r.top() + textYPos, text );
    }
#if 0
    p.setPen( red );
    p.drawRect( br );
#endif
    p.end();

    int xpos = width()/2  - pw/2;
    int ypos = height()/2 - ph/2;
    bitBlt( this, xpos, ypos,                   // copy pixmap to widget
            &pm, 0, 0, -1, -1 );
    eraseRect =  mtx.map( r );
}

FontSelect::FontSelect( QWidget *parent, const char *name)
    : QDialog( parent, name, 0 ), f( "Charter", 48, QFont::Bold )
{
    static const char *radios[] = {
        "Light (25)", "Normal (50)", "DemiBold (63)",
        "Bold (75)", "Black (87)"
    };
    int i;

    fontInternal = new QWidget( this );
    fontInternal->setFont( f );
    fontInternal->hide();

    familyL    = new QLabel(     this, "familyLabel" );
    sizeL      = new QLabel(     this, "sizeLabel" );
    family     = new QLineEdit(  this, "family" );
    sizeE      = new QLineEdit(  this, "pointSize" );
    italic     = new QCheckBox(  this, "italic" );
    underline  = new QCheckBox(  this, "underline" );
    strikeOut  = new QCheckBox(  this, "strikeOut" );
    apply      = new QPushButton( this, "apply" );
    sizeS      = new QSlider( QSlider::Horizontal, this,
                              "pointSizeSlider" );

    familyL->setGeometry( 10, yOff + 10, 100,20 );
    familyL->setText( "Family :" );

    sizeL->setGeometry( 10, yOff + 40, 100, 20 );
    sizeL->setText( "Point size :" );

    family->setGeometry( 110, yOff + 10, 100, 20 );
    family->setText( "Charter" );

    sizeE->setGeometry( 110, yOff + 40, 100, 20 );
    sizeE->setText( "48" );

    sizeS->setGeometry( 220, yOff + 40, 100, 20 );
    sizeS->setRange( 1, 100 );
    sizeS->setValue( 48 );
    sizeS->setTracking( FALSE );
    connect( sizeS, SIGNAL(valueChanged(int)), SLOT(newSize(int)) );
    connect( sizeS, SIGNAL(sliderMoved(int)),  SLOT(slidingSize(int)) );

    italic->setGeometry( 10, yOff + 70, 80, 20 );
    italic->setText( "Italic" );
    connect( italic, SIGNAL(clicked()), SLOT(newItalic()) );

    underline->setGeometry( 110, yOff + 70, 80, 20 );
    underline->setText( "Underline" );
    connect( underline, SIGNAL(clicked()), SLOT(newUnderline()) );

    strikeOut->setGeometry( 210, yOff + 70, 80, 20 );
    strikeOut->setText( "StrikeOut" );
    connect( strikeOut, SIGNAL(clicked()), SLOT(newStrikeOut()) );

    apply->setGeometry( 235, yOff + 10, 70, 20);
    apply->setText( "APPLY" );
    apply->setDefault( TRUE );
    connect( apply, SIGNAL(clicked()), SLOT(doApply()) );

    weight = new QButtonGroup( "Weight", this, "weightGroupBox" );
    weight->setGeometry( 10, yOff + 100, 120, 120 );
    connect( weight, SIGNAL(clicked(int)), SLOT(newWeight(int)) );
    QString wname;
    for( i = 0 ; i < 5 ; i++ ) {
        wname.sprintf("radioButton %i",i);
        rb[i] = new QRadioButton( weight, wname );
        rb[i]->setGeometry( 10, 15+i*20 , 95, 20 );
        rb[i]->setText( radios[i] );
    }
    rb[3]->setChecked( TRUE );

#ifdef _OS_WIN32_
    static const char *families[] = {
        "Arial", "Book Antiqua", "Bookman", "Century Schoolbook",
        "Comic Sans MS", "Courier New", "Garamond",
        "Haettenschweiler", "Impact", "Symbol", "Times New Roman",
        "Verdana", "WingDings", 0
    };
#else
    static const char *families[] = {
        "Charter", "Clean", "Courier", "Fixed", "Gothic", "Helvetica",
        "Lucida", "Lucidabright", "Lucidatypewriter", "Mincho", 
        "New century schoolbook", "Symbol", "Terminal", "Times", "Utopia",
        0
    };
#endif // _OS_WIN32_

    static const char *pSizes[]  = {
        "8", "10", "12", "14", "18", "24", "36", "48", "72", "96", 0
    };

    QPopupMenu *familyPopup = new QPopupMenu;
    const char **tmp;
    tmp = families;
    while( *tmp )
        familyPopup->insertItem( *tmp++ );

    QPopupMenu *pSize = new QPopupMenu;
    tmp = pSizes;
    while( *tmp )
        pSize->insertItem( *tmp++ );

    menu = new QMenuBar( this );
    menu->move( 0, 0 );
    menu->resize( 350, 30 );
    menu->insertItem( "Family", familyPopup );
    menu->insertItem( "Point size", pSize );

    connect( familyPopup, SIGNAL(activated(int)), SLOT(familyActivated(int)) );
    connect( pSize, SIGNAL(activated(int)), SLOT(pSizeActivated(int)) );

    static const char *mLabelStr[] = {
        "Family:", "Point size:", "Weight:", "Italic:"
    };

    mGroup = new QButtonGroup( this, "metricsGroupBox" );
    mGroup->setTitle( "Actual font" );
    mGroup->setGeometry(140, yOff + 100, 230, 100);
    for( i = 0 ; i < 4 ; i++ ) {
        wname.sprintf("MetricsLabel[%i][%i]",i,0);
        metrics[i][0] = new QLabel( mGroup, wname);
        metrics[i][0]->setGeometry(10, 15 + 20*i, 70, 20);
        metrics[i][0]->setText( mLabelStr[i] );

        wname.sprintf("MetricsLabel[%i][%i]",i,1);
        metrics[i][1] = new QLabel( mGroup, wname);
        metrics[i][1]->setGeometry(90, 15 + 20*i, 135, 20);
    }
    updateMetrics();
}

void FontSelect::newStrikeOut()
{
    f.setStrikeOut( strikeOut->isChecked() );
    doFont();
}

void FontSelect::doFont()
{
    QFont xyz = f;
    xyz.setPointSize( f.pointSize()+1 );
    xyz.setPointSize( f.pointSize() );
    fontInternal->setFont( xyz );
    updateMetrics();
}

void FontSelect::newUnderline()
{
    f.setUnderline( underline->isChecked() );
    doFont();
}

void FontSelect::newItalic()
{
    f.setItalic( italic->isChecked() );
    doFont();
}

void FontSelect::newFamily()
{
    f.setFamily( family->text() );
    doFont();
}

void FontSelect::newWeight( int id )
{
    switch( id ) {
        case 0 :
            f.setWeight( QFont::Light );
            break;
        case 1 :
            f.setWeight( QFont::Normal );
            break;
        case 2 :
            f.setWeight( QFont::DemiBold );
            break;
        case 3 :
            f.setWeight( QFont::Bold );
            break;
        case 4 :
            f.setWeight( QFont::Black );
            break;
        default:
            return;
    }
    doFont();
}

void FontSelect::newSize( int value )
{
    QString tmp;
    tmp.sprintf("%i", value);
    sizeE->setText( tmp );
    f.setPointSize( value );
    doFont();
}

void FontSelect::slidingSize( int value )
{
    QString tmp;

    tmp.sprintf("%i", value);
    sizeE->setText( tmp );
}

void FontSelect::doApply()
{
    int sz = atoi( sizeE->text() );
    if ( sz > 100) {
        sizeS->blockSignals( TRUE );
        sizeS->setValue( 100 );
        sizeS->blockSignals( FALSE );
        f.setPointSize( sz );
    } else {
        sizeS->setValue( atoi( sizeE->text() ) );
    }
    f.setFamily( family->text() );
    doFont();

    emit newFont( fontInternal->font() );
}

void FontSelect::familyActivated( int id )
{
    family->setText( ((QPopupMenu*)sender())->text(id) );
    newFamily();
}

void FontSelect::pSizeActivated( int id )
{
    int value = atoi( ( (QPopupMenu*)sender())->text( id ) );
    sizeS->blockSignals( TRUE );
    sizeS->setValue( value );
    sizeS->blockSignals( FALSE );
    newSize( value );
}

void FontSelect::updateMetrics()
{
    QFontInfo fi = fontInternal->fontInfo();
    metrics[0][1]->setText( fi.family() );
    metrics[1][1]->setNum( fi.pointSize() );
    metrics[2][1]->setNum( fi.weight() );
    metrics[3][1]->setNum( (int)fi.italic() );
}

/*
    Grand unifying widget, putting ShowXForm and XFormControl
    together.
*/

class XFormCenter : public QWidget
{
    Q_OBJECT
public:
    XFormCenter( QWidget *parent=0, const char *name=0 );
public slots:
    void setFont( const QFont &f ) { sx->setFont( f ); }
    void newMode( bool );
private:
    void resizeEvent( QResizeEvent* );
    ShowXForm   *sx;
    XFormControl *xc;
};

void XFormCenter::resizeEvent( QResizeEvent* )
{
    sx->resize( width() - 120, height() );
    xc->resize( 120, height() );
}

void XFormCenter::newMode( bool showPix )
{
    static bool first = TRUE;

    if ( sx->pixmapMode() == showPix )
        return;
    if ( showPix && first ) {
        first = FALSE;
        QPixmap pm;
        pm.load( "image.any" );
        sx->setPixmap( pm );
        return;
    }
    sx->setPixmapMode( showPix );
}

XFormCenter::XFormCenter( QWidget *parent, const char *name )
    : QWidget( parent, name )
{
    sx = new ShowXForm(this);
    sx->move( 120, 0 );             // the size is set by resizeEvent
    xc = new XFormControl(this);
    xc->move( 0, 0 );               // the size is set by resizeEvent
    xc->setFrameStyle( QFrame::Box | QFrame::Sunken );
    xc->setLineWidth( 2 );
    connect( xc, SIGNAL(newText(const char*)), sx,
                 SLOT(setText(const char*)) );
    connect( xc, SIGNAL(newMatrix(QWMatrix)),
             sx, SLOT(setMatrix(QWMatrix)) );
    connect( xc, SIGNAL(newFont(const QFont&)), sx,
                 SLOT(setFont(const QFont&)) );
    connect( xc, SIGNAL(newMode(bool)), SLOT(newMode(bool)) );
    sx->setText( "Troll" );
}

int main( int argc, char **argv )
{
    QApplication a( argc, argv );

#if 0
    QColor x( 0xaa, 0xbe, 0xff );       // kind of blue
    QColorGroup g( black, x, x.light(), x.dark(), x.dark(120),  black, white );
    QPalette p( g, g, g );
    a.setPalette( p );
#endif

    XFormCenter *xfc = new XFormCenter;
    xfc->setGeometry( 0, 0, 500, 400 );

    a.setMainWidget( xfc );
    xfc->show();
    return a.exec();
}

#include "xform.moc"                  // include metadata generated by the moc


Copyright © 1998 Troll TechTrademarks
Qt version 1.42