spinbox2.cpp 24.5 KB
Newer Older
1 2 3
/*
 *  spinbox2.cpp  -  spin box with extra pair of spin buttons (for Qt 3)
 *  Program:  kalarm
4
 *  Copyright © 2001-2009,2011 by David Jarvie <djarvie@kde.org>
5 6 7 8 9 10 11 12 13 14 15
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
David Jarvie's avatar
David Jarvie committed
16 17 18
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 20
 */

David Jarvie's avatar
David Jarvie committed
21 22
#include "kalarm.h"

Laurent Montel's avatar
Laurent Montel committed
23 24
#include "spinbox2.h"
#include "spinbox2_p.h"
25

Laurent Montel's avatar
Qt3to4  
Laurent Montel committed
26
#include <QMouseEvent>
David Jarvie's avatar
David Jarvie committed
27
#include <QStyleOptionSpinBox>
28
#include <QGraphicsPixmapItem>
29
#include <QPainter>
30
#include <QTimer>
Laurent Montel's avatar
Laurent Montel committed
31
#include <QFrame>
32
#include <QBrush>
33 34 35 36 37
#include <QStyle>
#include <QObject>
#include <QApplication>
#include <QPixmap>
#include <QMatrix>
David Jarvie's avatar
David Jarvie committed
38
#include <qdebug.h>
David Jarvie's avatar
David Jarvie committed
39

40
#include <stdlib.h>
41

42 43 44 45
/* List of styles which look better using spin buttons mirrored left-to-right.
 * This is needed for some styles which use rounded corners.
 */
static const char* mirrorStyles[] = {
46 47
    "QPlastiqueStyle", "QCleanlooksStyle",
    0    // list terminator
48 49
};
static bool isMirrorStyle(const QStyle*);
50 51
static bool isOxygenStyle(const QWidget*);
static QRect spinBoxEditFieldRect(const QWidget*, const QStyleOptionSpinBox&);
52

David Jarvie's avatar
David Jarvie committed
53 54
static inline QPixmap grabWidget(QWidget* w, QRect r = QRect())
{
55 56 57
    QPixmap p(r.isEmpty() ? w->size() : r.size());
    w->render(&p, QPoint(0,0), r, QWidget::DrawWindowBackground | QWidget::DrawChildren | QWidget::IgnoreMask);
    return p;
David Jarvie's avatar
David Jarvie committed
58
}
59

David Jarvie's avatar
David Jarvie committed
60
int SpinBox2::mRightToLeft = -1;
61

David Jarvie's avatar
David Jarvie committed
62
SpinBox2::SpinBox2(QWidget* parent)
63 64
    : QFrame(parent),
      mReverseWithLayout(true)
65
{
66 67 68 69 70
    mSpinboxFrame = new QFrame(this);
    mUpdown2 = new ExtraSpinBox(this);
//    mSpinbox = new MainSpinBox(0, 1, this, mSpinboxFrame);
    mSpinbox = new MainSpinBox(this, mSpinboxFrame);
    init();
71 72
}

73
SpinBox2::SpinBox2(int minValue, int maxValue, int pageStep, QWidget* parent)
74 75
    : QFrame(parent),
      mReverseWithLayout(true)
76
{
77 78 79 80 81
    mSpinboxFrame = new QFrame(this);
    mUpdown2 = new ExtraSpinBox(minValue, maxValue, this);
    mSpinbox = new MainSpinBox(minValue, maxValue, this, mSpinboxFrame);
    setSteps(1, pageStep);
    init();
82 83 84 85
}

void SpinBox2::init()
{
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
    if (mRightToLeft < 0)
        mRightToLeft = QApplication::isRightToLeft() ? 1 : 0;
    mMinValue        = mSpinbox->minimum();
    mMaxValue        = mSpinbox->maximum();
    mSingleStep      = mSpinbox->singleStep();
    mSingleShiftStep = mSpinbox->singleShiftStep();
    mPageStep        = mUpdown2->singleStep();
    mPageShiftStep   = mUpdown2->singleShiftStep();
    mSpinbox->setSelectOnStep(false);    // default
    mUpdown2->setSelectOnStep(false);    // always false
    setFocusProxy(mSpinbox);
    mUpdown2->setFocusPolicy(Qt::NoFocus);
    mSpinMirror = new SpinMirror(mUpdown2, mSpinbox, this);
    mSpinbox->installEventFilter(this);
    mUpdown2->installEventFilter(this);
    connect(mSpinbox, SIGNAL(valueChanged(int)), SLOT(valueChange()));
    connect(mSpinbox, SIGNAL(valueChanged(int)), SIGNAL(valueChanged(int)));
Laurent Montel's avatar
Laurent Montel committed
103
    connect(mSpinbox, SIGNAL(valueChanged(QString)), SIGNAL(valueChanged(QString)));
104 105
    connect(mUpdown2, SIGNAL(stepped(int)), SLOT(stepPage(int)));
    connect(mUpdown2, SIGNAL(painted()), SLOT(paintTimer()));
106 107 108 109
}

