[kwave] /: new plugin: export to K3B project file

Thomas Eschenbacher null at kde.org
Sat Apr 22 19:15:14 UTC 2017


Git commit 4fe719ff71c31e7bb1735519788b6cdb8844740d by Thomas Eschenbacher.
Committed on 22/04/2017 at 19:14.
Pushed by eschenbacher into branch 'master'.

new plugin: export to K3B project file

M  +5    -0    AUTHORS
M  +1    -0    CHANGES
M  +1    -0    CMakeLists.txt
M  +12   -0    LICENSES
M  +239  -1    doc/en/index.docbook
M  +1    -0    plugins/CMakeLists.txt
A  +43   -0    plugins/export_k3b/CMakeLists.txt
A  +95   -0    plugins/export_k3b/K3BExportDialog.cpp     [License: GPL (v2+)]
A  +95   -0    plugins/export_k3b/K3BExportDialog.h     [License: GPL (v2+)]
A  +789  -0    plugins/export_k3b/K3BExportPlugin.cpp     [License: GPL (v2+)]
A  +179  -0    plugins/export_k3b/K3BExportPlugin.h     [License: GPL (v2+)]
A  +125  -0    plugins/export_k3b/K3BExportWidget.cpp     [License: GPL (v2+)]
A  +83   -0    plugins/export_k3b/K3BExportWidget.h     [License: GPL (v2+)]
A  +204  -0    plugins/export_k3b/K3BExportWidgetBase.ui
A  +11   -0    plugins/export_k3b/kwaveplugin_export_k3b.desktop.in
M  +3    -6    plugins/saveblocks/SaveBlocksPlugin.cpp
M  +12   -0    scripts/screenshots.kwave

https://commits.kde.org/kwave/4fe719ff71c31e7bb1735519788b6cdb8844740d

diff --git a/AUTHORS b/AUTHORS
index d113f274..588b3135 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -62,6 +62,11 @@ Sven-Steffen Arndt <ssa29 at gmx.de>
 minor contributors, copyright holders and others
 ================================================
 
+Sebastian Trueg <trueg at k3b.org>,
+Gustavo Pichorim Boiko <gustavo.boiko at kdemail.net>
+Michal Malek <michalm at jabster.pl>
+	* parts of plugins/export_k3b/K3BExportPlugin.cpp
+
 Aaron Holtzman <aholtzma at ess.engr.uvic.ca>
 	* libkwave/cpu_accel.cpp
 
diff --git a/CHANGES b/CHANGES
index d41d049a..e38d2fea 100644
--- a/CHANGES
+++ b/CHANGES
@@ -3,6 +3,7 @@ xx.xx.x [xxxx-xx-xx]
 
   * reduced flicker of position widget
   * bugfix: deleting labels per menu did not work
