File indexing completed on 2024-03-24 04:21:45
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 "kameradevice.h" 0011 0012 #include <QComboBox> 0013 #include <QGroupBox> 0014 #include <QHBoxLayout> 0015 #include <QLabel> 0016 #include <QListView> 0017 #include <QPushButton> 0018 #include <QRadioButton> 0019 #include <QStackedWidget> 0020 #include <QStandardItemModel> 0021 #include <QVBoxLayout> 0022 0023 #include <KConfig> 0024 #include <KConfigGroup> 0025 #include <KLocalizedString> 0026 #include <KMessageBox> 0027 0028 extern "C" { 0029 #include <gphoto2.h> 0030 } 0031 0032 #include "kameraconfigdialog.h" 0033 0034 // Define some parts of the old API 0035 #define GP_PROMPT_OK 0 0036 #define GP_PROMPT_CANCEL -1 0037 0038 static const int INDEX_NONE = 0; 0039 static const int INDEX_SERIAL = 1; 0040 static const int INDEX_USB = 2; 0041 static GPContext *glob_context = nullptr; 0042 0043 #ifdef DEBUG 0044 static void gp_errordumper(GPLogLevel level, const char *domain, const char *str, void *data) 0045 { 0046 qCDebug(KAMERA_KCONTROL) << "GP_LOG: " << str; 0047 } 0048 0049 // Use with 0050 // gp_log_add_func(GP_LOG_LEVEL, gp_errordumper, NULL); 0051 // where LEVEL = { DATA, DEBUG, ERROR, VERBOSE, ALL } 0052 #endif 0053 0054 KCamera::KCamera(const QString &name, const QString &path) 0055 { 0056 m_name = name; 0057 m_model = name; 0058 m_path = path; 0059 m_camera = nullptr; 0060 m_abilitylist = nullptr; 0061 } 0062 0063 KCamera::~KCamera() 0064 { 0065 if (m_camera) 0066 gp_camera_free(m_camera); 0067 if (m_abilitylist) 0068 gp_abilities_list_free(m_abilitylist); 0069 } 0070 0071 bool KCamera::initInformation() 0072 { 0073 if (m_model.isNull()) { 0074 return false; 0075 } 0076 0077 if (gp_abilities_list_new(&m_abilitylist) != GP_OK) { 0078 Q_EMIT error(i18n("Could not allocate memory for the abilities list.")); 0079 return false; 0080 } 0081 if (gp_abilities_list_load(m_abilitylist, glob_context) != GP_OK) { 0082 Q_EMIT error(i18n("Could not load ability list.")); 0083 return false; 0084 } 0085 int index = gp_abilities_list_lookup_model(m_abilitylist, m_model.toLocal8Bit().data()); 0086 if (index < 0) { 0087 Q_EMIT error( 0088 i18n("Description of abilities for camera %1 is not available." 0089 " Configuration options may be incorrect.", 0090 m_model)); 0091 return false; 0092 } 0093 gp_abilities_list_get_abilities(m_abilitylist, index, &m_abilities); 0094 return true; 0095 } 0096 0097 bool KCamera::initCamera() 0098 { 0099 if (m_camera) { 0100 return m_camera; 0101 } else { 0102 int result; 0103 0104 initInformation(); 0105 0106 if (m_model.isNull() || m_path.isNull()) { 0107 return false; 0108 } 0109 0110 result = gp_camera_new(&m_camera); 0111 if (result != GP_OK) { 0112 // m_camera is not initialized, so we cannot get result as string 0113 Q_EMIT error(i18n("Could not access driver. Check your gPhoto2 installation.")); 0114 return false; 0115 } 0116 0117 // set the camera's model 0118 GPPortInfo info; 0119 GPPortInfoList *il; 0120 gp_port_info_list_new(&il); 0121 gp_port_info_list_load(il); 0122 gp_port_info_list_get_info(il, gp_port_info_list_lookup_path(il, m_path.toLocal8Bit().data()), &info); 0123 gp_camera_set_abilities(m_camera, m_abilities); 0124 gp_camera_set_port_info(m_camera, info); 0125 gp_port_info_list_free(il); 0126 0127 // this might take some time (esp. for non-existent camera) - better be done asynchronously 0128 result = gp_camera_init(m_camera, glob_context); 0129 if (result != GP_OK) { 0130 gp_camera_free(m_camera); 0131 m_camera = nullptr; 0132 Q_EMIT error(i18n("Unable to initialize camera. Check your port settings and camera connectivity and try again."), 0133 QString::fromLocal8Bit(gp_result_as_string(result))); 0134 return false; 0135 } 0136 0137 return m_camera; 0138 } 0139 } 0140 0141 Camera *KCamera::camera() 0142 { 0143 initCamera(); 0144 return m_camera; 0145 } 0146 0147 QString KCamera::summary() 0148 { 0149 int result; 0150 CameraText summary; 0151 0152 initCamera(); 0153 0154 result = gp_camera_get_summary(m_camera, &summary, glob_context); 0155 if (result != GP_OK) { 0156 return i18n("No camera summary information is available.\n"); 0157 } 0158 return QString::fromLocal8Bit(summary.text); 0159 } 0160 0161 bool KCamera::configure() 0162 { 0163 CameraWidget *window; 0164 int result; 0165 0166 initCamera(); 0167 0168 result = gp_camera_get_config(m_camera, &window, glob_context); 0169 if (result != GP_OK) { 0170 Q_EMIT error(i18n("Camera configuration failed."), QString::fromLocal8Bit(gp_result_as_string(result))); 0171 return false; 0172 } 0173 0174 KameraConfigDialog kcd(m_camera, window); 0175 result = kcd.exec() ? GP_PROMPT_OK : GP_PROMPT_CANCEL; 0176 0177 if (result == GP_PROMPT_OK) { 0178 result = gp_camera_set_config(m_camera, window, glob_context); 0179 if (result != GP_OK) { 0180 Q_EMIT error(i18n("Camera configuration failed."), QString::fromLocal8Bit(gp_result_as_string(result))); 0181 return false; 0182 } 0183 } 0184 0185 return true; 0186 } 0187 0188 bool KCamera::test() 0189 { 0190 // TODO: Make testing non-blocking (maybe via KIO?) 0191 // Currently, a failed serial test times out at about 30 sec. 0192 return camera() != nullptr; 0193 } 0194 0195 void KCamera::load(KConfig *config) 0196 { 0197 KConfigGroup group = config->group(m_name); 0198 if (m_model.isNull()) { 0199 m_model = group.readEntry("Model"); 0200 } 0201 if (m_path.isNull()) { 0202 m_path = group.readEntry("Path"); 0203 } 0204 invalidateCamera(); 0205 } 0206 0207 void KCamera::save(KConfig *config) 0208 { 0209 KConfigGroup group = config->group(m_name); 0210 group.writeEntry("Model", m_model); 0211 group.writeEntry("Path", m_path); 0212 } 0213 0214 QString KCamera::portName() 0215 { 0216 const QString port = m_path.left(m_path.indexOf(QLatin1Char(':'))).toLower(); 0217 if (port == QStringLiteral("serial")) 0218 return i18n("Serial"); 0219 if (port == QStringLiteral("usb")) 0220 return i18n("USB"); 0221 return i18n("Unknown port"); 0222 } 0223 0224 void KCamera::setName(const QString &name) 0225 { 0226 m_name = name; 0227 } 0228 0229 void KCamera::setModel(const QString &model) 0230 { 0231 m_model = model; 0232 invalidateCamera(); 0233 initInformation(); 0234 } 0235 0236 void KCamera::setPath(const QString &path) 0237 { 0238 m_path = path; 0239 invalidateCamera(); 0240 } 0241 0242 void KCamera::invalidateCamera() 0243 { 0244 if (m_camera) { 0245 gp_camera_free(m_camera); 0246 m_camera = nullptr; 0247 } 0248 } 0249 0250 bool KCamera::isTestable() const 0251 { 0252 return true; 0253 } 0254 0255 bool KCamera::isConfigurable() 0256 { 0257 initInformation(); 0258 return m_abilities.operations & GP_OPERATION_CONFIG; 0259 } 0260 0261 QStringList KCamera::supportedPorts() 0262 { 0263 initInformation(); 0264 QStringList ports; 0265 if (m_abilities.port & GP_PORT_SERIAL) { 0266 ports.append(QStringLiteral("serial")); 0267 } 0268 if (m_abilities.port & GP_PORT_USB) { 0269 ports.append(QStringLiteral("usb")); 0270 } 0271 return ports; 0272 } 0273 0274 CameraAbilities KCamera::abilities() const 0275 { 0276 return m_abilities; 0277 } 0278 0279 // ---------- KameraSelectCamera ------------ 0280 0281 KameraDeviceSelectDialog::KameraDeviceSelectDialog(QWidget *parent, KCamera *device) 0282 : QDialog(parent) 0283 { 0284 setWindowTitle(i18n("Select Camera Device")); 0285 0286 setModal(true); 0287 m_device = device; 0288 connect(m_device, qOverload<const QString &>(&KCamera::error), this, qOverload<const QString &>(&KameraDeviceSelectDialog::slot_error)); 0289 0290 connect(m_device, 0291 qOverload<const QString &, const QString &>(&KCamera::error), 0292 this, 0293 qOverload<const QString &, const QString &>(&KameraDeviceSelectDialog::slot_error)); 0294 0295 // a layout with horizontal boxes - this gives the two columns 0296 auto topLayout = new QHBoxLayout(this); 0297 0298 // the models list 0299 m_modelSel = new QListView(this); 0300 m_model = new QStandardItemModel(this); 0301 m_model->setColumnCount(1); 0302 m_model->setHeaderData(0, Qt::Horizontal, i18nc("@title:column", "Supported Cameras")); 0303 m_modelSel->setModel(m_model); 0304 0305 topLayout->addWidget(m_modelSel); 0306 connect(m_modelSel, &QListView::activated, this, &KameraDeviceSelectDialog::slot_setModel); 0307 connect(m_modelSel, &QListView::clicked, this, &KameraDeviceSelectDialog::slot_setModel); 0308 0309 // make sure listview only as wide as it needs to be 0310 m_modelSel->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); 0311 0312 auto rightLayout = new QVBoxLayout(); 0313 rightLayout->setContentsMargins(0, 0, 0, 0); 0314 topLayout->addLayout(rightLayout); 0315 0316 m_portSelectGroup = new QGroupBox(i18n("Port"), this); 0317 auto vertLayout = new QVBoxLayout; 0318 m_portSelectGroup->setLayout(vertLayout); 0319 m_portSelectGroup->setMinimumSize(100, 120); 0320 rightLayout->addWidget(m_portSelectGroup); 0321 // Create port type selection radiobuttons. 0322 m_serialRB = new QRadioButton(i18n("Serial")); 0323 vertLayout->addWidget(m_serialRB); 0324 m_serialRB->setWhatsThis( 0325 i18n("If this option is checked, the camera has " 0326 "to be connected to one of the computer's serial ports (known as COM " 0327 "ports in Microsoft Windows.)")); 0328 m_USBRB = new QRadioButton(i18n("USB")); 0329 vertLayout->addWidget(m_USBRB); 0330 m_USBRB->setWhatsThis( 0331 i18n("If this option is checked, the camera has to " 0332 "be connected to one of the computer's USB ports, or to a USB hub.")); 0333 0334 m_portSettingsGroup = new QGroupBox(i18n("Port Settings"), this); 0335 auto lay = new QVBoxLayout; 0336 m_portSettingsGroup->setLayout(lay); 0337 rightLayout->addWidget(m_portSettingsGroup); 0338 // Create port settings widget stack 0339 m_settingsStack = new QStackedWidget; 0340 auto grid2 = new QWidget(m_settingsStack); 0341 auto gridLayout2 = new QGridLayout(grid2); 0342 grid2->setLayout(gridLayout2); 0343 auto label2 = new QLabel(i18n("Port"), grid2); 0344 gridLayout2->addWidget(label2, 0, 0, Qt::AlignLeft); 0345 0346 lay->addWidget(grid2); 0347 lay->addWidget(m_settingsStack); 0348 connect(m_serialRB, &QRadioButton::toggled, this, &KameraDeviceSelectDialog::changeCurrentIndex); 0349 connect(m_USBRB, &QRadioButton::toggled, this, &KameraDeviceSelectDialog::changeCurrentIndex); 0350 0351 // none tab 0352 m_settingsStack->insertWidget(INDEX_NONE, new QLabel(i18n("No port type selected."), m_settingsStack)); 0353 0354 // serial tab 0355 auto grid = new QWidget(m_settingsStack); 0356 auto gridLayout = new QGridLayout(grid); 0357 grid->setLayout(gridLayout); 0358 0359 auto label = new QLabel(i18n("Port:"), grid); 0360 m_serialPortCombo = new QComboBox(grid); 0361 m_serialPortCombo->setEditable(true); 0362 m_serialPortCombo->setWhatsThis( 0363 i18n("Specify here the serial port to " 0364 "which you connect the camera.")); 0365 0366 gridLayout->addWidget(label, 1, 0, Qt::AlignLeft); 0367 gridLayout->addWidget(m_serialPortCombo, 1, 1, Qt::AlignRight); 0368 m_settingsStack->insertWidget(INDEX_SERIAL, grid); 0369 0370 m_settingsStack->insertWidget(INDEX_USB, new QLabel(i18n("No further configuration is required for USB cameras."), m_settingsStack)); 0371 0372 // Add the ok/cancel buttons to the bottom of the right side 0373 m_OkCancelButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 0374 QPushButton *okButton = m_OkCancelButtonBox->button(QDialogButtonBox::Ok); 0375 QPushButton *cancelButton = m_OkCancelButtonBox->button(QDialogButtonBox::Cancel); 0376 okButton->setDefault(true); 0377 // Set false enabled to allow the use of an equivalent 0378 // to enableButtonOk(true) in slot_setModel. 0379 okButton->setEnabled(false); 0380 okButton->setShortcut(Qt::CTRL | Qt::Key_Return); 0381 connect(okButton, &QPushButton::clicked, this, &KameraDeviceSelectDialog::accept); 0382 connect(cancelButton, &QPushButton::clicked, this, &KameraDeviceSelectDialog::close); 0383 // add a spacer 0384 rightLayout->addStretch(); 0385 0386 rightLayout->addWidget(m_OkCancelButtonBox); 0387 0388 // query gphoto2 for existing serial ports 0389 GPPortInfoList *list; 0390 GPPortInfo info; 0391 int gphoto_ports = 0; 0392 gp_port_info_list_new(&list); 0393 if (gp_port_info_list_load(list) >= 0) { 0394 gphoto_ports = gp_port_info_list_count(list); 0395 } 0396 for (int i = 0; i < gphoto_ports; i++) { 0397 if (gp_port_info_list_get_info(list, i, &info) >= 0) { 0398 char *xpath; 0399 gp_port_info_get_path(info, &xpath); 0400 if (strncmp(xpath, "serial:", 7) == 0) { 0401 m_serialPortCombo->addItem(QString::fromLocal8Bit(xpath).mid(7)); 0402 } 0403 } 0404 } 0405 gp_port_info_list_free(list); 0406 0407 populateCameraListView(); 0408 load(); 0409 0410 m_portSelectGroup->setEnabled(false); 0411 m_portSettingsGroup->setEnabled(false); 0412 } 0413 0414 void KameraDeviceSelectDialog::changeCurrentIndex() 0415 { 0416 auto send = dynamic_cast<QRadioButton *>(sender()); 0417 if (send) { 0418 if (send == m_serialRB) { 0419 m_settingsStack->setCurrentIndex(INDEX_SERIAL); 0420 } else if (send == m_USBRB) { 0421 m_settingsStack->setCurrentIndex(INDEX_USB); 0422 } 0423 } 0424 } 0425 0426 bool KameraDeviceSelectDialog::populateCameraListView() 0427 { 0428 gp_abilities_list_new(&m_device->m_abilitylist); 0429 gp_abilities_list_load(m_device->m_abilitylist, glob_context); 0430 int numCams = gp_abilities_list_count(m_device->m_abilitylist); 0431 CameraAbilities a; 0432 0433 if (numCams < 0) { 0434 // XXX libgphoto2 failed to get te camera list 0435 return false; 0436 } else { 0437 for (int x = 0; x < numCams; ++x) { 0438 if (gp_abilities_list_get_abilities(m_device->m_abilitylist, x, &a) == GP_OK) { 0439 auto cameraItem = new QStandardItem; 0440 cameraItem->setEditable(false); 0441 cameraItem->setText(a.model); 0442 m_model->appendRow(cameraItem); 0443 } 0444 } 0445 return true; 0446 } 0447 } 0448 0449 void KameraDeviceSelectDialog::save() 0450 { 0451 m_device->setModel(m_modelSel->currentIndex().data(Qt::DisplayRole).toString()); 0452 0453 if (m_serialRB->isChecked()) { 0454 m_device->setPath(QStringLiteral("serial:") + m_serialPortCombo->currentText()); 0455 } else if (m_USBRB->isChecked()) { 0456 m_device->setPath(QStringLiteral("usb:")); 0457 } 0458 } 0459 0460 void KameraDeviceSelectDialog::load() 0461 { 0462 QString path = m_device->path(); 0463 QString port = path.left(path.indexOf(QLatin1Char(':'))).toLower(); 0464 0465 if (port == QLatin1String("serial")) { 0466 setPortType(INDEX_SERIAL); 0467 } else if (port == QLatin1String("usb")) { 0468 setPortType(INDEX_USB); 0469 } 0470 0471 const QList<QStandardItem *> items = m_model->findItems(m_device->model()); 0472 for (QStandardItem *item : items) { 0473 const QModelIndex index = m_model->indexFromItem(item); 0474 m_modelSel->selectionModel()->select(index, QItemSelectionModel::Select); 0475 } 0476 } 0477 0478 void KameraDeviceSelectDialog::slot_setModel(const QModelIndex &modelIndex) 0479 { 0480 m_portSelectGroup->setEnabled(true); 0481 m_portSettingsGroup->setEnabled(true); 0482 0483 QString model = modelIndex.data(Qt::DisplayRole).toString(); 0484 0485 CameraAbilities abilities; 0486 int index = gp_abilities_list_lookup_model(m_device->m_abilitylist, model.toLocal8Bit().data()); 0487 if (index < 0) { 0488 slot_error( 0489 i18n("Description of abilities for camera %1 is not available." 0490 " Configuration options may be incorrect.", 0491 model)); 0492 } 0493 int result = gp_abilities_list_get_abilities(m_device->m_abilitylist, index, &abilities); 0494 if (result == GP_OK) { 0495 // enable radiobuttons for supported port types 0496 m_serialRB->setEnabled(abilities.port & GP_PORT_SERIAL); 0497 m_USBRB->setEnabled(abilities.port & GP_PORT_USB); 0498 // if there's only one available port type, make sure it's selected 0499 if (abilities.port == GP_PORT_SERIAL) { 0500 setPortType(INDEX_SERIAL); 0501 } 0502 if (abilities.port == GP_PORT_USB) { 0503 setPortType(INDEX_USB); 0504 } 0505 } else { 0506 slot_error( 0507 i18n("Description of abilities for camera %1 is not available." 0508 " Configuration options may be incorrect.", 0509 model)); 0510 } 0511 QPushButton *okButton = m_OkCancelButtonBox->button(QDialogButtonBox::Ok); 0512 okButton->setEnabled(true); 0513 } 0514 0515 void KameraDeviceSelectDialog::setPortType(int type) 0516 { 0517 // Enable the correct button 0518 if (type == INDEX_USB) { 0519 m_USBRB->setChecked(true); 0520 } else if (type == INDEX_SERIAL) { 0521 m_serialRB->setChecked(true); 0522 } 0523 0524 // Bring the right tab to the front 0525 m_settingsStack->setCurrentIndex(type); 0526 } 0527 0528 void KameraDeviceSelectDialog::slot_error(const QString &message) 0529 { 0530 KMessageBox::error(this, message); 0531 } 0532 0533 void KameraDeviceSelectDialog::slot_error(const QString &message, const QString &details) 0534 { 0535 KMessageBox::detailedError(this, message, details); 0536 } 0537 0538 #include "moc_kameradevice.cpp"