void SpinBox2::setReadOnly(bool ro)
{
110 111 112 113 114 115
    if (static_cast<int>(ro) != static_cast<int>(mSpinbox->isReadOnly()))
    {
        mSpinbox->setReadOnly(ro);
        mUpdown2->setReadOnly(ro);
        mSpinMirror->setReadOnly(ro);
    }
116 117 118 119
}

void SpinBox2::setReverseWithLayout(bool reverse)
{
120 121 122 123 124 125
    if (reverse != mReverseWithLayout)
    {
        mReverseWithLayout = reverse;
        setSteps(mSingleStep, mPageStep);
        setShiftSteps(mSingleShiftStep, mPageShiftStep);
    }
126 127 128 129
}

void SpinBox2::setEnabled(bool enabled)
{
130 131 132 133
    QFrame::setEnabled(enabled);
    mSpinbox->setEnabled(enabled);
    mUpdown2->setEnabled(enabled);
    updateMirror();
134 135 136 137
}

void SpinBox2::setWrapping(bool on)
{
138 139
    mSpinbox->setWrapping(on);
    mUpdown2->setWrapping(on);
140 141 142 143
}

QRect SpinBox2::up2Rect() const
{
144
    return mUpdown2->upRect();
145 146 147 148
}

QRect SpinBox2::down2Rect() const
{
149
    return mUpdown2->downRect();
150 151
}

David Jarvie's avatar
David Jarvie committed
152
void SpinBox2::setSingleStep(int step)
153
{
154 155 156 157 158
    mSingleStep = step;
    if (reverseButtons())
        mUpdown2->setSingleStep(step);   // reverse layout, but still set the right buttons
    else
        mSpinbox->setSingleStep(step);
159 160
}

David Jarvie's avatar
David Jarvie committed
161
void SpinBox2::setSteps(int single, int page)
162
{
163 164 165 166 167 168 169 170 171 172 173 174
    mSingleStep = single;
    mPageStep   = page;
    if (reverseButtons())
    {
        mUpdown2->setSingleStep(single);   // reverse layout, but still set the right buttons
        mSpinbox->setSingleStep(page);
    }
    else
    {
        mSpinbox->setSingleStep(single);
        mUpdown2->setSingleStep(page);
    }
175 176
}

David Jarvie's avatar
David Jarvie committed
177
void SpinBox2::setShiftSteps(int single, int page)
178
{
179 180 181 182 183 184 185 186 187 188 189 190
    mSingleShiftStep = single;
    mPageShiftStep   = page;
    if (reverseButtons())
    {
        mUpdown2->setSingleShiftStep(single);   // reverse layout, but still set the right buttons
        mSpinbox->setSingleShiftStep(page);
    }
    else
    {
        mSpinbox->setSingleShiftStep(single);
        mUpdown2->setSingleShiftStep(page);
    }
191 192 193 194
}

void SpinBox2::setButtonSymbols(QSpinBox::ButtonSymbols newSymbols)
{
195 196 197 198
    if (mSpinbox->buttonSymbols() == newSymbols)
        return;
    mSpinbox->setButtonSymbols(newSymbols);
    mUpdown2->setButtonSymbols(newSymbols);
199 200 201 202
}

int SpinBox2::bound(int val) const
{
203
    return (val < mMinValue) ? mMinValue : (val > mMaxValue) ? mMaxValue : val;
204 205
}

David Jarvie's avatar
David Jarvie committed
206
void SpinBox2::setMinimum(int val)
207
{
208 209 210
    mMinValue = val;
    mSpinbox->setMinimum(val);
    mUpdown2->setMinimum(val);
211 212
}

David Jarvie's avatar
David Jarvie committed
213
void SpinBox2::setMaximum(int val)
214
{
215 216 217
    mMaxValue = val;
    mSpinbox->setMaximum(val);
    mUpdown2->setMaximum(val);
218 219 220 221
}