+  * new plugin: export to K3B project file
 
 17.04.0 [2017-03-19]
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 70b6c873..b509ab12 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -219,6 +219,7 @@ INCLUDE(GenerateExportHeader)
 INCLUDE(FeatureSummary)
 
 FIND_PACKAGE(KF5 REQUIRED COMPONENTS
+    Archive
     Completion
     Config
     ConfigWidgets
diff --git a/LICENSES b/LICENSES
index c20a07ac..56bda8a6 100644
--- a/LICENSES
+++ b/LICENSES
@@ -630,6 +630,18 @@ COMPLETE LIST OF FILES AND THEIR LICENSE
 	plugins/debug/DebugPlugin.h			GPL2+
 	plugins/debug/kwaveplugin_debug.desktop.in	GPL2+
 
+	plugins/export_k3b/CMakeLists.txt		BSD (3 clause)
+	plugins/export_k3b/K3BExportDialog.cpp		GPL2+
+	plugins/export_k3b/K3BExportDialog.h		GPL2+
+	plugins/export_k3b/K3BExportPlugin.cpp		GPL2+ / Sebastian Trueg <trueg at k3b.org>,
+	                                                        Gustavo Pichorim Boiko <gustavo.boiko at kdemail.net>
+	                                                        Michal Malek <michalm at jabster.pl>
+	plugins/export_k3b/K3BExportPlugin.h		GPL2+
+	plugins/export_k3b/K3BExportWidget.cpp		GPL2+
+	plugins/export_k3b/K3BExportWidget.h		GPL2+
+	plugins/export_k3b/K3BExportWidegetBase.ui		GPL2+
+	plugins/export_k3b/kwaveplugin_export_k3b.desktop.in	GPL2+
+
 	plugins/goto/CMakeLists.txt			BSD (3 clause)
 	plugins/goto/GotoDialog.cpp			GPL2+
 	plugins/goto/GotoDialog.h			GPL2+
diff --git a/doc/en/index.docbook b/doc/en/index.docbook
index 84f17b1d..4079b4fc 100644
--- a/doc/en/index.docbook
+++ b/doc/en/index.docbook
@@ -17,6 +17,7 @@
   <!ENTITY url_flac "http://flac.sourceforge.net/">
   <!ENTITY url_homepage "http://kwave.sourceforge.net/">
   <!ENTITY url_id3lib "http://id3lib.sourceforge.net/">
+  <!ENTITY url_k3b "http://www.k3b.org/">
   <!ENTITY url_lame "http://lame.sourceforge.net/">
   <!ENTITY url_levelmeter "https://web.archive.org/web/*/http://www.rikkus.info/esound-level-meter/">
   <!ENTITY url_libaudiofile "http://www.68k.org/~michael/audiofile/">
@@ -147,6 +148,7 @@
   <!ENTITY no-i18n-plugin_codec_ogg "codec_ogg">
   <!ENTITY no-i18n-plugin_codec_wav "codec_wav">
   <!ENTITY no-i18n-plugin_debug "debug">
+  <!ENTITY no-i18n-plugin_export_k3b "export_k3b">
   <!ENTITY no-i18n-plugin_fileinfo "fileinfo">
   <!ENTITY no-i18n-plugin_goto "goto">
   <!ENTITY no-i18n-plugin_insert_at "insert_at">
@@ -4456,6 +4458,9 @@
 	    <indexdiv><title>d</title>
 		<indexentry><primaryie><link linkend="plugin_sect_debug" endterm="plugin_title_debug"/></primaryie></indexentry>
 	    </indexdiv>
+	    <indexdiv><title>e</title>
+		<indexentry><primaryie><link linkend="plugin_sect_export_k3b" endterm="plugin_title_export_k3b"/></primaryie></indexentry>
+	    </indexdiv>
 	    <indexdiv><title>f</title>
 		<indexentry><primaryie><link linkend="plugin_sect_fileinfo" endterm="plugin_title_fileinfo"/></primaryie></indexentry>
 	    </indexdiv>
@@ -5355,6 +5360,227 @@
     </variablelist>
     </sect1>
 
+    <!-- @PLUGIN@ export_k3b -->
+    <sect1 id="plugin_sect_export_k3b"><title id="plugin_title_export_k3b">&no-i18n-plugin_export_k3b; (Export to K3B Project)</title>
+	<screenshot>
+	    <screeninfo>Screenshot</screeninfo>
+	    <mediaobject>
+		<imageobject>
+		    <imagedata fileref="kwave-plugin-export_k3b.png" format="PNG"/>
+		</imageobject>
+		<textobject>
+		    <phrase>Screenshot of the K3B Export Plugin</phrase>
+		</textobject>
+	    </mediaobject>
+	</screenshot>
+	<variablelist>
+	    <varlistentry>
+		<term><emphasis role="bold">&i18n-plugin_lbl_internal_name;</emphasis></term>
+		<listitem><para><literal>&no-i18n-plugin_export_k3b;</literal></para></listitem>
+	    </varlistentry>
+	    <varlistentry>
+		<term><emphasis role="bold">&i18n-plugin_lbl_type;</emphasis></term>
+		<listitem><para>function</para></listitem>
+	    </varlistentry>
+	    <varlistentry>
+		<term><emphasis role="bold">&i18n-plugin_lbl_description;</emphasis></term>
+		<listitem>
+		    <para>
+			Saves all sections between markers into a separate file and creates
+			a K3B project file. After having successfully written all files it
+			is possible to start <ulink url="&url_k3b;">K3B</ulink> and
+			burn the result to an audio CD.
+			This is useful for splitting a file with a recording that consists
+			of several parts, which are separated by labels, and then burn it
+			to an audio CD with multiple tracks, including CD text meta data
+			which is extracted from the descriptions of the labels.
+		    </para>
+		    <para>
+			(This plugin is internally using the
+			<link linkend="plugin_sect_saveblocks" endterm="plugin_title_saveblocks"/>
+			plugin.)
+		    </para>
+		</listitem>
+	    </varlistentry>
+	    <varlistentry>
+		<term><emphasis role="bold">&i18n-plugin_lbl_parameters;</emphasis></term>
+		<listitem>
+		    <variablelist>
+			<varlistentry>
+			    <term><replaceable>filename</replaceable></term>
+			    <listitem>
+				<para>
+				    The name of the K3B project file, will be used as
+				    base name for the exported file names.
+				</para>
+			    </listitem>
+			</varlistentry>
+
+			<varlistentry>
+			    <term><replaceable>pattern</replaceable></term>
+			    <listitem>
+				<para>
+				    A pattern that will be used for detecting
+				    title and artist from the label at the start
+				    of a section.
+				    It supports the following wildcards which
+				    will be replaced by the corresponding content
+				    when creating the CD text meta data:
+				</para>
+				<informaltable frame='all'>
+				    <tgroup cols='2'>
+					<thead>
+					    <row>
+						<entry align='left'>wildcard</entry>
+						<entry align='left'>description</entry>
+					    </row>
+					</thead>
+					<tbody>
+					    <row>
+						<entry>&no-i18n-tag;<command>[%artist]</command></entry>
+						<entry>
+						    Will be replaced with the artist that performed
+						    the corresponding block or alternatively the author.
+						</entry>
+					    </row>
+					    <row>
+						<entry>&no-i18n-tag;<command>[%title]</command></entry>
+						<entry>
+						    Will be replaced with the title of the block, which is taken
+						    from the descriptive text of the label at the
+						    <emphasis>start</emphasis> of the block.
+						    If that text is empty it will fall back to the title
+						    of the file (see file information item
+						    "<link linkend="INF_NAME">Name</link>").
+						    If this also does not exist, it will fall back to the
+						    base file name as described above.
+						</entry>
+					    </row>
+					</tbody>
+				    </tgroup>
+				</informaltable>
+				<para>
+				    Example: <quote><command><literal>[%title] ([%artist])</literal></command></quote>
+				    will detect author <quote><command><literal>Beethoven</literal></command></quote> and title
+				    <quote><command><literal>Symphony No. 5</literal></command></quote> from the string
+				    <quote><command><literal>Symphony No. 5 (Beethoven)</literal></command></quote>.
+				</para>
+			    </listitem>
+			</varlistentry>
+
+			<varlistentry>
+			    <term><replaceable>selection only</replaceable></term>
+			    <listitem>
+				<para>
+				    <informaltable frame='all'>
+					<tgroup cols='2'>
+					    <thead>
+						<row>
+						    <entry align='left'>value</entry>
+						    <entry align='left'>description</entry>
+						</row>
+					    </thead>
+					    <tbody>
+						<row>
+						    <entry>&no-i18n-tag;<command>0</command></entry>
+						    <entry>
+							Save all sections of the whole file.
+						    </entry>
+						</row>
+						<row>
+						    <entry>&no-i18n-tag;<command>1</command></entry>
+						    <entry>
+							Save only the sections that are within the
+							current selection. If nothing is selected,
+							the whole file will be saved.
+						    </entry>
+						</row>
+					    </tbody>
+					</tgroup>
+				    </informaltable>
+				</para>
+			    </listitem>
+			</varlistentry>
+
+			<varlistentry>
+			    <term><replaceable>export location</replaceable></term>
+			    <listitem>
+				<para>
+				    Determines where the blocks should be saved.
+				    <informaltable frame='all'>
+					<tgroup cols='2'>
+					    <thead>
+						<row>
+						    <entry align='left'>value</entry>
+						    <entry align='left'>description</entry>
+						</row>
+					    </thead>
+					    <tbody>
+						<row>
+						    <entry>&no-i18n-tag;<command>0</command></entry>
+						    <entry>
+							Save to the same directory as the
+							K3B project file.
+						    </entry>
+						</row>
+						<row>
+						    <entry>&no-i18n-tag;<command>1</command></entry>
+						    <entry>
+							Save into a sub directory of the directory
+							of the K3B project file, using the K3B
+							project file name as base and appending
+							<quote><command><literal>.dir</literal></command></quote>.
+						    </entry>
+						</row>
+					    </tbody>
+					</tgroup>
+				    </informaltable>
+				</para>
+			    </listitem>
+			</varlistentry>
+
+			<varlistentry>
+			    <term><replaceable>overwrite policy</replaceable></term>
+			    <listitem>
+				<para>
+				    Determines where the numbering should start.
+				    <informaltable frame='all'>
+					<tgroup cols='2'>
+					    <thead>
+						<row>
+						    <entry align='left'>value</entry>
+						    <entry align='left'>description</entry>
+						</row>
+					    </thead>
+					    <tbody>
+						<row>
+						    <entry>&no-i18n-tag;<command>0</command></entry>
+						    <entry>
+							Always start with index 1, with the risk
+							of overwriting existing files.
+						    </entry>
+						</row>
+						<row>
+						    <entry>&no-i18n-tag;<command>1</command></entry>
+						    <entry>
+							Continue after the index of the highest
+							index that already exists, this avoids
+							overwriting existing files.
+						    </entry>
+						</row>
+					    </tbody>
+					</tgroup>
+				    </informaltable>
+				</para>
+			    </listitem>
+			</varlistentry>
+
+		    </variablelist>
+		</listitem>
+	    </varlistentry>
+	</variablelist>
+    </sect1>
+
     <!-- @PLUGIN@ fileinfo -->
     <sect1 id="plugin_sect_fileinfo"><title id="plugin_title_fileinfo">&no-i18n-plugin_fileinfo; (File Info)</title>
     <screenshot>
@@ -6618,7 +6844,7 @@
 					    <row>
 						<entry>&no-i18n-tag;<command>[%title]</command></entry>
 						<entry>
-						    Will be replaced with the title the block, which is taken
+						    Will be replaced with the title of the block, which is taken
 						    from the descriptive text of the label at the
 						    <emphasis>start</emphasis> of the block.
 						    If that text is empty it will fall back to the title
@@ -7722,6 +7948,18 @@
 			plugins/record/Record-PulseAudio.h
 		</para>
 	    </listitem>
+	    <listitem>
+		<para><emphasis role="bold">
+		    Sebastian Trueg <email>trueg at k3b.org</email>,
+
+		    Gustavo Pichorim Boiko <email>gustavo.boiko at kdemail.net</email>,
+
+		    Michal Malek <email>michalm at jabster.pl</email>
+		</emphasis></para>
+		<para>
+		    parts of plugins/export_k3b/K3BExportPlugin.cpp
+		</para>
+	    </listitem>
 	</itemizedlist>
     </para>
     </sect1>
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
index e2ee8038..0a2d9de6 100644
--- a/plugins/CMakeLists.txt
+++ b/plugins/CMakeLists.txt
@@ -105,6 +105,7 @@ ADD_SUBDIRECTORY( codec_mp3 )       # needs libmad + id3lib + "lame"
 ADD_SUBDIRECTORY( codec_ogg )       # needs libogg and (libvorbis or libopus)
 ADD_SUBDIRECTORY( codec_wav )       # needs libaudiofile
 ADD_SUBDIRECTORY( debug )
+ADD_SUBDIRECTORY( export_k3b )
 ADD_SUBDIRECTORY( fileinfo )
 ADD_SUBDIRECTORY( goto )
 ADD_SUBDIRECTORY( lowpass )
diff --git a/plugins/export_k3b/CMakeLists.txt b/plugins/export_k3b/CMakeLists.txt
new file mode 100644
index 00000000..caee2064
--- /dev/null
+++ b/plugins/export_k3b/CMakeLists.txt
@@ -0,0 +1,43 @@
+#############################################################################
+##    Kwave                - plugins/export_k3b/CMakeLists.txt
+##                           -------------------
+##    begin                : Thu Mar 23 2017
+##    copyright            : (C) 2017 by Thomas Eschenbacher
+##    email                : Thomas.Eschenbacher at gmx.de
+#############################################################################
+#
+#############################################################################
+#                                                                           #
+# Redistribution and use in source and binary forms, with or without        #
+# modification, are permitted provided that the following conditions        #
+# are met:                                                                  #
+#                                                                           #
+# 1. Redistributions of source code must retain the above copyright         #
+#    notice, this list of conditions and the following disclaimer.          #
+# 2. Redistributions in binary form must reproduce the above copyright      #
+#    notice, this list of conditions and the following disclaimer in the    #
+#    documentation and/or other materials provided with the distribution.   #
+#                                                                           #
+# For details see the accompanying cmake/COPYING-CMAKE-SCRIPTS file.        #
+#                                                                           #
+#############################################################################
+
+SET(plugin_export_k3b_LIB_SRCS
+    K3BExportDialog.cpp
+    K3BExportPlugin.cpp
+    K3BExportWidget.cpp
+)
+
+SET(plugin_export_k3b_LIB_UI
+    K3BExportWidgetBase.ui
+)
+
+SET(plugin_export_k3b_LIBS
+    KF5::Archive
+)
+
+KWAVE_PLUGIN(export_k3b)
+
+#############################################################################
+#############################################################################
+
diff --git a/plugins/export_k3b/K3BExportDialog.cpp b/plugins/export_k3b/K3BExportDialog.cpp
new file mode 100644
index 00000000..47f276b5
--- /dev/null
+++ b/plugins/export_k3b/K3BExportDialog.cpp
@@ -0,0 +1,95 @@
+/***************************************************************************
+ *    K3BExportDialog.cpp  -  Extended KwaveFileDialog for exporting to K3B
+ *                             -------------------
+ *    begin                : Thu Apr 13 2017
+ *    copyright            : (C) 2017 by Thomas Eschenbacher
+ *    email                : Thomas.Eschenbacher at gmx.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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "config.h"
+
+#include <QString>
+#include <QUrl>
+
+#include <KComboBox>
+#include <KUrlComboBox>
+
+#include "libkwave/String.h"
+
+#include "K3BExportDialog.h"
+#include "K3BExportWidget.h"
+
+//***************************************************************************
+Kwave::K3BExportDialog::K3BExportDialog(
+    const QString &startDir,
+    const QString &filter,
+    QWidget *parent,
+    const QUrl &last_url,
+    const QString &last_ext,
+    QString &pattern,
+    bool selection_only,
+    bool have_selection,
+    Kwave::K3BExportPlugin::export_location_t export_location,
+    Kwave::K3BExportPlugin::overwrite_policy_t overwrite_policy
+)
+    :Kwave::FileDialog(startDir, Kwave::FileDialog::SaveFile, filter, parent,
+                       last_url, last_ext),
+     m_widget(new(std::nothrow) Kwave::K3BExportWidget(
+	 this, pattern, selection_only, have_selection,
+	 export_location, overwrite_policy
+     ))
+{
+    Q_ASSERT(m_widget);
+    setCustomWidget(m_widget);
+}
+
+//***************************************************************************
+Kwave::K3BExportDialog::~K3BExportDialog()
+{
+    if (m_widget) delete m_widget;
+    m_widget = 0;
+}
+
+// //***************************************************************************
+QString Kwave::K3BExportDialog::pattern() const
+{
+    Q_ASSERT(m_widget);
+    return (m_widget) ? m_widget->pattern() : _("");
+}
+
+//***************************************************************************
+bool Kwave::K3BExportDialog::selectionOnly() const
+{
+    Q_ASSERT(m_widget);
+    return (m_widget) ? m_widget->selectionOnly() : false;
+}
+
+//***************************************************************************
+Kwave::K3BExportPlugin::export_location_t
+    Kwave::K3BExportDialog::exportLocation() const
+{
+    Q_ASSERT(m_widget);
+    return (m_widget) ? m_widget->exportLocation() :
+                        Kwave::K3BExportPlugin::EXPORT_TO_SUB_DIR;
+}
+
+//***************************************************************************
+Kwave::K3BExportPlugin::overwrite_policy_t
+    Kwave::K3BExportDialog::overwritePolicy() const
+{
+    Q_ASSERT(m_widget);
+    return (m_widget) ? m_widget->overwritePolicy() :
+                        Kwave::K3BExportPlugin::USE_NEW_FILE_NAMES;
+}
+
+//***************************************************************************
+//***************************************************************************
diff --git a/plugins/export_k3b/K3BExportDialog.h b/plugins/export_k3b/K3BExportDialog.h
new file mode 100644
index 00000000..70805145
--- /dev/null
+++ b/plugins/export_k3b/K3BExportDialog.h
@@ -0,0 +1,95 @@
+/***************************************************************************
+ *      K3BExportDialog.h  -  Extended KwaveFileDialog for exporting to K3B
+ *                             -------------------
+ *    begin                : Thu Apr 13 2017
+ *    copyright            : (C) 2017 by Thomas Eschenbacher
+ *    email                : Thomas.Eschenbacher at gmx.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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef K3B_EXPORT_DIALOG_H
+#define K3B_EXPORT_DIALOG_H
+
+#include "config.h"
+
+#include <QObject>
+#include <QString>
+#include <QWidget>
+
+#include "K3BExportPlugin.h"
+#include "libgui/FileDialog.h"
+
+class QUrl;
+
+namespace Kwave
+{
+
+    class K3BExportWidget;
+
+    class K3BExportDialog: public Kwave::FileDialog
+    {
+	Q_OBJECT
+    public:
+
+	/**
+	 * Constructor.
+	 * @see KFileFialog
+	 * @param startDir the start directory
+	 * @param filter string with a file type filter
+	 * @param parent the parent widget
+	 * @param last_url the last used URL
+	 * @param last_ext the last used extension (preset only)
+	 * @param pattern the pattern used for detecting title and artist
+	 * @param selection_only if true, save only the selection
+	 * @param have_selection if true, there is a selection
+	 * @param export_location where to export files with tracks
+	 * @param overwrite_policy overwrite existing files or use a new name
+	 */
+	K3BExportDialog(
+	    const QString &startDir,
+	    const QString &filter,
+	    QWidget *parent,
+	    const QUrl &last_url,
+	    const QString &last_ext,
+	    QString &pattern,
+	    bool selection_only,
+	    bool have_selection,
+	    Kwave::K3BExportPlugin::export_location_t export_location,
+	    Kwave::K3BExportPlugin::overwrite_policy_t overwrite_policy
+	);
+
+	/** Destructor */
+	virtual ~K3BExportDialog();
+
+	/** returns the title/artist detection pattern (as is, not escaped) */
+	QString pattern() const;
+
+	/** returns true if only the selection should be saved */
+	bool selectionOnly() const;
+
+	/** returns export location of the files of the tracks */
+	Kwave::K3BExportPlugin::export_location_t exportLocation() const;
+
+	/** returns the file overwrite policy */
+	Kwave::K3BExportPlugin::overwrite_policy_t overwritePolicy() const;
+
+    private:
+
+	/** the widget with extra settings for K3B export */
+	Kwave::K3BExportWidget *m_widget;
+
+    };
+}
+
+#endif /* K3B_EXPORT_DIALOG_H */
+
+//***************************************************************************
+//***************************************************************************
diff --git a/plugins/export_k3b/K3BExportPlugin.cpp b/plugins/export_k3b/K3BExportPlugin.cpp
new file mode 100644
index 00000000..8e9054bb
--- /dev/null
+++ b/plugins/export_k3b/K3BExportPlugin.cpp
@@ -0,0 +1,789 @@
+/*************************************************************************
+ *     K3BExportPlugin.cpp -  export of K3B project files
+ *                             -------------------
+ *    begin                : Thu Apr 13 2017
+ *    copyright            : (C) 2017 by Thomas Eschenbacher
+ *    email                : Thomas.Eschenbacher at gmx.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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "config.h"
+
+#include <errno.h>
+
+#include <QBuffer>
+#include <QByteArray>
+#include <QDir>
+#include <QDomDocument>
+#include <QDomElement>
+#include <QFileInfo>
+#include <QIODevice>
+#include <QMap>
+#include <QProcess>
+#include <QRegExp>
+#include <QTextStream>
+
+#include <KLocalizedString> // for the i18n macro
+#include <KZip>
+
+#include "libkwave/CodecManager.h"
+#include "libkwave/Encoder.h"
+#include "libkwave/FileInfo.h"
+#include "libkwave/Label.h"
+#include "libkwave/LabelList.h"
+#include "libkwave/Logger.h"
+#include "libkwave/MessageBox.h"
+#include "libkwave/MetaDataList.h"
+#include "libkwave/Parser.h"
+#include "libkwave/Plugin.h"
+#include "libkwave/PluginManager.h"
+#include "libkwave/SignalManager.h"
+#include "libkwave/String.h"
+#include "libkwave/Utils.h"
+
+#include "K3BExportDialog.h"
+#include "K3BExportPlugin.h"
+
+KWAVE_PLUGIN(export_k3b, K3BExportPlugin)
+
+/** mime type of K3B project files */
+#define K3B_PROJECT_MIME_TYPE "application/x-k3b"
+
+/** file suffix of K3B project files */
+#define K3B_FILE_SUFFIX _("*.k3b")
+
+/** number of digits to use for out files */
+#define OUTFILE_DIGITS 4
+
+/** file name pattern for out files */
+#define OUTFILE_PATTERN (_("[%0") + _("%1nr]").arg(OUTFILE_DIGITS))
+
+/** file suffix for out files */
+#define OUTFILE_SUFFIX  _(".wav")
+
+//***************************************************************************
+Kwave::K3BExportPlugin::K3BExportPlugin(QObject *parent,
+                                        const QVariantList &args)
+    :Kwave::Plugin(parent, args),
+     m_url(),
+     m_pattern(),
+     m_selection_only(false),
+     m_export_location(EXPORT_TO_SUB_DIR),
+     m_overwrite_policy(USE_NEW_FILE_NAMES),
+     m_block_info()
+{
+}
+
+//***************************************************************************
+Kwave::K3BExportPlugin::~K3BExportPlugin()
+{
+}
+
+//***************************************************************************
+int Kwave::K3BExportPlugin::interpreteParameters(QStringList &params)
+{
+    bool ok;
+    QString param;
+
+    // evaluate the parameter list
+    if (params.count() != 5)
+	return -EINVAL;
+
+    // the selected URL
+    m_url = QUrl::fromUserInput(Kwave::Parser::unescape(params[0]));
+    if (!m_url.isValid()) return -EINVAL;
+
+    // label pattern
+    m_pattern = Kwave::Parser::unescape(params[1]);
+
+    // selection only
+    param = params[2];
+    int v = param.toInt(&ok);
+    Q_ASSERT(ok);
+    if (!ok) return -EINVAL;
+    m_selection_only = (v != 0);
+
+    // export location
+    param = params[3];
+    int where = param.toInt(&ok);
+    Q_ASSERT(ok);
+    if (!ok) return -EINVAL;
+    if ((where != EXPORT_TO_SAME_DIR) &&
+	(where != EXPORT_TO_SUB_DIR)) return -EINVAL;
+    m_export_location = static_cast<export_location_t>(where);
+
+    // overwrite policy
+    param = params[4];
+    int overwrite = param.toInt(&ok);
+    Q_ASSERT(ok);
+    if (!ok) return -EINVAL;
+    if ((overwrite != OVERWRITE_EXISTING_FILES) &&
+	(overwrite != USE_NEW_FILE_NAMES)) return -EINVAL;
+    m_overwrite_policy = static_cast<overwrite_policy_t>(overwrite);
+
+    return 0;
+}
+
+//***************************************************************************
+void Kwave::K3BExportPlugin::scanBlocksToSave(const QString &base,
+                                              sample_index_t selection_left,
+                                              sample_index_t selection_right)
+{
+    sample_index_t block_start;
+    sample_index_t block_end = 0;
+
+    Kwave::LabelList labels(signalManager().metaData());
+    Kwave::LabelListIterator it(labels);
+    Kwave::Label label = (it.hasNext()) ? it.next() : Kwave::Label();
+
+    // get the title of the whole file, in case that a block does not have
+    // an own title
+    FileInfo info(signalManager().metaData());
+    QString file_title  = info.get(INF_NAME).toString();
+    QString file_artist = info.get(INF_AUTHOR).toString();
+
+    // fallback: if there is no INF_NAME either, fall back to the file
+    //           name as last resort
+    if (!file_title.length()) file_title = base;
+
+    m_block_info.clear();
+    QString prev_title = file_title;
+    for (unsigned int index = 1; ; ++index) {
+	block_start = block_end;
+	block_end   = (label.isNull()) ? signalLength() : label.pos();
+
+	QString block_title = (!label.isNull() && label.name().length()) ?
+	    label.name() : prev_title;
+
+	if ((block_end > selection_left) && (block_start <= selection_right)) {
+	    BlockInfo block;
+
+	    // init and set reasonable defaults
+	    block.m_index    = index;
+	    block.m_filename = QString();
+	    block.m_start    = block_start;
+	    block.m_length   = block_end - block_start;
+	    block.m_title    = block_title;
+	    block.m_artist   = file_artist;
+
+	    // detect title and artist
+	    detectBlockMetaData(block_title, m_pattern, block);
+	    m_block_info.append(block);
+
+	    prev_title = block.m_title;
+
+// 	    qDebug("#%d [%llu...%llu]", index, block_start, block_end);
+// 	    qDebug("    title  = '%s'", DBG(block.m_title));
+// 	    qDebug("    artist = '%s'", DBG(block.m_artist));
+	}
+
+	if (label.isNull()) break;
+	label = (it.hasNext()) ? it.next() : Kwave::Label();
+    }
+}
+
+//***************************************************************************
+QString Kwave::K3BExportPlugin::createFileName(const QString &pattern,
+                                               unsigned int index)
+{
+    QString name = pattern;
+    QString num  = _("%1").arg(index, OUTFILE_DIGITS, 10, QLatin1Char('0'));
+    name.replace(OUTFILE_PATTERN, num);
+    name += OUTFILE_SUFFIX;
+    return name;
+}
+
+//***************************************************************************
+bool Kwave::K3BExportPlugin::detectBlockMetaData(
+    const QString &text,
+    const QString &pattern,
+    Kwave::K3BExportPlugin::BlockInfo &block
+)
+{
+    if (!pattern.length()) {
+	// auto detect -> try all known patterns
+	foreach (const QString &p, knownPatterns())
+	    if (detectBlockMetaData(text, p, block))
+		return true;
+	return false;
+    }
+
+    // list of placeholders and pointers to the resulting strings
+    QMap <QString, QString *> map_patterns;
+    map_patterns.insert(_("[%artist]"), &block.m_artist);
+    map_patterns.insert(_("[%title]"),  &block.m_title);
+
+    // try to find the placeholders within the pattern
+    // NOTE: we use a map because it will automatically be sorted (by pos)
+    QString pattern_esc = Kwave::Parser::escape(pattern);
+    QMap <int, QString *>   map_result;
+    foreach (const QString &placeholder, map_patterns.keys()) {
+	QString placeholder_esc;
+	placeholder_esc = Kwave::Parser::escape(placeholder);
+	if (pattern_esc.contains(placeholder_esc)) {
+	    const QString rx_string = _("(.+)");
+	    int pos = pattern.indexOf(placeholder);
+	    pattern_esc.replace(placeholder_esc, rx_string);
+	    map_result.insert(pos, map_patterns[placeholder]);
+	}
+    }
+    if (map_result.isEmpty())
+	return false; // no placeholders found in the patterns
+
+    // relax the pattern: turn single whitespace to one or more whitespaces
+    pattern_esc.replace(QRegExp(_("(\\\\\\s)+")), _("\\s+"));
+
+    // try to match the pattern on the given text
+    QRegExp rx(pattern_esc, Qt::CaseInsensitive);
+    if (!rx.exactMatch(text.trimmed()))
+	return false; // does not match :-(
+
+    // we found a match
+    // -> now map the results into the corresponding result strings
+    for (int index = 0; index < map_result.count(); ++index) {
+	QString value = rx.cap(index + 1).trimmed();
+	if (value.length()) {
+	    QString *result = map_result[map_result.keys()[index]];
+	    if (result) *result = value;
+	}
+    }
+
+    return true;
+}
+
+//***************************************************************************
+void Kwave::K3BExportPlugin::load(QStringList &params)
+{
+    Q_UNUSED(params);
+
+    QString menu_path = _("File/Save/%1").arg(i18nc(
+	"menu: /File/Save/Export to K3B Project...",
+	"Export to K3B Project..."
+    ));
+    emitCommand(_("menu(plugin:setup(export_k3b),%1%2)").arg(
+	menu_path).arg(_("/#group(@SIGNAL)")));
+    emitCommand(_("menu(plugin:setup(export_k3b),%1%2)").arg(
+	menu_path).arg(_("/#icon(application-x-k3b)")));
+}
+
+//***************************************************************************
+QStringList *Kwave::K3BExportPlugin::setup(QStringList &params)
+{
+    // try to interpret the previous parameters
+    interpreteParameters(params);
+
+    sample_index_t selection_left  = 0;
+    sample_index_t selection_right = 0;
+    selection(0, &selection_left, &selection_right, false);
+
+    // enable the "selection only" checkbox only if there is something
+    // selected but not everything
+    bool selected_something = (selection_left != selection_right);
+    bool selected_all = ((selection_left == 0) &&
+        (selection_right + 1 >= signalLength()));
+    bool enable_selection_only = selected_something && !selected_all;
+
+    // show a "File / Save As..." dialog for the *.k3b file
+    QPointer<Kwave::K3BExportDialog> dialog =
+	new(std::nothrow) Kwave::K3BExportDialog(
+	    _("kfiledialog:///kwave_export_k3b"),
+	    K3B_FILE_SUFFIX + _("|") + i18nc(
+		"file type filter when exporting to K3B",
+		"K3B project file (*.k3b)"
+	    ),
+	    parentWidget(),
+	    QUrl::fromUserInput(signalName()),
+	    _("*.k3b"),
+	    m_pattern,
+	    m_selection_only,
+	    enable_selection_only,
+	    m_export_location,
+	    m_overwrite_policy
+	);
+    if (!dialog) return 0;
+
+    dialog->setWindowTitle(description());
+    if (dialog->exec() != QDialog::Accepted) {
+	delete dialog;
+	return 0;
+    }
+
+    QStringList *list = new(std::nothrow) QStringList();
+    Q_ASSERT(list);
+    if (!list) {
+	delete dialog;
+	return 0;
+    }
+
+    // user has pressed "OK"
+    QUrl url = dialog->selectedUrl();
+    if (url.isEmpty()) {
+	delete dialog;
+	delete list;
+	return 0;
+    }
+
+    QString name = url.path();
+    QFileInfo path(name);
+
+    // add the correct extension if necessary
+    if (path.suffix() != K3B_FILE_SUFFIX.mid(2))
+	url.setPath(name + K3B_FILE_SUFFIX.mid(1));
+
+    name                 = Kwave::Parser::escape(url.toString());
+    QString pattern      = Kwave::Parser::escape(dialog->pattern());
+    int export_location  = static_cast<int>(dialog->exportLocation());
+    int overwrite_policy = static_cast<int>(dialog->overwritePolicy());
+    bool selection_only  = (enable_selection_only) ?
+        dialog->selectionOnly() : m_selection_only;
+
+    *list << name;                              // url
+    *list << pattern;                           // pattern
+    *list << QString::number(selection_only);   // selection only
+    *list << QString::number(export_location);  // export location
+    *list << QString::number(overwrite_policy); // overwrite policy
+
+    emitCommand(_("plugin:execute(export_k3b,") +
+	name + _(",") + pattern + _(",")  +
+	QString::number(selection_only)   + _(",") +
+	QString::number(export_location)  + _(",") +
+	QString::number(overwrite_policy) + _(")")
+    );
+
+    if (dialog) delete dialog;
+    return list;
+}
+
+//***************************************************************************
+/*
+ * taken from K3B, libk3b/projects/k3bdoc.cpp
+ *
+ * Copyright (C) 2003-2008 Sebastian Trueg <trueg at k3b.org>
+ *
+ */
+void Kwave::K3BExportPlugin::saveGeneralDocumentData(QDomElement *part)
+{
+    QDomDocument doc = part->ownerDocument();
+    QDomElement mainElem = doc.createElement(_("general"));
+
+    QDomElement propElem = doc.createElement(_("writing_mode"));
+    propElem.appendChild(doc.createTextNode(_("auto")));
+    mainElem.appendChild(propElem);
+
+    propElem = doc.createElement(_("dummy"));
+    propElem.setAttribute(_("activated"), _("no"));
+    mainElem.appendChild(propElem);
+
+    propElem = doc.createElement(_("on_the_fly"));
+    propElem.setAttribute(_("activated"), _("true"));
+    mainElem.appendChild(propElem);
+
+    propElem = doc.createElement(_("only_create_images"));
+    propElem.setAttribute(_("activated"), _("no"));
+    mainElem.appendChild(propElem);
+
+    propElem = doc.createElement(_("remove_images"));
+    propElem.setAttribute(_("activated"), _("no"));
+    mainElem.appendChild(propElem);
+
+    part->appendChild( mainElem );
+}
+
+//***************************************************************************
+/*
+ * taken from K3B, libk3b/projects/audiocd/k3baudiodoc.cpp
+ *
+ * Copyright (C) 2003-2008 Sebastian Trueg <trueg at k3b.org>
+ * Copyright (C)      2009 Gustavo Pichorim Boiko <gustavo.boiko at kdemail.net>
+ * Copyright (C)      2010 Michal Malek <michalm at jabster.pl>
+ */
+void Kwave::K3BExportPlugin::saveDocumentData(QDomElement *docElem)
+{
+    #define GET_INF(inf) doc.createTextNode(info.get(inf).toString())
+
+    const Kwave::FileInfo info(signalManager().metaData());
+
+    QDomDocument doc = docElem->ownerDocument();
+    saveGeneralDocumentData(docElem);
+
+    // add normalize
+    QDomElement normalizeElem = doc.createElement(_("normalize"));
+    normalizeElem.appendChild(doc.createTextNode(_("no")));
+    docElem->appendChild(normalizeElem);
+
+    // add hide track
+    QDomElement hideFirstTrackElem = doc.createElement(_("hide_first_track"));
+    hideFirstTrackElem.appendChild(doc.createTextNode(_("no")));
+    docElem->appendChild(hideFirstTrackElem);
+
+    // save the audio cd ripping settings
+    // paranoia mode, read retries, and ignore read errors
+    // ------------------------------------------------------------
+    QDomElement ripMain = doc.createElement(_("audio_ripping"));
+    docElem->appendChild(ripMain);
+
+    QDomElement ripElem = doc.createElement(_("paranoia_mode"));
+    ripElem.appendChild(doc.createTextNode(_("0")));
+    ripMain.appendChild(ripElem);
+
+    ripElem = doc.createElement(_("read_retries"));
+    ripElem.appendChild(doc.createTextNode(_("0")));
+    ripMain.appendChild(ripElem);
+
+    ripElem = doc.createElement(_("ignore_read_errors"));
+    ripElem.appendChild(doc.createTextNode(_("no")));
+    ripMain.appendChild(ripElem);
+    // ------------------------------------------------------------
+
+    // save disc cd-text
+    // -------------------------------------------------------------
+    QDomElement cdTextMain = doc.createElement(_("cd-text"));
+    cdTextMain.setAttribute(_("activated"), _("yes"));
+
+    QDomElement cdTextElem = doc.createElement(_("title"));
+    cdTextElem.appendChild(GET_INF(INF_NAME));
+    cdTextMain.appendChild(cdTextElem);
+
+    cdTextElem = doc.createElement(_("artist"));
+    cdTextElem.appendChild(GET_INF(INF_AUTHOR));
+    cdTextMain.appendChild(cdTextElem);
+
+    cdTextElem = doc.createElement(_("arranger"));
+    cdTextElem.appendChild(GET_INF(INF_TECHNICAN));
+    cdTextMain.appendChild(cdTextElem);
+
+    cdTextElem = doc.createElement(_("songwriter"));
+    cdTextElem.appendChild(GET_INF(INF_PERFORMER));
+    cdTextMain.appendChild(cdTextElem);
+
+    cdTextElem = doc.createElement(_("composer"));
+    cdTextElem.appendChild(GET_INF(INF_ORGANIZATION));
+    cdTextMain.appendChild(cdTextElem);
+
+    cdTextElem = doc.createElement(_("disc_id"));
+    cdTextElem.appendChild(GET_INF(INF_CD));
+    cdTextMain.appendChild(cdTextElem);
+
+    cdTextElem = doc.createElement(_("upc_ean"));
+    cdTextElem.appendChild(GET_INF(INF_ISRC));
+    cdTextMain.appendChild(cdTextElem);
+
+    cdTextElem = doc.createElement(_("message"));
+    cdTextElem.appendChild(GET_INF(INF_COMMENTS));
+    cdTextMain.appendChild(cdTextElem);
+
+    docElem->appendChild( cdTextMain );
+    // -------------------------------------------------------------
+
+    // save the tracks
+    // -------------------------------------------------------------
+    QDomElement contentsElem = doc.createElement(_("contents"));
+
+    unsigned int index = 1;
+    foreach (const Kwave::K3BExportPlugin::BlockInfo &block, m_block_info) {
+	QString title      = block.m_title;
+	QString artist     = block.m_artist;
+	QString songwriter = QString();
+	QString url        = block.m_filename;
+
+	QDomElement trackElem = doc.createElement(_("track"));
+
+	// add sources
+	QDomElement sourcesParent = doc.createElement(_("sources"));
+	QDomElement sourceElem = doc.createElement(_("file"));
+	sourceElem.setAttribute(_("url"), url);
+	sourceElem.setAttribute(_("start_offset"), _("00:00:00"));
+	sourceElem.setAttribute(_("end_offset"), _("00:00:00"));
+	sourcesParent.appendChild(sourceElem);
+	trackElem.appendChild(sourcesParent);
+
+	// index 0
+	QDomElement index0Elem = doc.createElement(_("index0"));
+	index0Elem.appendChild(doc.createTextNode(QString::number(index)));
+	trackElem.appendChild(index0Elem);
+
+	// add cd-text
+	cdTextMain = doc.createElement(_("cd-text"));
+	cdTextElem = doc.createElement(_("title"));
+	cdTextElem.appendChild(doc.createTextNode(title));
+	cdTextMain.appendChild(cdTextElem);
+
+	cdTextElem = doc.createElement(_("artist"));
+	cdTextElem.appendChild(doc.createTextNode(artist));
+	cdTextMain.appendChild(cdTextElem);
+
+	cdTextElem = doc.createElement(_("arranger"));
+	cdTextElem.appendChild(GET_INF(INF_TECHNICAN));
+	cdTextMain.appendChild(cdTextElem);
+
+	cdTextElem = doc.createElement(_("songwriter"));
+	cdTextElem.appendChild(doc.createTextNode(songwriter));
+	cdTextMain.appendChild(cdTextElem );
+
+	cdTextElem = doc.createElement(_("composer"));
+	cdTextElem.appendChild(GET_INF(INF_ORGANIZATION));
+	cdTextMain.appendChild(cdTextElem);
+
+	cdTextElem = doc.createElement(_("isrc"));
+	cdTextElem.appendChild(GET_INF(INF_ISRC));
+	cdTextMain.appendChild(cdTextElem);
+
+	cdTextElem = doc.createElement(_("message"));
+	cdTextElem.appendChild(GET_INF(INF_COMMENTS));
+	cdTextMain.appendChild(cdTextElem);
+
+	trackElem.appendChild(cdTextMain);
+
+	// add copy protection
+	QDomElement copyElem = doc.createElement(_("copy_protection"));
+	copyElem.appendChild(doc.createTextNode(
+	    info.get(INF_COPYRIGHTED).toInt() ? _("yes") : _("no")
+	));
+	trackElem.appendChild(copyElem);
+
+	// add pre emphasis
+	copyElem = doc.createElement(_("pre_emphasis"));
+	copyElem.appendChild(doc.createTextNode(_("no")));
+	trackElem.appendChild(copyElem);
+
+	contentsElem.appendChild(trackElem);
+	index++;
+    }
+    // -------------------------------------------------------------
+
+    docElem->appendChild(contentsElem);
+}
+
+//***************************************************************************
+int Kwave::K3BExportPlugin::start(QStringList &params)
+{
+    qDebug("K3BExportPlugin::start()");
+
+    // interpret the parameters
+    int result = interpreteParameters(params);
+    if (result) return result;
+
+    // check the output file
+    if (!m_url.isLocalFile())
+	return -EINVAL; // sorry, KZip supports only local files
+
+    // determine output directory and file name pattern
+    QString   k3b_filename = m_url.path();
+    QFileInfo fi(k3b_filename);
+    QString   base = fi.completeBaseName();
+    QString   out_dir;
+    QString   out_pattern;
+    if (m_export_location == Kwave::K3BExportPlugin::EXPORT_TO_SUB_DIR) {
+	// export to a subdir with the name "<filename>.dir"
+	out_dir     = fi.absolutePath() + QDir::separator() + base + _(".dir");
+	out_pattern = _("track-") + OUTFILE_PATTERN;
+    } else {
+	// use the same directory as the *.k3b file
+	out_dir     = fi.absolutePath();
+	out_pattern = base + _("-track-") + OUTFILE_PATTERN;
+    }
+    qDebug("out_dir     = '%s'", DBG(out_dir));
+    qDebug("out_pattern = '%s'", DBG(out_pattern));
+
+    // determine the selection settings
+    sample_index_t selection_left  = 0;
+    sample_index_t selection_right = 0;
+    QList<unsigned int> tracks;
+    selection(&tracks, &selection_left, &selection_right, false);
+
+    // check: only mono or stereo files are supported
+    if ((tracks.count() != 1) && (tracks.count() != 2)) {
+	qWarning("sorry, K3B can not handle %u tracks", tracks.count());
+	Kwave::MessageBox::sorry(parentWidget(), i18n(
+	    "Only mono and stereo files can be used for an audio CD. "
+	    "You can either deselect some channels or export the file "
+	    "in a different file format that supports mono and stereo "
+	    "only (for example FLAC) and then try again."
+	));
+	return -EINVAL;
+    }
+
+    bool selected_something = (selection_left != selection_right);
+    bool selected_all = ( (selection_left == 0) &&
+                          ((selection_right + 1) >= signalLength()) );
+    bool enable_selection_only = selected_something && !selected_all;
+    bool selection_only = enable_selection_only && m_selection_only;
+    if (!selection_only) {
+	selection_left  = 0;
+	selection_right = signalLength() - 1;
+    }
+
+    // create a list of blocks to save, but not yet the output file names
+    scanBlocksToSave(base, selection_left, selection_right);
+    unsigned int count = m_block_info.count();
+    if (!count)
+	return -EINVAL;
+
+    // find the start index of the file numbering
+    unsigned int first = 1;
+    if (m_overwrite_policy == Kwave::K3BExportPlugin::USE_NEW_FILE_NAMES) {
+	// use new files, find out the highest existing index
+	QString pat = out_pattern;
+	pat.replace(OUTFILE_PATTERN, _("*"));
+	pat += OUTFILE_SUFFIX;
+
+	QDir dir(out_dir, pat);
+	QStringList files;
+	files = dir.entryList();
+
+	for (unsigned int i = first; i < (first + count); ++i) {
+	    QString name = createFileName(out_pattern, i);
+	    QRegExp rx(_("^(") + name + _(")$"), Qt::CaseInsensitive);
+	    QStringList matches = files.filter(rx);
+	    if (matches.count() > 0) first = i + 1;
+	}
+	qDebug("found first usable index -> %d", first);
+    } else {
+	// overwrite mode, always start at 1
+    }
+
+    // create the complete file names
+    for (unsigned int i = 0; i < count; ++i) {
+	m_block_info[i].m_filename = out_dir + QDir::separator() +
+	    createFileName(out_pattern, first + i);
+    }
+
+    result = saveBlocks(selection_only, out_dir, out_pattern);
+    if (result != 0)
+	return result; // aborted or failed -> do not create a k3b file
+
+    result = saveK3BFile(k3b_filename);
+    if (result != 0)
+	return result; // aborted or failed -> do not ask about starting k3b
+
+    if (Kwave::MessageBox::questionYesNo(parentWidget(), i18n(
+	"A K3B project file has been created and audio files have "
+	"been exported.\n"
+	"Should I start K3B and open the audio CD project now?"
+    )) == KMessageBox::Yes) {
+	// call k3b and pass the project file name (must be full path)
+	QStringList args;
+	args << k3b_filename;
+	if (!QProcess::startDetached(_("k3b"), args)) {
+	    return -EIO;
+	}
+    }
+
+    return result;
+}
+
+//***************************************************************************
+int Kwave::K3BExportPlugin::saveBlocks(bool selection_only,
+                                       const QString &out_dir,
+                                       const QString &out_pattern)
+{
+    QString first_filename = Kwave::Parser::escapeForFileName(
+	QUrl::fromLocalFile(createFileName(out_pattern, 1)).toString());
+
+    // remember the original file info remove all unsupported/ properties,
+    // to avoid that the saveblocks plugin complains...
+    const Kwave::FileInfo orig_file_info(signalManager().metaData());
+    Kwave::FileInfo file_info(orig_file_info);
+    QList<Kwave::FileProperty> unsupported_properties;
+    {
+	QString mimetype = Kwave::CodecManager::whatContains(m_url);
+	Kwave::Encoder *encoder = Kwave::CodecManager::encoder(mimetype);
+	if (encoder) {
+	    unsupported_properties = encoder->unsupportedProperties(
+		file_info.properties().keys());
+	    delete encoder;
+	}
+	if (!unsupported_properties.isEmpty()) {
+	    foreach (const Kwave::FileProperty &p, unsupported_properties) {
+		file_info.set(p, QVariant());
+	    }
+	}
+    }
+
+    // make sure that the file uses 16 bits/sample only
+    file_info.setBits(16);
+
+    signalManager().metaData().replace(Kwave::MetaDataList(file_info));
+
+    // call the saveblocks plugin and let it do the main work of exporting
+    // the *.wav files with all the tracks...
+
+    QStringList params;
+    params << out_dir + QDir::separator() + first_filename;
+    params << Kwave::Parser::escape(out_pattern);
+    params << ((m_overwrite_policy == USE_NEW_FILE_NAMES) ? _("0") : _("1"));
+    params << (selection_only ? _("1") : _("0"));
+    int result = manager().executePlugin(_("saveblocks"), &params);
+
+    // restore the original file info
+    signalManager().metaData().replace(Kwave::MetaDataList(orig_file_info));
+
+    return result;
+}
+
+//***************************************************************************
+int Kwave::K3BExportPlugin::saveK3BFile(const QString &k3b_filename)
+{
+    // create the K3B file
+    KZip zip(k3b_filename);
+
+    bool ok = zip.open(QIODevice::WriteOnly);
+    if (!ok) return -EIO;
+
+    // write the mime type
+    QByteArray app_type(K3B_PROJECT_MIME_TYPE);
+    zip.setCompression(KZip::NoCompression);
+    zip.setExtraField(KZip::NoExtraField);
+    zip.writeFile(_("mimetype"), app_type);
+
+    // export file global data
+    QByteArray xml;
+    QBuffer out(&xml);
+    out.open(QIODevice::WriteOnly);
+
+    // save the data in the document
+    QDomDocument xmlDoc(_("k3b_audio_project"));
+
+    xmlDoc.appendChild(xmlDoc.createProcessingInstruction(
+	_("xml"), _("version=\"1.0\" encoding=\"UTF-8\"")
+    ));
+    QDomElement docElem = xmlDoc.createElement(_("k3b_audio_project"));
+    xmlDoc.appendChild(docElem);
+    saveDocumentData(&docElem);
+    QTextStream xmlStream(&out);
+    xmlDoc.save(xmlStream, 0);
+
+    out.close();
+
+    zip.setCompression(KZip::NoCompression);
+    zip.setExtraField(KZip::NoExtraField);
+    zip.writeFile(_("maindata.xml"), xml.data());
+    zip.close();
+
+    return 0;
+}
+
+//***************************************************************************
+QStringList Kwave::K3BExportPlugin::knownPatterns()
+{
+    // list of all known detection patterns
+    QStringList patterns;
+    patterns << _("[%title] ([%artist])");
+    patterns << _("[%title], [%artist]");
+    patterns << _("[%artist]: [%title]");
+    patterns << _("[%artist] - [%title]");
+    return patterns;
+}
+
+//***************************************************************************
+#include "K3BExportPlugin.moc"
+//***************************************************************************
+//***************************************************************************
diff --git a/plugins/export_k3b/K3BExportPlugin.h b/plugins/export_k3b/K3BExportPlugin.h
new file mode 100644
index 00000000..12137e82
--- /dev/null
+++ b/plugins/export_k3b/K3BExportPlugin.h
@@ -0,0 +1,179 @@
+/*************************************************************************
+ *      K3BExportPlugin.h  -  export of K3B project files
+ *                             -------------------
+ *    begin                : Thu Apr 13 2017
+ *    copyright            : (C) 2017 by Thomas Eschenbacher
+ *    email                : Thomas.Eschenbacher at gmx.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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef K3B_EXPORT_PLUGIN_H
+#define K3B_EXPORT_PLUGIN_H
+
+#include "config.h"
+
+#include <QObject>
+#include <QStringList>
+#include <QUrl>
+#include <QVariantList>
+#include <QVector>
+
+#include "libkwave/Plugin.h"
+
+class QDomElement;
+
+namespace Kwave
+{
+
+    class K3BExportPlugin: public Kwave::Plugin
+    {
+	Q_OBJECT
+    public:
+
+	typedef enum {
+	    EXPORT_TO_SAME_DIR = 0,
+	    EXPORT_TO_SUB_DIR  = 1
+	} export_location_t ;
+
+	typedef enum {
+	    OVERWRITE_EXISTING_FILES = 0,
+	    USE_NEW_FILE_NAMES       = 1
+	} overwrite_policy_t;
+
+	/**
+	 * Constructor
+	 * @param parent reference to our plugin manager
+	 * @param args argument list [unused]
+	 */
+	K3BExportPlugin(QObject *parent, const QVariantList &args);
+
+	/** Destructor */
+	virtual ~K3BExportPlugin();
+
+	/** @see Kwave::Plugin::load() */
+	virtual void load(QStringList &params);
+
+	/**
+	 * Normally this method is used to set up all necessary parameters
+	 * for executing the plugin. This plugin uses it for performing
+	 * actions in the context of the GUI thread.
+	 *
+	 * @param params some parameters
+	 * @return string list with parameters or null pointer
+	 */
+	virtual QStringList *setup(QStringList &params);
+
+	/**
+	 * Saves the K3B project file, using the settings made in "setup()"
+	 * @see Kwave::Plugin::start()
+	 */
+	virtual int start(QStringList &params);
+
+	/** returns a list of all known detection patterns */
+	static QStringList knownPatterns();
+
+    protected:
+
+	typedef struct {
+	    unsigned int   m_index;    /**< track index [1...N]           */
+	    QString        m_filename; /**< file name for saving          */
+	    sample_index_t m_start;    /**< start of the block [samples]  */
+	    sample_index_t m_length;   /**< length of the block [samples] */
+	    QString        m_title;    /**< title of the block            */
+	    QString        m_artist;   /**< artist of the song            */
+	} BlockInfo;
+
+	/** reads values from the parameter list */
+	int interpreteParameters(QStringList &params);
+
+	/**
+	 * determines the blocks which should be saved, including
+	 * start position, length and title.
+	 * @param base the base name, without indices, extension etc...
+	 * @param selection_left index of the first sample
+	 * @param selection_right index of the last sample
+	 */
+	void scanBlocksToSave(const QString &base,
+	                      sample_index_t selection_left,
+	                      sample_index_t selection_right);
+
+	/**
+	 * create a filename (with extension) out of a given name pattern
+	 * and index
+	 * @param pattern the pattern for creating the filename
+	 * @param index the index of the current file
+	 * @return the name of the file
+	 */
+	QString createFileName(const QString &pattern, unsigned int index);
+
+	/**
+	 * detects the meta data of a block from splitting the description
+	 * text of a label
+	 * @param text the description of a label
+	 * @param pattern a pattern describing the format of the text
+	 * @param block a BlockInfo structure that receives artist and title
+	 * @return true if the pattern did match, false otherwise
+	 */
+	bool detectBlockMetaData(const QString &text,
+	                         const QString &pattern,
+	                         BlockInfo &block);
+
+	/** save the "general" section */
+	void saveGeneralDocumentData(QDomElement *part);
+
+	/** save the K3B project document data */
+	void saveDocumentData(QDomElement *docElem);
+
+	/**
+	 * save the blocks through the saveblocks plugin
+	 * @param selection_only if true, save only the selection
+	 * @param out_dir output directory for saving the blocks
+	 * @param out_pattern the pattern for creating the block filenames
+	 * @return zero if succeeded, or error code if failed
+	 */
+	int saveBlocks(bool selection_only,
+	               const QString &out_dir,
+	               const QString &out_pattern);
+
+	/**
+	 * save the *.k3b file
+	 * @param k3b_filename path to the *.k3b file
+	 * @return zero if succeeded, or error code if failed
+	 */
+	int saveK3BFile(const QString &k3b_filename);
+
+    private:
+
+	/** the URL of the project file */
+	QUrl m_url;
+
+	/** pattern for detecting title and artist */
+	QString m_pattern;
+
+	/** if true, export only the selected range */
+	bool m_selection_only;
+
+	/** where to export the files of the tracks, subdir, same dir, ... */
+	export_location_t m_export_location;
+
+	/** overwrite existing files or create a new file name */
+	overwrite_policy_t m_overwrite_policy;
+
+	/** list of all blocks to save */
+	QVector<BlockInfo> m_block_info;
+    };
+}
+
+#endif /* K3B_EXPORT_PLUGIN_H */
+
+//***************************************************************************
+//***************************************************************************
+
diff --git a/plugins/export_k3b/K3BExportWidget.cpp b/plugins/export_k3b/K3BExportWidget.cpp
new file mode 100644
index 00000000..d2aae300
--- /dev/null
+++ b/plugins/export_k3b/K3BExportWidget.cpp
@@ -0,0 +1,125 @@
+/***************************************************************************
+ * K3BExportWidget.cpp -  widget for K3B export options in the file open dlg
+ *                             -------------------
+ *    begin                : Thu Apr 13 2017
+ *    copyright            : (C) 2017 by Thomas Eschenbacher
+ *    email                : Thomas.Eschenbacher at gmx.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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "config.h"
+
+#include <QCheckBox>
+#include <QLineEdit>
+
+#include <KComboBox>
+
+#include "libkwave/String.h"
+
+#include "K3BExportWidget.h"
+
+//***************************************************************************
+Kwave::K3BExportWidget::K3BExportWidget(
+    QWidget *parent,
+    QString &pattern,
+    bool selection_only,
+    bool have_selection,
+    Kwave::K3BExportPlugin::export_location_t export_location,
+    Kwave::K3BExportPlugin::overwrite_policy_t overwrite_policy
+)
+    :QWidget(parent), Ui::K3BExportWidgetBase()
+{
+    setupUi(this);
+
+    cbLabelPattern->addItem(i18nc(
+	"default entry of the list of placeholder patterns in "
+	"the K3B export plugin (used for detecting title and artist "
+	"from a label description)",
+	"(auto detect)"
+    ));
+    foreach (const QString &p, Kwave::K3BExportPlugin::knownPatterns())
+	cbLabelPattern->addItem(p);
+
+    Q_ASSERT(cbLabelPattern);
+    if (pattern.trimmed().length())
+	cbLabelPattern->setCurrentText(pattern.trimmed());
+    else
+	cbLabelPattern->setCurrentIndex(0);
+
+    // the "selection only" checkbox
+    Q_ASSERT(chkSelectionOnly);
+    if (have_selection) {
+	// we have a selection
+	chkSelectionOnly->setEnabled(true);
+	chkSelectionOnly->setChecked(selection_only);
+    } else {
+	// no selection -> force it to "off"
+	chkSelectionOnly->setEnabled(false);
+	chkSelectionOnly->setChecked(false);
+    }
+
+    Q_ASSERT(cbExportLocation);
+    cbExportLocation->setCurrentIndex(static_cast<int>(export_location));
+
+    Q_ASSERT(cbOverwritePolicy);
+    cbOverwritePolicy->setCurrentIndex(static_cast<int>(overwrite_policy));
+}
+
+//***************************************************************************
+Kwave::K3BExportWidget::~K3BExportWidget()
+{
+}
+
+//***************************************************************************
+QString Kwave::K3BExportWidget::pattern() const
+{
+    Q_ASSERT(cbLabelPattern);
+    if (!cbLabelPattern) return QString();
+
+    // special handling: the first entry in the list is the default pattern
+    // (which is "auto-detect") -> map this to empty pattern
+    QString p = cbLabelPattern->currentText().trimmed();
+    if (p == cbLabelPattern->itemText(0)) return QString();
+
+    return p;
+}
+
+//***************************************************************************
+bool Kwave::K3BExportWidget::selectionOnly() const
+{
+    Q_ASSERT(chkSelectionOnly);
+    return (chkSelectionOnly) ? chkSelectionOnly->isChecked() : false;
+}
+
+//***************************************************************************
+Kwave::K3BExportPlugin::export_location_t
+    Kwave::K3BExportWidget::exportLocation() const
+{
+    Q_ASSERT(cbExportLocation);
+    return static_cast<Kwave::K3BExportPlugin::export_location_t>(
+	(cbExportLocation) ?
+	cbExportLocation->currentIndex() : 0
+    );
+}
+
+//***************************************************************************
+Kwave::K3BExportPlugin::overwrite_policy_t
+    Kwave::K3BExportWidget::overwritePolicy() const
+{
+    Q_ASSERT(cbOverwritePolicy);
+    return static_cast<Kwave::K3BExportPlugin::overwrite_policy_t>(
+	(cbOverwritePolicy) ?
+	cbOverwritePolicy->currentIndex() : 0
+    );
+}
+
+//***************************************************************************
+//***************************************************************************
diff --git a/plugins/export_k3b/K3BExportWidget.h b/plugins/export_k3b/K3BExportWidget.h
new file mode 100644
index 00000000..5495e89b
--- /dev/null
+++ b/plugins/export_k3b/K3BExportWidget.h
@@ -0,0 +1,83 @@
+/***************************************************************************
+ * K3BExportWidget.h  -  widget for K3B export options in the file open dlg
+ *                             -------------------
+ *    begin                : Thu Apr 13 2017
+ *    copyright            : (C) 2017 by Thomas Eschenbacher
+ *    email                : Thomas.Eschenbacher at gmx.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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef K3B_EXPORT_WIDGET_H
+#define K3B_EXPORT_WIDGET_H
+
+#include <QWidget>
+
+#include "K3BExportPlugin.h"
+#include "ui_K3BExportWidgetBase.h"
+
+namespace Kwave
+{
+    class K3BExportWidget: public QWidget, public Ui::K3BExportWidgetBase
+    {
+	Q_OBJECT
+    public:
+
+	/**
+	 * Constructor
+	 * @param widget pointer to the parent widget
+	 * @param pattern the pattern used for detecting title and artist
+	 * @param selection_only if true, save only the selection
+	 * @param have_selection if true, there is a selection
+	 * @param export_location where to export files with tracks
+	 * @param overwrite_policy overwrite existing files or use a new name
+	 */
+	K3BExportWidget(
+	    QWidget *widget,
+	    QString &pattern,
+	    bool selection_only,
+	    bool have_selection,
+	    Kwave::K3BExportPlugin::export_location_t export_location,
+	    Kwave::K3BExportPlugin::overwrite_policy_t overwrite_policy
+	);
+
+	/** Destructor */
+	virtual ~K3BExportWidget();
+
+	/** @see KPreviewWidgetBase::showPreview() */
+	virtual void showPreview(const QUrl &url)
+	{
+	    Q_UNUSED(url);
+	}
+
+	/** @see KPreviewWidgetBase::clearPreview */
+	virtual void clearPreview()
+	{
+	}
+
+	/** returns the title/artist detection pattern (as is, not escaped) */
+	QString pattern() const;
+
+	/** returns true if only the selection should be saved */
+	bool selectionOnly() const;
+
+	/** returns export location of the files of the tracks */
+	Kwave::K3BExportPlugin::export_location_t exportLocation() const;
+
+	/** returns the file overwrite policy */
+	Kwave::K3BExportPlugin::overwrite_policy_t overwritePolicy() const;
+
+    };
+}
+
+#endif /* K3B_EXPORT_WIDGET_H */
+
+//***************************************************************************
+//***************************************************************************
diff --git a/plugins/export_k3b/K3BExportWidgetBase.ui b/plugins/export_k3b/K3BExportWidgetBase.ui
new file mode 100644
index 00000000..5569473f
--- /dev/null
+++ b/plugins/export_k3b/K3BExportWidgetBase.ui
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>K3BExportWidgetBase</class>
+ <widget class="QWidget" name="K3BExportWidgetBase">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>410</width>
+    <height>219</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="spacing">
+    <number>20</number>
+   </property>
+   <property name="margin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="QGroupBox" name="grpInputSettings">
+     <property name="title">
+      <string>Input Settings</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <property name="margin">
+       <number>10</number>
+      </property>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QLabel" name="lblLabelFormat">
+          <property name="text">
+           <string>Format of Labels:</string>
+          </property>
+          <property name="wordWrap">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="KComboBox" name="cbLabelPattern">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="whatsThis">
+           <string><html><head/><body><p>Select a pattern for detecting title and artist from the text of a label.<br/>It currently understands the following placeholders:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier'; font-weight:600;">[%title] </span>title of the song</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier'; font-weight:600;">[%artist]</span> name of the artist</li></ul></body></html></string>
+          </property>
+          <property name="editable">
+           <bool>true</bool>
+          </property>
+          <property name="duplicatesEnabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="chkSelectionOnly">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="whatsThis">
+         <string>If checked, <b>save only the blocks that overlap with the current selection</b>, otherwise the whole file. <br><i>Please note that this option is disabled if nothing is selected or the selection already covers the whole file.</i></string>
+        </property>
+        <property name="text">
+         <string>Use Selection only</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="grpOutputSettings">
+     <property name="title">
+      <string>Output Settings</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <property name="leftMargin">
+       <number>10</number>
+      </property>
+      <property name="topMargin">
+       <number>10</number>
+      </property>
+      <property name="bottomMargin">
+       <number>10</number>
+      </property>
+      <item row="0" column="0">
+       <widget class="QLabel" name="lblExportLocation">
+        <property name="text">
+         <string>Export Location:</string>
+        </property>
+        <property name="wordWrap">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="KComboBox" name="cbExportLocation">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="whatsThis">
+         <string>Specify where to place the exported sound files which are used for the tracks of the audio CD.</string>
+        </property>
+        <property name="duplicatesEnabled">
+         <bool>false</bool>
+        </property>
+        <item>
+         <property name="text">
+          <string>Same Directory as K3B File</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Sub Directory</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="lblOverwritePolicy">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string>Overwrite Policy:</string>
+        </property>
+        <property name="wordWrap">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="KComboBox" name="cbOverwritePolicy">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="whatsThis">
+         <string>Select whether existing files are overwritten or new file names are used when exporting the files to use as tracks of the audio CD.</string>
+        </property>
+        <property name="duplicatesEnabled">
+         <bool>false</bool>
+        </property>
+        <item>
+         <property name="text">
+          <string>Overwrite Existing Files</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Use new File Names</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="10" margin="10"/>
+ <customwidgets>
+  <customwidget>
+   <class>KComboBox</class>
+   <extends>QComboBox</extends>
+   <header>kcombobox.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>cbLabelPattern</tabstop>
+  <tabstop>chkSelectionOnly</tabstop>
+  <tabstop>cbExportLocation</tabstop>
+  <tabstop>cbOverwritePolicy</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/plugins/export_k3b/kwaveplugin_export_k3b.desktop.in b/plugins/export_k3b/kwaveplugin_export_k3b.desktop.in
new file mode 100644
index 00000000..cfe7c2c6
--- /dev/null
+++ b/plugins/export_k3b/kwaveplugin_export_k3b.desktop.in
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=K3B Project Export
+Name[x-test]=xxK3B Project Exportxx
+Type=Service
+ServiceTypes=Kwave/Plugin
+X-KDE-PluginInfo-Author=Thomas Eschenbacher
+X-KDE-PluginInfo-Name=export_k3b
+X-KDE-PluginInfo-Version=@KWAVE_VERSION@:2.3
+X-KDE-PluginInfo-License=GPL-2.0+
+X-KDE-PluginInfo-EnabledByDefault=true
diff --git a/plugins/saveblocks/SaveBlocksPlugin.cpp b/plugins/saveblocks/SaveBlocksPlugin.cpp
index 075e58e1..c2f76808 100644
--- a/plugins/saveblocks/SaveBlocksPlugin.cpp
+++ b/plugins/saveblocks/SaveBlocksPlugin.cpp
@@ -202,14 +202,11 @@ int Kwave::SaveBlocksPlugin::start(QStringList &params)
     selection(0, &selection_left, &selection_right, false);
 
     bool selected_something = (selection_left != selection_right);
