kde-playground/kamera/kcontrol/kameradevice.cpp
Ivailo Monev c6324fa767 kamera: require Gphoto2 v2.5+
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2021-07-14 21:33:42 +03:00

525 lines
16 KiB
C++

/*
Copyright (C) 2001 The Kompany
2002-2003 Ilya Konstantinov <kde-devel@future.shiny.co.il>
2002-2003 Marcus Meissner <marcus@jet.franken.de>
2003 Nadeem Hasan <nhasan@nadmm.com>
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 of the License, 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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <QtGui/QComboBox>
#include <QtGui/QGroupBox>
#include <QtGui/QHBoxLayout>
#include <QtGui/QLabel>
#include <QtGui/QListView>
#include <QtGui/QRadioButton>
#include <QtGui/QStackedWidget>
#include <QtGui/QVBoxLayout>
#include <QStandardItemModel>
#include <KLocale>
#include <KConfig>
#include <KMessageBox>
extern "C" {
#include <gphoto2.h>
}
#include "kamera.h"
#include "kameraconfigdialog.h"
#include "moc_kameradevice.cpp"
// Define some parts of the old API
#define GP_PROMPT_OK 0
#define GP_PROMPT_CANCEL -1
static const int INDEX_NONE= 0;
static const int INDEX_SERIAL = 1;
static const int INDEX_USB= 2;
static GPContext *glob_context = 0;
KCamera::KCamera(const QString &name, const QString &path)
{
m_name = name;
m_model = name;
m_path = path;
m_camera = NULL;
m_abilitylist = NULL;
}
KCamera::~KCamera()
{
if (m_camera) {
gp_camera_free(m_camera);
}
if (m_abilitylist) {
gp_abilities_list_free(m_abilitylist);
}
}
bool KCamera::initInformation()
{
if (m_model.isNull()) {
return false;
}
if (gp_abilities_list_new(&m_abilitylist) != GP_OK) {
emit error(i18n("Could not allocate memory for the abilities list."));
return false;
}
if (gp_abilities_list_load(m_abilitylist, glob_context) != GP_OK) {
emit error(i18n("Could not load ability list."));
return false;
}
int index = gp_abilities_list_lookup_model(m_abilitylist, m_model.toLocal8Bit().data());
if (index < 0) {
emit error(i18n("Description of abilities for camera %1 is not available."
" Configuration options may be incorrect.", m_model));
return false;
}
gp_abilities_list_get_abilities(m_abilitylist, index, &m_abilities);
return true;
}
bool KCamera::initCamera()
{
if (m_camera) {
return m_camera;
} else {
int result;
initInformation();
if (m_model.isNull() || m_path.isNull()) {
return false;
}
result = gp_camera_new(&m_camera);
if (result != GP_OK) {
// m_camera is not initialized, so we cannot get result as string
emit error(i18n("Could not access driver. Check your gPhoto2 installation."));
return false;
}
// set the camera's model
GPPortInfo info;
GPPortInfoList *il;
gp_port_info_list_new(&il);
gp_port_info_list_load(il);
gp_port_info_list_get_info(il, gp_port_info_list_lookup_path(il, m_path.toLocal8Bit().data()), &info);
gp_camera_set_abilities(m_camera, m_abilities);
gp_camera_set_port_info(m_camera, info);
gp_port_info_list_free(il);
// this might take some time (esp. for non-existent camera) - better be done asynchronously
result = gp_camera_init(m_camera, glob_context);
if (result != GP_OK) {
gp_camera_free(m_camera);
m_camera = NULL;
emit error(
i18n("Unable to initialize camera. Check your port settings and camera connectivity and try again."),
QString::fromLocal8Bit(gp_result_as_string(result)));
return false;
}
return m_camera;
}
}
Camera* KCamera::camera()
{
initCamera();
return m_camera;
}
QString KCamera::summary()
{
int result;
CameraText summary;
initCamera();
result = gp_camera_get_summary(m_camera, &summary, glob_context);
if (result != GP_OK) {
return i18n("No camera summary information is available.\n");
}
return QString::fromLocal8Bit(summary.text);
}
bool KCamera::configure()
{
CameraWidget *window;
int result;
initCamera();
result = gp_camera_get_config(m_camera, &window, glob_context);
if (result != GP_OK) {
emit error(i18n("Camera configuration failed."), QString::fromLocal8Bit(gp_result_as_string(result)));
return false;
}
KameraConfigDialog kcd(m_camera, window);
result = kcd.exec() ? GP_PROMPT_OK : GP_PROMPT_CANCEL;
if (result == GP_PROMPT_OK) {
result = gp_camera_set_config(m_camera, window, glob_context);
if (result != GP_OK) {
emit error(i18n("Camera configuration failed."), QString::fromLocal8Bit(gp_result_as_string(result)));
return false;
}
}
return true;
}
bool KCamera::test()
{
// TODO: Make testing non-blocking (maybe via KIO?)
// Currently, a failed serial test times out at about 30 sec.
return camera() != 0;
}
void KCamera::load(KConfig *config)
{
KConfigGroup group = config->group(m_name);
if (m_model.isNull()) {
m_model = group.readEntry("Model");
}
if (m_path.isNull()) {
m_path = group.readEntry("Path");
}
invalidateCamera();
}
void KCamera::save(KConfig *config)
{
KConfigGroup group = config->group(m_name);
group.writeEntry("Model", m_model);
group.writeEntry("Path", m_path);
}
QString KCamera::portName()
{
QString port = m_path.left(m_path.indexOf(":")).toLower();
if (port == "serial") {
return i18n("Serial");
}
if (port == "usb") {
return i18n("USB");
}
return i18n("Unknown port");
}
void KCamera::setName(const QString &name)
{
m_name = name;
}
void KCamera::setModel(const QString &model)
{
m_model = model;
invalidateCamera();
initInformation();
}
void KCamera::setPath(const QString &path)
{
m_path = path;
invalidateCamera();
}
void KCamera::invalidateCamera()
{
if (m_camera) {
gp_camera_free(m_camera);
m_camera = NULL;
}
}
bool KCamera::isTestable() const
{
return true;
}
bool KCamera::isConfigurable()
{
initInformation();
return m_abilities.operations & GP_OPERATION_CONFIG;
}
QStringList KCamera::supportedPorts()
{
initInformation();
QStringList ports;
if (m_abilities.port & GP_PORT_SERIAL) {
ports.append("serial");
}
if (m_abilities.port & GP_PORT_USB) {
ports.append("usb");
}
return ports;
}
CameraAbilities KCamera::abilities()
{
return m_abilities;
}
// ---------- KameraSelectCamera ------------
KameraDeviceSelectDialog::KameraDeviceSelectDialog(QWidget *parent, KCamera *device)
: KDialog(parent)
{
setCaption(i18n("Select Camera Device"));
setButtons(Ok | Cancel);
setDefaultButton(Ok);
setModal(true);
showButtonSeparator(true);
m_device = device;
connect(m_device, SIGNAL(error(const QString &)),
SLOT(slot_error(const QString &)));
connect(m_device, SIGNAL(error(const QString &, const QString &)),
SLOT(slot_error(const QString &, const QString &)));
QWidget *page = new QWidget( this );
setMainWidget(page);
// a layout with vertical boxes
QHBoxLayout *topLayout = new QHBoxLayout(page);
topLayout->setSpacing(KDialog::spacingHint());
topLayout->setMargin(0);
// the models list
m_modelSel = new QListView(page);
m_model = new QStandardItemModel(this);
m_model->setColumnCount(1);
m_model->setHeaderData(0, Qt::Horizontal, i18nc("@title:column", "Supported Cameras"));
m_modelSel->setModel(m_model);
topLayout->addWidget( m_modelSel );
connect(m_modelSel, SIGNAL(activated(const QModelIndex &)),
SLOT(slot_setModel(const QModelIndex &)));
// make sure listview only as wide as it needs to be
m_modelSel->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred));
QVBoxLayout *rightLayout = new QVBoxLayout();
rightLayout->setSpacing(KDialog::spacingHint());
rightLayout->setMargin(0);
topLayout->addLayout( rightLayout );
m_portSelectGroup = new QGroupBox(i18n("Port"), page);
QVBoxLayout *vertLayout = new QVBoxLayout;
m_portSelectGroup->setLayout( vertLayout );
rightLayout->addWidget(m_portSelectGroup);
m_portSettingsGroup = new QGroupBox(i18n("Port Settings"), page);
QVBoxLayout *lay = new QVBoxLayout;
m_portSettingsGroup->setLayout( lay );
rightLayout->addWidget(m_portSettingsGroup);
// Create port type selection radiobuttons.
m_serialRB = new QRadioButton(i18n("Serial"));
vertLayout->addWidget(m_serialRB );
m_serialRB->setWhatsThis( i18n("If this option is checked, the camera has to be connected to one of the computer's serial ports (known as COM ports in Microsoft Windows.)"));
m_USBRB = new QRadioButton(i18n("USB"));
vertLayout->addWidget(m_USBRB );
m_USBRB->setWhatsThis( i18n("If this option is checked, the camera has to be connected to one of the computer's USB ports, or to a USB hub."));
// Create port settings widget stack
m_settingsStack = new QStackedWidget;
QWidget *grid2 = new QWidget(m_settingsStack);
QGridLayout *gridLayout2 = new QGridLayout(grid2);
gridLayout2->setSpacing(KDialog::spacingHint());
grid2->setLayout(gridLayout2);
QLabel *label2 = new QLabel(i18n("Port"), grid2);
gridLayout2->addWidget(label2, 0, 0, Qt::AlignLeft);
lay->addWidget(grid2);
lay->addWidget( m_settingsStack );
connect(m_serialRB, SIGNAL( toggled(bool) ),
this, SLOT( changeCurrentIndex() ) );
connect(m_USBRB, SIGNAL( toggled(bool) ),
this, SLOT( changeCurrentIndex() ) );
// none tab
m_settingsStack->insertWidget(INDEX_NONE, new QLabel(i18n("No port type selected."), m_settingsStack));
// serial tab
QWidget *grid = new QWidget(m_settingsStack);
QGridLayout *gridLayout = new QGridLayout(grid);
gridLayout->setSpacing(KDialog::spacingHint());
grid->setLayout(gridLayout);
QLabel *label = new QLabel(i18n("Port:"), grid);
m_serialPortCombo = new QComboBox(grid);
m_serialPortCombo->setEditable(true);
m_serialPortCombo->setWhatsThis( i18n("Specify here the serial port to which you connect the camera."));
gridLayout->addWidget(label, 1, 0, Qt::AlignLeft);
gridLayout->addWidget(m_serialPortCombo, 1, 1, Qt::AlignRight);
m_settingsStack->insertWidget(INDEX_SERIAL, grid);
m_settingsStack->insertWidget(INDEX_USB, new
QLabel(i18n("No further configuration is required for USB cameras."),
m_settingsStack));
// query gphoto2 for existing serial ports
GPPortInfoList *list;
GPPortInfo info;
int gphoto_ports=0;
gp_port_info_list_new(&list);
if (gp_port_info_list_load(list) >= 0) {
gphoto_ports = gp_port_info_list_count(list);
}
for (int i = 0; i < gphoto_ports; i++) {
if (gp_port_info_list_get_info(list, i, &info) >= 0) {
char *xpath;
gp_port_info_get_path(info, &xpath);
if (strncmp(xpath, "serial:", 7) == 0) {
m_serialPortCombo->addItem(QString::fromLocal8Bit(xpath).mid(7));
}
}
}
gp_port_info_list_free(list);
// add a spacer
rightLayout->addStretch();
populateCameraListView();
load();
enableButtonOk(false);
m_portSelectGroup->setEnabled(false);
m_portSettingsGroup->setEnabled(false);
}
void KameraDeviceSelectDialog::changeCurrentIndex()
{
QRadioButton *send = dynamic_cast<QRadioButton*>(sender());
if (send) {
if (send == m_serialRB) {
m_settingsStack->setCurrentIndex(INDEX_SERIAL);
} else if (send == m_USBRB) {
m_settingsStack->setCurrentIndex(INDEX_USB);
}
}
}
bool KameraDeviceSelectDialog::populateCameraListView()
{
gp_abilities_list_new (&m_device->m_abilitylist);
gp_abilities_list_load(m_device->m_abilitylist, glob_context);
int numCams = gp_abilities_list_count(m_device->m_abilitylist);
CameraAbilities a;
if (numCams < 0) {
// XXX libgphoto2 failed to get te camera list
return false;
} else {
for(int x = 0; x < numCams; ++x) {
if (gp_abilities_list_get_abilities(m_device->m_abilitylist, x, &a) == GP_OK) {
QStandardItem *cameraItem = new QStandardItem;
cameraItem->setEditable(false);
cameraItem->setText(a.model);
m_model->appendRow(cameraItem);
}
}
return true;
}
}
void KameraDeviceSelectDialog::save()
{
m_device->setModel(m_modelSel->currentIndex().data(Qt::DisplayRole).toString());
if (m_serialRB->isChecked()) {
m_device->setPath("serial:" + m_serialPortCombo->currentText());
} else if (m_USBRB->isChecked() ) {
m_device->setPath("usb:");
}
}
void KameraDeviceSelectDialog::load()
{
QString path = m_device->path();
QString port = path.left(path.indexOf(':')).toLower();
if (port == "serial") {
setPortType(INDEX_SERIAL);
}
if (port == "usb") {
setPortType(INDEX_USB);
}
QList<QStandardItem *> items = m_model->findItems(m_device->model());
foreach (QStandardItem *item, items) {
const QModelIndex index = m_model->indexFromItem(item);
m_modelSel->selectionModel()->select(index, QItemSelectionModel::Select);
}
}
void KameraDeviceSelectDialog::slot_setModel(const QModelIndex &modelIndex)
{
enableButtonOk(true);
m_portSelectGroup->setEnabled(true);
m_portSettingsGroup->setEnabled(true);
QString model = modelIndex.data(Qt::DisplayRole).toString();
CameraAbilities abilities;
int index = gp_abilities_list_lookup_model(m_device->m_abilitylist, model.toLocal8Bit().data());
if(index < 0) {
slot_error(i18n("Description of abilities for camera %1 is not available."
" Configuration options may be incorrect.", model));
}
int result = gp_abilities_list_get_abilities(m_device->m_abilitylist, index, &abilities);
if (result == GP_OK) {
// enable radiobuttons for supported port types
m_serialRB->setEnabled(abilities.port & GP_PORT_SERIAL);
m_USBRB->setEnabled(abilities.port & GP_PORT_USB);
// if there's only one available port type, make sure it's selected
if (abilities.port == GP_PORT_SERIAL) {
setPortType(INDEX_SERIAL);
}
if (abilities.port == GP_PORT_USB) {
setPortType(INDEX_USB);
}
} else {
slot_error(i18n("Description of abilities for camera %1 is not available."
" Configuration options may be incorrect.", model));
}
}
void KameraDeviceSelectDialog::setPortType(int type)
{
// Enable the correct button
if (type == INDEX_USB) {
m_USBRB->setChecked(true);
} else if (type == INDEX_SERIAL) {
m_serialRB->setChecked(true);
}
// Bring the right tab to the front
m_settingsStack->setCurrentIndex(type);
}
void KameraDeviceSelectDialog::slot_error(const QString &message)
{
KMessageBox::error(this, message);
}
void KameraDeviceSelectDialog::slot_error(const QString &message, const QString &details)
{
KMessageBox::detailedError(this, message, details);
}