1 /*
2
3 Copyright (C) 2001 The Kompany
4 2002-2003 Ilya Konstantinov <kde-devel@future.shiny.co.il>
5 2002-2003 Marcus Meissner <marcus@jet.franken.de>
6 2003 Nadeem Hasan <nhasan@nadmm.com>
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
22 */
23
24 #include "kamera.h"
25
26 #include <QLabel>
27 #include <QListView>
28 #include <QVBoxLayout>
29 #include <QApplication>
30 #include <QStandardItemModel>
31 #include <QMenu>
32 #include <QIcon>
33
34 #include <KConfig>
35 #include <KMessageBox>
36 #include <KLocalizedString>
37 #include <KToolBar>
38 #include <KProtocolInfo>
39 #include <KActionCollection>
40 #include <KConfigGroup>
41 #include "kameraconfigdialog.h"
42 #include "kameradevice.h"
43
K_PLUGIN_FACTORY(KKameraConfigFactory,registerPlugin<KKameraConfig> ();)44 K_PLUGIN_FACTORY(KKameraConfigFactory, registerPlugin<KKameraConfig>();)
45
46 Q_LOGGING_CATEGORY(KAMERA_KCONTROL, "kamera.kcontrol")
47
48 // --------------- Camera control center module widget ---
49
50 KKameraConfig::KKameraConfig(QWidget *parent, const QVariantList &)
51 : KCModule(parent)
52 {
53 #ifdef DEBUG_KAMERA_KCONTROL
54 QLoggingCategory::setFilterRules(QStringLiteral("kamera.kcontrol.debug = true"));
55 #endif
56 m_devicePopup = new QMenu(this);
57 m_actions = new KActionCollection(this);
58 m_config = new KConfig(KProtocolInfo::config(QStringLiteral("camera")), KConfig::SimpleConfig);
59 m_deviceModel = new QStandardItemModel(this);
60
61 m_context = gp_context_new();
62 if (m_context) {
63 // Register the callback functions
64 gp_context_set_cancel_func(m_context, cbGPCancel, this);
65 gp_context_set_idle_func(m_context, cbGPIdle, this);
66
67 displayGPSuccessDialogue();
68 } else {
69 displayGPFailureDialogue();
70 }
71 }
72
~KKameraConfig()73 KKameraConfig::~KKameraConfig()
74 {
75 delete m_config;
76 }
77
defaults()78 void KKameraConfig::defaults()
79 {
80 }
81
displayGPFailureDialogue()82 void KKameraConfig::displayGPFailureDialogue()
83 {
84 QVBoxLayout *topLayout = new QVBoxLayout(this);
85 topLayout->setSpacing(0);
86 topLayout->setContentsMargins(0, 0, 0, 0);
87 QLabel *label = new QLabel(i18n("Unable to initialize the gPhoto2 libraries."), this);
88 topLayout->addWidget(label);
89 }
90
displayGPSuccessDialogue()91 void KKameraConfig::displayGPSuccessDialogue()
92 {
93 // set the kcontrol module buttons
94 setButtons(Help | Apply );
95
96 // create a layout with two vertical boxes
97 QVBoxLayout *topLayout = new QVBoxLayout(this);
98 topLayout->setSpacing(0);
99 topLayout->setContentsMargins(0, 0, 0, 0);
100
101 m_toolbar = new KToolBar(this, "ToolBar");
102 topLayout->addWidget(m_toolbar);
103 m_toolbar->setMovable(false);
104
105 // create list of devices - this is the large white box
106 m_deviceSel = new QListView(this);
107 topLayout->addWidget(m_deviceSel);
108
109 m_deviceSel->setModel(m_deviceModel);
110
111 connect(m_deviceSel, &QListView::customContextMenuRequested,
112 this, &KKameraConfig::slot_deviceMenu);
113 connect(m_deviceSel, &QListView::doubleClicked,
114 this, &KKameraConfig::slot_configureCamera);
115 connect(m_deviceSel, &QListView::activated,
116 this, &KKameraConfig::slot_deviceSelected);
117 connect(m_deviceSel, &QListView::clicked,
118 this, &KKameraConfig::slot_deviceSelected);
119
120 m_deviceSel->setViewMode(QListView::IconMode);
121 m_deviceSel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
122 m_deviceSel->setContextMenuPolicy(Qt::CustomContextMenu);
123
124 // create actions, add to the toolbar
125 QAction *act;
126 act = m_actions->addAction(QStringLiteral("camera_add"));
127 act->setIcon(QIcon::fromTheme(QStringLiteral("camera-photo")));
128 act->setText(i18n("Add"));
129 connect(act, &QAction::triggered, this, &KKameraConfig::slot_addCamera);
130 act->setWhatsThis(i18n("Click this button to add a new camera."));
131 m_toolbar->addAction(act);
132 m_toolbar->addSeparator();
133
134 act = m_actions->addAction(QStringLiteral("camera_test"));
135 act->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok")));
136 act->setText(i18n("Test"));
137 connect(act, &QAction::triggered, this, &KKameraConfig::slot_testCamera);
138 act->setWhatsThis(i18n("Click this button to test the connection to the selected camera."));
139 m_toolbar->addAction(act);
140
141 act = m_actions->addAction(QStringLiteral("camera_remove"));
142 act->setIcon(QIcon::fromTheme(QStringLiteral("user-trash")));
143 act->setText(i18n("Remove"));
144 connect(act, &QAction::triggered, this, &KKameraConfig::slot_removeCamera);
145 act->setWhatsThis(i18n("Click this button to remove the selected camera from the list."));
146 m_toolbar->addAction(act);
147
148 act = m_actions->addAction(QStringLiteral("camera_configure"));
149 act->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
150 act->setText(i18n("Configure..."));
151 connect(act, &QAction::triggered, this, &KKameraConfig::slot_configureCamera);
152 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."));
153 m_toolbar->addAction(act);
154
155 act = m_actions->addAction(QStringLiteral("camera_summary"));
156 act->setIcon(QIcon::fromTheme(QStringLiteral("hwinfo")));
157 act->setText(i18n("Information"));
158 connect(act, &QAction::triggered, this, &KKameraConfig::slot_cameraSummary);
159 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."));
160 m_toolbar->addAction(act);
161 m_toolbar->addSeparator();
162
163 act = m_actions->addAction(QStringLiteral("camera_cancel"));
164 act->setIcon(QIcon::fromTheme(QStringLiteral("process-stop")));
165 act->setText(i18n("Cancel"));
166 connect(act, &QAction::triggered, this, &KKameraConfig::slot_cancelOperation);
167 act->setWhatsThis(i18n("Click this button to cancel the current camera operation."));
168 act->setEnabled(false);
169 m_toolbar->addAction(act);
170 }
171
populateDeviceListView()172 void KKameraConfig::populateDeviceListView()
173 {
174 m_deviceModel->clear();
175 CameraDevicesMap::ConstIterator itEnd = m_devices.constEnd();
176 for (CameraDevicesMap::ConstIterator it = m_devices.constBegin(); it != itEnd; ++it) {
177 if (it.value()) {
178 QStandardItem *deviceItem = new QStandardItem;
179 deviceItem->setEditable(false);
180 deviceItem->setText(it.key());
181 deviceItem->setIcon(QIcon::fromTheme(QStringLiteral("camera-photo")));
182 m_deviceModel->appendRow(deviceItem);
183 }
184 }
185 slot_deviceSelected(m_deviceSel->currentIndex());
186 }
187
save(void)188 void KKameraConfig::save(void)
189 {
190 CameraDevicesMap::Iterator it;
191
192 for (it = m_devices.begin(); it != m_devices.end(); it++)
193 {
194 it.value()->save(m_config);
195 }
196 m_config->sync();
197 }
198
load(void)199 void KKameraConfig::load(void)
200 {
201 QStringList groupList = m_config->groupList();
202
203 QStringList::Iterator it;
204 int i, count;
205 CameraList *list;
206 CameraAbilitiesList *al;
207 GPPortInfoList *il;
208 const char *model, *value;
209 KCamera *kcamera;
210
211 for (it = groupList.begin(); it != groupList.end(); it++) {
212 if (*it != QStringLiteral("<default>")) {
213 KConfigGroup cg(m_config, *it);
214 if (cg.readEntry("Path").contains(QStringLiteral("usb:"))) {
215 continue;
216 }
217
218 // Load configuration for Serial port cameras
219 qCDebug(KAMERA_KCONTROL) << "Loading configuration for serial port camera: "
220 << *it;
221 kcamera = new KCamera(*it, cg.readEntry("Path"));
222 connect(kcamera, qOverload<const QString&>(&KCamera::error),
223 this, qOverload<const QString&>(&KKameraConfig::slot_error));
224
225 connect(kcamera, qOverload<const QString&, const QString&>(&KCamera::error),
226 this, qOverload<const QString&, const QString&>(&KKameraConfig::slot_error));
227
228 kcamera->load(m_config);
229 m_devices[*it] = kcamera;
230 }
231 }
232 m_cancelPending = false;
233
234 gp_list_new (&list);
235
236 gp_abilities_list_new (&al);
237 gp_abilities_list_load (al, m_context);
238 gp_port_info_list_new (&il);
239 gp_port_info_list_load (il);
240 gp_abilities_list_detect (al, il, list, m_context);
241 gp_abilities_list_free (al);
242 gp_port_info_list_free (il);
243
244 count = gp_list_count (list);
245
246 QMap<QString,QString> ports, names;
247
248 for (i = 0 ; i<count ; i++) {
249 gp_list_get_name (list, i, &model);
250 gp_list_get_value (list, i, &value);
251
252 ports[value] = model;
253 if (!strcmp(value,"usb:")) {
254 names[model] = value;
255 }
256 }
257
258 if (ports.contains(QStringLiteral("usb:")) && names[ports[QStringLiteral("usb:")]]!=QStringLiteral("usb:")) {
259 ports.remove(QStringLiteral("usb:"));
260 }
261
262 QMap<QString,QString>::iterator portit;
263
264 for (portit = ports.begin() ; portit != ports.end(); portit++) {
265 qCDebug(KAMERA_KCONTROL) << "Adding USB camera: " << portit.value() << " at " << portit.key();
266
267 kcamera = new KCamera(portit.value(), portit.key());
268
269 connect(kcamera, qOverload<const QString&>(&KCamera::error),
270 this, qOverload<const QString&>(&KKameraConfig::slot_error));
271
272 connect(kcamera, qOverload<const QString&, const QString&>(&KCamera::error),
273 this, qOverload<const QString&, const QString&>(&KKameraConfig::slot_error));
274
275 m_devices[portit.value()] = kcamera;
276 }
277 populateDeviceListView();
278
279 gp_list_free (list);
280 }
281
beforeCameraOperation()282 void KKameraConfig::beforeCameraOperation()
283 {
284 m_cancelPending = false;
285
286 m_actions->action(QStringLiteral("camera_test"))->setEnabled(false);
287 m_actions->action(QStringLiteral("camera_remove"))->setEnabled(false);
288 m_actions->action(QStringLiteral("camera_configure"))->setEnabled(false);
289 m_actions->action(QStringLiteral("camera_summary"))->setEnabled(false);
290
291 m_actions->action(QStringLiteral("camera_cancel"))->setEnabled(true);
292 }
293
afterCameraOperation()294 void KKameraConfig::afterCameraOperation()
295 {
296 m_actions->action(QStringLiteral("camera_cancel"))->setEnabled(false);
297
298 // if we're regaining control after a Cancel...
299 if (m_cancelPending) {
300 qApp->restoreOverrideCursor();
301 m_cancelPending = false;
302 }
303
304 // if any item was selected before the operation was run
305 // it makes sense for the relevant toolbar buttons to be enabled
306 slot_deviceSelected(m_deviceSel->currentIndex());
307 }
308
suggestName(const QString & name)309 QString KKameraConfig::suggestName(const QString &name)
310 {
311 QString new_name = name;
312 new_name.remove(QLatin1Char('/')); // we cannot have a slash in a URI's host
313
314 if (!m_devices.contains(new_name)) return new_name;
315
316 // try new names with a number appended until we find a free one
317 int i = 1;
318 while (i++ < 0xffff) {
319 new_name = name + QStringLiteral(" (") + QString::number(i) + QLatin1Char(')');
320 if (!m_devices.contains(new_name)) return new_name;
321 }
322
323 return QString();
324 }
325
slot_addCamera()326 void KKameraConfig::slot_addCamera()
327 {
328 KCamera *m_device = new KCamera(QString(), QString());
329 connect(m_device, qOverload<const QString&>(&KCamera::error),
330 this, qOverload<const QString&>(&KKameraConfig::slot_error));
331
332 connect(m_device, qOverload<const QString&, const QString&>(&KCamera::error),
333 this, qOverload<const QString&, const QString&>(&KKameraConfig::slot_error));
334
335 KameraDeviceSelectDialog dialog(this, m_device);
336 if (dialog.exec() == QDialog::Accepted) {
337 dialog.save();
338 m_device->setName(suggestName(m_device->model()));
339 m_devices.insert(m_device->name(), m_device);
340 populateDeviceListView();
341 Q_EMIT changed(true);
342 } else {
343 delete m_device;
344 }
345 }
346
slot_removeCamera()347 void KKameraConfig::slot_removeCamera()
348 {
349 const QString name = m_deviceSel->currentIndex().data(Qt::DisplayRole).toString();
350 if (m_devices.contains(name)) {
351 KCamera *m_device = m_devices.value(name);
352 m_devices.remove(name);
353 delete m_device;
354 m_config->deleteGroup(name);
355 populateDeviceListView();
356 Q_EMIT changed(true);
357 }
358 }
359
slot_testCamera()360 void KKameraConfig::slot_testCamera()
361 {
362 beforeCameraOperation();
363
364 const QString name = m_deviceSel->currentIndex().data(Qt::DisplayRole).toString();
365 if (m_devices.contains(name)) {
366 KCamera *m_device = m_devices.value(name);
367 if (m_device->test()) {
368 KMessageBox::information(this, i18n("Camera test was successful."));
369 }
370 }
371
372 afterCameraOperation();
373 }
374
slot_configureCamera()375 void KKameraConfig::slot_configureCamera()
376 {
377 const QString name = m_deviceSel->currentIndex().data(Qt::DisplayRole).toString();
378 if (m_devices.contains(name)) {
379 KCamera *m_device = m_devices[name];
380 m_device->configure();
381 }
382 }
383
slot_cameraSummary()384 void KKameraConfig::slot_cameraSummary()
385 {
386 const QString name = m_deviceSel->currentIndex().data(Qt::DisplayRole).toString();
387 if (m_devices.contains(name)) {
388 KCamera *m_device = m_devices[name];
389 QString summary = m_device->summary();
390 if (!summary.isNull()) {
391 KMessageBox::information(this, summary);
392 }
393 }
394 }
395
slot_cancelOperation()396 void KKameraConfig::slot_cancelOperation()
397 {
398 m_cancelPending = true;
399 // Prevent the user from keeping clicking Cancel
400 m_actions->action(QStringLiteral("camera_cancel"))->setEnabled(false);
401 // and indicate that the click on Cancel did have some effect
402 qApp->setOverrideCursor(Qt::WaitCursor);
403 }
404
slot_deviceMenu(const QPoint & point)405 void KKameraConfig::slot_deviceMenu(const QPoint &point)
406 {
407 QModelIndex index = m_deviceSel->indexAt(point);
408 if (index.isValid()) {
409 m_devicePopup->clear();
410 m_devicePopup->addAction(m_actions->action(QStringLiteral("camera_test")));
411 m_devicePopup->addAction(m_actions->action(QStringLiteral("camera_remove")));
412 m_devicePopup->addAction(m_actions->action(QStringLiteral("camera_configure")));
413 m_devicePopup->addAction(m_actions->action(QStringLiteral("camera_summary")));
414 m_devicePopup->exec(m_deviceSel->viewport()->mapToGlobal(point));
415 }
416 }
417
slot_deviceSelected(const QModelIndex & index)418 void KKameraConfig::slot_deviceSelected(const QModelIndex &index)
419 {
420 bool isValid = index.isValid();
421 m_actions->action(QStringLiteral("camera_test"))->setEnabled(isValid);
422 m_actions->action(QStringLiteral("camera_remove"))->setEnabled(isValid);
423 m_actions->action(QStringLiteral("camera_configure"))->setEnabled(isValid);
424 m_actions->action(QStringLiteral("camera_summary"))->setEnabled(isValid);
425 }
426
cbGPIdle(GPContext *,void *)427 void KKameraConfig::cbGPIdle(GPContext * /*context*/, void * /*data*/)
428 {
429 /*KKameraConfig *self( reinterpret_cast<KKameraConfig*>(data) );*/
430 qApp->processEvents();
431 }
432
cbGPCancel(GPContext *,void * data)433 GPContextFeedback KKameraConfig::cbGPCancel(GPContext * /*context*/, void *data)
434 {
435 KKameraConfig *self( reinterpret_cast<KKameraConfig*>(data) );
436
437 // Since in practice no camera driver supports idle callbacks yet,
438 // we'll use the cancel callback as opportunity to process events
439 qApp->processEvents();
440
441 // If a cancel request is pending, ask gphoto to cancel
442 if (self->m_cancelPending) {
443 return GP_CONTEXT_FEEDBACK_CANCEL;
444 } else {
445 return GP_CONTEXT_FEEDBACK_OK;
446 }
447 }
448
quickHelp() const449 QString KKameraConfig::quickHelp() const
450 {
451 return i18n("<h1>Digital Camera</h1>\n"
452 "This module allows you to configure support for your digital camera.\n"
453 "You need to select the camera's model and the port it is connected\n"
454 "to on your computer (e.g. USB, Serial, Firewire). If your camera does not\n"
455 "appear on the list of <i>Supported Cameras</i>, go to the\n"
456 "<a href=\"http://www.gphoto.org\">GPhoto web site</a> for a possible update.<br><br>\n"
457 "To view and download images from the digital camera, go to the address\n"
458 "<a href=\"camera:/\">camera:/</a> in Konqueror and other KDE applications.");
459 }
460
slot_error(const QString & message)461 void KKameraConfig::slot_error(const QString &message)
462 {
463 KMessageBox::error(this, message);
464 }
465
slot_error(const QString & message,const QString & details)466 void KKameraConfig::slot_error(const QString &message, const QString &details)
467 {
468 KMessageBox::detailedError(this, message, details);
469 }
470
471 #include "kamera.moc"
472