void SpinBox2::valueChange()
{
222 223 224 225 226
    int val = mSpinbox->value();
    bool blocked = mUpdown2->signalsBlocked();
    mUpdown2->blockSignals(true);
    mUpdown2->setValue(val);
    mUpdown2->blockSignals(blocked);
227 228 229 230 231 232 233 234 235
}

/******************************************************************************
* Called when the widget is about to be displayed.
* (At construction time, the spin button widths cannot be determined correctly,
*  so we need to wait until now to definitively rearrange the widget.)
*/
void SpinBox2::showEvent(QShowEvent*)
{
236 237 238
    setUpdown2Size();   // set the new size of the second pair of spin buttons
    arrange();
    mSpinMirror->setFrame();
239 240 241 242
}

QSize SpinBox2::sizeHint() const
{
243 244 245 246
    getMetrics();
    QSize size = mSpinbox->sizeHint();
    size.setWidth(size.width() - wSpinboxHide + wUpdown2);
    return size;
247 248 249 250
}

QSize SpinBox2::minimumSizeHint() const
{
251 252 253 254
    getMetrics();
    QSize size = mSpinbox->minimumSizeHint();
    size.setWidth(size.width() - wSpinboxHide + wUpdown2);
    return size;
255 256 257 258
}

void SpinBox2::styleChange(QStyle&)
{
259 260 261
    setUpdown2Size();   // set the new size of the second pair of spin buttons
    arrange();
    mSpinMirror->setFrame();
262 263
}

264 265
void SpinBox2::paintEvent(QPaintEvent* e)
{
266 267
    QFrame::paintEvent(e);
    QTimer::singleShot(0, this, SLOT(updateMirrorFrame()));
268 269 270 271
}

void SpinBox2::paintTimer()
{
272
    QTimer::singleShot(0, this, SLOT(updateMirrorButtons()));
273 274 275 276
}

void SpinBox2::updateMirrorButtons()
{
277
    mSpinMirror->setButtons();
278 279 280 281
}

void SpinBox2::updateMirrorFrame()
{
282
    mSpinMirror->setFrame();
283 284 285 286
}

void SpinBox2::spinboxResized(QResizeEvent* e)
{
287 288 289 290 291 292
    int h = e->size().height();
    if (h != mUpdown2->height())
    {
        mUpdown2->setFixedSize(mUpdown2->width(), e->size().height());
        setUpdown2Size();
    }
293 294 295 296 297 298 299 300
}

/******************************************************************************
* Set the size of the second spin button widget.
* It is necessary to fix the size to avoid infinite recursion in arrange().
*/
void SpinBox2::setUpdown2Size()
{
301
    mSpinMirror->setButtons();
302 303
}

304 305 306 307 308 309
/******************************************************************************
* Called when the extra pair of spin buttons has repainted after a style change. 
* Updates the mirror image of the spin buttons.
*/
void SpinBox2::updateMirror()
{
310 311
    mSpinMirror->setButtons();
    mSpinMirror->setFrame();
312 313
}

David Jarvie's avatar
David Jarvie committed
314 315
bool SpinBox2::eventFilter(QObject* obj, QEvent* e)
{
316 317 318
    bool updateButtons = false;
    if (obj == mSpinbox)
    {
Laurent Montel's avatar
Laurent Montel committed
319
//if (e->type() != QEvent::Paint) qDebug()<<e->type();
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
        switch (e->type())
        {
            case QEvent::Enter:
            case QEvent::Leave:
                QApplication::postEvent(mUpdown2, new QEvent(e->type()));
                updateButtons = true;
                break;
            case QEvent::HoverEnter:
            {
                QHoverEvent* he = (QHoverEvent*)e;
                QApplication::postEvent(mUpdown2, new QHoverEvent(e->type(), QPoint(1, he->pos().y()), he->oldPos()));
                updateButtons = true;
                break;
            }
            case QEvent::HoverLeave:
            {
                QHoverEvent* he = (QHoverEvent*)e;
                QApplication::postEvent(mUpdown2, new QHoverEvent(e->type(), he->pos(), QPoint(1, he->oldPos().y())));
                updateButtons = true;
                break;
            }
            case QEvent::FocusIn:
            case QEvent::FocusOut:
            {
                QFocusEvent* fe = (QFocusEvent*)e;
                QApplication::postEvent(mUpdown2, new QFocusEvent(e->type(), fe->reason()));
                updateButtons = true;
                break;
            }
            default:
                break;
        }
    }
    else if (obj == mUpdown2)
    {
        switch (e->type())
        {
            case QEvent::Enter:
            case QEvent::Leave:
            case QEvent::HoverEnter:
            case QEvent::HoverLeave:
            case QEvent::EnabledChange:
                updateButtons = true;
                break;
            default:
                break;
        }
    }
    if (updateButtons)
        QTimer::singleShot(0, this, SLOT(updateMirrorButtons()));
    return false;
David Jarvie's avatar
David Jarvie committed
371 372
}

