/** This file is part of WebScraps ** * * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).* * All rights reserved. * Contact: Nokia Corporation (qt-info@nokia.com)** * You may use this file under the terms of the BSD license as follows: * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * 3. Neither the name of Nokia Corporation and its Subsidiary(-ies) nor the names of * its contributors may be used to endorse or promote products derived from this software * without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "webscrap.h" const int WebScrapContainer::s_titlePadding = 25; // padding on the top of the scrap const int WebScrapContainer::s_padding = 2; // padding on the other three sides WebScrap::WebScrap(QUrl url, QSize pageSize, QRect scrapRect, QGraphicsItem * parent) : QGraphicsWebView(parent) , m_isLoading(false) { setGeometry(QRectF(pos(), scrapRect.size())); page()->setViewportSize(pageSize); setScrapRect(scrapRect); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); page()->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); connect(this, SIGNAL(loadStarted()), SLOT(onLoadStarted())); connect(this, SIGNAL(loadFinished(bool)), SLOT(onLoadFinished())); setUrl(url); setDimensionsFixed(true); connect(page(), SIGNAL(linkClicked(QUrl)), this, SLOT(openUrlInExternalBrowser(QUrl))); m_refreshTimer = new QTimer(); connect(m_refreshTimer, SIGNAL(timeout()), this, SLOT(onRefresh())); setRefreshInterval(10000); // 10 secs m_loadingGradient.setCoordinateMode(QGradient::ObjectBoundingMode); m_loadingGradient.setColorAt(0, Qt::white); m_loadingGradient.setColorAt(1, QColor(Qt::gray).lighter(200)); m_overlayGradient.setCoordinateMode(QGradient::ObjectBoundingMode); m_overlayGradient.setStart(0, 0); m_overlayGradient.setFinalStop(0, 0.6); m_overlayGradient.setColorAt(0, QColor(0, 0, 0, 200)); m_overlayGradient.setColorAt(1, QColor(0, 0, 0, 0)); m_loadingPix = QPixmap(":/icons/loading.png").scaled(20, 20, Qt::KeepAspectRatio, Qt::SmoothTransformation); m_adjustablePix = QPixmap(":/icons/adjustable.png").scaled(20, 20, Qt::KeepAspectRatio, Qt::SmoothTransformation); m_arrowPix = QPixmap(":/icons/arrow.png").scaled(20, 20, Qt::KeepAspectRatio, Qt::SmoothTransformation); m_scalePix = QPixmap(":/icons/scale.png").scaled(20, 20, Qt::KeepAspectRatio, Qt::SmoothTransformation); } void WebScrap::setScrapRect(const QRect &r) { m_scrapRect = r; if (! m_isLoading) page()->mainFrame()->setScrollPosition(scrapRect().topLeft()); } QRect WebScrap::scrapRect() const { return m_scrapRect; } void WebScrap::savePos() { m_savedPos = pos(); } QPointF WebScrap::savedPos() const { return m_savedPos; } void WebScrap::setRefreshInterval(int msecs) { m_refreshInterval = msecs; m_refreshTimer->setInterval(msecs); m_refreshTimer->start(); } int WebScrap::refreshInterval() const { return m_refreshInterval; } const QTimer* WebScrap::refreshTimer() const { return m_refreshTimer; } void WebScrap::setDimensionsFixed(bool fixed) { m_isDimensionsFixed = fixed; setMouseClicksEnabled(fixed); update(); } bool WebScrap::dimensionsFixed() const { return m_isDimensionsFixed; } void WebScrap::setMouseClicksEnabled(bool enabled) { m_isMouseClicksEnabled = enabled; } bool WebScrap::mouseClicksEnabled() const { return m_isMouseClicksEnabled; } void WebScrap::onLoadStarted() { m_isLoading = true; } void WebScrap::onLoadFinished() { page()->mainFrame()->setScrollPosition(scrapRect().topLeft()); m_isLoading = false; update(); } void WebScrap::onRefresh() { if (!m_isDimensionsFixed) return; // save current look as an image, so we can paint that while it's loading m_webshot = QPixmap(scrapRect().size()); m_webshot.fill(Qt::transparent); QPainter painter(&m_webshot); page()->mainFrame()->render(&painter); if (! m_isLoading) reload(); } void WebScrap::openUrlInExternalBrowser(const QUrl &url) { QDesktopServices::openUrl(url); } void WebScrap::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (!dimensionsFixed()) { QPointF delta = event->lastPos() - event->pos(); setScrapRect(scrapRect().adjusted(delta.x(), delta.y(), delta.x(), delta.y())); event->accept(); } else { QGraphicsWidget::mouseMoveEvent(event); } } bool WebScrap::event(QEvent *e) { if (!mouseClicksEnabled()) { if (e->type() == QEvent::GraphicsSceneMousePress || e->type() == QEvent::GraphicsSceneMouseRelease || e->type() == QEvent::GraphicsSceneMouseDoubleClick) { e->accept(); return true; } } return QGraphicsWebView::event(e); } void WebScrap::paint(QPainter* painter, const QStyleOptionGraphicsItem* options, QWidget* widget) { painter->save(); QPainterPath clipPath; clipPath.addRoundedRect(rect(), 10, 10); painter->setBrush(Qt::NoBrush); painter->setPen(Qt::NoPen); painter->drawPath(clipPath); painter->setClipPath(clipPath); if (! m_isLoading) { painter->fillRect(options->rect, Qt::white); QGraphicsWebView::paint(painter, options, widget); painter->save(); painter->setOpacity(0.9); if (dimensionsFixed()) { painter->drawPixmap(rect().bottomRight().x() - 5 - m_scalePix.width(), rect().bottomRight().y() - 5 - m_scalePix.height(), m_scalePix); } else { painter->drawPixmap(rect().bottomRight().x() - 5 - m_arrowPix.width(), rect().bottomRight().y() - 5 - m_arrowPix.height(), m_arrowPix); } painter->restore(); painter->setOpacity(0.6); if (!dimensionsFixed()) painter->drawPixmap(rect().center().x() - m_adjustablePix.width() / 2, rect().center().y() - m_adjustablePix.height() / 2, m_adjustablePix); } else { painter->setOpacity(1.0); painter->drawPixmap(0, 0, m_webshot); painter->drawPixmap(rect().center().x() - m_loadingPix.width() / 2, rect().center().y() - m_loadingPix.height() / 2, m_loadingPix); painter->setOpacity(0.6); painter->fillRect(m_webshot.rect(), m_loadingGradient); } painter->setOpacity(0.2); painter->setPen(Qt::NoPen); painter->fillRect(rect(), m_overlayGradient); painter->restore(); } bool WebScrap::highlightText(const QString& text) { page()->findText(QString(""), QWebPage::HighlightAllOccurrences); // clear any existing highlight if (text.isEmpty()) return false; bool found = page()->findText(text, QWebPage::HighlightAllOccurrences); if ((url().host() + url().path()).contains(text, Qt::CaseInsensitive)) return true; if (title().contains(text, Qt::CaseInsensitive)) return true; if (found) { foreach(QWebElement element, page()->mainFrame()->findAllElements("*")) { if (element.firstChild().isNull() && element.geometry().intersects(m_scrapRect) && element.toPlainText().contains(text, Qt::CaseInsensitive)) return true; } } return false; } WebScrapContainer::WebScrapContainer(WebScrap *scrap, QGraphicsScene *scene, qreal xScale, qreal yScale) : QGraphicsWidget(0) , m_scene(scene) , m_scrap(scrap) , m_located(false) , m_searchMatched(false) , m_enterAnimation(0) , m_leaveAnimation(0) { scene->addItem(this); // create the frame QRect scrapRect = scrap->scrapRect(); QRect containerRect(0, 0, (scrapRect.width() * xScale) + s_padding * 2, (scrapRect.height() * yScale) + s_titlePadding + s_padding); setGeometry(containerRect); setBoundingRect(containerRect); setFlag(QGraphicsItem::ItemIsMovable, true); setCursor(Qt::SizeAllCursor); scrap->setParentItem(this); scrap->setPos(s_padding, s_titlePadding); // for paint m_backgroundBrush = palette().background(); m_titleBrush = palette().foreground(); connect(scrap, SIGNAL(titleChanged(QString)), SLOT(setTitle(QString))); // create the tool bar createToolbar(); createEditToolbar(); // tie scrap to resizer m_resizer = new ResizeUiHelper(m_scrap, this); m_resizer->setGripSize(QSizeF(50, 50)); m_resizer->setAllResizeGripsEnabled(false); m_resizer->setResizeGripEnabled(ResizeUiHelper::BottomRight, true); connect(m_resizer, SIGNAL(rectResized(QRectF)), SLOT(handleRectResized(QRectF))); m_scrapScale = new QGraphicsScale(this); m_scrapScale->setXScale(xScale); m_scrapScale->setYScale(yScale); QList txs; txs << m_scrapScale; m_scrap->setTransformations(txs); m_glow = new QGraphicsDropShadowEffect(this); m_glow->setProperty("color", Qt::black); m_glow->setProperty("offset", QPointF(-1, -4)); m_glow->setProperty("blurRadius", 5); setGraphicsEffect(m_glow); } void WebScrapContainer::setBoundingRect(QRectF rect) { m_boundingRect = rect; } QRectF WebScrapContainer::boundingRect() const { return m_boundingRect; } void WebScrapContainer::setTitle(const QString& title) { m_title = title; } void WebScrapContainer::mousePressEvent(QGraphicsSceneMouseEvent *event) { emit frameClicked(); QGraphicsWidget::mousePressEvent(event); } void WebScrapContainer::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { QPropertyAnimation *enterAnim = enterAnimation(); if (enterAnim) enterAnim->setEndValue(pos()); QGraphicsWidget::mouseReleaseEvent(event); } void WebScrapContainer::createToolbar() { m_toolbar = new GraphicsToolBar(m_scene); QString stylesheet = "QToolButton {" " margin: 0px;" " padding: 1px;" " height: " + QString::number(s_titlePadding - 10) + "px;" + "}"; QStyleOptionFrameV2 panel; initStyleOption(&panel); m_adjustButton = new QToolButton; m_adjustButton->setIcon(style()->standardIcon(QStyle::SP_FileDialogDetailedView, &panel)); m_adjustButton->setToolTip(tr("Fix scrap dimensions")); m_adjustButton->setCheckable(true); m_adjustButton->setChecked(false); m_adjustButton->setAutoRaise(true); m_adjustButton->setStyleSheet(stylesheet); m_closeButton = new QToolButton; m_closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton, &panel)); m_closeButton->setToolTip(tr("Close")); m_closeButton->setAutoRaise(true); m_closeButton->setStyleSheet(stylesheet); m_reloadButton = new QToolButton; m_reloadButton->setIcon(style()->standardIcon(QStyle::SP_BrowserReload, &panel)); m_reloadButton->setToolTip(tr("Reload")); m_reloadButton->setAutoRaise(true); m_reloadButton->setStyleSheet(stylesheet); m_toolbar->addWidget(m_adjustButton); m_toolbar->addWidget(m_reloadButton); m_toolbar->addWidget(m_closeButton); m_toolbar->layout()->setContentsMargins(2, 2, 8, 2); m_toolbar->setVisible(true); m_toolbar->setGraphicsEffectsEnabled(false); m_toolbar->setParentItem(this); m_toolbar->setZValue(900); m_toolbar->layout()->activate(); m_toolbar->setPos(boundingRect().topRight() - QPointF(m_toolbar->rect().width(), 0)); m_toolbar->setFlag(QGraphicsItem::ItemIsMovable, false); m_toolbar->setBackgroundBrush(palette().background()); connect(m_closeButton, SIGNAL(clicked()), SLOT(handleCloseButtonClicked())); connect(m_adjustButton, SIGNAL(clicked(bool)), SLOT(handleEditButtonClicked(bool))); connect(m_reloadButton, SIGNAL(clicked()), m_scrap, SLOT(onRefresh())); } void WebScrapContainer::createEditToolbar() { m_editToolbar = new GraphicsToolBar(m_scene); QLabel *label = new QLabel(tr("Refresh every")); m_refreshIntervalLineEdit = new QLineEdit; m_refreshIntervalLineEdit->setFixedWidth(50); m_refreshIntervalLineEdit->setValidator(new QIntValidator(1, 99999, this)); m_refreshUnitComboBox = new QComboBox; m_refreshUnitComboBox->addItem("Minutes"); m_refreshUnitComboBox->addItem("Hours"); m_refreshUnitComboBox->addItem("Days"); m_resetZoomPushButton = new QPushButton(tr("Reset zoom")); int minutes = webScrap()->refreshInterval() / 60000; if ((minutes % 60) == 0) { if ((minutes % (60 * 24)) == 0) { // days m_refreshIntervalLineEdit->setText(QString("%1").arg(minutes / (60 * 24))); m_refreshUnitComboBox->setCurrentIndex(2); } else { // hours m_refreshIntervalLineEdit->setText(QString("%1").arg(minutes / 60)); m_refreshUnitComboBox->setCurrentIndex(1); } } else { // minutes m_refreshIntervalLineEdit->setText(QString("%1").arg(minutes)); m_refreshUnitComboBox->setCurrentIndex(0); } m_editToolbar->addWidget(label); m_editToolbar->addWidget(m_refreshIntervalLineEdit); m_editToolbar->addWidget(m_refreshUnitComboBox); m_editToolbar->addWidget(m_resetZoomPushButton); m_editToolbar->layout()->setContentsMargins(10, 2, 10, 2); QGraphicsLinearLayout *llayout = static_cast(m_editToolbar->layout()); if (llayout) llayout->setItemSpacing(2, 50); m_editToolbar->setGraphicsEffectsEnabled(false); m_editToolbar->setOpacity(1.0); m_editToolbar->setParentItem(this); m_editToolbar->setZValue(901); m_editToolbar->layout()->activate(); m_editToolbar->setPos(boundingRect().topRight() - QPointF(m_editToolbar->boundingRect().width() + 30, -30)); m_editToolbar->setFlag(QGraphicsItem::ItemIsMovable, false); m_editToolbar->setVisible(false); m_editToolbar->setBackgroundBrush(palette().background()); connect(m_resetZoomPushButton, SIGNAL(clicked()), SLOT(handleResetZoomClicked())); } void WebScrapContainer::handleCloseButtonClicked() { QMessageBox msg(scene()->views().first()); msg.setWindowTitle(tr("Removing scrap")); msg.setText(tr("Are you sure you want to remove this scrap?")); QPushButton *removeButton = msg.addButton(tr("Remove"), QMessageBox::ActionRole); QPushButton *keepButton = msg.addButton(tr("Keep"), QMessageBox::RejectRole); msg.setDefaultButton(keepButton); msg.setIcon(QMessageBox::Question); msg.exec(); if (msg.clickedButton() == removeButton) emit removeSelf(); } void WebScrapContainer::handleEditButtonClicked(bool checked) { m_editToolbar->setVisible(checked); m_scrap->setDimensionsFixed(!checked); if (!checked) { int minutes = m_refreshIntervalLineEdit->text().toInt(); switch (m_refreshUnitComboBox->currentIndex()) { case 2: minutes *= 24; // days case 1: minutes *= 60; // hours case 0: minutes *= 1; // minutes default: break; } m_scrap->setRefreshInterval(minutes * 60 * 1000); // milliseconds } if (checked) setGraphicsEffect(0); else setGraphicsEffect(glow()); m_reloadButton->setEnabled(!checked); m_closeButton->setEnabled(!checked); } void WebScrapContainer::handleResetZoomClicked() { m_scrapScale->setXScale(1.0); m_scrapScale->setYScale(1.0); QSize scrapSize = webScrap()->size().toSize(); setGeometry(geometry().left(), geometry().top(), scrapSize.width() + s_padding * 2, scrapSize.height() + s_titlePadding + s_padding); setBoundingRect(QRectF(0, 0, scrapSize.width() + s_padding * 2 * 2, scrapSize.height() + s_titlePadding + s_padding)); m_toolbar->setPos(boundingRect().topRight() - QPointF(m_toolbar->rect().width(), 0)); m_editToolbar->setPos(boundingRect().topRight() - QPointF(m_editToolbar->boundingRect().width() + 30, -30)); m_resizer->setRect(m_scrap->boundingRect()); } void WebScrapContainer::handleRectResized(QRectF rect) { if (m_scrap->dimensionsFixed()) { m_scrapScale->setXScale(rect.width() * m_scrapScale->xScale() / m_scrap->scrapRect().width()); m_scrapScale->setYScale(rect.height() * m_scrapScale->yScale() / m_scrap->scrapRect().height()); m_resizer->setRect(m_scrap->boundingRect()); } else { QRect scrapRect = rect.toRect(); scrapRect.moveTopLeft(m_scrap->scrapRect().topLeft()); m_scrap->setScrapRect(scrapRect); m_scrap->setGeometry(QRectF(m_scrap->pos(), scrapRect.size())); } QSize scrapSize = QSize(rect.width() * m_scrapScale->xScale(), rect.height() * m_scrapScale->yScale()); setGeometry(geometry().left(), geometry().top(), scrapSize.width() + s_padding * 2, scrapSize.height() + s_titlePadding + s_padding); setBoundingRect(QRectF(0, 0, scrapSize.width() + s_padding * 2, scrapSize.height() + s_titlePadding + s_padding)); m_toolbar->setPos(boundingRect().topRight() - QPointF(m_toolbar->rect().width(), 0)); m_editToolbar->setPos(boundingRect().topRight() - QPointF(m_editToolbar->boundingRect().width() + 30, -30)); update(); } WebScrap* WebScrapContainer::webScrap() const { return m_scrap; } const QGraphicsScale* WebScrapContainer::scrapScale() const { return m_scrapScale; } void WebScrapContainer::setLocated(bool located) { m_located = located; updateGlow(); } void WebScrapContainer::setSearchMatched(bool matched) { m_searchMatched = matched; updateGlow(); } QGraphicsDropShadowEffect* WebScrapContainer::glow() const { return m_glow; } void WebScrapContainer::updateGlow() { if (m_located) glow()->setProperty("color", Qt::blue); else if (m_searchMatched) glow()->setProperty("color", QColor("#ff6600")); else glow()->setProperty("color", Qt::black); } void WebScrapContainer::setEnterAnimation(QPropertyAnimation *animation) { m_enterAnimation = animation; } QPropertyAnimation* WebScrapContainer::enterAnimation() const { return m_enterAnimation; } void WebScrapContainer::setLeaveAnimation(QPropertyAnimation *animation) { m_leaveAnimation = animation; } QPropertyAnimation* WebScrapContainer::leaveAnimation() const { return m_leaveAnimation; } void WebScrapContainer::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(widget); painter->setPen(Qt::NoPen); painter->setBrush(m_backgroundBrush); painter->drawRoundedRect(boundingRect(), 10, 10); painter->setPen(QPen(m_titleBrush, 0)); painter->setBrush(Qt::NoBrush); QString title = painter->fontMetrics().elidedText(m_title, Qt::ElideRight, option->rect.width() - m_toolbar->rect().width()); int height = painter->fontMetrics().height(); painter->drawText(option->rect.adjusted(10, (s_titlePadding - height) / 2, 0, 0), title); }