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