373 374 375 376 377
/******************************************************************************
* Set the positions and sizes of all the child widgets. 
*/
void SpinBox2::arrange()
{
378 379 380 381 382 383 384 385
    getMetrics();
    mUpdown2->move(-mUpdown2->width(), 0);   // keep completely hidden
    QRect arrowRect = style()->visualRect((mRightToLeft ? Qt::RightToLeft : Qt::LeftToRight), rect(), QRect(0, 0, wUpdown2, height()));
    QRect r(wUpdown2, 0, width() - wUpdown2, height());
    if (mRightToLeft)
        r.moveLeft(0);
    mSpinboxFrame->setGeometry(r);
    mSpinbox->setGeometry(mRightToLeft ? 0 : -wSpinboxHide, 0, mSpinboxFrame->width() + wSpinboxHide, height());
Laurent Montel's avatar
Laurent Montel committed
386
//    qDebug() << "arrowRect="<<arrowRect<<", mUpdown2="<<mUpdown2->geometry()<<", mSpinboxFrame="<<mSpinboxFrame->geometry()<<", mSpinbox="<<mSpinbox->geometry()<<", width="<<width();
David Jarvie's avatar
David Jarvie committed
387

388 389 390 391
    mSpinMirror->resize(wUpdown2, mUpdown2->height());
    mSpinMirror->setGeometry(arrowRect);
    mSpinMirror->setButtonPos(mButtonPos);
    mSpinMirror->setButtons();
392 393 394 395 396 397 398 399
}

/******************************************************************************
* Calculate the width and position of the extra pair of spin buttons.
* Style-specific adjustments are made for a better appearance. 
*/
void SpinBox2::getMetrics() const
{
400 401 402 403 404 405 406
    QStyleOptionSpinBox option;
    mUpdown2->initStyleOption(option);
    QStyle* udStyle = mUpdown2->style();
    QRect butRect = udStyle->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxUp)
                  | udStyle->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxDown);
    if (style()->inherits("PlastikStyle"))
        butRect.setLeft(butRect.left() - 1);    // Plastik excludes left border from spin widget rectangle
407
    QRect r = spinBoxEditFieldRect(mSpinbox, option);
408
    wSpinboxHide = mRightToLeft ? mSpinbox->style()->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxFrame).right() - r.right() : r.left();
409
    QRect edRect = spinBoxEditFieldRect(mUpdown2, option);
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
    int butx;
    if (isMirrorStyle(udStyle))
    {
        if (mRightToLeft)
        {
            wUpdown2 = edRect.left();
            butx = butRect.left();
        }
        else
        {
            int x = edRect.right() + 1;
            wUpdown2 = mUpdown2->width() - x;
            butx = butRect.left() - x;
        }
    }
    else
    {
        r = udStyle->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxFrame);
        if (mRightToLeft)
        {
            wUpdown2 = edRect.left() - r.left();
            butx = wUpdown2 - (butRect.left() - r.left() + butRect.width());
        }
        else
        {
            wUpdown2 = r.width() - edRect.right() - 1;
            butx = r.right() - butRect.right();
        }
    }
    mButtonPos = QPoint(butx, butRect.top());
Laurent Montel's avatar
Laurent Montel committed
440
//    qDebug() << "wUpdown2="<<wUpdown2<<", wSpinboxHide="<<wSpinboxHide<<", frame right="<<r.right() - butRect.right();
441 442 443 444 445 446 447 448 449
}