-    bool selected_all = ((selection_left == 0) &&
-                         (selection_right+1 >= signalLength()));
+    bool selected_all = ( (selection_left == 0) &&
+                          ((selection_right + 1) >= signalLength()) );
     bool enable_selection_only = selected_something && !selected_all;
     bool selection_only = enable_selection_only && m_selection_only;
-
-    if (selection_only) {
-	selection(0, &selection_left, &selection_right, true);
-    } else {
+    if (!selection_only) {
 	selection_left  = 0;
 	selection_right = signalLength() - 1;
     }
diff --git a/scripts/screenshots.kwave b/scripts/screenshots.kwave
index 0de3ff83..5fcfab99 100644
--- a/scripts/screenshots.kwave
+++ b/scripts/screenshots.kwave
@@ -173,6 +173,18 @@ plugin-codec_mp3:
     sync()
 
 #
+# screenshot of the K3B export setup dialog
+#
+plugin-export_k3b:
+    selectall()
+    select_track:off(1)
+    delayed(500,window:screenshot(Kwave::K3BExportDialog, /var/tmp/screenshots/${LANG}/kwave-plugin-export_k3b.png))
+    delayed(100,window:close(Kwave::K3BExportDialog))
+    plugin:setup(export_k3b)
+    sync()
+    select_track:on(1)
+
+#
 # screenshot of the File Info dialog
 #
 plugin-fileinfo:


More information about the kde-doc-english mailing list