File indexing completed on 2025-01-19 03:59:41

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2003-02-03
0007  * Description : Cameras list container
0008  *
0009  * SPDX-FileCopyrightText: 2003-2005 by Renchi Raju <renchi dot raju at gmail dot com>
0010  * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "cameralist.h"
0017 
0018 // Qt includes
0019 
0020 #include <QDateTime>
0021 #include <QDomDocument>
0022 #include <QDomElement>
0023 #include <QFile>
0024 #include <QString>
0025 #include <QTextStream>
0026 #include <QApplication>
0027 #include <QMessageBox>
0028 
0029 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
0030 #   include <QTextCodec>
0031 #endif
0032 
0033 // KDE includes
0034 
0035 #include <klocalizedstring.h>
0036 
0037 // Local includes
0038 
0039 #include "cameratype.h"
0040 #include "gpcamera.h"
0041 #include "digikam_debug.h"
0042 
0043 namespace Digikam
0044 {
0045 
0046 CameraList* CameraList::m_defaultList = nullptr;
0047 
0048 CameraList* CameraList::defaultList()
0049 {
0050     return m_defaultList;
0051 }
0052 
0053 class Q_DECL_HIDDEN CameraList::Private
0054 {
0055 public:
0056 
0057     explicit Private()
0058       : modified(false)
0059     {
0060     }
0061 
0062     bool               modified;
0063 
0064     QList<CameraType*> clist;
0065     QString            file;
0066 };
0067 
0068 CameraList::CameraList(QObject* const parent, const QString& file)
0069     : QObject(parent),
0070       d      (new Private)
0071 {
0072     d->file = file;
0073     qCDebug(DIGIKAM_GENERAL_LOG) << "Camera XML data: " << d->file;
0074 
0075     if (!m_defaultList)
0076     {
0077         m_defaultList = this;
0078     }
0079 }
0080 
0081 CameraList::~CameraList()
0082 {
0083     save();
0084     clear();
0085     delete d;
0086 
0087     if (m_defaultList == this)
0088     {
0089         m_defaultList = nullptr;
0090     }
0091 }
0092 
0093 bool CameraList::load()
0094 {
0095     d->modified = false;
0096 
0097     QFile cfile(d->file);
0098 
0099     if (!cfile.open(QIODevice::ReadOnly))
0100     {
0101         return false;
0102     }
0103 
0104     QDomDocument doc(QLatin1String("cameralist"));
0105 
0106     if (!doc.setContent(&cfile))
0107     {
0108         cfile.close();
0109 
0110         return false;
0111     }
0112 
0113     QDomElement docElem = doc.documentElement();
0114 
0115     if (docElem.tagName() != QLatin1String("cameralist"))
0116     {
0117         cfile.close();
0118 
0119         return false;
0120     }
0121 
0122     for (QDomNode n = docElem.firstChild() ; !n.isNull() ; n = n.nextSibling())
0123     {
0124         QDomElement e = n.toElement();
0125 
0126         if (e.isNull())
0127         {
0128             continue;
0129         }
0130 
0131         if (e.tagName() != QLatin1String("item"))
0132         {
0133             continue;
0134         }
0135 
0136         QString title           = e.attribute(QLatin1String("title"));
0137         QString model           = e.attribute(QLatin1String("model"));
0138         QString port            = e.attribute(QLatin1String("port"));
0139         QString path            = e.attribute(QLatin1String("path"));
0140         int sn                  = e.attribute(QLatin1String("startingnumber")).toInt();
0141         CameraType* const ctype = new CameraType(title, model, port, path, sn);
0142         insertPrivate(ctype);
0143     }
0144 
0145     cfile.close();
0146 
0147     return true;
0148 }
0149 
0150 bool CameraList::save()
0151 {
0152     // If not modified don't save the file
0153 
0154     if (!d->modified)
0155     {
0156         return true;
0157     }
0158 
0159     QDomDocument doc(QLatin1String("cameralist"));
0160     doc.setContent(QLatin1String("<!DOCTYPE XMLCameraList><cameralist version=\"1.2\" client=\"digikam\"/>"));
0161 
0162     QDomElement docElem = doc.documentElement();
0163 
0164     Q_FOREACH (CameraType* const ctype, d->clist)
0165     {
0166         QDomElement elem = doc.createElement(QLatin1String("item"));
0167         elem.setAttribute(QLatin1String("title"),          ctype->title());
0168         elem.setAttribute(QLatin1String("model"),          ctype->model());
0169         elem.setAttribute(QLatin1String("port"),           ctype->port());
0170         elem.setAttribute(QLatin1String("path"),           ctype->path());
0171         elem.setAttribute(QLatin1String("startingnumber"), QString::number(ctype->startingNumber()));
0172         docElem.appendChild(elem);
0173     }
0174 
0175     QFile cfile(d->file);
0176 
0177     if (!cfile.open(QIODevice::WriteOnly))
0178     {
0179         qCDebug(DIGIKAM_GENERAL_LOG) << "Cannot open Camera XML file to save data (" << d->file << ")";
0180         return false;
0181     }
0182 
0183     QTextStream stream(&cfile);
0184 
0185 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
0186 
0187     // In Qt5 only. Qt6 uses UTF-8 by default.
0188 
0189     stream.setCodec(QTextCodec::codecForName("UTF-8"));
0190 
0191 #endif
0192 
0193     stream.setAutoDetectUnicode(true);
0194     stream << doc.toString();
0195     cfile.close();
0196 
0197     d->modified = false;
0198 
0199     return true;
0200 }
0201 
0202 void CameraList::insert(CameraType* const ctype)
0203 {
0204     if (!ctype)
0205     {
0206         return;
0207     }
0208 
0209     d->modified = true;
0210     insertPrivate(ctype);
0211 }
0212 
0213 void CameraList::remove(CameraType* const ctype)
0214 {
0215     if (!ctype)
0216     {
0217         return;
0218     }
0219 
0220     d->modified = true;
0221     removePrivate(ctype);
0222 }
0223 
0224 void CameraList::insertPrivate(CameraType* const ctype)
0225 {
0226     if (!ctype)
0227     {
0228         return;
0229     }
0230 
0231     d->clist.append(ctype);
0232 
0233     Q_EMIT signalCameraAdded(ctype);
0234 }
0235 
0236 void CameraList::removePrivate(CameraType* const ctype)
0237 {
0238     if (!ctype)
0239     {
0240         return;
0241     }
0242 
0243     Q_EMIT signalCameraRemoved(ctype->action());
0244 
0245     int i = d->clist.indexOf(ctype);
0246 
0247     if (i != -1)
0248     {
0249         delete d->clist.takeAt(i);
0250     }
0251 }
0252 
0253 void CameraList::clear()
0254 {
0255     while (!d->clist.isEmpty())
0256     {
0257         d->modified = true;
0258         removePrivate(d->clist.first());
0259     }
0260 }
0261 
0262 QList<CameraType*>* CameraList::cameraList() const
0263 {
0264     return &d->clist;
0265 }
0266 
0267 CameraType* CameraList::find(const QString& title) const
0268 {
0269     Q_FOREACH (CameraType* const ctype, d->clist)
0270     {
0271         if (ctype->title() == title)
0272         {   // cppcheck-suppress useStlAlgorithm
0273             return ctype;
0274         }
0275     }
0276 
0277     return nullptr;
0278 }
0279 
0280 CameraType* CameraList::autoDetect(bool& retry)
0281 {
0282     retry = false;
0283 
0284     QString model, port;
0285 
0286     if (GPCamera::autoDetect(model, port) != 0)
0287     {
0288         retry = (QMessageBox::warning(qApp->activeWindow(), qApp->applicationName(),
0289                                       i18n("Failed to auto-detect camera; "
0290                                            "please make sure it is connected "
0291                                            "properly and is turned on. "
0292                                            "Would you like to try again?"),
0293                                       QMessageBox::Yes | QMessageBox::No)
0294                  == QMessageBox::Yes);
0295 
0296         return nullptr;
0297     }
0298 
0299     // Check if the camera is already in the list
0300 
0301     Q_FOREACH (CameraType* const ctype, d->clist)
0302     {
0303         // We can get away with checking only the model, as the auto-detection
0304         // works only for usb cameras. so the port is always usb:
0305 
0306         if (ctype->model() == model)
0307         {   // cppcheck-suppress useStlAlgorithm
0308             return ctype;
0309         }
0310     }
0311 
0312     // Looks like a new camera
0313 
0314     // NOTE: libgphoto2 now (2.1.4+) expects port names to be
0315     // something like "usb:001,012". but on linux these port numbers
0316     // will change every time camera is reconnected. gphoto port funcs
0317     // also allow regexp match, so the safe bet is to just pass in
0318     // "usb:" and cross your fingers that user doesn't have multiple cameras
0319     // connected at the same time (whack them if they do).
0320 
0321     if (port.startsWith(QLatin1String("usb:")))
0322     {
0323         port = QLatin1String("usb:");
0324     }
0325 
0326     CameraType* const ctype = new CameraType(model, model, port, QLatin1String("/"), 1);
0327     insert(ctype);
0328 
0329     return ctype;
0330 }
0331 
0332 bool CameraList::findConnectedCamera(int vendorId, int productId, QString& model, QString& port)
0333 {
0334     return GPCamera::findConnectedUsbCamera(vendorId, productId, model, port);
0335 }
0336 
0337 bool CameraList::changeCameraStartIndex(const QString& cameraTitle, int startIndex)
0338 {
0339     CameraType* const cam = find(cameraTitle);
0340 
0341     if (cam)
0342     {
0343         cam->setStartingNumber(startIndex);
0344         d->modified = true;
0345         save();
0346 
0347         return true;
0348     }
0349 
0350     return false;
0351 }
0352 
0353 } // namespace Digikam
0354 
0355 #include "moc_cameralist.cpp"