/******************************************************************************
* Called when the extra pair of spin buttons is clicked to step the value.
* Normally this is a page step, but with a right-to-left language where the
* button functions are reversed, this is a line step.
*/
void SpinBox2::stepPage(int step)
{
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
    if (abs(step) == mUpdown2->singleStep())
        mSpinbox->setValue(mUpdown2->value());
    else
    {
        // It's a shift step
        int oldValue = mSpinbox->value();
        if (!reverseButtons())
        {
            // The button pairs have the normal function.
            // Page shift stepping - step up or down to a multiple of the
            // shift page increment, leaving unchanged the part of the value
            // which is the remainder from the page increment.
            if (oldValue >= 0)
                oldValue -= oldValue % mUpdown2->singleStep();
            else
                oldValue += (-oldValue) % mUpdown2->singleStep();
        }
        int adjust = mSpinbox->shiftStepAdjustment(oldValue, step);
        if (adjust == -step
        &&  ((step > 0  &&  oldValue + step >= mSpinbox->maximum())
          || (step < 0  &&  oldValue + step <= mSpinbox->minimum())))
            adjust = 0;    // allow stepping to the minimum or maximum value
        mSpinbox->addValue(adjust + step);
    }
    mSpinbox->setFocus();
    if (mSpinbox->selectOnStep())
        mSpinbox->selectAll();

    // Make the covering arrows image show the pressed arrow
    mSpinMirror->setButtons();
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
}


/*=============================================================================
= Class SpinBox2::MainSpinBox
=============================================================================*/

/******************************************************************************
* Return the initial adjustment to the value for a shift step up or down, for
* the main (visible) spin box.
* Normally this is a line step, but with a right-to-left language where the
* button functions are reversed, this is a page step.
*/
int SpinBox2::MainSpinBox::shiftStepAdjustment(int oldValue, int shiftStep)
{
495 496 497 498 499 500 501 502 503 504 505 506
    if (owner->reverseButtons())
    {
        // The button pairs have the opposite function from normal.
        // Page shift stepping - step up or down to a multiple of the
        // shift page increment, leaving unchanged the part of the value
        // which is the remainder from the page increment.
        if (oldValue >= 0)
            oldValue -= oldValue % singleStep();
        else
            oldValue += (-oldValue) % singleStep();
    }
    return SpinBox::shiftStepAdjustment(oldValue, shiftStep);
507 508 509 510 511 512 513 514 515 516 517 518
}


/*=============================================================================
= Class ExtraSpinBox
=============================================================================*/

/******************************************************************************
* Repaint the widget.
*/
void ExtraSpinBox::paintEvent(QPaintEvent* e)
{
519 520 521 522 523
    SpinBox::paintEvent(e);
    if (!mInhibitPaintSignal)
        emit painted();
    else
        --mInhibitPaintSignal;
524 525 526 527 528 529 530
}


/*=============================================================================
= Class SpinMirror
=============================================================================*/

David Jarvie's avatar
David Jarvie committed
531
SpinMirror::SpinMirror(ExtraSpinBox* spinbox, SpinBox* mainspin, QWidget* parent)
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
    : QGraphicsView(parent),
      mSpinbox(spinbox),
      mMainSpinbox(mainspin),
      mReadOnly(false),
      mMirrored(false)
{
    setScene(new QGraphicsScene(this));
    setAttribute(Qt::WA_Hover);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setFrameStyle(QFrame::NoFrame);
    setMouseTracking(mSpinbox->hasMouseTracking());
    mButtons = scene()->addPixmap(QPixmap());
    mButtons->setZValue(1);
    mButtons->setAcceptedMouseButtons(Qt::LeftButton);
    mMirrored = isMirrorStyle(style());
    setMirroredState();
549 550 551 552
}

void SpinMirror::setMirroredState(bool clear)
{
553 554 555 556 557
    // Some styles only look right when the buttons are mirrored
    if (mMirrored)
        setMatrix(QMatrix(-1, 0, 0, 1, width() - 1 , 0));  // mirror left to right
    else if (clear)
        setMatrix(QMatrix());
558 559
}

