File indexing completed on 2022-09-27 13:41:19

0001 /*
0002 
0003     Copyright (C) 2001 The Kompany
0004           2002-2003 Ilya Konstantinov <kde-devel@future.shiny.co.il>
0005           2002-2003 Marcus Meissner <marcus@jet.franken.de>
0006           2003      Nadeem Hasan <nhasan@nadmm.com>
0007 
0008     This program is free software; you can redistribute it and/or modify
0009     it under the terms of the GNU General Public License as published by
0010     the Free Software Foundation; either version 2 of the License, or
0011     (at your option) any later version.
0012 
0013     This program is distributed in the hope that it will be useful,
0014     but WITHOUT ANY WARRANTY; without even the implied warranty of
0015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0016     GNU General Public License for more details.
0017 
0018     You should have received a copy of the GNU General Public License
0019     along with this program; if not, write to the Free Software
0020     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
0021 
0022 */
0023 
0024 #include "kamera.h"
0025 
0026 #include <QLabel>
0027 #include <QListView>
0028 #include <QVBoxLayout>
0029 #include <QApplication>
0030 #include <QStandardItemModel>
0031 #include <QMenu>
0032 #include <QIcon>
0033 
0034 #include <KConfig>
0035 #include <KMessageBox>
0036 #include <KLocalizedString>
0037 #include <KToolBar>
0038 #include <KProtocolInfo>
0039 #include <KActionCollection>
0040 #include <KConfigGroup>
0041 #include "kameraconfigdialog.h"
0042 #include "kameradevice.h"
0043 
0044 K_PLUGIN_CLASS_WITH_JSON(KKameraConfig, "kamera.json")
0045 
0046 Q_LOGGING_CATEGORY(KAMERA_KCONTROL, "kamera.kcontrol")
0047 
0048 // --------------- Camera control center module widget ---
0049 
0050 KKameraConfig::KKameraConfig(QWidget *parent, const QVariantList &)
0051     : KCModule(parent)
0052 {
0053 #ifdef DEBUG_KAMERA_KCONTROL
0054     QLoggingCategory::setFilterRules(QStringLiteral("kamera.kcontrol.debug = true"));
0055 #endif
0056     m_devicePopup = new QMenu(this);
0057     m_actions = new KActionCollection(this);
0058     m_config = new KConfig(KProtocolInfo::config(QStringLiteral("camera")), KConfig::SimpleConfig);
0059     m_deviceModel = new QStandardItemModel(this);
0060 
0061     m_context = gp_context_new();
0062     if (m_context) {
0063         // Register the callback functions
0064         gp_context_set_cancel_func(m_context, cbGPCancel, this);
0065         gp_context_set_idle_func(m_context, cbGPIdle, this);
0066 
0067         displayGPSuccessDialogue();
0068     } else {
0069         displayGPFailureDialogue();
0070     }
0071 }
0072 
0073 KKameraConfig::~KKameraConfig()
0074 {
0075     delete m_config;
0076 }
0077 
0078 void KKameraConfig::defaults()
0079 {
0080 }
0081 
0082 void KKameraConfig::displayGPFailureDialogue()
0083 {
0084     auto topLayout = new QVBoxLayout(this);
0085     topLayout->setSpacing(0);
0086     topLayout->setContentsMargins(0, 0, 0, 0);
0087     auto label = new QLabel(i18n("Unable to initialize the gPhoto2 libraries."), this);
0088     topLayout->addWidget(label);
0089 }
0090 
0091 void KKameraConfig::displayGPSuccessDialogue()
0092 {
0093     // set the kcontrol module buttons
0094     setButtons(Help | Apply );
0095 
0096     // create a layout with two vertical boxes
0097     auto topLayout = new QVBoxLayout(this);
0098     topLayout->setSpacing(0);
0099     topLayout->setContentsMargins(0, 0, 0, 0);
0100 
0101     m_toolbar = new KToolBar(this, "ToolBar");
0102     topLayout->addWidget(m_toolbar);
0103     m_toolbar->setMovable(false);
0104 
0105     // create list of devices - this is the large white box
0106     m_deviceSel = new QListView(this);
0107     topLayout->addWidget(m_deviceSel);
0108 
0109     m_deviceSel->setModel(m_deviceModel);
0110 
0111     connect(m_deviceSel, &QListView::customContextMenuRequested,
0112         this, &KKameraConfig::slot_deviceMenu);
0113     connect(m_deviceSel, &QListView::doubleClicked,
0114         this, &KKameraConfig::slot_configureCamera);
0115     connect(m_deviceSel, &QListView::activated,
0116         this, &KKameraConfig::slot_deviceSelected);
0117     connect(m_deviceSel, &QListView::clicked,
0118         this, &KKameraConfig::slot_deviceSelected);
0119 
0120     m_deviceSel->setViewMode(QListView::IconMode);
0121     m_deviceSel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
0122     m_deviceSel->setContextMenuPolicy(Qt::CustomContextMenu);
0123 
0124     // create actions, add to the toolbar
0125     QAction *act;
0126     act = m_actions->addAction(QStringLiteral("camera_add"));
0127     act->setIcon(QIcon::fromTheme(QStringLiteral("camera-photo")));
0128     act->setText(i18n("Add"));
0129     connect(act, &QAction::triggered, this, &KKameraConfig::slot_addCamera);
0130     act->setWhatsThis(i18n("Click this button to add a new camera."));
0131     m_toolbar->addAction(act);
0132     m_toolbar->addSeparator();
0133 
0134     act = m_actions->addAction(QStringLiteral("camera_test"));
0135     act->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok")));
0136     act->setText(i18n("Test"));
0137     connect(act, &QAction::triggered, this, &KKameraConfig::slot_testCamera);
0138     act->setWhatsThis(i18n("Click this button to test the connection to the selected camera."));
0139     m_toolbar->addAction(act);
0140 
0141     act = m_actions->addAction(QStringLiteral("camera_remove"));
0142     act->setIcon(QIcon::fromTheme(QStringLiteral("user-trash")));
0143     act->setText(i18n("Remove"));
0144     connect(act, &QAction::triggered, this, &KKameraConfig::slot_removeCamera);
0145     act->setWhatsThis(i18n("Click this button to remove the selected camera from the list."));
0146     m_toolbar->addAction(act);
0147 
0148     act = m_actions->addAction(QStringLiteral("camera_configure"));
0149     act->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
0150     act->setText(i18n("Configure..."));
0151     connect(act, &QAction::triggered, this, &KKameraConfig::slot_configureCamera);
0152     act->setWhatsThis(i18n("Click this button to change the configuration of the selected camera.<br><br>The availability of this feature and the contents of the Configuration dialog depend on the camera model."));
0153     m_toolbar->addAction(act);
0154 
0155     act = m_actions->addAction(QStringLiteral("camera_summary"));
0156     act->setIcon(QIcon::fromTheme(QStringLiteral("hwinfo")));
0157     act->setText(i18n("Information"));
0158     connect(act, &QAction::triggered, this, &KKameraConfig::slot_cameraSummary);
0159     act->setWhatsThis(i18n("Click this button to view a summary of the current status of the selected camera.<br><br>The availability of this feature and the contents of the Information dialog depend on the camera model."));
0160     m_toolbar->addAction(act);
0161     m_toolbar->addSeparator();
0162 
0163     act = m_actions->addAction(QStringLiteral("camera_cancel"));
0164     act->setIcon(QIcon::fromTheme(QStringLiteral("process-stop")));
0165     act->setText(i18n("Cancel"));
0166     connect(act, &QAction::triggered, this, &KKameraConfig::slot_cancelOperation);
0167     act->setWhatsThis(i18n("Click this button to cancel the current camera operation."));
0168     act->setEnabled(false);
0169     m_toolbar->addAction(act);
0170 }
0171 
0172 void KKameraConfig::populateDeviceListView()
0173 {
0174     m_deviceModel->clear();
0175     CameraDevicesMap::ConstIterator itEnd = m_devices.constEnd();
0176     for (CameraDevicesMap::ConstIterator it = m_devices.constBegin(); it != itEnd; ++it) {
0177         if (it.value()) {
0178             auto deviceItem = new QStandardItem;
0179             deviceItem->setEditable(false);
0180             deviceItem->setText(it.key());
0181             deviceItem->setIcon(QIcon::fromTheme(QStringLiteral("camera-photo")));
0182             m_deviceModel->appendRow(deviceItem);
0183         }
0184     }
0185     slot_deviceSelected(m_deviceSel->currentIndex());
0186 }
0187 
0188 void KKameraConfig::save()
0189 {
0190     CameraDevicesMap::Iterator it;
0191 
0192     for (it = m_devices.begin(); it != m_devices.end(); it++)
0193     {
0194         it.value()->save(m_config);
0195     }
0196     m_config->sync();
0197 }
0198 
0199 void KKameraConfig::load()
0200 {
0201     QStringList groupList = m_config->groupList();
0202 
0203     QStringList::Iterator it;
0204     int i, count;
0205     CameraList *list;
0206     CameraAbilitiesList *al;
0207     GPPortInfoList *il;
0208     const char *model, *value;
0209     KCamera *kcamera;
0210 
0211     for (it = groupList.begin(); it != groupList.end(); it++) {
0212         if (*it != QStringLiteral("<default>")) {
0213             KConfigGroup cg(m_config, *it);
0214             if (cg.readEntry("Path").contains(QStringLiteral("usb:"))) {
0215                 continue;
0216             }
0217 
0218             // Load configuration for Serial port cameras
0219             qCDebug(KAMERA_KCONTROL) << "Loading configuration for serial port camera: "
0220                                      << *it;
0221             kcamera = new KCamera(*it, cg.readEntry("Path"));
0222             connect(kcamera, qOverload<const QString&>(&KCamera::error),
0223                 this, qOverload<const QString&>(&KKameraConfig::slot_error));
0224 
0225             connect(kcamera, qOverload<const QString&, const QString&>(&KCamera::error),
0226                 this, qOverload<const QString&, const QString&>(&KKameraConfig::slot_error));
0227 
0228             kcamera->load(m_config);
0229             m_devices[*it] = kcamera;
0230         }
0231     }
0232     m_cancelPending = false;
0233 
0234     gp_list_new (&list);
0235 
0236     gp_abilities_list_new (&al);
0237     gp_abilities_list_load (al, m_context);
0238     gp_port_info_list_new (&il);
0239     gp_port_info_list_load (il);
0240     gp_abilities_list_detect (al, il, list, m_context);
0241     gp_abilities_list_free (al);
0242     gp_port_info_list_free (il);
0243 
0244     count = gp_list_count (list);
0245 
0246     QMap<QString,QString>   ports, names;
0247 
0248     for (i = 0 ; i<count ; i++) {
0249         gp_list_get_name  (list, i, &model);
0250         gp_list_get_value (list, i, &value);
0251 
0252         ports[value] = model;
0253         if (!strcmp(value,"usb:")) {
0254             names[model] = value;
0255         }
0256     }
0257 
0258     if (ports.contains(QStringLiteral("usb:")) && names[ports[QStringLiteral("usb:")]]!=QStringLiteral("usb:")) {
0259         ports.remove(QStringLiteral("usb:"));
0260     }
0261 
0262     QMap<QString,QString>::iterator portit;
0263 
0264     for (portit = ports.begin() ; portit != ports.end(); portit++) {
0265         qCDebug(KAMERA_KCONTROL) << "Adding USB camera: " << portit.value() << " at " << portit.key();
0266 
0267         kcamera = new KCamera(portit.value(), portit.key());
0268 
0269         connect(kcamera, qOverload<const QString&>(&KCamera::error),
0270             this, qOverload<const QString&>(&KKameraConfig::slot_error));
0271 
0272         connect(kcamera, qOverload<const QString&, const QString&>(&KCamera::error),
0273             this, qOverload<const QString&, const QString&>(&KKameraConfig::slot_error));
0274 
0275         m_devices[portit.value()] = kcamera;
0276     }
0277     populateDeviceListView();
0278 
0279     gp_list_free (list);
0280 }
0281 
0282 void KKameraConfig::beforeCameraOperation()
0283 {
0284     m_cancelPending = false;
0285 
0286     m_actions->action(QStringLiteral("camera_test"))->setEnabled(false);
0287     m_actions->action(QStringLiteral("camera_remove"))->setEnabled(false);
0288     m_actions->action(QStringLiteral("camera_configure"))->setEnabled(false);
0289     m_actions->action(QStringLiteral("camera_summary"))->setEnabled(false);
0290 
0291     m_actions->action(QStringLiteral("camera_cancel"))->setEnabled(true);
0292 }
0293 
0294 void KKameraConfig::afterCameraOperation()
0295 {
0296     m_actions->action(QStringLiteral("camera_cancel"))->setEnabled(false);
0297 
0298     // if we're regaining control after a Cancel...
0299     if (m_cancelPending) {
0300         qApp->restoreOverrideCursor();
0301         m_cancelPending = false;
0302     }
0303 
0304     // if any item was selected before the operation was run
0305     // it makes sense for the relevant toolbar buttons to be enabled
0306     slot_deviceSelected(m_deviceSel->currentIndex());
0307 }
0308 
0309 QString KKameraConfig::suggestName(const QString &name)
0310 {
0311     QString new_name = name;
0312     new_name.remove(QLatin1Char('/')); // we cannot have a slash in a URI's host
0313 
0314     if (!m_devices.contains(new_name)) return new_name;
0315 
0316     // try new names with a number appended until we find a free one
0317     int i = 1;
0318     while (i++ < 0xffff) {
0319         new_name = name + QStringLiteral(" (") + QString::number(i) + QLatin1Char(')');
0320         if (!m_devices.contains(new_name)) return new_name;
0321     }
0322 
0323     return {};
0324 }
0325 
0326 void KKameraConfig::slot_addCamera()
0327 {
0328         auto m_device = new KCamera(QString(), QString());
0329     connect(m_device, qOverload<const QString&>(&KCamera::error),
0330                 this, qOverload<const QString&>(&KKameraConfig::slot_error));
0331 
0332         connect(m_device, qOverload<const QString&, const QString&>(&KCamera::error),
0333                 this, qOverload<const QString&,  const QString&>(&KKameraConfig::slot_error));
0334 
0335     KameraDeviceSelectDialog dialog(this, m_device);
0336     if (dialog.exec() == QDialog::Accepted) {
0337         dialog.save();
0338         m_device->setName(suggestName(m_device->model()));
0339         m_devices.insert(m_device->name(), m_device);
0340         populateDeviceListView();
0341         Q_EMIT changed(true);
0342     } else {
0343         delete m_device;
0344     }
0345 }
0346 
0347 void KKameraConfig::slot_removeCamera()
0348 {
0349     const QString name = m_deviceSel->currentIndex().data(Qt::DisplayRole).toString();
0350     if (m_devices.contains(name)) {
0351         KCamera *m_device = m_devices.value(name);
0352         m_devices.remove(name);
0353         delete m_device;
0354         m_config->deleteGroup(name);
0355         populateDeviceListView();
0356         Q_EMIT changed(true);
0357     }
0358 }
0359 
0360 void KKameraConfig::slot_testCamera()
0361 {
0362     beforeCameraOperation();
0363 
0364     const QString name = m_deviceSel->currentIndex().data(Qt::DisplayRole).toString();
0365     if (m_devices.contains(name)) {
0366         KCamera *m_device = m_devices.value(name);
0367         if (m_device->test()) {
0368             KMessageBox::information(this, i18n("Camera test was successful."));
0369         }
0370     }
0371 
0372     afterCameraOperation();
0373 }
0374 
0375 void KKameraConfig::slot_configureCamera()
0376 {
0377     const QString name = m_deviceSel->currentIndex().data(Qt::DisplayRole).toString();
0378     if (m_devices.contains(name)) {
0379         KCamera *m_device = m_devices[name];
0380         m_device->configure();
0381     }
0382 }
0383 
0384 void KKameraConfig::slot_cameraSummary()
0385 {
0386     const QString name = m_deviceSel->currentIndex().data(Qt::DisplayRole).toString();
0387     if (m_devices.contains(name)) {
0388         KCamera *m_device = m_devices[name];
0389         QString summary = m_device->summary();
0390         if (!summary.isNull()) {
0391             KMessageBox::information(this, summary);
0392         }
0393     }
0394 }
0395 
0396 void KKameraConfig::slot_cancelOperation()
0397 {
0398     m_cancelPending = true;
0399     // Prevent the user from keeping clicking Cancel
0400     m_actions->action(QStringLiteral("camera_cancel"))->setEnabled(false);
0401     // and indicate that the click on Cancel did have some effect
0402     qApp->setOverrideCursor(Qt::WaitCursor);
0403 }
0404 
0405 void KKameraConfig::slot_deviceMenu(const QPoint &point)
0406 {
0407     QModelIndex index = m_deviceSel->indexAt(point);
0408     if (index.isValid()) {
0409         m_devicePopup->clear();
0410         m_devicePopup->addAction(m_actions->action(QStringLiteral("camera_test")));
0411         m_devicePopup->addAction(m_actions->action(QStringLiteral("camera_remove")));
0412         m_devicePopup->addAction(m_actions->action(QStringLiteral("camera_configure")));
0413         m_devicePopup->addAction(m_actions->action(QStringLiteral("camera_summary")));
0414         m_devicePopup->exec(m_deviceSel->viewport()->mapToGlobal(point));
0415     }
0416 }
0417 
0418 void KKameraConfig::slot_deviceSelected(const QModelIndex &index)
0419 {
0420     bool isValid = index.isValid();
0421     m_actions->action(QStringLiteral("camera_test"))->setEnabled(isValid);
0422     m_actions->action(QStringLiteral("camera_remove"))->setEnabled(isValid);
0423     m_actions->action(QStringLiteral("camera_configure"))->setEnabled(isValid);
0424     m_actions->action(QStringLiteral("camera_summary"))->setEnabled(isValid);
0425 }
0426 
0427 void KKameraConfig::cbGPIdle(GPContext * /*context*/, void * /*data*/)
0428 {
0429     /*KKameraConfig *self( reinterpret_cast<KKameraConfig*>(data) );*/
0430     qApp->processEvents();
0431 }
0432 
0433 GPContextFeedback KKameraConfig::cbGPCancel(GPContext * /*context*/, void *data)
0434 {
0435     auto self( reinterpret_cast<KKameraConfig*>(data) );
0436 
0437     // Since in practice no camera driver supports idle callbacks yet,
0438     // we'll use the cancel callback as opportunity to process events
0439     qApp->processEvents();
0440 
0441     // If a cancel request is pending, ask gphoto to cancel
0442     if (self->m_cancelPending) {
0443         return GP_CONTEXT_FEEDBACK_CANCEL;
0444     } else {
0445         return GP_CONTEXT_FEEDBACK_OK;
0446     }
0447 }
0448 
0449 QString KKameraConfig::quickHelp() const
0450 {
0451     return i18n("<h1>Digital Camera</h1>\n"
0452       "This module allows you to configure support for your digital camera.\n"
0453       "You need to select the camera's model and the port it is connected\n"
0454       "to on your computer (e.g. USB, Serial, Firewire). If your camera does not\n"
0455       "appear on the list of <i>Supported Cameras</i>, go to the\n"
0456       "<a href=\"http://www.gphoto.org\">GPhoto web site</a> for a possible update.<br><br>\n"
0457       "To view and download images from the digital camera, go to the address\n"
0458       "<a href=\"camera:/\">camera:/</a> in Konqueror and other KDE applications.");
0459 }
0460 
0461 void KKameraConfig::slot_error(const QString &message)
0462 {
0463     KMessageBox::error(this, message);
0464 }
0465 
0466 void KKameraConfig::slot_error(const QString &message, const QString &details)
0467 {
0468     KMessageBox::detailedError(this, message, details);
0469 }
0470 
0471 #include "kamera.moc"