diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 47d455dd55be855e4cc691c32f687f723d9247ee (patch) | |
tree | 52e236aaa2576bdb3840ebede26619692fed6d7d /kamera | |
download | tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.tar.gz tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegraphics@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kamera')
24 files changed, 3083 insertions, 0 deletions
diff --git a/kamera/AUTHORS b/kamera/AUTHORS new file mode 100644 index 00000000..b278e9ce --- /dev/null +++ b/kamera/AUTHORS @@ -0,0 +1,4 @@ + Copyright (C) 2001 The Kompany + 2001-2003 Ilya Konstantinov <kde-devel@future.shiny.co.il> + 2001-2007 Marcus Meissner <marcus@jet.franken.de> + 2003 Nadeem Hasan <nhasan@nadmm.com> diff --git a/kamera/Makefile.am b/kamera/Makefile.am new file mode 100644 index 00000000..f012f4a1 --- /dev/null +++ b/kamera/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = kcontrol kioslave pics diff --git a/kamera/README b/kamera/README new file mode 100644 index 00000000..592a84f4 --- /dev/null +++ b/kamera/README @@ -0,0 +1,5 @@ +Dependencies: + +You need libgphoto 2.0 final (or later). + +libgphoto2 2.3.1 or newer is recommended. diff --git a/kamera/configure.in.in b/kamera/configure.in.in new file mode 100644 index 00000000..3e2fd1c6 --- /dev/null +++ b/kamera/configure.in.in @@ -0,0 +1,182 @@ +dnl KDE_FIND_GPHOTO2 - Find gphoto2 libraries and include files +dnl +dnl Adapted from kdebase/nsplugins/configure.in.in + +AC_DEFUN([KDE_FIND_GPHOTO2], +[ + + + +# Clear working variables +gphoto2_includes= +gphoto2_libraries= + + + +# Process user input to configure +AC_ARG_WITH(kamera, +AC_HELP_STRING([--without-kamera],[do not build kamera (gphoto2 required)]), +[if test "$withval" = "no" ; then + gphoto2_includes=none + gphoto2_libraries=none +fi])dnl + +AC_ARG_WITH(gphoto2-includes, +AC_HELP_STRING([--with-gphoto2-includes=DIR],[gphoto2 include files are in DIR]), +gphoto2_includes="$withval") + +AC_ARG_WITH(gphoto2-libraries, +AC_HELP_STRING([--with-gphoto2-libraries=DIR],[gphoto2 libraries are in DIR]), +gphoto2_libraries="$withval") + +AC_MSG_CHECKING(for gPhoto2) +# the pkg-config way first, if user did not use --with- +AC_CHECK_PROG(gphoto2_config,gphoto2-config,gphoto2-config,no) +AC_CHECK_PROG(gphoto2_port_config,gphoto2-port-config,gphoto2-port-config,no) +if test "$gphoto2_includes" = "" -a "$gphoto2_libraries" = "" -a "$gphoto2_config" != "no" -a "$gphoto2_port_config" != "no" +then + with_kamera="yes" + GPHOTO2_INCS="`$gphoto2_config --cflags` `$gphoto2_port_config --cflags`" + GPHOTO2_LIBS="`$gphoto2_config --libs` `$gphoto2_port_config --libs`" +else +# +# Search for gphoto2 include files. +# + if test "$gphoto2_includes" = ""; then + AC_CACHE_VAL(ac_cv_gphoto2_includes, [ + ac_gphoto2_save_LIBS="$LIBS" + LIBS="-lgphoto2 $LIBS" + ac_cv_gphoto2_includes="none" + AC_TRY_COMPILE([#include <gphoto2.h>],[int a;], + [ + # gphoto2.h is in the standard search path. + ac_cv_gphoto2_includes= + ],[ + # gphoto2.h is not in the standard search path. + # Locate it and put its directory in `gphoto2_includes' + for dir in /usr/include /usr/local/include \ + /usr/include/gphoto2 /usr/local/include/gphoto2; do + if test -f "$dir/gphoto2.h"; then + ac_cv_gphoto2_includes="$dir" + break + fi + done + ]) + # + LIBS="$ac_gphoto2_save_LIBS" + ]) + gphoto2_includes="$ac_cv_gphoto2_includes" + fi + + # + # Search for libgphoto2 + # + if test "$gphoto2_libraries" = ""; then + AC_CACHE_VAL(ac_cv_gphoto2_libraries,[ + ac_gphoto2_save_LIBS="$LIBS" + LIBS="-lgphoto2_port -lgphoto2 $LIBS" + ac_cv_gphoto2_libraries="none" + AC_TRY_LINK([#include <gphoto2.h>],[gp_context_progress_start(0,0,0,0);], [ + # libgphoto2 is in the standard search path. + ac_cv_gphoto2_libraries= + ],[ + # libgphoto2 is not in the standard search path. + # Locate it and put its directory in `gphoto2_libraries' + for dir in /usr/lib /usr/local/lib; do + if test -d "$dir" && test "`ls $dir/libgphoto2.* 2> /dev/null`" != ""; then + ac_cv_gphoto2_libraries="$dir" + break + fi + done + ]) + # + LIBS="$ac_gphoto2_save_LIBS" + ]) + # + gphoto2_libraries="$ac_cv_gphoto2_libraries" + fi +# Initialise compiler and linker flag variables for export + if test "$gphoto2_includes" = "none" -o "$gphoto2_libraries" = "none" ; then + with_kamera="no" + else + with_kamera="yes" + + if test "$gphoto2_libraries" = "" -o "$gphoto2_libraries" = "none"; then + GPHOTO2_LIBS="-lgphoto2" + else + GPHOTO2_LIBS="-L$gphoto2_libraries -lgphoto2" + fi + if test "$gphoto2_includes" != "" -a "$gphoto2_includes" != "none"; then + GPHOTO2_INCS="-I$gphoto2_includes" + fi + fi +fi + +if test "$with_kamera" = "yes" ; then + # Check if it works. + ac_gphoto2_save_LIBS="$LIBS" + ac_gphoto2_save_CFLAGS="$CFLAGS" + LIBS="$LIBS $GPHOTO2_LIBS" + CFLAGS="$CFLAGS $GPHOTO2_INCS" + AC_TRY_LINK([#include <gphoto2.h>],[gp_context_progress_start(0,0,0,0);], [ + # It works. + AC_DEFINE(HAVE_GPHOTO2,1,[Define if you have gPhoto2 installed]) + ],[ + with_kamera="no" + ]) + LIBS="$ac_gphoto2_save_LIBS" + CFLAGS="$ac_gphoto2_save_CFLAGS" +fi +dnl **** Check for va_copy **** +AC_CACHE_CHECK([for va_copy], ac_cv_c_va_copy, + AC_TRY_LINK( + [#include <stdarg.h>], + [va_list ap1, ap2; + va_copy(ap1,ap2); + ], + [ac_cv_c_va_copy="yes"], + [ac_cv_c_va_copy="no"]) + ) +if test "$ac_cv_c_va_copy" = "yes" +then + AC_DEFINE(HAVE_VA_COPY, 1, [Define if we have va_copy]) +fi +AC_CACHE_CHECK([for __va_copy], ac_cv_c___va_copy, + AC_TRY_LINK( + [#include <stdarg.h>], + [va_list ap1, ap2; + __va_copy(ap1,ap2); + ], + [ac_cv_c___va_copy="yes"], + [ac_cv_c___va_copy="no"]) + ) +if test "$ac_cv_c___va_copy" = "yes" +then + AC_DEFINE(HAVE___VA_COPY, 1, [Define if we have __va_copy]) +fi + +# Export compiler and linker flags for replacement in Makefile +AC_SUBST(GPHOTO2_INCS) +AC_SUBST(GPHOTO2_LIBS) + + +# Display results of configuration +gphoto2_libraries_result="$gphoto2_libraries" +gphoto2_includes_result="$gphoto2_includes" + +test "$gphoto2_libraries" = "" && gphoto2_libraries_result="in default path" +test "$gphoto2_includes" = "" && gphoto2_includes_result="in default path" + +test "$gphoto2_libraries" = "none" && gphoto2_libraries_result="(none)" +test "$gphoto2_includes" = "none" && gphoto2_includes_result="(none)" + +AC_MSG_RESULT( + [gphoto2 libraries $gphoto2_libraries_result, gphoto2 headers $gphoto2_includes_result]) + +]) dnl end of KDE_FIND_GPHOTO2 definition + +KDE_FIND_GPHOTO2 +if test "$with_kamera" = "no"; then +dnl AC_MSG_WARN([You need to install gphoto 2.0 (or later), e.g. http://gphoto.net/dist/gphoto2-2.0.tar.gz if your distributor doesn't have a package]) + DO_NOT_COMPILE="$DO_NOT_COMPILE kamera" +fi diff --git a/kamera/kcontrol/Makefile.am b/kamera/kcontrol/Makefile.am new file mode 100644 index 00000000..9fd30f46 --- /dev/null +++ b/kamera/kcontrol/Makefile.am @@ -0,0 +1,16 @@ +kde_module_LTLIBRARIES = kcm_kamera.la + +kcm_kamera_la_SOURCES = kamera.cpp kameradevice.cpp kameraconfigdialog.cpp + +kcm_kamera_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +kcm_kamera_la_LIBADD = $(LIB_KIO) $(GPHOTO2_LIBS) +INCLUDES= $(all_includes) $(GPHOTO2_INCS) + +kcm_kamera_la_METASOURCES = AUTO + +noinst_HEADERS = kamera.h kameradevice.h kameraconfigdialog.h + +messages: + $(XGETTEXT) $(kcm_kamera_la_SOURCES) -o $(podir)/kcmkamera.pot + +xdg_apps_DATA = kamera.desktop diff --git a/kamera/kcontrol/kamera.cpp b/kamera/kcontrol/kamera.cpp new file mode 100644 index 00000000..0fdc416a --- /dev/null +++ b/kamera/kcontrol/kamera.cpp @@ -0,0 +1,423 @@ +/* + + 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 <qlabel.h> +#include <qlayout.h> + +#include <kgenericfactory.h> +#include <ksimpleconfig.h> +#include <kaction.h> +#include <kiconloader.h> +#include <kmessagebox.h> +#include <kiconview.h> +#include <kdialog.h> +#include <klocale.h> +#include <ktoolbar.h> +#include <kpopupmenu.h> +#include <kprotocolinfo.h> +#include <kdebug.h> + +#include "kameraconfigdialog.h" +#include "kameradevice.h" +#include "kamera.h" +#include "kamera.moc" + +typedef KGenericFactory<KKameraConfig, QWidget> KKameraConfigFactory; +K_EXPORT_COMPONENT_FACTORY( kcm_kamera, KKameraConfigFactory( "kcmkamera" ) ) + +// --------------- Camera control center module widget --- + +KKameraConfig *KKameraConfig::m_instance = NULL; + +KKameraConfig::KKameraConfig(QWidget *parent, const char *name, const QStringList &) + : KCModule(KKameraConfigFactory::instance(), parent, name) +{ + m_devicePopup = new KPopupMenu(this); + m_actions = new KActionCollection(this); + m_config = new KSimpleConfig(KProtocolInfo::config("camera")); + + m_context = gp_context_new(); + if (m_context) { + + // Register the callback functions + gp_context_set_cancel_func(m_context, cbGPCancel, this); + gp_context_set_idle_func(m_context, cbGPIdle, this); + + displayGPSuccessDialogue(); + + // load existing configuration + load(); + + } else { + + displayGPFailureDialogue(); + } + + // store instance for frontend_prompt + m_instance = this; +} + +KKameraConfig::~KKameraConfig() +{ + delete m_config; +} + +void KKameraConfig::defaults() +{ + load( true ); +} + +void KKameraConfig::displayGPFailureDialogue(void) +{ + new QLabel(i18n("Unable to initialize the gPhoto2 libraries."), this); +} + +void KKameraConfig::displayGPSuccessDialogue(void) +{ + // set the kcontrol module buttons + setButtons(Help | Apply | Cancel | Ok); + + // create a layout with two vertical boxes + QVBoxLayout *topLayout = new QVBoxLayout(this, 0, 0); + topLayout->setAutoAdd(true); + + m_toolbar = new KToolBar(this, "ToolBar"); + m_toolbar->setMovingEnabled(false); + + // create list of devices + m_deviceSel = new KIconView(this); + + connect(m_deviceSel, SIGNAL(rightButtonClicked(QIconViewItem *, const QPoint &)), + SLOT(slot_deviceMenu(QIconViewItem *, const QPoint &))); + connect(m_deviceSel, SIGNAL(doubleClicked(QIconViewItem *)), + SLOT(slot_configureCamera())); + connect(m_deviceSel, SIGNAL(selectionChanged(QIconViewItem *)), + SLOT(slot_deviceSelected(QIconViewItem *))); + + m_deviceSel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + + // create actions + KAction *act; + + act = new KAction(i18n("Add"), "camera", 0, this, SLOT(slot_addCamera()), m_actions, "camera_add"); + act->setWhatsThis(i18n("Click this button to add a new camera.")); + act->plug(m_toolbar); + m_toolbar->insertLineSeparator(); + act = new KAction(i18n("Test"), "camera_test", 0, this, SLOT(slot_testCamera()), m_actions, "camera_test"); + act->setWhatsThis(i18n("Click this button to remove the selected camera from the list.")); + act->plug(m_toolbar); + act = new KAction(i18n("Remove"), "edittrash", 0, this, SLOT(slot_removeCamera()), m_actions, "camera_remove"); + act->setWhatsThis(i18n("Click this button to remove the selected camera from the list.")); + act->plug(m_toolbar); + act = new KAction(i18n("Configure..."), "configure", 0, this, SLOT(slot_configureCamera()), m_actions, "camera_configure"); + 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.")); + act->plug(m_toolbar); + act = new KAction(i18n("Information"), "hwinfo", 0, this, SLOT(slot_cameraSummary()), m_actions, "camera_summary"); + 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 Configuration dialog depend on the camera model.")); + act->plug(m_toolbar); + m_toolbar->insertLineSeparator(); + act = new KAction(i18n("Cancel"), "stop", 0, this, SLOT(slot_cancelOperation()), m_actions, "camera_cancel"); + act->setWhatsThis(i18n("Click this button to cancel the current camera operation.")); + act->setEnabled(false); + act->plug(m_toolbar); +} + +void KKameraConfig::populateDeviceListView(void) +{ + m_deviceSel->clear(); + CameraDevicesMap::Iterator it; + for (it = m_devices.begin(); it != m_devices.end(); it++) { + if (it.data()) { + new QIconViewItem(m_deviceSel, it.key(), DesktopIcon("camera")); + } + } + slot_deviceSelected(m_deviceSel->currentItem()); +} + +void KKameraConfig::save(void) +{ + CameraDevicesMap::Iterator it; + + for (it = m_devices.begin(); it != m_devices.end(); it++) + { + it.data()->save(m_config); + } + m_config->sync(); +} + +void KKameraConfig::load(void) +{ + load( false ); +} + +void KKameraConfig::load(bool useDefaults ) +{ + m_config->setReadDefaults( useDefaults ); + QStringList groupList = m_config->groupList(); + QStringList::Iterator it; + int i, count; + CameraList *list; + CameraAbilitiesList *al; + GPPortInfoList *il; + const char *model, *value; + KCamera *kcamera; + + for (it = groupList.begin(); it != groupList.end(); it++) { + if (*it != "<default>") { + m_config->setGroup(*it); + if (m_config->readEntry("Path").contains("usb:")) + continue; + + kcamera = new KCamera(*it,m_config->readEntry("Path")); + connect(kcamera, SIGNAL(error(const QString &)), SLOT(slot_error(const QString &))); + connect(kcamera, SIGNAL(error(const QString &, const QString &)), SLOT(slot_error(const QString &, const QString &))); + kcamera->load(m_config); + m_devices[*it] = kcamera; + } + } + m_cancelPending = false; + + gp_list_new (&list); + + gp_abilities_list_new (&al); + gp_abilities_list_load (al, m_context); + gp_port_info_list_new (&il); + gp_port_info_list_load (il); + gp_abilities_list_detect (al, il, list, m_context); + gp_abilities_list_free (al); + gp_port_info_list_free (il); + + count = gp_list_count (list); + + QMap<QString,QString> ports, names; + + for (i = 0 ; i<count ; i++) { + gp_list_get_name (list, i, &model); + gp_list_get_value (list, i, &value); + + ports[value] = model; + if (!strcmp(value,"usb:")) + names[model] = value; + } + if (ports.contains("usb:") && names[ports["usb:"]]!="usb:") + ports.remove("usb:"); + + QMap<QString,QString>::iterator portit; + + for (portit = ports.begin() ; portit != ports.end(); portit++) { + /* kdDebug() << "Adding USB camera: " << portit.data() << " at " << portit.key() << endl; */ + + kcamera = new KCamera(portit.data(),portit.key()); + connect(kcamera, SIGNAL(error(const QString &)), SLOT(slot_error(const QString &))); + connect(kcamera, SIGNAL(error(const QString &, const QString &)), SLOT(slot_error(const QString &, const QString &))); + m_devices[portit.data()] = kcamera; + } + populateDeviceListView(); + + gp_list_free (list); + + emit changed( useDefaults ); +} + +void KKameraConfig::beforeCameraOperation(void) +{ + m_cancelPending = false; + + m_actions->action("camera_test")->setEnabled(false); + m_actions->action("camera_remove")->setEnabled(false); + m_actions->action("camera_configure")->setEnabled(false); + m_actions->action("camera_summary")->setEnabled(false); + + m_actions->action("camera_cancel")->setEnabled(true); +} + +void KKameraConfig::afterCameraOperation(void) +{ + m_actions->action("camera_cancel")->setEnabled(false); + + // if we're regaining control after a Cancel... + if (m_cancelPending) { + qApp->restoreOverrideCursor(); + m_cancelPending = false; + } + + // if any item was selected before the operation was run + // it makes sense for the relevant toolbar buttons to be enabled + slot_deviceSelected(m_deviceSel->currentItem()); +} + +QString KKameraConfig::suggestName(const QString &name) +{ + QString new_name = name; + new_name.replace("/", ""); // we cannot have a slash in a URI's host + + if (!m_devices.contains(new_name)) return new_name; + + // try new names with a number appended until we find a free one + int i = 1; + while (i++ < 0xffff) { + new_name = name + " (" + QString::number(i) + ")"; + if (!m_devices.contains(new_name)) return new_name; + } + + return QString::null; +} + +void KKameraConfig::slot_addCamera() +{ + KCamera *m_device = new KCamera(QString::null,QString::null); + 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 &))); + KameraDeviceSelectDialog dialog(this, m_device); + if (dialog.exec() == QDialog::Accepted) { + dialog.save(); + m_device->setName(suggestName(m_device->model())); + m_devices.insert(m_device->name(), m_device); + populateDeviceListView(); + emit changed(true); + } else { + delete m_device; + } +} + +void KKameraConfig::slot_removeCamera() +{ + QString name = m_deviceSel->currentItem()->text(); + if (m_devices.contains(name)) { + KCamera *m_device = m_devices[name]; + m_devices.remove(name); + delete m_device; + m_config->deleteGroup(name, true); + populateDeviceListView(); + emit changed(true); + } +} + +void KKameraConfig::slot_testCamera() +{ + beforeCameraOperation(); + + QString name = m_deviceSel->currentItem()->text(); + if (m_devices.contains(name)) { + KCamera *m_device = m_devices[name]; + if (m_device->test()) + KMessageBox::information(this, i18n("Camera test was successful.")); + } + + afterCameraOperation(); +} + +void KKameraConfig::slot_configureCamera() +{ + QString name = m_deviceSel->currentItem()->text(); + if (m_devices.contains(name)) { + KCamera *m_device = m_devices[name]; + m_device->configure(); + } +} + +void KKameraConfig::slot_cameraSummary() +{ + QString summary; + QString name = m_deviceSel->currentItem()->text(); + if (m_devices.contains(name)) { + KCamera *m_device = m_devices[name]; + summary = m_device->summary(); + if (!summary.isNull()) { + KMessageBox::information(this, summary); + } + } +} + +void KKameraConfig::slot_cancelOperation() +{ + m_cancelPending = true; + // Prevent the user from keeping clicking Cancel + m_actions->action("camera_cancel")->setEnabled(false); + // and indicate that the click on Cancel did have some effect + qApp->setOverrideCursor(Qt::WaitCursor); +} + +void KKameraConfig::slot_deviceMenu(QIconViewItem *item, const QPoint &point) +{ + if (item) { + m_devicePopup->clear(); + m_actions->action("camera_test")->plug(m_devicePopup); + m_actions->action("camera_remove")->plug(m_devicePopup); + m_actions->action("camera_configure")->plug(m_devicePopup); + m_actions->action("camera_summary")->plug(m_devicePopup); + m_devicePopup->popup(point); + } +} + +void KKameraConfig::slot_deviceSelected(QIconViewItem *item) +{ + m_actions->action("camera_test")->setEnabled(item); + m_actions->action("camera_remove")->setEnabled(item); + m_actions->action("camera_configure")->setEnabled(item); + m_actions->action("camera_summary")->setEnabled(item); +} + +void KKameraConfig::cbGPIdle(GPContext * /*context*/, void * /*data*/) +{ + /*KKameraConfig *self( reinterpret_cast<KKameraConfig*>(data) );*/ + + qApp->processEvents(); +} + +GPContextFeedback KKameraConfig::cbGPCancel(GPContext * /*context*/, void *data) +{ + KKameraConfig *self( reinterpret_cast<KKameraConfig*>(data) ); + + // Since in practice no camera driver supports idle callbacks yet, + // we'll use the cancel callback as opportunity to process events + qApp->processEvents(); + + // If a cancel request is pending, ask gphoto to cancel + if (self->m_cancelPending) + return GP_CONTEXT_FEEDBACK_CANCEL; + else + return GP_CONTEXT_FEEDBACK_OK; +} + +QString KKameraConfig::quickHelp() const +{ + return i18n("<h1>Digital Camera</h1>\n" + "This module allows you to configure support for your digital camera.\n" + "You would need to select the camera's model and the port it is connected\n" + "to on your computer (e.g. USB, Serial, Firewire). If your camera doesn't\n" + "appear in the list of <i>Supported Cameras</i>, go to the\n" + "<a href=\"http://www.gphoto.org\">GPhoto web site</a> for a possible update.<br><br>\n" + "To view and download images from the digital camera, go to address\n" + "<a href=\"camera:/\">camera:/</a> in Konqueror and other KDE applications."); +} + +void KKameraConfig::slot_error(const QString &message) +{ + KMessageBox::error(this, message); +} + +void KKameraConfig::slot_error(const QString &message, const QString &details) +{ + KMessageBox::detailedError(this, message, details); +} + diff --git a/kamera/kcontrol/kamera.desktop b/kamera/kcontrol/kamera.desktop new file mode 100644 index 00000000..0fda1401 --- /dev/null +++ b/kamera/kcontrol/kamera.desktop @@ -0,0 +1,193 @@ +[Desktop Entry] +Comment=Configure Kamera +Comment[af]=Konfigureer Kamera +Comment[ar]=إعداد Kamera +Comment[az]=Kameranı Quraşdır +Comment[bg]=Настройване на цифров фотоапарат +Comment[br]=Kefluniañ Kamera +Comment[bs]=Podesi kameru +Comment[ca]=Configura Kamera +Comment[cs]=Nastavení Kamery +Comment[cy]=Ffurfweddu Kamera +Comment[da]=Indstil kamera +Comment[de]=Kamera einrichten +Comment[el]=Ρύθμιση Kamera +Comment[eo]=Agordu fotilon +Comment[es]=Configura Kamera +Comment[et]=Kaamera seadistamine +Comment[eu]=Konfiguratu Kamera +Comment[fa]=پیکربندی Kamera +Comment[fi]=Kameran asetukset +Comment[fr]=Configuration de Kamera +Comment[ga]=Cumraigh Kamera +Comment[gl]=Configurar Kamera +Comment[he]=שינוי הגדרות Kamera +Comment[hi]=कॉन्फ़िगर केमरा +Comment[hr]=Podesi Kameru +Comment[hu]=A digitális fényképezőgépek beállításai +Comment[id]=Konfigurasi kamera +Comment[is]=Stilla samskiptaforrit stafrænna myndavéla (Kamera) +Comment[it]=Configura Kamera +Comment[ja]=カメラの設定 +Comment[kk]=Kamera баптаулары +Comment[km]=កំណត់រចនាសម្ព័ន្ធ Kamera +Comment[ko]=카메라 설정 +Comment[lt]=Konfigūruoti Kamera +Comment[mk]=Конфигурирајте ја Kamera +Comment[ms]=Konfigurasi Kamera +Comment[mt]=Ikkonfigura Kamera +Comment[nb]=Tilpass Kamera +Comment[nds]=Kamera instellen +Comment[ne]=क्यामेरा कन्फिगर गर्नुहोस् +Comment[nl]=Camera instellen +Comment[nn]=Set opp Kamera +Comment[nso]=Beakanya Kamera +Comment[pa]=ਕੈਮਰਾ ਸੰਰਚਨਾ +Comment[pl]=Konfiguracja Kamery +Comment[pt]=Configuração do Kamera +Comment[pt_BR]=Configurar Kamera +Comment[ro]=Configurează aparatul foto digital +Comment[ru]=Настройка камеры +Comment[se]=Heivet govvenapperáhta +Comment[sk]=Nastaviť program Kamera +Comment[sl]=Nastavitve fotoaparata +Comment[sr]=Подеси Kamera-у +Comment[sr@Latn]=Podesi Kamera-u +Comment[sv]=Anpassa kamera +Comment[ta]=காமிராவை அமை +Comment[tg]=Танзимоти камера +Comment[th]=ปรับแต่ง Kamera +Comment[tr]=Kamera'yı Yapılandır +Comment[uk]=Налаштувати Kamera +Comment[uz]=Fotoaparatni moslash +Comment[uz@cyrillic]=Фотоапаратни мослаш +Comment[ven]=Dzudzanya kamera +Comment[xh]=Qwalasela Umfoti +Comment[zh_CN]=配置 Kamera +Comment[zh_HK]=設定 Kamera +Comment[zh_TW]=設定照相機 +Comment[zu]=Hlanganisa ikhamera +Keywords=gphoto,camera,digicam,webcam,kamera +Keywords[ar]=gphoto,كاميرا,كاميرا رقمية,كاميرا ويب,kamera +Keywords[az]=gphoto,kamera,digicam,veb kamera,Kamera,webcam +Keywords[bg]=фото, апарат, фотоапарат, камера, цифров, цифрова, gphoto, camera, digicam, webcam, kamera +Keywords[br]=gphoto,kamera,digicam,webcam,kamera +Keywords[ca]=gphoto,càmera,digicam,webcam,kamera +Keywords[cs]=gphoto,Kamera,Digitální kamera,Webová kamera,Foto +Keywords[da]=gphoto,kamera,digicam,webcam +Keywords[de]=gphoto,Kamera,Digicam,Webcam,Digitalkamera +Keywords[el]=gphoto,κάμερα,digicam,webcam,kamera +Keywords[eo]=gphoto,kamerao,fotilo,cifereca fotilo,TTT-fotilo +Keywords[es]=gphoto,cámara,digicam,webcam,kamera +Keywords[et]=gphoto,kaamera,digitaalkaamera,veebikaamera,kamera +Keywords[eu]=gphoto,kamera,digicam,webcam,kamera +Keywords[fa]=gphoto، دوربین، دوربین رقمی، دوربین وب، kamera +Keywords[fi]=gphoto,kamera,digicam,webcam +Keywords[fr]=gphoto,camera,digicam,webcam,kamera,caméscope,caméra,appareil photo +Keywords[he]=gphoto,kamera,מצלמה,מצלמת רשת,מצלמה דיגיטלית, amera,digicam,webcam +Keywords[hi]=जीफोटो,केमरा,डिजिकेम,वेबकेम,केमरा +Keywords[hu]=gphoto,fényképezőgép,digitális fényképezőgép,webkamera,videókamera +Keywords[is]=gphoto,myndavél,stafræn myndavél,webcam,kamera +Keywords[it]=gphoto,fotocamera,macchina fotografica digitale,webcam,kamera +Keywords[ja]=gphoto,カメラ,デジカム,ウェブカム,kamera +Keywords[km]=gphoto,ម៉ាស៊ីនថតរូប,digicam,ម៉ាស៊ីនថតតាមបណ្ដាញ,kamera +Keywords[ko]=gphoto,camera,digicam,webcam,kamera,사진,카메라,사진기,웹캠 +Keywords[lv]=gfoto,camera,digicam,webcam,kamera +Keywords[nb]=gphoto,kamera,digicam,webcam,webkamera +Keywords[nds]=gphoto,Kamera,Webcam,Kamera,Nettkamera +Keywords[ne]=जी फोटो, क्यामेरा, डिजिक्याम, वेबक्याम, कामेरा +Keywords[nl]=gphoto,camera,digicam,webcam,kamera,foto +Keywords[nn]=gphoto,fotoapparat,digitalt kamera,webkamera,vevkamera,kamera +Keywords[pl]=gphoto,kamera,kamera cyfrowa,kamera sieciowa +Keywords[pt]=gphoto,câmara,digicam,webcam,kamera +Keywords[pt_BR]=gphoto,câmera,câmera digital,webcam,kamera +Keywords[ro]=gphoto,aparat,foto,digicam,webcam,camera,kamera +Keywords[ru]=gphoto,camera,digicam,webcam,kamera,камера,фото +Keywords[sl]=gphoto,kamera,digicam,webcam,foto,fotoaparat,spletna kamera +Keywords[sr]=gphoto,camera,digicam,webcam,kamera,камера +Keywords[sr@Latn]=gphoto,camera,digicam,webcam,kamera,kamera +Keywords[sv]=gphoto,kamera,digital kamera,webbkamera,kamera +Keywords[ta]=ஜிபோட்டோ, காமிரா, டிஜிகேம்,வலைதள காமிரா, காமிரா +Keywords[tg]=gphoto,camera,digicam,webcam,kamera,камера,фото +Keywords[tr]=gphoto,kamera,digicam,web kamera,Kamera,webcam +Keywords[uk]=gphoto,камера,цифрова камера,камера Тенет,kamera +Keywords[ven]=Tshinepe tsha g,Tshaudzhia zwifanyiso,digicam,webcam,Tshaudzhiazwifanyiso +Keywords[xh]=gphoto,umfoti,digicam,webcam,umfoti +Keywords[zh_CN]=gphoto,camera,digicam,webcam,kamera,照相机,数码相机,摄像头 +Keywords[zh_TW]=gphoto,camera,digicam,webcam,kamera,照相機 +Keywords[zu]=gphoto,ikhamera,digicam,webcam,ikhamera +Name=Digital Camera +Name[af]=Digitaal Kamera +Name[ar]=كاميرا رقمية +Name[az]=Digital Kamera +Name[bg]=Фотоапарат +Name[br]=Kamera niverel +Name[bs]=Digitalna kamera +Name[ca]=Càmera digital +Name[cs]=Digitální fotoaparát +Name[cy]=Camera Digidol +Name[da]=Digitalt kamera +Name[de]=Digitalkamera +Name[el]=Ψηφιακή κάμερα +Name[eo]=Cifereca fotilo +Name[es]=Cámara digital +Name[et]=Digitaalkaamera +Name[eu]=Kamera digitala +Name[fa]=دوربین رقمی +Name[fi]=Digitaalikamera +Name[fr]=Appareil photo numérique +Name[ga]=Ceamara Digiteach +Name[gl]=Cámara dixital +Name[he]=מצלמה דיגיטלית +Name[hi]=डिजिटल कैमरा +Name[hr]=Digitalna kamera +Name[hu]=Digitális fényképezőgép +Name[is]=Stafræn myndavél +Name[it]=Macchina fotografica digitale +Name[ja]=デジタルカメラ +Name[kk]=Цифрлық камера +Name[km]=ម៉ាស៊ីនថតរូបឌីជីថល +Name[lt]=Skaitmeninė kamera +Name[lv]=Digitālā Kamera +Name[mk]=Дигитална камера +Name[ms]=Kamera Digital +Name[mt]=Kamera diġitali +Name[nb]=Digitalkamera +Name[nds]=Digitaalkamera +Name[ne]= डिजिटल क्यामेरा +Name[nl]=Digitale camera +Name[nn]=Digitalkamera +Name[nso]=Camera ya Digital +Name[pa]=ਡਿਜ਼ੀਟਲ ਕੈਮਰਾ +Name[pl]=Aparat cyfrowy +Name[pt]=Máquina Fotográfica Digital +Name[pt_BR]=Câmera Digital +Name[ro]=Aparat foto digital +Name[ru]=Цифровая камера +Name[se]=Digitalalaš govvenapperáhtta +Name[sk]=Digitálny fotoaparát +Name[sl]=Digitalni fotoaparat +Name[sr]=Дигитална камера +Name[sr@Latn]=Digitalna kamera +Name[sv]=Digitalkamera +Name[ta]= Digital Camera +Name[tg]=Камераи digital +Name[th]=กล้องดิจิตอล +Name[tr]=Sayısal Kamera +Name[uk]=Цифровий фотоапарат +Name[uz]=Fotoaparat +Name[uz@cyrillic]=Фотоапарат +Name[ven]=Tshau dzhia zwifanyiso tsha didzhithala +Name[xh]=Ikhamera Yesuntswana +Name[zh_CN]=数码相机 +Name[zh_HK]=數碼相機 +Name[zh_TW]=數位相機 +Name[zu]=Ikhamera ebonisa inani ngalinye +Terminal=false +Type=Application +X-KDE-Library=kamera +X-KDE-ModuleType=Library +Icon=camera +Exec=kcmshell kamera +DocPath=kamera/index.html +Categories=Qt;KDE;Settings;X-KDE-settings-hardware; diff --git a/kamera/kcontrol/kamera.h b/kamera/kcontrol/kamera.h new file mode 100644 index 00000000..35f93bc0 --- /dev/null +++ b/kamera/kcontrol/kamera.h @@ -0,0 +1,115 @@ +/* + + 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. + +*/ +#ifndef __kamera_h__ +#define __kamera_h__ + +#include <kcmodule.h> +#include <gphoto2.h> + +class QWidget; +class QRadioButton; +class QPushButton; +class QComboBox; +class QVButtonGroup; +class QLineEdit; +class QWidgetStack; +class QCheckBox; +class QIconViewItem; + +class KCamera; +class KameraDeviceSelectDialog; +class KSimpleConfig; +class KIconView; +class KActionCollection; +class KToolBar; +class KPopupMenu; + +class KKameraConfig : public KCModule +{ + Q_OBJECT + friend class KameraDeviceSelectDialog; + +public: + KKameraConfig(QWidget *parent, const char *name, const QStringList &); + virtual ~KKameraConfig(); + + // KCModule interface methods + void load(); + void load(bool useDefaults); + void save(); + void defaults(); + int buttons(); + QString quickHelp() const; + +protected: + QString suggestName(const QString &name); + +protected slots: + void slot_deviceMenu(QIconViewItem *item, const QPoint &point); + void slot_deviceSelected(QIconViewItem *item); + void slot_addCamera(); + void slot_removeCamera(); + void slot_configureCamera(); + void slot_cameraSummary(); + void slot_testCamera(); + void slot_cancelOperation(); + void slot_error(const QString &message); + void slot_error(const QString &message, const QString &details); + +private: + void displayGPFailureDialogue(void); + void displayGPSuccessDialogue(void); + void displayCameraAbilities(const CameraAbilities &abilities); + void populateDeviceListView(void); + void beforeCameraOperation(void); + void afterCameraOperation(void); + + // gphoto callbacks + static void cbGPIdle(GPContext *context, void *data); + static GPContextFeedback cbGPCancel(GPContext *context, void *data); + +private: + typedef QMap<QString, KCamera *> CameraDevicesMap; + + KSimpleConfig *m_config; + CameraDevicesMap m_devices; + bool m_cancelPending; + + // gphoto members + GPContext *m_context; + + // widgets for the cameras listview + KIconView *m_deviceSel; + KActionCollection *m_actions; + QPushButton *m_addCamera, *m_removeCamera, *m_testCamera, *m_configureCamera; + KToolBar *m_toolbar; + KPopupMenu *m_devicePopup; + + // true if libgphoto2 was initialised successfully in + // the constructor + bool m_gpInitialised; + + static KKameraConfig *m_instance; +}; + +#endif diff --git a/kamera/kcontrol/kameraconfigdialog.cpp b/kamera/kcontrol/kameraconfigdialog.cpp new file mode 100644 index 00000000..5af0b33d --- /dev/null +++ b/kamera/kcontrol/kameraconfigdialog.cpp @@ -0,0 +1,317 @@ +/* + + 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 <qlayout.h> +#include <qgrid.h> +#include <qlabel.h> +#include <qvgroupbox.h> +#include <qcheckbox.h> +#include <qradiobutton.h> +#include <qlineedit.h> +#include <qcombobox.h> +#include <qslider.h> +#include <qvbuttongroup.h> +#include <qhbuttongroup.h> +#include <qvbox.h> +#include <qtabwidget.h> +#include <qwhatsthis.h> + +#include <klocale.h> + +#include "kameraconfigdialog.h" +#include "kameraconfigdialog.moc" + +KameraConfigDialog::KameraConfigDialog(Camera */*camera*/, + CameraWidget *widget, + QWidget *parent, + const char *name) : +KDialogBase(parent, name, true, QString::null, Ok|Cancel, Ok ), +m_widgetRoot(widget) +{ + QFrame *main = makeMainWidget(); + QVBoxLayout *topLayout = new QVBoxLayout(main, 0, spacingHint()); + topLayout->setAutoAdd(true); + + m_tabWidget = 0; + + appendWidget(main, widget); +} + +void KameraConfigDialog::appendWidget(QWidget *parent, CameraWidget *widget) +{ + QWidget *newParent = parent; + + CameraWidgetType widget_type; + const char *widget_name; + const char *widget_info; + const char *widget_label; + float widget_value_float; + int widget_value_int; + const char *widget_value_string; + gp_widget_get_type(widget, &widget_type); + gp_widget_get_label(widget, &widget_label); + gp_widget_get_info(widget, &widget_info); + gp_widget_get_name(widget, &widget_name); + + QString whats_this = QString::fromLocal8Bit(widget_info); // gphoto2 doesn't seem to have any standard for i18n + + // Add this widget to parent + switch(widget_type) { + case GP_WIDGET_WINDOW: + { + setCaption(widget_label); + + break; + } + case GP_WIDGET_SECTION: + { + if (!m_tabWidget) + m_tabWidget = new QTabWidget(parent); + QWidget *tab = new QWidget(m_tabWidget); + // widgets are to be aligned vertically in the tab + QVBoxLayout *tabLayout = new QVBoxLayout(tab, marginHint(), + spacingHint()); + m_tabWidget->insertTab(tab, widget_label); + QVBox *tabContainer = new QVBox(tab); + tabContainer->setSpacing(spacingHint()); + tabLayout->addWidget(tabContainer); + newParent = tabContainer; + + tabLayout->addStretch(); + + break; + } + case GP_WIDGET_TEXT: + { + gp_widget_get_value(widget, &widget_value_string); + + QGrid *grid = new QGrid(2, Horizontal, parent); + grid->setSpacing(spacingHint()); + new QLabel(QString::fromLocal8Bit( widget_label )+":", grid); + QLineEdit *lineEdit = new QLineEdit(widget_value_string, grid); + m_wmap.insert(widget, lineEdit); + + if (!whats_this.isEmpty()) + QWhatsThis::add(grid, whats_this); + + break; + } + case GP_WIDGET_RANGE: + { + float widget_low; + float widget_high; + float widget_increment; + gp_widget_get_range(widget, &widget_low, &widget_high, &widget_increment); + gp_widget_get_value(widget, &widget_value_float); + + QGroupBox *groupBox = new QVGroupBox(widget_label, parent); + QSlider *slider = new QSlider( + ( int )widget_low, + ( int )widget_high, + ( int )widget_increment, + ( int )widget_value_float, + QSlider::Horizontal, + groupBox ); + m_wmap.insert(widget, slider); + + if (!whats_this.isEmpty()) + QWhatsThis::add(groupBox, whats_this); + + break; + } + case GP_WIDGET_TOGGLE: + { + gp_widget_get_value(widget, &widget_value_int); + + QCheckBox *checkBox = new QCheckBox(widget_label, parent); + checkBox->setChecked(widget_value_int); + m_wmap.insert(widget, checkBox); + + if (!whats_this.isEmpty()) + QWhatsThis::add(checkBox, whats_this); + + break; + } + case GP_WIDGET_RADIO: + { + gp_widget_get_value(widget, &widget_value_string); + + int count = gp_widget_count_choices(widget); + + // for less than 5 options, align them horizontally + QButtonGroup *buttonGroup; + if (count > 4) + buttonGroup = new QVButtonGroup(widget_label, parent); + else + buttonGroup = new QHButtonGroup(widget_label, parent); + + for(int i = 0; i < count; ++i) { + const char *widget_choice; + gp_widget_get_choice(widget, i, &widget_choice); + + new QRadioButton(widget_choice, buttonGroup); + if(!strcmp(widget_value_string, widget_choice)) + buttonGroup->setButton(i); + } + m_wmap.insert(widget, buttonGroup); + + if (!whats_this.isEmpty()) + QWhatsThis::add(buttonGroup, whats_this); + + break; + } + case GP_WIDGET_MENU: + { + gp_widget_get_value(widget, &widget_value_string); + + QComboBox *comboBox = new QComboBox(FALSE, parent); + comboBox->clear(); + for(int i = 0; i < gp_widget_count_choices(widget); ++i) { + const char *widget_choice; + gp_widget_get_choice(widget, i, &widget_choice); + + comboBox->insertItem(widget_choice); + if(!strcmp(widget_value_string, widget_choice)) + comboBox->setCurrentItem(i); + } + m_wmap.insert(widget, comboBox); + + if (!whats_this.isEmpty()) + QWhatsThis::add(comboBox, whats_this); + + break; + } + case GP_WIDGET_BUTTON: + { + // TODO + // I can't see a way of implementing this. Since there is + // no way of telling which button sent you a signal, we + // can't map to the appropriate widget->callback + new QLabel(i18n("Button (not supported by KControl)"), parent); + + break; + } + case GP_WIDGET_DATE: + { + // TODO + new QLabel(i18n("Date (not supported by KControl)"), parent); + + break; + } + default: + return; + } + + // Append all this widgets children + for(int i = 0; i < gp_widget_count_children(widget); ++i) { + CameraWidget *widget_child; + gp_widget_get_child(widget, i, &widget_child); + appendWidget(newParent, widget_child); + } + + // Things that must be done after all children were added +/* + switch (widget_type) { + case GP_WIDGET_SECTION: + { + tabLayout->addItem( new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding) ); + break; + } + } +*/ +} + +void KameraConfigDialog::updateWidgetValue(CameraWidget *widget) +{ + CameraWidgetType widget_type; + gp_widget_get_type(widget, &widget_type); + + switch(widget_type) { + case GP_WIDGET_WINDOW: + // nothing to do + break; + case GP_WIDGET_SECTION: + // nothing to do + break; + case GP_WIDGET_TEXT: + { + QLineEdit *lineEdit = static_cast<QLineEdit *>(m_wmap[widget]); + gp_widget_set_value(widget, (void *)lineEdit->text().local8Bit().data()); + + break; + } + case GP_WIDGET_RANGE: + { + QSlider *slider = static_cast<QSlider *>(m_wmap[widget]); + float value_float = slider->value(); + gp_widget_set_value(widget, (void *)&value_float); + + break; + } + case GP_WIDGET_TOGGLE: + { + QCheckBox *checkBox = static_cast<QCheckBox *>(m_wmap[widget]); + int value_int = checkBox->isChecked() ? 1 : 0; + gp_widget_set_value(widget, (void *)&value_int); + + break; + } + case GP_WIDGET_RADIO: + { + QButtonGroup *buttonGroup = static_cast<QVButtonGroup *>(m_wmap[widget]); + gp_widget_set_value(widget, (void *)buttonGroup->selected()->text().local8Bit().data()); + + break; + } + case GP_WIDGET_MENU: + { + QComboBox *comboBox = static_cast<QComboBox *>(m_wmap[widget]); + gp_widget_set_value(widget, (void *)comboBox->currentText().local8Bit().data()); + + break; + } + case GP_WIDGET_BUTTON: + // nothing to do + break; + case GP_WIDGET_DATE: + { + // not implemented + break; + } + } + + // Copy child widget values + for(int i = 0; i < gp_widget_count_children(widget); ++i) { + CameraWidget *widget_child; + gp_widget_get_child(widget, i, &widget_child); + updateWidgetValue(widget_child); + } +} + +void KameraConfigDialog::slotOk() +{ + // Copy Qt widget values into CameraWidget hierarchy + updateWidgetValue(m_widgetRoot); + + // 'ok' dialog + accept(); +} diff --git a/kamera/kcontrol/kameraconfigdialog.h b/kamera/kcontrol/kameraconfigdialog.h new file mode 100644 index 00000000..73b2a012 --- /dev/null +++ b/kamera/kcontrol/kameraconfigdialog.h @@ -0,0 +1,53 @@ +/* + + 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. + +*/ +#ifndef __kameraconfigdialog_h__ +#define __kameraconfigdialog_h__ + +#include <qmap.h> +#include <kdialogbase.h> +#include <qtabwidget.h> + +extern "C" { + #include <gphoto2.h> +} + +class KameraConfigDialog : public KDialogBase +{ + Q_OBJECT +public: + KameraConfigDialog(Camera *camera, CameraWidget *widget, + QWidget *parent = 0, const char *name = 0); + +private slots: + void slotOk(); + +private: + void appendWidget(QWidget *parent, CameraWidget *widget); + void updateWidgetValue(CameraWidget *widget); + + QMap<CameraWidget *, QWidget *> m_wmap; + CameraWidget *m_widgetRoot; + QTabWidget *m_tabWidget; +}; + +#endif diff --git a/kamera/kcontrol/kameradevice.cpp b/kamera/kcontrol/kameradevice.cpp new file mode 100644 index 00000000..010bf694 --- /dev/null +++ b/kamera/kcontrol/kameradevice.cpp @@ -0,0 +1,476 @@ +/* + + 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 <qlayout.h> +#include <qwidgetstack.h> +#include <qvbuttongroup.h> +#include <qvgroupbox.h> +#include <qcombobox.h> +#include <qlineedit.h> +#include <qradiobutton.h> +#include <qwhatsthis.h> +#include <qlabel.h> +#include <qgrid.h> + +#include <klocale.h> +#include <kconfig.h> +#include <klistview.h> +#include <kmessagebox.h> +#include <kdebug.h> + +extern "C" { + #include <gphoto2.h> +} + +#include "kamera.h" +#include "kameraconfigdialog.h" +#include "kameradevice.moc" + +// 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= 3; +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; +} + +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) + return false; + + if(gp_abilities_list_new(&m_abilitylist) != GP_OK) { + emit error(i18n("Could not allocate memory for 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.local8Bit().data()); + if(index < 0) { + emit error(i18n("Description of abilities for camera %1 is not available." + " Configuration options may be incorrect.").arg(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 || !m_path) + 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.local8Bit().data()), &info); + gp_port_info_list_free(il); + gp_camera_set_abilities(m_camera, m_abilities); + gp_camera_set_port_info(m_camera, info); + + // this might take some time (esp. for non-existant 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."), + 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(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."), 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."), 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) +{ + config->setGroup(m_name); + if (m_model.isNull()) + m_model = config->readEntry("Model"); + if (m_path.isNull()) + m_path = config->readEntry("Path"); + invalidateCamera(); +} + +void KCamera::save(KConfig *config) +{ + config->setGroup(m_name); + config->writeEntry("Model", m_model); + config->writeEntry("Path", m_path); +} + +QString KCamera::portName() +{ + QString port = m_path.left(m_path.find(":")).lower(); + 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) + : KDialogBase(parent, "kkameradeviceselect", true, i18n("Select Camera Device"), Ok | Cancel, Ok, 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, 0, KDialog::spacingHint()); + + // the models list + m_modelSel = new KListView(page); + topLayout->addWidget( m_modelSel ); + m_modelSel->addColumn(i18n("Supported Cameras")); + m_modelSel->setColumnWidthMode(0, QListView::Maximum); + connect(m_modelSel, SIGNAL(selectionChanged(QListViewItem *)), + SLOT(slot_setModel(QListViewItem *))); + // make sure listview only as wide as it needs to be + m_modelSel->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, + QSizePolicy::Preferred)); + + QVBoxLayout *rightLayout = new QVBoxLayout(0L, 0, KDialog::spacingHint()); + topLayout->addLayout( rightLayout ); + + m_portSelectGroup = new QVButtonGroup(i18n("Port"), page); + rightLayout->addWidget(m_portSelectGroup); + m_portSettingsGroup = new QVGroupBox(i18n("Port Settings"), page); + rightLayout->addWidget(m_portSettingsGroup); + + // Create port type selection radiobuttons. + m_serialRB = new QRadioButton(i18n("Serial"), m_portSelectGroup); + m_portSelectGroup->insert(m_serialRB, INDEX_SERIAL); + QWhatsThis::add(m_serialRB, i18n("If this option is checked, the camera would have to be connected one of the serial ports (known as COM in Microsoft Windows) in your computer.")); + m_USBRB = new QRadioButton(i18n("USB"), m_portSelectGroup); + m_portSelectGroup->insert(m_USBRB, INDEX_USB); + QWhatsThis::add(m_USBRB, i18n("If this option is checked, the camera would have to be connected to one of the USB slots in your computer or USB hub.")); + // Create port settings widget stack + m_settingsStack = new QWidgetStack(m_portSettingsGroup); + connect(m_portSelectGroup, SIGNAL(clicked(int)), + m_settingsStack, SLOT(raiseWidget(int))); + + // none tab + m_settingsStack->addWidget(new QLabel(i18n("No port type selected."), + m_settingsStack), INDEX_NONE); + + // serial tab + QGrid *grid = new QGrid(2, m_settingsStack); + grid->setSpacing(KDialog::spacingHint()); + new QLabel(i18n("Port:"), grid); + m_serialPortCombo = new QComboBox(TRUE, grid); + QWhatsThis::add(m_serialPortCombo, i18n("Here you should choose the serial port you connect the camera to.")); + m_settingsStack->addWidget(grid, INDEX_SERIAL); + + grid = new QGrid(2, m_settingsStack); + grid->setSpacing(KDialog::spacingHint()); + new QLabel(i18n("Port"), grid); + + m_settingsStack->addWidget(new + QLabel(i18n("No further configuration is required for USB."), + m_settingsStack), INDEX_USB); + + // 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) { + if (strncmp(info.path, "serial:", 7) == 0) + m_serialPortCombo->insertItem(QString::fromLatin1(info.path).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 ); +} + +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) { + new QListViewItem(m_modelSel, a.model); + } + } + return true; + } +} + +void KameraDeviceSelectDialog::save() +{ + m_device->setModel(m_modelSel->currentItem()->text(0)); + + if (m_portSelectGroup->selected()) { + QString type = m_portSelectGroup->selected()->text(); + + if(type == i18n("Serial")) + m_device->setPath("serial:" + m_serialPortCombo->currentText()); + else if(type == i18n("USB")) + m_device->setPath("usb:"); + } else { + // This camera has no port type (e.g. "Directory Browse" camera). + // Do nothing. + } +} + +void KameraDeviceSelectDialog::load() +{ + QString path = m_device->path(); + QString port = path.left(path.find(":")).lower(); + + if (port == "serial") setPortType(INDEX_SERIAL); + if (port == "usb") setPortType(INDEX_USB); + + QListViewItem *modelItem = m_modelSel->firstChild(); + if( modelItem) + { + do { + if (modelItem->text(0) == m_device->model()) { + m_modelSel->setSelected(modelItem, true); + m_modelSel->ensureItemVisible(modelItem); + } + } while ( ( modelItem = modelItem->nextSibling() ) ); + } +} + +void KameraDeviceSelectDialog::slot_setModel(QListViewItem *item) +{ + enableButtonOK(true); + m_portSelectGroup->setEnabled(true); + m_portSettingsGroup->setEnabled(true); + + QString model = item->text(0); + + CameraAbilities abilities; + int index = gp_abilities_list_lookup_model(m_device->m_abilitylist, model.local8Bit().data()); + if(index < 0) { + slot_error(i18n("Description of abilities for camera %1 is not available." + " Configuration options may be incorrect.").arg(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); + + // turn off any selected port + QButton *selected = m_portSelectGroup->selected(); + if(selected != NULL) + selected->toggle(); + + // 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.").arg(model)); + } +} + +void KameraDeviceSelectDialog::setPortType(int type) +{ + // Enable the correct button + m_portSelectGroup->setButton(type); + + // Bring the right tab to the front + m_settingsStack->raiseWidget(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); +} diff --git a/kamera/kcontrol/kameradevice.h b/kamera/kcontrol/kameradevice.h new file mode 100644 index 00000000..aae24e02 --- /dev/null +++ b/kamera/kcontrol/kameradevice.h @@ -0,0 +1,117 @@ +/* + + 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. + +*/ +#ifndef __kameradevice_h__ +#define __kameradevice_h__ + +#include <qmap.h> +#include <kdialogbase.h> +#include <config.h> + +class KConfig; +class QString; +class KListView; +class QWidgetStack; +class QVButtonGroup; +class QVGroupBox; +class QComboBox; +class QLineEdit; +class QRadioButton; + +class KCamera : public QObject { + friend class KameraDeviceSelectDialog; + Q_OBJECT +public: + KCamera(const QString &name, const QString &path); + ~KCamera(); + void invalidateCamera(); + bool configure(); + void load(KConfig *m_config); + void save(KConfig *m_config); + bool test(); + QStringList supportedPorts(); + + Camera* camera(); + QString name() const { return m_name ; } + QString model() const { return m_model; } + QString path() const { return m_path; } + QString portName(); + + QString summary(); + CameraAbilities abilities(); + + void setName(const QString &name); + void setModel(const QString &model); + void setPath(const QString &path); + + bool isTestable() const; + bool isConfigurable(); + +signals: + void error(const QString &message); + void error(const QString &message, const QString &details); + +protected: + bool initInformation(); + bool initCamera(); +// void doConfigureCamera(Camera *camera, CameraWidget *widgets); +// int frontend_prompt(Camera *camera, CameraWidget *widgets); + + Camera *m_camera; +// KConfig *m_config; + QString m_name; // the camera's real name + QString m_model; + QString m_path; + CameraAbilities m_abilities; + CameraAbilitiesList *m_abilitylist; +}; + +class KameraDeviceSelectDialog : public KDialogBase +{ + Q_OBJECT +public: + KameraDeviceSelectDialog(QWidget *parent, KCamera *device); + void save(); + void load(); +protected slots: + void slot_setModel(QListViewItem *item); + void slot_error(const QString &message); + void slot_error(const QString &message, const QString &details); +protected: + KCamera *m_device; + + bool populateCameraListView(void); + void setPortType(int type); + + // port settings widgets + KListView *m_modelSel; + QLineEdit *m_nameEdit; + QWidgetStack *m_settingsStack; + QVButtonGroup *m_portSelectGroup; + QVGroupBox *m_portSettingsGroup; + QComboBox *m_serialPortCombo; + // port selection radio buttons + QRadioButton *m_serialRB; + QRadioButton *m_USBRB; +}; + +#endif diff --git a/kamera/kioslave/Makefile.am b/kamera/kioslave/Makefile.am new file mode 100644 index 00000000..4c6c148e --- /dev/null +++ b/kamera/kioslave/Makefile.am @@ -0,0 +1,17 @@ +# $Id$ +# Makefile for kdebase/kioslave/kamera + +INCLUDES= -I$(srcdir)/../.. -I$(srcdir)/.. $(all_includes) $(GPHOTO2_INCS) + +####### Files + +kde_module_LTLIBRARIES = kio_kamera.la + +kio_kamera_la_SOURCES = kamera.cpp +kio_kamera_la_LIBADD = $(LIB_KIO) -lgphoto2 +kio_kamera_la_LDFLAGS = $(all_libraries) $(GPHOTO2_LIBS) -module $(KDE_PLUGIN) + +noinst_HEADERS = kamera.h + +kde_services_DATA = camera.protocol + diff --git a/kamera/kioslave/camera.protocol b/kamera/kioslave/camera.protocol new file mode 100644 index 00000000..947d02b9 --- /dev/null +++ b/kamera/kioslave/camera.protocol @@ -0,0 +1,16 @@ +[Protocol] +exec=kio_kamera +protocol=camera +input=none +output=filesystem +listing=Name,Type +reading=true +writing=false +deleting=true +source=true +makedir=false +linking=false +moving=false +Icon=camera +maxInstances=1 +Class=:local diff --git a/kamera/kioslave/kamera.cpp b/kamera/kioslave/kamera.cpp new file mode 100644 index 00000000..ab4eb469 --- /dev/null +++ b/kamera/kioslave/kamera.cpp @@ -0,0 +1,1066 @@ +/* + + Copyright (C) 2001 The Kompany + 2001-2003 Ilya Konstantinov <kde-devel@future.shiny.co.il> + 2001-2007 Marcus Meissner <marcus@jet.franken.de> + + 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 <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <signal.h> +#include <errno.h> + +#include <qfile.h> +#include <qtextstream.h> + +#include <kdebug.h> +#include <kinstance.h> +#include <kstandarddirs.h> +#include <kconfig.h> +#include <ksimpleconfig.h> +#include <klocale.h> +#include <kprotocolinfo.h> +#include <kio/slaveconfig.h> + +#include <config.h> + +#include "kamera.h" + +#define MAXIDLETIME 30 /* seconds */ + +#define tocstr(x) ((x).local8Bit()) + +using namespace KIO; + +extern "C" +{ + KDE_EXPORT int kdemain(int argc, char **argv); + + static void frontendCameraStatus(GPContext *context, const char *format, va_list args, void *data); + static unsigned int frontendProgressStart( + GPContext *context, float totalsize, const char *format, + va_list args, void *data + ); + static void frontendProgressUpdate( + GPContext *context, unsigned int id, float current, void *data + ); +} + +int kdemain(int argc, char **argv) +{ + KInstance instance("kio_kamera"); + + if(argc != 4) { + kdDebug(7123) << "Usage: kio_kamera protocol " + "domain-socket1 domain-socket2" << endl; + exit(-1); + } + KameraProtocol slave(argv[2], argv[3]); + slave.dispatchLoop(); + return 0; +} + +KameraProtocol::KameraProtocol(const QCString &pool, const QCString &app) +: SlaveBase("camera", pool, app), +m_camera(NULL) +{ + // attempt to initialize libgphoto2 and chosen camera (requires locking) + // (will init m_camera, since the m_camera's configuration is empty) + m_camera = 0; + m_file = NULL; + m_config = new KSimpleConfig(KProtocolInfo::config("camera")); + m_context = gp_context_new(); + actiondone = true; + cameraopen = false; + m_lockfile = locateLocal("tmp", "kamera"); + idletime = 0; +} + +// This handler is getting called every second. We use it to do the +// delayed close of the camera. +// Logic is: +// - No more requests in the queue (signaled by actiondone) AND +// - We are MAXIDLETIME seconds idle OR +// - Another slave wants to have access to the camera. +// +// The existance of a lockfile is used to signify "please give up camera". +// +void KameraProtocol::special(const QByteArray&) { + kdDebug(7123) << "KameraProtocol::special() at " << getpid() << endl; + + if (!actiondone && cameraopen) { + struct stat stbuf; + if ((-1!=::stat(m_lockfile.utf8(),&stbuf)) || (idletime++ >= MAXIDLETIME)) { + kdDebug(7123) << "KameraProtocol::special() closing camera." << endl; + closeCamera(); + setTimeoutSpecialCommand(-1); + } else { + // continue to wait + setTimeoutSpecialCommand(1); + } + } else { + // We let it run until the slave gets no actions anymore. + setTimeoutSpecialCommand(1); + } + actiondone = false; +} + +KameraProtocol::~KameraProtocol() +{ + kdDebug(7123) << "KameraProtocol::~KameraProtocol()" << endl; + delete m_config; + if(m_camera) { + closeCamera(); + gp_camera_free(m_camera); + m_camera = NULL; + } +} + +// initializes the camera for usage - should be done before operations over the wire +bool KameraProtocol::openCamera(QString &str) { + idletime = 0; + actiondone = true; + if (!m_camera) { + reparseConfiguration(); + } else { + if (!cameraopen) { + int ret, tries = 15; + kdDebug(7123) << "KameraProtocol::openCamera at " << getpid() << endl; + while (tries--) { + ret = gp_camera_init(m_camera, m_context); + if ( (ret == GP_ERROR_IO_USB_CLAIM) || + (ret == GP_ERROR_IO_LOCK)) { + // just create / touch if not there + int fd = ::open(m_lockfile.utf8(),O_CREAT|O_WRONLY,0600); + if (fd != -1) ::close(fd); + ::sleep(1); + kdDebug(7123) << "openCamera at " << getpid() << "- busy, ret " << ret << ", trying again." << endl; + continue; + } + if (ret == GP_OK) break; + str = gp_result_as_string(ret); + return false; + } + ::unlink(m_lockfile.utf8()); + setTimeoutSpecialCommand(1); + kdDebug(7123) << "openCamera succeeded at " << getpid() << endl; + cameraopen = true; + } + } + return true; +} + +// should be done after operations over the wire +void KameraProtocol::closeCamera(void) +{ + int gpr; + + if (!m_camera) + return; + + kdDebug(7123) << "KameraProtocol::closeCamera at " << getpid() << endl; + if ((gpr=gp_camera_exit(m_camera,m_context))!=GP_OK) { + kdDebug(7123) << "closeCamera failed with " << gp_result_as_string(gpr) << endl; + } + // HACK: gp_camera_exit() in gp 2.0 does not close the port if there + // is no camera_exit function. + gp_port_close(m_camera->port); + cameraopen = false; + return; +} + +static QString fix_foldername(QString ofolder) { + QString folder = ofolder; + if (folder.length() > 1) { + while ((folder.length()>1) && (folder.right(1) == "/")) + folder = folder.left(folder.length()-1); + } + if (folder.length() == 0) + folder = "/"; + return folder; +} + +// The KIO slave "get" function (starts a download from the camera) +// The actual returning of the data is done in the frontend callback functions. +void KameraProtocol::get(const KURL &url) +{ + kdDebug(7123) << "KameraProtocol::get(" << url.path() << ")" << endl; + + CameraFileType fileType; + int gpr; + if (url.host().isEmpty()) { + error(KIO::ERR_DOES_NOT_EXIST, url.path()); + return; + } + + if(!openCamera()) { + error(KIO::ERR_DOES_NOT_EXIST, url.path()); + return; + } + + // fprintf(stderr,"get(%s)\n",url.path().latin1()); + +#define GPHOTO_TEXT_FILE(xx) \ + if (!url.path().compare("/" #xx ".txt")) { \ + CameraText xx; \ + gpr = gp_camera_get_##xx(m_camera, &xx, m_context); \ + if (gpr != GP_OK) { \ + error(KIO::ERR_DOES_NOT_EXIST, url.path()); \ + return; \ + } \ + QByteArray chunkDataBuffer; \ + chunkDataBuffer.setRawData(xx.text, strlen(xx.text)); \ + data(chunkDataBuffer); \ + processedSize(strlen(xx.text)); \ + chunkDataBuffer.resetRawData(xx.text, strlen(xx.text)); \ + finished(); \ + return; \ + } + + GPHOTO_TEXT_FILE(about); + GPHOTO_TEXT_FILE(manual); + GPHOTO_TEXT_FILE(summary); + +#undef GPHOTO_TEXT_FILE + // emit info message + infoMessage( i18n("Retrieving data from camera <b>%1</b>").arg(url.user()) ); + + // Note: There's no need to re-read directory for each get() anymore + gp_file_new(&m_file); + + // emit the total size (we must do it before sending data to allow preview) + CameraFileInfo info; + + gpr = gp_camera_file_get_info(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), &info, m_context); + if (gpr != GP_OK) { + // fprintf(stderr,"Folder %s / File %s not found, gpr is %d\n",folder.latin1(), url.fileName().latin1(), gpr); + gp_file_unref(m_file); + if ((gpr == GP_ERROR_FILE_NOT_FOUND) || (gpr == GP_ERROR_DIRECTORY_NOT_FOUND)) + error(KIO::ERR_DOES_NOT_EXIST, url.path()); + else + error(KIO::ERR_UNKNOWN, gp_result_as_string(gpr)); + return; + } + + // at last, a proper API to determine whether a thumbnail was requested. + if(cameraSupportsPreview() && metaData("thumbnail") == "1") { + kdDebug(7123) << "get() retrieving the thumbnail" << endl; + fileType = GP_FILE_TYPE_PREVIEW; + if (info.preview.fields & GP_FILE_INFO_SIZE) + totalSize(info.preview.size); + if (info.preview.fields & GP_FILE_INFO_TYPE) + mimeType(info.preview.type); + } else { + kdDebug(7123) << "get() retrieving the full-scale photo" << endl; + fileType = GP_FILE_TYPE_NORMAL; + if (info.file.fields & GP_FILE_INFO_SIZE) + totalSize(info.file.size); + if (info.preview.fields & GP_FILE_INFO_TYPE) + mimeType(info.file.type); + } + + // fetch the data + m_fileSize = 0; + gpr = gp_camera_file_get(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), fileType, m_file, m_context); + if ( (gpr == GP_ERROR_NOT_SUPPORTED) && + (fileType == GP_FILE_TYPE_PREVIEW) + ) { + // If we get here, the file info command information + // will either not be used, or still valid. + fileType = GP_FILE_TYPE_NORMAL; + gpr = gp_camera_file_get(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), fileType, m_file, m_context); + } + switch(gpr) { + case GP_OK: + break; + case GP_ERROR_FILE_NOT_FOUND: + case GP_ERROR_DIRECTORY_NOT_FOUND: + gp_file_unref(m_file); + m_file = NULL; + error(KIO::ERR_DOES_NOT_EXIST, url.fileName()); + return ; + default: + gp_file_unref(m_file); + m_file = NULL; + error(KIO::ERR_UNKNOWN, gp_result_as_string(gpr)); + return; + } + // emit the mimetype + // NOTE: we must first get the file, so that CameraFile->name would be set + const char *fileMimeType; + gp_file_get_mime_type(m_file, &fileMimeType); + mimeType(fileMimeType); + + // We need to pass left over data here. Some camera drivers do not + // implement progress callbacks! + const char *fileData; + long unsigned int fileSize; + // This merely returns us a pointer to gphoto's internal data + // buffer -- there's no expensive memcpy + gpr = gp_file_get_data_and_size(m_file, &fileData, &fileSize); + if (gpr != GP_OK) { + kdDebug(7123) << "get():: get_data_and_size failed." << endl; + gp_file_free(m_file); + m_file = NULL; + error(KIO::ERR_UNKNOWN, gp_result_as_string(gpr)); + return; + } + // make sure we're not sending zero-sized chunks (=EOF) + // also make sure we send only if the progress did not send the data + // already. + if ((fileSize > 0) && (fileSize - m_fileSize)>0) { + unsigned long written = 0; + QByteArray chunkDataBuffer; + + // We need to split it up here. Someone considered it funny + // to discard any data() larger than 16MB. + // + // So nearly any Movie will just fail.... + while (written < fileSize-m_fileSize) { + unsigned long towrite = 1024*1024; // 1MB + + if (towrite > fileSize-m_fileSize-written) + towrite = fileSize-m_fileSize-written; + chunkDataBuffer.setRawData(fileData + m_fileSize + written, towrite); + processedSize(m_fileSize + written + towrite); + data(chunkDataBuffer); + chunkDataBuffer.resetRawData(fileData + m_fileSize + written, towrite); + written += towrite; + } + m_fileSize = fileSize; + setFileSize(fileSize); + } + + finished(); + gp_file_unref(m_file); /* just unref, might be stored in fs */ + m_file = NULL; +} + +// The KIO slave "stat" function. +void KameraProtocol::stat(const KURL &url) +{ + kdDebug(7123) << "stat(\"" << url.path() << "\")" << endl; + + if (url.path() == "") { + KURL rooturl(url); + + kdDebug(7123) << "redirecting to /" << endl; + rooturl.setPath("/"); + rooturl.setHost(url.host()); + rooturl.setUser(url.user()); + redirection(rooturl); + finished(); + return; + } + + if(url.path() == "/") + statRoot(); + else + statRegular(url); +} + +// Implements stat("/") -- which always returns the same value. +void KameraProtocol::statRoot(void) +{ + UDSEntry entry; + UDSAtom atom; + + atom.m_uds = UDS_NAME; + atom.m_str = "/"; + entry.append(atom); + + atom.m_uds = UDS_FILE_TYPE; + atom.m_long = S_IFDIR; + entry.append(atom); + + atom.m_uds = UDS_ACCESS; + atom.m_long = S_IRUSR | S_IRGRP | S_IROTH | + S_IWUSR | S_IWGRP | S_IWOTH; + entry.append(atom); + + statEntry(entry); + + finished(); + + // This call happens on autodetect by kdemm. So close the camera, but + // only if no more requests are pending. + idletime = MAXIDLETIME; +} + +// Implements a regular stat() of a file / directory, returning all we know about it +void KameraProtocol::statRegular(const KURL &url) +{ + UDSEntry entry; + int gpr; + + kdDebug(7123) << "statRegular(\"" << url.path() << "\")" << endl; + if (openCamera() == false) { + error(KIO::ERR_DOES_NOT_EXIST, url.path()); + return; + } + + // fprintf(stderr,"statRegular(%s)\n",url.path().latin1()); + + // Is "url" a directory? + CameraList *dirList; + gp_list_new(&dirList); + kdDebug(7123) << "statRegular() Requesting directories list for " << url.directory() << endl; + + gpr = gp_camera_folder_list_folders(m_camera, tocstr(fix_foldername(url.directory(false))), dirList, m_context); + if (gpr != GP_OK) { + if ((gpr == GP_ERROR_FILE_NOT_FOUND) || (gpr == GP_ERROR_DIRECTORY_NOT_FOUND)) + error(KIO::ERR_DOES_NOT_EXIST, url.path()); + else + error(KIO::ERR_UNKNOWN, gp_result_as_string(gpr)); + gp_list_free(dirList); + return; + } + +#define GPHOTO_TEXT_FILE(xx) \ + if (!url.path().compare("/"#xx".txt")) { \ + CameraText xx; \ + gpr = gp_camera_get_about(m_camera, &xx, m_context); \ + if (gpr != GP_OK) { \ + error(KIO::ERR_DOES_NOT_EXIST, url.fileName()); \ + return; \ + } \ + translateTextToUDS(entry,#xx".txt",xx.text); \ + statEntry(entry); \ + finished(); \ + return; \ + } + GPHOTO_TEXT_FILE(about); + GPHOTO_TEXT_FILE(manual); + GPHOTO_TEXT_FILE(summary); +#undef GPHOTO_TEXT_FILE + + const char *name; + for(int i = 0; i < gp_list_count(dirList); i++) { + gp_list_get_name(dirList, i, &name); + if (url.fileName().compare(name) == 0) { + gp_list_free(dirList); + UDSEntry entry; + translateDirectoryToUDS(entry, url.fileName()); + statEntry(entry); + finished(); + return; + } + } + gp_list_free(dirList); + + // Is "url" a file? + CameraFileInfo info; + gpr = gp_camera_file_get_info(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), &info, m_context); + if (gpr != GP_OK) { + if ((gpr == GP_ERROR_FILE_NOT_FOUND) || (gpr == GP_ERROR_DIRECTORY_NOT_FOUND)) + error(KIO::ERR_DOES_NOT_EXIST, url.path()); + else + error(KIO::ERR_UNKNOWN, gp_result_as_string(gpr)); + return; + } + translateFileToUDS(entry, info, url.fileName()); + statEntry(entry); + finished(); +} + +// The KIO slave "del" function. +void KameraProtocol::del(const KURL &url, bool isFile) +{ + kdDebug(7123) << "KameraProtocol::del(" << url.path() << ")" << endl; + + if(!openCamera()) { + error(KIO::ERR_CANNOT_DELETE, url.fileName()); + return; + } + if (!cameraSupportsDel()) { + error(KIO::ERR_CANNOT_DELETE, url.fileName()); + return; + } + if(isFile){ + CameraList *list; + gp_list_new(&list); + int ret; + + ret = gp_camera_file_delete(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), m_context); + + if(ret != GP_OK) { + error(KIO::ERR_CANNOT_DELETE, url.fileName()); + } else { + finished(); + } + } +} + +// The KIO slave "listDir" function. +void KameraProtocol::listDir(const KURL &url) +{ + kdDebug(7123) << "KameraProtocol::listDir(" << url.path() << ")" << endl; + + if (url.host().isEmpty()) { + KURL xurl; + // List the available cameras + QStringList groupList = m_config->groupList(); + kdDebug(7123) << "Found cameras: " << groupList.join(", ") << endl; + QStringList::Iterator it; + UDSEntry entry; + UDSAtom atom; + + + /* + * What we do: + * - Autodetect cameras and remember them with their ports. + * - List all saved and possible offline cameras. + * - List all autodetected and not yet printed cameras. + */ + QMap<QString,QString> ports, names; + QMap<QString,int> modelcnt; + + /* Autodetect USB cameras ... */ + GPContext *glob_context = NULL; + int i, count; + CameraList *list; + CameraAbilitiesList *al; + GPPortInfoList *il; + + gp_list_new (&list); + gp_abilities_list_new (&al); + gp_abilities_list_load (al, glob_context); + gp_port_info_list_new (&il); + gp_port_info_list_load (il); + gp_abilities_list_detect (al, il, list, glob_context); + gp_abilities_list_free (al); + gp_port_info_list_free (il); + + count = gp_list_count (list); + + for (i = 0 ; i<count ; i++) { + const char *model, *value; + + gp_list_get_name (list, i, &model); + gp_list_get_value (list, i, &value); + + ports[value] = model; + // NOTE: We might get different ports than usb: later! + if (strcmp(value, "usb:")) + names[model] = value; + + /* Save them, even though we can autodetect them for + * offline listing. + */ + m_config->setGroup(model); + m_config->writeEntry("Model",model); + m_config->writeEntry("Path",value); + modelcnt[model]++; + } + gp_list_free (list); + + /* Avoid duplicated entry for usb: and usb:001,042 entries. */ + if (ports.contains("usb:") && names[ports["usb:"]]!="usb:") + ports.remove("usb:"); + + for (it = groupList.begin(); it != groupList.end(); it++) { + QString m_cfgPath; + if (*it == "<default>") + continue; + + m_config->setGroup(*it); + m_cfgPath = m_config->readEntry("Path"); + + /* If autodetect by USB autodetect ... skip it here. + * We leave unattached USB cameras in here, because the user + * might plug them in later and does not want to press reload. + * We add them with port "usb:". + */ + if (modelcnt[*it] > 0) + continue; + + entry.clear(); + atom.m_uds = UDS_FILE_TYPE;atom.m_long = S_IFDIR;entry.append(atom); + atom.m_uds = UDS_NAME;atom.m_str = *it;entry.append(atom); + atom.m_uds = UDS_ACCESS; + atom.m_long = S_IRUSR | S_IRGRP | S_IROTH | + S_IWUSR | S_IWGRP | S_IWOTH; + entry.append(atom); + + atom.m_uds = UDS_URL; + + xurl.setProtocol("camera"); + xurl.setUser(*it); + /* Avoid setting usb:xxx,yyy. */ + if (m_cfgPath.contains("usb:")>0) { + names[*it] = "usb:"; + xurl.setHost("usb:"); + } else { + xurl.setHost(m_cfgPath); + } + xurl.setPath("/"); + atom.m_str = xurl.url(); + entry.append(atom); + + listEntry(entry, false); + } + + QMap<QString,QString>::iterator portsit; + + for (portsit = ports.begin(); portsit != ports.end(); portsit++) { + entry.clear(); + atom.m_uds = UDS_FILE_TYPE;atom.m_long = S_IFDIR; entry.append(atom); + atom.m_uds = UDS_NAME;atom.m_str = portsit.data();entry.append(atom); + + atom.m_uds = UDS_ACCESS; + atom.m_long = S_IRUSR | S_IRGRP | S_IROTH | + S_IWUSR | S_IWGRP | S_IWOTH; + entry.append(atom); + + atom.m_uds = UDS_URL; + xurl.setProtocol("camera"); + xurl.setHost(portsit.key()); + xurl.setUser(portsit.data()); + xurl.setPath("/"); + atom.m_str = xurl.url(); + entry.append(atom); + + listEntry(entry, false); + } + listEntry(entry, true); + + finished(); + return; + } + + if (url.path() == "") { + KURL rooturl(url); + + kdDebug(7123) << "redirecting to /" << endl; + rooturl.setPath("/"); + rooturl.setHost(url.host()); + rooturl.setUser(url.user()); + redirection(rooturl); + finished(); + return; + } + + if (!openCamera()) { + error(KIO::ERR_COULD_NOT_READ,url.path()); + return; + } + + CameraList *dirList; + CameraList *fileList; + CameraList *specialList; + gp_list_new(&dirList); + gp_list_new(&fileList); + gp_list_new(&specialList); + int gpr; + + if (!url.path().compare("/")) { + CameraText text; + if (GP_OK == gp_camera_get_manual(m_camera, &text, m_context)) + gp_list_append(specialList,"manual.txt",NULL); + if (GP_OK == gp_camera_get_about(m_camera, &text, m_context)) + gp_list_append(specialList,"about.txt",NULL); + if (GP_OK == gp_camera_get_summary(m_camera, &text, m_context)) + gp_list_append(specialList,"summary.txt",NULL); + } + + gpr = readCameraFolder(url.path(), dirList, fileList); + if(gpr != GP_OK) { + kdDebug(7123) << "read Camera Folder failed:" << gp_result_as_string(gpr) <<endl; + gp_list_free(dirList); + gp_list_free(fileList); + gp_list_free(specialList); + error(KIO::ERR_COULD_NOT_READ, gp_result_as_string(gpr)); + return; + } + + totalSize(gp_list_count(specialList) + gp_list_count(dirList) + gp_list_count(fileList)); + + UDSEntry entry; + const char *name; + + for(int i = 0; i < gp_list_count(dirList); ++i) { + gp_list_get_name(dirList, i, &name); + translateDirectoryToUDS(entry, QString::fromLocal8Bit(name)); + listEntry(entry, false); + } + + CameraFileInfo info; + + for(int i = 0; i < gp_list_count(fileList); ++i) { + gp_list_get_name(fileList, i, &name); + // we want to know more info about files (size, type...) + gp_camera_file_get_info(m_camera, tocstr(url.path()), name, &info, m_context); + translateFileToUDS(entry, info, QString::fromLocal8Bit(name)); + listEntry(entry, false); + } + if (!url.path().compare("/")) { + CameraText text; + if (GP_OK == gp_camera_get_manual(m_camera, &text, m_context)) { + translateTextToUDS(entry, "manual.txt", text.text); + listEntry(entry, false); + } + if (GP_OK == gp_camera_get_about(m_camera, &text, m_context)) { + translateTextToUDS(entry, "about.txt", text.text); + listEntry(entry, false); + } + if (GP_OK == gp_camera_get_summary(m_camera, &text, m_context)) { + translateTextToUDS(entry, "summary.txt", text.text); + listEntry(entry, false); + } + } + + + gp_list_free(fileList); + gp_list_free(dirList); + gp_list_free(specialList); + + listEntry(entry, true); // 'entry' is not used in this case - we only signal list completion + finished(); +} + +void KameraProtocol::setHost(const QString& host, int port, const QString& user, const QString& pass ) +{ + kdDebug(7123) << "KameraProtocol::setHost(" << host << ", " << port << ", " << user << ", " << pass << ")" << endl; + int gpr, idx; + + if (!host.isEmpty()) { + kdDebug(7123) << "model is " << user << ", port is " << host << endl; + if (m_camera) { + kdDebug(7123) << "Configuration change detected" << endl; + closeCamera(); + gp_camera_unref(m_camera); + m_camera = NULL; + infoMessage( i18n("Reinitializing camera") ); + } else { + kdDebug(7123) << "Initializing camera" << endl; + infoMessage( i18n("Initializing camera") ); + } + // fetch abilities + CameraAbilitiesList *abilities_list; + gp_abilities_list_new(&abilities_list); + gp_abilities_list_load(abilities_list, m_context); + idx = gp_abilities_list_lookup_model(abilities_list, tocstr(user)); + if (idx < 0) { + gp_abilities_list_free(abilities_list); + kdDebug(7123) << "Unable to get abilities for model: " << user << endl; + error(KIO::ERR_UNKNOWN, gp_result_as_string(idx)); + return; + } + gp_abilities_list_get_abilities(abilities_list, idx, &m_abilities); + gp_abilities_list_free(abilities_list); + + // fetch port + GPPortInfoList *port_info_list; + GPPortInfo port_info; + gp_port_info_list_new(&port_info_list); + gp_port_info_list_load(port_info_list); + idx = gp_port_info_list_lookup_path(port_info_list, tocstr(host)); + + /* Handle erronously passed usb:XXX,YYY */ + if ((idx < 0) && host.startsWith("usb:")) + idx = gp_port_info_list_lookup_path(port_info_list, "usb:"); + if (idx < 0) { + gp_port_info_list_free(port_info_list); + kdDebug(7123) << "Unable to get port info for path: " << host << endl; + error(KIO::ERR_UNKNOWN, gp_result_as_string(idx)); + return; + } + gp_port_info_list_get_info(port_info_list, idx, &port_info); + gp_port_info_list_free(port_info_list); + + // create a new camera object + gpr = gp_camera_new(&m_camera); + if(gpr != GP_OK) { + error(KIO::ERR_UNKNOWN, gp_result_as_string(gpr)); + return; + } + + // register gphoto2 callback functions + gp_context_set_status_func(m_context, frontendCameraStatus, this); + gp_context_set_progress_funcs(m_context, frontendProgressStart, frontendProgressUpdate, NULL, this); + // gp_camera_set_message_func(m_camera, ..., this) + + // set model and port + gp_camera_set_abilities(m_camera, m_abilities); + gp_camera_set_port_info(m_camera, port_info); + gp_camera_set_port_speed(m_camera, 0); // TODO: the value needs to be configurable + kdDebug(7123) << "Opening camera model " << user << " at " << host << endl; + + QString errstr; + if (!openCamera(errstr)) { + kdDebug(7123) << "Unable to init camera: " << gp_result_as_string(gpr) << endl; + error(KIO::ERR_SERVICE_NOT_AVAILABLE, errstr); + gp_camera_exit(m_camera, m_context); + return; + } + } +} + +void KameraProtocol::reparseConfiguration(void) +{ + // we have no global config, do we? +} + +// translate a simple text to a UDS entry +void KameraProtocol::translateTextToUDS(UDSEntry &udsEntry, const QString &fn, + const char *text +) { + UDSAtom atom; + + udsEntry.clear(); + + atom.m_uds = UDS_FILE_TYPE; // UDS type + atom.m_long = S_IFREG; // file + udsEntry.append(atom); + + atom.m_uds = UDS_NAME; + atom.m_str = fn; + udsEntry.append(atom); + + atom.m_uds = UDS_SIZE; + atom.m_long = strlen(text); + udsEntry.append(atom); + + atom.m_uds = UDS_ACCESS; + atom.m_long = S_IRUSR | S_IRGRP | S_IROTH; + udsEntry.append(atom); +} + +// translate a CameraFileInfo to a UDSEntry which we can return as a directory listing entry +void KameraProtocol::translateFileToUDS(UDSEntry &udsEntry, const CameraFileInfo &info, QString name) +{ + UDSAtom atom; + + udsEntry.clear(); + + atom.m_uds = UDS_FILE_TYPE; // UDS type + atom.m_long = S_IFREG; // file + udsEntry.append(atom); + + atom.m_uds = UDS_NAME; + if (info.file.fields & GP_FILE_INFO_NAME) + atom.m_str = QString::fromLocal8Bit(info.file.name); + else + atom.m_str = name; + udsEntry.append(atom); + + if (info.file.fields & GP_FILE_INFO_SIZE) { + atom.m_uds = UDS_SIZE; + atom.m_long = info.file.size; + udsEntry.append(atom); + } + + if (info.file.fields & GP_FILE_INFO_MTIME) { + atom.m_uds = UDS_MODIFICATION_TIME; + atom.m_long = info.file.mtime; + udsEntry.append(atom); + } else { + atom.m_uds = UDS_MODIFICATION_TIME; + atom.m_long = time(NULL); /* NOW */ + udsEntry.append(atom); + } + + if (info.file.fields & GP_FILE_INFO_TYPE) { + atom.m_uds = UDS_MIME_TYPE; + atom.m_str = QString::fromLatin1(info.file.type); + udsEntry.append(atom); + } + + if (info.file.fields & GP_FILE_INFO_PERMISSIONS) { + atom.m_uds = UDS_ACCESS; + atom.m_long = 0; + atom.m_long |= (info.file.permissions & GP_FILE_PERM_READ) ? (S_IRUSR | S_IRGRP | S_IROTH) : 0; + // we cannot represent individual FP_FILE_PERM_DELETE permission in the Unix access scheme + // since the parent directory's write permission defines that + udsEntry.append(atom); + } else { + // basic permissions, in case the camera doesn't provide permissions info + atom.m_uds = UDS_ACCESS; + atom.m_long = S_IRUSR | S_IRGRP | S_IROTH; + udsEntry.append(atom); + } + + // TODO: We do not handle info.preview in any way +} + +// translate a directory name to a UDSEntry which we can return as a directory listing entry +void KameraProtocol::translateDirectoryToUDS(UDSEntry &udsEntry, const QString &dirname) +{ + UDSAtom atom; + + udsEntry.clear(); + + atom.m_uds = UDS_FILE_TYPE; // UDS type + atom.m_long = S_IFDIR; // directory + udsEntry.append(atom); + + atom.m_uds = UDS_NAME; + atom.m_str = dirname; + udsEntry.append(atom); + + atom.m_uds = UDS_ACCESS; + atom.m_long = S_IRUSR | S_IRGRP | S_IROTH | + S_IWUSR | S_IWGRP | S_IWOTH; + udsEntry.append(atom); + + atom.m_uds = UDS_MIME_TYPE; + atom.m_str = "inode/directory"; + udsEntry.append(atom); +} + +bool KameraProtocol::cameraSupportsDel(void) +{ + return (m_abilities.file_operations & GP_FILE_OPERATION_DELETE); +} + +bool KameraProtocol::cameraSupportsPut(void) +{ + return (m_abilities.folder_operations & GP_FOLDER_OPERATION_PUT_FILE); +} + +bool KameraProtocol::cameraSupportsPreview(void) +{ + return (m_abilities.file_operations & GP_FILE_OPERATION_PREVIEW); +} + +int KameraProtocol::readCameraFolder(const QString &folder, CameraList *dirList, CameraList *fileList) +{ + kdDebug(7123) << "KameraProtocol::readCameraFolder(" << folder << ")" << endl; + + int gpr; + if((gpr = gp_camera_folder_list_folders(m_camera, tocstr(folder), dirList, m_context)) != GP_OK) + return gpr; + if((gpr = gp_camera_folder_list_files(m_camera, tocstr(folder), fileList, m_context)) != GP_OK) + return gpr; + return GP_OK; +} + +void frontendProgressUpdate( + GPContext * /*context*/, unsigned int /*id*/, float /*current*/, void *data +) { + KameraProtocol *object = (KameraProtocol*)data; + + // This code will get the last chunk of data retrieved from the + // camera and pass it to KIO, to allow progressive display + // of the downloaded photo. + + const char *fileData = NULL; + long unsigned int fileSize = 0; + + // This merely returns us a pointer to gphoto's internal data + // buffer -- there's no expensive memcpy + if (!object->getFile()) + return; + gp_file_get_data_and_size(object->getFile(), &fileData, &fileSize); + // make sure we're not sending zero-sized chunks (=EOF) + if (fileSize > 0) { + // XXX using assign() here causes segfault, prolly because + // gp_file_free is called before chunkData goes out of scope + QByteArray chunkDataBuffer; + chunkDataBuffer.setRawData(fileData + object->getFileSize(), fileSize - object->getFileSize()); + // Note: this will fail with sizes > 16MB ... + object->data(chunkDataBuffer); + object->processedSize(fileSize); + chunkDataBuffer.resetRawData(fileData + object->getFileSize(), fileSize - object->getFileSize()); + object->setFileSize(fileSize); + } +} + +unsigned int frontendProgressStart( + GPContext * /*context*/, float totalsize, const char *format, va_list args, + void *data +) { + KameraProtocol *object = (KameraProtocol*)data; + char *status; + + /* We must copy the va_list to walk it twice, or all hell + * breaks loose on non-i386 platforms. + */ +#if defined(HAVE_VA_COPY) || defined(HAVE___VA_COPY) + va_list xvalist; +# ifdef HAVE_VA_COPY + va_copy(xvalist, args); +# elif HAVE___VA_COPY + __va_copy(xvalist, args); +# endif + int size=vsnprintf(NULL, 0, format, xvalist); + if(size<=0) + return GP_OK; // vsnprintf is broken, better don't do anything. + + status=new char[size+1]; +# ifdef HAVE_VA_COPY + va_copy(xvalist, args); +# elif HAVE___VA_COPY + __va_copy(xvalist, args); +# endif + vsnprintf(status, size+1, format, xvalist); +#else + /* We cannot copy the va_list, so make sure we + * walk it just _once_. + */ + status=new char[300]; + vsnprintf(status, 300, format, args); +#endif + + object->infoMessage(QString::fromLocal8Bit(status)); + delete [] status; + object->totalSize((int)totalsize); // hack: call slot directly + return GP_OK; +} + +// this callback function is activated on every status message from gphoto2 +static void frontendCameraStatus(GPContext * /*context*/, const char *format, va_list args, void *data) +{ + KameraProtocol *object = (KameraProtocol*)data; + char *status; + + /* We must copy the va_list to walk it twice, or all hell + * breaks loose on non-i386 platforms. + */ +#if defined(HAVE_VA_COPY) || defined(HAVE___VA_COPY) + va_list xvalist; +# ifdef HAVE_VA_COPY + va_copy(xvalist, args); +# elif HAVE___VA_COPY + __va_copy(xvalist, args); +# endif + int size=vsnprintf(NULL, 0, format, xvalist); + if(size<=0) + return; // vsnprintf is broken, better don't do anything. + + status=new char[size+1]; +# ifdef HAVE_VA_COPY + va_copy(xvalist, args); +# elif HAVE___VA_COPY + __va_copy(xvalist, args); +# endif + vsnprintf(status, size+1, format, xvalist); +#else + /* We cannot copy the va_list, so make sure we + * walk it just _once_. + */ + status=new char[300]; + vsnprintf(status, 300, format, args); +#endif + object->infoMessage(QString::fromLocal8Bit(status)); + delete [] status; +} diff --git a/kamera/kioslave/kamera.h b/kamera/kioslave/kamera.h new file mode 100644 index 00000000..765f6560 --- /dev/null +++ b/kamera/kioslave/kamera.h @@ -0,0 +1,81 @@ +/* + + Copyright (C) 2001 The Kompany + 2001-2003 Ilya Konstantinov <kde-devel@future.shiny.co.il> + 2001-2007 Marcus Meissner <marcus@jet.franken.de> + + 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. + +*/ + +#ifndef __kamera_h__ +#define __kamera_h__ + +#include <config.h> +#include <kio/slavebase.h> +#include <gphoto2.h> + +class KSimpleConfig; + +class KameraProtocol : public KIO::SlaveBase +{ +public: + KameraProtocol(const QCString &pool, const QCString &app); + virtual ~KameraProtocol(); + + virtual void get(const KURL &url); + virtual void stat(const KURL &url); + virtual void del(const KURL &url, bool isFile); + virtual void setHost(const QString& host, int port, const QString& user, const QString& pass ); + virtual void listDir(const KURL &url); + virtual void special(const QByteArray &data); + + CameraFile *getFile() { return m_file; } + int getFileSize() { return m_fileSize; } + void setFileSize(int newfs) { m_fileSize = newfs; } + +private: + Camera *m_camera; + CameraAbilities m_abilities; + KSimpleConfig *m_config; + + GPContext *m_context; + + void reparseConfiguration(void); + bool openCamera(QString& str); + bool openCamera(void ) { + QString errstr; + return openCamera(errstr); + } + void closeCamera(void); + + void statRoot(void); + void statRegular(const KURL &url); + void translateTextToUDS(KIO::UDSEntry &udsEntry, const QString &info, const char *txt); + void translateFileToUDS(KIO::UDSEntry &udsEntry, const CameraFileInfo &info, QString name); + void translateDirectoryToUDS(KIO::UDSEntry &udsEntry, const QString &dirname); + bool cameraSupportsPreview(void); + bool cameraSupportsDel(void); + bool cameraSupportsPut(void); + int readCameraFolder(const QString &folder, CameraList *dirList, CameraList *fileList); + + QString m_lockfile; + int idletime; + + int m_fileSize; + CameraFile *m_file; + bool actiondone, cameraopen; +}; +#endif diff --git a/kamera/pics/Makefile.am b/kamera/pics/Makefile.am new file mode 100644 index 00000000..b74ac1ca --- /dev/null +++ b/kamera/pics/Makefile.am @@ -0,0 +1 @@ +KDE_ICON = camera camera_test diff --git a/kamera/pics/cr16-action-camera_test.png b/kamera/pics/cr16-action-camera_test.png Binary files differnew file mode 100644 index 00000000..543710fb --- /dev/null +++ b/kamera/pics/cr16-action-camera_test.png diff --git a/kamera/pics/cr16-app-camera.png b/kamera/pics/cr16-app-camera.png Binary files differnew file mode 100644 index 00000000..364167f0 --- /dev/null +++ b/kamera/pics/cr16-app-camera.png diff --git a/kamera/pics/cr16-device-camera.png b/kamera/pics/cr16-device-camera.png Binary files differnew file mode 100644 index 00000000..364167f0 --- /dev/null +++ b/kamera/pics/cr16-device-camera.png diff --git a/kamera/pics/cr22-device-camera.png b/kamera/pics/cr22-device-camera.png Binary files differnew file mode 100644 index 00000000..745affe7 --- /dev/null +++ b/kamera/pics/cr22-device-camera.png diff --git a/kamera/pics/cr22-filesys-camera.png b/kamera/pics/cr22-filesys-camera.png Binary files differnew file mode 100644 index 00000000..745affe7 --- /dev/null +++ b/kamera/pics/cr22-filesys-camera.png diff --git a/kamera/pics/cr32-device-camera.png b/kamera/pics/cr32-device-camera.png Binary files differnew file mode 100644 index 00000000..6876fa90 --- /dev/null +++ b/kamera/pics/cr32-device-camera.png diff --git a/kamera/pics/cr32-filesys-camera.png b/kamera/pics/cr32-filesys-camera.png Binary files differnew file mode 100644 index 00000000..6876fa90 --- /dev/null +++ b/kamera/pics/cr32-filesys-camera.png |