David Jarvie's avatar
David Jarvie committed
560
void SpinMirror::setFrame()
561
{
562 563 564 565 566 567 568 569
    // Paint the left hand frame of the main spinbox.
    // Use the part to the left of the edit field, plus a slice at
    // the left of the edit field stretched for the rest of the width.
    // This avoids possibly grabbing text and displaying it in the
    // spin button area.
    QGraphicsScene* c = scene();
    QStyleOptionSpinBox option;
    option.initFrom(mMainSpinbox);
570
    QRect r = spinBoxEditFieldRect(mMainSpinbox, option);
571 572 573 574 575 576 577 578 579
    bool rtl = QApplication::isRightToLeft();
    QPixmap p;
    if (mMirrored)
    {
        int x = rtl ? 0 : mMainSpinbox->width() - width();
        p = grabWidget(mMainSpinbox, QRect(x, 0, width(), height()));
    }
    else
    {
580 581 582 583 584
        // Grab a single pixel wide vertical slice through the main spinbox, between the
        // frame and edit field.
        bool oxygen  = mMainSpinbox->style()->inherits("Oxygen::Style"); // KDE >= 4.4 Oxygen style
        bool oxygen1 = mMainSpinbox->style()->inherits("OxygenStyle");   // KDE <= 4.3 Oxygen style
        int editOffsetY = oxygen ? 5 : oxygen1 ? 6 : 2;   // offset to edit field
Laurent Montel's avatar
Laurent Montel committed
585
        int editOffsetX = (oxygen || oxygen1) ? 4 : 2;   // offset to edit field
586 587 588 589
        int x = rtl ? r.right() - editOffsetX : r.left() + editOffsetX;
        p = grabWidget(mMainSpinbox, QRect(x, 0, 1, height()));
        // Blot out edit field stuff from the middle of the slice
        QPixmap dot = grabWidget(mMainSpinbox, QRect(x, editOffsetY, 1, 1));
590 591 592
        QPainter painter(&p);
        painter.drawTiledPixmap(0, editOffsetY, 1, height() - 2*editOffsetY, dot, 0, 0);
        painter.end();
593 594 595
        // Horizontally fill the mirror widget with the vertical slice
        p = p.scaled(size());
        // Grab the left hand border of the main spinbox, and draw it into the mirror widget.
596 597 598 599
        QRect endr = rect();
        if (rtl)
        {
            int mr = mMainSpinbox->width() - 1;
600
            endr.setWidth(mr - r.right() + editOffsetX);
601 602 603
            endr.moveRight(mr);
        }
        else
604
            endr.setWidth(r.left() + editOffsetX);
605 606 607 608
        x = rtl ? width() - endr.width() : 0;
        mMainSpinbox->render(&p, QPoint(x, 0), endr, QWidget::DrawWindowBackground | QWidget::DrawChildren | QWidget::IgnoreMask);
    }
    c->setBackgroundBrush(p);
609 610
}

David Jarvie's avatar
David Jarvie committed
611
void SpinMirror::setButtons()
612
{
613 614 615 616 617 618
    mSpinbox->inhibitPaintSignal(2);
    QStyleOptionSpinBox option;
    mSpinbox->initStyleOption(option);
    QStyle* st = mSpinbox->style();
    QRect r = st->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxUp)
            | st->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxDown);
619
    if (isOxygenStyle(mSpinbox))
620 621 622 623 624 625 626 627 628
    {
        // They don't use all their height, so shorten them to 
        // allow frame highlighting to work properly.
        r.setTop(r.top() + 1);
        r.setHeight(r.height() - 2);
    }
    mSpinbox->inhibitPaintSignal(1);
    mButtons->setPixmap(grabWidget(mSpinbox, r));
    mSpinbox->inhibitPaintSignal(0);
629 630 631 632
}

void SpinMirror::setButtonPos(const QPoint& pos)
{
Laurent Montel's avatar
Laurent Montel committed
633
    //qDebug()<<pos;
634 635
    int x = pos.x();
    int y = pos.y();
636
    if (isOxygenStyle(this))
637 638 639 640 641 642 643
    {
        // Oxygen spin buttons don't use all their height. Prevent
        // the top overlapping the frame highlighting. Their height
        // is shortened in setButton() above.
        ++y;
    }
    mButtons->setPos(x, y);
644 645
}

646
void SpinMirror::resizeEvent(QResizeEvent* e)
647
{
648 649 650
    QSize sz = e->size();
    scene()->setSceneRect(0, 0, sz.width(), sz.height());
    setMirroredState();
651 652 653 654
}

void SpinMirror::styleChange(QStyle& st)
{
655 656
    mMirrored = isMirrorStyle(&st);
    setMirroredState(true);
657 658 659
}

