diff --git a/share/qtcreator/welcomescreen/images_areaofinterest.xml b/share/qtcreator/welcomescreen/images_areaofinterest.xml new file mode 100644 index 0000000000000000000000000000000000000000..7643e620b379325a79cc79650a1248109028fe3d --- /dev/null +++ b/share/qtcreator/welcomescreen/images_areaofinterest.xml @@ -0,0 +1,172 @@ +<?xml version="1.0" encoding="UTF-8"?> +<areas> + <area image="2dpainting-example.png" x="270" y="90" width="118" height="92"/> + <area image="addressbook-example.png" x="4" y="50" width="138" height="111"/> + <area image="addressbook-tutorial-part1-screenshot.png" x="9" y="25" width="112" height="93"/> + <area image="analogclock-example.png" x="82" y="98" width="125" height="89"/> + <area image="animatedtiles-example.png" x="191" y="197" width="128" height="121"/> + <area image="appchooser-example.png" x="189" y="214" width="119" height="117"/> + <area image="application.png" x="4" y="9" width="145" height="117"/> + <area image="audiodevices-example.png" x="36" y="119" width="173" height="101"/> + <area image="audioinput-example.png" x="0" y="0" width="120" height="120"/> + <area image="audiooutput-example.png" x="16" y="39" width="152" height="56"/> + <area image="basicdrawing-example.png" x="155" y="190" width="160" height="133"/> + <area image="basiclayouts-example.png" x="8" y="44" width="173" height="97"/> + <area image="basicsortfiltermodel-example.png" x="9" y="25" width="125" height="93"/> + <area image="borderlayout-example.png" x="1" y="26" width="151" height="128"/> + <area image="broadcastreceiver-example.png" x="73" y="36" width="145" height="57"/> + <area image="broadcastsender-example.png" x="99" y="34" width="130" height="64"/> + <area image="cachedtable-example.png" x="12" y="33" width="140" height="107"/> + <area image="calculator-example.png" x="205" y="27" width="126" height="122"/> + <area image="calculatorbuilder-example.png" x="20" y="41" width="131" height="82"/> + <area image="calculatorform-example.png" x="14" y="33" width="155" height="72"/> + <area image="calendar-example.png" x="12" y="34" width="162" height="124"/> + <area image="calendarwidgetexample.png" x="105" y="66" width="142" height="110"/> + <area image="charactermap-example.png" x="13" y="56" width="171" height="108"/> + <area image="chart-example.png" x="419" y="59" width="150" height="117"/> + <area image="classwizard.png" x="24" y="111" width="152" height="126"/> + <area image="codecs-example.png" x="163" y="98" width="134" height="94"/> + <area image="codeeditor-example.png" x="14" y="48" width="143" height="107"/> + <area image="collidingmice-example.png" x="45" y="86" width="171" height="133"/> + <area image="coloreditorfactoryimage.png" x="50" y="35" width="155" height="109"/> + <area image="combowidgetmapper-example.png" x="38" y="168" width="131" height="89"/> + <area image="completer-example.png" x="11" y="141" width="159" height="107"/> + <area image="concentriccircles-example.png" x="306" y="321" width="50" height="43"/> + <area image="configdialog-example.png" x="94" y="27" width="122" height="122"/> + <area image="containerextension-example.png" x="30" y="21" width="110" height="97"/> + <area image="customcompleter-example.png" x="3" y="69" width="151" height="112"/> + <area image="customsortfiltermodel-example.png" x="10" y="27" width="152" height="117"/> + <area image="customwidgetplugin-example.png" x="16" y="18" width="129" height="124"/> + <area image="defaultprototypes-example.png" x="2" y="15" width="104" height="111"/> + <area image="diagramscene.png" x="1" y="47" width="164" height="138"/> + <area image="digitalclock-example.png" x="57" y="37" width="129" height="96"/> + <area image="dirview-example.png" x="21" y="96" width="122" height="90"/> + <area image="dockwidgets-example.png" x="498" y="262" width="180" height="109"/> + <area image="dombookmarks-example.png" x="2" y="53" width="131" height="129"/> + <area image="draganddroppuzzle-example.png" x="183" y="230" width="145" height="127"/> + <area image="dragdroprobot-example.png" x="119" y="101" width="115" height="129"/> + <area image="draggableicons-example.png" x="181" y="67" width="111" height="124"/> + <area image="draggabletext-example.png" x="48" y="78" width="96" height="90"/> + <area image="drilldown-example.png" x="119" y="34" width="162" height="132"/> + <area image="dropsite-example.png" x="32" y="319" width="181" height="91"/> + <area image="elasticnodes-example.png" x="72" y="71" width="120" height="107"/> + <area image="extension-example.png" x="15" y="31" width="198" height="84"/> + <area image="fancybrowser-example.png" x="385" y="201" width="170" height="94"/> + <area image="fetchmore-example.png" x="11" y="35" width="129" height="99"/> + <area image="findfiles-example.png" x="61" y="5" width="158" height="138"/> + <area image="flowlayout-example.png" x="14" y="34" width="165" height="92"/> + <area image="fontsampler-example.png" x="4" y="42" width="148" height="104"/> + <area image="formextractor-example.png" x="56" y="137" width="144" height="108"/> + <area image="fortuneclient-example.png" x="7" y="31" width="172" height="102"/> + <area image="fortuneserver-example.png" x="72" y="29" width="121" height="86"/> + <area image="framebufferobject-example.png" x="147" y="114" width="178" height="131"/> + <area image="framebufferobject2-example.png" x="194" y="152" width="127" height="133"/> + <area image="fridgemagnets-example.png" x="211" y="51" width="114" height="111"/> + <area image="frozencolumn-example.png" x="6" y="22" width="127" height="97"/> + <area image="googlechat-example.png" x="384" y="416" width="177" height="120"/> + <area image="googlesuggest-example.png" x="18" y="18" width="129" height="109"/> + <area image="grabber-example.png" x="180" y="105" width="158" height="142"/> + <area image="groupbox-example.png" x="11" y="30" width="163" height="101"/> + <area image="hellogl-example.png" x="93" y="96" width="118" height="144"/> + <area image="http-example.png" x="17" y="33" width="179" height="87"/> + <area image="i18n-example.png" x="15" y="103" width="130" height="108"/> + <area image="icons-example.png" x="243" y="87" width="160" height="114"/> + <area image="imagecomposition-example.png" x="618" y="72" width="137" height="127"/> + <area image="imageviewer-example.png" x="10" y="24" width="154" height="145"/> + <area image="inputpanel-example.png" x="59" y="131" width="163" height="140"/> + <area image="itemviewspuzzle-example.png" x="410" y="141" width="129" height="99"/> + <area image="licensewizard-example.png" x="147" y="40" width="176" height="117"/> + <area image="lineedits-example.png" x="236" y="26" width="125" height="118"/> + <area image="linguist-arrowpad_en.png" x="12" y="52" width="149" height="98"/> + <area image="linguist-hellotr_en.png" x="28" y="2" width="118" height="61"/> + <area image="linguist-trollprint_10_en.png" x="0" y="0" width="120" height="120"/> + <area image="localfortuneclient-example.png" x="8" y="27" width="161" height="86"/> + <area image="localfortuneserver-example.png" x="13" y="29" width="159" height="88"/> + <area image="loopback-example.png" x="11" y="35" width="122" height="116"/> + <area image="mandelbrot-example.png" x="67" y="77" width="161" height="123"/> + <area image="masterdetail-example.png" x="7" y="15" width="114" height="114"/> + <area image="mdi-example.png" x="134" y="46" width="147" height="127"/> + <area image="menus-example.png" x="0" y="0" width="120" height="120"/> + <area image="moveblocks-example.png" x="35" y="59" width="86" height="89"/> + <area image="movie-example.png" x="14" y="306" width="168" height="126"/> + <area image="network-chat-example.png" x="58" y="68" width="166" height="115"/> + <area image="orderform-example.png" x="247" y="212" width="157" height="106"/> + <area image="overpainting-example.png" x="119" y="69" width="172" height="139"/> + <area image="padnavigator-example.png" x="14" y="15" width="325" height="301"/> + <area image="painterpaths-example.png" x="9" y="286" width="172" height="113"/> + <area image="pbuffers-example.png" x="100" y="262" width="145" height="141"/> + <area image="pbuffers2-example.png" x="196" y="95" width="156" height="131"/> + <area image="pixelator-example.png" x="242" y="127" width="119" height="110"/> + <area image="plugandpaint.png" x="42" y="83" width="183" height="97"/> + <area image="portedasteroids-example.png" x="487" y="8" width="161" height="109"/> + <area image="portedcanvas-example.png" x="393" y="178" width="151" height="133"/> + <area image="previewer-example.png" x="108" y="62" width="186" height="119"/> + <area image="qml-clocks-example.png" x="244" y="90" width="157" height="133"/> + <area image="qml-corkboards-example.png" x="37" y="22" width="133" height="124"/> + <area image="qml-dialcontrol-example.png" x="84" y="70" width="123" height="104"/> + <area image="qml-dynamicscene-example.png" x="31" y="191" width="117" height="122"/> + <area image="qml-flipable-example.png" x="253" y="121" width="146" height="85"/> + <area image="qml-progressbar-example.png" x="2" y="11" width="158" height="96"/> + <area image="qml-scrollbar-example.png" x="1" y="2" width="490" height="348"/> + <area image="qml-searchbox-example.png" x="119" y="61" width="153" height="123"/> + <area image="qml-slideswitch-example.png" x="119" y="86" width="161" height="71"/> + <area image="qml-spinner-example.png" x="59" y="95" width="121" height="109"/> + <area image="qml-tabwidget-example.png" x="65" y="5" width="169" height="61"/> + <area image="qml-tic-tac-toe-example.png" x="26" y="48" width="381" height="372"/> + <area image="qml-tvtennis-example.png" x="225" y="249" width="134" height="111"/> + <area image="qobjectxmlmodel-example.png" x="9" y="41" width="139" height="119"/> + <area image="querymodel-example.png" x="462" y="48" width="129" height="113"/> + <area image="recentfiles-example.png" x="0" y="0" width="173" height="138"/> + <area image="recipes-example.png" x="3" y="139" width="154" height="103"/> + <area image="regexp-example.png" x="12" y="34" width="166" height="114"/> + <area image="relationaltablemodel-example.png" x="3" y="28" width="172" height="85"/> + <area image="rogue-example.png" x="7" y="30" width="115" height="100"/> + <area image="rsslistingexample.png" x="13" y="30" width="157" height="110"/> + <area image="samplebuffers-example.png" x="88" y="279" width="174" height="116"/> + <area image="saxbookmarks-example.png" x="4" y="53" width="143" height="114"/> + <area image="schema-example.png" x="55" y="104" width="169" height="98"/> + <area image="screenshot-example.png" x="7" y="169" width="176" height="135"/> + <area image="scribble-example.png" x="152" y="126" width="206" height="161"/> + <area image="sdi-example.png" x="3" y="9" width="150" height="132"/> + <area image="securesocketclient.png" x="10" y="25" width="148" height="101"/> + <area image="settingseditor-example.png" x="13" y="152" width="165" height="121"/> + <area image="shapedclock-example.png" x="107" y="36" width="131" height="115"/> + <area image="sharedmemory-example_1.png" x="149" y="18" width="164" height="79"/> + <area image="simpledommodel-example.png" x="7" y="84" width="134" height="111"/> + <area image="simpletreemodel-example.png" x="3" y="33" width="172" height="118"/> + <area image="simplewidgetmapper-example.png" x="11" y="36" width="160" height="91"/> + <area image="sliders-example.png" x="461" y="42" width="182" height="127"/> + <area image="spinboxdelegate-example.png" x="82" y="86" width="152" height="96"/> + <area image="spinboxes-example.png" x="263" y="65" width="169" height="113"/> + <area image="standarddialogs-example.png" x="187" y="33" width="178" height="149"/> + <area image="stardelegate.png" x="220" y="21" width="180" height="95"/> + <area image="states-example.png" x="236" y="258" width="176" height="77"/> + <area image="stickman-example.png" x="1" y="1" width="467" height="510"/> + <area image="styles-enabledwood.png" x="12" y="24" width="185" height="95"/> + <area image="stylesheet-coffee-plastique.png" x="40" y="64" width="180" height="115"/> + <area image="svggenerator-example.png" x="162" y="64" width="90" height="72"/> + <area image="svgviewer-example.png" x="193" y="245" width="102" height="91"/> + <area image="syntaxhighlighter-example.png" x="4" y="67" width="153" height="101"/> + <area image="systemtray-example.png" x="163" y="36" width="181" height="113"/> + <area image="t1.png" x="0" y="0" width="108" height="56"/> + <area image="tabdialog-example.png" x="7" y="30" width="168" height="105"/> + <area image="tablemodel-example.png" x="342" y="44" width="157" height="116"/> + <area image="tabletexample.png" x="26" y="70" width="167" height="112"/> + <area image="tetrix-example.png" x="46" y="72" width="112" height="112"/> + <area image="textobject-example.png" x="86" y="73" width="99" height="80"/> + <area image="textures-example.png" x="245" y="70" width="154" height="137"/> + <area image="threadedfortuneserver-example.png" x="100" y="30" width="122" height="85"/> + <area image="tooltips-example.png" x="96" y="63" width="128" height="129"/> + <area image="torrent-example.png" x="5" y="29" width="175" height="126"/> + <area image="trafficinfo-example.png" x="45" y="1" width="207" height="62"/> + <area image="trafficlight-example.png" x="8" y="219" width="112" height="104"/> + <area image="transformations-example.png" x="484" y="92" width="128" height="125"/> + <area image="treemodelcompleter-example.png" x="11" y="319" width="181" height="109"/> + <area image="trivialwizard-example-introduction.png" x="248" y="333" width="170" height="61"/> + <area image="undoframeworkexample.png" x="176" y="1" width="136" height="121"/> + <area image="wiggly-example.png" x="10" y="58" width="155" height="113"/> + <area image="windowflags-example.png" x="9" y="82" width="140" height="119"/> + <area image="worldtimeclockbuilder-example.png" x="99" y="57" width="154" height="93"/> + <area image="worldtimeclockplugin-example.png" x="109" y="34" width="141" height="116"/> + <area image="xmlstreamexample-screenshot.png" x="2" y="54" width="139" height="98"/> +</areas> diff --git a/share/qtcreator/welcomescreen/widgets/Delegate.qml b/share/qtcreator/welcomescreen/widgets/Delegate.qml index 04223e6850b6fdba2afca7db763a28219367c77d..49cfc58d1b13083cdae7d58b5ddb557197e91e9e 100644 --- a/share/qtcreator/welcomescreen/widgets/Delegate.qml +++ b/share/qtcreator/welcomescreen/widgets/Delegate.qml @@ -75,7 +75,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter border.bottom: 4 border.right: 4 - border.top: 5 + border.top: 4 border.left: 4 source: "images/dropshadow.png" @@ -83,13 +83,11 @@ Rectangle { id: imageItem visible: !delegate.isVideo - anchors.rightMargin: 4 - anchors.leftMargin: 4 - anchors.bottomMargin: 4 - anchors.topMargin: 4 - anchors.fill: parent + anchors.centerIn: parent asynchronous: true - + sourceSize.height: 145 + sourceSize.width: 188 + fillMode: Image.Center } Image { diff --git a/share/qtcreator/welcomescreen/widgets/images/dropshadow.png b/share/qtcreator/welcomescreen/widgets/images/dropshadow.png index 3d8b0b47adaba4f3053c92bb7be74f811950824d..01069b33967d6c0ad76e9db8eaadfdf34420bcf4 100644 Binary files a/share/qtcreator/welcomescreen/widgets/images/dropshadow.png and b/share/qtcreator/welcomescreen/widgets/images/dropshadow.png differ diff --git a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp index 00ee3c1d398fe02099d5dcef47c000430f03b633..757855a417b45d0c51aecbece51b13959ee24c1b 100644 --- a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp +++ b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp @@ -33,6 +33,7 @@ #include "gettingstartedwelcomepage.h" #include "exampleslistmodel.h" +#include "screenshotcropper.h" #include <utils/pathchooser.h> #include <utils/fileutils.h> @@ -170,12 +171,13 @@ public: // gets called by declarative in separate thread QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) { + Q_UNUSED(size) QMutexLocker lock(&m_mutex); QUrl url = QUrl::fromEncoded(id.toAscii()); - if (!m_fetcher.asynchronousFetchData(url)) - return QImage(); + if (!m_fetcher.asynchronousFetchData(url)) + return QImage(); if (m_fetcher.data().isEmpty()) return QImage(); @@ -184,11 +186,9 @@ public: imgBuffer.open(QIODevice::ReadOnly); QImageReader reader(&imgBuffer); QImage img = reader.read(); - if (size && requestedSize != *size) - img = img.scaled(requestedSize); m_fetcher.clearData(); - return img; + return ScreenshotCropper::croppedImage(img, id, requestedSize); } private: Fetcher m_fetcher; diff --git a/src/plugins/qtsupport/qtsupport.pro b/src/plugins/qtsupport/qtsupport.pro index 25ffad2f1452b7c7d8df0ecd57196cdc7984191f..088a61e7d0f0bb3e07e9b5242e2b9fc3b13350b9 100644 --- a/src/plugins/qtsupport/qtsupport.pro +++ b/src/plugins/qtsupport/qtsupport.pro @@ -25,7 +25,8 @@ HEADERS += \ profilereader.h \ qtparser.h \ gettingstartedwelcomepage.h \ - exampleslistmodel.h + exampleslistmodel.h \ + screenshotcropper.h SOURCES += \ qtsupportplugin.cpp \ @@ -41,7 +42,8 @@ SOURCES += \ profilereader.cpp \ qtparser.cpp \ gettingstartedwelcomepage.cpp \ - exampleslistmodel.cpp + exampleslistmodel.cpp \ + screenshotcropper.cpp FORMS += \ showbuildlog.ui \ diff --git a/src/plugins/qtsupport/screenshotcropper.cpp b/src/plugins/qtsupport/screenshotcropper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e678e40cf065e2db9b8398a7bddedac7024fddf8 --- /dev/null +++ b/src/plugins/qtsupport/screenshotcropper.cpp @@ -0,0 +1,141 @@ +#include "screenshotcropper.h" + +#include <coreplugin/icore.h> +#include <QtCore/QXmlStreamReader> +#include <QtCore/QXmlStreamWriter> +#include <QtCore/QDebug> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtGui/QPainter> + +namespace QtSupport { +namespace Internal { + +#ifdef QT_CREATOR +Q_GLOBAL_STATIC_WITH_INITIALIZER(AreasOfInterest, areasOfInterest, { + *x = ScreenshotCropper::loadAreasOfInterest(Core::ICore::instance()->resourcePath() + QLatin1String("/welcomescreen/images_areaofinterest.xml")); +}) +#else +Q_GLOBAL_STATIC(AreasOfInterest, areasOfInterest) +#endif // QT_CREATOR + +static inline QString fileNameForPath(const QString &path) +{ + return QFileInfo(path).fileName(); +} + +static QRect cropRectForAreaOfInterest(const QSize &imageSize, const QSize &cropSize, const QRect &areaOfInterest) +{ + QRect result; + const qreal cropSizeToAreaSizeFactor = qMin(cropSize.width() / qreal(areaOfInterest.width()), + cropSize.height() / qreal(areaOfInterest.height())); + if (cropSizeToAreaSizeFactor >= 1) { + const QPoint areaOfInterestCenter = areaOfInterest.center(); + const int cropX = qBound(0, + areaOfInterestCenter.x() - cropSize.width() / 2, + imageSize.width() - cropSize.width()); + const int cropY = qBound(0, + areaOfInterestCenter.y() - cropSize.height() / 2, + imageSize.height() - cropSize.height()); + const int cropWidth = qMin(imageSize.width(), cropSize.width()); + const int cropHeight = qMin(imageSize.height(), cropSize.height()); + result = QRect(cropX, cropY, cropWidth, cropHeight); + } else { + QSize resultSize = cropSize.expandedTo(areaOfInterest.size()); + result = QRect(QPoint(), resultSize); + } + return result; +} + +QImage ScreenshotCropper::croppedImage(const QImage &sourceImage, const QString &filePath, const QSize &cropSize) +{ + const QRect areaOfInterest = areasOfInterest()->value(fileNameForPath(filePath)); + + if (areaOfInterest.isValid()) { + const QRect cropRect = cropRectForAreaOfInterest(sourceImage.size(), cropSize, areaOfInterest); + const QSize cropRectSize = cropRect.size(); + const QImage result = sourceImage.copy(cropRect); + if (cropRectSize.width() > cropSize.width() || cropRectSize.height() > cropSize.height()) + return result.scaled(cropSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); + else + return result; + } + + return sourceImage.scaled(cropSize, Qt::KeepAspectRatio); +} + +static int areaAttribute(const QXmlStreamAttributes &attributes, const QString &name) +{ + bool ok; + const int result = attributes.value(name).toString().toInt(&ok); + if (!ok) + qWarning() << Q_FUNC_INFO << "Could not parse" << name << "for" << attributes.value(QLatin1String("image")).toString(); + return result; +} + +static const QString xmlTagAreas = QLatin1String("areas"); +static const QString xmlTagArea = QLatin1String("area"); +static const QString xmlAttributeImage = QLatin1String("image"); +static const QString xmlAttributeX = QLatin1String("x"); +static const QString xmlAttributeY = QLatin1String("y"); +static const QString xmlAttributeWidth = QLatin1String("width"); +static const QString xmlAttributeHeight = QLatin1String("height"); + +AreasOfInterest ScreenshotCropper::loadAreasOfInterest(const QString &areasXmlFile) +{ + AreasOfInterest areasOfInterest; + QFile xmlFile(areasXmlFile); + if (!xmlFile.open(QIODevice::ReadOnly)) { + qWarning() << Q_FUNC_INFO << "Could not open file" << areasXmlFile; + return areasOfInterest; + } + QXmlStreamReader reader(&xmlFile); + while (!reader.atEnd()) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement: + if (reader.name() == xmlTagArea) { + const QXmlStreamAttributes attributes = reader.attributes(); + const QString imageName = attributes.value(xmlAttributeImage).toString(); + if (imageName.isEmpty()) + qWarning() << Q_FUNC_INFO << "Could not parse name"; + + const QRect area(areaAttribute(attributes, xmlAttributeX), areaAttribute(attributes, xmlAttributeY), + areaAttribute(attributes, xmlAttributeWidth), areaAttribute(attributes, xmlAttributeHeight)); + areasOfInterest.insert(imageName, area); + } + break; + default: // nothing + break; + } + } + + return areasOfInterest; +} + +bool ScreenshotCropper::saveAreasOfInterest(const QString &areasXmlFile, AreasOfInterest &areas) +{ + QFile file(areasXmlFile); + if (!file.open(QIODevice::WriteOnly)) + return false; + QXmlStreamWriter writer(&file); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeStartElement(xmlTagAreas); + QMapIterator<QString, QRect> i(areas); + while (i.hasNext()) { + i.next(); + writer.writeStartElement(xmlTagArea); + writer.writeAttribute(xmlAttributeImage, i.key()); + writer.writeAttribute(xmlAttributeX, QString::number(i.value().x())); + writer.writeAttribute(xmlAttributeY, QString::number(i.value().y())); + writer.writeAttribute(xmlAttributeWidth, QString::number(i.value().width())); + writer.writeAttribute(xmlAttributeHeight, QString::number(i.value().height())); + writer.writeEndElement(); // xmlTagArea + } + writer.writeEndElement(); // xmlTagAreas + writer.writeEndDocument(); + return true; +} + +} // namespace Internal +} // namespace QtSupport diff --git a/src/plugins/qtsupport/screenshotcropper.h b/src/plugins/qtsupport/screenshotcropper.h new file mode 100644 index 0000000000000000000000000000000000000000..71c1fd499e4f4edcf2a72369fcbb5f3d6a4659a9 --- /dev/null +++ b/src/plugins/qtsupport/screenshotcropper.h @@ -0,0 +1,24 @@ +#ifndef SCREENSHOTCROPPER_H +#define SCREENSHOTCROPPER_H + +#include <QtCore/QMap> +#include <QtCore/QRect> +#include <QtGui/QImage> + +namespace QtSupport { +namespace Internal { + +typedef QMap<QString, QRect> AreasOfInterest; + +class ScreenshotCropper +{ +public: + static QImage croppedImage(const QImage &sourceImage, const QString &filePath, const QSize &cropSize); + static AreasOfInterest loadAreasOfInterest(const QString &areasXmlFile); + static bool saveAreasOfInterest(const QString &areasXmlFile, AreasOfInterest &areas); +}; + +} // namespace Internal +} // namespace QtSupport + +#endif // SCREENSHOTCROPPER_H diff --git a/src/tools/screenshotcropper/cropimageview.cpp b/src/tools/screenshotcropper/cropimageview.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9473825d5370543b63a459018819808401df0377 --- /dev/null +++ b/src/tools/screenshotcropper/cropimageview.cpp @@ -0,0 +1,59 @@ +#include "cropimageview.h" +#include <QtGui/QPainter> +#include <QtGui/QMouseEvent> + +CropImageView::CropImageView(QWidget *parent) + : QWidget(parent) +{ +} + +void CropImageView::mousePressEvent(QMouseEvent *event) +{ + setArea(QRect(event->pos(), m_area.bottomRight())); + update(); +} + +void CropImageView::mouseMoveEvent(QMouseEvent *event) +{ + setArea(QRect(m_area.topLeft(), event->pos())); + update(); +} + +void CropImageView::mouseReleaseEvent(QMouseEvent *event) +{ + mouseMoveEvent(event); +} + +void CropImageView::setImage(const QImage &image) +{ + m_image = image; + setMinimumSize(image.size()); + update(); +} + +void CropImageView::setArea(const QRect &area) +{ + m_area = m_image.rect().intersected(area); + emit cropAreaChanged(m_area); + update(); +} + +void CropImageView::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + QPainter p(this); + + if (!m_image.isNull()) + p.drawImage(0, 0, m_image); + + if (!m_area.isNull()) { + p.setPen(Qt::white); + p.drawRect(m_area); + QPen redDashes; + redDashes.setColor(Qt::red); + redDashes.setStyle(Qt::DashLine); + p.setPen(redDashes); + p.drawRect(m_area); + } +} + diff --git a/src/tools/screenshotcropper/cropimageview.h b/src/tools/screenshotcropper/cropimageview.h new file mode 100644 index 0000000000000000000000000000000000000000..a9f3ee38d9bc603d88d6417ce83545b5b7ddcae5 --- /dev/null +++ b/src/tools/screenshotcropper/cropimageview.h @@ -0,0 +1,30 @@ +#ifndef CROPIMAGEVIEW_H +#define CROPIMAGEVIEW_H + +#include <QWidget> + +class CropImageView : public QWidget +{ + Q_OBJECT + +public: + explicit CropImageView(QWidget *parent = 0); + + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + +public slots: + void setImage(const QImage &image); + void setArea(const QRect &area); + +signals: + void cropAreaChanged(const QRect &area); + +private: + QImage m_image; + QRect m_area; +}; + +#endif // CROPIMAGEVIEW_H diff --git a/src/tools/screenshotcropper/main.cpp b/src/tools/screenshotcropper/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cf1a1dc743c120c46acc6bd7178995f7ffa08e6e --- /dev/null +++ b/src/tools/screenshotcropper/main.cpp @@ -0,0 +1,36 @@ +#include <QtGui> +#include "screenshotcropperwindow.h" + +using namespace QtSupport::Internal; + +const QString settingsKeyAreasXmlFile = QLatin1String("areasXmlFile"); +const QString settingsKeyImagesFolder = QLatin1String("imagesFolder"); + +static void promptPaths(QString &areasXmlFile, QString &imagesFolder) +{ + QSettings settings(QLatin1String("Nokia"), QLatin1String("Qt Creator Screenshot Cropper")); + + areasXmlFile = settings.value(settingsKeyAreasXmlFile).toString(); + areasXmlFile = QFileDialog::getOpenFileName(0, QLatin1String("Select the 'images_areaofinterest.xml' file in Qt Creator's sources"), areasXmlFile); + settings.setValue(settingsKeyAreasXmlFile, areasXmlFile); + + imagesFolder = settings.value(settingsKeyImagesFolder).toString(); + imagesFolder = QFileDialog::getExistingDirectory(0, QLatin1String("Select the 'doc/src/images' folder in Qt's sources"), imagesFolder); + settings.setValue(settingsKeyImagesFolder, imagesFolder); +} + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + QString areasXmlFile; + QString imagesFolder; + promptPaths(areasXmlFile, imagesFolder); + + ScreenShotCropperWindow w; + w.show(); + w.loadData(areasXmlFile, imagesFolder); + w.selectImage(0); + + return a.exec(); +} diff --git a/src/tools/screenshotcropper/screenshotcropper.pro b/src/tools/screenshotcropper/screenshotcropper.pro new file mode 100644 index 0000000000000000000000000000000000000000..dcf1a32e6f8ffb9f47491a76b92bd753fa65ea21 --- /dev/null +++ b/src/tools/screenshotcropper/screenshotcropper.pro @@ -0,0 +1,17 @@ +SOURCES += \ + main.cpp\ + screenshotcropperwindow.cpp \ + cropimageview.cpp \ + ../../plugins/qtsupport/screenshotcropper.cpp + +HEADERS += \ + screenshotcropperwindow.h \ + cropimageview.h \ + ../../plugins/qtsupport/screenshotcropper.h + +INCLUDEPATH += \ + ../../plugins/qtsupport \ + ../../plugins + +FORMS += \ + screenshotcropperwindow.ui diff --git a/src/tools/screenshotcropper/screenshotcropperwindow.cpp b/src/tools/screenshotcropper/screenshotcropperwindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..524b5f00dc7b08b8a73b99c57eba0604b2194e10 --- /dev/null +++ b/src/tools/screenshotcropper/screenshotcropperwindow.cpp @@ -0,0 +1,53 @@ +#include "screenshotcropperwindow.h" +#include "ui_screenshotcropperwindow.h" +#include <QtGui/QListWidget> +#include <QtCore/QDebug> + +using namespace QtSupport::Internal; + +ScreenShotCropperWindow::ScreenShotCropperWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::ScreenShotCropperWindow) +{ + ui->setupUi(this); + connect(ui->m_filenamesList, SIGNAL(currentRowChanged(int)), SLOT(selectImage(int))); + connect(ui->m_cropImageView, SIGNAL(cropAreaChanged(QRect)), SLOT(setArea(QRect))); + connect(ui->m_buttonBox, SIGNAL(accepted()), SLOT(saveData())); + connect(ui->m_buttonBox, SIGNAL(rejected()), SLOT(close())); +} + +ScreenShotCropperWindow::~ScreenShotCropperWindow() +{ + delete ui; +} + +void ScreenShotCropperWindow::loadData(const QString &areasXmlFile, const QString &imagesFolder) +{ + m_areasOfInterestFile = areasXmlFile; + m_areasOfInterest = ScreenshotCropper::loadAreasOfInterest(m_areasOfInterestFile); + m_imagesFolder = imagesFolder; + foreach (const QString &pngFile, m_areasOfInterest.keys()) + ui->m_filenamesList->addItem(pngFile); +} + +void ScreenShotCropperWindow::selectImage(int index) +{ + const QString fileName = ui->m_filenamesList->item(index)->text(); + ui->m_cropImageView->setImage(QImage(m_imagesFolder + QLatin1Char('/') + fileName)); + ui->m_cropImageView->setArea(m_areasOfInterest.value(fileName)); +} + +void ScreenShotCropperWindow::setArea(const QRect &area) +{ + const QListWidgetItem *item = ui->m_filenamesList->currentItem(); + if (!item) + return; + const QString currentFile = item->text(); + m_areasOfInterest.insert(currentFile, area); +} + +void ScreenShotCropperWindow::saveData() +{ + if (!ScreenshotCropper::saveAreasOfInterest(m_areasOfInterestFile, m_areasOfInterest)) + qFatal("Cannot write %s", qPrintable(m_areasOfInterestFile)); +} diff --git a/src/tools/screenshotcropper/screenshotcropperwindow.h b/src/tools/screenshotcropper/screenshotcropperwindow.h new file mode 100644 index 0000000000000000000000000000000000000000..5941545010693fe3db7aff34a6673f0f31041d11 --- /dev/null +++ b/src/tools/screenshotcropper/screenshotcropperwindow.h @@ -0,0 +1,35 @@ +#ifndef SCREENSHOTCROPPERWINDOW_H +#define SCREENSHOTCROPPERWINDOW_H + +#include <QMainWindow> +#include "screenshotcropper.h" + +using namespace QtSupport::Internal; + +namespace Ui { +class ScreenShotCropperWindow; +} + +class ScreenShotCropperWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit ScreenShotCropperWindow(QWidget *parent = 0); + ~ScreenShotCropperWindow(); + + void loadData(const QString &areasXmlFile, const QString &imagesFolder); + +public slots: + void selectImage(int index); + void setArea(const QRect &area); + void saveData(); + +private: + AreasOfInterest m_areasOfInterest; + QString m_areasOfInterestFile; + QString m_imagesFolder; + Ui::ScreenShotCropperWindow *ui; +}; + +#endif // SCREENSHOTCROPPERWINDOW_H diff --git a/src/tools/screenshotcropper/screenshotcropperwindow.ui b/src/tools/screenshotcropper/screenshotcropperwindow.ui new file mode 100644 index 0000000000000000000000000000000000000000..9d50284c63ab81d058a4c682d492f37a34645cf4 --- /dev/null +++ b/src/tools/screenshotcropper/screenshotcropperwindow.ui @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ScreenShotCropperWindow</class> + <widget class="QMainWindow" name="ScreenShotCropperWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>680</width> + <height>488</height> + </rect> + </property> + <property name="windowTitle"> + <string>ScreenShotCropperWindow</string> + </property> + <widget class="QWidget" name="centralWidget"> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QSplitter" name="splitter"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <widget class="QListWidget" name="m_filenamesList"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QScrollArea" name="scrollArea"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="widgetResizable"> + <bool>true</bool> + </property> + <widget class="QWidget" name="scrollAreaWidgetContents"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>398</width> + <height>435</height> + </rect> + </property> + <layout class="QHBoxLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="CropImageView" name="m_cropImageView" native="true"/> + </item> + </layout> + </widget> + </widget> + </widget> + </item> + <item row="1" column="0"> + <widget class="QDialogButtonBox" name="m_buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Close|QDialogButtonBox::Save</set> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + <layoutdefault spacing="6" margin="11"/> + <customwidgets> + <customwidget> + <class>CropImageView</class> + <extends>QWidget</extends> + <header>cropimageview.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui>