diff --git a/core/libs/dimg/filters/nr/nrfilter.h b/core/libs/dimg/filters/nr/nrfilter.h index 46dc76b39f..fb3c20b925 100644 --- a/core/libs/dimg/filters/nr/nrfilter.h +++ b/core/libs/dimg/filters/nr/nrfilter.h @@ -1,135 +1,136 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-25 * Description : Wavelets Noise Reduction threaded image filter. * This filter work in YCrCb color space. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2010 by Martin Klapetek * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_NR_FILTER_H #define DIGIKAM_NR_FILTER_H // Qt includes #include #include // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" namespace Digikam { class DIGIKAM_EXPORT NRContainer { public: explicit NRContainer(); ~NRContainer(); public: /** * Separated values per chanel */ double thresholds[3]; ///< Y, Cb, Cr thresholds. double softness[3]; ///< Y, Cb, Cr softness. }; //! qDebug() stream operator. Writes property @a inf to the debug output in a nicely formatted way. DIGIKAM_EXPORT QDebug operator<<(QDebug dbg, const NRContainer& inf); // -------------------------------------------------------------------------- class DIGIKAM_EXPORT NRFilter : public DImgThreadedFilter { private: struct Args { explicit Args() : start(0), stop(0), thold(nullptr), lpass(nullptr), hpass(nullptr), stdev(nullptr), samples(nullptr), + fimg(nullptr), threshold(0.0), softness(0.0) { } uint start; uint stop; float* thold; uint* lpass; uint* hpass; double* stdev; uint* samples; float** fimg; float threshold; double softness; }; public: explicit NRFilter(QObject* const parent = nullptr); NRFilter(DImg* const orgImage, QObject* const parent, const NRContainer& settings); ~NRFilter(); void readParameters(const FilterAction& action) override; virtual FilterAction filterAction() override; virtual QString filterIdentifier() const override; static QString FilterIdentifier(); static QString DisplayableName(); static QList SupportedVersions(); static int CurrentVersion(); static void srgb2ycbcr(float** const fimg, int size); private: void filterImage() override; void waveletDenoise(float* fimg[3], unsigned int width, unsigned int height, float threshold, double softness); inline void hatTransform(float* const temp, float* const base, int st, int size, int sc); void ycbcr2srgb(float** const fimg, int size); void calculteStdevMultithreaded(const Args& prm); void thresholdingMultithreaded(const Args& prm); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_NR_FILTER_H */ diff --git a/core/libs/dimg/filters/transform/autocrop.cpp b/core/libs/dimg/filters/transform/autocrop.cpp index 88cc66aa5f..d9e391018a 100644 --- a/core/libs/dimg/filters/transform/autocrop.cpp +++ b/core/libs/dimg/filters/transform/autocrop.cpp @@ -1,1055 +1,1089 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-10-18 * Description : Auto Crop analyzer * Algorithm based on black point detection on * the basis of spiral traversal * * Copyright (C) 2013 by Sayantan Datta * Copyright (C) 2013-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "autocrop.h" // C++ includes #include #include // Qt includes #include #include #include #include #include #include // Local includes #include "digikam_debug.h" namespace Digikam { class Q_DECL_HIDDEN AutoCrop::Private { public: explicit Private() { } QRect cropArea; }; AutoCrop::AutoCrop(DImg* const img, QObject* const parent) : DImgThreadedAnalyser(parent, QLatin1String("AutoCrop")), d(new Private) { setOriginalImage(*img); } AutoCrop::~AutoCrop() { delete d; } QRect AutoCrop::spiralClockwiseTraversal(const QImage& source, int topCrop, int bottomCrop) { int i, j, ni; if (topCrop == -1) { topCrop = 0; } if (bottomCrop == -1) { bottomCrop = source.height(); } QSize resultsize = QSize(source.width(), (source.height() - topCrop - (source.height() - bottomCrop))); QImage threshold = QImage(resultsize, QImage::Format_RGB888); for (i = topCrop, ni = 0 ; i < bottomCrop ; ++i, ++ni) { for (j = 0 ; j < source.width() ; ++j) { threshold.setPixel(j, ni, source.pixel(j, i)); } } QColor c; int limitcolumn = threshold.width(); int limitrow = threshold.height(); int centeri = ((limitrow/2)-1); int centerj = ((limitcolumn/2)-1); int startrighti = 0; int startrightj = 0; int endrighti = 0; int endrightj = 0; int startlefti = 0; int startleftj = 0; int endlefti = 0; int endleftj = 0; int startupi = 0; int startupj = 0; int endupi = 0; int endupj = 0; int startdowni = 0; int startdownj = 0; int enddowni = 0; int enddownj = 0; int travelright = 0; int travelleft = 0; int travelup = 0; int traveldown = 0; int rightmargin = 0; int leftmargin = 0; int bottommargin = 0; int topmargin = 0; int counter = 0; bool fixtopmargin = false; bool fixrightmargin = false; bool fixleftmargin = false; bool fixbottommargin = false; - //int count = limitcolumn + limitrow -1; +/* + int count = limitcolumn + limitrow -1; +*/ bool rightEdge = false; bool leftEdge = false; bool topEdge = false; bool bottomEdge = false; endupi = centeri; endupj = centerj; travelright = -1; traveldown = -1; travelleft = 0; travelup = 0; qCDebug(DIGIKAM_DIMG_LOG) << "Center pixel : " << centerj << " , " << centeri; while (true) { //qCDebug(DIGIKAM_DIMG_LOG) << "count = "<= threshold.width()) { qCDebug(DIGIKAM_DIMG_LOG) << "We cannot go right anymore"; fixrightmargin = true; rightmargin = limitcolumn - 1; rightEdge = true; } } break; } travelright += 2; if (fixrightmargin == true) + { travelright--; + } if (fixleftmargin == true) + { travelright--; + } //qCDebug(DIGIKAM_DIMG_LOG) << "TRAVELING RIGHT"; //qCDebug(DIGIKAM_DIMG_LOG) << "Endupi" << endupi; startrighti = endupi; startrightj = endupj; endrightj = startrightj + travelright; //qCDebug(DIGIKAM_DIMG_LOG) << "Moving Right EndRight = " << endrightj; if (endrightj >= limitcolumn) { qCDebug(DIGIKAM_DIMG_LOG) << "We have reached limitcolumn, i.e. width"; endrightj = limitcolumn - 1; fixrightmargin = true; rightmargin = limitcolumn - 1; counter++; travelright--; rightEdge = true; } i = startrighti; j = startrightj; (void)j; // Remove clang warnings. for (j = startrightj + 1 ; j <= endrightj ; ++j) { //qCDebug(DIGIKAM_DIMG_LOG) << "At pixel "<< j << " , " << i; c = QColor::fromRgb(threshold.pixel(j,i)); if (c == Qt::black) { // we have found an empty space + fixtopmargin = true; topmargin = i; endupi++; travelup--; break; } } endrighti = startrighti; break; } case 1: // traveling down { if (fixrightmargin == true) { if (fixbottommargin == false) { enddowni++; if (enddowni >= limitrow) { fixbottommargin = true; bottommargin = limitrow - 1; bottomEdge = true; } } //endrightj--; //qCDebug(DIGIKAM_DIMG_LOG) << "Traveling down : Case Skipped\n"; break; } traveldown += 2; - if (fixbottommargin==true) + if (fixbottommargin == true) + { traveldown--; + } - if (fixtopmargin==true) + if (fixtopmargin == true) + { traveldown--; + } startdowni = endrighti; startdownj = endrightj; enddowni = startdowni + traveldown; //qCDebug(DIGIKAM_DIMG_LOG) << "Moving Down EndDown = " << enddowni; if (enddowni >= limitrow) { qCDebug(DIGIKAM_DIMG_LOG) << "We have reached limitrow, i.e. Height"; enddowni = limitrow - 1; counter++; bottommargin = limitrow - 1; fixbottommargin = true; traveldown--; bottomEdge = true; } i = startdowni; (void)i; // Remove clang warning. j = startdownj; for (i = startdowni + 1 ; i <= enddowni ; ++i) { // qCDebug(DIGIKAM_DIMG_LOG) << "At pixel "<< j << " , " << i; c = QColor::fromRgb(threshold.pixel(j,i)); if (c == Qt::black) { //we have found an empty space + fixrightmargin = true; rightmargin = j; endrightj--; travelright--; break; } } enddownj = startdownj; break; } case 2 : //traveling left { if (fixbottommargin == true) { if (fixleftmargin == false) { endleftj--; if (endleftj < 0) { fixleftmargin = true; leftmargin = 0; leftEdge = true; } } break; } travelleft += 2; if (fixleftmargin == true) + { travelleft--; + } if (fixrightmargin == true) + { travelleft--; + } startlefti = enddowni; startleftj = enddownj; endleftj = startleftj - travelleft; //qCDebug(DIGIKAM_DIMG_LOG) << "Moving Left Endleft = " << endleftj; if (endleftj < 0) { qCDebug(DIGIKAM_DIMG_LOG) << "We have gone too left"; endleftj = 0; counter++; leftmargin = 0; fixleftmargin = true; travelleft--; leftEdge = true; } i = startlefti; j = startleftj; (void)j; // Remove clang warning. for (j = startleftj - 1 ; j >= endleftj ; --j) { //qCDebug(DIGIKAM_DIMG_LOG) << "At pixel "<< j << " , " << i; c = QColor::fromRgb(threshold.pixel(j,i)); if (c == Qt::black) { - //we have found an empty space + // we have found an empty space + fixbottommargin = true; bottommargin = i; enddowni--; traveldown--; break; } } endlefti = startlefti; break; } case 3: //traveling up { if (fixleftmargin == true) { if (fixtopmargin == false) { endupi--; endupj = leftmargin; if (endupi < 0) { fixtopmargin = true; topmargin = 0; topEdge = true; } } break; } travelup += 2; if (fixbottommargin == true) + { travelup--; + } if (fixtopmargin == true) + { travelup--; + } startupi = endlefti; startupj = endleftj; endupi = startupi - travelup; //qCDebug(DIGIKAM_DIMG_LOG) << "Moving Up Endup = " << endupi; if (endupi < 0) { qCDebug(DIGIKAM_DIMG_LOG) << "We have gone too right"; endupi = 0; topEdge = true; counter++; fixtopmargin = true; topmargin = 0; travelup--; } i = startupi; (void)i; // Remove clang warning. j = startupj; for (i = startupi - 1 ; i >= endupi ; --i) { //qCDebug(DIGIKAM_DIMG_LOG) << "At pixel "<< j << " , " << i; c = QColor::fromRgb(threshold.pixel(j, i)); if (c == Qt::black) { - //we have found an empty space + // we have found an empty space + fixleftmargin = true; leftmargin = j; endleftj++; travelleft--; break; } } endupj = startupj; break; } } counter++; - if (fixbottommargin == true && - fixtopmargin == true && - fixleftmargin == true && - fixrightmargin == true) + if ((fixbottommargin == true) && + (fixtopmargin == true) && + (fixleftmargin == true) && + (fixrightmargin == true)) break; } // qCDebug(DIGIKAM_DIMG_LOG) << "Count : " << count; qCDebug(DIGIKAM_DIMG_LOG) << "Endupi : " << endupi; qCDebug(DIGIKAM_DIMG_LOG) << "Endupj : " << endupj; qCDebug(DIGIKAM_DIMG_LOG) << "Endrighti : " << endrighti; qCDebug(DIGIKAM_DIMG_LOG) << "Endrightj : " << endrightj; qCDebug(DIGIKAM_DIMG_LOG) << "Enddowni : " << enddowni; qCDebug(DIGIKAM_DIMG_LOG) << "Enddownj : " << enddownj; qCDebug(DIGIKAM_DIMG_LOG) << "Endlefti : " << endlefti; qCDebug(DIGIKAM_DIMG_LOG) << "Endleftj : " << endleftj; qCDebug(DIGIKAM_DIMG_LOG) << "Done" << endl; qCDebug(DIGIKAM_DIMG_LOG) << "Left Margin : " << leftmargin; qCDebug(DIGIKAM_DIMG_LOG) << "Right Margin : " << rightmargin; qCDebug(DIGIKAM_DIMG_LOG) << "Top Margin : " << topmargin; qCDebug(DIGIKAM_DIMG_LOG) << "Bottom Margin : " << bottommargin; qCDebug(DIGIKAM_DIMG_LOG) << "Done" << endl; qCDebug(DIGIKAM_DIMG_LOG) << "Left Edge : " << leftEdge; qCDebug(DIGIKAM_DIMG_LOG) << "Right Edge : " << rightEdge; qCDebug(DIGIKAM_DIMG_LOG) << "Top Edge : " << topEdge; qCDebug(DIGIKAM_DIMG_LOG) << "Bottom Edge : " << bottomEdge; qCDebug(DIGIKAM_DIMG_LOG) << "Done" << endl; if (bottomEdge) { bottommargin++; } if (topEdge) { topmargin--; } if (leftEdge) { leftmargin--; } if (rightEdge) { rightmargin++; } //----------------------releasing images QPoint icp1; icp1.setX(leftmargin + 1); icp1.setY(topCrop + topmargin + 1); QPoint icp2; icp2.setX(rightmargin - 1); icp2.setY(topCrop + bottommargin - 1); QRect cropArea; cropArea.setTopLeft(icp1); cropArea.setBottomRight(icp2); return cropArea; } void AutoCrop::startAnalyse() { QImage img = m_orgImage.copyQImage(); int breakflag = 0; int topRow = -1; int topColumn = -1; int bottomRow = -1; int bottomColumn = -1; int leftRow = -1; int leftColumn = -1; int rightRow = -1; int rightColumn = -1; QColor c; int i,j; postProgress(5); //---------------------- Finding the outer boundaries of the image (i.e. with black portions) - /* This would be done in 4 steps + /** + * This would be done in 4 steps * 1. Search column wise: * (a) From the left to the right, this is to get the left boundary * (b) From the right to the left, this is to get the right boundary * 2. Search row wise : * (a) From the top to the bottom, this is to get the top boundary * (b) From the bottom to the top, this is to get the bottom boundary */ // 1(a) Traversing the image from top to bottom, left to right, to get left boundary breakflag = 0; int width = img.width(); int height = img.height(); for (i = 0 ; i < width ; ++i) { for (j = 0 ; j < height ; ++j) { c = QColor::fromRgb(img.pixel(i, j)); - if (c == Qt::black || !c.isValid()) + if ((c == Qt::black) || !c.isValid()) { // Nothing to do. } else { //we have found our pixel leftRow = j; leftColumn = i; breakflag = 1; break; } } if (breakflag == 1) + { break; + } } qCDebug(DIGIKAM_DIMG_LOG) << "Done Till step 1(a)"; postProgress(30); //1(b) Traversing the image from top to bottom, right to left, to get right boundary breakflag = 0; (void)breakflag; // Remove clang warnings. for (i = 0 ; i < width ; ++i) { for (j = 0 ; j < height ; ++j) { c = QColor::fromRgb(img.pixel(i, j)); - if (c == Qt::black || !c.isValid()) + if ((c == Qt::black) || !c.isValid()) { // Nothing to do. } else { //we have found our pixel rightRow = j; rightColumn = i; break; } } } qCDebug(DIGIKAM_DIMG_LOG) << "Done Till step 1(b)"; postProgress(50); // 2(a) Traversing the image left to right, top to down, to get top boundary breakflag = 0; for (i = 0 ; i < height ; ++i) { for (j = 0 ; j < width ; ++j) { c = QColor::fromRgb(img.pixel(j, i)); - if (c == Qt::black || !c.isValid()) + if ((c == Qt::black) || !c.isValid()) { // Nothing to do. } else { //we have found our pixel topRow = i; topColumn = j; breakflag = 1; break; } } if (breakflag == 1) + { break; + } } qCDebug(DIGIKAM_DIMG_LOG) << "Done Till step 2(a)"; postProgress(70); // 2(b) Traversing the image from left to right, bottom up, to get lower boundary breakflag = 0; for (i = height - 1 ; i >= 0 ; --i) { for (j = 0 ; j < width ; ++j) { c = QColor::fromRgb(img.pixel(j,i)); - if (c == Qt::black || !c.isValid()) + if ((c == Qt::black) || !c.isValid()) { // Nothing to do. } else { //we have found our pixel bottomRow = i; bottomColumn = j; breakflag = 1; break; } } if (breakflag == 1) + { break; + } } qCDebug(DIGIKAM_DIMG_LOG) << "Done Till step 2(b)"; postProgress(90); //------making the required output-------------------- QString outercropParameters; outercropParameters.append(QLatin1String("TopMost Pixel : ( ")); outercropParameters.append(QString::number(topRow)); outercropParameters.append(QLatin1String(", ")); outercropParameters.append(QString::number(topColumn)); outercropParameters.append(QLatin1String(")\nBottomMost Pixel : ( ")); outercropParameters.append(QString::number(bottomRow)); outercropParameters.append(QLatin1String(", ")); outercropParameters.append(QString::number(bottomColumn)); outercropParameters.append(QLatin1String(")\nLeftMost Pixel : ( ")); outercropParameters.append(QString::number(leftRow)); outercropParameters.append(QLatin1String(", ")); outercropParameters.append(QString::number(leftColumn)); outercropParameters.append(QLatin1String(")\nRightMost Pixel : ( ")); outercropParameters.append(QString::number(rightRow)); outercropParameters.append(QLatin1String(", ")); outercropParameters.append(QString::number(rightColumn)); outercropParameters.append(QLatin1String(")\nDONE")); qCDebug(DIGIKAM_DIMG_LOG) << outercropParameters; postProgress(91); QPoint p1; p1.setX(leftColumn); p1.setY(topRow); QPoint p2; p2.setX(rightColumn); p2.setY(bottomRow); QRect crop; crop.setTopLeft(p1); crop.setBottomRight(p2); // crop Image to outerCrop QImage result; QSize resultsize = QSize(crop.width(), crop.height()); result = QImage(resultsize, QImage::Format_RGB888); int ni, nj; qCDebug(DIGIKAM_DIMG_LOG) << "Outer Crop area:"; qCDebug(DIGIKAM_DIMG_LOG) << "From "<< crop.top() << " to " << crop.bottom() << " & " << crop.left() << " to " << crop.right(); for (i = crop.top(), ni = 0 ; i <= crop.bottom() ; ++i, ++ni) { for (j = crop.left(), nj = 0 ; j <= crop.right() ; ++j, ++nj) { result.setPixel(nj, ni, img.pixel(j, i)); } } //---------------------threshold the image QImage threshold = QImage(resultsize,QImage::Format_RGB888); int toggleflag1 = 0,toggleflag2 = 0; int whitepixelCount = 0; //-----initialize for (i = 0 ; i < result.height() ; ++i) { for (j = 0 ; j < result.width() ; ++j) { threshold.setPixel(j, i, Qt::black); } } //----------mark points on horizontal scan for (i = 0 ; i < result.height() ; ++i) { toggleflag1 = -1; toggleflag2 = -1; for (j = 0 ; j < result.width() ; ++j) { c = QColor::fromRgb(result.pixel(j, i)); if (c != Qt::black) { toggleflag1 = j; break; } } for (j = (result.width()-1) ; j >= 0 ; --j) { c = QColor::fromRgb(result.pixel(j, i)); if (c != Qt::black) { toggleflag2 = j; break; } } if (toggleflag1 >= 0) { for (j = toggleflag1 ; j <= toggleflag2 ; ++j) { threshold.setPixel(j, i, qRgb(255, 255, 255)); } } } //----------fill black points on vertical scan for (j = 0 ; j < result.width() ; ++j) { toggleflag1 = -1; toggleflag2 = -2; for (i = 0 ; i < result.height() ; ++i) { c = QColor::fromRgb(result.pixel(j, i)); if (c != Qt::black) { toggleflag1 = i; break; } } for (i = (result.height()-1) ; i >= 0 ; --i) { c = QColor::fromRgb(result.pixel(j, i)); if (c != Qt::black) { toggleflag2 = i; break; } } if (toggleflag1 >= 0) { for (i = 0 ; i < toggleflag1 ; ++i) { threshold.setPixel(j, i, qRgb(0, 0, 0)); } } if (toggleflag2 >= 0) { for (i = (toggleflag2+1) ; i < (result.height()) ; ++i) { threshold.setPixel(j, i, qRgb(0, 0, 0)); } } } // ---count of white pixel in threshold for (i = 0 ; i < threshold.height() ; ++i) { for (j = 0 ; j < threshold.width() ; ++j) { c = QColor::fromRgb(threshold.pixel(j, i)); if (c == Qt::white) { whitepixelCount++; } } } qCDebug(DIGIKAM_DIMG_LOG) << "White pixel count in thresholded image = " << whitepixelCount; qCDebug(DIGIKAM_DIMG_LOG) << "Thresholding Complete\n"; //---------------------inner crop QRect InrCrop = spiralClockwiseTraversal(threshold,-1,-1); QPoint icp1; icp1.setX(InrCrop.topLeft().x() + leftColumn); icp1.setY(InrCrop.topLeft().y() + topRow); QPoint icp2; icp2.setX(InrCrop.bottomRight().x() + leftColumn); icp2.setY(InrCrop.bottomRight().y() + topRow); QRect cropArea; cropArea.setTopLeft(icp1); cropArea.setBottomRight(icp2); qCDebug(DIGIKAM_DIMG_LOG) << "cropArea : "< (whitepixelCount / 1.43)) { d->cropArea.setTopLeft(icp1); d->cropArea.setBottomRight(icp2); qCDebug(DIGIKAM_DIMG_LOG) << "Inner Crop Area : " << d->cropArea; return; } else { //threshold.save("ThresholdedImage.jpg",0,100); qCDebug(DIGIKAM_DIMG_LOG) << "Area not adequate!"; qCDebug(DIGIKAM_DIMG_LOG) << "Extra Cropping Required"; // --- Step 2 -- Search between local minima qCDebug(DIGIKAM_DIMG_LOG) << "In local minima function"; // We need to find the maxima between the first two local minima from either side int* const blackpointCount = new int[threshold.width()]; int leftminima = 0; int rightminima = (threshold.width()-1); int topCropLine = 0; int bottomCropLine = threshold.height()-1; int temp; int temppos; int count; (void) bottomCropLine; // Remove clang warnings. // initialize black point count for (i = 0 ; i < threshold.width() ; ++i) { blackpointCount[i] = 0; } for (j = 0 ; j < threshold.width() ; ++j) { count = 0; for (i = 0 ; i < threshold.height(); ++i) { c = QColor::fromRgb(threshold.pixel(j, i)); if (c == Qt::black) { count++; } else { break; } } blackpointCount[j] = count; } qCDebug(DIGIKAM_DIMG_LOG) << "Top black element count Data Entry Completed"; // --- Searching left minima for (j = 1 ; j < threshold.width() ; ++j) { if ((blackpointCount[j] > blackpointCount[j-1]) && (blackpointCount[j] < (0.2*threshold.height()))) { leftminima = j-1; break; } } for (j = (threshold.width()-2) ; j >= 0 ; --j) { if ((blackpointCount[j] > blackpointCount[j+1]) && (blackpointCount[j] < (0.2*threshold.height()))) { rightminima = j+1; break; } } qCDebug(DIGIKAM_DIMG_LOG) << "Top Part right minima : " << rightminima << " Left Minima : " << leftminima; // --- find the maximum among these minima temp = blackpointCount[leftminima]; temppos = leftminima; for (j = leftminima+1 ; j <= rightminima ; ++j) { if (temp < blackpointCount[j]) { temp = blackpointCount[j]; temppos = j; } } topCropLine = temp; qCDebug(DIGIKAM_DIMG_LOG) << "Found top crop line"; qCDebug(DIGIKAM_DIMG_LOG) << "Found in column = " << temppos << "and the topCropLine is "<< topCropLine; qCDebug(DIGIKAM_DIMG_LOG) << "Searching for bottom crop line"; //-----For the bottom of the image // initialize black point count + for (i = 0 ; i < threshold.width() ; ++i) { blackpointCount[i] = 0; } for (j = 0 ; j < threshold.width() ; ++j) { count = 0; for (i = (threshold.height()-1) ; i >= 0; --i) { c = QColor::fromRgb(result.pixel(j, i)); if (c == Qt::black) { count++; } else { break; } } blackpointCount[j] = count; } qCDebug(DIGIKAM_DIMG_LOG) << "Bottom black element count Data Entry Completed"; // --- Searching left minima for (j = 1 ; j < threshold.width() ; ++j) { if ((blackpointCount[j] > blackpointCount[j-1]) && (blackpointCount[j] < (0.2*threshold.height()))) { leftminima = j-1; break; } } for (j = (threshold.width()-2) ; j >= 0 ; --j) { if ((blackpointCount[j] > blackpointCount[j+1]) && (blackpointCount[j] < (0.2*threshold.height()))) { rightminima = j+1; break; } } // --- find the maximum among these minima temp = blackpointCount[leftminima]; temppos = leftminima; for (j = leftminima+1 ; j <= rightminima ; ++j) { if (temp < blackpointCount[j]) { temp = blackpointCount[j]; temppos = j; } } bottomCropLine = temp; qCDebug(DIGIKAM_DIMG_LOG) << "Found top crop line"; qCDebug(DIGIKAM_DIMG_LOG) << "Found in column = " << temppos; QRect newCrop = spiralClockwiseTraversal(threshold, topCropLine, (threshold.height()-bottomCropLine)); if (newCrop != crop) { icp1.setX(newCrop.topLeft().x() + leftColumn); icp1.setY(newCrop.topLeft().y() + topRow); icp2.setX(newCrop.bottomRight().x() + leftColumn); icp2.setY(newCrop.bottomRight().y() + topRow); d->cropArea.setTopLeft(icp1); d->cropArea.setBottomRight(icp2); } delete [] blackpointCount; } qCDebug(DIGIKAM_DIMG_LOG) << "Inner Crop Area : " << cropArea; /* return(cropArea); resultsize = QSize (cropArea.width(), cropArea.height()); QImage ic = QImage(resultsize,img.format()); for (i = cropArea.top(), ni = 0 ; i <= cropArea.bottom() ; i++, ni++ ) { for (j = cropArea.left(), nj = 0 ; j <= cropArea.right() ; j++, nj++ ) { ic.setPixel(nj, ni, img.pixel(j, i)); } } qCDebug(DIGIKAM_DIMG_LOG) << "From "<cropArea; } QRect AutoCrop::autoInnerCrop() const { return d->cropArea; } } // namespace Digikam diff --git a/core/libs/dimg/filters/transform/autocrop.h b/core/libs/dimg/filters/transform/autocrop.h index 9d6ee4d67e..d3ac7ab8d4 100644 --- a/core/libs/dimg/filters/transform/autocrop.h +++ b/core/libs/dimg/filters/transform/autocrop.h @@ -1,79 +1,83 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-10-18 * Description : Auto Crop analyzer * * Copyright (C) 2013 by Sayantan Datta * Copyright (C) 2013-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_AUTO_CROP_H #define DIGIKAM_AUTO_CROP_H // Qt includes #include #include #include // Local includes #include "digikam_export.h" #include "nrfilter.h" #include "dimg.h" #include "dimgthreadedanalyser.h" namespace Digikam { class DIGIKAM_EXPORT AutoCrop : public DImgThreadedAnalyser { public: - /** Standard constructor with image container to parse + /** + * Standard constructor with image container to parse */ explicit AutoCrop(DImg* const orgImage, QObject* const parent = nullptr); ~AutoCrop(); - /** Perform auto-crop analyze to find best inner crop. Use autoInnerCrop() - * to get computed area. + /** + * Perform auto-crop analyze to find best inner crop. Use autoInnerCrop() + * to get computed area. */ - void startAnalyse() override; + void startAnalyse() override; - /** Return inner crop area detected by startAnalyse(). + /** + * Return inner crop area detected by startAnalyse(). */ QRect autoInnerCrop() const; private: - /** Takes in a binary image and crops it on the basis of black point - * detection, spirally moving outwards. - * topCrop can be set to explicitly crop a upper portion of the image - * bottomCrop can be set to explicitly crop a bottom portion of the image + /** + * Takes in a binary image and crops it on the basis of black point + * detection, spirally moving outwards. + * topCrop can be set to explicitly crop a upper portion of the image + * bottomCrop can be set to explicitly crop a bottom portion of the image */ QRect spiralClockwiseTraversal(const QImage& source, int topCrop = -1, int bottomCrop = -1); private: class Private; Private* d; }; } // namespace Digikam #endif // DIGIKAM_AUTO_CROP_H diff --git a/core/libs/dimg/filters/transform/contentawarefilter.cpp b/core/libs/dimg/filters/transform/contentawarefilter.cpp index bf3039d16c..4595aec5c0 100644 --- a/core/libs/dimg/filters/transform/contentawarefilter.cpp +++ b/core/libs/dimg/filters/transform/contentawarefilter.cpp @@ -1,444 +1,460 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-02-01 * Description : Content aware resizer class. * * Copyright (C) 2009 by Julien Pontabry * Copyright (C) 2009-2020 by Gilles Caulier * Copyright (C) 2010 by Martin Klapetek * * 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, 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. * * ============================================================ */ #include "contentawarefilter.h" // Liquid rescale library include // Pragma directives to reduce warnings from Liblqr header files. #if defined(Q_CC_GNU) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" #endif #include // Restore warnings #if defined(Q_CC_GNU) # pragma GCC diagnostic pop #endif // Qt includes #include // KDE includes #include // Local includes #include "digikam_debug.h" namespace Digikam { // Static methods. LqrRetVal s_carverProgressInit(const gchar* init_message); LqrRetVal s_carverProgressUpdate(gdouble percentage); LqrRetVal s_carverProgressEnd(const gchar* end_message); // Static members. -/** Resizement is decomposed in 2 stages: horizontal and vertical. +/** + * Resizement is decomposed in 2 stages: horizontal and vertical. */ bool s_stage = false; bool s_wResize = false; bool s_hResize = false; ContentAwareFilter* s_resiser = nullptr; static LqrEnergyFuncBuiltinType toLqrEnergy(ContentAwareContainer::EnergyFunction func) { switch (func) { case ContentAwareContainer::GradientNorm: default: return LQR_EF_GRAD_NORM; case ContentAwareContainer::SumOfAbsoluteValues: return LQR_EF_GRAD_SUMABS; case ContentAwareContainer::XAbsoluteValue: return LQR_EF_GRAD_XABS; case ContentAwareContainer::LumaGradientNorm: return LQR_EF_LUMA_GRAD_NORM; case ContentAwareContainer::LumaSumOfAbsoluteValues: return LQR_EF_LUMA_GRAD_SUMABS; case ContentAwareContainer::LumaXAbsoluteValue: return LQR_EF_LUMA_GRAD_XABS; } } static LqrResizeOrder toLqrOrder(Qt::Orientation direction) { switch (direction) { case Qt::Horizontal: default: return LQR_RES_ORDER_HOR; case Qt::Vertical: return LQR_RES_ORDER_VERT; } } // -------------------------------------------------------------------------------------- class Q_DECL_HIDDEN ContentAwareFilter::Private { public: explicit Private() + : carver(nullptr), + progress(nullptr) { - carver = nullptr; - progress = nullptr; } ContentAwareContainer settings; LqrCarver* carver; LqrProgress* progress; }; ContentAwareFilter::ContentAwareFilter(QObject* const parent) : DImgThreadedFilter(parent), d(new Private) { initFilter(); } ContentAwareFilter::ContentAwareFilter(DImg* const orgImage, QObject* const parent, const ContentAwareContainer& settings) : DImgThreadedFilter(orgImage, parent, QLatin1String("ContentAwareFilter")), d(new Private) { initFilter(); s_stage = false; s_resiser = this; d->settings = settings; d->carver = lqr_carver_new_ext(m_orgImage.bits(), m_orgImage.width(), m_orgImage.height(), 4, m_orgImage.sixteenBit() ? LQR_COLDEPTH_16I : LQR_COLDEPTH_8I); if (d->carver) { // Non null carver object operations // Ask Lqr library to preserve our picture + lqr_carver_set_preserve_input_image(d->carver); // Initialize the carver object + lqr_carver_init(d->carver, d->settings.step, d->settings.rigidity); // Create a progress object + d->progress = lqr_progress_new(); lqr_progress_set_init(d->progress, s_carverProgressInit); lqr_progress_set_update(d->progress, s_carverProgressUpdate); lqr_progress_set_end(d->progress, s_carverProgressEnd); lqr_carver_set_progress(d->carver, d->progress); lqr_carver_set_side_switch_frequency(d->carver, d->settings.side_switch_freq); // Set enlargement steps as suggested by Carlo Baldassi + lqr_carver_set_enl_step(d->carver, 1.5); // Choose a gradient function + lqr_carver_set_energy_function_builtin(d->carver, toLqrEnergy(d->settings.func)); // Choose the resize order + lqr_carver_set_resize_order(d->carver, toLqrOrder(d->settings.resize_order)); // Set a bias if any mask + if (!d->settings.mask.isNull()) { buildBias(d->settings.mask); } // Set skin tone mask if option is activated + if (d->settings.preserve_skin_tones) { buildSkinToneBias(); } } } ContentAwareFilter::~ContentAwareFilter() { cancelFilter(); if (d->carver) { lqr_carver_destroy(d->carver); } delete d; } QString ContentAwareFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Content-Aware Filter")); } void ContentAwareFilter::getEnergyImage() { if (!d->carver) { return; } int w = lqr_carver_get_width(d->carver); int h = lqr_carver_get_height(d->carver); guchar* buff = (guchar*) malloc(w * h * 3 * sizeof(guchar)); lqr_carver_get_energy_image(d->carver, buff, 1, LQR_COLDEPTH_8I, LQR_RGBA_IMAGE); } void ContentAwareFilter::filterImage() { if (!d->carver) { return; } uint x = 0; uint y = 0; uint w = 0; uint h = 0; s_wResize = (m_orgImage.width() == d->settings.width) ? false : true; s_hResize = (m_orgImage.height() == d->settings.height) ? false : true; // Liquid rescale + lqr_carver_resize(d->carver, d->settings.width, d->settings.height); if (!runningFlag()) { return; } // Create a new image + w = lqr_carver_get_width(d->carver); h = lqr_carver_get_height(d->carver); m_destImage = DImg(w, h, m_orgImage.sixteenBit()); // Write pixels in the DImg structure image + lqr_carver_scan_reset(d->carver); void* rgb = nullptr; uchar* rgbOut8 = nullptr; unsigned short* rgbOut16 = nullptr; if (m_orgImage.sixteenBit()) { while (runningFlag() && lqr_carver_scan_ext(d->carver, (gint*)&x, (gint*)&y, &rgb)) { rgbOut16 = (unsigned short*)rgb; m_destImage.setPixelColor(x, y, DColor(rgbOut16[2], rgbOut16[1], rgbOut16[0], 65535, true)); } } else { while (runningFlag() && lqr_carver_scan_ext(d->carver, (gint*)&x, (gint*)&y, &rgb)) { rgbOut8 = (uchar*)rgb; m_destImage.setPixelColor(x, y, DColor(rgbOut8[2], rgbOut8[1], rgbOut8[0], 255, false)); } } } void ContentAwareFilter::progressCallback(int progress) { if (progress % 5 == 0) { postProgress(progress); } //qCDebug(DIGIKAM_DIMG_LOG) << "Content Aware Resizing: " << progress << " %"; } void ContentAwareFilter::cancelFilter() { // Handle cancel operations with lqr library. + qCDebug(DIGIKAM_DIMG_LOG) << "Stop LibLqr computation..."; lqr_carver_cancel(d->carver); DImgThreadedFilter::cancelFilter(); } bool ContentAwareFilter::isSkinTone(const DColor& color) { // NOTE: color is previously converted to eight bits. + double R = color.red() / 255.0; double G = color.green() / 255.0; double B = color.blue() / 255.0; double S = R + G + B; - return(((B / G) < 1.249) && + return( + ((B / G) < 1.249) && ((S / 3.0 * R) > 0.696) && ((1.0 / 3.0 - B / S) > 0.014) && ((G / (3.0 * S)) < 0.108) ); } void ContentAwareFilter::buildSkinToneBias() { DColor c; - for (uint x = 0; x < m_orgImage.width(); ++x) + for (uint x = 0 ; x < m_orgImage.width() ; ++x) { - for (uint y = 0; y < m_orgImage.height(); ++y) + for (uint y = 0 ; y < m_orgImage.height() ; ++y) { c = m_orgImage.getPixelColor(x, y); c.convertToEightBit(); gdouble bias = 10000 * isSkinTone(c); lqr_carver_bias_add_xy(d->carver, bias, x, y); } } } void ContentAwareFilter::buildBias(const QImage& mask) { QColor pixColor; int r, g, b, a; - for (int x = 0; x < mask.width(); ++x) + for (int x = 0 ; x < mask.width() ; ++x) { - for (int y = 0; y < mask.height(); ++y) + for (int y = 0 ; y < mask.height() ; ++y) { pixColor = QColor::fromRgba(mask.pixel(x, y)); pixColor.getRgb(&r, &g, &b, &a); gdouble bias = 0.0; if (g == 255) { bias = 1000000.0; } if (r == 255) { bias = -1000000.0; } lqr_carver_bias_add_xy(d->carver, bias, x, y); } } } FilterAction ContentAwareFilter::filterAction() { bool isReproducible = d->settings.mask.isNull(); DefaultFilterAction action(isReproducible); action.addParameter(QLatin1String("height"), d->settings.height); action.addParameter(QLatin1String("preserve_skin_tones"), d->settings.preserve_skin_tones); action.addParameter(QLatin1String("rigidity"), d->settings.rigidity); action.addParameter(QLatin1String("side_switch_freq"), d->settings.side_switch_freq); action.addParameter(QLatin1String("step"), d->settings.step); action.addParameter(QLatin1String("width"), d->settings.width); action.addParameter(QLatin1String("func"), d->settings.func); action.addParameter(QLatin1String("resize_order"), d->settings.resize_order); return std::move(action); } void ContentAwareFilter::readParameters(const FilterAction& action) { d->settings.height = action.parameter(QLatin1String("height")).toUInt(); d->settings.preserve_skin_tones = action.parameter(QLatin1String("preserve_skin_tones")).toBool(); d->settings.rigidity = action.parameter(QLatin1String("rigidity")).toDouble(); d->settings.side_switch_freq = action.parameter(QLatin1String("side_switch_freq")).toInt(); d->settings.step = action.parameter(QLatin1String("step")).toInt(); d->settings.width = action.parameter(QLatin1String("width")).toUInt(); d->settings.func = (ContentAwareContainer::EnergyFunction)action.parameter(QLatin1String("func")).toInt(); d->settings.resize_order = (Qt::Orientation)action.parameter(QLatin1String("resize_order")).toInt(); } // ------------------------------------------------------------------------------------ // Static methods. LqrRetVal s_carverProgressInit(const gchar* /*init_message*/) { if (!s_stage) { s_resiser->progressCallback(0); } else { s_resiser->progressCallback(50); } return LQR_OK; } LqrRetVal s_carverProgressUpdate(gdouble percentage) { int progress; if (!s_stage) { if (!s_wResize || !s_hResize) { progress = (int)(percentage * 100.0); } else { progress = (int)(percentage * 50.0); } } else { progress = (int)(50.0 + percentage * 50.0); } s_resiser->progressCallback(progress); + return LQR_OK; } LqrRetVal s_carverProgressEnd(const gchar* /*end_message*/) { if (!s_stage) { if (!s_wResize || !s_hResize) { s_resiser->progressCallback(100); } else { s_resiser->progressCallback(50); } s_stage = true; } else { s_resiser->progressCallback(100); } return LQR_OK; } } // namespace Digikam diff --git a/core/libs/dimg/filters/transform/contentawarefilter.h b/core/libs/dimg/filters/transform/contentawarefilter.h index 2aadf602c0..2424f79bf4 100644 --- a/core/libs/dimg/filters/transform/contentawarefilter.h +++ b/core/libs/dimg/filters/transform/contentawarefilter.h @@ -1,148 +1,152 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-02-01 * Description : Content aware resizer class. * * Copyright (C) 2009 by Julien Pontabry * Copyright (C) 2009-2020 by Gilles Caulier * Copyright (C) 2010 by Martin Klapetek * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_CONTENT_AWARE_FILTER_H #define DIGIKAM_CONTENT_AWARE_FILTER_H // Qt includes #include // Local includes #include "digikam_config.h" #include "dcolor.h" #include "digikam_export.h" #include "dimgthreadedfilter.h" namespace Digikam { class DIGIKAM_EXPORT ContentAwareContainer { public: enum EnergyFunction { GradientNorm = 0, SumOfAbsoluteValues, XAbsoluteValue, LumaGradientNorm, LumaSumOfAbsoluteValues, LumaXAbsoluteValue }; +public: + ContentAwareContainer() + : preserve_skin_tones(false), + width(0), + height(0), + step(1), + side_switch_freq(4), + rigidity(0.0), + func(GradientNorm), + resize_order(Qt::Horizontal) { - preserve_skin_tones = false; - width = 0; - height = 0; - step = 1; - side_switch_freq = 4; - rigidity = 0.0; - func = GradientNorm; - resize_order = Qt::Horizontal; }; ~ContentAwareContainer() { }; public: bool preserve_skin_tones; uint width; uint height; int step; int side_switch_freq; double rigidity; QImage mask; EnergyFunction func; Qt::Orientation resize_order; }; // ----------------------------------------------------------------------------------------- class DIGIKAM_EXPORT ContentAwareFilter : public DImgThreadedFilter { public: explicit ContentAwareFilter(QObject* const parent = nullptr); - explicit ContentAwareFilter(DImg* const orgImage, QObject* const parent = nullptr, const ContentAwareContainer& settings = ContentAwareContainer()); + explicit ContentAwareFilter(DImg* const orgImage, + QObject* const parent = nullptr, + const ContentAwareContainer& settings = ContentAwareContainer()); ~ContentAwareFilter(); void progressCallback(int progress); static QString FilterIdentifier() { return QLatin1String("digikam:ContentAwareFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + virtual FilterAction filterAction() override; + void readParameters(const FilterAction& action) override; private: - void cancelFilter() override; - void filterImage() override; + void cancelFilter() override; + void filterImage() override; void buildBias(const QImage& mask); void buildSkinToneBias(); inline bool isSkinTone(const DColor& c); void getEnergyImage(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_CONTENT_AWARE_FILTER_H diff --git a/core/libs/dimg/filters/transform/freerotationfilter.cpp b/core/libs/dimg/filters/transform/freerotationfilter.cpp index 909a3c3d94..144623f98b 100644 --- a/core/libs/dimg/filters/transform/freerotationfilter.cpp +++ b/core/libs/dimg/filters/transform/freerotationfilter.cpp @@ -1,435 +1,448 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-07-18 * Description : Free rotation threaded image filter. * * Copyright (C) 2004-2020 by Gilles Caulier * Copyright (C) 2009-2010 by Andi Clemens * Copyright (C) 2010 by Martin Klapetek * * 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, 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. * * ============================================================ */ #include "freerotationfilter.h" // C++ includes #include #include // KDE includes #include // Local includes #include "dimg.h" #include "dpixelsaliasfilter.h" #include "digikam_globals.h" namespace Digikam { class Q_DECL_HIDDEN FreeRotationFilter::Private { public: explicit Private() { } FreeRotationContainer settings; }; FreeRotationFilter::FreeRotationFilter(QObject* const parent) : DImgThreadedFilter(parent), d(new Private) { initFilter(); } FreeRotationFilter::FreeRotationFilter(DImg* const orgImage, QObject* const parent, const FreeRotationContainer& settings) : DImgThreadedFilter(orgImage, parent, QLatin1String("FreeRotation")), d(new Private) { d->settings = settings; initFilter(); } FreeRotationFilter::~FreeRotationFilter() { cancelFilter(); delete d; } QString FreeRotationFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Free Rotation")); } QSize FreeRotationFilter::getNewSize() const { return d->settings.newSize; } double FreeRotationFilter::calculateAngle(int x1, int y1, int x2, int y2) { QPoint p1(x1, y1); QPoint p2(x2, y2); return calculateAngle(p1, p2); } double FreeRotationFilter::calculateAngle(const QPoint& p1, const QPoint& p2) { // check for invalid points. This should have been handled by the calling method, // but we want to be really sure here - if (p1.x() < 0 || - p1.y() < 0 || - p2.x() < 0 || - p2.y() < 0) + if ((p1.x() < 0) || + (p1.y() < 0) || + (p2.x() < 0) || + (p2.y() < 0)) { return 0.0; } // check if points are equal + if (p1 == p2) { return 0.0; } // if y() is equal, no angle needs to be calculated + if (p1.y() == p2.y()) { return 0.0; } // if x() is equal, angle equals 90° + if (p1.x() == p2.x()) { return 90.0; } // do we rotate to the left (counter clock wise)? + bool ccw = ((p1.x() < p2.x()) && (p2.y() > p1.y())) || ((p1.x() > p2.x()) && (p2.y() < p1.y())); // calculate the angle + double ly = fabs((double)p2.y() - p1.y()); double lx = fabs((double)p2.x() - p1.x()); double angle = atan2(ly, lx) * 180.0 / M_PI; angle = ccw ? -angle : angle; return angle; } void FreeRotationFilter::filterImage() { int progress; - int w, h, nw, nh, j, i = 0; + int w, h, nw, nh, j, i = 0; int nNewHeight, nNewWidth; int nhdx, nhdy, nhsx, nhsy; double lfSin, lfCos, lfx, lfy; - int nWidth = m_orgImage.width(); - int nHeight = m_orgImage.height(); + int nWidth = m_orgImage.width(); + int nHeight = m_orgImage.height(); uchar* pBits = m_orgImage.bits(); unsigned short* pBits16 = reinterpret_cast(m_orgImage.bits()); // first of all, we need to calculate the sin and cos of the given angle - lfSin = sin(d->settings.angle * -DEG2RAD); - lfCos = cos(d->settings.angle * -DEG2RAD); + lfSin = sin(d->settings.angle * -DEG2RAD); + lfCos = cos(d->settings.angle * -DEG2RAD); // now, we have to calc the new size for the destination image if ((lfSin * lfCos) < 0) { nNewWidth = lround(fabs(nWidth * lfCos - nHeight * lfSin)); nNewHeight = lround(fabs(nWidth * lfSin - nHeight * lfCos)); } else { nNewWidth = lround(fabs(nWidth * lfCos + nHeight * lfSin)); nNewHeight = lround(fabs(nWidth * lfSin + nHeight * lfCos)); } // getting the destination's center position nhdx = nNewWidth / 2; nhdy = nNewHeight / 2; // getting the source's center position nhsx = nWidth / 2; nhsy = nHeight / 2; // now, we have to alloc a new image bool sixteenBit = m_orgImage.sixteenBit(); m_destImage = DImg(nNewWidth, nNewHeight, sixteenBit, m_orgImage.hasAlpha()); if (m_destImage.isNull()) { return; } m_destImage.fill(DColor(d->settings.backgroundColor.rgb(), sixteenBit)); uchar* pResBits = m_destImage.bits(); unsigned short* pResBits16 = reinterpret_cast(m_destImage.bits()); DPixelsAliasFilter alias; // main loop - for (h = 0; runningFlag() && (h < nNewHeight); ++h) + for (h = 0 ; runningFlag() && (h < nNewHeight) ; ++h) { nh = h - nhdy; - for (w = 0; runningFlag() && (w < nNewWidth); ++w) + for (w = 0 ; runningFlag() && (w < nNewWidth) ; ++w) { nw = w - nhdx; i = setPosition(nNewWidth, w, h); lfx = (double)nw * lfCos - (double)nh * lfSin + nhsx; lfy = (double)nw * lfSin + (double)nh * lfCos + nhsy; if (isInside(nWidth, nHeight, (int)lfx, (int)lfy)) { if (d->settings.antiAlias) { if (!sixteenBit) + { alias.pixelAntiAliasing(pBits, nWidth, nHeight, lfx, lfy, &pResBits[i + 3], &pResBits[i + 2], &pResBits[i + 1], &pResBits[i]); + } else + { alias.pixelAntiAliasing16(pBits16, nWidth, nHeight, lfx, lfy, &pResBits16[i + 3], &pResBits16[i + 2], &pResBits16[i + 1], &pResBits16[i]); + } } else { j = setPosition(nWidth, (int)lfx, (int)lfy); for (int p = 0 ; p < 4 ; ++p) { if (!sixteenBit) { pResBits[i] = pBits[j]; } else { pResBits16[i] = pBits16[j]; } ++i; ++j; } } } } // Update the progress bar in dialog. + progress = (int)(((double) h * 100.0) / nNewHeight); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } // Compute the rotated destination image size using original image dimensions. + int W, H; double absAngle = fabs(d->settings.angle); // stop here when no angle was set + if (absAngle == 0.0) { return; } if (absAngle < 90.0) { W = (int)(d->settings.orgW * cos(absAngle * DEG2RAD) + d->settings.orgH * sin(absAngle * DEG2RAD)); H = (int)(d->settings.orgH * cos(absAngle * DEG2RAD) + d->settings.orgW * sin(absAngle * DEG2RAD)); } else { H = (int)(d->settings.orgW * cos((absAngle - 90.0) * DEG2RAD) + d->settings.orgH * sin((absAngle - 90.0) * DEG2RAD)); W = (int)(d->settings.orgH * cos((absAngle - 90.0) * DEG2RAD) + d->settings.orgW * sin((absAngle - 90.0) * DEG2RAD)); } // Auto-cropping destination image without black holes around. + QRect autoCrop; switch (d->settings.autoCrop) { case FreeRotationContainer::WidestArea: { // 'Widest Area' method (by Renchi Raju). autoCrop.setX((int)(nHeight * sin(absAngle * DEG2RAD))); autoCrop.setY((int)(nWidth * sin(absAngle * DEG2RAD))); autoCrop.setWidth((int)(nNewWidth - 2 * nHeight * sin(absAngle * DEG2RAD))); autoCrop.setHeight((int)(nNewHeight - 2 * nWidth * sin(absAngle * DEG2RAD))); if (!autoCrop.isValid()) { m_destImage = DImg(m_orgImage.width(), m_orgImage.height(), m_orgImage.sixteenBit(), m_orgImage.hasAlpha()); m_destImage.fill(DColor(d->settings.backgroundColor.rgb(), sixteenBit)); d->settings.newSize = QSize(); } else { m_destImage = m_destImage.copy(autoCrop); d->settings.newSize.setWidth((int)(W - 2 * d->settings.orgH * sin(absAngle * DEG2RAD))); d->settings.newSize.setHeight((int)(H - 2 * d->settings.orgW * sin(absAngle * DEG2RAD))); } break; } case FreeRotationContainer::LargestArea: { // 'Largest Area' method (by Gerhard Kulzer). float gamma = 0.0f; if (nHeight > nWidth) { gamma = atan((float) nWidth / (float) nHeight); if (absAngle < 90.0) { autoCrop.setHeight((int)((float) nWidth / cos(absAngle * DEG2RAD) / (tan(gamma) + tan(absAngle * DEG2RAD)))); autoCrop.setWidth((int)((float) autoCrop.height() * tan(gamma))); } else { autoCrop.setWidth((int)((float) nWidth / cos((absAngle - 90.0) * DEG2RAD) / (tan(gamma) + tan((absAngle - 90.0) * DEG2RAD)))); autoCrop.setHeight((int)((float) autoCrop.width() * tan(gamma))); } } else { gamma = atan((float) nHeight / (float) nWidth); if (absAngle < 90.0) { autoCrop.setWidth((int)((float) nHeight / cos(absAngle * DEG2RAD) / (tan(gamma) + tan(absAngle * DEG2RAD)))); autoCrop.setHeight((int)((float) autoCrop.width() * tan(gamma))); } else { autoCrop.setHeight((int)((float) nHeight / cos((absAngle - 90.0) * DEG2RAD) / (tan(gamma) + tan((absAngle - 90.0) * DEG2RAD)))); autoCrop.setWidth((int)((float) autoCrop.height() * tan(gamma))); } } autoCrop.moveCenter(QPoint(nNewWidth / 2, nNewHeight / 2)); if (!autoCrop.isValid()) { m_destImage = DImg(m_orgImage.width(), m_orgImage.height(), m_orgImage.sixteenBit(), m_orgImage.hasAlpha()); m_destImage.fill(DColor(d->settings.backgroundColor.rgb(), sixteenBit)); d->settings.newSize = QSize(); } else { m_destImage = m_destImage.copy(autoCrop); gamma = atan((float) d->settings.orgH / (float) d->settings.orgW); if (absAngle < 90.0) { d->settings.newSize.setWidth((int)((float) d->settings.orgH / cos(absAngle * DEG2RAD) / (tan(gamma) + tan(absAngle * DEG2RAD)))); d->settings.newSize.setHeight((int)((float) d->settings.newSize.width() * tan(gamma))); } else { d->settings.newSize.setHeight((int)((float) d->settings.orgH / cos((absAngle - 90.0) * DEG2RAD) / (tan(gamma) + tan((absAngle - 90.0) * DEG2RAD)))); d->settings.newSize.setWidth((int)((float) d->settings.newSize.height() * tan(gamma))); } } break; } default: // No auto cropping. { d->settings.newSize.setWidth(W); d->settings.newSize.setHeight(H); break; } } } int FreeRotationFilter::setPosition(int Width, int X, int Y) { return (Y * Width * 4 + 4 * X); } bool FreeRotationFilter::isInside(int Width, int Height, int X, int Y) { bool bIsWOk = ((X < 0) ? false : (X >= Width) ? false : true); bool bIsHOk = ((Y < 0) ? false : (Y >= Height) ? false : true); return (bIsWOk && bIsHOk); } FilterAction FreeRotationFilter::filterAction() { FilterAction action(FilterIdentifier(), CurrentVersion()); action.setDisplayableName(DisplayableName()); action.addParameter(QLatin1String("angle"), d->settings.angle); action.addParameter(QLatin1String("antiAlias"), d->settings.antiAlias); action.addParameter(QLatin1String("autoCrop"), d->settings.autoCrop); action.addParameter(QLatin1String("newSize"), d->settings.newSize); action.addParameter(QLatin1String("orgH"), d->settings.orgH); action.addParameter(QLatin1String("orgW"), d->settings.orgW); action.addParameter(QLatin1String("backgroundColorR"), d->settings.backgroundColor.red()); action.addParameter(QLatin1String("backgroundColorG"), d->settings.backgroundColor.green()); action.addParameter(QLatin1String("backgroundColorB"), d->settings.backgroundColor.blue()); action.addParameter(QLatin1String("backgroundColorA"), d->settings.backgroundColor.alpha()); return action; } void FreeRotationFilter::readParameters(const FilterAction& action) { d->settings.angle = action.parameter(QLatin1String("angle")).toDouble(); d->settings.antiAlias = action.parameter(QLatin1String("antiAlias")).toBool(); d->settings.autoCrop = action.parameter(QLatin1String("autoCrop")).toInt(); d->settings.newSize = action.parameter(QLatin1String("newSize")).toSize(); d->settings.orgH = action.parameter(QLatin1String("orgH")).toInt(); d->settings.orgW = action.parameter(QLatin1String("orgW")).toInt(); d->settings.backgroundColor.setRed(action.parameter(QLatin1String("backgroundColorR")).toInt()); d->settings.backgroundColor.setGreen(action.parameter(QLatin1String("backgroundColorG")).toInt()); d->settings.backgroundColor.setBlue(action.parameter(QLatin1String("backgroundColorB")).toInt()); d->settings.backgroundColor.setAlpha(action.parameter(QLatin1String("backgroundColorA")).toInt()); } } // namespace Digikam diff --git a/core/libs/dimg/filters/transform/freerotationfilter.h b/core/libs/dimg/filters/transform/freerotationfilter.h index 1680670a2e..637b3f1efd 100644 --- a/core/libs/dimg/filters/transform/freerotationfilter.h +++ b/core/libs/dimg/filters/transform/freerotationfilter.h @@ -1,139 +1,141 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-07-18 * Description : Free rotation threaded image filter. * * Copyright (C) 2004-2020 by Gilles Caulier * Copyright (C) 2009-2010 by Andi Clemens * Copyright (C) 2010 by Martin Klapetek * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_FREE_ROTATION_FILTER_H #define DIGIKAM_FREE_ROTATION_FILTER_H // Qt includes #include #include // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" namespace Digikam { class DIGIKAM_EXPORT FreeRotationContainer { public: enum AutoCropTypes { NoAutoCrop = 0, WidestArea, LargestArea }; public: FreeRotationContainer() + : antiAlias(true), + autoCrop(NoAutoCrop), + orgW(0), + orgH(0), + angle(0.0), + backgroundColor(Qt::black) { - angle = 0.0; - antiAlias = true; - autoCrop = NoAutoCrop; - backgroundColor = Qt::black; - orgW = 0; - orgH = 0; }; - ~FreeRotationContainer() {}; + ~FreeRotationContainer() + { + }; public: bool antiAlias; int autoCrop; int orgW; int orgH; double angle; QSize newSize; QColor backgroundColor; }; // ----------------------------------------------------------------------------------------- class DIGIKAM_EXPORT FreeRotationFilter : public DImgThreadedFilter { public: explicit FreeRotationFilter(QObject* const parent = nullptr); explicit FreeRotationFilter(DImg* const orgImage, QObject* const parent=nullptr, const FreeRotationContainer& settings=FreeRotationContainer()); virtual ~FreeRotationFilter(); QSize getNewSize() const; static double calculateAngle(int x1, int y1, int x2, int y2); static double calculateAngle(const QPoint& p1, const QPoint& p2); static QString FilterIdentifier() { return QLatin1String("digikam:FreeRotationFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + virtual FilterAction filterAction() override; + void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; inline int setPosition (int Width, int X, int Y); inline bool isInside (int Width, int Height, int X, int Y); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_FREE_ROTATION_FILTER_H diff --git a/core/libs/dimg/filters/transform/freerotationsettings.cpp b/core/libs/dimg/filters/transform/freerotationsettings.cpp index 3cfd19e3fe..eae003adeb 100644 --- a/core/libs/dimg/filters/transform/freerotationsettings.cpp +++ b/core/libs/dimg/filters/transform/freerotationsettings.cpp @@ -1,207 +1,210 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-03-16 * Description : Free rotation settings view. * * Copyright (C) 2010-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "freerotationsettings.h" // Qt includes #include #include #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "dexpanderbox.h" #include "dnuminput.h" #include "digikam_debug.h" #include "dcombobox.h" namespace Digikam { class Q_DECL_HIDDEN FreeRotationSettings::Private { public: explicit Private() : antialiasInput(nullptr), angleInput(nullptr), fineAngleInput(nullptr), autoCropCB(nullptr) - {} + { + } static const QString configAutoCropTypeEntry; static const QString configAntiAliasingEntry; public: QCheckBox* antialiasInput; DIntNumInput* angleInput; DDoubleNumInput* fineAngleInput; DComboBox* autoCropCB; }; const QString FreeRotationSettings::Private::configAutoCropTypeEntry(QLatin1String("Auto Crop Type")); const QString FreeRotationSettings::Private::configAntiAliasingEntry(QLatin1String("Anti Aliasing")); // -------------------------------------------------------- FreeRotationSettings::FreeRotationSettings(QWidget* const parent) : QWidget(parent), d(new Private) { - const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); + const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); QGridLayout* const grid = new QGridLayout(this); // -------------------------------------------------------- QLabel* const label3 = new QLabel(i18n("Main angle:")); d->angleInput = new DIntNumInput; d->angleInput->setRange(-180, 180, 1); d->angleInput->setDefaultValue(0); d->angleInput->setWhatsThis(i18n("An angle in degrees by which to rotate the image. " "A positive angle rotates the image clockwise; " "a negative angle rotates it counter-clockwise.")); QLabel* const label4 = new QLabel(i18n("Fine angle:")); d->fineAngleInput = new DDoubleNumInput; d->fineAngleInput->setRange(-1.0, 1.0, 0.01); d->fineAngleInput->setDefaultValue(0); d->fineAngleInput->setWhatsThis(i18n("This value in degrees will be added to main angle value " "to set fine target angle.")); d->antialiasInput = new QCheckBox(i18n("Anti-Aliasing")); d->antialiasInput->setWhatsThis(i18n("Enable this option to apply the anti-aliasing filter " "to the rotated image. " "In order to smooth the target image, it will be blurred a little.")); QLabel* const label5 = new QLabel(i18n("Auto-crop:")); d->autoCropCB = new DComboBox; d->autoCropCB->addItem(i18nc("no autocrop", "None")); d->autoCropCB->addItem(i18n("Widest Area")); d->autoCropCB->addItem(i18n("Largest Area")); d->autoCropCB->setDefaultIndex(FreeRotationContainer::NoAutoCrop); d->autoCropCB->setWhatsThis(i18n("Select the method to process image auto-cropping " "to remove black frames around a rotated image here.")); // ------------------------------------------------------------- grid->addWidget(label3, 0, 0, 1, 1); grid->addWidget(d->angleInput, 1, 0, 1, 2); grid->addWidget(label4, 2, 0, 1, 1); grid->addWidget(d->fineAngleInput, 3, 0, 1, 2); grid->addWidget(d->antialiasInput, 4, 0, 1, -1); grid->addWidget(label5, 5, 0, 1, 1); grid->addWidget(d->autoCropCB, 5, 1, 1, 1); grid->setContentsMargins(spacing, spacing, spacing, spacing); grid->setSpacing(spacing); // ------------------------------------------------------------- connect(d->antialiasInput, SIGNAL(toggled(bool)), this, SIGNAL(signalSettingsChanged())); connect(d->autoCropCB, SIGNAL(activated(int)), this, SIGNAL(signalSettingsChanged())); connect(d->angleInput, SIGNAL(valueChanged(int)), this, SIGNAL(signalSettingsChanged())); connect(d->fineAngleInput, SIGNAL(valueChanged(double)), this, SIGNAL(signalSettingsChanged())); } FreeRotationSettings::~FreeRotationSettings() { delete d; } FreeRotationContainer FreeRotationSettings::settings() const { FreeRotationContainer prm; prm.angle = (double)d->angleInput->value() + d->fineAngleInput->value(); prm.antiAlias = d->antialiasInput->isChecked(); prm.autoCrop = d->autoCropCB->currentIndex(); + return prm; } void FreeRotationSettings::setSettings(const FreeRotationContainer& settings) { blockSignals(true); d->angleInput->setValue((int)(settings.angle)); d->fineAngleInput->setValue(settings.angle - (double)d->angleInput->value()); d->antialiasInput->setChecked(settings.antiAlias); d->autoCropCB->setCurrentIndex(settings.autoCrop); blockSignals(false); } void FreeRotationSettings::resetToDefault() { blockSignals(true); d->angleInput->slotReset(); d->fineAngleInput->slotReset(); d->antialiasInput->setChecked(true); d->autoCropCB->slotReset(); blockSignals(false); } FreeRotationContainer FreeRotationSettings::defaultSettings() const { FreeRotationContainer prm; prm.angle = d->angleInput->defaultValue(); prm.antiAlias = true; prm.autoCrop = d->autoCropCB->defaultIndex(); + return prm; } void FreeRotationSettings::readSettings(KConfigGroup& group) { d->autoCropCB->setCurrentIndex(group.readEntry(d->configAutoCropTypeEntry, d->autoCropCB->defaultIndex())); d->antialiasInput->setChecked(group.readEntry(d->configAntiAliasingEntry, true)); d->angleInput->slotReset(); d->fineAngleInput->slotReset(); } void FreeRotationSettings::writeSettings(KConfigGroup& group) { FreeRotationContainer prm = settings(); group.writeEntry(d->configAutoCropTypeEntry, d->autoCropCB->currentIndex()); group.writeEntry(d->configAntiAliasingEntry, d->antialiasInput->isChecked()); } } // namespace Digikam diff --git a/core/libs/dimg/filters/transform/freerotationsettings.h b/core/libs/dimg/filters/transform/freerotationsettings.h index 13ae0ccd1a..b299cb352e 100644 --- a/core/libs/dimg/filters/transform/freerotationsettings.h +++ b/core/libs/dimg/filters/transform/freerotationsettings.h @@ -1,71 +1,71 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-03-16 * Description : Free rotation settings view. * * Copyright (C) 2010-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_FREE_ROTATION_SETTINGS_H #define DIGIKAM_FREE_ROTATION_SETTINGS_H // Local includes #include // Local includes #include "digikam_export.h" #include "freerotationfilter.h" class KConfigGroup; namespace Digikam { class DIGIKAM_EXPORT FreeRotationSettings : public QWidget { Q_OBJECT public: explicit FreeRotationSettings(QWidget* const parent); ~FreeRotationSettings(); FreeRotationContainer defaultSettings() const; void resetToDefault(); - FreeRotationContainer settings() const; + FreeRotationContainer settings() const; void setSettings(const FreeRotationContainer& settings); void readSettings(KConfigGroup& group); void writeSettings(KConfigGroup& group); Q_SIGNALS: void signalSettingsChanged(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_FREE_ROTATION_SETTINGS_H diff --git a/core/libs/dimg/filters/transform/shearfilter.cpp b/core/libs/dimg/filters/transform/shearfilter.cpp index a38a38b224..735f1403ff 100644 --- a/core/libs/dimg/filters/transform/shearfilter.cpp +++ b/core/libs/dimg/filters/transform/shearfilter.cpp @@ -1,273 +1,289 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-07-18 * Description : Shear tool threaded image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2010 by Martin Klapetek * * Original Shear algorithms copyrighted 2005 by * Pieter Z. Voloshyn . * * 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, 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. * * ============================================================ */ #include "shearfilter.h" // C++ includes #include #include // KDE includes #include // Local includes #include "digikam_globals.h" #include "dimg.h" #include "dpixelsaliasfilter.h" namespace Digikam { class Q_DECL_HIDDEN ShearFilter::Private { public: explicit Private() + : antiAlias(true), + orgW(0), + orgH(0), + hAngle(0), + vAngle(0), + backgroundColor(Qt::black) { - antiAlias = true; - orgW = 0; - orgH = 0; - hAngle = 0; - vAngle = 0; - backgroundColor = Qt::black; } bool antiAlias; int orgW; int orgH; float hAngle; float vAngle; QColor backgroundColor; QSize newSize; }; ShearFilter::ShearFilter(QObject* const parent) : DImgThreadedFilter(parent), d(new Private) { initFilter(); } ShearFilter::ShearFilter(DImg* const orgImage, QObject* const parent, float hAngle, float vAngle, bool antialiasing, const QColor& backgroundColor, int orgW, int orgH) : DImgThreadedFilter(orgImage, parent, QLatin1String("sheartool")), d(new Private) { d->hAngle = hAngle; d->vAngle = vAngle; d->orgW = orgW; d->orgH = orgH; d->antiAlias = antialiasing; d->backgroundColor = backgroundColor; initFilter(); } ShearFilter::~ShearFilter() { cancelFilter(); } QString ShearFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Shear Tool")); } QSize ShearFilter::getNewSize() const { return d->newSize; } void ShearFilter::filterImage() { int progress; - int x, y, p = 0, pt; + int x, y, p = 0, pt; int new_width, new_height; double nx, ny, dx, dy; double horz_factor, vert_factor; double horz_add, vert_add; double horz_beta_angle, vert_beta_angle; int nWidth = m_orgImage.width(); int nHeight = m_orgImage.height(); uchar* pBits = m_orgImage.bits(); unsigned short* pBits16 = reinterpret_cast(m_orgImage.bits()); // get beta ( complementary ) angle for horizontal and vertical angles + horz_beta_angle = (((d->hAngle < 0.0) ? 180.0 : 90.0) - d->hAngle) * DEG2RAD; vert_beta_angle = (((d->vAngle < 0.0) ? 180.0 : 90.0) - d->vAngle) * DEG2RAD; // get new distance for width and height values + horz_add = nHeight * ((d->hAngle < 0.0) ? sin(horz_beta_angle) : cos(horz_beta_angle)); vert_add = nWidth * ((d->vAngle < 0.0) ? sin(vert_beta_angle) : cos(vert_beta_angle)); // get absolute values for the distances + horz_add = fabs(horz_add); vert_add = fabs(vert_add); // get new image size ( original size + distance ) + new_width = (int)horz_add + nWidth; new_height = (int)vert_add + nHeight; // get scale factor for width and height + horz_factor = horz_add / new_height; vert_factor = vert_add / new_width; // if horizontal angle is greater than zero... // else, initial distance is equal to maximum distance ( in negative form ) + if (d->hAngle > 0.0) { // initial distance is zero and scale is negative ( to decrease ) - dx = 0; + + dx = 0; horz_factor *= -1.0; } else { dx = -horz_add; } // if vertical angle is greater than zero... // else, initial distance is equal to maximum distance ( in negative form ) + if (d->vAngle > 0.0) { // initial distance is zero and scale is negative ( to decrease ) dy = 0; vert_factor *= -1.0; } else { dy = -vert_add; } // allocates a new image with the new size bool sixteenBit = m_orgImage.sixteenBit(); m_destImage = DImg(new_width, new_height, sixteenBit, m_orgImage.hasAlpha()); m_destImage.fill(DColor(d->backgroundColor.rgb(), sixteenBit)); uchar* pResBits = m_destImage.bits(); unsigned short* pResBits16 = reinterpret_cast(m_destImage.bits()); DPixelsAliasFilter alias; - for (y = 0; y < new_height; ++y) + for (y = 0 ; y < new_height ; ++y) { - for (x = 0; x < new_width; ++x, p += 4) + for (x = 0 ; x < new_width ; ++x, p += 4) { // get new positions + nx = x + dx + y * horz_factor; ny = y + dy + x * vert_factor; // if is inside the source image + if (isInside(nWidth, nHeight, lround(nx), lround(ny))) { if (d->antiAlias) { if (!sixteenBit) + { alias.pixelAntiAliasing(pBits, nWidth, nHeight, nx, ny, &pResBits[p + 3], &pResBits[p + 2], &pResBits[p + 1], &pResBits[p]); + } else + { alias.pixelAntiAliasing16(pBits16, nWidth, nHeight, nx, ny, &pResBits16[p + 3], &pResBits16[p + 2], &pResBits16[p + 1], &pResBits16[p]); + } } else { pt = setPosition(nWidth, lround(nx), lround(ny)); for (int z = 0 ; z < 4 ; ++z) { if (!sixteenBit) { pResBits[p + z] = pBits[pt + z]; } else { pResBits16[p + z] = pBits16[pt + z]; } } } } } // Update the progress bar in dialog. + progress = (int)(((double)y * 100.0) / new_height); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } // To compute the rotated destination image size using original image dimensions. + int W = (int)(fabs(d->orgH * ((d->hAngle < 0.0) ? sin(horz_beta_angle) : cos(horz_beta_angle)))) + d->orgW; int H = (int)(fabs(d->orgW * ((d->vAngle < 0.0) ? sin(vert_beta_angle) : cos(vert_beta_angle)))) + d->orgH; d->newSize.setWidth(W); d->newSize.setHeight(H); } FilterAction ShearFilter::filterAction() { FilterAction action(FilterIdentifier(), CurrentVersion()); action.setDisplayableName(DisplayableName()); action.addParameter(QLatin1String("antiAlias"), d->antiAlias); action.addParameter(QLatin1String("hAngle"), d->hAngle); action.addParameter(QLatin1String("orgH"), d->orgH); action.addParameter(QLatin1String("orgW"), d->orgW); action.addParameter(QLatin1String("vAngle"), d->vAngle); action.addParameter(QLatin1String("backgroundColorR"), d->backgroundColor.red()); action.addParameter(QLatin1String("backgroundColorG"), d->backgroundColor.green()); action.addParameter(QLatin1String("backgroundColorB"), d->backgroundColor.blue()); action.addParameter(QLatin1String("backgroundColorA"), d->backgroundColor.alpha()); return action; } void ShearFilter::readParameters(const FilterAction& action) { d->antiAlias = action.parameter(QLatin1String("antiAlias")).toBool(); d->hAngle = action.parameter(QLatin1String("hAngle")).toFloat(); d->orgH = action.parameter(QLatin1String("orgH")).toInt(); d->orgW = action.parameter(QLatin1String("orgW")).toInt(); d->vAngle = action.parameter(QLatin1String("vAngle")).toFloat(); d->backgroundColor.setRed(action.parameter(QLatin1String("backgroundColorR")).toInt()); d->backgroundColor.setGreen(action.parameter(QLatin1String("backgroundColorG")).toInt()); d->backgroundColor.setBlue(action.parameter(QLatin1String("backgroundColorB")).toInt()); d->backgroundColor.setAlpha(action.parameter(QLatin1String("backgroundColorA")).toInt()); } } // namespace Digikam diff --git a/core/libs/dimg/filters/transform/shearfilter.h b/core/libs/dimg/filters/transform/shearfilter.h index a21c71a924..880cc6bc93 100644 --- a/core/libs/dimg/filters/transform/shearfilter.h +++ b/core/libs/dimg/filters/transform/shearfilter.h @@ -1,104 +1,104 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-07-18 * Description : Shear tool threaded image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2010 by Martin Klapetek * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_SHEAR_FILTER_H #define DIGIKAM_SHEAR_FILTER_H // Qt includes #include #include // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" #include "digikam_globals.h" namespace Digikam { class DIGIKAM_EXPORT ShearFilter : public DImgThreadedFilter { public: explicit ShearFilter(QObject* const parent = nullptr); explicit ShearFilter(DImg* const orgImage, QObject* const parent=nullptr, float hAngle=0.0, float vAngle=0.0, bool antialiasing=true, const QColor& backgroundColor=Qt::black, int orgW=0, int orgH=0); ~ShearFilter(); QSize getNewSize() const; static QString FilterIdentifier() { return QLatin1String("digikam:ShearFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + virtual FilterAction filterAction() override; + void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; inline int setPosition (int Width, int X, int Y) { return (Y*Width*4 + 4*X); }; inline bool isInside (int Width, int Height, int X, int Y) { bool bIsWOk = ((X < 0) ? false : (X >= Width ) ? false : true); bool bIsHOk = ((Y < 0) ? false : (Y >= Height) ? false : true); return (bIsWOk && bIsHOk); }; private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_SHEAR_FILTER_H diff --git a/core/libs/dimg/filters/wb/wbcontainer.cpp b/core/libs/dimg/filters/wb/wbcontainer.cpp index 77406d45a9..b6d5a31e11 100644 --- a/core/libs/dimg/filters/wb/wbcontainer.cpp +++ b/core/libs/dimg/filters/wb/wbcontainer.cpp @@ -1,90 +1,94 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-16-01 * Description : white balance color correction. * * Copyright (C) 2007-2020 by Gilles Caulier * Copyright (C) 2008 by Guillaume Castagnino * Copyright (C) 2010 by Martin Klapetek * * 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, 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. * * ============================================================ */ // Local includes #include "wbcontainer.h" #include "wbfilter.h" namespace Digikam { +/** + * Neutral color temperature settings. + */ WBContainer::WBContainer() + : black(0.0), + expositionMain(0.0), + expositionFine(0.0), + temperature(6500.0), + green(1.0), + dark(0.5), + gamma(1.0), + saturation(1.0) { - // Neutral color temperature settings. - black = 0.0; - expositionMain = 0.0; - expositionFine = 0.0; - temperature = 6500.0; - green = 1.0; - dark = 0.5; - gamma = 1.0; - saturation = 1.0; } bool WBContainer::isDefault() const { return (*this == WBContainer()); } bool WBContainer::operator==(const WBContainer& other) const { - return black == other.black && - expositionMain == other.expositionMain && - expositionFine == other.expositionFine && - temperature == other.temperature && - green == other.green && - dark == other.dark && - gamma == other.gamma && - saturation == other.saturation; + return ( + (black == other.black) && + (expositionMain == other.expositionMain) && + (expositionFine == other.expositionFine) && + (temperature == other.temperature) && + (green == other.green) && + (dark == other.dark) && + (gamma == other.gamma) && + (saturation == other.saturation) + ); } void WBContainer::writeToFilterAction(FilterAction& action, const QString& prefix) const { action.addParameter(prefix + QLatin1String("black"), black); action.addParameter(prefix + QLatin1String("expositionMain"), expositionMain); action.addParameter(prefix + QLatin1String("expositionFine"), expositionFine); action.addParameter(prefix + QLatin1String("temperature"), temperature); action.addParameter(prefix + QLatin1String("green"), green); action.addParameter(prefix + QLatin1String("dark"), dark); action.addParameter(prefix + QLatin1String("gamma"), gamma); action.addParameter(prefix + QLatin1String("saturation"), saturation); } WBContainer WBContainer::fromFilterAction(const FilterAction& action, const QString& prefix) { WBContainer settings; settings.black = action.parameter(prefix + QLatin1String("black"), settings.black); settings.expositionMain = action.parameter(prefix + QLatin1String("expositionMain"), settings.expositionMain); settings.expositionFine = action.parameter(prefix + QLatin1String("expositionFine"), settings.expositionFine); settings.temperature = action.parameter(prefix + QLatin1String("temperature"), settings.temperature); settings.green = action.parameter(prefix + QLatin1String("green"), settings.green); settings.dark = action.parameter(prefix + QLatin1String("dark"), settings.dark); settings.gamma = action.parameter(prefix + QLatin1String("gamma"), settings.gamma); settings.saturation = action.parameter(prefix + QLatin1String("saturation"), settings.saturation); return settings; } } // namespace Digikam diff --git a/core/libs/dimg/filters/wb/wbcontainer.h b/core/libs/dimg/filters/wb/wbcontainer.h index d2c135a8d8..6fbed78b31 100644 --- a/core/libs/dimg/filters/wb/wbcontainer.h +++ b/core/libs/dimg/filters/wb/wbcontainer.h @@ -1,69 +1,72 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-12-15 * Description : white balance color correction settings container * * Copyright (C) 2007-2020 by Gilles Caulier * Copyright (C) 2008 by Guillaume Castagnino * Copyright (C) 2010 by Martin Klapetek * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_WB_CONTAINER_H #define DIGIKAM_WB_CONTAINER_H // Qt includes #include // Local includes #include "digikam_export.h" namespace Digikam { class FilterAction; class DIGIKAM_EXPORT WBContainer { public: WBContainer(); - bool isDefault() const; - bool operator==(const WBContainer& other) const; + bool isDefault() const; + bool operator==(const WBContainer& other) const; - void writeToFilterAction(FilterAction& action, const QString& prefix = QString()) const; - static WBContainer fromFilterAction(const FilterAction& action, const QString& prefix = QString()); + void writeToFilterAction(FilterAction& action, + const QString& prefix = QString()) const; + + static WBContainer fromFilterAction(const FilterAction& action, + const QString& prefix = QString()); public: double black; double expositionMain; double expositionFine; double temperature; double green; double dark; double gamma; double saturation; }; } // namespace Digikam #endif // DIGIKAM_WB_CONTAINER_H diff --git a/core/libs/dimg/filters/wb/wbfilter.cpp b/core/libs/dimg/filters/wb/wbfilter.cpp index 209ea6dd58..e264e3f76e 100644 --- a/core/libs/dimg/filters/wb/wbfilter.cpp +++ b/core/libs/dimg/filters/wb/wbfilter.cpp @@ -1,403 +1,410 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-16-01 * Description : white balance color correction. * * Copyright (C) 2007-2020 by Gilles Caulier * Copyright (C) 2008 by Guillaume Castagnino * Copyright (C) 2010 by Martin Klapetek * * 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, 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. * * ============================================================ */ #include "wbfilter.h" // C++ includes #include #include // KDE includes #include // Local includes #include "dimg.h" #include "digikam_debug.h" #include "imagehistogram.h" namespace Digikam { class Q_DECL_HIDDEN WBFilter::Private { public: explicit Private() + : BP(0), + WP(0), + rgbMax(0), + mr(1.0), + mg(1.0), + mb(1.0) { - mr = 1.0; - mg = 1.0; - mb = 1.0; - BP = 0; - WP = 0; - rgbMax = 0; for (int i = 0 ; i < 65536 ; ++i) { curve[i] = 0.0; } } int BP; int WP; uint rgbMax; double curve[65536]; double mr; double mg; double mb; }; WBFilter::WBFilter(QObject* const parent) : DImgThreadedFilter(parent), d(new Private) { initFilter(); } WBFilter::WBFilter(DImg* const orgImage, QObject* const parent, const WBContainer& settings) : DImgThreadedFilter(orgImage, parent, QLatin1String("WBFilter")), m_settings(settings), d(new Private) { initFilter(); } WBFilter::WBFilter(const WBContainer& settings, DImgThreadedFilter* const master, const DImg& orgImage, const DImg& destImage, int progressBegin, int progressEnd) : DImgThreadedFilter(master, orgImage, destImage, progressBegin, progressEnd, QLatin1String("WBFilter")), m_settings(settings), d(new Private) { filterImage(); } WBFilter::~WBFilter() { cancelFilter(); delete d; } QString WBFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("White Balance Tool")); } void WBFilter::filterImage() { d->WP = m_orgImage.sixteenBit() ? 65536 : 256; d->rgbMax = m_orgImage.sixteenBit() ? 65536 : 256; // Set final lut. + setLUTv(); setRGBmult(m_settings.temperature, m_settings.green, d->mr, d->mg, d->mb); qCDebug(DIGIKAM_DIMG_LOG) << "T(K):" << m_settings.temperature << "=> R:" << d->mr << " G:" << d->mg << " B:" << d->mb << " BP:" << d->BP << " WP:" << d->WP; // Apply White balance adjustments. + adjustWhiteBalance(m_orgImage.bits(), m_orgImage.width(), m_orgImage.height(), m_orgImage.sixteenBit()); m_destImage = m_orgImage; } void WBFilter::autoWBAdjustementFromColor(const QColor& tc, double& temperature, double& green) { // Calculate Temperature and Green component from color picked. double mr = 0.0; double mg = 0.0; double mb = 0.0; qCDebug(DIGIKAM_DIMG_LOG) << "Sums: R:" << tc.red() << " G:" << tc.green() << " B:" << tc.blue(); - /* This is a dichotomic search based on Blue and Red layers ratio - to find the matching temperature - adapted from ufraw (0.12.1) RGB_to_Temperature - */ + /** + * This is a dichotomic search based on Blue and Red layers ratio + * to find the matching temperature + * adapted from ufraw (0.12.1) RGB_to_Temperature + */ double tmin = 2000.0; double tmax = 12000.0; double mBR = (double)tc.blue() / (double)tc.red(); green = 1.0; for (temperature = (tmin + tmax) / 2 ; tmax - tmin > 10 ; temperature = (tmin + tmax) / 2) { qCDebug(DIGIKAM_DIMG_LOG) << "Intermediate Temperature (K):" << temperature; setRGBmult(temperature, green, mr, mg, mb); if (mr / mb > mBR) { tmax = temperature; } else { tmin = temperature; } } // Calculate the green level to neutralize picture + green = (mr / mg) / ((double)tc.green() / (double)tc.red()); qCDebug(DIGIKAM_DIMG_LOG) << "Temperature (K):" << temperature; qCDebug(DIGIKAM_DIMG_LOG) << "Green component:" << green; } void WBFilter::autoExposureAdjustement(const DImg* const img, double& black, double& expo) { // Create an histogram of original image. ImageHistogram* const histogram = new ImageHistogram(*img); histogram->calculate(); // Calculate optimal exposition and black level int i; double sum, stop; uint rgbMax = img->sixteenBit() ? 65536 : 256; // Cutoff at 0.5% of the histogram. stop = img->width() * img->height() / 200; for (i = rgbMax, sum = 0 ; (i >= 0) && (sum < stop) ; --i) { sum += histogram->getValue(LuminosityChannel, i); } expo = -log((double)(i + 1) / rgbMax) / log(2); qCDebug(DIGIKAM_DIMG_LOG) << "White level at:" << i; for (i = 1, sum = 0 ; (i < (int)rgbMax) && (sum < stop) ; ++i) { sum += histogram->getValue(LuminosityChannel, i); } black = (double)i / rgbMax; black /= 2; qCDebug(DIGIKAM_DIMG_LOG) << "Black:" << black << " Exposition:" << expo; delete histogram; } void WBFilter::setRGBmult(double& temperature, double& green, double& mr, double& mg, double& mb) { // Original implementation by Tanner Helland // http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ double temp = temperature / 100.0; double mx; if (temp > 120.0) { temp = 120.0; } if (temp <= 66.0) { mr = 1.0; mg = temp; mg = 99.4708025861 * log(mg) - 161.1195681661; } else { mr = temp - 60.0; mr = 329.698727466 * pow(mr, -0.1332047592); mr = 1.0 / (1.0 / 255.0 * CLAMP(mr, 0.0, 255.0)); mg = temp - 60.0; mg = 288.1221695283 * pow(mg, -0.0755148492); } mg = 1.0 / 255.0 * CLAMP(mg, 0.0, 255.0); // Apply green multiplier + mg = mg / green; mg = 1.0 / mg; if (temp >= 66.0) { mb = 1.0; } else { if (temp <= 19.0) { mb = 0.0; } else { mb = temp - 10.0; mb = 138.5177312231 * log(mb) - 305.0447927307; mb = 1.0 / (1.0 / 255.0 * CLAMP(mb, 0.0, 255.0)); } } // Calculate to an average of 1.0 + mx = ((mr + mg + mb) / 3.0) - 0.01; mr /= mx; mg /= mx; mb /= mx; } void WBFilter::setLUTv() { double b = pow(2, m_settings.expositionMain + m_settings.expositionFine); d->BP = (uint)(d->rgbMax * m_settings.black); d->WP = (uint)(d->rgbMax / b); if (d->WP - d->BP < 1) { d->WP = d->BP + 1; } d->curve[0] = 0.0; // We will try to reproduce the same Gamma effect here than BCG tool. + double gamma; if (m_settings.gamma >= 1.0) { gamma = 0.335 * (2.0 - m_settings.gamma) + 0.665; } else { gamma = 1.8 * (2.0 - m_settings.gamma) - 0.8; } for (int i = 1 ; i < (int)d->rgbMax ; ++i) { double x = (double)(i - d->BP) / (d->WP - d->BP); d->curve[i] = (i < d->BP) ? 0 : (d->rgbMax - 1) * pow((double)x, gamma); d->curve[i] *= (1 - m_settings.dark * exp(-x * x / 0.002)); d->curve[i] /= (double)i; } } void WBFilter::adjustWhiteBalance(uchar* const data, int width, int height, bool sixteenBit) { uint size = (uint)(width * height); uint j; int progress; if (!sixteenBit) // 8 bits image. { uchar red, green, blue; uchar* ptr = data; for (j = 0 ; runningFlag() && (j < size) ; ++j) { int idx, rv[3]; - blue = ptr[0]; - green = ptr[1]; - red = ptr[2]; + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; - rv[0] = (int)(blue * d->mb); - rv[1] = (int)(green * d->mg); - rv[2] = (int)(red * d->mr); - idx = qMax(rv[0], rv[1]); - idx = qMax(idx, rv[2]); - idx = qMin(idx, (int)d->rgbMax - 1); + rv[0] = (int)(blue * d->mb); + rv[1] = (int)(green * d->mg); + rv[2] = (int)(red * d->mr); + idx = qMax(rv[0], rv[1]); + idx = qMax(idx, rv[2]); + idx = qMin(idx, (int)d->rgbMax - 1); ptr[0] = (uchar)pixelColor(rv[0], idx); ptr[1] = (uchar)pixelColor(rv[1], idx); ptr[2] = (uchar)pixelColor(rv[2], idx); ptr += 4; progress = (int)(((double)j * 100.0) / size); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } else // 16 bits image. { unsigned short red, green, blue; unsigned short* ptr = reinterpret_cast(data); for (j = 0 ; runningFlag() && (j < size) ; ++j) { int idx, rv[3]; - blue = ptr[0]; - green = ptr[1]; - red = ptr[2]; + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; - rv[0] = (int)(blue * d->mb); - rv[1] = (int)(green * d->mg); - rv[2] = (int)(red * d->mr); - idx = qMax(rv[0], rv[1]); - idx = qMax(idx, rv[2]); - idx = qMin(idx, (int)d->rgbMax - 1); + rv[0] = (int)(blue * d->mb); + rv[1] = (int)(green * d->mg); + rv[2] = (int)(red * d->mr); + idx = qMax(rv[0], rv[1]); + idx = qMax(idx, rv[2]); + idx = qMin(idx, (int)d->rgbMax - 1); ptr[0] = pixelColor(rv[0], idx); ptr[1] = pixelColor(rv[1], idx); ptr[2] = pixelColor(rv[2], idx); ptr += 4; progress = (int)(((double)j * 100.0) / size); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } } unsigned short WBFilter::pixelColor(int colorMult, int index) { int r = (colorMult > (int)d->rgbMax) ? d->rgbMax : colorMult; int c = (index - m_settings.saturation * (index - r)) * d->curve[index]; return ((unsigned short)CLAMP(c, 0, (int)(d->rgbMax - 1))); } FilterAction WBFilter::filterAction() { FilterAction action(FilterIdentifier(), CurrentVersion()); action.setDisplayableName(DisplayableName()); m_settings.writeToFilterAction(action); return action; } void WBFilter::readParameters(const FilterAction& action) { m_settings = WBContainer::fromFilterAction(action); } } // namespace Digikam diff --git a/core/libs/dimg/filters/wb/wbfilter.h b/core/libs/dimg/filters/wb/wbfilter.h index 806158e841..1b084c4939 100644 --- a/core/libs/dimg/filters/wb/wbfilter.h +++ b/core/libs/dimg/filters/wb/wbfilter.h @@ -1,109 +1,110 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-16-01 * Description : white balance color correction. * * Copyright (C) 2007-2020 by Gilles Caulier * Copyright (C) 2008 by Guillaume Castagnino * Copyright (C) 2010 by Martin Klapetek * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_WB_FILTER_H #define DIGIKAM_WB_FILTER_H // Qt includes #include // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" #include "digikam_globals.h" #include "wbcontainer.h" namespace Digikam { class DImg; class DIGIKAM_EXPORT WBFilter : public DImgThreadedFilter { public: explicit WBFilter(QObject* const parent = nullptr); explicit WBFilter(DImg* const orgImage, QObject* const parent=nullptr, const WBContainer& settings=WBContainer()); explicit WBFilter(const WBContainer& settings, DImgThreadedFilter* const master, const DImg& orgImage, const DImg& destImage, int progressBegin = 0, int progressEnd = 100); virtual ~WBFilter(); - void readParameters(const FilterAction& action) override; static void autoExposureAdjustement(const DImg* const img, double& black, double& expo); static void autoWBAdjustementFromColor(const QColor& tc, double& temperature, double& green); static QString FilterIdentifier() { return QLatin1String("digikam:WhiteBalanceFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 2; } static int CurrentVersion() { return 2; } - virtual QString filterIdentifier() const override + void readParameters(const FilterAction& action) override; + + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; + virtual FilterAction filterAction() override; protected: - void filterImage() override; + void filterImage() override; protected: WBContainer m_settings; private: void setLUTv(); void adjustWhiteBalance(uchar* const data, int width, int height, bool sixteenBit); inline unsigned short pixelColor(int colorMult, int index); static void setRGBmult(double& temperature, double& green, double& mr, double& mg, double& mb); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_WB_FILTER_H diff --git a/core/libs/dimg/filters/wb/wbsettings.cpp b/core/libs/dimg/filters/wb/wbsettings.cpp index 0bd35352d0..8e6e753bde 100644 --- a/core/libs/dimg/filters/wb/wbsettings.cpp +++ b/core/libs/dimg/filters/wb/wbsettings.cpp @@ -1,608 +1,609 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-02-26 * Description : White Balance settings view. * * Copyright (C) 2010-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "wbsettings.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "dlayoutbox.h" #include "dexpanderbox.h" #include "dfiledialog.h" #include "dnuminput.h" #include "digikam_debug.h" #include "dcombobox.h" namespace Digikam { class Q_DECL_HIDDEN WBSettings::Private { public: enum TemperaturePreset { None = -1, Candle = 1850, Lamp40W = 2680, Lamp100W = 2800, Lamp200W = 3000, Sunrise = 3200, StudioLamp = 3400, MoonLight = 4100, Neutral = 4750, DaylightD50 = 5000, Flash = 5500, Sun = 5770, XenonLamp = 6420, DaylightD65 = 6500 }; public: explicit Private() : DefaultTemperature(DaylightD65), pickTemperature(nullptr), autoAdjustExposure(nullptr), adjTemperatureLabel(nullptr), temperaturePresetLabel(nullptr), darkLabel(nullptr), blackLabel(nullptr), mainExposureLabel(nullptr), fineExposureLabel(nullptr), gammaLabel(nullptr), saturationLabel(nullptr), greenLabel(nullptr), exposureLabel(nullptr), temperatureLabel(nullptr), temperaturePresetCB(nullptr), temperatureInput(nullptr), darkInput(nullptr), blackInput(nullptr), mainExposureInput(nullptr), fineExposureInput(nullptr), gammaInput(nullptr), saturationInput(nullptr), greenInput(nullptr) - {} + { + } QString addTemperatureDescription(const QString& desc, TemperaturePreset preset) const { int index = temperaturePresetCB->combo()->findData((int)preset); QString itemText = temperaturePresetCB->combo()->itemText(index); QString tempDesc = QString::fromLatin1("

%1: %2 (%3K).

") .arg(itemText) .arg(desc) .arg((int)preset); if (preset == None) { tempDesc.remove(QRegExp(QLatin1String("\\(.*\\)"))); } return tempDesc; } public: static const QString configDarkInputEntry; static const QString configBlackInputEntry; static const QString configMainExposureEntry; static const QString configFineExposureEntry; static const QString configGammaInputEntry; static const QString configSaturationInputEntry; static const QString configGreenInputEntry; static const QString configTemperatureInputEntry; const int DefaultTemperature; QToolButton* pickTemperature; QToolButton* autoAdjustExposure; QLabel* adjTemperatureLabel; QLabel* temperaturePresetLabel; QLabel* darkLabel; QLabel* blackLabel; QLabel* mainExposureLabel; QLabel* fineExposureLabel; QLabel* gammaLabel; QLabel* saturationLabel; QLabel* greenLabel; QLabel* exposureLabel; QLabel* temperatureLabel; DComboBox* temperaturePresetCB; DDoubleNumInput* temperatureInput; DDoubleNumInput* darkInput; DDoubleNumInput* blackInput; DDoubleNumInput* mainExposureInput; DDoubleNumInput* fineExposureInput; DDoubleNumInput* gammaInput; DDoubleNumInput* saturationInput; DDoubleNumInput* greenInput; }; const QString WBSettings::Private::configDarkInputEntry(QLatin1String("Dark")); const QString WBSettings::Private::configBlackInputEntry(QLatin1String("Black")); const QString WBSettings::Private::configMainExposureEntry(QLatin1String("MainExposure")); const QString WBSettings::Private::configFineExposureEntry(QLatin1String("FineExposure")); const QString WBSettings::Private::configGammaInputEntry(QLatin1String("Gamma")); const QString WBSettings::Private::configSaturationInputEntry(QLatin1String("Saturation")); const QString WBSettings::Private::configGreenInputEntry(QLatin1String("Green")); const QString WBSettings::Private::configTemperatureInputEntry(QLatin1String("Temperature")); // -------------------------------------------------------- WBSettings::WBSettings(QWidget* const parent) : QWidget(parent), d(new Private) { - const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); + const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); QGridLayout* const grid = new QGridLayout(parent); d->temperatureLabel = new QLabel(i18n("" "Color Temperature (K): ")); d->temperatureLabel->setOpenExternalLinks(true); - d->adjTemperatureLabel = new QLabel(i18n("Adjustment:")); - d->temperatureInput = new DDoubleNumInput; + d->adjTemperatureLabel = new QLabel(i18n("Adjustment:")); + d->temperatureInput = new DDoubleNumInput; d->temperatureInput->setDecimals(1); d->temperatureInput->setRange(1750.0, 12000.0, 10.0); d->temperatureInput->setDefaultValue((double)d->DefaultTemperature); d->temperatureInput->setWhatsThis(i18n("Set here the white balance color temperature in Kelvin.")); d->temperaturePresetLabel = new QLabel(i18n("Preset:")); d->temperaturePresetCB = new DComboBox; d->temperaturePresetCB->combo()->addItem(i18n("Candle"), QVariant(d->Candle)); d->temperaturePresetCB->combo()->addItem(i18n("40W Lamp"), QVariant(d->Lamp40W)); d->temperaturePresetCB->combo()->addItem(i18n("100W Lamp"), QVariant(d->Lamp100W)); d->temperaturePresetCB->combo()->addItem(i18n("200W Lamp"), QVariant(d->Lamp200W)); d->temperaturePresetCB->combo()->addItem(i18n("Sunrise"), QVariant(d->Sunrise)); d->temperaturePresetCB->combo()->addItem(i18n("Studio Lamp"), QVariant(d->StudioLamp)); d->temperaturePresetCB->combo()->addItem(i18n("Moonlight"), QVariant(d->MoonLight)); d->temperaturePresetCB->combo()->addItem(i18n("Neutral"), QVariant(d->Neutral)); d->temperaturePresetCB->combo()->addItem(i18n("Daylight D50"), QVariant(d->DaylightD50)); d->temperaturePresetCB->combo()->addItem(i18n("Photo Flash"), QVariant(d->Flash)); d->temperaturePresetCB->combo()->addItem(i18n("Sun"), QVariant(d->Sun)); d->temperaturePresetCB->combo()->addItem(i18n("Xenon Lamp"), QVariant(d->XenonLamp)); d->temperaturePresetCB->combo()->addItem(i18n("Daylight D65"), QVariant(d->DaylightD65)); d->temperaturePresetCB->combo()->addItem(i18nc("no temperature preset", "None"), QVariant(d->None)); d->temperaturePresetCB->setDefaultIndex(d->temperaturePresetCB->combo()->findData(QVariant(d->DefaultTemperature))); QString toolTip = QString::fromLatin1("

%1

").arg(i18n("Select the white balance color temperature preset to use.")); toolTip += d->addTemperatureDescription(i18n("candle light"), d->Candle); toolTip += d->addTemperatureDescription(i18n("40 Watt incandescent lamp"), d->Lamp40W); toolTip += d->addTemperatureDescription(i18n("100 Watt incandescent lamp"), d->Lamp100W); toolTip += d->addTemperatureDescription(i18n("200 Watt incandescent lamp"), d->Lamp200W); toolTip += d->addTemperatureDescription(i18n("sunrise or sunset light"), d->Sunrise); toolTip += d->addTemperatureDescription(i18n("tungsten lamp used in photo studio or " "light at 1 hour from dusk/dawn"), d->StudioLamp); toolTip += d->addTemperatureDescription(i18n("moon light"), d->MoonLight); toolTip += d->addTemperatureDescription(i18n("neutral color temperature"), d->Neutral); toolTip += d->addTemperatureDescription(i18n("sunny daylight around noon"), d->DaylightD50); toolTip += d->addTemperatureDescription(i18n("electronic photo flash"), d->Flash); toolTip += d->addTemperatureDescription(i18n("effective sun temperature"), d->Sun); toolTip += d->addTemperatureDescription(i18n("xenon lamp or light arc"), d->XenonLamp); toolTip += d->addTemperatureDescription(i18n("overcast sky light"), d->DaylightD65); toolTip += d->addTemperatureDescription(i18n("no preset value"), d->None); d->temperaturePresetCB->setToolTip(toolTip); d->pickTemperature = new QToolButton; d->pickTemperature->setIcon(QIcon::fromTheme(QLatin1String("color-picker-grey"))); d->pickTemperature->setCheckable(true); d->pickTemperature->setToolTip(i18n("Temperature tone color picker.")); d->pickTemperature->setWhatsThis(i18n("With this button, you can pick the color from the original " "image used to set the white color balance temperature and " "green component.")); DLineWidget* const line = new DLineWidget(Qt::Horizontal); // ------------------------------------------------------------- d->blackLabel = new QLabel(i18n("Black point:")); d->blackInput = new DDoubleNumInput; d->blackInput->setDecimals(2); d->blackInput->setRange(0.0, 0.05, 0.01); d->blackInput->setWhatsThis(i18n("Set here the black level value.")); d->blackInput->setDefaultValue(0.0); d->darkLabel = new QLabel(i18n("Shadows:")); d->darkInput = new DDoubleNumInput; d->darkInput->setDecimals(2); d->darkInput->setRange(0.0, 1.0, 0.01); d->darkInput->setDefaultValue(0.5); d->darkInput->setWhatsThis(i18n("Set here the shadow noise suppression level.")); d->saturationLabel = new QLabel(i18n("Saturation:")); d->saturationInput = new DDoubleNumInput; d->saturationInput->setDecimals(2); d->saturationInput->setRange(0.0, 2.0, 0.01); d->saturationInput->setDefaultValue(1.0); d->saturationInput->setWhatsThis(i18n("Set here the saturation value.")); d->gammaLabel = new QLabel(i18n("Gamma:")); d->gammaInput = new DDoubleNumInput; d->gammaInput->setDecimals(2); d->gammaInput->setRange(0.1, 3.0, 0.01); d->gammaInput->setDefaultValue(1.0); d->gammaInput->setWhatsThis(i18n("Set here the gamma correction value.")); d->greenLabel = new QLabel(i18n("Green:")); d->greenInput = new DDoubleNumInput; d->greenInput->setDecimals(2); d->greenInput->setRange(0.2, 2.5, 0.01); d->greenInput->setDefaultValue(1.0); d->greenInput->setWhatsThis(i18n("Set here the green component to control the magenta color " "cast removal level.")); DLineWidget* const line2 = new DLineWidget(Qt::Horizontal); // ------------------------------------------------------------- d->exposureLabel = new QLabel(i18n("" "Exposure Compensation (E.V): ")); d->exposureLabel->setOpenExternalLinks(true); d->mainExposureLabel = new QLabel(i18nc("main exposure value", "Main:")); d->autoAdjustExposure = new QToolButton; d->autoAdjustExposure->setIcon(QIcon::fromTheme(QLatin1String("system-run"))); d->autoAdjustExposure->setToolTip(i18n("Auto exposure adjustments")); d->autoAdjustExposure->setWhatsThis(i18n("With this button, you can automatically adjust Exposure " "and Black Point values.")); d->mainExposureInput = new DDoubleNumInput; d->mainExposureInput->setDecimals(2); d->mainExposureInput->setRange(-6.0, 8.0, 0.1); d->mainExposureInput->setDefaultValue(0.0); d->mainExposureInput->setWhatsThis(i18n("Set here the main exposure compensation value in E.V.")); d->fineExposureLabel = new QLabel(i18nc("fine exposure adjustment", "Fine:")); d->fineExposureInput = new DDoubleNumInput; d->fineExposureInput->setDecimals(2); d->fineExposureInput->setRange(-0.5, 0.5, 0.01); d->fineExposureInput->setDefaultValue(0.0); d->fineExposureInput->setWhatsThis(i18n("This value in E.V will be added to main exposure " "compensation value to set fine exposure adjustment.")); // ------------------------------------------------------------- grid->addWidget(d->temperatureLabel, 0, 0, 1, 6); grid->addWidget(d->adjTemperatureLabel, 1, 0, 1, 1); grid->addWidget(d->pickTemperature, 1, 1, 1, 1); grid->addWidget(d->temperatureInput, 1, 2, 1, 4); grid->addWidget(d->temperaturePresetLabel, 2, 0, 1, 1); grid->addWidget(d->temperaturePresetCB, 2, 2, 1, 4); grid->addWidget(line, 3, 0, 1, 6); grid->addWidget(d->blackLabel, 4, 0, 1, 1); grid->addWidget(d->blackInput, 4, 1, 1, 5); grid->addWidget(d->darkLabel, 5, 0, 1, 1); grid->addWidget(d->darkInput, 5, 1, 1, 5); grid->addWidget(d->saturationLabel, 6, 0, 1, 1); grid->addWidget(d->saturationInput, 6, 1, 1, 5); grid->addWidget(d->gammaLabel, 7, 0, 1, 1); grid->addWidget(d->gammaInput, 7, 1, 1, 5); grid->addWidget(d->greenLabel, 8, 0, 1, 1); grid->addWidget(d->greenInput, 8, 1, 1, 5); grid->addWidget(line2, 9, 0, 1, 6); grid->addWidget(d->exposureLabel, 10, 0, 1, 6); grid->addWidget(d->mainExposureLabel, 11, 0, 1, 1); grid->addWidget(d->autoAdjustExposure, 11, 1, 1, 1); grid->addWidget(d->mainExposureInput, 11, 2, 1, 4); grid->addWidget(d->fineExposureLabel, 12, 0, 1, 2); grid->addWidget(d->fineExposureInput, 12, 2, 1, 4); grid->setRowStretch(13, 10); grid->setContentsMargins(spacing, spacing, spacing, spacing); grid->setSpacing(spacing); // ------------------------------------------------------------- connect(d->temperaturePresetCB, SIGNAL(activated(int)), this, SLOT(slotTemperaturePresetChanged(int))); connect(d->temperatureInput, SIGNAL(valueChanged(double)), this, SLOT(slotTemperatureChanged(double))); connect(d->darkInput, SIGNAL(valueChanged(double)), this, SIGNAL(signalSettingsChanged())); connect(d->blackInput, SIGNAL(valueChanged(double)), this, SIGNAL(signalSettingsChanged())); connect(d->mainExposureInput, SIGNAL(valueChanged(double)), this, SIGNAL(signalSettingsChanged())); connect(d->fineExposureInput, SIGNAL(valueChanged(double)), this, SIGNAL(signalSettingsChanged())); connect(d->gammaInput, SIGNAL(valueChanged(double)), this, SIGNAL(signalSettingsChanged())); connect(d->saturationInput, SIGNAL(valueChanged(double)), this, SIGNAL(signalSettingsChanged())); connect(d->greenInput, SIGNAL(valueChanged(double)), this, SIGNAL(signalSettingsChanged())); connect(d->autoAdjustExposure, SIGNAL(clicked()), this, SIGNAL(signalAutoAdjustExposure())); connect(d->pickTemperature, SIGNAL(released()), this, SIGNAL(signalPickerColorButtonActived())); } WBSettings::~WBSettings() { delete d; } bool WBSettings::pickTemperatureIsOn() { return d->pickTemperature->isChecked(); } void WBSettings::setOnPickTemperature(bool b) { d->pickTemperature->setChecked(b); } void WBSettings::showAdvancedButtons(bool b) { d->pickTemperature->setVisible(b); d->autoAdjustExposure->setVisible(b); } void WBSettings::slotTemperatureChanged(double temperature) { int index = d->temperaturePresetCB->combo()->findData(QVariant((int)temperature)); if (index == -1) { index = d->temperaturePresetCB->combo()->findData(QVariant((int)d->None)); } d->temperaturePresetCB->setCurrentIndex(index); emit signalSettingsChanged(); } void WBSettings::slotTemperaturePresetChanged(int tempPreset) { bool ok = true; int temperature = d->temperaturePresetCB->combo()->itemData(tempPreset).toInt(&ok); if (!ok) { temperature = d->DefaultTemperature; } if (temperature != -1) { d->temperatureInput->setValue((double)temperature); } emit signalSettingsChanged(); } WBContainer WBSettings::settings() const { WBContainer prm; prm.black = d->blackInput->value(); prm.expositionMain = d->mainExposureInput->value(); prm.expositionFine = d->fineExposureInput->value(); prm.temperature = d->temperatureInput->value(); prm.green = d->greenInput->value(); prm.dark = d->darkInput->value(); prm.gamma = d->gammaInput->value(); prm.saturation = d->saturationInput->value(); return prm; } void WBSettings::setSettings(const WBContainer& settings) { blockSignals(true); d->blackInput->setValue(settings.black); d->mainExposureInput->setValue(settings.expositionMain); d->fineExposureInput->setValue(settings.expositionFine); d->temperatureInput->setValue(settings.temperature); d->greenInput->setValue(settings.green); d->darkInput->setValue(settings.dark); d->gammaInput->setValue(settings.gamma); d->saturationInput->setValue(settings.saturation); slotTemperatureChanged(d->temperatureInput->value()); blockSignals(false); } void WBSettings::resetToDefault() { blockSignals(true); // Neutral color temperature settings is D65. d->blackInput->slotReset(); d->darkInput->slotReset(); d->gammaInput->slotReset(); d->greenInput->slotReset(); d->mainExposureInput->slotReset(); d->fineExposureInput->slotReset(); d->saturationInput->slotReset(); d->temperatureInput->slotReset(); d->temperaturePresetCB->slotReset(); slotTemperaturePresetChanged(d->temperaturePresetCB->defaultIndex()); blockSignals(false); } WBContainer WBSettings::defaultSettings() const { WBContainer prm; prm.black = d->blackInput->defaultValue(); prm.expositionMain = d->mainExposureInput->defaultValue(); prm.expositionFine = d->fineExposureInput->defaultValue(); prm.temperature = d->temperatureInput->defaultValue(); prm.green = d->greenInput->defaultValue(); prm.dark = d->darkInput->defaultValue(); prm.gamma = d->gammaInput->defaultValue(); prm.saturation = d->saturationInput->defaultValue(); return prm; } void WBSettings::readSettings(KConfigGroup& group) { WBContainer prm; prm.black = group.readEntry(d->configBlackInputEntry, d->blackInput->defaultValue()); prm.temperature = group.readEntry(d->configTemperatureInputEntry, d->temperatureInput->defaultValue()); prm.green = group.readEntry(d->configGreenInputEntry, d->greenInput->defaultValue()); prm.dark = group.readEntry(d->configDarkInputEntry, d->darkInput->defaultValue()); prm.gamma = group.readEntry(d->configGammaInputEntry, d->gammaInput->defaultValue()); prm.saturation = group.readEntry(d->configSaturationInputEntry, d->saturationInput->defaultValue()); prm.expositionMain = group.readEntry(d->configMainExposureEntry, d->mainExposureInput->defaultValue()); prm.expositionFine = group.readEntry(d->configFineExposureEntry, d->fineExposureInput->defaultValue()); setSettings(prm); } void WBSettings::writeSettings(KConfigGroup& group) { group.writeEntry(d->configDarkInputEntry, d->darkInput->value()); group.writeEntry(d->configBlackInputEntry, d->blackInput->value()); group.writeEntry(d->configMainExposureEntry, d->mainExposureInput->value()); group.writeEntry(d->configFineExposureEntry, d->fineExposureInput->value()); group.writeEntry(d->configGammaInputEntry, d->gammaInput->value()); group.writeEntry(d->configSaturationInputEntry, d->saturationInput->value()); group.writeEntry(d->configGreenInputEntry, d->greenInput->value()); group.writeEntry(d->configTemperatureInputEntry, d->temperatureInput->value()); } void WBSettings::loadSettings() { QUrl loadWhiteBalanceFile = DFileDialog::getOpenFileUrl(qApp->activeWindow(), i18n("White Color Balance Settings File to Load"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)), QLatin1String("*")); if (loadWhiteBalanceFile.isEmpty()) { return; } QFile file(loadWhiteBalanceFile.toLocalFile()); if (file.open(QIODevice::ReadOnly)) { QTextStream stream(&file); if (stream.readLine() != QLatin1String("# White Color Balance Configuration File V2")) { QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(), i18n("\"%1\" is not a White Color Balance settings text file.", loadWhiteBalanceFile.fileName())); file.close(); return; } blockSignals(true); d->temperatureInput->setValue(stream.readLine().toDouble()); d->darkInput->setValue(stream.readLine().toDouble()); d->blackInput->setValue(stream.readLine().toDouble()); d->mainExposureInput->setValue(stream.readLine().toDouble()); d->fineExposureInput->setValue(stream.readLine().toDouble()); d->gammaInput->setValue(stream.readLine().toDouble()); d->saturationInput->setValue(stream.readLine().toDouble()); d->greenInput->setValue(stream.readLine().toDouble()); slotTemperatureChanged(d->temperatureInput->value()); blockSignals(false); } else { QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(), i18n("Cannot load settings from the White Color Balance text file.")); } file.close(); } void WBSettings::saveAsSettings() { QUrl saveWhiteBalanceFile = DFileDialog::getSaveFileUrl(qApp->activeWindow(), i18n("White Color Balance Settings File to Save"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)), QLatin1String("*")); if (saveWhiteBalanceFile.isEmpty()) { return; } QFile file(saveWhiteBalanceFile.toLocalFile()); if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); stream << QLatin1String("# White Color Balance Configuration File V2\n"); stream << d->temperatureInput->value() << QLatin1Char('\n'); stream << d->darkInput->value() << QLatin1Char('\n'); stream << d->blackInput->value() << QLatin1Char('\n'); stream << d->mainExposureInput->value() << QLatin1Char('\n'); stream << d->fineExposureInput->value() << QLatin1Char('\n'); stream << d->gammaInput->value() << QLatin1Char('\n'); stream << d->saturationInput->value() << QLatin1Char('\n'); stream << d->greenInput->value() << QLatin1Char('\n'); } else { QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(), i18n("Cannot save settings to the White Color Balance text file.")); } file.close(); } } // namespace Digikam diff --git a/core/libs/dimg/filters/wb/wbsettings.h b/core/libs/dimg/filters/wb/wbsettings.h index b18aca65bf..5a37f76dae 100644 --- a/core/libs/dimg/filters/wb/wbsettings.h +++ b/core/libs/dimg/filters/wb/wbsettings.h @@ -1,86 +1,86 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-02-26 * Description : White Balance settings view. * * Copyright (C) 2010-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_WB_SETTINGS_H #define DIGIKAM_WB_SETTINGS_H // Local includes #include // Local includes #include "digikam_export.h" #include "wbfilter.h" class KConfigGroup; namespace Digikam { class DIGIKAM_EXPORT WBSettings : public QWidget { Q_OBJECT public: explicit WBSettings(QWidget* const parent); ~WBSettings(); WBContainer defaultSettings() const; void resetToDefault(); - WBContainer settings() const; + WBContainer settings() const; void setSettings(const WBContainer& settings); void readSettings(KConfigGroup& group); void writeSettings(KConfigGroup& group); void loadSettings(); void saveAsSettings(); bool pickTemperatureIsOn(); void setOnPickTemperature(bool b); void showAdvancedButtons(bool b); Q_SIGNALS: void signalSettingsChanged(); void signalPickerColorButtonActived(); void signalAutoAdjustExposure(); private Q_SLOTS: void slotTemperatureChanged(double temperature); void slotTemperaturePresetChanged(int tempPreset); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_WB_SETTINGS_H diff --git a/core/libs/widgets/colors/colorgradientwidget.cpp b/core/libs/widgets/colors/colorgradientwidget.cpp index f872b15afc..32fcbf70e8 100644 --- a/core/libs/widgets/colors/colorgradientwidget.cpp +++ b/core/libs/widgets/colors/colorgradientwidget.cpp @@ -1,169 +1,170 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2004-07-28 * Description : a color gradient widget * * Copyright (C) 2004-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "colorgradientwidget.h" // Qt includes #include #include namespace Digikam { class Q_DECL_HIDDEN ColorGradientWidget::Private { public: explicit Private() + : orientation(Qt::Horizontal) { - orientation = Qt::Horizontal; } Qt::Orientation orientation; QColor color1; QColor color2; }; ColorGradientWidget::ColorGradientWidget(Qt::Orientation orientation, int size, QWidget* const parent) : QWidget(parent), d(new Private) { d->orientation = orientation; d->color1.setRgb(0, 0, 0); d->color2.setRgb(255, 255, 255); setAttribute(Qt::WA_DeleteOnClose); if (d->orientation == Qt::Horizontal) { setFixedHeight(size); } else { setFixedWidth(size); } setContentsMargins(1, 1, 1, 1); } ColorGradientWidget::~ColorGradientWidget() { delete d; } void ColorGradientWidget::setColors(const QColor& col1, const QColor& col2) { d->color1 = col1; d->color2 = col2; update(); } void ColorGradientWidget::paintEvent(QPaintEvent*) { QImage image(contentsRect().width(), contentsRect().height(), QImage::Format_ARGB32); QColor col, color1, color2, colorf; float scale; if (!isEnabled()) { // Widget is disable : drawing grayed frame. + color1 = palette().color(QPalette::Disabled, QPalette::WindowText); color2 = palette().color(QPalette::Disabled, QPalette::Window); colorf = palette().color(QPalette::Disabled, QPalette::WindowText); } else { color1 = d->color1; color2 = d->color2; colorf = palette().color(QPalette::Active, QPalette::WindowText); } int redDiff = color2.red() - color1.red(); int greenDiff = color2.green() - color1.green(); int blueDiff = color2.blue() - color1.blue(); if (d->orientation == Qt::Vertical) { for (int y = 0 ; y < image.height() ; ++y) { scale = 1.0 * y / image.height(); col.setRgb(color1.red() + int(redDiff * scale), color1.green() + int(greenDiff * scale), color1.blue() + int(blueDiff * scale)); unsigned int* p = reinterpret_cast(image.scanLine(y)); for (int x = 0 ; x < image.width() ; ++x) { *p++ = col.rgb(); } } } else { unsigned int* p = reinterpret_cast(image.scanLine(0)); for (int x = 0 ; x < image.width() ; ++x) { scale = 1.0 * x / image.width(); col.setRgb(color1.red() + int(redDiff * scale), color1.green() + int(greenDiff * scale), color1.blue() + int(blueDiff * scale)); *p++ = col.rgb(); } for (int y = 1 ; y < image.height() ; ++y) { memcpy(image.scanLine(y), image.scanLine(y - 1), sizeof(unsigned int) * image.width()); } } const int psize = 256; QColor ditherPalette[psize]; for (int s = 0 ; s < psize ; ++s) { ditherPalette[s].setRgb(color1.red() + redDiff * s / psize, color1.green() + greenDiff * s / psize, color1.blue() + blueDiff * s / psize); } QPixmap pm = QPixmap::fromImage(image); QPainter p(this); p.drawPixmap(contentsRect(), pm); p.setPen(colorf); p.drawRect(rect()); p.end(); } } // namespace Digikam diff --git a/core/libs/widgets/colors/dcolorchoosermode.cpp b/core/libs/widgets/colors/dcolorchoosermode.cpp index 38d8c0eaa7..333ed62ae0 100644 --- a/core/libs/widgets/colors/dcolorchoosermode.cpp +++ b/core/libs/widgets/colors/dcolorchoosermode.cpp @@ -1,94 +1,99 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-02-20 * Description : color chooser widgets * * Copyright (C) 2010 by Christoph Feck * Copyright (C) 2015-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "dcolorchoosermode_p.h" // Qt includes #include namespace Digikam { qreal getComponentValue(const QColor& color, DColorChooserMode chooserMode) { switch (chooserMode) { case ChooserRed: return color.redF(); + case ChooserGreen: return color.greenF(); + case ChooserBlue: return color.blueF(); + case ChooserHue: return color.hueF(); + case ChooserSaturation: return color.saturationF(); + default: return color.valueF(); } } void setComponentValue(QColor& color, DColorChooserMode chooserMode, qreal value) { if (chooserMode >= ChooserRed) { - if (chooserMode == ChooserRed) + if (chooserMode == ChooserRed) { color.setRedF(value); } else if (chooserMode == ChooserGreen) { color.setGreenF(value); } else { // chooserMode == ChooserBlue color.setBlueF(value); } } else { qreal h=0, s=0, v=0, a=0; color.getHsvF(&h, &s, &v, &a); - if (chooserMode == ChooserHue) + if (chooserMode == ChooserHue) { h = value; } else if (chooserMode == ChooserSaturation) { s = value; } else { // chooserMode == ChooserValue v = value; } color.setHsvF(h, s, v, a); } } } // namespace Digikam diff --git a/core/libs/widgets/colors/dcolorchoosermode_p.h b/core/libs/widgets/colors/dcolorchoosermode_p.h index c5584a5885..875d2231ca 100644 --- a/core/libs/widgets/colors/dcolorchoosermode_p.h +++ b/core/libs/widgets/colors/dcolorchoosermode_p.h @@ -1,118 +1,122 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-02-20 * Description : color chooser widgets * * Copyright (C) 2010 by Christoph Feck * Copyright (C) 2015-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_DCOLOR_CHOOSER_MODE_PRIVATE_H #define DIGIKAM_DCOLOR_CHOOSER_MODE_PRIVATE_H #include "dcolorchoosermode.h" // Qt includes #include class QColor; namespace Digikam { -/** get/set color component +/** + * get/set color component */ extern qreal getComponentValue(const QColor& color, DColorChooserMode chooserMode); extern void setComponentValue(QColor& color, DColorChooserMode chooserMode, qreal value); -/** number of linear gradient ranges needed for color component +/** + * number of linear gradient ranges needed for color component */ static inline int componentValueSteps(DColorChooserMode chooserMode) { if (chooserMode == ChooserHue) { return 6; } else { return 1; } } -/** color component that is used for X in the XY selector +/** + * color component that is used for X in the XY selector */ static inline DColorChooserMode chooserXMode(DColorChooserMode chooserMode) { if (chooserMode >= ChooserRed) { return (chooserMode == ChooserRed ? ChooserGreen : ChooserRed); } else { return (chooserMode == ChooserHue ? ChooserSaturation : ChooserHue); } } -/** color component that is used for Y in the XY selector +/** + * color component that is used for Y in the XY selector */ static inline DColorChooserMode chooserYMode(DColorChooserMode chooserMode) { if (chooserMode >= ChooserRed) { return (chooserMode == ChooserBlue ? ChooserGreen : ChooserBlue); } else { return (chooserMode == ChooserValue ? ChooserSaturation : ChooserValue); } } static inline int componentXSteps(DColorChooserMode chooserMode) { return componentValueSteps(chooserXMode(chooserMode)); } static inline int componentYSteps(DColorChooserMode chooserMode) { return componentValueSteps(chooserYMode(chooserMode)); } static inline qreal getComponentX(const QColor& color, DColorChooserMode chooserMode) { return getComponentValue(color, chooserXMode(chooserMode)); } static inline qreal getComponentY(const QColor& color, DColorChooserMode chooserMode) { return getComponentValue(color, chooserYMode(chooserMode)); } static inline void setComponentX(QColor& color, DColorChooserMode chooserMode, qreal x) { setComponentValue(color, chooserXMode(chooserMode), x); } static inline void setComponentY(QColor& color, DColorChooserMode chooserMode, qreal y) { setComponentValue(color, chooserYMode(chooserMode), y); } } // namespace Digikam #endif // DIGIKAM_DCOLOR_CHOOSER_MODE_PRIVATE_H diff --git a/core/libs/widgets/colors/dcolorselector.h b/core/libs/widgets/colors/dcolorselector.h index 27d90def38..7f8e028e69 100644 --- a/core/libs/widgets/colors/dcolorselector.h +++ b/core/libs/widgets/colors/dcolorselector.h @@ -1,75 +1,76 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-02-20 * Description : color selector widget * * Copyright (C) 2010-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_DCOLOR_SELECTOR_H #define DIGIKAM_DCOLOR_SELECTOR_H // Qt includes #include #include // Local includes #include "digikam_export.h" namespace Digikam { -/** A widget to chosse a color from a palette. +/** + * A widget to chosse a color from a palette. */ class DIGIKAM_EXPORT DColorSelector : public QPushButton { Q_OBJECT public: explicit DColorSelector(QWidget* const parent=nullptr); virtual ~DColorSelector(); void setColor(const QColor& color); QColor color() const; void setAlphaChannelEnabled(bool); Q_SIGNALS: void signalColorSelected(const QColor&); private Q_SLOTS: void slotBtnClicked(); private: void paintEvent(QPaintEvent*) override; private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_DCOLOR_SELECTOR_H diff --git a/core/libs/widgets/colors/dcolorvalueselector.cpp b/core/libs/widgets/colors/dcolorvalueselector.cpp index eebbbbfce9..014e673a2c 100644 --- a/core/libs/widgets/colors/dcolorvalueselector.cpp +++ b/core/libs/widgets/colors/dcolorvalueselector.cpp @@ -1,488 +1,496 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 1997-02-20 * Description : color chooser widgets * * Copyright (C) 1997 by Martin Jones * Copyright (C) 2015-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "dcolorvalueselector.h" // Qt includes #include #include #include #include #include #include // Local includes #include "dcolorchoosermode_p.h" namespace Digikam { class Q_DECL_HIDDEN DSelector::Private { public: explicit Private() - : arrowsize(5) + : arrowsize(5), + m_indent(true), + arrowPE(QStyle::PE_IndicatorArrowLeft) { - arrowPE = QStyle::PE_IndicatorArrowLeft; - m_indent = true; } const int arrowsize; bool m_indent; QStyle::PrimitiveElement arrowPE; }; DSelector::DSelector(QWidget* const parent) : QAbstractSlider(parent), d(new Private) { setOrientation(Qt::Horizontal); } DSelector::DSelector(Qt::Orientation o, QWidget* const parent) : QAbstractSlider(parent), d(new Private) { setOrientation(o); if (o == Qt::Horizontal) + { setArrowDirection(Qt::UpArrow); + } } DSelector::~DSelector() { delete d; } void DSelector::setIndent(bool i) { d->m_indent = i; } bool DSelector::indent() const { return d->m_indent; } QRect DSelector::contentsRect() const { int w = indent() ? style()->pixelMetric(QStyle::PM_DefaultFrameWidth) : 0; int iw = (w < d->arrowsize) ? d->arrowsize : w; if (orientation() == Qt::Vertical) { if (arrowDirection() == Qt::RightArrow) { return QRect(w + d->arrowsize, iw, width() - w*2 - d->arrowsize, height() - iw*2); } else { return QRect(w, iw, width() - w*2 - d->arrowsize, height() - iw*2); } } else { // Qt::Horizontal if (arrowDirection() == Qt::UpArrow) { return QRect(iw, w, width() - 2*iw, height() - w*2 - d->arrowsize); } else { return QRect(iw, w + d->arrowsize, width() - 2*iw, height() - w*2 - d->arrowsize); } } } void DSelector::paintEvent(QPaintEvent*) { QPainter painter; int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); int iw = (w < d->arrowsize) ? d->arrowsize : w; painter.begin(this); drawContents(&painter); QBrush brush; QPoint pos = calcArrowPos(value()); drawArrow(&painter, pos); if (indent()) { QStyleOptionFrame opt; opt.initFrom(this); opt.state = QStyle::State_Sunken; if (orientation() == Qt::Vertical) + { opt.rect.adjust(0, iw - w, -5, w - iw); + } else + { opt.rect.adjust(iw - w, 0, w - iw, -5); + } QBrush oldBrush = painter.brush(); painter.setBrush(Qt::NoBrush); style()->drawPrimitive(QStyle::PE_Frame, &opt, &painter, this); painter.setBrush(oldBrush); } - painter.end(); } void DSelector::mousePressEvent(QMouseEvent* e) { setSliderDown(true); moveArrow(e->pos()); } void DSelector::mouseMoveEvent(QMouseEvent* e) { moveArrow(e->pos()); } void DSelector::mouseReleaseEvent(QMouseEvent* e) { moveArrow(e->pos()); setSliderDown(false); } void DSelector::wheelEvent(QWheelEvent* e) { int val = value() + e->delta()/120; setSliderDown(true); setValue(val); setSliderDown(false); } void DSelector::moveArrow(const QPoint& pos) { int val; int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); int iw = (w < d->arrowsize) ? d->arrowsize : w; if (orientation() == Qt::Vertical) { - val = (maximum() - minimum()) * (height() - pos.y() - iw) - / (height() - iw * 2) + minimum(); + val = (maximum() - minimum()) * (height() - pos.y() - iw) / + (height() - iw * 2) + minimum(); } else { - val = (maximum() - minimum()) * ( pos.x() - iw) - / (width() - iw * 2) + minimum(); + val = (maximum() - minimum()) * ( pos.x() - iw) / + (width() - iw * 2) + minimum(); } setValue(val); update(); } QPoint DSelector::calcArrowPos(int val) { QPoint p; int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); int iw = (w < d->arrowsize) ? d->arrowsize : w; if (orientation() == Qt::Vertical) { p.setY(height() - iw - 1 - (height() - 2 * iw - 1) * val / (maximum() - minimum())); if (d->arrowPE == QStyle::PE_IndicatorArrowRight) { p.setX(0); } else { p.setX(width() - 5); } } else { p.setX(iw + (width() - 2 * iw - 1) * val / (maximum() - minimum())); if (d->arrowPE == QStyle::PE_IndicatorArrowDown) { p.setY(0); } else { p.setY(height() - 5); } } return p; } void DSelector::setArrowDirection(Qt::ArrowType direction) { switch (direction) { case Qt::UpArrow: if (orientation() == Qt::Horizontal) { d->arrowPE = QStyle::PE_IndicatorArrowUp; } else { d->arrowPE = QStyle::PE_IndicatorArrowLeft; } break; case Qt::DownArrow: if (orientation() == Qt::Horizontal) { d->arrowPE = QStyle::PE_IndicatorArrowDown; } else { d->arrowPE = QStyle::PE_IndicatorArrowRight; } break; case Qt::LeftArrow: if (orientation() == Qt::Vertical) { d->arrowPE = QStyle::PE_IndicatorArrowLeft; } else { d->arrowPE = QStyle::PE_IndicatorArrowDown; } break; case Qt::RightArrow: if (orientation() == Qt::Vertical) { d->arrowPE = QStyle::PE_IndicatorArrowRight; } else { d->arrowPE = QStyle::PE_IndicatorArrowUp; } break; case Qt::NoArrow: break; } } Qt::ArrowType DSelector::arrowDirection() const { switch (d->arrowPE) { case QStyle::PE_IndicatorArrowUp: return Qt::UpArrow; break; + case QStyle::PE_IndicatorArrowDown: return Qt::DownArrow; break; + case QStyle::PE_IndicatorArrowRight: return Qt::RightArrow; break; + case QStyle::PE_IndicatorArrowLeft: default: return Qt::LeftArrow; break; } } void DSelector::drawArrow(QPainter* painter, const QPoint& pos) { painter->setPen(QPen()); painter->setBrush(QBrush(palette().color(QPalette::ButtonText))); QStyleOption o; if (orientation() == Qt::Vertical) { o.rect = QRect(pos.x(), pos.y() - d->arrowsize / 2, d->arrowsize, d->arrowsize); } else { o.rect = QRect(pos.x() - d->arrowsize / 2, pos.y(), d->arrowsize, d->arrowsize); } style()->drawPrimitive(d->arrowPE, &o, painter, this); } // ------------------------------------------------------------------------------------- class Q_DECL_HIDDEN DColorValueSelector::Private { public: explicit Private(DColorValueSelector* const q) : q(q), hue(0), saturation(0), color(0), mode(ChooserClassic) { } DColorValueSelector* q; int hue; int saturation; int color; DColorChooserMode mode; QPixmap pixmap; }; DColorValueSelector::DColorValueSelector(QWidget* const parent) : DSelector(Qt::Vertical, parent), d(new Private(this)) { setRange(0, 255); } DColorValueSelector::DColorValueSelector(Qt::Orientation o, QWidget* const parent) : DSelector(o, parent), d(new Private(this)) { setRange(0, 255); } DColorValueSelector::~DColorValueSelector() { delete d; } int DColorValueSelector::hue() const { return d->hue; } void DColorValueSelector::setHue(int hue) { d->hue = hue; } int DColorValueSelector::saturation() const { return d->saturation; } void DColorValueSelector::setSaturation(int saturation) { d->saturation = saturation; } int DColorValueSelector::colorValue() const { return d->color; } void DColorValueSelector::setColorValue(int colorValue) { d->color = colorValue; } void DColorValueSelector::updateContents() { drawPalette(&d->pixmap); } void DColorValueSelector::resizeEvent(QResizeEvent*) { updateContents(); } void DColorValueSelector::drawContents(QPainter* painter) { painter->drawPixmap(contentsRect().x(), contentsRect().y(), d->pixmap); } void DColorValueSelector::setChooserMode(DColorChooserMode c) { if (c == ChooserHue) { setRange(0, 360); } else { setRange(0, 255); } d->mode = c; } DColorChooserMode DColorValueSelector::chooserMode() const { return d->mode; } void DColorValueSelector::drawPalette(QPixmap* pixmap) { QColor color; if (chooserMode() == ChooserHue) { color.setHsv(hue(), 255, 255); } else { color.setHsv(hue(), saturation(), colorValue()); } QLinearGradient gradient; if (orientation() == Qt::Vertical) { gradient.setStart(0, contentsRect().height()); gradient.setFinalStop(0, 0); } else { gradient.setStart(0, 0); gradient.setFinalStop(contentsRect().width(), 0); } const int steps = componentValueSteps(chooserMode()); - for (int v = 0; v <= steps; ++v) + for (int v = 0 ; v <= steps ; ++v) { setComponentValue(color, chooserMode(), v * (1.0 / steps)); gradient.setColorAt(v * (1.0 / steps), color); } *pixmap = QPixmap(contentsRect().size()); QPainter painter(pixmap); painter.fillRect(pixmap->rect(), gradient); } } // namespace Digikam diff --git a/core/libs/widgets/colors/dcolorvalueselector.h b/core/libs/widgets/colors/dcolorvalueselector.h index 08104f6bc3..a7f4012dc7 100644 --- a/core/libs/widgets/colors/dcolorvalueselector.h +++ b/core/libs/widgets/colors/dcolorvalueselector.h @@ -1,234 +1,234 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 1997-02-20 * Description : color chooser widgets * * Copyright (C) 1997 by Martin Jones * Copyright (C) 2015-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_DCOLOR_VALUE_SELECTOR_H #define DIGIKAM_DCOLOR_VALUE_SELECTOR_H // Qt includes #include #include // Local includes #include "dcolorchoosermode.h" #include "digikam_export.h" namespace Digikam { /** * DSelector is the base class for other widgets which * provides the ability to choose from a one-dimensional * range of values. An example is the KGradientSelector * which allows to choose from a range of colors. * * A custom drawing routine for the widget surface has * to be provided by the subclass. */ class DIGIKAM_EXPORT DSelector : public QAbstractSlider { Q_OBJECT Q_PROPERTY(int value READ value WRITE setValue) Q_PROPERTY(int minValue READ minimum WRITE setMinimum) Q_PROPERTY(int maxValue READ maximum WRITE setMaximum) Q_PROPERTY(bool indent READ indent WRITE setIndent) Q_PROPERTY(Qt::ArrowType arrowDirection READ arrowDirection WRITE setArrowDirection) public: explicit DSelector(QWidget *parent=nullptr); explicit DSelector(Qt::Orientation o, QWidget* const parent=nullptr); ~DSelector(); /** - * @return the rectangle on which subclasses should draw. - */ - QRect contentsRect() const; + * @return the rectangle on which subclasses should draw. + */ + QRect contentsRect() const; /** - * Sets the indent option of the widget to i. - * This determines whether a shaded frame is drawn. - */ + * Sets the indent option of the widget to i. + * This determines whether a shaded frame is drawn. + */ void setIndent(bool i); /** - * @return whether the indent option is set. - */ - bool indent() const; + * @return whether the indent option is set. + */ + bool indent() const; /** - * Sets the arrow direction. - */ + * Sets the arrow direction. + */ void setArrowDirection(Qt::ArrowType direction); /** - * @return the current arrow direction - */ - Qt::ArrowType arrowDirection() const; + * @return the current arrow direction + */ + Qt::ArrowType arrowDirection() const; protected: /** - * Override this function to draw the contents of the control. - * The default implementation does nothing. - * - * Draw only within contentsRect(). - */ + * Override this function to draw the contents of the control. + * The default implementation does nothing. + * + * Draw only within contentsRect(). + */ virtual void drawContents(QPainter*) {}; /** - * Override this function to draw the cursor which - * indicates the current value. - */ + * Override this function to draw the cursor which + * indicates the current value. + */ virtual void drawArrow(QPainter* painter, const QPoint& pos); - virtual void paintEvent(QPaintEvent*) override; - virtual void mousePressEvent(QMouseEvent* e) override; - virtual void mouseMoveEvent(QMouseEvent* e) override; - virtual void mouseReleaseEvent(QMouseEvent* e) override; - virtual void wheelEvent(QWheelEvent*) override; + virtual void paintEvent(QPaintEvent*) override; + virtual void mousePressEvent(QMouseEvent* e) override; + virtual void mouseMoveEvent(QMouseEvent* e) override; + virtual void mouseReleaseEvent(QMouseEvent* e) override; + virtual void wheelEvent(QWheelEvent*) override; private: QPoint calcArrowPos(int val); void moveArrow(const QPoint& pos); private: class Private; friend class Private; Private* const d; Q_DISABLE_COPY(DSelector) }; // ----------------------------------------------------------------------------------- class DIGIKAM_EXPORT DColorValueSelector : public DSelector { Q_OBJECT Q_PROPERTY( int hue READ hue WRITE setHue) Q_PROPERTY( int saturation READ saturation WRITE setSaturation) Q_PROPERTY( int colorValue READ colorValue WRITE setColorValue) public: explicit DColorValueSelector(QWidget* const parent = nullptr); explicit DColorValueSelector(Qt::Orientation o, QWidget* const parent = nullptr); ~DColorValueSelector(); /** * Updates the widget's contents. */ void updateContents(); /** * Returns the current hue value. * * @return The hue value (0-359) */ - int hue() const; + int hue() const; /** * Sets the hue value. Doesn't automatically update the widget; * you have to call updateContents manually. * * @param hue Sets the hue value (0-359) */ void setHue(int hue); /** * Returns the current saturation value. * * @return The saturation value (0-255) */ - int saturation() const; + int saturation() const; /** * Sets the saturation value. Doesn't automatically update the widget; * you have to call updateContents manually. * * @param saturation Sets the saturation value (0-255) */ void setSaturation(int saturation); /** * Returns the current color value. * * @return The color value (0-255) */ - int colorValue() const; + int colorValue() const; /** * Sets the color value. Doesn't automatically update the widget; * you have to call updateContents manually. * * @param colorValue Sets the color value (0-255) */ void setColorValue(int colorValue); /** * Sets the chooser mode. Doesn't automatically update the widget; * you have to call updateContents manually. * * @param chooserMode Sets the chooser mode (one of the DColorChooserMode constants) */ void setChooserMode(DColorChooserMode chooserMode); /** * Returns the current chooser mode. * * @return The chooser mode (one of the DColorChooserMode constants) */ DColorChooserMode chooserMode() const; protected: /** * Draws the contents of the widget on a pixmap, * which is used for buffering. */ virtual void drawPalette(QPixmap*); virtual void resizeEvent(QResizeEvent*) override; /** * Reimplemented from DSelector. The drawing is * buffered in a pixmap here. As real drawing * routine, drawPalette() is used. */ - virtual void drawContents(QPainter*) override; + virtual void drawContents(QPainter*) override; private: class Private; friend class Private; Private *const d; Q_DISABLE_COPY(DColorValueSelector) }; } // namespace Digikam #endif // DIGIKAM_DCOLOR_VALUE_SELECTOR_H diff --git a/core/libs/widgets/colors/dgradientslider.cpp b/core/libs/widgets/colors/dgradientslider.cpp index 65d2147b88..eab0062f49 100644 --- a/core/libs/widgets/colors/dgradientslider.cpp +++ b/core/libs/widgets/colors/dgradientslider.cpp @@ -1,324 +1,331 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-07-03 * Description : a color gradient slider * * Copyright (C) 2008-2020 by Gilles Caulier * Copyright (C) 2008 by Cyrille Berger * * 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, 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. * * ============================================================ */ #include "dgradientslider.h" // Qt includes #include #include #include #include namespace Digikam { class Q_DECL_HIDDEN DGradientSlider::Private { public: enum Cursor { NoCursor = 0, LeftCursor, RightCursor, MiddleCursor }; public: explicit Private() + : showMiddleCursor(false), + leftCursor(0.0), + middleCursor(0.5), + rightCursor(1.0), + leftColor(Qt::black), + rightColor(Qt::white), + parent(nullptr), + activeCursor(NoCursor) { - activeCursor = NoCursor; - parent = nullptr; - leftCursor = 0.0; - middleCursor = 0.5; - rightCursor = 1.0; - showMiddleCursor = false; - leftColor = Qt::black; - rightColor = Qt::white; - middleColor.setRgb((leftColor.red() + rightColor.red()) / 2, (leftColor.green() + rightColor.green()) / 2, (leftColor.blue() + rightColor.blue()) / 2); } bool showMiddleCursor; double leftCursor; double middleCursor; double rightCursor; QColor leftColor; QColor rightColor; QColor middleColor; DGradientSlider* parent; Cursor activeCursor; public: int gradientHeight() const { return (parent->height() / 3); } int cursorWidth() const { return gradientHeight(); } int gradientWidth() const { return (parent->width() - cursorWidth()); } int gradientOffset() const { return (cursorWidth() / 2); } }; DGradientSlider::DGradientSlider(QWidget* const parent) : QWidget(parent), d(new Private) { d->parent = this; setMinimumWidth(256); setFixedHeight(20); } DGradientSlider::~DGradientSlider() { delete d; } int DGradientSlider::gradientOffset() const { return d->gradientOffset(); } void DGradientSlider::drawCursorAt(QPainter& painter, double pos, const QColor& brushColor, int width, int height, int gradientWidth) { painter.setBrush(brushColor); int pos2 = (int)(gradientWidth * pos); QPoint points[3]; points[0] = QPoint(pos2, 3 * height - 1); points[1] = QPoint(pos2 + width / 2, 2 * height); points[2] = QPoint(pos2 + width, 3 * height - 1); painter.drawPolygon(points, 3); } void DGradientSlider::paintEvent(QPaintEvent*) { int grdHeight = d->gradientHeight(); int curWidth = d->cursorWidth(); int grdWidth = d->gradientWidth(); int grdOffset = d->gradientOffset(); QPainter painter(this); + // Draw first gradient + QLinearGradient lrGradient(QPointF(0, 0), QPointF(grdWidth, 0)); lrGradient.setColorAt(0.0, d->leftColor); lrGradient.setColorAt(1.0, d->rightColor); painter.setPen(Qt::NoPen); painter.setBrush(lrGradient); painter.drawRect(grdOffset, 0, grdWidth, grdHeight); // Draw second gradient + QLinearGradient lrcGradient(QPointF(0, 0), QPointF(grdWidth, 0)); lrcGradient.setColorAt(d->leftCursor, d->leftColor); if (d->showMiddleCursor) { lrcGradient.setColorAt(d->middleCursor, d->middleColor); } lrcGradient.setColorAt(d->rightCursor, d->rightColor); painter.setBrush(lrcGradient); painter.drawRect(grdOffset, grdHeight, grdWidth, grdHeight); // Draw cursors + painter.setPen(palette().color(QPalette::Text)); drawCursorAt(painter, d->leftCursor, d->leftColor, curWidth, grdHeight, grdWidth); if (d->showMiddleCursor) { drawCursorAt(painter, d->middleCursor, d->middleColor, curWidth, grdHeight, grdWidth); } drawCursorAt(painter, d->rightCursor, d->rightColor, curWidth, grdHeight, grdWidth); } inline bool isCursorClicked(const QPoint& pos, double cursorPos, int width, int height, int gradientWidth) { int pos2 = (int)(gradientWidth * cursorPos); return ((pos.y() >= 2 * height) && (pos.y() < 3 * height) && (pos.x() >= pos2) && (pos.x() <= (pos2 + width))); } void DGradientSlider::mousePressEvent(QMouseEvent* e) { if (e->button() == Qt::LeftButton) { int grdHeight = d->gradientHeight(); int curWidth = d->cursorWidth(); int grdWidth = d->gradientWidth(); // Select cursor - if (isCursorClicked(e->pos(), d->leftCursor , curWidth, grdHeight, grdWidth)) + + if (isCursorClicked(e->pos(), d->leftCursor , curWidth, grdHeight, grdWidth)) { d->activeCursor = Private::LeftCursor; } else if (d->showMiddleCursor && isCursorClicked(e->pos(), d->middleCursor , curWidth, grdHeight, grdWidth)) { d->activeCursor = Private::MiddleCursor; } else if (isCursorClicked(e->pos(), d->rightCursor, curWidth, grdHeight, grdWidth)) { d->activeCursor = Private::RightCursor; } } } void DGradientSlider::mouseReleaseEvent(QMouseEvent*) { d->activeCursor = Private::NoCursor; } void DGradientSlider::mouseMoveEvent(QMouseEvent* e) { double v = (e->pos().x() - d->gradientOffset()) / (double) d->gradientWidth(); switch (d->activeCursor) { case Private::LeftCursor: setLeftValue(v); break; + case Private::MiddleCursor: setMiddleValue(v); break; + case Private::RightCursor: setRightValue(v); break; + default: break; } } void DGradientSlider::leaveEvent(QEvent*) { d->activeCursor = Private::NoCursor; } void DGradientSlider::showMiddleCursor(bool b) { d->showMiddleCursor = b; } double DGradientSlider::leftValue() const { return d->leftCursor; } double DGradientSlider::rightValue() const { return d->rightCursor; } double DGradientSlider::middleValue() const { return d->middleCursor; } void DGradientSlider::adjustMiddleValue(double newLeftValue, double newRightValue) { double newDist = newRightValue - newLeftValue; double oldDist = d->rightCursor - d->leftCursor; double oldPos = d->middleCursor - d->leftCursor; d->middleCursor = oldPos * newDist / oldDist + newLeftValue; } void DGradientSlider::setRightValue(double v) { if ((v <= 1.0) && (v > d->leftCursor) && (v != d->rightCursor)) { adjustMiddleValue(d->leftCursor, v); d->rightCursor = v; update(); emit rightValueChanged(v); emit middleValueChanged(d->middleCursor); } } void DGradientSlider::setLeftValue(double v) { - if ((v >= 0.0) && + if ((v >= 0.0) && (v != d->leftCursor) && (v < d->rightCursor)) { adjustMiddleValue(v, d->rightCursor); d->leftCursor = v; update(); emit leftValueChanged(v); emit middleValueChanged(d->middleCursor); } } void DGradientSlider::setMiddleValue(double v) { - if (v > d->leftCursor && v != d->middleCursor && v < d->rightCursor) + if ((v > d->leftCursor) && (v != d->middleCursor) && (v < d->rightCursor)) { d->middleCursor = v; update(); emit middleValueChanged(v); } } void DGradientSlider::setColors(const QColor& lcolor, const QColor& rcolor) { d->leftColor = lcolor; d->rightColor = rcolor; update(); } } // namespace Digikam diff --git a/core/libs/widgets/colors/dhuesaturationselect.cpp b/core/libs/widgets/colors/dhuesaturationselect.cpp index 18e7e34fa7..132a13e0b6 100644 --- a/core/libs/widgets/colors/dhuesaturationselect.cpp +++ b/core/libs/widgets/colors/dhuesaturationselect.cpp @@ -1,408 +1,441 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 1997-02-20 * Description : color chooser widgets * * Copyright (C) 1997 by Martin Jones (mjones at kde dot org) * Copyright (C) 2015-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "dhuesaturationselect.h" // Qt includes #include #include #include #include // Local includes #include "digikam_debug.h" #include "dcolorchoosermode_p.h" namespace Digikam { class Q_DECL_HIDDEN DPointSelect::Private { public: explicit Private(DPointSelect* const q): q(q), px(0), py(0), xPos(0), yPos(0), minX(0), maxX(100), minY(0), maxY(100), m_markerColor(Qt::white) { } void setValues(int _xPos, int _yPos); public: DPointSelect* q; int px; int py; int xPos; int yPos; int minX; int maxX; int minY; int maxY; QColor m_markerColor; }; void DPointSelect::Private::setValues(int _xPos, int _yPos) { int w = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth); xPos = _xPos; yPos = _yPos; - if (xPos > maxX) + if (xPos > maxX) + { xPos = maxX; + } else if (xPos < minX) + { xPos = minX; + } - if (yPos > maxY) + if (yPos > maxY) + { yPos = maxY; + } else if (yPos < minY) + { yPos = minY; + } Q_ASSERT(maxX != minX); int xp = w + (q->width() - 2 * w) * xPos / (maxX - minX); Q_ASSERT(maxY != minY); int yp = q->height() - w - (q->height() - 2 * w) * yPos / (maxY - minY); q->setPosition(xp, yp); } DPointSelect::DPointSelect(QWidget* const parent) : QWidget(parent), d(new Private(this)) { } DPointSelect::~DPointSelect() { delete d; } int DPointSelect::xValue() const { return d->xPos; } int DPointSelect::yValue() const { return d->yPos; } void DPointSelect::setRange(int _minX, int _minY, int _maxX, int _maxY) { if (_maxX == _minX) { qCWarning(DIGIKAM_GENERAL_LOG) << "DPointSelect::setRange invalid range: " << _maxX << " == " << _minX << " (for X) "; return; } if (_maxY == _minY) { qCWarning(DIGIKAM_GENERAL_LOG) << "DPointSelect::setRange invalid range: " << _maxY << " == " << _minY << " (for Y) "; return; } int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); d->px = w; d->py = w; d->minX = _minX; d->minY = _minY; d->maxX = _maxX; d->maxY = _maxY; } void DPointSelect::setXValue(int _xPos) { setValues(_xPos, d->yPos); } void DPointSelect::setYValue(int _yPos) { setValues(d->xPos, _yPos); } void DPointSelect::setValues(int _xPos, int _yPos) { d->setValues(_xPos, _yPos); } void DPointSelect::setMarkerColor(const QColor &col) { d->m_markerColor = col; } QRect DPointSelect::contentsRect() const { int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + return rect().adjusted(w, w, -w, -w); } QSize DPointSelect::minimumSizeHint() const { int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + return QSize(2 * w, 2 * w); } void DPointSelect::paintEvent(QPaintEvent * /* ev */) { QStyleOptionFrame opt; opt.initFrom(this); QPainter painter; painter.begin(this); drawContents(&painter); drawMarker(&painter, d->px, d->py); style()->drawPrimitive(QStyle::PE_Frame, &opt, &painter, this); painter.end(); } void DPointSelect::mousePressEvent(QMouseEvent* e) { mouseMoveEvent(e); } void DPointSelect::mouseMoveEvent(QMouseEvent* e) { int xVal, yVal; int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); valuesFromPosition(e->pos().x() - w, e->pos().y() - w, xVal, yVal); setValues(xVal, yVal); emit valueChanged(d->xPos, d->yPos); } void DPointSelect::wheelEvent(QWheelEvent* e) { if (e->orientation() == Qt::Horizontal) + { setValues(xValue() + e->delta()/120, yValue()); + } else + { setValues(xValue(), yValue() + e->delta()/120); + } emit valueChanged(d->xPos, d->yPos); } void DPointSelect::valuesFromPosition(int x, int y, int& xVal, int& yVal) const { int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); xVal = ((d->maxX - d->minX) * (x - w)) / (width() - 2 * w); yVal = d->maxY - (((d->maxY - d->minY) * (y - w)) / (height() - 2 * w)); - if (xVal > d->maxX) + if (xVal > d->maxX) + { xVal = d->maxX; + } else if (xVal < d->minX) + { xVal = d->minX; + } - if (yVal > d->maxY) + if (yVal > d->maxY) + { yVal = d->maxY; + } else if (yVal < d->minY) + { yVal = d->minY; + } } void DPointSelect::setPosition(int xp, int yp) { int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); - if (xp < w) + if (xp < w) + { xp = w; + } else if (xp > width() - w) + { xp = width() - w; + } - if (yp < w) + if (yp < w) + { yp = w; + } else if (yp > height() - w) + { yp = height() - w; + } d->px = xp; d->py = yp; update(); } void DPointSelect::drawMarker(QPainter* p, int xp, int yp) { QPen pen(d->m_markerColor); p->setPen(pen); p->drawEllipse(xp - 4, yp - 4, 8, 8); } // -------------------------------------------------------------------------------------------------------- class Q_DECL_HIDDEN DHueSaturationSelector::Private { public: explicit Private(DHueSaturationSelector* const q) : q(q), mode(ChooserClassic), hue(0), saturation(0), color(0) { } DHueSaturationSelector* q; QPixmap pixmap; /** * Stores the chooser mode */ DColorChooserMode mode; /** * Stores the values for hue, saturation and luminosity */ int hue; int saturation; int color; }; DHueSaturationSelector::DHueSaturationSelector(QWidget* const parent) : DPointSelect(parent), d(new Private(this)) { setChooserMode(ChooserClassic); } DHueSaturationSelector::~DHueSaturationSelector() { delete d; } DColorChooserMode DHueSaturationSelector::chooserMode() const { return d->mode; } void DHueSaturationSelector::setChooserMode(DColorChooserMode chooserMode) { int x = 0; int y = 255; switch (chooserMode) { case ChooserSaturation: case ChooserValue: x = 359; break; + default: x = 255; break; } setRange(0, 0, x, y); d->mode = chooserMode; } int DHueSaturationSelector::hue() const { return d->hue; } void DHueSaturationSelector::setHue(int hue) { d->hue = hue; } int DHueSaturationSelector::saturation() const { return d->saturation; } void DHueSaturationSelector::setSaturation(int saturation) { d->saturation = saturation; } int DHueSaturationSelector::colorValue() const { return d->color; } void DHueSaturationSelector::setColorValue(int color) { d->color = color; } void DHueSaturationSelector::updateContents() { drawPalette(&d->pixmap); } void DHueSaturationSelector::resizeEvent(QResizeEvent*) { updateContents(); } void DHueSaturationSelector::drawContents(QPainter* painter) { painter->drawPixmap(contentsRect().x(), contentsRect().y(), d->pixmap); } void DHueSaturationSelector::drawPalette(QPixmap* pixmap) { int xSteps = componentXSteps(chooserMode()); int ySteps = componentYSteps(chooserMode()); QColor color; - color.setHsv(hue(), saturation(), chooserMode() == ChooserClassic ? 192 : colorValue()); + color.setHsv(hue(), saturation(), (chooserMode() == ChooserClassic) ? 192 : colorValue()); QImage image(QSize(xSteps + 1, ySteps + 1), QImage::Format_RGB32); - for (int y = 0; y <= ySteps; ++y) + for (int y = 0 ; y <= ySteps ; ++y) { setComponentY(color, chooserMode(), y * (1.0 / ySteps)); - for (int x = 0; x <= xSteps; ++x) + for (int x = 0 ; x <= xSteps ; ++x) { setComponentX(color, chooserMode(), x * (1.0 / xSteps)); image.setPixel(x, ySteps - y, color.rgb()); } } QPixmap pix(contentsRect().size()); QPainter painter(&pix); + // Bilinear filtering + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); QRectF srcRect(0.5, 0.5, xSteps, ySteps); QRectF destRect(QPointF(0, 0), contentsRect().size()); painter.drawImage(destRect, image, srcRect); painter.end(); *pixmap = pix; } } // namespace Digikam diff --git a/core/libs/widgets/colors/dhuesaturationselect.h b/core/libs/widgets/colors/dhuesaturationselect.h index 3c71ea7e52..e4bd7e45b0 100644 --- a/core/libs/widgets/colors/dhuesaturationselect.h +++ b/core/libs/widgets/colors/dhuesaturationselect.h @@ -1,263 +1,263 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 1997-02-20 * Description : color chooser widgets * * Copyright (C) 1997 by Martin Jones (mjones at kde dot org) * Copyright (C) 2015-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_DHUE_SATURATION_SELECT_H #define DIGIKAM_DHUE_SATURATION_SELECT_H // Qt includes #include #include // Local includes #include "digikam_export.h" #include "dcolorchoosermode.h" namespace Digikam { class DIGIKAM_EXPORT DPointSelect : public QWidget { Q_OBJECT Q_PROPERTY(int xValue READ xValue WRITE setXValue) Q_PROPERTY(int yValue READ yValue WRITE setYValue) public: /** * Constructs a two-dimensional selector widget which * has a value range of [0..100] in both directions. */ explicit DPointSelect(QWidget* const parent); ~DPointSelect(); /** * Sets the current values in horizontal and * vertical direction. * @param xPos the horizontal value * @param yPos the vertical value */ void setValues(int xPos, int yPos); /** * Sets the current horizontal value * @param xPos the horizontal value */ void setXValue(int xPos); /** * Sets the current vertical value * @param yPos the vertical value */ void setYValue(int yPos); /** * Sets the range of possible values. */ void setRange(int minX, int minY, int maxX, int maxY); /** * Sets the color used to draw the marker * @param col the color */ void setMarkerColor(const QColor& col); /** * @return the current value in horizontal direction. */ - int xValue() const; + int xValue() const; /** * @return the current value in vertical direction. */ - int yValue() const; + int yValue() const; /** * @return the rectangle on which subclasses should draw. */ - QRect contentsRect() const; + QRect contentsRect() const; /** * Reimplemented to give the widget a minimum size */ virtual QSize minimumSizeHint() const override; Q_SIGNALS: /** * This signal is emitted whenever the user chooses a value, * e.g. by clicking with the mouse on the widget. */ void valueChanged(int x, int y); protected: /** * Override this function to draw the contents of the widget. * The default implementation does nothing. * * Draw within contentsRect() only. */ virtual void drawContents(QPainter*) {}; /** * Override this function to draw the marker which * indicates the currently selected value pair. */ virtual void drawMarker(QPainter* p, int xp, int yp); virtual void paintEvent(QPaintEvent* e) override; virtual void mousePressEvent(QMouseEvent* e) override; virtual void mouseMoveEvent(QMouseEvent* e) override; virtual void wheelEvent(QWheelEvent*) override; /** * Converts a pixel position to its corresponding values. */ void valuesFromPosition(int x, int y, int& xVal, int& yVal) const; private: void setPosition(int xp, int yp); private: DPointSelect(); // Disable default constructor. Q_DISABLE_COPY(DPointSelect) class Private; friend class Private; Private* const d; }; // -------------------------------------------------------------------------------- class DIGIKAM_EXPORT DHueSaturationSelector : public DPointSelect { Q_OBJECT public: /** * Constructs a hue/saturation selection widget. */ explicit DHueSaturationSelector(QWidget* const parent = nullptr); /** * Destructor. */ ~DHueSaturationSelector(); /** * Sets the chooser mode. The allowed modes are defined * in DColorChooserMode. * * @param The chooser mode as defined in DColorChooserMode */ void setChooserMode(DColorChooserMode chooserMode); /** * Returns the chooser mode. * * @return The chooser mode (defined in DColorChooserMode) */ DColorChooserMode chooserMode() const; /** * Returns the hue value * * @return The hue value (0-360) */ - int hue() const; + int hue() const; /** * Sets the hue value (0-360) * * @param hue The hue value (0-360) */ void setHue(int hue); /** * Returns the saturation (0-255) * * @return The saturation (0-255) */ - int saturation() const; + int saturation() const; /** * Sets the saturation (0-255) * * @param saturation The saturation (0-255) */ void setSaturation(int saturation); /** * Returns the color value (also known as luminosity, 0-255) * * @return The color value (0-255) */ - int colorValue() const; + int colorValue() const; /** * Sets the color value (0-255) * * @param colorValue The color value (0-255) */ void setColorValue(int color); /** * Updates the contents */ void updateContents(); protected: /** * Draws the contents of the widget on a pixmap, * which is used for buffering. */ virtual void drawPalette(QPixmap* pixmap); virtual void resizeEvent(QResizeEvent*) override; /** * Reimplemented from DPointSelect. This drawing is * buffered in a pixmap here. As real drawing * routine, drawPalette() is used. */ virtual void drawContents(QPainter* painter) override; private: Q_DISABLE_COPY(DHueSaturationSelector) class Private; friend class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_DHUE_SATURATION_SELECT_H diff --git a/core/libs/widgets/combo/comboboxutilities.cpp b/core/libs/widgets/combo/comboboxutilities.cpp index 5ffaddc74c..6b75c8cf5b 100644 --- a/core/libs/widgets/combo/comboboxutilities.cpp +++ b/core/libs/widgets/combo/comboboxutilities.cpp @@ -1,485 +1,507 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-03-14 * Description : User interface for searches * * Copyright (C) 2008-2012 by Marcel Wiesweg * * 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, 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. * * ============================================================ */ #include "comboboxutilities.h" // Qt includes #include #include #include #include #include #include #include #include #include #include // Local includes #include "digikam_debug.h" namespace Digikam { ProxyLineEdit::ProxyLineEdit(QWidget* const parent) : QLineEdit(parent), m_widget(nullptr) { m_layout = new QVBoxLayout; m_layout->setSpacing(0); m_layout->setContentsMargins(QMargins()); setLayout(m_layout); // unset text edit cursor + unsetCursor(); // unset clear button per default + setClearButtonShown(false); connect(this, SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString))); } void ProxyLineEdit::setWidget(QWidget* widget) { if (m_widget) { delete m_widget; } m_widget = widget; m_widget->setParent(this); m_layout->addWidget(m_widget); } void ProxyLineEdit::setClearButtonShown(bool show) { setClearButtonEnabled(show); int rightMargin = show ? height() : 0; m_layout->setContentsMargins(0, 0, rightMargin, 0); } void ProxyLineEdit::slotTextChanged(const QString& text) { if (text.isEmpty() && isClearButtonEnabled()) { emit signalClearButtonPressed(); } } -// NOTE: see bug #326718: We need to use QLineEdit parent class with these methods -// to have clear button working fine. +/** + * NOTE: see bug #326718: We need to use QLineEdit parent class with these methods + * to have clear button working fine. + */ void ProxyLineEdit::mousePressEvent(QMouseEvent* event) { QLineEdit::mousePressEvent(event); } void ProxyLineEdit::mouseReleaseEvent(QMouseEvent* event) { QLineEdit::mouseReleaseEvent(event); } /** * We just re-implement all relevant QWidget event handlers and call * the QWidget implementation, not the QLineEdit one. */ void ProxyLineEdit::mouseMoveEvent(QMouseEvent* event) { QWidget::mouseMoveEvent(event); } void ProxyLineEdit::mouseDoubleClickEvent(QMouseEvent* event) { QWidget::mouseDoubleClickEvent(event); } void ProxyLineEdit::keyPressEvent(QKeyEvent* event) { QWidget::keyPressEvent(event); } void ProxyLineEdit::focusInEvent(QFocusEvent* event) { QWidget::focusInEvent(event); } void ProxyLineEdit::focusOutEvent(QFocusEvent* event) { QWidget::focusOutEvent(event); } void ProxyLineEdit::paintEvent(QPaintEvent* event) { QWidget::paintEvent(event); } void ProxyLineEdit::dragEnterEvent(QDragEnterEvent* event) { QWidget::dragEnterEvent(event); } void ProxyLineEdit::dragMoveEvent(QDragMoveEvent* event) { QWidget::dragMoveEvent(event); } void ProxyLineEdit::dragLeaveEvent(QDragLeaveEvent* event) { QWidget::dragLeaveEvent(event); } void ProxyLineEdit::dropEvent(QDropEvent* event) { QWidget::dropEvent(event); } void ProxyLineEdit::changeEvent(QEvent* event) { QWidget::changeEvent(event); } void ProxyLineEdit::contextMenuEvent(QContextMenuEvent* event) { QWidget::contextMenuEvent(event); } void ProxyLineEdit::inputMethodEvent(QInputMethodEvent* event) { QWidget::inputMethodEvent(event); } QSize ProxyLineEdit::minimumSizeHint() const { return QWidget::minimumSizeHint(); } QSize ProxyLineEdit::sizeHint() const { return QWidget::sizeHint(); } // ------------------------------------------------------------------------- ProxyClickLineEdit::ProxyClickLineEdit(QWidget* const parent) : ProxyLineEdit(parent) { } void ProxyClickLineEdit::mouseReleaseEvent(QMouseEvent* event) { ProxyLineEdit::mouseReleaseEvent(event); if (event->button() == Qt::LeftButton) { emit leftClicked(); event->accept(); } } // ------------------------------------------------------------------------- ModelIndexBasedComboBox::ModelIndexBasedComboBox(QWidget* const parent) : QComboBox(parent) { } void ModelIndexBasedComboBox::hidePopup() { m_currentIndex = view()->selectionModel()->currentIndex(); QComboBox::hidePopup(); } void ModelIndexBasedComboBox::showPopup() { QComboBox::showPopup(); if (m_currentIndex.isValid()) { view()->selectionModel()->setCurrentIndex(m_currentIndex, QItemSelectionModel::ClearAndSelect); } } QModelIndex ModelIndexBasedComboBox::currentIndex() const { return m_currentIndex; } void ModelIndexBasedComboBox::setCurrentIndex(const QModelIndex& index) { m_currentIndex = index; view()->selectionModel()->setCurrentIndex(m_currentIndex, QItemSelectionModel::ClearAndSelect); } // ------------------------------------------------------------------------- StayPoppedUpComboBox::StayPoppedUpComboBox(QWidget* const parent) : ModelIndexBasedComboBox(parent) { m_view = nullptr; } void StayPoppedUpComboBox::installView(QAbstractItemView* view) { if (m_view) { return; } // Create view + m_view = view; // set on combo box + setView(m_view); // Removing these event filters works just as the eventFilter() solution below, // but is much more dependent on Qt internals and not guaranteed to work in the future. - //m_view->removeEventFilter(m_view->parent()); - //m_view->viewport()->removeEventFilter(m_view->parent()); - +/* + m_view->removeEventFilter(m_view->parent()); + m_view->viewport()->removeEventFilter(m_view->parent()); +*/ // Install event filters, _after_ setView() is called + m_view->installEventFilter(this); m_view->viewport()->installEventFilter(this); } bool StayPoppedUpComboBox::eventFilter(QObject* o, QEvent* e) { // The combo box has installed an event filter on the view. // If it catches a valid mouse button release there, it will hide the popup. // Here we prevent this by eating the event ourselves, // and then dispatching it to its destination. - if (o == m_view || o == m_view->viewport()) + + if ((o == m_view) || (o == m_view->viewport())) { switch (e->type()) { case QEvent::MouseButtonRelease: { QMouseEvent* m = static_cast(e); if (m_view->isVisible() && m_view->rect().contains(m->pos())) { if (o == m_view) { o->event(e); } else + { // Viewport: Calling event() does not work, viewportEvent() is needed. // This is the event that gets redirected to the QTreeView finally! - { + sendViewportEventToView(e); } // we have dispatched the event privately; we filter it out from the main dispatching + return true; } + break; } + case QEvent::ContextMenu: { if (o != m_view) { // for whatever reason, the position of the event is slightly wrong + QContextMenuEvent* m = static_cast(e); QPoint correctPos = m_view->viewport()->mapFromGlobal(m->globalPos()); QContextMenuEvent corrected(m->reason(), correctPos, m->globalPos(), m->modifiers()); sendViewportEventToView(&corrected); + return true; } + break; } + default: break; } } return QComboBox::eventFilter(o, e); } // ------------------------------------------------------------------------- class Q_DECL_HIDDEN TreeViewComboBoxTreeView : public QTreeView { public: // Needed to make viewportEvent() public TreeViewComboBoxTreeView() : QTreeView() { } virtual bool viewportEvent(QEvent* event) override { return QTreeView::viewportEvent(event); } }; TreeViewComboBox::TreeViewComboBox(QWidget* const parent) : StayPoppedUpComboBox(parent) { } void TreeViewComboBox::installView(QAbstractItemView* view) { // parent does the heavy work + if (view) { StayPoppedUpComboBox::installView(view); } else { QPointer tview = new TreeViewComboBoxTreeView; StayPoppedUpComboBox::installView(tview); } } void TreeViewComboBox::sendViewportEventToView(QEvent* e) { static_cast(m_view)->viewportEvent(e); } QTreeView* TreeViewComboBox::view() const { return static_cast(m_view); } // ------------------------------------------------------------------------- class Q_DECL_HIDDEN ListViewComboBoxListView : public QListView { public: // Needed to make viewportEvent() public ListViewComboBoxListView() : QListView() { } virtual bool viewportEvent(QEvent* event) override { return QListView::viewportEvent(event); } }; ListViewComboBox::ListViewComboBox(QWidget* const parent) : StayPoppedUpComboBox(parent) { } void ListViewComboBox::installView(QAbstractItemView* view) { // parent does the heavy work + if (view) { StayPoppedUpComboBox::installView(view); } else { QPointer lview = new ListViewComboBoxListView; StayPoppedUpComboBox::installView(lview); } } void ListViewComboBox::sendViewportEventToView(QEvent* e) { static_cast(m_view)->viewportEvent(e); } QListView* ListViewComboBox::view() const { return static_cast(m_view); } // ------------------------------------------------------------------------- class Q_DECL_HIDDEN TreeViewComboBoxLineEdit : public QLineEdit { public: // This line edit works like a weblink: // Readonly; A mouse press shows the popup; Cursor is the pointing hand. explicit TreeViewComboBoxLineEdit(QComboBox* const box) - : QLineEdit(box) + : QLineEdit(box), + m_box(box) { - m_box = box; setReadOnly(true); setCursor(Qt::PointingHandCursor); } virtual void mouseReleaseEvent(QMouseEvent* event) override { QLineEdit::mouseReleaseEvent(event); m_box->showPopup(); } virtual void wheelEvent(QWheelEvent* /*event*/) override { m_box->showPopup(); } public: QComboBox* m_box; }; +// ------------------------------------------------------------------------- + TreeViewLineEditComboBox::TreeViewLineEditComboBox(QWidget* const parent) : TreeViewComboBox(parent), m_comboLineEdit(nullptr) { } void TreeViewLineEditComboBox::setLineEditText(const QString& text) { if (m_comboLineEdit) { m_comboLineEdit->setText(text); } } void TreeViewLineEditComboBox::installView(QAbstractItemView* view) { // parent does the heavy work + TreeViewComboBox::installView(view); installLineEdit(); } void TreeViewLineEditComboBox::installLineEdit() { if (!m_comboLineEdit) { setLineEdit(new TreeViewComboBoxLineEdit(this)); } } void TreeViewLineEditComboBox::setLineEdit(QLineEdit* edit) { m_comboLineEdit = edit; TreeViewComboBox::setLineEdit(edit); } } // namespace Digikam diff --git a/core/libs/widgets/combo/comboboxutilities.h b/core/libs/widgets/combo/comboboxutilities.h index 4d1ea54350..b196f29160 100644 --- a/core/libs/widgets/combo/comboboxutilities.h +++ b/core/libs/widgets/combo/comboboxutilities.h @@ -1,303 +1,316 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-03-14 * Description : User interface for searches * * Copyright (C) 2008-2012 by Marcel Wiesweg * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_COMBOBOX_UTILITIES_H #define DIGIKAM_COMBOBOX_UTILITIES_H // Qt includes #include #include #include #include - -// KDE includes - #include // Local includes #include "digikam_export.h" class QVBoxLayout; class QTreeView; namespace Digikam { class DIGIKAM_EXPORT ProxyLineEdit : public QLineEdit { Q_OBJECT public: /** * This class will not act as a QLineEdit at all, * but present another widget (any kind of widget) * instead in the space assigned to the QLineEdit. * Use this class if you need to pass a QLineEdit but * want actually to use a different widget. */ explicit ProxyLineEdit(QWidget* const parent = nullptr); - /// After constructing, set the actual widget here + /** + * After constructing, set the actual widget here + */ virtual void setWidget(QWidget* widget); void setClearButtonShown(bool show); Q_SIGNALS: void signalClearButtonPressed(); private Q_SLOTS: void slotTextChanged(const QString& text); protected: - QSize minimumSizeHint() const override; - QSize sizeHint() const override; - - virtual void mousePressEvent(QMouseEvent* event) override; - virtual void mouseMoveEvent(QMouseEvent* event) override; - virtual void mouseReleaseEvent(QMouseEvent* event) override; - virtual void mouseDoubleClickEvent(QMouseEvent* event) override; - virtual void keyPressEvent(QKeyEvent* event) override; - virtual void focusInEvent(QFocusEvent* event) override; - virtual void focusOutEvent(QFocusEvent* event) override; - virtual void paintEvent(QPaintEvent* event) override; - virtual void dragEnterEvent(QDragEnterEvent* event) override; - virtual void dragMoveEvent(QDragMoveEvent* e) override; - virtual void dragLeaveEvent(QDragLeaveEvent* e) override; - virtual void dropEvent(QDropEvent* event) override; - virtual void changeEvent(QEvent* event) override; + QSize minimumSizeHint() const override; + QSize sizeHint() const override; + + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseMoveEvent(QMouseEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void mouseDoubleClickEvent(QMouseEvent* event) override; + virtual void keyPressEvent(QKeyEvent* event) override; + virtual void focusInEvent(QFocusEvent* event) override; + virtual void focusOutEvent(QFocusEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; + virtual void dragEnterEvent(QDragEnterEvent* event) override; + virtual void dragMoveEvent(QDragMoveEvent* e) override; + virtual void dragLeaveEvent(QDragLeaveEvent* e) override; + virtual void dropEvent(QDropEvent* event) override; + virtual void changeEvent(QEvent* event) override; virtual void contextMenuEvent(QContextMenuEvent* event) override; virtual void inputMethodEvent(QInputMethodEvent* event) override; protected: QWidget* m_widget; QVBoxLayout* m_layout; }; // ------------------------------------------------------------------------- class DIGIKAM_EXPORT ProxyClickLineEdit : public ProxyLineEdit { Q_OBJECT public: /** * A ProxyLineEdit that emits leftClicked() on * mouse press event. * Press on the held widget will result in the signal * if the widget does not accept() them. */ explicit ProxyClickLineEdit(QWidget* const parent = nullptr); Q_SIGNALS: void leftClicked(); protected: void mouseReleaseEvent(QMouseEvent* event) override; }; // ------------------------------------------------------------------------- class DIGIKAM_EXPORT ModelIndexBasedComboBox : public QComboBox { public: /** * QComboBox has a current index based on a single integer. * This is not sufficient for more complex models. * This class is a combo box that stores a current index * based on QModelIndex. */ explicit ModelIndexBasedComboBox(QWidget* const parent = nullptr); QModelIndex currentIndex() const; void setCurrentIndex(const QModelIndex& index); virtual void hidePopup() override; virtual void showPopup() override; protected: QPersistentModelIndex m_currentIndex; }; // ------------------------------------------------------------------------- class DIGIKAM_EXPORT StayPoppedUpComboBox : public ModelIndexBasedComboBox { Q_OBJECT public: - /** This class provides an abstract QComboBox with a custom view - * (which is created by implementing subclasses) - * instead of the usual QListView. - * The Pop-up of the combo box will stay open after selecting an item; - * it will be closed by clicking outside, but not inside the widget. - * You need three steps: - * Construct the object, call setModel() with an appropriate - * QAbstractItemModel, then call installView() to replace - * the standard combo box view with a view. + /** + * This class provides an abstract QComboBox with a custom view + * (which is created by implementing subclasses) + * instead of the usual QListView. + * The Pop-up of the combo box will stay open after selecting an item; + * it will be closed by clicking outside, but not inside the widget. + * You need three steps: + * Construct the object, call setModel() with an appropriate + * QAbstractItemModel, then call installView() to replace + * the standard combo box view with a view. */ explicit StayPoppedUpComboBox(QWidget* const parent = nullptr); protected: - /** Replace the standard combo box list view with the given view. - * The view will be set as the view of the combo box - * (including re-parenting) and be stored in the m_view variable. + /** + * Replace the standard combo box list view with the given view. + * The view will be set as the view of the combo box + * (including re-parenting) and be stored in the m_view variable. */ void installView(QAbstractItemView* view); - /** Implement in subclass: - * Send the given event to the viewportEvent() method of m_view. - * This method is protected for a usual QAbstractItemView. - * You can override, pass a view, and call parent implementation. - * The existing view will be used. You must then also - * reimplement sendViewportEventToView. + /** + * Implement in subclass: + * Send the given event to the viewportEvent() method of m_view. + * This method is protected for a usual QAbstractItemView. + * You can override, pass a view, and call parent implementation. + * The existing view will be used. You must then also + * reimplement sendViewportEventToView. */ virtual void sendViewportEventToView(QEvent* e) = 0; virtual bool eventFilter(QObject* watched, QEvent* event) override; protected: QAbstractItemView* m_view; }; // ------------------------------------------------------------------------- class DIGIKAM_EXPORT TreeViewComboBox : public StayPoppedUpComboBox { Q_OBJECT public: - /** This class provides a QComboBox with a QTreeView - * instead of the usual QListView. - * You need three steps: - * Construct the object, call setModel() with an appropriate - * QAbstractItemModel, then call installView() to replace - * the standard combo box view with a QTreeView. + /** + * This class provides a QComboBox with a QTreeView + * instead of the usual QListView. + * You need three steps: + * Construct the object, call setModel() with an appropriate + * QAbstractItemModel, then call installView() to replace + * the standard combo box view with a QTreeView. */ explicit TreeViewComboBox(QWidget* parent = nullptr); - /** Replace the standard combo box list view with a QTreeView. - * Call this after installing an appropriate model. */ + /** + * Replace the standard combo box list view with a QTreeView. + * Call this after installing an appropriate model. + */ virtual void installView(QAbstractItemView* view = nullptr); - /** Returns the QTreeView of this class. Valid after installView() has been called */ + /** + * Returns the QTreeView of this class. Valid after installView() has been called + */ QTreeView* view() const; protected: virtual void sendViewportEventToView(QEvent* e) override; }; // ------------------------------------------------------------------------- class DIGIKAM_EXPORT ListViewComboBox : public StayPoppedUpComboBox { Q_OBJECT public: - /** This class provides an implementation of a StayPoppedUpComboBox - * with a QListView. This is the standard view of a QComboBox, - * but in conjunction with StayPoppedUpComboBox some extra steps are needed. - * You need three steps: - * Construct the object, call setModel() with an appropriate - * QAbstractItemModel, then call installView(). + /** + * This class provides an implementation of a StayPoppedUpComboBox + * with a QListView. This is the standard view of a QComboBox, + * but in conjunction with StayPoppedUpComboBox some extra steps are needed. + * You need three steps: + * Construct the object, call setModel() with an appropriate + * QAbstractItemModel, then call installView(). */ explicit ListViewComboBox(QWidget* parent = nullptr); - /** Returns the QTreeView of this class. Valid after installView() has been called. + /** + * Returns the QTreeView of this class. Valid after installView() has been called. */ QListView* view() const; - /** Replace the standard combo box list view with a QTreeView. - * Call this after installing an appropriate model. + /** + * Replace the standard combo box list view with a QTreeView. + * Call this after installing an appropriate model. */ virtual void installView(QAbstractItemView* view = nullptr); protected: virtual void sendViewportEventToView(QEvent* e) override; }; // ------------------------------------------------------------------------- class DIGIKAM_EXPORT TreeViewLineEditComboBox : public TreeViewComboBox { public: - /** This class provides a TreeViewComboBox - * with a read-only line edit. - * The text in the line edit can be adjusted. The combo box will - * open on a click on the line edit. - * You need three steps: - * Construct the object, call setModel() with an appropriate - * QAbstractItemModel, then call installView() to replace - * the standard combo box view with a QTreeView. + /** + * This class provides a TreeViewComboBox + * with a read-only line edit. + * The text in the line edit can be adjusted. The combo box will + * open on a click on the line edit. + * You need three steps: + * Construct the object, call setModel() with an appropriate + * QAbstractItemModel, then call installView() to replace + * the standard combo box view with a QTreeView. */ explicit TreeViewLineEditComboBox(QWidget* const parent = nullptr); - - /** Set the text of the line edit (the text that is visible - * if the popup is not opened). - * Applicable only for default installLineEdit() implementation. + /** + * Set the text of the line edit (the text that is visible + * if the popup is not opened). + * Applicable only for default installLineEdit() implementation. */ void setLineEditText(const QString& text); void setLineEdit(QLineEdit* edit); - /** Replace the standard combo box list view with a QTreeView. - * Call this after installing an appropriate model. + /** + * Replace the standard combo box list view with a QTreeView. + * Call this after installing an appropriate model. */ virtual void installView(QAbstractItemView* view = nullptr) override; protected: - /** Sets a line edit. Called by installView(). - * The default implementation is described above. - * An empty implementation will keep the default QComboBox line edit. + /** + * Sets a line edit. Called by installView(). + * The default implementation is described above. + * An empty implementation will keep the default QComboBox line edit. */ virtual void installLineEdit(); protected: QLineEdit* m_comboLineEdit; }; } // namespace Digikam #endif // DIGIKAM_COMBOBOX_UTILITIES_H diff --git a/core/libs/widgets/combo/dcombobox.cpp b/core/libs/widgets/combo/dcombobox.cpp index c35ec0e8d8..af52978a63 100644 --- a/core/libs/widgets/combo/dcombobox.cpp +++ b/core/libs/widgets/combo/dcombobox.cpp @@ -1,152 +1,152 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-08-16 * Description : a combo box widget re-implemented with a * reset button to switch to a default item * * Copyright (C) 2008-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "dcombobox.h" // Qt includes #include #include #include #include #include // KDE includes #include namespace Digikam { class Q_DECL_HIDDEN DComboBox::Private { public: explicit Private() + : defaultIndex(0), + resetButton(nullptr), + combo(nullptr) { - defaultIndex = 0; - resetButton = nullptr; - combo = nullptr; } int defaultIndex; QToolButton* resetButton; QComboBox* combo; }; DComboBox::DComboBox(QWidget* const parent) : QWidget(parent), d(new Private) { QHBoxLayout* const hlay = new QHBoxLayout(this); d->combo = new QComboBox(this); d->resetButton = new QToolButton(this); d->resetButton->setAutoRaise(true); d->resetButton->setFocusPolicy(Qt::NoFocus); d->resetButton->setIcon(QIcon::fromTheme(QLatin1String("document-revert"))); d->resetButton->setToolTip(i18nc("@info:tooltip", "Reset to default value")); hlay->addWidget(d->combo); hlay->addWidget(d->resetButton); hlay->setStretchFactor(d->combo, 10); hlay->setContentsMargins(QMargins()); hlay->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); // ------------------------------------------------------------- connect(d->resetButton, &QToolButton::clicked, this, &DComboBox::slotReset); connect(d->combo, static_cast(&QComboBox::activated), this, &DComboBox::slotItemActivated); connect(d->combo, static_cast(&QComboBox::currentIndexChanged), this, &DComboBox::slotCurrentIndexChanged); } DComboBox::~DComboBox() { delete d; } QComboBox* DComboBox::combo() const { return d->combo; } void DComboBox::addItem(const QString& t, int index) { d->combo->addItem(t, index); } void DComboBox::insertItem(int index, const QString& t, const QVariant& data) { d->combo->insertItem(index, t, data); } int DComboBox::currentIndex() const { return d->combo->currentIndex(); } void DComboBox::setCurrentIndex(int v) { d->combo->setCurrentIndex(v); } int DComboBox::defaultIndex() const { return d->defaultIndex; } void DComboBox::setDefaultIndex(int v) { d->defaultIndex = v; d->combo->setCurrentIndex(d->defaultIndex); slotItemActivated(v); } void DComboBox::slotReset() { d->combo->setCurrentIndex(d->defaultIndex); d->resetButton->setEnabled(false); slotItemActivated(d->defaultIndex); emit reset(); } void DComboBox::slotItemActivated(int v) { d->resetButton->setEnabled(v != d->defaultIndex); emit activated(v); } void DComboBox::slotCurrentIndexChanged(int v) { d->resetButton->setEnabled(v != d->defaultIndex); emit currentIndexChanged(v); } } // namespace Digikam diff --git a/core/libs/widgets/combo/dcombobox.h b/core/libs/widgets/combo/dcombobox.h index d8a1a3cf38..8e1ec94595 100644 --- a/core/libs/widgets/combo/dcombobox.h +++ b/core/libs/widgets/combo/dcombobox.h @@ -1,85 +1,86 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-08-16 * Description : a combo box widget re-implemented with a * reset button to switch to a default item * * Copyright (C) 2008-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_DCOMBO_BOX_H #define DIGIKAM_DCOMBO_BOX_H // Qt includes #include #include #include // Local includes #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT DComboBox : public QWidget { Q_OBJECT public: explicit DComboBox(QWidget* const parent=nullptr); ~DComboBox(); void setCurrentIndex(int d); int currentIndex() const; void setDefaultIndex(int d); int defaultIndex() const; - QComboBox* combo() const; + QComboBox* combo() const; void addItem(const QString& t, int index = -1); - void insertItem(int index, const QString& t, const QVariant& data = QVariant()); + void insertItem(int index, const QString& t, + const QVariant& data = QVariant()); Q_SIGNALS: void reset(); void activated(int); void currentIndexChanged(int); public Q_SLOTS: void slotReset(); private Q_SLOTS: void slotItemActivated(int); void slotCurrentIndexChanged(int); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_DCOMBO_BOX_H diff --git a/core/libs/widgets/combo/squeezedcombobox.cpp b/core/libs/widgets/combo/squeezedcombobox.cpp index aec050f7a9..33cf7dca55 100644 --- a/core/libs/widgets/combo/squeezedcombobox.cpp +++ b/core/libs/widgets/combo/squeezedcombobox.cpp @@ -1,218 +1,229 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-08-21 * Description : a combo box with a width not depending of text * content size * * Copyright (C) 2006-2020 by Gilles Caulier * Copyright (C) 2008 by Andi Clemens * Copyright (C) 2005 by Tom Albers * * 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, 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. * * ============================================================ */ #include "squeezedcombobox.h" // Qt includes #include #include #include #include #include namespace Digikam { class Q_DECL_HIDDEN SqueezedComboBox::Private { public: explicit Private() + : timer(nullptr) { - timer = nullptr; } QMap originalItems; QTimer* timer; }; SqueezedComboBox::SqueezedComboBox(QWidget* const parent, const char* name) : QComboBox(parent), d(new Private) { setObjectName(QString::fromUtf8(name)); setMinimumWidth(100); d->timer = new QTimer(this); d->timer->setSingleShot(true); connect(d->timer, &QTimer::timeout, this, &SqueezedComboBox::slotTimeOut); connect(this, static_cast(&SqueezedComboBox::activated), this, &SqueezedComboBox::slotUpdateToolTip); } SqueezedComboBox::~SqueezedComboBox() { d->originalItems.clear(); delete d->timer; delete d; } bool SqueezedComboBox::contains(const QString& text) const { if (text.isEmpty()) + { return false; + } for (QMap::const_iterator it = d->originalItems.constBegin() ; it != d->originalItems.constEnd() ; ++it) { if (it.value() == text) + { return true; + } } return false; } QSize SqueezedComboBox::sizeHint() const { ensurePolished(); QFontMetrics fm = fontMetrics(); #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) int maxW = count() ? 18 : 7 * fm.horizontalAdvance(QLatin1Char('x')) + 18; #else int maxW = count() ? 18 : 7 * fm.width(QLatin1Char('x')) + 18; #endif int maxH = qMax(fm.lineSpacing(), 14) + 2; QStyleOptionComboBox options; options.initFrom(this); return style()->sizeFromContents(QStyle::CT_ComboBox, &options, QSize(maxW, maxH), this).expandedTo(QApplication::globalStrut()); } void SqueezedComboBox::insertSqueezedItem(const QString& newItem, int index, const QVariant& userData) { d->originalItems[index] = newItem; QComboBox::insertItem(index, squeezeText(newItem), userData); // if this is the first item, set the tooltip. + if (index == 0) + { slotUpdateToolTip(0); + } } void SqueezedComboBox::insertSqueezedList(const QStringList& newItems, int index) { for (QStringList::const_iterator it = newItems.constBegin() ; it != newItems.constEnd() ; ++it) { insertSqueezedItem(*it, index); ++index; } } void SqueezedComboBox::addSqueezedItem(const QString& newItem, const QVariant& userData) { insertSqueezedItem(newItem, count(), userData); } void SqueezedComboBox::setCurrent(const QString& itemText) { QString squeezedText = squeezeText(itemText); qint32 itemIndex = findText(squeezedText); if (itemIndex >= 0) + { setCurrentIndex(itemIndex); + } } void SqueezedComboBox::resizeEvent(QResizeEvent *) { d->timer->start(200); } void SqueezedComboBox::slotTimeOut() { for (QMap::iterator it = d->originalItems.begin() ; it != d->originalItems.end() ; ++it) { setItemText(it.key(), squeezeText(it.value())); } } QString SqueezedComboBox::squeezeText(const QString& original) const { // not the complete widgetSize is usable. Need to compensate for that. int widgetSize = width() - 30; QFontMetrics fm(fontMetrics()); // If we can fit the full text, return that. #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) if (fm.horizontalAdvance(original) < widgetSize) #else if (fm.width(original) < widgetSize) #endif + { return(original); + } // We need to squeeze. QString sqItem = original; // prevent empty return value; #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) widgetSize = widgetSize-fm.horizontalAdvance(QLatin1String("...")); #else widgetSize = widgetSize-fm.width(QLatin1String("...")); #endif for (int i = 0 ; i != original.length() ; ++i) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) if ((int)fm.horizontalAdvance(original.right(i)) > widgetSize) #else if ((int)fm.width(original.right(i)) > widgetSize) #endif { sqItem = QString(original.left(i) + QLatin1String("...")); break; } } return sqItem; } void SqueezedComboBox::slotUpdateToolTip(int index) { setToolTip(d->originalItems[index]); } QString SqueezedComboBox::itemHighlighted() const { int curItem = currentIndex(); return d->originalItems[curItem]; } QString SqueezedComboBox::item(int index) const { return d->originalItems[index]; } } // namespace Digikam diff --git a/core/libs/widgets/combo/squeezedcombobox.h b/core/libs/widgets/combo/squeezedcombobox.h index c67812178f..6f2b30326e 100644 --- a/core/libs/widgets/combo/squeezedcombobox.h +++ b/core/libs/widgets/combo/squeezedcombobox.h @@ -1,161 +1,163 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-08-21 * Description : a combo box with a width not depending of text * content size * * Copyright (C) 2006-2020 by Gilles Caulier * Copyright (C) 2008 by Andi Clemens * Copyright (C) 2005 by Tom Albers * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_SQUEEZED_COMBOBOX_H #define DIGIKAM_SQUEEZED_COMBOBOX_H // Qt includes #include // Local includes #include "digikam_export.h" namespace Digikam { -/** @class SqueezedComboBox +/** + * @class SqueezedComboBox * * This widget is a QComboBox, but then a little bit * different. It only shows the right part of the items * depending on de size of the widget. When it is not * possible to show the complete item, it will be shortened * and "..." will be prepended. */ class DIGIKAM_EXPORT SqueezedComboBox : public QComboBox { Q_OBJECT public: /** * Constructor * @param parent parent widget * @param name name to give to the widget */ - explicit SqueezedComboBox(QWidget* const parent = nullptr, const char* name = nullptr ); + explicit SqueezedComboBox(QWidget* const parent = nullptr, const char* name = nullptr); + /** * destructor */ virtual ~SqueezedComboBox(); /** * * Returns true if the combobox contains the original (not-squeezed) * version of text. * @param text the original (not-squeezed) text to check for */ bool contains(const QString& text) const; /** * This inserts a item to the list. See QComboBox::insertItem() * for details. Please do not use QComboBox::insertItem() to this * widget, as that will fail. * @param newItem the original (long version) of the item which needs * to be added to the combobox * @param index the position in the widget. * @param userData custom meta-data assigned to new item. */ void insertSqueezedItem(const QString& newItem, int index, const QVariant& userData=QVariant()); /** * This inserts items to the list. See QComboBox::insertItems() * for details. Please do not use QComboBox:: insertItems() to this * widget, as that will fail. * @param newItems the originals (long version) of the items which needs * to be added to the combobox * @param index the position in the widget. */ void insertSqueezedList(const QStringList& newItems, int index); /** * Append an item. * @param newItem the original (long version) of the item which needs * to be added to the combobox * @param userData custom meta-data assigned to new item. */ void addSqueezedItem(const QString& newItem, const QVariant& userData=QVariant()); /** * Set the current item to the one matching the given text. * * @param itemText the original (long version) of the item text */ void setCurrent(const QString& itemText); /** * This method returns the full text (not squeezed) of the currently * highlighted item. * @return full text of the highlighted item */ QString itemHighlighted() const; /** * This method returns the full text (not squeezed) for the index. * @param index the position in the widget. * @return full text of the item */ QString item(int index) const; /** * Sets the sizeHint() of this widget. */ virtual QSize sizeHint() const override; private Q_SLOTS: void slotTimeOut(); void slotUpdateToolTip(int index); private: void resizeEvent(QResizeEvent*) override; QString squeezeText(const QString& original) const; // Prevent these from being used. QString currentText() const; void setCurrentText(const QString& itemText); void insertItem(const QString& text); void insertItem(qint32 index, const QString& text); void insertItem(int index, const QIcon& icon, const QString& text, const QVariant& userData=QVariant()); void insertItems(int index, const QStringList& list); void addItem(const QString& text); void addItem(const QIcon& icon, const QString& text, const QVariant& userData=QVariant()); void addItems(const QStringList& texts); QString itemText(int index) const; private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_SQUEEZED_COMBOBOX_H diff --git a/core/libs/widgets/combo/timezonecombobox.cpp b/core/libs/widgets/combo/timezonecombobox.cpp index a7f9dc9730..d9cff63af5 100644 --- a/core/libs/widgets/combo/timezonecombobox.cpp +++ b/core/libs/widgets/combo/timezonecombobox.cpp @@ -1,118 +1,120 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2015-05-29 * Description : a combobox with time zones. * * Copyright (C) 2015 by Maik Qualmann * Copyright (C) 2006-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "timezonecombobox.h" namespace Digikam { TimeZoneComboBox::TimeZoneComboBox(QWidget* const parent) : QComboBox(parent) { QStringList timeZones; timeZones << QLatin1String("") << QLatin1String("-12:00") << QLatin1String("-11:00") << QLatin1String("-10:00") << QLatin1String("-09:30") << QLatin1String("-09:00") << QLatin1String("-08:00") << QLatin1String("-07:00") << QLatin1String("-06:00") << QLatin1String("-05:00") << QLatin1String("-04:30") << QLatin1String("-04:00") << QLatin1String("-03:30") << QLatin1String("-03:00") << QLatin1String("-02:00") << QLatin1String("-01:00") << QLatin1String("+00:00") << QLatin1String("+01:00") << QLatin1String("+02:00") << QLatin1String("+03:00") << QLatin1String("+03:30") << QLatin1String("+04:00") << QLatin1String("+04:30") << QLatin1String("+05:00") << QLatin1String("+05:30") << QLatin1String("+05:45") << QLatin1String("+06:00") << QLatin1String("+06:30") << QLatin1String("+07:00") << QLatin1String("+08:00") << QLatin1String("+09:00") << QLatin1String("+09:30") << QLatin1String("+10:00") << QLatin1String("+10:30") << QLatin1String("+11:00") << QLatin1String("+11:30") << QLatin1String("+12:00") << QLatin1String("+12:45") << QLatin1String("+13:00") << QLatin1String("+13:45") << QLatin1String("+14:00"); addItems(timeZones); } TimeZoneComboBox::~TimeZoneComboBox() { } void TimeZoneComboBox::setToUTC() { setCurrentIndex(findText(QLatin1String("+00:00"))); } void TimeZoneComboBox::setTimeZone(const QString& timeStr) { if (timeStr.length() < 6) { setCurrentIndex(0); return; } QString timeZone = timeStr.right(6); if (timeZone.endsWith(QLatin1Char('Z'))) { setToUTC(); } else if ((timeZone.startsWith(QLatin1Char('+')) || timeZone.startsWith(QLatin1Char('-'))) && - timeZone.mid(3, 1) == QLatin1String(":")) + (timeZone.mid(3, 1) == QLatin1String(":"))) { setCurrentIndex(findText(timeZone)); } else { setCurrentIndex(0); } } QString TimeZoneComboBox::getTimeZone() const { return currentText(); } int TimeZoneComboBox::timeZoneOffset() const { QString tz = currentText(); if (tz.isEmpty()) + { return 0; + } int hh = QString(QString(tz[1]) + QString(tz[2])).toInt(); int mm = QString(QString(tz[4]) + QString(tz[5])).toInt(); int offset = hh*3600 + mm*60; if (tz[0] == QLatin1Char('-')) { offset = (-1) * offset; } return offset; } } // namespace Digikam diff --git a/core/libs/widgets/common/abstractitemdragdrophandler.cpp b/core/libs/widgets/common/abstractitemdragdrophandler.cpp index 6d6cc6434a..222d1c4d95 100644 --- a/core/libs/widgets/common/abstractitemdragdrophandler.cpp +++ b/core/libs/widgets/common/abstractitemdragdrophandler.cpp @@ -1,80 +1,80 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-04-26 * Description : Qt Model for Images - drag and drop handling * * Copyright (C) 2009-2011 by Marcel Wiesweg * * 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, 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. * * ============================================================ */ #include "abstractitemdragdrophandler.h" // Qt includes #include #include namespace Digikam { AbstractItemDragDropHandler::AbstractItemDragDropHandler(QAbstractItemModel* const model) : QObject(model), m_model(model) { } QAbstractItemModel* AbstractItemDragDropHandler::model() const { return m_model; } bool AbstractItemDragDropHandler::dropEvent(QAbstractItemView*, const QDropEvent*, const QModelIndex&) { return false; } Qt::DropAction AbstractItemDragDropHandler::accepts(const QDropEvent*, const QModelIndex&) { return Qt::IgnoreAction; } QStringList AbstractItemDragDropHandler::mimeTypes() const { return QStringList(); } QMimeData* AbstractItemDragDropHandler::createMimeData(const QList&) { return nullptr; } bool AbstractItemDragDropHandler::acceptsMimeData(const QMimeData* mime) { QStringList modelTypes = mimeTypes(); - for (int i = 0; i < modelTypes.count(); ++i) + for (int i = 0 ; i < modelTypes.count() ; ++i) { if (mime->hasFormat(modelTypes.at(i))) //&& (e->dropAction() & model->supportedDropActions())) { return true; } } return false; } } // namespace Digikam diff --git a/core/libs/widgets/common/abstractitemdragdrophandler.h b/core/libs/widgets/common/abstractitemdragdrophandler.h index 81275f9a82..4877bca401 100644 --- a/core/libs/widgets/common/abstractitemdragdrophandler.h +++ b/core/libs/widgets/common/abstractitemdragdrophandler.h @@ -1,82 +1,91 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-04-26 * Description : Qt Model for Images - drag and drop handling * * Copyright (C) 2009-2011 by Marcel Wiesweg * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_ABSTRACT_ITEM_DRAG_DROP_HANDLER_H #define DIGIKAM_ABSTRACT_ITEM_DRAG_DROP_HANDLER_H // Qt includes #include // Local includes #include "digikam_export.h" class QAbstractItemView; class QDropEvent; namespace Digikam { class DIGIKAM_EXPORT AbstractItemDragDropHandler : public QObject { public: explicit AbstractItemDragDropHandler(QAbstractItemModel* const model); virtual ~AbstractItemDragDropHandler() {} QAbstractItemModel* model() const; - /** Gives the view and the occurring drop event. - * The index is the index where the drop was dropped on. - * It may be invalid (dropped on decoration, viewport) - * Returns true if the event is to be accepted. + /** + * Gives the view and the occurring drop event. + * The index is the index where the drop was dropped on. + * It may be invalid (dropped on decoration, viewport) + * Returns true if the event is to be accepted. */ virtual bool dropEvent(QAbstractItemView* view, const QDropEvent* e, const QModelIndex& droppedOn); - /** Returns if the given mime data is accepted for drop on dropIndex. - * Returns the proposed action, or Qt::IgnoreAction if not accepted. */ + /** + * Returns if the given mime data is accepted for drop on dropIndex. + * Returns the proposed action, or Qt::IgnoreAction if not accepted. + */ virtual Qt::DropAction accepts(const QDropEvent* e, const QModelIndex& dropIndex); - /** Returns the supported mime types. - * Called by the default implementation of model's mimeTypes(). */ + /** + * Returns the supported mime types. + * Called by the default implementation of model's mimeTypes(). + */ virtual QStringList mimeTypes() const; - /** Create a mime data object for starting a drag from the given Albums */ + /** + * Create a mime data object for starting a drag from the given Albums + */ virtual QMimeData* createMimeData(const QList&); - /** Returns if the given mime data can be handled. acceptsMimeData shall return true - * if a drop of the given mime data will be accepted on any index or place at all. - * If this returns false, the more specific method accepts() will not be called for this drag. - * The default implementation uses mimeTypes() to check for supported mime types. - * There is usually no need to reimplement this. */ + /** + * Returns if the given mime data can be handled. acceptsMimeData shall return true + * if a drop of the given mime data will be accepted on any index or place at all. + * If this returns false, the more specific method accepts() will not be called for this drag. + * The default implementation uses mimeTypes() to check for supported mime types. + * There is usually no need to reimplement this. + */ virtual bool acceptsMimeData(const QMimeData* data); protected: QAbstractItemModel* m_model; }; } // namespace Digikam #endif // DIGIKAM_ABSTRACT_ITEM_DRAG_DROP_HANDLER_H diff --git a/core/libs/widgets/common/dragdropimplementations.cpp b/core/libs/widgets/common/dragdropimplementations.cpp index c39eb76e40..aa3a93abf2 100644 --- a/core/libs/widgets/common/dragdropimplementations.cpp +++ b/core/libs/widgets/common/dragdropimplementations.cpp @@ -1,278 +1,281 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-04-26 * Description : Qt Model for Images - drag and drop handling * * Copyright (C) 2009-2011 by Marcel Wiesweg * * 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, 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. * * ============================================================ */ #include "dragdropimplementations.h" // Qt includes #include #include #include #include #include #include namespace Digikam { // ------------ Model sample implementation ------------- DragDropModelImplementation::DragDropModelImplementation() : m_dragDropHandler(nullptr) { } Qt::ItemFlags DragDropModelImplementation::dragDropFlags(const QModelIndex& index) const { Q_UNUSED(index); if (!m_dragDropHandler) { return nullptr; } - return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + return (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); } Qt::ItemFlags DragDropModelImplementation::dragDropFlagsV2(const QModelIndex& index) const { Qt::ItemFlags flags; if (isDragEnabled(index)) { flags |= Qt::ItemIsDragEnabled; } if (isDropEnabled(index)) { flags |= Qt::ItemIsDropEnabled; } return flags; } bool DragDropModelImplementation::isDragEnabled(const QModelIndex& index) const { Q_UNUSED(index); + return true; } bool DragDropModelImplementation::isDropEnabled(const QModelIndex& index) const { Q_UNUSED(index); + return true; } Qt::DropActions DragDropModelImplementation::supportedDropActions() const { return Qt::CopyAction|Qt::MoveAction; } QStringList DragDropModelImplementation::mimeTypes() const { if (m_dragDropHandler) { return m_dragDropHandler->mimeTypes(); } return QStringList(); } bool DragDropModelImplementation::dropMimeData(const QMimeData*, Qt::DropAction, int, int, const QModelIndex&) { // we require custom solutions + return false; } QMimeData* DragDropModelImplementation::mimeData(const QModelIndexList& indexes) const { if (!m_dragDropHandler) { return nullptr; } return m_dragDropHandler->createMimeData(indexes); } void DragDropModelImplementation::setDragDropHandler(AbstractItemDragDropHandler* handler) { m_dragDropHandler = handler; } AbstractItemDragDropHandler* DragDropModelImplementation::dragDropHandler() const { return m_dragDropHandler; } // ------------ View sample implementation ------------- void DragDropViewImplementation::cut() { QMimeData* const data = asView()->model()->mimeData(asView()->selectionModel()->selectedIndexes()); if (data) { encodeIsCutSelection(data, true); qApp->clipboard()->setMimeData(data); } } void DragDropViewImplementation::copy() { QMimeData* const data = asView()->model()->mimeData(asView()->selectionModel()->selectedIndexes()); if (data) { encodeIsCutSelection(data, false); qApp->clipboard()->setMimeData(data); } } void DragDropViewImplementation::paste() { const QMimeData* const data = qApp->clipboard()->mimeData(QClipboard::Clipboard); if (!data) { return; } // We need to have a real (context menu action) or fake (Ctrl+V shortcut) mouse position QPoint eventPos = asView()->mapFromGlobal(QCursor::pos()); if (!asView()->rect().contains(eventPos)) { eventPos = QPoint(0, 0); } bool cutAction = decodeIsCutSelection(data); QDropEvent event(eventPos, cutAction ? Qt::MoveAction : Qt::CopyAction, data, Qt::NoButton, cutAction ? Qt::ShiftModifier : Qt::ControlModifier); QModelIndex index = asView()->indexAt(event.pos()); if (!dragDropHandler()->accepts(&event, index)) { return; } dragDropHandler()->dropEvent(asView(), &event, index); } void DragDropViewImplementation::startDrag(Qt::DropActions supportedActions) { QModelIndexList indexes = asView()->selectionModel()->selectedIndexes(); if (indexes.count() > 0) { QMimeData* const data = asView()->model()->mimeData(indexes); if (!data) { return; } QPixmap pixmap = pixmapForDrag(indexes); QDrag* const drag = new QDrag(asView()); drag->setPixmap(pixmap); drag->setMimeData(data); drag->exec(supportedActions, Qt::CopyAction); } } void DragDropViewImplementation::dragEnterEvent(QDragEnterEvent* e) { AbstractItemDragDropHandler* const handler = dragDropHandler(); if (handler && handler->acceptsMimeData(e->mimeData())) { e->accept(); } else { e->ignore(); } } void DragDropViewImplementation::dragMoveEvent(QDragMoveEvent* e) { // Note: Must call parent view first. This is done by the DECLARE... macro. AbstractItemDragDropHandler* const handler = dragDropHandler(); if (handler) { QModelIndex index = asView()->indexAt(e->pos()); Qt::DropAction action = handler->accepts(e, mapIndexForDragDrop(index)); if (action == Qt::IgnoreAction) { e->ignore(); } else { e->setDropAction(action); e->accept(); } } } void DragDropViewImplementation::dropEvent(QDropEvent* e) { // Note: Must call parent view first. This is done by the DECLARE... macro. AbstractItemDragDropHandler* const handler = dragDropHandler(); if (handler) { QModelIndex index = asView()->indexAt(e->pos()); if (handler->dropEvent(asView(), e, mapIndexForDragDrop(index))) { e->accept(); } } } static const QString mimeTypeCutSelection(QLatin1String("application/x-kde-cutselection")); void DragDropViewImplementation::encodeIsCutSelection(QMimeData* mime, bool cut) { const QByteArray cutSelection = cut ? "1" : "0"; mime->setData(mimeTypeCutSelection, cutSelection); } bool DragDropViewImplementation::decodeIsCutSelection(const QMimeData* mime) { QByteArray a = mime->data(mimeTypeCutSelection); if (a.isEmpty()) { return false; } return (a.at(0) == '1'); // true if 1 } } // namespace Digikam diff --git a/core/libs/widgets/common/dragdropimplementations.h b/core/libs/widgets/common/dragdropimplementations.h index d8127058e2..9828b9a9c1 100644 --- a/core/libs/widgets/common/dragdropimplementations.h +++ b/core/libs/widgets/common/dragdropimplementations.h @@ -1,159 +1,161 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-01-02 * Description : Sample implementations for drag and drop handling * * Copyright (C) 2009-2011 by Marcel Wiesweg * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_DRAG_DROP_IMPLEMENTATIONS_H #define DIGIKAM_DRAG_DROP_IMPLEMENTATIONS_H // Qt includes #include // Local includes #include "digikam_export.h" #include "abstractitemdragdrophandler.h" namespace Digikam { class DIGIKAM_EXPORT DragDropModelImplementation { public: /** * A class providing a sample implementation for a QAbstractItemModel * redirecting drag-and-drop support to a handler. * Include the macro DECLARE_Model_DRAG_DROP_METHODS in your derived QAbstractItemModel class. */ - DragDropModelImplementation(); virtual ~DragDropModelImplementation() {} /** * Implements the relevant QAbstractItemModel methods for drag and drop. * All functionality is redirected to the handler. * dropMimeData() always returns false, leaving implementation to the view. */ - Qt::DropActions supportedDropActions() const; - QStringList mimeTypes() const; + Qt::DropActions supportedDropActions() const; + QStringList mimeTypes() const; bool dropMimeData(const QMimeData*, Qt::DropAction, int, int, const QModelIndex&); QMimeData* mimeData(const QModelIndexList& indexes) const; /** * Call from your flags() method, adding the relevant drag drop flags. * Default implementation enables both drag and drop on the index * if a drag drop handler is set. * Reimplement to fine-tune. Note: There is an alternative below. */ - virtual Qt::ItemFlags dragDropFlags(const QModelIndex& index) const; + virtual Qt::ItemFlags dragDropFlags(const QModelIndex& index) const; /** * This is an alternative approach to dragDropFlags(). * dragDropFlagsV2 calls the virtual methods isDragEnabled() * and isDropEnabled() which you then reimplement. * Use simple dragDropFlags() if you need not customization, * or reimplement dragDropFlags() if you fine-tune it yourself. */ - Qt::ItemFlags dragDropFlagsV2(const QModelIndex& index) const; - virtual bool isDragEnabled(const QModelIndex& index) const; - virtual bool isDropEnabled(const QModelIndex& index) const; + Qt::ItemFlags dragDropFlagsV2(const QModelIndex& index) const; + virtual bool isDragEnabled(const QModelIndex& index) const; + virtual bool isDropEnabled(const QModelIndex& index) const; - /// Set a drag drop handler. + /** + * Set a drag drop handler. + */ void setDragDropHandler(AbstractItemDragDropHandler* handler); - AbstractItemDragDropHandler* dragDropHandler() const; - - #define DECLARE_MODEL_DRAG_DROP_METHODS \ - virtual Qt::DropActions supportedDropActions() const override \ - { return DragDropModelImplementation::supportedDropActions(); } \ - virtual QStringList mimeTypes() const override \ - { return DragDropModelImplementation::mimeTypes(); } \ - virtual bool dropMimeData(const QMimeData* d, Qt::DropAction a, int r, int c, const QModelIndex& p) override \ - { return DragDropModelImplementation::dropMimeData(d, a, r, c, p); } \ - virtual QMimeData* mimeData(const QModelIndexList& indexes) const override \ + AbstractItemDragDropHandler* dragDropHandler() const; + + #define DECLARE_MODEL_DRAG_DROP_METHODS \ + virtual Qt::DropActions supportedDropActions() const override \ + { return DragDropModelImplementation::supportedDropActions(); } \ + virtual QStringList mimeTypes() const override \ + { return DragDropModelImplementation::mimeTypes(); } \ + virtual bool dropMimeData(const QMimeData* d, Qt::DropAction a, \ + int r, int c, const QModelIndex& p) override \ + { return DragDropModelImplementation::dropMimeData(d, a, r, c, p); } \ + virtual QMimeData* mimeData(const QModelIndexList& indexes) const override \ { return DragDropModelImplementation::mimeData(indexes); } protected: AbstractItemDragDropHandler* m_dragDropHandler; }; // -------------------------------------------------------------------------------------------- class DIGIKAM_EXPORT DragDropViewImplementation { public: virtual ~DragDropViewImplementation() {} virtual void cut(); virtual void copy(); virtual void paste(); protected: /// This one is implemented by DECLARE_VIEW_DRAG_DROP_METHODS - virtual QAbstractItemView* asView() = 0; + virtual QAbstractItemView* asView() = 0; /// You need to implement these three methods /// Returns the drag drop handler - virtual AbstractItemDragDropHandler* dragDropHandler() const = 0; + virtual AbstractItemDragDropHandler* dragDropHandler() const = 0; /** * Maps the given index of the view's model to an index of the handler's model, * which can be a source model of the view's model. */ - virtual QModelIndex mapIndexForDragDrop(const QModelIndex& index) const = 0; + virtual QModelIndex mapIndexForDragDrop(const QModelIndex& index) const = 0; /** * Creates a pixmap for dragging the given indexes. */ virtual QPixmap pixmapForDrag(const QList& indexes) const = 0; /** * Implements the relevant QAbstractItemView methods for drag and drop. */ void dragEnterEvent(QDragEnterEvent* event); void dragMoveEvent(QDragMoveEvent* e); void dropEvent(QDropEvent* e); void startDrag(Qt::DropActions supportedActions); - #define DECLARE_VIEW_DRAG_DROP_METHODS(ParentViewClass) \ - virtual QAbstractItemView* asView() override { return this; } \ - void dragEnterEvent(QDragEnterEvent* e) override \ - { DragDropViewImplementation::dragEnterEvent(e); } \ - void dragMoveEvent(QDragMoveEvent* e) override \ - { ParentViewClass::dragMoveEvent(e); \ - DragDropViewImplementation::dragMoveEvent(e); } \ - void dropEvent(QDropEvent* e) override \ - { ParentViewClass::dropEvent(e); \ - DragDropViewImplementation::dropEvent(e); } \ - void startDrag(Qt::DropActions supportedActions) override \ - { DragDropViewImplementation::startDrag(supportedActions); } \ + #define DECLARE_VIEW_DRAG_DROP_METHODS(ParentViewClass) \ + virtual QAbstractItemView* asView() override { return this; } \ + void dragEnterEvent(QDragEnterEvent* e) override \ + { DragDropViewImplementation::dragEnterEvent(e); } \ + void dragMoveEvent(QDragMoveEvent* e) override \ + { ParentViewClass::dragMoveEvent(e); \ + DragDropViewImplementation::dragMoveEvent(e); } \ + void dropEvent(QDropEvent* e) override \ + { ParentViewClass::dropEvent(e); \ + DragDropViewImplementation::dropEvent(e); } \ + void startDrag(Qt::DropActions supportedActions) override \ + { DragDropViewImplementation::startDrag(supportedActions); } \ void encodeIsCutSelection(QMimeData* mime, bool isCutSelection); bool decodeIsCutSelection(const QMimeData* mimeData); }; } // namespace Digikam #endif // DIGIKAM_DRAG_DROP_IMPLEMENTATIONS_H diff --git a/core/libs/widgets/common/modelcompleter.cpp b/core/libs/widgets/common/modelcompleter.cpp index 65d4b5cc7c..f2f0d4a903 100644 --- a/core/libs/widgets/common/modelcompleter.cpp +++ b/core/libs/widgets/common/modelcompleter.cpp @@ -1,295 +1,301 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-06-13 * Description : A QCompleter for AbstractAlbumModels * * Copyright (C) 2007-2020 by Gilles Caulier * Copyright (C) 2009-2010 by Johannes Wienke * Copyright (C) 2010-2011 by Marcel Wiesweg * * 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, 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. * * ============================================================ */ #include "modelcompleter.h" // Qt includes #include #include #include // Local includes #include "digikam_debug.h" namespace Digikam { class Q_DECL_HIDDEN ModelCompleter::Private { public: explicit Private() : displayRole(Qt::DisplayRole), uniqueIdRole(Qt::DisplayRole), delayedModelTimer(nullptr), stringModel(nullptr), model(nullptr) { } int displayRole; int uniqueIdRole; QTimer* delayedModelTimer; QStringListModel* stringModel; QPointer model; /** * This map maps model indexes to their current text representation in the * completion object. This is needed because if data changes in one index, * the old text value is not known anymore, so that it cannot be removed * from the completion object. + * TODO: if we want to use models that return unique strings but not integer, add support */ - //TODO: if we want to use models that return unique strings but not integer, add support QHash idToTextHash; }; ModelCompleter::ModelCompleter(QObject* const parent) : QCompleter(parent), d(new Private) { d->stringModel = new QStringListModel(this); setModel(d->stringModel); setModelSorting(CaseSensitivelySortedModel); setCaseSensitivity(Qt::CaseInsensitive); setCompletionMode(PopupCompletion); setCompletionRole(Qt::DisplayRole); setFilterMode(Qt::MatchContains); setMaxVisibleItems(10); setCompletionColumn(0); d->delayedModelTimer = new QTimer(this); d->delayedModelTimer->setInterval(500); d->delayedModelTimer->setSingleShot(true); connect(d->delayedModelTimer, SIGNAL(timeout()), this, SLOT(slotDelayedModelTimer())); connect(this, SIGNAL(activated(QModelIndex)), this, SIGNAL(activated())); connect(this, SIGNAL(highlighted(QModelIndex)), this, SLOT(slotHighlighted(QModelIndex))); } ModelCompleter::~ModelCompleter() { delete d; } void ModelCompleter::setItemModel(QAbstractItemModel* const model, int uniqueIdRole, int displayRole) { // first release old model + if (d->model) { disconnect(d->model); d->idToTextHash.clear(); d->stringModel->setStringList(QStringList()); } d->model = model; d->displayRole = displayRole; d->uniqueIdRole = uniqueIdRole; // connect to the new model + if (d->model) { connect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(slotRowsInserted(QModelIndex,int,int))); connect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int))); connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotDataChanged(QModelIndex,QModelIndex))); connect(d->model, SIGNAL(modelReset()), this, SLOT(slotModelReset())); // do an initial sync wit the new model + sync(d->model); } } QAbstractItemModel* ModelCompleter::itemModel() const { return d->model; } void ModelCompleter::addItem(const QString& item) { QStringList stringList = d->stringModel->stringList(); d->stringModel->setStringList(stringList << item); d->stringModel->sort(0); } QStringList ModelCompleter::items() const { return d->stringModel->stringList(); } void ModelCompleter::slotDelayedModelTimer() { QStringList stringList = d->idToTextHash.values(); stringList.removeDuplicates(); stringList.sort(); d->stringModel->setStringList(stringList); } void ModelCompleter::slotRowsInserted(const QModelIndex& parent, int start, int end) { for (int i = start ; i <= end ; ++i) { // this cannot work if this is called from rowsAboutToBeInserted // because then the model doesn't know the index yet. So never do this // ;) + const QModelIndex child = d->model->index(i, 0, parent); if (child.isValid()) { sync(d->model, child); } else { qCDebug(DIGIKAM_WIDGETS_LOG) << "inserted rows are not valid for parent" << parent << parent.data(d->displayRole).toString() << "and child" << child; } } d->delayedModelTimer->start(); } void ModelCompleter::slotRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) { for (int i = start ; i <= end ; ++i) { QModelIndex index = d->model->index(i, 0, parent); if (!index.isValid()) { qCDebug(DIGIKAM_WIDGETS_LOG) << "Received an invalid index to be removed"; continue; } int id = index.data(d->uniqueIdRole).toInt(); if (d->idToTextHash.contains(id)) { QString itemName = d->idToTextHash.value(id); d->idToTextHash.remove(id); + // only delete an item in the completion object if there is no other // item with the same display name + if (d->idToTextHash.keys(itemName).isEmpty()) { d->delayedModelTimer->start(); } } else { qCWarning(DIGIKAM_WIDGETS_LOG) << "idToTextHash seems to be out of sync with the model." << "There is no entry for model index" << index; } } } void ModelCompleter::slotModelReset() { sync(d->model); } void ModelCompleter::slotDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { for (int row = topLeft.row() ; row <= bottomRight.row() ; ++row) { if (!d->model->hasIndex(row, topLeft.column(), topLeft.parent())) { qCDebug(DIGIKAM_WIDGETS_LOG) << "Got wrong change event for index with row" << row << ", column" << topLeft.column() << "and parent" << topLeft.parent() << "in model" << d->model << ". Ignoring it."; continue; } QModelIndex index = d->model->index(row, topLeft.column(), topLeft.parent()); if (!index.isValid()) { qCDebug(DIGIKAM_WIDGETS_LOG) << "illegal index in changed data"; continue; } int id = index.data(d->uniqueIdRole).toInt(); QString itemName = index.data(d->displayRole).toString(); d->idToTextHash[id] = itemName; d->delayedModelTimer->start(); } } void ModelCompleter::sync(QAbstractItemModel* const model) { d->idToTextHash.clear(); for (int i = 0 ; i < model->rowCount() ; ++i) { const QModelIndex index = model->index(i, 0); sync(model, index); } d->delayedModelTimer->start(); } void ModelCompleter::sync(QAbstractItemModel* const model, const QModelIndex& index) { QString itemName = index.data(d->displayRole).toString(); d->idToTextHash.insert(index.data(d->uniqueIdRole).toInt(), itemName); for (int i = 0 ; i < model->rowCount(index) ; ++i) { const QModelIndex child = model->index(i, 0, index); sync(model, child); } } void ModelCompleter::slotHighlighted(const QModelIndex& index) { if (index.isValid()) { QString itemName = index.data().toString(); if (d->idToTextHash.values().count(itemName) == 1) { emit highlighted(d->idToTextHash.key(itemName)); } } } } // namespace Digikam diff --git a/core/libs/widgets/common/modelcompleter.h b/core/libs/widgets/common/modelcompleter.h index 0ca1ca5559..95213493c9 100644 --- a/core/libs/widgets/common/modelcompleter.h +++ b/core/libs/widgets/common/modelcompleter.h @@ -1,92 +1,92 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-06-13 * Description : A QCompleter for AbstractAlbumModels * * Copyright (C) 2007-2020 by Gilles Caulier * Copyright (C) 2009-2010 by Johannes Wienke * Copyright (C) 2010-2011 by Marcel Wiesweg * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_MODEL_COMPLETER_H #define DIGIKAM_MODEL_COMPLETER_H // QT includes #include #include #include // Local includes #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT ModelCompleter : public QCompleter { Q_OBJECT public: explicit ModelCompleter(QObject* const parent = nullptr); ~ModelCompleter(); /** * If the given model is != null, the model is used to populate the * completion for this text field. * * @param model to fill from or null for manual mode * @param uniqueIdRole a role for which the model will return a unique integer for each entry * @param displayRole the role to retrieve the text for completion, default is Qt::DisplayRole. */ void setItemModel(QAbstractItemModel* const model, int uniqueIdRole, int displayRole = Qt::DisplayRole); QAbstractItemModel* itemModel() const; void addItem(const QString& item); - QStringList items() const; + QStringList items() const; Q_SIGNALS: void highlighted(int albumId); void activated(); private Q_SLOTS: void slotDelayedModelTimer(); void slotHighlighted(const QModelIndex& index); void slotRowsInserted(const QModelIndex& parent, int start, int end); void slotRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end); void slotDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); void slotModelReset(); private: void sync(QAbstractItemModel* const model); void sync(QAbstractItemModel* const model, const QModelIndex& index); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_MODEL_COMPLETER_H diff --git a/core/libs/widgets/common/searchtextbar.cpp b/core/libs/widgets/common/searchtextbar.cpp index 00de8e52ff..de30d81a7d 100644 --- a/core/libs/widgets/common/searchtextbar.cpp +++ b/core/libs/widgets/common/searchtextbar.cpp @@ -1,329 +1,334 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-11-25 * Description : a bar used to search a string. * * Copyright (C) 2007-2020 by Gilles Caulier * Copyright (C) 2009-2010 by Johannes Wienke * * 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, 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. * * ============================================================ */ #include "searchtextbar.h" // Qt includes #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "digikam_debug.h" #include "albumfiltermodel.h" namespace Digikam { bool operator==(const SearchTextSettings& a, const SearchTextSettings& b) { - return a.caseSensitive == b.caseSensitive && a.text == b.text; + return ((a.caseSensitive == b.caseSensitive) && (a.text == b.text)); } class Q_DECL_HIDDEN SearchTextBar::Private { public: explicit Private() : optionAutoCompletionModeEntry(QLatin1String("AutoCompletionMode")), optionCaseSensitiveEntry(QLatin1String("CaseSensitive")), textQueryCompletion(false), hasCaseSensitive(true), highlightOnResult(true), hasResultColor(200, 255, 200), hasNoResultColor(255, 200, 200), completer(nullptr), filterModel(nullptr) { } QString optionAutoCompletionModeEntry; QString optionCaseSensitiveEntry; bool textQueryCompletion; bool hasCaseSensitive; bool highlightOnResult; QColor hasResultColor; QColor hasNoResultColor; ModelCompleter* completer; QPointer filterModel; SearchTextSettings settings; }; SearchTextBar::SearchTextBar(QWidget* const parent, const QString& name, const QString& msg) : QLineEdit(parent), StateSavingObject(this), d(new Private) { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); setObjectName(name + QLatin1String(" Search Text Tool")); setClearButtonEnabled(true); setPlaceholderText(msg.isNull() ? i18n("Search...") : msg); d->completer = new ModelCompleter(this); setCompleter(d->completer); connect(this, SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString))); connect(d->completer, static_cast(&ModelCompleter::activated), [this](void){ emit completerActivated(); }); connect(d->completer, static_cast(&ModelCompleter::highlighted), [this](const int albumId){ emit completerHighlighted(albumId); }); loadState(); } SearchTextBar::~SearchTextBar() { saveState(); delete d; } void SearchTextBar::doLoadState() { KConfigGroup group = getConfigGroup(); completer()->setCompletionMode((QCompleter::CompletionMode)group.readEntry(entryName(d->optionAutoCompletionModeEntry), (int)QCompleter::PopupCompletion)); d->settings.caseSensitive = (Qt::CaseSensitivity)group.readEntry(entryName(d->optionCaseSensitiveEntry), (int)Qt::CaseInsensitive); setIgnoreCase(d->settings.caseSensitive == Qt::CaseInsensitive); } void SearchTextBar::doSaveState() { KConfigGroup group = getConfigGroup(); if (completer()->completionMode() != QCompleter::PopupCompletion) { group.writeEntry(entryName(d->optionAutoCompletionModeEntry), (int)completer()->completionMode()); } else { group.deleteEntry(entryName(d->optionAutoCompletionModeEntry)); } group.writeEntry(entryName(d->optionCaseSensitiveEntry), (int)d->settings.caseSensitive); group.sync(); } void SearchTextBar::setTextQueryCompletion(bool b) { d->textQueryCompletion = b; } bool SearchTextBar::hasTextQueryCompletion() const { return d->textQueryCompletion; } void SearchTextBar::setHighlightOnResult(bool highlight) { d->highlightOnResult = highlight; if (!highlight) { setPalette(QPalette()); } } void SearchTextBar::setModel(QAbstractItemModel* model, int uniqueIdRole, int displayRole) { d->completer->setItemModel(model, uniqueIdRole, displayRole); } void SearchTextBar::setModel(AbstractAlbumModel* model) { d->completer->setItemModel(model, AbstractAlbumModel::AlbumIdRole, AbstractAlbumModel::AlbumTitleRole); } void SearchTextBar::setFilterModel(AlbumFilterModel* filterModel) { // if there already was a model, disconnect from this model + if (d->filterModel) { disconnect(d->filterModel); } d->filterModel = filterModel; // connect to new model if desired + if (d->filterModel) { connect(this, SIGNAL(signalSearchTextSettings(SearchTextSettings)), d->filterModel, SLOT(setSearchTextSettings(SearchTextSettings))); connect(d->filterModel, SIGNAL(hasSearchResult(bool)), this, SLOT(slotSearchResult(bool))); } } SearchTextBar::HighlightState SearchTextBar::getCurrentHighlightState() const { if (palette() == QPalette()) { return NEUTRAL; } else if (palette().color(QPalette::Active, QPalette::Base) == d->hasResultColor) { return HAS_RESULT; } else if (palette().color(QPalette::Active, QPalette::Base) == d->hasNoResultColor) { return NO_RESULT; } qCDebug(DIGIKAM_WIDGETS_LOG) << "Impossible highlighting state"; return NEUTRAL; } void SearchTextBar::setCaseSensitive(bool b) { d->hasCaseSensitive = b; // reset settings if selecting case sensitivity is not allowed + if (!b) { d->settings.caseSensitive = Qt::CaseInsensitive; } // re-emit signal with changed settings + if (!text().isEmpty()) { emit signalSearchTextSettings(d->settings); } } bool SearchTextBar::hasCaseSensitive() const { return d->hasCaseSensitive; } void SearchTextBar::setSearchTextSettings(const SearchTextSettings& settings) { d->settings = settings; } SearchTextSettings SearchTextBar::searchTextSettings() const { return d->settings; } ModelCompleter* SearchTextBar::completerModel() const { return d->completer; } void SearchTextBar::slotTextChanged(const QString& text) { if (text.isEmpty()) { setPalette(QPalette()); } d->settings.text = text; emit signalSearchTextSettings(d->settings); } void SearchTextBar::slotSearchResult(bool match) { // only highlight if text is not empty or highlighting is disabled. + if (text().isEmpty() || !d->highlightOnResult) { setPalette(QPalette()); return; } QPalette pal = palette(); pal.setColor(QPalette::Active, QPalette::Base, match ? d->hasResultColor : d->hasNoResultColor); pal.setColor(QPalette::Active, QPalette::Text, Qt::black); setPalette(pal); } void SearchTextBar::contextMenuEvent(QContextMenuEvent* e) { QAction* cs = nullptr; QMenu* const menu = createStandardContextMenu(); if (d->hasCaseSensitive) { cs = menu->addAction(i18n("Case sensitive")); cs->setCheckable(true); cs->setChecked(d->settings.caseSensitive == Qt::CaseInsensitive ? false : true); } menu->exec(e->globalPos()); if (d->hasCaseSensitive) { setIgnoreCase(!cs->isChecked()); } delete menu; } void SearchTextBar::setIgnoreCase(bool ignore) { if (hasCaseSensitive()) { if (ignore) { completer()->setCaseSensitivity(Qt::CaseInsensitive); d->settings.caseSensitive = Qt::CaseInsensitive; } else { completer()->setCaseSensitivity(Qt::CaseSensitive); d->settings.caseSensitive = Qt::CaseSensitive; } } else { completer()->setCaseSensitivity(Qt::CaseInsensitive); d->settings.caseSensitive = Qt::CaseInsensitive; } emit signalSearchTextSettings(d->settings); } } // namespace Digikam diff --git a/core/libs/widgets/common/searchtextbar.h b/core/libs/widgets/common/searchtextbar.h index c763a687a0..dfa4c7464c 100644 --- a/core/libs/widgets/common/searchtextbar.h +++ b/core/libs/widgets/common/searchtextbar.h @@ -1,206 +1,206 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-11-25 * Description : a bar used to search a string. * * Copyright (C) 2007-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_SEARCH_TEXT_BAR_H #define DIGIKAM_SEARCH_TEXT_BAR_H // Qt includes #include #include #include // Local includes #include "digikam_export.h" #include "modelcompleter.h" #include "statesavingobject.h" namespace Digikam { class AbstractAlbumModel; class AlbumFilterModel; class DIGIKAM_EXPORT SearchTextSettings { public: SearchTextSettings() + : caseSensitive(Qt::CaseInsensitive) { - caseSensitive = Qt::CaseInsensitive; } Qt::CaseSensitivity caseSensitive; QString text; }; bool DIGIKAM_EXPORT operator==(const SearchTextSettings& a, const SearchTextSettings& b); /** * A text input for searching entries with visual feedback. * Can be used on QAbstractItemModels. * * @author Gilles Caulier */ class DIGIKAM_EXPORT SearchTextBar : public QLineEdit, public StateSavingObject { Q_OBJECT public: /** * Possible highlighting states a SearchTextBar can have. */ enum HighlightState { /** * No highlighting at all. Background is colored in a neutral way * according to the theme. */ NEUTRAL, /** * The background color of the text input indicates that a result was * found. */ HAS_RESULT, /** * The background color indicates that no result was found. */ NO_RESULT }; public: explicit SearchTextBar(QWidget* const parent, const QString& name, const QString& msg=QString()); ~SearchTextBar(); void setTextQueryCompletion(bool b); - bool hasTextQueryCompletion() const; + bool hasTextQueryCompletion() const; /** * Tells whether highlighting for found search results shall be used or not * (green and red). * * Default behavior has highlighting enabled. * * @param highlight true activates green and red highlighting, * with false the normal widget background * color will be displayed always */ void setHighlightOnResult(bool highlight); /** * If the given model is != null, the model is used to populate the * completion for this text field. * * @param model to fill from or null for manual mode * @param uniqueIdRole a role for which the model will return a unique integer for each entry * @param displayRole the role to retrieve the text for completion, default is Qt::DisplayRole. */ void setModel(QAbstractItemModel* model, int uniqueIdRole, int displayRole = Qt::DisplayRole); void setModel(AbstractAlbumModel* model); /** * Sets the filter model this text bar shall use to invoke filtering on and * reading the result for highlighting from. * * @param filterModel filter model to use for filtering. null * means there is no model to use and external * connections need to be created with * signalSearchTextSettings and slotSearchResult */ void setFilterModel(AlbumFilterModel* filterModel); /** * Tells the current highlighting state of the text input indicated via the * background color. * * @return current highlight state */ - HighlightState getCurrentHighlightState() const; + HighlightState getCurrentHighlightState() const; /** * Indicate whether this search text bar can be toggled to between case- * sensitive and -insensitive or if always case-insensitive shall be * used. * * @param b if true the user can decide the toggle between * case sensitivity, on false every search is case- * insensitive */ void setCaseSensitive(bool b); - bool hasCaseSensitive() const; + bool hasCaseSensitive() const; void setSearchTextSettings(const SearchTextSettings& settings); - SearchTextSettings searchTextSettings() const; - ModelCompleter* completerModel() const; + SearchTextSettings searchTextSettings() const; + ModelCompleter* completerModel() const; Q_SIGNALS: void signalSearchTextSettings(const SearchTextSettings& settings); void completerHighlighted(int albumId); void completerActivated(); public Q_SLOTS: void slotSearchResult(bool match); private Q_SLOTS: void slotTextChanged(const QString&); protected: - virtual void doLoadState() override; - virtual void doSaveState() override; + virtual void doLoadState() override; + virtual void doSaveState() override; private: void contextMenuEvent(QContextMenuEvent* e) override; /** * If hasCaseSensitive returns true this tells the search * text bar whether to ignore case or not. * * @param ignore if true, case is ignored in the emitted * search text settings and the completion */ void setIgnoreCase(bool ignore); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_SEARCH_TEXT_BAR_H diff --git a/core/libs/widgets/files/dbinaryiface.cpp b/core/libs/widgets/files/dbinaryiface.cpp index 7d63cb8191..b3a41d2518 100644 --- a/core/libs/widgets/files/dbinaryiface.cpp +++ b/core/libs/widgets/files/dbinaryiface.cpp @@ -1,397 +1,404 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-12-23 * Description : Autodetect binary program and version * * Copyright (C) 2009-2020 by Gilles Caulier * Copyright (C) 2012-2016 by Benjamin Girault * * 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, 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. * * ============================================================ */ #include "dbinaryiface.h" // Qt includes #include #include // KDE includes #include #include #include // Local includes #include "dfiledialog.h" #include "digikam_debug.h" #include "digikam_config.h" #include "digikam_globals.h" namespace Digikam { DBinaryIface::DBinaryIface(const QString& binaryName, const QString& projectName, const QString& url, const QString& toolName, const QStringList& args, const QString& desc) : m_checkVersion(false), m_headerStarts(QLatin1String("")), m_headerLine(0), m_minimalVersion(QLatin1String("")), m_configGroup(!toolName.isEmpty() ? QString::fromLatin1("%1 Settings").arg(toolName) : QLatin1String("")), m_binaryBaseName(goodBaseName(binaryName)), m_binaryArguments(args), m_projectName(projectName), m_url(QUrl(url)), m_isFound(false), m_developmentVersion(false), m_version(QLatin1String("")), m_pathDir(QLatin1String("")), m_description(desc), m_pathWidget(nullptr), m_binaryLabel(nullptr), m_versionLabel(nullptr), m_pathButton(nullptr), m_downloadButton(nullptr), m_lineEdit(nullptr), m_statusIcon(nullptr) { } DBinaryIface::DBinaryIface(const QString& binaryName, const QString& minimalVersion, const QString& header, const int headerLine, const QString& projectName, const QString& url, const QString& toolName, const QStringList& args, const QString& desc) : m_checkVersion(true), m_headerStarts(header), m_headerLine(headerLine), m_minimalVersion(minimalVersion), m_configGroup(!toolName.isEmpty() ? QString::fromLatin1("%1 Settings").arg(toolName) : QLatin1String("")), m_binaryBaseName(goodBaseName(binaryName)), m_binaryArguments(args), m_projectName(projectName), m_url(QUrl(url)), m_isFound(false), m_developmentVersion(false), m_version(QLatin1String("")), m_pathDir(QLatin1String("")), m_description(desc), m_pathWidget(nullptr), m_binaryLabel(nullptr), m_versionLabel(nullptr), m_pathButton(nullptr), m_downloadButton(nullptr), m_lineEdit(nullptr), m_statusIcon(nullptr) { } DBinaryIface::~DBinaryIface() { } const QString& DBinaryIface::version() const { return m_version; } bool DBinaryIface::versionIsRight() const { if (!m_checkVersion) + { return true; + } QRegExp reg(QLatin1String("^(\\d*[.]\\d*)")); version().indexOf(reg); float floatVersion = reg.capturedTexts()[0].toFloat(); return (!version().isNull() && isFound() && floatVersion >= minimalVersion().toFloat()); } bool DBinaryIface::versionIsRight(const float customVersion) const { if (!m_checkVersion) + { return true; + } QRegExp reg(QLatin1String("^(\\d*[.]\\d*)")); version().indexOf(reg); float floatVersion = reg.capturedTexts()[0].toFloat(); qCDebug(DIGIKAM_GENERAL_LOG) << "Found (" << isFound() << ") :: Version : " << version() << "(" << floatVersion << ") [" << customVersion << "]"; return (!version().isNull() && isFound() && floatVersion >= customVersion); } QString DBinaryIface::findHeader(const QStringList& output, const QString& header) const { foreach (const QString& s, output) { if (s.startsWith(header)) return s; } return QString(); } bool DBinaryIface::parseHeader(const QString& output) { QString firstLine = output.section(QLatin1Char('\n'), m_headerLine, m_headerLine); qCDebug(DIGIKAM_GENERAL_LOG) << path() << " help header line: \n" << firstLine; if (firstLine.startsWith(m_headerStarts)) { QString version = firstLine.remove(0, m_headerStarts.length()); if (version.startsWith(QLatin1String("Pre-Release "))) { version.remove(QLatin1String("Pre-Release ")); // Special case with Hugin beta. m_developmentVersion = true; } setVersion(version); return true; } return false; } void DBinaryIface::setVersion(QString& version) { QRegExp versionRegExp(QLatin1String("\\d*(\\.\\d+)*")); version.indexOf(versionRegExp); m_version = versionRegExp.capturedTexts()[0]; } void DBinaryIface::slotNavigateAndCheck() { QUrl start; if (isValid() && !m_pathDir.isEmpty()) { start = QUrl::fromLocalFile(m_pathDir); } else { #if defined Q_OS_OSX start = QUrl::fromLocalFile(QLatin1String("/Applications/")); #elif defined Q_OS_WIN start = QUrl::fromLocalFile(QLatin1String("C:/Program Files/")); #else start = QUrl::fromLocalFile(QLatin1String("/usr/bin/")); #endif } - QString f = DFileDialog::getOpenFileName(nullptr, i18n("Navigate to %1", m_binaryBaseName), - start.toLocalFile(), - m_binaryBaseName); + QString f = DFileDialog::getOpenFileName(nullptr, i18n("Navigate to %1", m_binaryBaseName), + start.toLocalFile(), + m_binaryBaseName); QString dir = QUrl::fromLocalFile(f).adjusted(QUrl::RemoveFilename).toLocalFile(); m_searchPaths << dir; if (checkDirForPath(dir)) { emit signalSearchDirectoryAdded(dir); } } void DBinaryIface::slotAddPossibleSearchDirectory(const QString& dir) { if (!isValid()) { m_searchPaths << dir; checkDirForPath(dir); } else { m_searchPaths << dir; } } void DBinaryIface::slotAddSearchDirectory(const QString& dir) { m_searchPaths << dir; checkDirForPath(dir); // Forces the use of that directory } QString DBinaryIface::readConfig() { if (m_configGroup.isEmpty()) + { return QLatin1String(""); + } KConfig config; KConfigGroup group = config.group(m_configGroup); return group.readPathEntry(QString::fromUtf8("%1Binary").arg(m_binaryBaseName), QLatin1String("")); } void DBinaryIface::writeConfig() { if (m_configGroup.isEmpty()) return; KConfig config; KConfigGroup group = config.group(m_configGroup); group.writePathEntry(QString::fromUtf8("%1Binary").arg(m_binaryBaseName), m_pathDir); } QString DBinaryIface::path(const QString& dir) const { if (dir.isEmpty()) { return baseName(); } if (dir.endsWith(QLatin1Char('/'))) { return QString::fromUtf8("%1%2").arg(dir).arg(baseName()); } return QString::fromUtf8("%1%2%3").arg(dir).arg(QLatin1Char('/')).arg(baseName()); } void DBinaryIface::setup(const QString& prev) { QString previousDir = prev; if (!previousDir.isEmpty()) { m_searchPaths << previousDir; checkDirForPath(previousDir); return; } - previousDir = readConfig(); + previousDir = readConfig(); m_searchPaths << previousDir; checkDirForPath(previousDir); if ((!previousDir.isEmpty()) && !isValid()) { m_searchPaths << QLatin1String(""); checkDirForPath(QLatin1String("")); } } bool DBinaryIface::checkDirForPath(const QString& possibleDir) { bool ret = false; QString possiblePath = path(possibleDir); qCDebug(DIGIKAM_GENERAL_LOG) << "Testing " << possiblePath << "..."; if (m_binaryArguments.isEmpty()) { if (QFile::exists(possiblePath)) { m_pathDir = possibleDir; m_isFound = true; writeConfig(); qCDebug(DIGIKAM_GENERAL_LOG) << "Found " << path(); - ret = true; + ret = true; } } else { QProcess process; process.setProcessChannelMode(QProcess::MergedChannels); process.setProcessEnvironment(adjustedEnvironmentForAppImage()); process.start(possiblePath, m_binaryArguments); bool val = process.waitForFinished(); if (val && (process.error() != QProcess::FailedToStart)) { m_isFound = true; if (m_checkVersion) { QString stdOut = QString::fromUtf8(process.readAllStandardOutput()); if (parseHeader(stdOut)) { m_pathDir = possibleDir; writeConfig(); qCDebug(DIGIKAM_GENERAL_LOG) << "Found " << path() << " version: " << version(); - ret = true; + ret = true; } else { // TODO: do something if the version is not right or not found } } else { m_pathDir = possibleDir; writeConfig(); qCDebug(DIGIKAM_GENERAL_LOG) << "Found " << path(); - ret = true; + ret = true; } } } emit signalBinaryValid(); + return ret; } bool DBinaryIface::recheckDirectories() { if (isValid()) { // No need for recheck if it is already valid... return true; } foreach (const QString& dir, m_searchPaths) { checkDirForPath(dir); if (isValid()) { return true; } } return false; } QString DBinaryIface::goodBaseName(const QString& b) { #ifdef Q_OS_WIN if (b.endsWith(QLatin1String(".jar"))) // Special case if we check a java archive. return b; else return b + QLatin1String(".exe"); #else return b; #endif // Q_OS_WIN } } // namespace Digikam diff --git a/core/libs/widgets/files/dbinarysearch.cpp b/core/libs/widgets/files/dbinarysearch.cpp index 7de098fd47..53dc1eb0b8 100644 --- a/core/libs/widgets/files/dbinarysearch.cpp +++ b/core/libs/widgets/files/dbinarysearch.cpp @@ -1,196 +1,215 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-01-05 * Description : a widget to find missing binaries. * * Copyright (C) 2012-2012 by Benjamin Girault * * 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, 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. * * ============================================================ */ #include "dbinarysearch.h" // Qt includes #include #include // KDE includes #include // Local includes #include "digikam_debug.h" namespace Digikam { class Q_DECL_HIDDEN DBinarySearch::Private { public: explicit Private() + : downloadLabel(nullptr) { - downloadLabel = nullptr; } QVector binaryIfaces; QVector items; QLabel* downloadLabel; }; DBinarySearch::DBinarySearch(QWidget* const parent) : QTreeWidget(parent), d(new Private) { setIconSize(QSize(16, 16)); setAlternatingRowColors(true); setSelectionMode(QAbstractItemView::NoSelection); setSortingEnabled(false); setAllColumnsShowFocus(true); setRootIsDecorated(false); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setColumnCount(5); setHeaderLabels(QStringList() << QLatin1String("") << i18n("Binary") << i18n("Version") << QLatin1String("") << QLatin1String("")); header()->setSectionResizeMode(Status, QHeaderView::ResizeToContents); header()->setSectionResizeMode(Binary, QHeaderView::Stretch); header()->setSectionResizeMode(Version, QHeaderView::Stretch); header()->setSectionResizeMode(Button, QHeaderView::Stretch); header()->setSectionResizeMode(Link, QHeaderView::Stretch); d->downloadLabel = new QLabel(parentWidget()); - qobject_cast(parentWidget()->layout())->addWidget(this, 0, 0); + QGridLayout* const layout = qobject_cast(parentWidget()->layout()); + + if (layout) + { + layout->addWidget(this, 0, 0); + } } DBinarySearch::~DBinarySearch() { delete d; } void DBinarySearch::addBinary(DBinaryIface& binary) { delete d->downloadLabel; binary.recheckDirectories(); d->binaryIfaces.append(&binary); d->items.append(new QTreeWidgetItem()); - QTreeWidgetItem* const item = d->items[d->items.size() - 1]; + QTreeWidgetItem* const item = d->items[d->items.size() - 1]; item->setIcon(Status, QIcon::fromTheme(QLatin1String("dialog-cancel")).pixmap(16, 16)); item->setText(Binary, binary.baseName()); item->setText(Version, binary.version()); item->setToolTip(Binary, binary.description()); item->setToolTip(Status, i18n("Binary not found.")); item->setToolTip(Version, i18n("Minimal version number required for this binary is %1", binary.minimalVersion())); insertTopLevelItem(d->binaryIfaces.size() - 1, item); QPushButton* const findButton = new QPushButton(i18n("Find")); setItemWidget(item, Button, findButton); QLabel* const downloadLabel = new QLabel(i18n(" or download", binary.url().url())); downloadLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse); downloadLabel->setOpenExternalLinks(true); setItemWidget(item, Link, downloadLabel); // Starts a dialog to find the binary + connect(findButton, SIGNAL(clicked(bool)), &binary, SLOT(slotNavigateAndCheck())); // Rechecks full validity when a binary is found and valid + connect(&binary, SIGNAL(signalBinaryValid()), this, SLOT(slotAreBinariesFound())); // Scans (if no binary were found) a new directory where a binary was found + connect(&binary, SIGNAL(signalSearchDirectoryAdded(QString)), this, SIGNAL(signalAddPossibleDirectory(QString))); connect(this, SIGNAL(signalAddPossibleDirectory(QString)), &binary, SLOT(slotAddPossibleSearchDirectory(QString))); // Force scan of a new directory + connect(this, SIGNAL(signalAddDirectory(QString)), &binary, SLOT(slotAddSearchDirectory(QString))); d->downloadLabel = new QLabel(i18n( "

Warning: Some necessary binaries have not been found on " "your system. If you have these binaries installed, please click the 'Find' button to locate them on your " "system, otherwise please download and install them to proceed.

"), parentWidget()); QGridLayout* const layout = qobject_cast(parentWidget()->layout()); - layout->addWidget(d->downloadLabel, layout->rowCount(), 0); + + if (layout) + { + layout->addWidget(d->downloadLabel, layout->rowCount(), 0); + } + d->downloadLabel->setContentsMargins(20, 20, 20, 20); d->downloadLabel->setWordWrap(true); d->downloadLabel->hide(); } void DBinarySearch::addDirectory(const QString& dir) { emit signalAddPossibleDirectory(dir); } bool DBinarySearch::allBinariesFound() { bool ret = true; foreach (DBinaryIface* const binary, d->binaryIfaces) { int index = d->binaryIfaces.indexOf(binary); if (binary->isValid()) { if (!binary->developmentVersion()) { d->items[index]->setIcon(Status, QIcon::fromTheme(QLatin1String("dialog-ok-apply")).pixmap(16, 16)); d->items[index]->setToolTip(Status, QString()); } else { d->items[index]->setIcon(Status, QIcon::fromTheme(QLatin1String("dialog-warning")).pixmap(16, 16)); d->items[index]->setToolTip(Status, i18n("A development version has been detect. " "There is no guarantee on the behavior of this binary.")); d->downloadLabel->show(); } d->items[index]->setText(Version, binary->version()); - qobject_cast(itemWidget(d->items[index], Button))->setText(i18n("Change")); + QPushButton* const btn = qobject_cast(itemWidget(d->items[index], Button)); + + if (btn) + { + btn->setText(i18n("Change")); + } } else { ret = false; } } if (ret) { d->downloadLabel->hide(); } return ret; } void DBinarySearch::slotAreBinariesFound() { bool allFound = allBinariesFound(); emit signalBinariesFound(allFound); qCDebug(DIGIKAM_GENERAL_LOG) << "All Binaries Found : " << allFound; } } // namespace Digikam diff --git a/core/libs/widgets/files/dfiledialog.cpp b/core/libs/widgets/files/dfiledialog.cpp index f6de8ef615..ac76466448 100644 --- a/core/libs/widgets/files/dfiledialog.cpp +++ b/core/libs/widgets/files/dfiledialog.cpp @@ -1,167 +1,177 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * date : 2017-07-04 * Description : wrapper for the QFileDialog * * Copyright (C) 2014-2020 by Gilles Caulier * Copyright (C) 2017 by Maik Qualmann * * 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, 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. * * ============================================================ */ #include "dfiledialog.h" // Qt includes #include // KDE includes #include #include // Local includes #include "digikam_config.h" namespace Digikam { DFileDialog::DFileDialog(QWidget* const parent, Qt::WindowFlags flags) : QFileDialog(parent, flags) { setOption(getNativeFileDialogOption()); } DFileDialog::DFileDialog(QWidget* const parent, const QString& caption, const QString& directory, const QString& filter) : QFileDialog(parent, caption, directory, filter) { setOption(getNativeFileDialogOption()); } DFileDialog::~DFileDialog() { } QString DFileDialog::getExistingDirectory(QWidget* const parent, const QString& caption, const QString& dir, Options options) { options |= getNativeFileDialogOption(); + return QFileDialog::getExistingDirectory(parent, caption, dir, options); } QUrl DFileDialog::getExistingDirectoryUrl(QWidget* const parent, const QString& caption, const QUrl& dir, Options options, const QStringList& supportedSchemes) { options |= getNativeFileDialogOption(); + return QFileDialog::getExistingDirectoryUrl(parent, caption, dir, options, supportedSchemes); } QString DFileDialog::getOpenFileName(QWidget* const parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, Options options) { options |= getNativeFileDialogOption(); + return QFileDialog::getOpenFileName(parent, caption, dir, filter, selectedFilter, options); } QStringList DFileDialog::getOpenFileNames(QWidget* const parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, Options options) { options |= getNativeFileDialogOption(); + return QFileDialog::getOpenFileNames(parent, caption, dir, filter, selectedFilter, options); } QUrl DFileDialog::getOpenFileUrl(QWidget* const parent, const QString& caption, const QUrl& dir, const QString& filter, QString* selectedFilter, Options options, const QStringList& supportedSchemes) { options |= getNativeFileDialogOption(); + return QFileDialog::getOpenFileUrl(parent, caption, dir, filter, selectedFilter, options, supportedSchemes); } QList DFileDialog::getOpenFileUrls(QWidget* const parent, const QString& caption, const QUrl& dir, const QString& filter, QString* selectedFilter, Options options, const QStringList& supportedSchemes) { options |= getNativeFileDialogOption(); + return QFileDialog::getOpenFileUrls(parent, caption, dir, filter, selectedFilter, options, supportedSchemes); } QString DFileDialog::getSaveFileName(QWidget* const parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, Options options) { options |= getNativeFileDialogOption(); + return QFileDialog::getSaveFileName(parent, caption, dir, filter, selectedFilter, options); } QUrl DFileDialog::getSaveFileUrl(QWidget* const parent, const QString& caption, const QUrl& dir, const QString& filter, QString* selectedFilter, Options options, const QStringList& supportedSchemes) { options |= getNativeFileDialogOption(); + return QFileDialog::getSaveFileUrl(parent, caption, dir, filter, selectedFilter, options, supportedSchemes); } QFileDialog::Option DFileDialog::getNativeFileDialogOption() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group; if (qApp->applicationName() == QLatin1String("digikam")) { group = config->group(QLatin1String("General Settings")); } else { group = config->group(QLatin1String("ImageViewer Settings")); } #ifdef Q_OS_OSX bool useNativeFileDialog = group.readEntry(QLatin1String("Use Native File Dialog"), true); #else bool useNativeFileDialog = group.readEntry(QLatin1String("Use Native File Dialog"), false); #endif if (useNativeFileDialog) + { return (QFileDialog::Option)0; + } return QFileDialog::DontUseNativeDialog; } } // namespace Digikam diff --git a/core/libs/widgets/files/dfiledialog.h b/core/libs/widgets/files/dfiledialog.h index d87048f73a..93783491de 100644 --- a/core/libs/widgets/files/dfiledialog.h +++ b/core/libs/widgets/files/dfiledialog.h @@ -1,106 +1,117 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * date : 2017-07-04 * Description : wrapper for the QFileDialog * * Copyright (C) 2014-2020 by Gilles Caulier * Copyright (C) 2017 by Maik Qualmann * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_DFILE_DIALOG_H #define DIGIKAM_DFILE_DIALOG_H // Qt includes #include // Local includes #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT DFileDialog : public QFileDialog { Q_OBJECT public: - explicit DFileDialog(QWidget* const parent, Qt::WindowFlags flags); - explicit DFileDialog(QWidget* const parent = nullptr, const QString& caption = QString(), - const QString& directory = QString(), - const QString& filter = QString()); + explicit DFileDialog(QWidget* const parent, + Qt::WindowFlags flags); + + explicit DFileDialog(QWidget* const parent = nullptr, + const QString& caption = QString(), + const QString& directory = QString(), + const QString& filter = QString()); virtual ~DFileDialog(); - static QString getExistingDirectory(QWidget* const parent = nullptr, const QString& caption = QString(), - const QString& dir = QString(), - Options options = ShowDirsOnly); - - static QUrl getExistingDirectoryUrl(QWidget* const parent = nullptr, const QString& caption = QString(), - const QUrl& dir = QUrl(), - Options options = ShowDirsOnly, - const QStringList& supportedSchemes = QStringList()); - - static QString getOpenFileName(QWidget* const parent = nullptr, const QString& caption = QString(), - const QString& dir = QString(), - const QString& filter = QString(), - QString* selectedFilter = nullptr, - Options options = Options()); - - static QStringList getOpenFileNames(QWidget* const parent = nullptr, const QString& caption = QString(), - const QString& dir = QString(), - const QString& filter = QString(), - QString* selectedFilter = nullptr, - Options options = Options()); - - static QUrl getOpenFileUrl(QWidget* const parent = nullptr, const QString& caption = QString(), - const QUrl& dir = QUrl(), - const QString& filter = QString(), - QString* selectedFilter = nullptr, - Options options = Options(), - const QStringList& supportedSchemes = QStringList()); - - static QList getOpenFileUrls(QWidget* const parent = nullptr, const QString& caption = QString(), - const QUrl& dir = QUrl(), - const QString& filter = QString(), - QString* selectedFilter = nullptr, - Options options = Options(), - const QStringList& supportedSchemes = QStringList()); - - static QString getSaveFileName(QWidget* const parent = nullptr, const QString& caption = QString(), - const QString& dir = QString(), - const QString& filter = QString(), - QString* selectedFilter = nullptr, - Options options = Options()); - - static QUrl getSaveFileUrl(QWidget* const parent = nullptr, const QString& caption = QString(), - const QUrl& dir = QUrl(), - const QString& filter = QString(), - QString* selectedFilter = nullptr, - Options options = Options(), - const QStringList& supportedSchemes = QStringList()); + static QString getExistingDirectory(QWidget* const parent = nullptr, + const QString& caption = QString(), + const QString& dir = QString(), + Options options = ShowDirsOnly); + + static QUrl getExistingDirectoryUrl(QWidget* const parent = nullptr, + const QString& caption = QString(), + const QUrl& dir = QUrl(), + Options options = ShowDirsOnly, + const QStringList& supportedSchemes = QStringList()); + + static QString getOpenFileName(QWidget* const parent = nullptr, + const QString& caption = QString(), + const QString& dir = QString(), + const QString& filter = QString(), + QString* selectedFilter = nullptr, + Options options = Options()); + + static QStringList getOpenFileNames(QWidget* const parent = nullptr, + const QString& caption = QString(), + const QString& dir = QString(), + const QString& filter = QString(), + QString* selectedFilter = nullptr, + Options options = Options()); + + static QUrl getOpenFileUrl(QWidget* const parent = nullptr, + const QString& caption = QString(), + const QUrl& dir = QUrl(), + const QString& filter = QString(), + QString* selectedFilter = nullptr, + Options options = Options(), + const QStringList& supportedSchemes = QStringList()); + + static QList getOpenFileUrls(QWidget* const parent = nullptr, + const QString& caption = QString(), + const QUrl& dir = QUrl(), + const QString& filter = QString(), + QString* selectedFilter = nullptr, + Options options = Options(), + const QStringList& supportedSchemes = QStringList()); + + static QString getSaveFileName(QWidget* const parent = nullptr, + const QString& caption = QString(), + const QString& dir = QString(), + const QString& filter = QString(), + QString* selectedFilter = nullptr, + Options options = Options()); + + static QUrl getSaveFileUrl(QWidget* const parent = nullptr, + const QString& caption = QString(), + const QUrl& dir = QUrl(), + const QString& filter = QString(), + QString* selectedFilter = nullptr, + Options options = Options(), + const QStringList& supportedSchemes = QStringList()); private: static QFileDialog::Option getNativeFileDialogOption(); }; } // namespace Digikam #endif // DIGIKAM_DFILE_DIALOG_H diff --git a/core/libs/widgets/files/dfileselector.cpp b/core/libs/widgets/files/dfileselector.cpp index 15ea0b3fd5..961fd62d8e 100644 --- a/core/libs/widgets/files/dfileselector.cpp +++ b/core/libs/widgets/files/dfileselector.cpp @@ -1,152 +1,155 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * date : 2014-09-12 * Description : a file or folder selector widget * * Copyright (C) 2014-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "dfileselector.h" // KDE includes #include // Local includes #include "digikam_debug.h" namespace Digikam { class Q_DECL_HIDDEN DFileSelector::Private { public: explicit Private() + : edit(nullptr), + btn(nullptr), + fdMode(QFileDialog::ExistingFile), + fdOptions(QFileDialog::Options()) { - edit = nullptr; - btn = nullptr; - fdMode = QFileDialog::ExistingFile; - fdOptions = QFileDialog::Options(); } QLineEdit* edit; QPushButton* btn; QFileDialog::FileMode fdMode; QString fdFilter; QString fdTitle; QFileDialog::Options fdOptions; }; DFileSelector::DFileSelector(QWidget* const parent) : DHBox(parent), d(new Private) { d->edit = new QLineEdit(this); d->btn = new QPushButton(i18n("Browse..."), this); setStretchFactor(d->edit, 10); connect(d->btn, SIGNAL(clicked()), this, SLOT(slotBtnClicked())); } DFileSelector::~DFileSelector() { delete d; } QLineEdit* DFileSelector::lineEdit() const { return d->edit; } void DFileSelector::setFileDlgPath(const QString& path) { d->edit->setText(QDir::toNativeSeparators(path)); } QString DFileSelector::fileDlgPath() const { return QDir::fromNativeSeparators(d->edit->text()); } void DFileSelector::setFileDlgMode(QFileDialog::FileMode mode) { d->fdMode = mode; } void DFileSelector::setFileDlgFilter(const QString& filter) { d->fdFilter = filter; } void DFileSelector::setFileDlgTitle(const QString& title) { d->fdTitle = title; } void DFileSelector::setFileDlgOptions(QFileDialog::Options opts) { d->fdOptions = opts; } void DFileSelector::slotBtnClicked() { if (d->fdMode == QFileDialog::ExistingFiles) { qCDebug(DIGIKAM_WIDGETS_LOG) << "Multiple selection is not supported"; return; } // Never pass a parent to File Dialog, else dupplicate dialogs will be shown + DFileDialog* const fileDlg = new DFileDialog; fileDlg->setDirectory(QFileInfo(fileDlgPath()).filePath()); + // It is important to set up the mode first and then the options + fileDlg->setFileMode(d->fdMode); fileDlg->setOptions(d->fdOptions); if (!d->fdFilter.isNull()) { fileDlg->setNameFilter(d->fdFilter); } if (!d->fdTitle.isNull()) { fileDlg->setWindowTitle(d->fdTitle); } emit signalOpenFileDialog(); if (fileDlg->exec() == QDialog::Accepted) { QStringList sel = fileDlg->selectedFiles(); if (!sel.isEmpty()) { setFileDlgPath(sel.first()); emit signalUrlSelected(QUrl::fromLocalFile(sel.first())); } } delete fileDlg; } } // namespace Digikam diff --git a/core/libs/widgets/files/dfileselector.h b/core/libs/widgets/files/dfileselector.h index 487909a172..2c183327ec 100644 --- a/core/libs/widgets/files/dfileselector.h +++ b/core/libs/widgets/files/dfileselector.h @@ -1,82 +1,83 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * date : 2014-09-12 * Description : a file or folder selector widget * * Copyright (C) 2014-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_DFILE_SELECTOR_H #define DIGIKAM_DFILE_SELECTOR_H // Qt includes #include #include #include #include // Local includes #include "dlayoutbox.h" #include "dfiledialog.h" #include "digikam_export.h" namespace Digikam { -/** A widget to chosse a single local file or path. - * Use line edit and file dialog properties to customize operation modes. +/** + * A widget to chosse a single local file or path. + * Use line edit and file dialog properties to customize operation modes. */ class DIGIKAM_EXPORT DFileSelector : public DHBox { Q_OBJECT public: explicit DFileSelector(QWidget* const parent=nullptr); virtual ~DFileSelector(); QLineEdit* lineEdit() const; void setFileDlgPath(const QString& path); QString fileDlgPath() const; void setFileDlgMode(QFileDialog::FileMode mode); void setFileDlgFilter(const QString& filter); void setFileDlgTitle(const QString& title); void setFileDlgOptions(QFileDialog::Options opts); Q_SIGNALS: void signalOpenFileDialog(); void signalUrlSelected(const QUrl&); private Q_SLOTS: void slotBtnClicked(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_DFILE_SELECTOR_H diff --git a/core/libs/widgets/files/filesaveconflictbox.cpp b/core/libs/widgets/files/filesaveconflictbox.cpp index 342da276da..17610fdd59 100644 --- a/core/libs/widgets/files/filesaveconflictbox.cpp +++ b/core/libs/widgets/files/filesaveconflictbox.cpp @@ -1,129 +1,129 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-09-13 * Description : a widget to provide conflict rules to save image. * * Copyright (C) 2006-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "filesaveconflictbox.h" // Qt includes #include #include #include #include #include #include #include // KDE includes #include #include #include namespace Digikam { class Q_DECL_HIDDEN FileSaveConflictBox::Private { public: explicit Private() + : conflictLabel(nullptr), + conflictButtonGroup(nullptr), + storeDiffButton(nullptr), + overwriteButton(nullptr) { - conflictLabel = nullptr; - conflictButtonGroup = nullptr; - storeDiffButton = nullptr; - overwriteButton = nullptr; } QLabel* conflictLabel; QButtonGroup* conflictButtonGroup; QRadioButton* storeDiffButton; QRadioButton* overwriteButton; }; FileSaveConflictBox::FileSaveConflictBox(QWidget* const parent) : QWidget(parent), d(new Private) { setAttribute(Qt::WA_DeleteOnClose); const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); QGridLayout* const grid = new QGridLayout(this); d->conflictLabel = new QLabel(i18n("If Target File Exists:"), this); QWidget* const conflictBox = new QWidget(this); QVBoxLayout* const vlay = new QVBoxLayout(conflictBox); d->conflictButtonGroup = new QButtonGroup(conflictBox); d->storeDiffButton = new QRadioButton(i18n("Store as a different name"), conflictBox); d->overwriteButton = new QRadioButton(i18n("Overwrite automatically"), conflictBox); d->conflictButtonGroup->addButton(d->overwriteButton, OVERWRITE); d->conflictButtonGroup->addButton(d->storeDiffButton, DIFFNAME); d->conflictButtonGroup->setExclusive(true); d->storeDiffButton->setChecked(true); vlay->setContentsMargins(spacing, spacing, spacing, spacing); vlay->setSpacing(spacing); vlay->addWidget(d->storeDiffButton); vlay->addWidget(d->overwriteButton); grid->addWidget(d->conflictLabel, 1, 0, 1, 2); grid->addWidget(conflictBox, 2, 0, 1, 2); grid->setRowStretch(3, 10); grid->setContentsMargins(spacing, spacing, spacing, spacing); grid->setSpacing(spacing); connect(d->conflictButtonGroup, static_cast(&QButtonGroup::buttonClicked), this, &FileSaveConflictBox::signalConflictButtonChanged); } FileSaveConflictBox::~FileSaveConflictBox() { delete d; } void FileSaveConflictBox::resetToDefault() { setConflictRule(OVERWRITE); } FileSaveConflictBox::ConflictRule FileSaveConflictBox::conflictRule() const { return((ConflictRule)(d->conflictButtonGroup->checkedId())); } void FileSaveConflictBox::setConflictRule(ConflictRule r) { d->conflictButtonGroup->button((int)r)->setChecked(true); } void FileSaveConflictBox::readSettings(KConfigGroup& group) { setConflictRule((FileSaveConflictBox::ConflictRule)group.readEntry("Conflict", (int)(FileSaveConflictBox::DIFFNAME))); } void FileSaveConflictBox::writeSettings(KConfigGroup& group) { group.writeEntry("Conflict", (int)conflictRule()); } } // namespace Digikam diff --git a/core/libs/widgets/files/filesaveoptionsbox.cpp b/core/libs/widgets/files/filesaveoptionsbox.cpp index 8bd70a10cc..de7e517615 100644 --- a/core/libs/widgets/files/filesaveoptionsbox.cpp +++ b/core/libs/widgets/files/filesaveoptionsbox.cpp @@ -1,281 +1,296 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-08-02 * Description : a stack of widgets to set image file save * options into image editor. * * Copyright (C) 2007-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #include "filesaveoptionsbox.h" // Qt includes #include #include #include #include #include #include #include // KDE includes #include #include #include // Local includes #include "digikam_debug.h" #include "digikam_config.h" #include "jpegsettings.h" #include "pngsettings.h" #include "tiffsettings.h" #include "pgfsettings.h" #ifdef HAVE_JASPER # include "jp2ksettings.h" #endif // HAVE_JASPER #ifdef HAVE_X265 # include "heifsettings.h" #endif // HAVE_X265 namespace Digikam { class Q_DECL_HIDDEN FileSaveOptionsBox::Private { public: explicit Private() : noneOptions(nullptr), noneGrid(nullptr), labelNone(nullptr), JPEGOptions(nullptr), PNGOptions(nullptr), TIFFOptions(nullptr), + #ifdef HAVE_JASPER JPEG2000Options(nullptr), #endif // HAVE_JASPER + #ifdef HAVE_X265 HEIFOptions(nullptr), #endif // HAVE_X265 + PGFOptions(nullptr) { } QWidget* noneOptions; QGridLayout* noneGrid; QLabel* labelNone; JPEGSettings* JPEGOptions; PNGSettings* PNGOptions; TIFFSettings* TIFFOptions; #ifdef HAVE_JASPER JP2KSettings* JPEG2000Options; #endif // HAVE_JASPER #ifdef HAVE_X265 HEIFSettings* HEIFOptions; #endif // HAVE_X265 PGFSettings* PGFOptions; }; FileSaveOptionsBox::FileSaveOptionsBox(QWidget* const parent) : QStackedWidget(parent), d(new Private) { setAttribute(Qt::WA_DeleteOnClose); //-- NONE Settings ------------------------------------------------------ d->noneOptions = new QWidget(this); d->noneGrid = new QGridLayout(d->noneOptions); d->noneGrid->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); d->noneOptions->setLayout(d->noneGrid); d->labelNone = new QLabel(i18n("No options available"), d->noneOptions); d->noneGrid->addWidget(d->labelNone, 0, 0, 0, 1); //-- JPEG Settings ------------------------------------------------------ d->JPEGOptions = new JPEGSettings(this); //-- PNG Settings ------------------------------------------------------- d->PNGOptions = new PNGSettings(this); //-- TIFF Settings ------------------------------------------------------ d->TIFFOptions = new TIFFSettings(this); //-- JPEG 2000 Settings ------------------------------------------------- + #ifdef HAVE_JASPER d->JPEG2000Options = new JP2KSettings(this); #endif // HAVE_JASPER //-- PGF Settings ------------------------------------------------- d->PGFOptions = new PGFSettings(this); //-- HEIF Settings ------------------------------------------------- + #ifdef HAVE_X265 d->HEIFOptions = new HEIFSettings(this); #endif // HAVE_X265 //----------------------------------------------------------------------- insertWidget(DImg::NONE, d->noneOptions); insertWidget(DImg::JPEG, d->JPEGOptions); insertWidget(DImg::PNG, d->PNGOptions); insertWidget(DImg::TIFF, d->TIFFOptions); #ifdef HAVE_JASPER insertWidget(DImg::JP2K, d->JPEG2000Options); #endif // HAVE_JASPER insertWidget(DImg::PGF, d->PGFOptions); #ifdef HAVE_X265 insertWidget(DImg::HEIF, d->HEIFOptions); #endif // HAVE_X265 //----------------------------------------------------------------------- readSettings(); } FileSaveOptionsBox::~FileSaveOptionsBox() { delete d; } void FileSaveOptionsBox::setImageFileFormat(const QString& ext) { qCDebug(DIGIKAM_WIDGETS_LOG) << "Format selected: " << ext; setCurrentIndex(discoverFormat(ext, DImg::NONE)); } DImg::FORMAT FileSaveOptionsBox::discoverFormat(const QString& filename, DImg::FORMAT fallback) { qCDebug(DIGIKAM_WIDGETS_LOG) << "Trying to discover format based on filename '" << filename << "', fallback = " << fallback; QStringList splitParts = filename.split(QLatin1Char('.')); QString ext; if (splitParts.size() < 2) { qCDebug(DIGIKAM_WIDGETS_LOG) << "filename '" << filename << "' does not contain an extension separated by a point."; ext = filename; } else { ext = splitParts.at(splitParts.size() - 1); } ext = ext.toUpper(); DImg::FORMAT format = fallback; if (ext.contains(QLatin1String("JPEG")) || ext.contains(QLatin1String("JPG")) || ext.contains(QLatin1String("JPE"))) { format = DImg::JPEG; } else if (ext.contains(QLatin1String("PNG"))) { format = DImg::PNG; } else if (ext.contains(QLatin1String("TIFF")) || ext.contains(QLatin1String("TIF"))) { format = DImg::TIFF; } + #ifdef HAVE_JASPER else if (ext.contains(QLatin1String("JP2")) || ext.contains(QLatin1String("JPX")) || ext.contains(QLatin1String("JPC")) || ext.contains(QLatin1String("PGX")) || ext.contains(QLatin1String("J2K"))) { format = DImg::JP2K; } #endif // HAVE_JASPER + #ifdef HAVE_X265 else if (ext.contains(QLatin1String("HEIC")) || ext.contains(QLatin1String("HEIF"))) { format = DImg::HEIF; } #endif // HAVE_X265 + else if (ext.contains(QLatin1String("PGF"))) { format = DImg::PGF; } else { qCWarning(DIGIKAM_WIDGETS_LOG) << "Using fallback format " << fallback; } qCDebug(DIGIKAM_WIDGETS_LOG) << "Discovered format: " << format; return format; } void FileSaveOptionsBox::applySettings() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group("ImageViewer Settings"); group.writeEntry(QLatin1String("JPEGCompression"), d->JPEGOptions->getCompressionValue()); group.writeEntry(QLatin1String("JPEGSubSampling"), d->JPEGOptions->getSubSamplingValue()); group.writeEntry(QLatin1String("PNGCompression"), d->PNGOptions->getCompressionValue()); group.writeEntry(QLatin1String("TIFFCompression"), d->TIFFOptions->getCompression()); + #ifdef HAVE_JASPER group.writeEntry(QLatin1String("JPEG2000Compression"), d->JPEG2000Options->getCompressionValue()); group.writeEntry(QLatin1String("JPEG2000LossLess"), d->JPEG2000Options->getLossLessCompression()); #endif // HAVE_JASPER + group.writeEntry(QLatin1String("PGFCompression"), d->PGFOptions->getCompressionValue()); group.writeEntry(QLatin1String("PGFLossLess"), d->PGFOptions->getLossLessCompression()); + #ifdef HAVE_X265 group.writeEntry(QLatin1String("HEIFCompression"), d->HEIFOptions->getCompressionValue()); group.writeEntry(QLatin1String("HEIFLossLess"), d->HEIFOptions->getLossLessCompression()); #endif // HAVE_X265 config->sync(); } void FileSaveOptionsBox::readSettings() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group("ImageViewer Settings"); d->JPEGOptions->setCompressionValue( group.readEntry(QLatin1String("JPEGCompression"), 75) ); - d->JPEGOptions->setSubSamplingValue( group.readEntry(QLatin1String("JPEGSubSampling"), 1) ); // Medium subsampling + d->JPEGOptions->setSubSamplingValue( group.readEntry(QLatin1String("JPEGSubSampling"), 1) ); ///< Medium subsampling d->PNGOptions->setCompressionValue( group.readEntry(QLatin1String("PNGCompression"), 9) ); d->TIFFOptions->setCompression( group.readEntry(QLatin1String("TIFFCompression"), false) ); + #ifdef HAVE_JASPER d->JPEG2000Options->setCompressionValue( group.readEntry(QLatin1String("JPEG2000Compression"), 75) ); d->JPEG2000Options->setLossLessCompression( group.readEntry(QLatin1String("JPEG2000LossLess"), true) ); #endif // HAVE_JASPER + d->PGFOptions->setCompressionValue( group.readEntry(QLatin1String("PGFCompression"), 3) ); d->PGFOptions->setLossLessCompression( group.readEntry(QLatin1String("PGFLossLess"), true) ); + #ifdef HAVE_X265 d->HEIFOptions->setCompressionValue( group.readEntry(QLatin1String("HEIFCompression"), 75) ); d->HEIFOptions->setLossLessCompression( group.readEntry(QLatin1String("HEIFLossLess"), true) ); #endif // HAVE_X265 + } } // namespace Digikam diff --git a/core/libs/widgets/fonts/dfontproperties.cpp b/core/libs/widgets/fonts/dfontproperties.cpp index 748a2dcd9c..16eb407000 100644 --- a/core/libs/widgets/fonts/dfontproperties.cpp +++ b/core/libs/widgets/fonts/dfontproperties.cpp @@ -1,1433 +1,1458 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-12-23 * Description : a widget to change font properties. * * Copyright (C) 2008-2020 by Gilles Caulier * Copyright (C) 1996 by Bernd Johannes Wuebben * Copyright (c) 1999 by Preston Brown * Copyright (c) 1999 by Mario Weilguni * * 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, 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. * * ============================================================ */ #include "dfontproperties.h" // C++ includes #include // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include namespace Digikam { static bool localeLessThan(const QString& a, const QString& b) { - return QString::localeAwareCompare(a, b) < 0; + return (QString::localeAwareCompare(a, b) < 0); } static int minimumListWidth(const QListWidget* list) { int w = 0; for (int i = 0 ; i < list->count() ; ++i) { int itemWidth = list->visualItemRect(list->item(i)).width(); // ...and add a space on both sides for not too tight look. #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) itemWidth += list->fontMetrics().horizontalAdvance(QLatin1Char(' ')) * 2; #else itemWidth += list->fontMetrics().width(QLatin1Char(' ')) * 2; #endif w = qMax(w, itemWidth); } if (w == 0) { w = 40; } w += list->frameWidth() * 2; w += list->verticalScrollBar()->sizeHint().width(); return w; } static int minimumListHeight(const QListWidget* list, int numVisibleEntry) { int w = (list->count() > 0) ? list->visualItemRect(list->item(0)).height() : list->fontMetrics().lineSpacing(); if (w < 0) { w = 10; } if (numVisibleEntry <= 0) { numVisibleEntry = 4; } return (w * numVisibleEntry + 2 * list->frameWidth()); } static QString formatFontSize(qreal size) { return QLocale::system().toString(size, 'f', (size == floor(size)) ? 0 : 1); } // ----------------------------------------------------------------------------------- class Q_DECL_HIDDEN DFontProperties::Private { public: explicit Private(DFontProperties* const qq) - : q(qq) + : q(qq), + sizeOfFont(nullptr), + sampleEdit(nullptr), + familyLabel(nullptr), + styleLabel(nullptr), + familyCheckbox(nullptr), + styleCheckbox(nullptr), + sizeCheckbox(nullptr), + sizeLabel(nullptr), + familyListBox(nullptr), + styleListBox(nullptr), + sizeListBox(nullptr), + sizeIsRelativeCheckBox(nullptr), + selectedSize(-1), + customSizeRow(-1), + signalsAllowed(true), + usingFixed(true) { palette.setColor(QPalette::Active, QPalette::Text, Qt::black); palette.setColor(QPalette::Active, QPalette::Base, Qt::white); - signalsAllowed = true; - selectedSize = -1; - customSizeRow = -1; - usingFixed = true; - sizeOfFont = nullptr; - sampleEdit = nullptr; - familyLabel = nullptr; - styleLabel = nullptr; - familyCheckbox = nullptr; - styleCheckbox = nullptr; - sizeCheckbox = nullptr; - sizeLabel = nullptr; - familyListBox = nullptr; - styleListBox = nullptr; - sizeListBox = nullptr; - sizeIsRelativeCheckBox = nullptr; } void setFamilyBoxItems(const QStringList& fonts); void fillFamilyListBox(bool onlyFixedFonts = false); int nearestSizeRow(qreal val, bool customize); qreal fillSizeList(const QList& sizes = QList()); qreal setupSizeListBox(const QString& family, const QString& style); void setupDisplay(); QString styleIdentifier(const QFont& font); /** * Split the compound raw font name into family and foundry. * * @param name the raw font name reported by Qt * @param family the storage for family name * @param foundry the storage for foundry name */ void splitFontString(const QString& name, QString* family, QString* foundry = nullptr); /** * Translate the font name for the user. * Primarily for generic fonts like Serif, Sans-Serif, etc. * * @param name the raw font name reported by Qt * @return translated font name */ QString translateFontName(const QString& name); /** * Compose locale-aware sorted list of translated font names, * with generic fonts handled in a special way. * The mapping of translated to raw names can be reported too if required. * * @param names raw font names as reported by Qt * @param trToRawNames storage for mapping of translated to raw names * @return sorted list of translated font names */ QStringList translateFontNameList(const QStringList& names, QHash* trToRawNames = nullptr); void _d_toggled_checkbox(); void _d_family_chosen_slot(const QString&); void _d_size_chosen_slot(const QString&); void _d_style_chosen_slot(const QString&); void _d_displaySample(const QFont& font); void _d_size_value_slot(double); public: DFontProperties* q; QPalette palette; QDoubleSpinBox* sizeOfFont; QTextEdit* sampleEdit; QLabel* familyLabel; QLabel* styleLabel; QCheckBox* familyCheckbox; QCheckBox* styleCheckbox; QCheckBox* sizeCheckbox; QLabel* sizeLabel; QListWidget* familyListBox; QListWidget* styleListBox; QListWidget* sizeListBox; QCheckBox* sizeIsRelativeCheckBox; QFont selFont; QString selectedStyle; qreal selectedSize; QString standardSizeAtCustom; int customSizeRow; bool signalsAllowed; bool usingFixed; - // Mappings of translated to Qt originated family and style strings. + /** + * Mappings of translated to Qt originated family and style strings. + */ QHash qtFamilies; QHash qtStyles; - // Mapping of translated style strings to internal style identifiers. + /** + * Mapping of translated style strings to internal style identifiers. + */ QHash styleIDs; }; DFontProperties::DFontProperties(QWidget* const parent, const DisplayFlags& flags, const QStringList& fontList, int visibleListSize, Qt::CheckState* const sizeIsRelativeState) : QWidget(parent), d(new DFontProperties::Private(this)) { d->usingFixed = flags & FixedFontsOnly; setWhatsThis(i18n("Here you can choose the font to be used.")); // The top layout is divided vertically into a splitter with font // attribute widgets and preview on the top, and XLFD data at the bottom. + QVBoxLayout* const topLayout = new QVBoxLayout(this); topLayout->setContentsMargins(0, 0, 0, 0); const int spacingHint = style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); int checkBoxGap = spacingHint / 2; // The splitter contains font attribute widgets in the top part, // and the font preview in the bottom part. // The splitter is there to allow the user to resize the font preview. + QSplitter* const splitter = new QSplitter(Qt::Vertical, this); splitter->setChildrenCollapsible(false); topLayout->addWidget(splitter); // Build the grid of font attribute widgets for the upper splitter part. QWidget* page = nullptr; QGridLayout* gridLayout = nullptr; int row = 0; if (flags & DisplayFrame) { page = new QGroupBox(i18n("Requested Font"), this); splitter->addWidget(page); gridLayout = new QGridLayout(page); row = 1; } else { page = new QWidget(this); splitter->addWidget(page); gridLayout = new QGridLayout(page); gridLayout->setContentsMargins(0, 0, 0, 0); } // first, create the labels across the top QHBoxLayout* const familyLayout = new QHBoxLayout(); familyLayout->addSpacing(checkBoxGap); if (flags & ShowDifferences) { d->familyCheckbox = new QCheckBox(i18n("Font"), page); connect(d->familyCheckbox, SIGNAL(toggled(bool)), this, SLOT(_d_toggled_checkbox())); familyLayout->addWidget(d->familyCheckbox, 0, Qt::AlignLeft); d->familyCheckbox->setWhatsThis(i18n("Enable this checkbox to change the font family settings.")); d->familyCheckbox->setToolTip(i18n("Change font family?")); d->familyLabel = nullptr; } else { d->familyCheckbox = nullptr; d->familyLabel = new QLabel(i18nc("@label", "Font:"), page); familyLayout->addWidget(d->familyLabel, 1, Qt::AlignLeft); } gridLayout->addLayout(familyLayout, row, 0); QHBoxLayout* const styleLayout = new QHBoxLayout(); if (flags & ShowDifferences) { d->styleCheckbox = new QCheckBox(i18n("Font style"), page); connect(d->styleCheckbox, SIGNAL(toggled(bool)), this, SLOT(_d_toggled_checkbox())); styleLayout->addWidget(d->styleCheckbox, 0, Qt::AlignLeft); d->styleCheckbox->setWhatsThis(i18n("Enable this checkbox to change the font style settings.")); d->styleCheckbox->setToolTip(i18n("Change font style?")); d->styleLabel = nullptr; } else { d->styleCheckbox = nullptr; d->styleLabel = new QLabel(i18n("Font style:"), page); styleLayout->addWidget(d->styleLabel, 1, Qt::AlignLeft); } styleLayout->addSpacing(checkBoxGap); gridLayout->addLayout(styleLayout, row, 1); QHBoxLayout* const sizeLayout = new QHBoxLayout(); if (flags & ShowDifferences) { d->sizeCheckbox = new QCheckBox(i18n("Size"), page); connect(d->sizeCheckbox, SIGNAL(toggled(bool)), this, SLOT(_d_toggled_checkbox())); sizeLayout->addWidget(d->sizeCheckbox, 0, Qt::AlignLeft); d->sizeCheckbox->setWhatsThis(i18n("Enable this checkbox to change the font size settings.")); d->sizeCheckbox->setToolTip(i18n("Change font size?")); d->sizeLabel = nullptr; } else { d->sizeCheckbox = nullptr; d->sizeLabel = new QLabel(i18nc("@label:listbox Font size", "Size:"), page); sizeLayout->addWidget(d->sizeLabel, 1, Qt::AlignLeft); } sizeLayout->addSpacing(checkBoxGap); sizeLayout->addSpacing(checkBoxGap); // prevent label from eating border gridLayout->addLayout(sizeLayout, row, 2); row ++; // now create the actual boxes that hold the info d->familyListBox = new QListWidget(page); d->familyListBox->setEnabled(flags ^ ShowDifferences); gridLayout->addWidget(d->familyListBox, row, 0); QString fontFamilyWhatsThisText(i18n("Here you can choose the font family to be used.")); d->familyListBox->setWhatsThis(fontFamilyWhatsThisText); if (flags & ShowDifferences) { d->familyCheckbox->setWhatsThis(fontFamilyWhatsThisText); } else { d->familyLabel->setWhatsThis(fontFamilyWhatsThisText); } connect(d->familyListBox, SIGNAL(currentTextChanged(QString)), this, SLOT(_d_family_chosen_slot(QString))); if (!fontList.isEmpty()) { d->setFamilyBoxItems(fontList); } else { d->fillFamilyListBox(flags & FixedFontsOnly); } d->familyListBox->setMinimumWidth(minimumListWidth(d->familyListBox)); d->familyListBox->setMinimumHeight(minimumListHeight(d->familyListBox, visibleListSize)); d->styleListBox = new QListWidget(page); d->styleListBox->setEnabled(flags ^ ShowDifferences); gridLayout->addWidget(d->styleListBox, row, 1); d->styleListBox->setWhatsThis(i18n("Here you can choose the font style to be used.")); if (flags & ShowDifferences) { ((QWidget *)d->styleCheckbox)->setWhatsThis(fontFamilyWhatsThisText); } else { ((QWidget *)d->styleLabel)->setWhatsThis(fontFamilyWhatsThisText); } // Populate usual styles, to determine minimum list width; // will be replaced later with correct styles. + d->styleListBox->addItem(i18n("Normal")); d->styleListBox->addItem(i18n("Italic")); d->styleListBox->addItem(i18n("Oblique")); d->styleListBox->addItem(i18n("Bold")); d->styleListBox->addItem(i18n("Bold Italic")); d->styleListBox->setMinimumWidth(minimumListWidth(d->styleListBox)); d->styleListBox->setMinimumHeight(minimumListHeight(d->styleListBox, visibleListSize)); connect(d->styleListBox, SIGNAL(currentTextChanged(QString)), this, SLOT(_d_style_chosen_slot(QString))); d->sizeListBox = new QListWidget(page); d->sizeOfFont = new QDoubleSpinBox(page); d->sizeOfFont->setMinimum(4); d->sizeOfFont->setMaximum(999); d->sizeOfFont->setDecimals(1); d->sizeOfFont->setSingleStep(1); d->sizeListBox->setEnabled(flags ^ ShowDifferences); d->sizeOfFont->setEnabled(flags ^ ShowDifferences); if (sizeIsRelativeState) { QString sizeIsRelativeCBText = i18n("Relative"); QString sizeIsRelativeCBToolTipText = i18n("Font size
fixed or relative
to environment"); QString sizeIsRelativeCBWhatsThisText = i18n("Here you can switch between fixed font size and font size " "to be calculated dynamically and adjusted to changing " "environment (e.g. widget dimensions, paper size)."); d->sizeIsRelativeCheckBox = new QCheckBox(sizeIsRelativeCBText, page); d->sizeIsRelativeCheckBox->setTristate(flags & ShowDifferences); QGridLayout* const sizeLayout2 = new QGridLayout(); sizeLayout2->setSpacing(spacingHint / 2); gridLayout->addLayout(sizeLayout2, row, 2); - sizeLayout2->setColumnStretch(1, 1); // to prevent text from eating the right border + sizeLayout2->setColumnStretch(1, 1); // to prevent text from eating the right border sizeLayout2->addWidget(d->sizeOfFont, 0, 0, 1, 2); sizeLayout2->addWidget(d->sizeListBox, 1, 0, 1, 2); sizeLayout2->addWidget(d->sizeIsRelativeCheckBox, 2, 0, Qt::AlignLeft); d->sizeIsRelativeCheckBox->setWhatsThis(sizeIsRelativeCBWhatsThisText); d->sizeIsRelativeCheckBox->setToolTip(sizeIsRelativeCBToolTipText); } else { d->sizeIsRelativeCheckBox = nullptr; QGridLayout* const sizeLayout2 = new QGridLayout(); sizeLayout2->setSpacing(spacingHint / 2); gridLayout->addLayout(sizeLayout2, row, 2); sizeLayout2->addWidget(d->sizeOfFont, 0, 0); sizeLayout2->addWidget(d->sizeListBox, 1, 0); } QString fontSizeWhatsThisText = i18n("Here you can choose the font size to be used."); d->sizeListBox->setWhatsThis(fontSizeWhatsThisText); if (flags & ShowDifferences) { ((QWidget*)d->sizeCheckbox)->setWhatsThis(fontSizeWhatsThisText); } else { ((QWidget*)d->sizeLabel)->setWhatsThis(fontSizeWhatsThisText); } // Populate with usual sizes, to determine minimum list width; // will be replaced later with correct sizes. + d->fillSizeList(); d->sizeListBox->setMinimumWidth(minimumListWidth(d->sizeListBox) + d->sizeListBox->fontMetrics().maxWidth()); d->sizeListBox->setMinimumHeight(minimumListHeight(d->sizeListBox, visibleListSize)); connect(d->sizeOfFont, SIGNAL(valueChanged(double)), this, SLOT(_d_size_value_slot(double))); connect(d->sizeListBox, SIGNAL(currentTextChanged(QString)), this, SLOT(_d_size_chosen_slot(QString))); row ++; // Completed the font attribute grid. // Add the font preview into the lower part of the splitter. d->sampleEdit = new QTextEdit(page); d->sampleEdit->setAcceptRichText(false); QFont tmpFont(font().family(), 64, QFont::Black); d->sampleEdit->setFont(tmpFont); d->sampleEdit->setMinimumHeight(d->sampleEdit->fontMetrics().lineSpacing()); + // tr: A classical test phrase, with all letters of the English alphabet. // Replace it with a sample text in your language, such that it is // representative of language's writing system. // If you wish, you can input several lines of text separated by \n. + setSampleText(i18n("The Quick Brown Fox Jumps Over The Lazy Dog")); d->sampleEdit->setTextCursor(QTextCursor(d->sampleEdit->document())); QString sampleEditWhatsThisText = i18n("This sample text illustrates the current settings. " "You may edit it to test special characters."); d->sampleEdit->setWhatsThis(sampleEditWhatsThisText); connect(this, SIGNAL(fontSelected(QFont)), this, SLOT(_d_displaySample(QFont))); splitter->addWidget(d->sampleEdit); // Finished setting up the splitter. // Finished setting up the chooser layout. // lets initialize the display if possible if (d->usingFixed) { setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont), d->usingFixed); } else { setFont(QGuiApplication::font(), d->usingFixed); } // check or uncheck or gray out the "relative" checkbox if (sizeIsRelativeState && d->sizeIsRelativeCheckBox) { setSizeIsRelative(*sizeIsRelativeState); } // Set focus to the size list as this is the most commonly changed property + d->sizeListBox->setFocus(); } DFontProperties::~DFontProperties() { delete d; } void DFontProperties::setColor(const QColor& col) { d->palette.setColor(QPalette::Active, QPalette::Text, col); QPalette pal = d->sampleEdit->palette(); pal.setColor(QPalette::Active, QPalette::Text, col); d->sampleEdit->setPalette(pal); QTextCursor cursor = d->sampleEdit->textCursor(); d->sampleEdit->selectAll(); d->sampleEdit->setTextColor(col); d->sampleEdit->setTextCursor(cursor); } QColor DFontProperties::color() const { return d->palette.color(QPalette::Active, QPalette::Text); } void DFontProperties::setBackgroundColor(const QColor& col) { d->palette.setColor(QPalette::Active, QPalette::Base, col); QPalette pal = d->sampleEdit->palette(); pal.setColor(QPalette::Active, QPalette::Base, col); d->sampleEdit->setPalette(pal); } QColor DFontProperties::backgroundColor() const { return d->palette.color(QPalette::Active, QPalette::Base); } void DFontProperties::setSizeIsRelative(Qt::CheckState relative) { // check or uncheck or gray out the "relative" checkbox if (d->sizeIsRelativeCheckBox) { if (Qt::PartiallyChecked == relative) { d->sizeIsRelativeCheckBox->setCheckState(Qt::PartiallyChecked); } else { d->sizeIsRelativeCheckBox->setCheckState((Qt::Checked == relative) ? Qt::Checked : Qt::Unchecked); } } } Qt::CheckState DFontProperties::sizeIsRelative() const { return d->sizeIsRelativeCheckBox ? d->sizeIsRelativeCheckBox->checkState() : Qt::PartiallyChecked; } QString DFontProperties::sampleText() const { return d->sampleEdit->toPlainText(); } void DFontProperties::setSampleText(const QString& text) { d->sampleEdit->setPlainText(text); } void DFontProperties::setSampleBoxVisible(bool visible) { d->sampleEdit->setVisible(visible); } QSize DFontProperties::sizeHint(void) const { return minimumSizeHint(); } void DFontProperties::enableColumn(int column, bool state) { if (column & FamilyList) { d->familyListBox->setEnabled(state); } if (column & StyleList) { d->styleListBox->setEnabled(state); } if (column & SizeList) { d->sizeListBox->setEnabled(state); d->sizeOfFont->setEnabled(state); } } void DFontProperties::makeColumnVisible(int column, bool state) { if (column & FamilyList) { d->familyListBox->setVisible(state); d->familyLabel->setVisible(state); } if (column & StyleList) { d->styleListBox->setVisible(state); d->styleLabel->setVisible(state); } if (column & SizeList) { d->sizeListBox->setVisible(state); d->sizeOfFont->setVisible(state); d->sizeLabel->setVisible(state); } } void DFontProperties::setFont(const QFont& aFont, bool onlyFixed) { d->selFont = aFont; d->selectedSize = aFont.pointSizeF(); if (d->selectedSize == -1) { d->selectedSize = QFontInfo(aFont).pointSizeF(); } if (onlyFixed != d->usingFixed) { d->usingFixed = onlyFixed; d->fillFamilyListBox(d->usingFixed); } d->setupDisplay(); } DFontProperties::FontDiffFlags DFontProperties::fontDiffFlags() const { FontDiffFlags diffFlags = NoFontDiffFlags; if (d->familyCheckbox && d->familyCheckbox->isChecked()) { diffFlags |= FontDiffFamily; } if (d->styleCheckbox && d->styleCheckbox->isChecked()) { diffFlags |= FontDiffStyle; } if (d->sizeCheckbox && d->sizeCheckbox->isChecked()) { diffFlags |= FontDiffSize; } return diffFlags; } QFont DFontProperties::font() const { return d->selFont; } void DFontProperties::Private::_d_toggled_checkbox() { familyListBox->setEnabled(familyCheckbox->isChecked()); styleListBox->setEnabled(styleCheckbox->isChecked()); sizeListBox->setEnabled(sizeCheckbox->isChecked()); sizeOfFont->setEnabled(sizeCheckbox->isChecked()); } void DFontProperties::Private::_d_family_chosen_slot(const QString& family) { if (!signalsAllowed) { return; } signalsAllowed = false; QString currentFamily; if (family.isEmpty()) { Q_ASSERT(familyListBox->currentItem()); if (familyListBox->currentItem()) { currentFamily = qtFamilies[familyListBox->currentItem()->text()]; } } else { currentFamily = qtFamilies[family]; } // Get the list of styles available in this family. QFontDatabase dbase; QStringList styles = dbase.styles(currentFamily); if (styles.isEmpty()) { styles.append(i18n("Normal")); } // Filter style strings and add to the listbox. QString pureFamily; splitFontString(family, &pureFamily); QStringList filteredStyles; qtStyles.clear(); styleIDs.clear(); foreach (const QString& style, styles) { // Sometimes the font database will report an invalid style, // that falls back to another when set. // Remove such styles, by checking set/get round-trip. + QFont testFont = dbase.font(currentFamily, style, 10); if (dbase.styleString(testFont) != style) { styles.removeAll(style); continue; } QString fstyle = style; if (!filteredStyles.contains(fstyle)) { filteredStyles.append(fstyle); qtStyles.insert(fstyle, style); styleIDs.insert(fstyle, styleIdentifier(testFont)); } } styleListBox->clear(); styleListBox->addItems(filteredStyles); // Try to set the current style in the listbox to that previous. + int listPos = filteredStyles.indexOf(selectedStyle.isEmpty() ? i18n("Normal") : selectedStyle); if (listPos < 0) { // Make extra effort to have Italic selected when Oblique was chosen, // and vice versa, as that is what the user would probably want. + QString styleIt = i18n("Italic"); QString styleOb = i18n("Oblique"); for (int i = 0 ; i < 2 ; ++i) { int pos = selectedStyle.indexOf(styleIt); if (pos >= 0) { QString style = selectedStyle; style.replace(pos, styleIt.length(), styleOb); listPos = filteredStyles.indexOf(style); if (listPos >= 0) { break; } } std::swap(styleIt, styleOb); } } styleListBox->setCurrentRow(listPos >= 0 ? listPos : 0); QString currentStyle = qtStyles[styleListBox->currentItem()->text()]; // Recompute the size listbox for this family/style. qreal currentSize = setupSizeListBox(currentFamily, currentStyle); sizeOfFont->setValue(currentSize); selFont = dbase.font(currentFamily, currentStyle, int(currentSize)); if (dbase.isSmoothlyScalable(currentFamily, currentStyle) && selFont.pointSize() == floor(currentSize)) { selFont.setPointSizeF(currentSize); } emit q->fontSelected(selFont); signalsAllowed = true; } void DFontProperties::Private::_d_style_chosen_slot(const QString& style) { if (!signalsAllowed) { return; } signalsAllowed = false; QFontDatabase dbase; QString currentFamily = qtFamilies[familyListBox->currentItem()->text()]; QString currentStyle; if (style.isEmpty()) { currentStyle = qtStyles[styleListBox->currentItem()->text()]; } else { currentStyle = qtStyles[style]; } // Recompute the size listbox for this family/style. qreal currentSize = setupSizeListBox(currentFamily, currentStyle); sizeOfFont->setValue(currentSize); selFont = dbase.font(currentFamily, currentStyle, int(currentSize)); if (dbase.isSmoothlyScalable(currentFamily, currentStyle) && selFont.pointSize() == floor(currentSize)) { selFont.setPointSizeF(currentSize); } emit q->fontSelected(selFont); if (!style.isEmpty()) { selectedStyle = currentStyle; } signalsAllowed = true; } void DFontProperties::Private::_d_size_chosen_slot(const QString& size) { if (!signalsAllowed) { return; } signalsAllowed = false; qreal currentSize; if (size.isEmpty()) { currentSize = QLocale::system().toDouble(sizeListBox->currentItem()->text()); } else { currentSize = QLocale::system().toDouble(size); } // Reset the customized size slot in the list if not needed. if (customSizeRow >= 0 && selFont.pointSizeF() != currentSize) { sizeListBox->item(customSizeRow)->setText(standardSizeAtCustom); customSizeRow = -1; } sizeOfFont->setValue(currentSize); selFont.setPointSizeF(currentSize); emit q->fontSelected(selFont); if (!size.isEmpty()) { selectedSize = currentSize; } signalsAllowed = true; } void DFontProperties::Private::_d_size_value_slot(double dval) { if (!signalsAllowed) { return; } signalsAllowed = false; // We compare with qreal, so convert for platforms where qreal != double. qreal val = qreal(dval); QFontDatabase dbase; QString family = qtFamilies[familyListBox->currentItem()->text()]; QString style = qtStyles[styleListBox->currentItem()->text()]; // Reset current size slot in list if it was customized. if (customSizeRow >= 0 && sizeListBox->currentRow() == customSizeRow) { sizeListBox->item(customSizeRow)->setText(standardSizeAtCustom); customSizeRow = -1; } bool canCustomize = true; // For Qt-bad-sizes workaround: skip this block unconditionally if (!dbase.isSmoothlyScalable(family, style)) { // Bitmap font, allow only discrete sizes. // Determine the nearest in the direction of change. canCustomize = false; int nrows = sizeListBox->count(); int row = sizeListBox->currentRow(); int nrow; if (val - selFont.pointSizeF() > 0) { for (nrow = row + 1 ; nrow < nrows ; ++nrow) { if (QLocale::system().toDouble(sizeListBox->item(nrow)->text()) >= val) { break; } } } else { for (nrow = row - 1 ; nrow >= 0 ; --nrow) { if (QLocale::system().toDouble(sizeListBox->item(nrow)->text()) <= val) { break; } } } // Make sure the new row is not out of bounds. nrow = nrow < 0 ? 0 : nrow >= nrows ? nrows - 1 : nrow; // Get the size from the new row and set the spinbox to that size. val = QLocale::system().toDouble(sizeListBox->item(nrow)->text()); sizeOfFont->setValue(val); } // Set the current size in the size listbox. int row = nearestSizeRow(val, canCustomize); sizeListBox->setCurrentRow(row); selectedSize = val; selFont.setPointSizeF(val); emit q->fontSelected(selFont); signalsAllowed = true; } void DFontProperties::Private::_d_displaySample(const QFont& font) { sampleEdit->setFont(font); } int DFontProperties::Private::nearestSizeRow(qreal val, bool customize) { qreal diff = 1000; int row = 0; for (int r = 0; r < sizeListBox->count(); ++r) { qreal cval = QLocale::system().toDouble(sizeListBox->item(r)->text()); if (qAbs(cval - val) < diff) { diff = qAbs(cval - val); row = r; } } // For Qt-bad-sizes workaround: ignore value of customize, use true if (customize && diff > 0) { customSizeRow = row; standardSizeAtCustom = sizeListBox->item(row)->text(); sizeListBox->item(row)->setText(formatFontSize(val)); } return row; } qreal DFontProperties::Private::fillSizeList(const QList& sizes_) { if (!sizeListBox) { return 0; } QList sizes = sizes_; bool canCustomize = false; if (sizes.count() == 0) { static const int c[] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 32, 48, 64, 72, 80, 96, 128, 0 }; for (int i = 0 ; c[i] ; ++i) { sizes.append(c[i]); } // Since sizes were not supplied, this is a vector font, // and size slot customization is allowed. + canCustomize = true; } // Insert sizes into the listbox. + sizeListBox->clear(); + std::sort(sizes.begin(), sizes.end()); foreach (qreal size, sizes) { sizeListBox->addItem(formatFontSize(size)); } // Return the nearest to selected size. // If the font is vector, the nearest size is always same as selected, // thus size slot customization is allowed. // If the font is bitmap, the nearest size need not be same as selected, // thus size slot customization is not allowed. customSizeRow = -1; int row = nearestSizeRow(selectedSize, canCustomize); return QLocale::system().toDouble(sizeListBox->item(row)->text()); } qreal DFontProperties::Private::setupSizeListBox(const QString& family, const QString& style) { QFontDatabase dbase; QList sizes; if (dbase.isSmoothlyScalable(family, style)) { // A vector font. //sampleEdit->setPaletteBackgroundPixmap( VectorPixmap ); // TODO } else { // A bitmap font. //sampleEdit->setPaletteBackgroundPixmap( BitmapPixmap ); // TODO QList smoothSizes = dbase.smoothSizes(family, style); foreach (int size, smoothSizes) { sizes.append(size); } } // Fill the listbox (uses default list of sizes if the given is empty). // Collect the best fitting size to selected size, to use if not smooth. + qreal bestFitSize = fillSizeList(sizes); // Set the best fit size as current in the listbox if available. + const QList selectedSizeList = sizeListBox->findItems(formatFontSize(bestFitSize), Qt::MatchExactly); if (!selectedSizeList.isEmpty()) { sizeListBox->setCurrentItem(selectedSizeList.first()); } return bestFitSize; } void DFontProperties::Private::setupDisplay() { QFontDatabase dbase; QString family = selFont.family().toLower(); QString styleID = styleIdentifier(selFont); qreal size = selFont.pointSizeF(); if (size == -1) { size = QFontInfo(selFont).pointSizeF(); } int numEntries, i; // Direct family match. numEntries = familyListBox->count(); for (i = 0 ; i < numEntries ; ++i) { if (family == qtFamilies[familyListBox->item(i)->text()].toLower()) { familyListBox->setCurrentRow(i); break; } } // 1st family fallback. if (i == numEntries) { if (family.contains(QLatin1Char('['))) { family = family.left(family.indexOf(QLatin1Char('['))).trimmed(); for (i = 0 ; i < numEntries ; ++i) { if (family == qtFamilies[familyListBox->item(i)->text()].toLower()) { familyListBox->setCurrentRow(i); break; } } } } // 2nd family fallback. if (i == numEntries) { QString fallback = family + QLatin1String(" ["); for (i = 0 ; i < numEntries ; ++i) { if (qtFamilies[familyListBox->item(i)->text()].toLower().startsWith(fallback)) { familyListBox->setCurrentRow(i); break; } } } // 3rd family fallback. if (i == numEntries) { for (i = 0 ; i < numEntries ; ++i) { if (qtFamilies[familyListBox->item(i)->text()].toLower().startsWith(family)) { familyListBox->setCurrentRow(i); break; } } } // Family fallback in case nothing matched. Otherwise, diff doesn't work if (i == numEntries) { familyListBox->setCurrentRow(0); } // By setting the current item in the family box, the available // styles and sizes for that family have been collected. // Try now to set the current items in the style and size boxes. // Set current style in the listbox. numEntries = styleListBox->count(); for (i = 0 ; i < numEntries ; ++i) { if (styleID == styleIDs[styleListBox->item(i)->text()]) { styleListBox->setCurrentRow(i); break; } } if (i == numEntries) { // Style not found, fallback. + styleListBox->setCurrentRow(0); } // Set current size in the listbox. // If smoothly scalable, allow customizing one of the standard size slots, // otherwise just select the nearest available size. QString currentFamily = qtFamilies[familyListBox->currentItem()->text()]; QString currentStyle = qtStyles[styleListBox->currentItem()->text()]; bool canCustomize = dbase.isSmoothlyScalable(currentFamily, currentStyle); sizeListBox->setCurrentRow(nearestSizeRow(size, canCustomize)); // Set current size in the spinbox. sizeOfFont->setValue(QLocale::system().toDouble(sizeListBox->currentItem()->text())); } void DFontProperties::getFontList(QStringList& list, uint fontListCriteria) { QFontDatabase dbase; QStringList lstSys(dbase.families()); // if we have criteria; then check fonts before adding if (fontListCriteria) { QStringList lstFonts; for (QStringList::const_iterator it = lstSys.constBegin() ; it != lstSys.constEnd() ; ++it) { if ((fontListCriteria & FixedWidthFonts) > 0 && !dbase.isFixedPitch(*it)) { continue; } if (((fontListCriteria & (SmoothScalableFonts | ScalableFonts)) == ScalableFonts) && !dbase.isBitmapScalable(*it)) { continue; } if ((fontListCriteria & SmoothScalableFonts) > 0 && !dbase.isSmoothlyScalable(*it)) { continue; } lstFonts.append(*it); } if ((fontListCriteria & FixedWidthFonts) > 0) { // Fallback.. if there are no fixed fonts found, it's probably a // bug in the font server or Qt. In this case, just use 'fixed' if (lstFonts.count() == 0) { lstFonts.append(QLatin1String("fixed")); } } lstSys = lstFonts; } lstSys.sort(); list = lstSys; } void DFontProperties::Private::setFamilyBoxItems(const QStringList& fonts) { signalsAllowed = false; QStringList trfonts = translateFontNameList(fonts, &qtFamilies); familyListBox->clear(); familyListBox->addItems(trfonts); signalsAllowed = true; } void DFontProperties::Private::fillFamilyListBox(bool onlyFixedFonts) { QStringList fontList; getFontList(fontList, onlyFixedFonts ? FixedWidthFonts : 0); setFamilyBoxItems(fontList); } -/** Human-readable style identifiers returned by QFontDatabase::styleString() - * do not always survive round trip of QFont serialization/deserialization, - * causing wrong style in the style box to be highlighted when - * the chooser dialog is opened. This will cause the style to be changed - * when the dialog is closed and the user did not touch the style box. - * Hence, construct custom style identifiers sufficient for the purpose. +/** + * Human-readable style identifiers returned by QFontDatabase::styleString() + * do not always survive round trip of QFont serialization/deserialization, + * causing wrong style in the style box to be highlighted when + * the chooser dialog is opened. This will cause the style to be changed + * when the dialog is closed and the user did not touch the style box. + * Hence, construct custom style identifiers sufficient for the purpose. */ QString DFontProperties::Private::styleIdentifier(const QFont& font) { const QChar comma(QLatin1Char(',')); - return (QString::number(font.weight()) + comma + + return ( + QString::number(font.weight()) + comma + QString::number((int)font.style()) + comma + QString::number(font.stretch()) ); } void DFontProperties::Private::splitFontString(const QString& name, QString* family, QString* foundry) { int p1 = name.indexOf(QLatin1Char('[')); if (p1 < 0) { if (family) { *family = name.trimmed(); } if (foundry) { foundry->clear(); } } else { int p2 = name.indexOf(QLatin1Char(']'), p1); - p2 = p2 > p1 ? p2 : name.length(); + p2 = (p2 > p1) ? p2 : name.length(); if (family) { *family = name.left(p1).trimmed(); } if (foundry) { *foundry = name.mid(p1 + 1, p2 - p1 - 1).trimmed(); } } } QString DFontProperties::Private::translateFontName(const QString& name) { QString family, foundry; splitFontString(name, &family, &foundry); // Obtain any regular translations for the family and foundry. QString trFamily = QCoreApplication::translate("FontHelpers", family.toUtf8().constData(), "@item Font name"); QString trFoundry = foundry; if (!foundry.isEmpty()) { trFoundry = QCoreApplication::translate("FontHelpers", foundry.toUtf8().constData(), "@item Font foundry"); } // Assemble full translation. QString trfont; if (foundry.isEmpty()) { // i18n: Filter by which the translators can translate, or otherwise // operate on the font names not put up for regular translation. + trfont = QCoreApplication::translate("FontHelpers", "%1", "@item Font name").arg(trFamily); } else { // i18n: Filter by which the translators can translate, or otherwise // operate on the font names not put up for regular translation. + trfont = QCoreApplication::translate("FontHelpers", "%1 [%2]", "@item Font name [foundry]") .arg(trFamily).arg(trFoundry); } return trfont; } QStringList DFontProperties::Private::translateFontNameList(const QStringList& names, QHash* trToRawNames) { // Generic fonts, in the inverse of desired order. + QStringList genericNames; genericNames.append(QLatin1String("Monospace")); genericNames.append(QLatin1String("Serif")); genericNames.append(QLatin1String("Sans Serif")); // Translate fonts, but do not add generics to the list right away. QStringList trNames; QHash trMap; foreach (const QString& name, names) { QString trName = translateFontName(name); if (!genericNames.contains(name)) { trNames.append(trName); } trMap.insert(trName, name); } // Sort real fonts alphabetically. std::sort(trNames.begin(), trNames.end(), localeLessThan); // Prepend generic fonts, in the predefined order. foreach (const QString& genericName, genericNames) { QString trGenericName = translateFontName(genericName); if (trMap.contains(trGenericName)) { trNames.prepend(trGenericName); } } if (trToRawNames) { *trToRawNames = trMap; } return trNames; } } // namespace Digikam #include "moc_dfontproperties.cpp" diff --git a/core/libs/widgets/fonts/dfontselect.h b/core/libs/widgets/fonts/dfontselect.h index c211a21172..66020efd51 100644 --- a/core/libs/widgets/fonts/dfontselect.h +++ b/core/libs/widgets/fonts/dfontselect.h @@ -1,83 +1,83 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-12-23 * Description : a widget to select between system font or a custom font. * * Copyright (C) 2008-2020 by Gilles Caulier * * 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, 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. * * ============================================================ */ #ifndef DIGIKAM_DFONT_SELECT_H #define DIGIKAM_DFONT_SELECT_H // Qt includes #include // Local includes #include "dlayoutbox.h" #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT DFontSelect : public DHBox { Q_OBJECT public: enum FontMode { SystemFont=0, CustomFont }; public: explicit DFontSelect(const QString& text, QWidget* const parent=nullptr); virtual ~DFontSelect(); void setMode(FontMode mode); FontMode mode() const; - QFont font() const; + QFont font() const; void setFont(const QFont& font); Q_SIGNALS: void signalFontChanged(); protected: bool event(QEvent* e) override; private Q_SLOTS: void slotOpenFontDialog(); void slotChangeMode(int index); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_DFONT_SELECT_H