/******************************************************************************
David Jarvie's avatar
David Jarvie committed
660 661
* Pass on to the extra spinbox all mouse events which occur over the spin
* button area.
662
*/
663
void SpinMirror::mouseEvent(QMouseEvent* e)
664
{
665 666 667 668 669 670 671 672 673
    if (mReadOnly)
        return;
    QPoint pt = e->pos();
    QGraphicsItem* item = scene()->itemAt(pt);
    if (item == mButtons)
        pt = spinboxPoint(pt);
    else
        pt = QPoint(0, 0);  // allow auto-repeat to stop
    QApplication::postEvent(mSpinbox, new QMouseEvent(e->type(), pt, e->button(), e->buttons(), e->modifiers()));
674 675
}

676 677 678 679 680 681
/******************************************************************************
* Pass on to the extra spinbox all wheel events which occur over the spin
* button area.
*/
void SpinMirror::wheelEvent(QWheelEvent* e)
{
682 683 684 685 686 687 688 689 690
    if (mReadOnly)
        return;
    QPoint pt = e->pos();
    QGraphicsItem* item = scene()->itemAt(pt);
    if (item == mButtons)
    {
        pt = spinboxPoint(pt);
        QApplication::postEvent(mSpinbox, new QWheelEvent(pt, e->delta(), e->buttons(), e->modifiers()), e->orientation());
    }
691 692
}

693 694 695 696 697
/******************************************************************************
* Translate SpinMirror coordinates to those of the mirrored spinbox.
*/
QPoint SpinMirror::spinboxPoint(const QPoint& param) const
{
698 699 700 701 702 703
    QRect r = mSpinbox->upRect();
    QPointF ptf = mButtons->mapFromScene(param.x(), param.y());
    QPoint pt(ptf.x(), ptf.y());
    pt.setX(ptf.x() + r.left());
    pt.setY(ptf.y() + r.top());
    return pt;
704 705
}

David Jarvie's avatar
David Jarvie committed
706 707 708 709
/******************************************************************************
* Pass on to the main spinbox events which are needed to activate mouseover and
* other graphic effects when the mouse cursor enters and leaves the widget.
*/
David Jarvie's avatar
David Jarvie committed
710 711
bool SpinMirror::event(QEvent* e)
{
Laurent Montel's avatar
Laurent Montel committed
712
//qDebug()<<e->type();
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
    QHoverEvent *he = 0;
    switch (e->type())
    {
        case QEvent::Leave:
            if (mMainSpinbox->rect().contains(mMainSpinbox->mapFromGlobal(QCursor::pos())))
                break;
            // fall through to QEvent::Enter
        case QEvent::Enter:
            QApplication::postEvent(mMainSpinbox, new QEvent(e->type()));
            break;
        case QEvent::HoverLeave:
            he = (QHoverEvent*)e;
            if (mMainSpinbox->rect().contains(mMainSpinbox->mapFromGlobal(QCursor::pos())))
                break;
            // fall through to QEvent::HoverEnter
        case QEvent::HoverEnter:
            he = (QHoverEvent*)e;
            QApplication::postEvent(mMainSpinbox, new QHoverEvent(e->type(), he->pos(), he->oldPos()));
            break;
        case QEvent::HoverMove:
            he = (QHoverEvent*)e;
            break;
        case QEvent::FocusIn:
            mMainSpinbox->setFocus();
            break;
        default:
            break;
    }

    if (he)
    {
        QApplication::postEvent(mSpinbox, new QHoverEvent(e->type(), spinboxPoint(he->pos()), spinboxPoint(he->oldPos())));
        setButtons();
    }

    return QGraphicsView::event(e);
David Jarvie's avatar
David Jarvie committed
749
}
750 751 752 753 754 755 756 757


/******************************************************************************
* Determine whether the extra pair of spin buttons needs to be mirrored
* left-to-right in the specified style.
*/
static bool isMirrorStyle(const QStyle* style)
{
758 759 760 761
    for (const char** s = mirrorStyles;  *s;  ++s)
        if (style->inherits(*s))
            return true;
    return false;
762 763
}

764 765 766 767 768 769 770 771 772 773
static bool isOxygenStyle(const QWidget* w)
{
    return w->style()->inherits("Oxygen::Style")  ||  w->style()->inherits("OxygenStyle");
}

static QRect spinBoxEditFieldRect(const QWidget* w, const QStyleOptionSpinBox& option)
{
    QRect r = w->style()->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxEditField);
    if (isOxygenStyle(w))
    {
Laurent Montel's avatar
Laurent Montel committed
774
        int xadjust = 3;
775 776 777 778
        r.adjust(xadjust, 2, -xadjust, -2);
    }
    return r;
}
779 780


781
// vim: et sw=4: