00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "dialog.h"
00024
00025 #include <QPainter>
00026 #include <QSvgRenderer>
00027 #include <QResizeEvent>
00028 #include <QMouseEvent>
00029 #ifdef Q_WS_X11
00030 #include <QX11Info>
00031 #endif
00032 #include <QBitmap>
00033 #include <QTimer>
00034 #include <QtGui/QVBoxLayout>
00035 #include <QtGui/QGraphicsSceneEvent>
00036 #include <QtGui/QGraphicsView>
00037 #include <QtGui/QGraphicsWidget>
00038 #include <QApplication>
00039 #include <QDesktopWidget>
00040
00041 #include <kdebug.h>
00042 #include <kwindowsystem.h>
00043 #include <netwm.h>
00044
00045 #include "plasma/applet.h"
00046 #include "plasma/animator.h"
00047 #include "plasma/extender.h"
00048 #include "plasma/private/extender_p.h"
00049 #include "plasma/framesvg.h"
00050 #include "plasma/theme.h"
00051
00052 #ifdef Q_WS_X11
00053 #include <X11/Xlib.h>
00054 #endif
00055
00056 namespace Plasma
00057 {
00058
00059 class DialogPrivate
00060 {
00061 public:
00062 DialogPrivate(Dialog *dialog)
00063 : q(dialog),
00064 background(0),
00065 view(0),
00066 graphicsWidget(0),
00067 resizeCorners(Dialog::NoCorner),
00068 resizeStartCorner(Dialog::NoCorner),
00069 moveTimer(0),
00070 hideAnimId(0)
00071 {
00072 }
00073
00074 ~DialogPrivate()
00075 {
00076 }
00077
00078 void themeChanged();
00079 void adjustView();
00080 void updateResizeCorners();
00081 void progressHide(qreal amount);
00082 void progressShow(qreal amount);
00083
00084 Plasma::Dialog *q;
00085
00090 Plasma::FrameSvg *background;
00091 QGraphicsView *view;
00092 QGraphicsWidget *graphicsWidget;
00093 Dialog::ResizeCorners resizeCorners;
00094 QMap<Dialog::ResizeCorner, QRect> resizeAreas;
00095 Dialog::ResizeCorner resizeStartCorner;
00096 QTimer *moveTimer;
00097 Direction hideDirection;
00098 int hideAnimId;
00099 };
00100
00101 void DialogPrivate::themeChanged()
00102 {
00103 qreal topHeight;
00104 qreal leftWidth;
00105 qreal rightWidth;
00106 qreal bottomHeight;
00107
00108
00109
00110 FrameSvg::EnabledBorders borders = FrameSvg::AllBorders;
00111
00112 Extender *extender = qobject_cast<Extender*>(graphicsWidget);
00113 if (extender) {
00114 background->getMargins(leftWidth, topHeight, rightWidth, bottomHeight);
00115
00116 switch (extender->d->applet->location()) {
00117 case BottomEdge:
00118 borders ^= FrameSvg::BottomBorder;
00119 leftWidth = 0;
00120 rightWidth = 0;
00121 bottomHeight = 0;
00122 break;
00123
00124 case TopEdge:
00125 borders ^= FrameSvg::TopBorder;
00126 topHeight = 0;
00127 leftWidth = 0;
00128 rightWidth = 0;
00129 break;
00130
00131 case LeftEdge:
00132 borders ^= FrameSvg::LeftBorder;
00133 leftWidth = 0;
00134 rightWidth = 0;
00135 break;
00136
00137 case RightEdge:
00138 borders ^= FrameSvg::RightBorder;
00139 leftWidth = 0;
00140 rightWidth = 0;
00141 break;
00142
00143 default:
00144 break;
00145 }
00146 } else if (q->isVisible()) {
00147 QDesktopWidget *desktop = QApplication::desktop();
00148 QRect avail = desktop->availableGeometry(desktop->screenNumber(q));
00149 QRect dialogGeom = q->geometry();
00150
00151 if (dialogGeom.left() <= avail.left()) {
00152 borders ^= FrameSvg::LeftBorder;
00153 }
00154 if (dialogGeom.top() <= avail.top()) {
00155 borders ^= FrameSvg::TopBorder;
00156 }
00157
00158 if (dialogGeom.right() + 2 > avail.right()) {
00159 borders ^= FrameSvg::RightBorder;
00160 }
00161 if (dialogGeom.bottom() + 2 > avail.bottom()) {
00162 borders ^= FrameSvg::BottomBorder;
00163 }
00164 }
00165
00166 background->setEnabledBorders(borders);
00167
00168 if (!extender) {
00169 background->getMargins(leftWidth, topHeight, rightWidth, bottomHeight);
00170 }
00171
00172 q->setContentsMargins(leftWidth, topHeight, rightWidth, bottomHeight);
00173 q->update();
00174 }
00175
00176 void DialogPrivate::adjustView()
00177 {
00178 if (view && graphicsWidget) {
00179 QSize prevSize = q->size();
00180
00181
00182
00183
00184
00185
00186
00187
00188 int left, top, right, bottom;
00189 q->getContentsMargins(&left, &top, &right, &bottom);
00190
00191 q->setMinimumSize(qMin(int(graphicsWidget->minimumSize().width()) + left + right, QWIDGETSIZE_MAX),
00192 qMin(int(graphicsWidget->minimumSize().height()) + top + bottom, QWIDGETSIZE_MAX));
00193 q->setMaximumSize(qMin(int(graphicsWidget->maximumSize().width()) + left + right, QWIDGETSIZE_MAX),
00194 qMin(int(graphicsWidget->maximumSize().height()) + top + bottom, QWIDGETSIZE_MAX));
00195 q->resize(qMin(int(graphicsWidget->size().width()) + left + right, QWIDGETSIZE_MAX),
00196 qMin(int(graphicsWidget->size().height()) + top + bottom, QWIDGETSIZE_MAX));
00197 q->updateGeometry();
00198
00199
00200
00201 QRectF sceneRect(graphicsWidget->sceneBoundingRect());
00202
00203 sceneRect.setWidth(qMax(qreal(1), sceneRect.width()));
00204 sceneRect.setHeight(qMax(qreal(1), sceneRect.height()));
00205 view->setSceneRect(sceneRect);
00206
00207 view->resize(graphicsWidget->size().toSize());
00208 view->centerOn(graphicsWidget);
00209
00210
00211 qreal topHeight;
00212 qreal leftWidth;
00213 qreal rightWidth;
00214 qreal bottomHeight;
00215
00216 background->getMargins(leftWidth, topHeight, rightWidth, bottomHeight);
00217
00218 if (rightWidth == 0) {
00219 q->move(q->pos().x() + (prevSize.width() - q->size().width()), q->pos().y());
00220 }
00221 if (bottomHeight == 0) {
00222 q->move(q->pos().x(), q->pos().y() + (prevSize.height() - q->size().height()));
00223 }
00224
00225 if (q->size() != prevSize) {
00226
00227 emit q->dialogResized();
00228 }
00229 }
00230 }
00231
00232 Dialog::Dialog(QWidget *parent, Qt::WindowFlags f)
00233 : QWidget(parent, f),
00234 d(new DialogPrivate(this))
00235 {
00236 setAttribute(Qt::WA_TranslucentBackground);
00237 setWindowFlags(Qt::FramelessWindowHint);
00238 d->background = new FrameSvg(this);
00239 d->background->setImagePath("dialogs/background");
00240 d->background->setEnabledBorders(FrameSvg::AllBorders);
00241 d->background->resizeFrame(size());
00242
00243 QPalette pal = palette();
00244 pal.setColor(backgroundRole(), Qt::transparent);
00245 setPalette(pal);
00246
00247 connect(d->background, SIGNAL(repaintNeeded()), this, SLOT(update()));
00248
00249 connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeChanged()));
00250 d->themeChanged();
00251
00252 setMouseTracking(true);
00253 }
00254
00255 Dialog::~Dialog()
00256 {
00257 delete d;
00258 }
00259
00260 void Dialog::paintEvent(QPaintEvent *e)
00261 {
00262 QRect target = e->rect();
00263 QRect source = target;
00264
00265 if (d->hideAnimId) {
00266 target = rect();
00267 source = target;
00268 switch (d->hideDirection) {
00269 case Plasma::Up:
00270 case Plasma::Down: {
00271 int bottomMargin = height() - d->view->geometry().bottom();
00272 target.setBottom(d->view->viewport()->geometry().bottom() - bottomMargin);
00273 source.setTop(-d->view->viewport()->y() + bottomMargin);
00274 }
00275 break;
00276
00277 case Plasma::Right: {
00278 target.setLeft(d->view->viewport()->x());
00279 source.setRight(target.width() - 1);
00280 }
00281 break;
00282
00283 case Plasma::Left: {
00284 target.setWidth(d->view->viewport()->geometry().right() - 1);
00285 source.setLeft(width() - target.width());
00286 }
00287 break;
00288 }
00289 }
00290
00291 QPainter p(this);
00292 p.setCompositionMode(QPainter::CompositionMode_Source);
00293 d->background->paintFrame(&p, target, source);
00294 }
00295
00296 void Dialog::mouseMoveEvent(QMouseEvent *event)
00297 {
00298 if (d->resizeAreas[Dialog::NorthEast].contains(event->pos()) && d->resizeCorners & Dialog::NorthEast) {
00299 setCursor(Qt::SizeBDiagCursor);
00300 } else if (d->resizeAreas[Dialog::NorthWest].contains(event->pos()) && d->resizeCorners & Dialog::NorthWest) {
00301 setCursor(Qt::SizeFDiagCursor);
00302 } else if (d->resizeAreas[Dialog::SouthEast].contains(event->pos()) && d->resizeCorners & Dialog::SouthEast) {
00303 setCursor(Qt::SizeFDiagCursor);
00304 } else if (d->resizeAreas[Dialog::SouthWest].contains(event->pos()) && d->resizeCorners & Dialog::SouthWest) {
00305 setCursor(Qt::SizeBDiagCursor);
00306 } else if (!(event->buttons() & Qt::LeftButton)) {
00307 unsetCursor();
00308 }
00309
00310
00311 if (d->resizeStartCorner != Dialog::NoCorner) {
00312 int newWidth;
00313 int newHeight;
00314 QPoint position;
00315
00316 switch(d->resizeStartCorner) {
00317 case Dialog::NorthEast:
00318 newWidth = qMin(maximumWidth(), qMax(minimumWidth(), event->x()));
00319 newHeight = qMin(maximumHeight(), qMax(minimumHeight(), height() - event->y()));
00320 position = QPoint(x(), y() + height() - newHeight);
00321 break;
00322 case Dialog::NorthWest:
00323 newWidth = qMin(maximumWidth(), qMax(minimumWidth(), width() - event->x()));
00324 newHeight = qMin(maximumHeight(), qMax(minimumHeight(), height() - event->y()));
00325 position = QPoint(x() + width() - newWidth, y() + height() - newHeight);
00326 break;
00327 case Dialog::SouthWest:
00328 newWidth = qMin(maximumWidth(), qMax(minimumWidth(), width() - event->x()));
00329 newHeight = qMin(maximumHeight(), qMax(minimumHeight(), event->y()));
00330 position = QPoint(x() + width() - newWidth, y());
00331 break;
00332 case Dialog::SouthEast:
00333 newWidth = qMin(maximumWidth(), qMax(minimumWidth(), event->x()));
00334 newHeight = qMin(maximumHeight(), qMax(minimumHeight(), event->y()));
00335 position = QPoint(x(), y());
00336 break;
00337 default:
00338 newWidth = qMin(maximumWidth(), qMax(minimumWidth(), width()));
00339 newHeight = qMin(maximumHeight(), qMax(minimumHeight(), height()));
00340 position = QPoint(x(), y());
00341 break;
00342 }
00343
00344 setGeometry(QRect(position, QSize(newWidth, newHeight)));
00345 }
00346
00347 QWidget::mouseMoveEvent(event);
00348 }
00349
00350 void Dialog::mousePressEvent(QMouseEvent *event)
00351 {
00352 if (d->resizeAreas[Dialog::NorthEast].contains(event->pos()) && d->resizeCorners & Dialog::NorthEast) {
00353 d->resizeStartCorner = Dialog::NorthEast;
00354
00355 } else if (d->resizeAreas[Dialog::NorthWest].contains(event->pos()) && d->resizeCorners & Dialog::NorthWest) {
00356 d->resizeStartCorner = Dialog::NorthWest;
00357
00358 } else if (d->resizeAreas[Dialog::SouthEast].contains(event->pos()) && d->resizeCorners & Dialog::SouthEast) {
00359 d->resizeStartCorner = Dialog::SouthEast;
00360
00361 } else if (d->resizeAreas[Dialog::SouthWest].contains(event->pos()) && d->resizeCorners & Dialog::SouthWest) {
00362 d->resizeStartCorner = Dialog::SouthWest;
00363
00364 } else {
00365 d->resizeStartCorner = Dialog::NoCorner;
00366 }
00367
00368 QWidget::mousePressEvent(event);
00369 }
00370
00371 void Dialog::mouseReleaseEvent(QMouseEvent *event)
00372 {
00373 if (d->resizeStartCorner != Dialog::NoCorner) {
00374 d->resizeStartCorner = Dialog::NoCorner;
00375 unsetCursor();
00376 emit dialogResized();
00377 }
00378
00379 QWidget::mouseReleaseEvent(event);
00380 }
00381
00382 void Dialog::keyPressEvent(QKeyEvent *event)
00383 {
00384 if (event->key() == Qt::Key_Escape) {
00385 hide();
00386 }
00387 }
00388
00389 bool Dialog::event(QEvent *event)
00390 {
00391 if (event->type() == QEvent::Paint) {
00392 QPainter p(this);
00393 p.setCompositionMode(QPainter::CompositionMode_Source);
00394 p.fillRect(rect(), Qt::transparent);
00395 }
00396
00397 return QWidget::event(event);
00398 }
00399
00400 void Dialog::resizeEvent(QResizeEvent *e)
00401 {
00402 d->background->resizeFrame(e->size());
00403
00404 setMask(d->background->mask());
00405
00406 if (d->resizeStartCorner != Dialog::NoCorner && d->view && d->graphicsWidget) {
00407 d->graphicsWidget->resize(d->view->size());
00408
00409 QRectF sceneRect(d->graphicsWidget->sceneBoundingRect());
00410 sceneRect.setWidth(qMax(qreal(1), sceneRect.width()));
00411 sceneRect.setHeight(qMax(qreal(1), sceneRect.height()));
00412 d->view->setSceneRect(sceneRect);
00413 d->view->centerOn(d->graphicsWidget);
00414 }
00415
00416 d->updateResizeCorners();
00417 }
00418
00419 void DialogPrivate::updateResizeCorners()
00420 {
00421 const int resizeAreaMargin = 20;
00422 const QRect r = q->rect();
00423
00424 resizeAreas.clear();
00425 if (resizeCorners & Dialog::NorthEast) {
00426 resizeAreas[Dialog::NorthEast] = QRect(r.right() - resizeAreaMargin, 0,
00427 resizeAreaMargin, resizeAreaMargin);
00428 }
00429
00430 if (resizeCorners & Dialog::NorthWest) {
00431 resizeAreas[Dialog::NorthWest] = QRect(0, 0, resizeAreaMargin, resizeAreaMargin);
00432 }
00433
00434 if (resizeCorners & Dialog::SouthEast) {
00435 resizeAreas[Dialog::SouthEast] = QRect(r.right() - resizeAreaMargin,
00436 r.bottom() - resizeAreaMargin,
00437 resizeAreaMargin, resizeAreaMargin);
00438 }
00439
00440 if (resizeCorners & Dialog::SouthWest) {
00441 resizeAreas[Dialog::SouthWest] = QRect(0, r.bottom() - resizeAreaMargin,
00442 resizeAreaMargin, resizeAreaMargin);
00443 }
00444
00445 }
00446
00447 void Dialog::setGraphicsWidget(QGraphicsWidget *widget)
00448 {
00449 if (d->graphicsWidget) {
00450 d->graphicsWidget->removeEventFilter(this);
00451 }
00452
00453 d->graphicsWidget = widget;
00454
00455 if (widget) {
00456 if (!layout()) {
00457 QVBoxLayout *lay = new QVBoxLayout(this);
00458 lay->setMargin(0);
00459 lay->setSpacing(0);
00460 }
00461
00462 d->themeChanged();
00463
00464 if (!d->view) {
00465 d->view = new QGraphicsView(this);
00466 d->view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00467 d->view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00468 d->view->setFrameShape(QFrame::NoFrame);
00469 d->view->viewport()->setAutoFillBackground(false);
00470 layout()->addWidget(d->view);
00471 }
00472
00473 d->view->setScene(widget->scene());
00474 d->adjustView();
00475
00476 adjustSize();
00477
00478 widget->installEventFilter(this);
00479 } else {
00480 delete d->view;
00481 d->view = 0;
00482 }
00483 }
00484
00485 QGraphicsWidget *Dialog::graphicsWidget()
00486 {
00487 return d->graphicsWidget;
00488 }
00489
00490 bool Dialog::eventFilter(QObject *watched, QEvent *event)
00491 {
00492 if (d->resizeStartCorner == Dialog::NoCorner && watched == d->graphicsWidget &&
00493 (event->type() == QEvent::GraphicsSceneResize || event->type() == QEvent::GraphicsSceneMove)) {
00494 d->adjustView();
00495 }
00496
00497 return QWidget::eventFilter(watched, event);
00498 }
00499
00500 void Dialog::hideEvent(QHideEvent * event)
00501 {
00502 Q_UNUSED(event);
00503 emit dialogVisible(false);
00504 }
00505
00506 void Dialog::showEvent(QShowEvent * event)
00507 {
00508 Q_UNUSED(event);
00509
00510
00511 d->themeChanged();
00512
00513 if (d->graphicsWidget && d->view && d->graphicsWidget->size().toSize() != d->view->size()) {
00514 d->adjustView();
00515 }
00516
00517 if (d->view) {
00518 d->view->setFocus();
00519 }
00520
00521 if (d->graphicsWidget) {
00522 d->graphicsWidget->setFocus();
00523 }
00524
00525 emit dialogVisible(true);
00526 }
00527
00528 void Dialog::moveEvent(QMoveEvent *event)
00529 {
00530 Q_UNUSED(event)
00531
00532 if (!d->moveTimer) {
00533 d->moveTimer = new QTimer(this);
00534 d->moveTimer->setSingleShot(true);
00535 connect(d->moveTimer, SIGNAL(timeout()), this, SLOT(themeChanged()));
00536 }
00537
00538 d->moveTimer->start(200);
00539 }
00540
00541 void Dialog::setResizeHandleCorners(ResizeCorners corners)
00542 {
00543 if (d->resizeCorners != corners) {
00544 d->resizeCorners = corners;
00545 d->updateResizeCorners();
00546 }
00547 }
00548
00549 Dialog::ResizeCorners Dialog::resizeCorners() const
00550 {
00551 return d->resizeCorners;
00552 }
00553
00554 void Dialog::animatedHide(Plasma::Direction direction)
00555 {
00556 if (d->hideAnimId) {
00557
00558 return;
00559 }
00560
00561 if (KWindowSystem::compositingActive() && d->view) {
00562
00563 d->hideDirection = direction;
00564 d->hideAnimId = Animator::self()->customAnimation(20, 200, Animator::EaseOutCurve,
00565 this, "progressHide");
00566 } else {
00567 hide();
00568 }
00569 }
00570
00571 void Dialog::animatedShow(Plasma::Direction direction)
00572 {
00573 if (d->hideAnimId) {
00574
00575 return;
00576 }
00577
00578 if (KWindowSystem::compositingActive() && d->view) {
00579
00580 d->hideDirection = direction;
00581 d->hideAnimId = Animator::self()->customAnimation(5, 100, Animator::EaseInCurve,
00582 this, "progressShow");
00583 } else {
00584 show();
00585 }
00586 }
00587
00588 void DialogPrivate::progressShow(qreal amount)
00589 {
00590
00591 if (qFuzzyCompare(amount, qreal(1.0))) {
00592 view->viewport()->move(0, 0);
00593 hideAnimId = 0;
00594 q->update();
00595 q->show();
00596 return;
00597 }
00598
00599 int xtrans = 0;
00600 int ytrans = 0;
00601
00602 switch (hideDirection) {
00603 case Plasma::Up:
00604 ytrans = (1.0 - amount) * view->height();
00605 break;
00606
00607 case Plasma::Down:
00608 ytrans = (amount * view->height()) - view->height();
00609 break;
00610
00611 case Plasma::Right:
00612 xtrans = (amount * view->width()) - view->width();
00613 break;
00614
00615 case Plasma::Left:
00616 xtrans = (1.0 - amount) * view->width();
00617 break;
00618 }
00619
00620 view->viewport()->move(xtrans, ytrans);
00621 q->update();
00622 q->show();
00623 }
00624
00625 void DialogPrivate::progressHide(qreal amount)
00626 {
00627
00628 if (qFuzzyCompare(amount, qreal(1.0))) {
00629 q->hide();
00630 view->viewport()->move(0, 0);
00631 hideAnimId = 0;
00632 return;
00633 }
00634
00635 int xtrans = 0;
00636 int ytrans = 0;
00637
00638 switch (hideDirection) {
00639 case Plasma::Up:
00640 ytrans = -(amount * view->height());
00641 break;
00642
00643 case Plasma::Down:
00644 ytrans = amount * view->height();
00645 break;
00646
00647 case Plasma::Right:
00648 xtrans = amount * view->width();
00649 break;
00650
00651 case Plasma::Left:
00652 xtrans = -(amount * view->width());
00653 break;
00654 }
00655
00656 view->viewport()->move(xtrans, ytrans);
00657 q->update();
00658 }
00659
00660 bool Dialog::inControlArea(const QPoint &point)
00661 {
00662 foreach (const QRect &r, d->resizeAreas) {
00663 if (r.contains(point)) {
00664 return true;
00665 }
00666 }
00667 return false;
00668 }
00669
00670 }
00671 #include "dialog.moc"