[Amarok] 9cd5d39 APG: new PlaylistLength constraint

Soren Harward stharward at gmail.com
Fri Sep 17 18:44:58 UTC 2010


	A	 src/playlistgenerator/constraints/PlaylistDurationEditWidget.ui	 [License: Trivialfile.]


	A	 src/playlistgenerator/constraints/PlaylistDuration.h	 [License: UNKNOWN]


	A	 src/playlistgenerator/constraints/PlaylistDuration.cpp	 [License: UNKNOWN]

commit 9cd5d397a7ea6ef93a470e0081885b612b33e26b
Author: Soren Harward <stharward at gmail.com>
Date:   Mon Aug 16 21:22:41 2010 -0400

    APG: new PlaylistLength constraint
    
    A new constraint for the APG whereby you can specify the total number of
    tracks in the generated playlist.  Complements the existing "Playlist
    Duration" constraint, which specifies the total play time of all tracks
    in the playlist.
    
    CCMAIL: amarok at kde.org

diff --git a/ChangeLog b/ChangeLog
index edd0bfb..aa8e89a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -4,6 +4,9 @@ Amarok ChangeLog
 
 VERSION NEXT
   FEATURES:
+    * New "Playlist Length" constraint for the APG, which allows you to specify
+      the number of tracks in the playlist.  What was the "Playlist Length"
+      constraint is now called "Playlist Duration".
 
   CHANGES:
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ea5d155..d5fe1b1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -529,6 +529,7 @@ set(apg_SRCS
     playlistgenerator/constraints/Checkpoint.cpp
     playlistgenerator/constraints/CheckpointSupport.cpp
     playlistgenerator/constraints/Matching.cpp
+    playlistgenerator/constraints/PlaylistDuration.cpp
     playlistgenerator/constraints/PlaylistLength.cpp
     playlistgenerator/constraints/PreventDuplicates.cpp
     playlistgenerator/constraints/TagMatch.cpp
@@ -540,6 +541,7 @@ kde4_add_ui_files(apg_SRCS
     playlistgenerator/ConstraintGroupEditWidget.ui
     playlistgenerator/PresetEditDialog.ui
     playlistgenerator/constraints/CheckpointEditWidget.ui
+    playlistgenerator/constraints/PlaylistDurationEditWidget.ui
     playlistgenerator/constraints/PlaylistLengthEditWidget.ui
     playlistgenerator/constraints/PreventDuplicatesEditWidget.ui
     playlistgenerator/constraints/TagMatchEditWidget.ui
diff --git a/src/playlistgenerator/ConstraintFactory.cpp b/src/playlistgenerator/ConstraintFactory.cpp
index 7739237..d8af294 100644
--- a/src/playlistgenerator/ConstraintFactory.cpp
+++ b/src/playlistgenerator/ConstraintFactory.cpp
@@ -21,6 +21,7 @@
 #include "ConstraintGroup.h"
 #include "ConstraintNode.h"
 #include "constraints/Checkpoint.h"
+#include "constraints/PlaylistDuration.h"
 #include "constraints/PlaylistLength.h"
 #include "constraints/PreventDuplicates.h"
 #include "constraints/TagMatch.h"
@@ -78,16 +79,21 @@ ConstraintFactory::ConstraintFactory()
     m_registryNames[r->m_name] = r;
     m_registryUntranslateNames[r->m_i18nName] = r->m_name;
 
-    r = ConstraintTypes::PlaylistLength::registerMe();
+    r = ConstraintTypes::PlaylistDuration::registerMe();
     m_registryIds[1] = r;
     m_registryNames[r->m_name] = r;
     m_registryUntranslateNames[r->m_i18nName] = r->m_name;
 
-    r = ConstraintTypes::PreventDuplicates::registerMe();
+    r = ConstraintTypes::PlaylistLength::registerMe();
     m_registryIds[2] = r;
     m_registryNames[r->m_name] = r;
     m_registryUntranslateNames[r->m_i18nName] = r->m_name;
 
+    r = ConstraintTypes::PreventDuplicates::registerMe();
+    m_registryIds[3] = r;
+    m_registryNames[r->m_name] = r;
+    m_registryUntranslateNames[r->m_i18nName] = r->m_name;
+
     r = ConstraintTypes::Checkpoint::registerMe();
     m_registryIds[4] = r;
     m_registryNames[r->m_name] = r;
diff --git a/src/playlistgenerator/ConstraintNode.h b/src/playlistgenerator/ConstraintNode.h
index 0a9b6ec..185a59d 100644
--- a/src/playlistgenerator/ConstraintNode.h
+++ b/src/playlistgenerator/ConstraintNode.h
@@ -42,7 +42,7 @@ class QWidget;
  *                                          |
  *          +-----------------+-------------+-----+--------  ... etc
  *          |                 |                   |
- *      Matching     PreventDuplicates     PlaylistLength
+ *      Matching     PreventDuplicates     PlaylistDuration
  *          |
  *      TagMatch
  *
diff --git a/src/playlistgenerator/PresetModel.cpp b/src/playlistgenerator/PresetModel.cpp
index 3d1a04d..9b74636 100644
--- a/src/playlistgenerator/PresetModel.cpp
+++ b/src/playlistgenerator/PresetModel.cpp
@@ -323,7 +323,7 @@ const QString APG::PresetModel::presetExamples =
 "  <generatorpreset title=\"%3\">"
 "    <constrainttree>"
 "      <group matchtype=\"all\">"
-"        <constraint comparison=\"1\" length=\"3600000\" type=\"PlaylistLength\" strictness=\"0.3\"/>"
+"        <constraint comparison=\"1\" length=\"3600000\" type=\"PlaylistDuration\" strictness=\"0.3\"/>"
 "        <constraint field=\"2\" type=\"PreventDuplicates\"/>"
 "      </group>"
 "    </constrainttree>"
@@ -334,7 +334,7 @@ const QString APG::PresetModel::presetExamples =
 "        <constraint field=\"0\" type=\"PreventDuplicates\"/>"
 "        <constraint field=\"last played\" comparison=\"3\" invert=\"true\" type=\"TagMatch\" value=\"7 days\" strictness=\"0.4\"/>"
 "        <constraint field=\"rating\" comparison=\"2\" invert=\"false\" type=\"TagMatch\" value=\"6\" strictness=\"1\"/>"
-"        <constraint comparison=\"1\" length=\"10800000\" type=\"PlaylistLength\" strictness=\"0.3\"/>"
+"        <constraint comparison=\"1\" length=\"10800000\" type=\"PlaylistDuration\" strictness=\"0.3\"/>"
 "      </group>"
 "    </constrainttree>"
 "  </generatorpreset>"
@@ -347,8 +347,8 @@ const QString APG::PresetModel::presetExamples =
 "          <constraint field=\"genre\" comparison=\"3\" invert=\"false\" type=\"TagMatch\" value=\"Industrial\" strictness=\"1\"/>"
 "        </group>"
 "        <group matchtype=\"all\">"
-"          <constraint comparison=\"2\" length=\"4500000\" type=\"PlaylistLength\" strictness=\"0.4\"/>"
-"          <constraint comparison=\"0\" length=\"4800000\" type=\"PlaylistLength\" strictness=\"1\"/>"
+"          <constraint comparison=\"2\" length=\"4500000\" type=\"PlaylistDuration\" strictness=\"0.4\"/>"
+"          <constraint comparison=\"0\" length=\"4800000\" type=\"PlaylistDuration\" strictness=\"1\"/>"
 "        </group>"
 "      </group>"
 "    </constrainttree>"
diff --git a/src/playlistgenerator/TODO b/src/playlistgenerator/TODO
index 06f503c..0c56154 100644
--- a/src/playlistgenerator/TODO
+++ b/src/playlistgenerator/TODO
@@ -7,7 +7,6 @@ Backend:
 Constraints:
 - fix preventduplicates: the delta functions are kinda broken
 - last.fm or echonest similar artists constraint
-- playlist length (ie, # of tracks) constraint
 
 GUI:
 - context menu for APGCategory
\ No newline at end of file
diff --git a/src/playlistgenerator/constraints/PlaylistDuration.cpp b/src/playlistgenerator/constraints/PlaylistDuration.cpp
new file mode 100644
index 0000000..5d83dfe
--- /dev/null
+++ b/src/playlistgenerator/constraints/PlaylistDuration.cpp
@@ -0,0 +1,338 @@
+/****************************************************************************************
+ * Copyright (c) 2008-2010 Soren Harward <stharward at gmail.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, see <http://www.gnu.org/licenses/>.                           *
+ ****************************************************************************************/
+
+#define DEBUG_PREFIX "Constraint::PlaylistDuration"
+
+#include "PlaylistDuration.h"
+
+#include "playlistgenerator/Constraint.h"
+#include "playlistgenerator/ConstraintFactory.h"
+
+#include "core/collections/QueryMaker.h"
+#include "core/support/Debug.h"
+
+#include <KRandom>
+
+#include <QtGlobal>
+
+#include <stdlib.h>
+#include <math.h>
+
+Constraint*
+ConstraintTypes::PlaylistDuration::createFromXml( QDomElement& xmlelem, ConstraintNode* p )
+{
+    if ( p ) {
+        return new PlaylistDuration( xmlelem, p );
+    } else {
+        return 0;
+    }
+}
+
+Constraint*
+ConstraintTypes::PlaylistDuration::createNew( ConstraintNode* p )
+{
+    if ( p ) {
+        return new PlaylistDuration( p );
+    } else {
+        return 0;
+    }
+}
+
+ConstraintFactoryEntry*
+ConstraintTypes::PlaylistDuration::registerMe()
+{
+    return new ConstraintFactoryEntry( "PlaylistDuration",
+                                       i18n("Playlist Duration"),
+                                       i18n("Sets the preferred duration of the playlist"),
+                                       &PlaylistDuration::createFromXml, &PlaylistDuration::createNew );
+}
+
+ConstraintTypes::PlaylistDuration::PlaylistDuration( QDomElement& xmlelem, ConstraintNode* p )
+        : Constraint( p )
+{
+    DEBUG_BLOCK
+    QDomAttr a;
+
+    a = xmlelem.attributeNode( "duration" );
+    if ( !a.isNull() )
+        m_duration = a.value().toInt();
+
+    a = xmlelem.attributeNode( "comparison" );
+    if ( !a.isNull() )
+        m_comparison = a.value().toInt();
+
+    a = xmlelem.attributeNode( "strictness" );
+    if ( !a.isNull() )
+        m_strictness = a.value().toDouble();
+
+    debug() << getName();
+}
+
+ConstraintTypes::PlaylistDuration::PlaylistDuration( ConstraintNode* p )
+        : Constraint( p )
+        , m_duration( 0 )
+        , m_comparison( CompareNumEquals )
+        , m_strictness( 1.0 )
+{
+    DEBUG_BLOCK
+    debug() << "new default PlaylistLength";
+}
+
+QWidget*
+ConstraintTypes::PlaylistDuration::editWidget() const
+{
+    PlaylistDurationEditWidget* e = new PlaylistDurationEditWidget( m_duration, m_comparison, static_cast<int>( 10*m_strictness ) );
+    connect( e, SIGNAL( comparisonChanged( const int ) ), this, SLOT( setComparison( const int ) ) );
+    connect( e, SIGNAL( durationChanged( const int ) ), this, SLOT( setDuration( const int ) ) );
+    connect( e, SIGNAL( strictnessChanged( const int ) ), this, SLOT( setStrictness( const int ) ) );
+    return e;
+}
+
+void
+ConstraintTypes::PlaylistDuration::toXml( QDomDocument& doc, QDomElement& elem ) const
+{
+    QDomElement c = doc.createElement( "constraint" );
+    c.setAttribute( "type", "PlaylistDuration" );
+    c.setAttribute( "duration", QString::number( m_duration ) );
+    c.setAttribute( "comparison", QString::number( m_comparison ) );
+    c.setAttribute( "strictness", QString::number( m_strictness ) );
+    elem.appendChild( c );
+}
+
+QString
+ConstraintTypes::PlaylistDuration::getName() const
+{
+    QString v( i18n("Playlist duration: %1 %2") );
+    return v.arg( comparisonToString() ).arg( QTime().addMSecs( m_duration ).toString( "H:mm:ss" ) );
+}
+
+Collections::QueryMaker*
+ConstraintTypes::PlaylistDuration::initQueryMaker( Collections::QueryMaker* qm ) const
+{
+    return qm;
+}
+
+double
+ConstraintTypes::PlaylistDuration::satisfaction( const Meta::TrackList& tl )
+{
+    m_totalDuration = 0;
+    foreach( Meta::TrackPtr t, tl ) {
+        m_totalDuration += t->length();
+    }
+    return transformDuration( m_totalDuration );
+}
+
+double
+ConstraintTypes::PlaylistDuration::deltaS_insert( const Meta::TrackList&, const Meta::TrackPtr t, const int ) const
+{
+    qint64 l = m_totalDuration + t->length();
+    double newS = transformDuration( l );
+    double oldS = transformDuration( m_totalDuration );
+    return newS - oldS;
+}
+
+double
+ConstraintTypes::PlaylistDuration::deltaS_replace( const Meta::TrackList& tl, const Meta::TrackPtr t, const int i ) const
+{
+    int l = m_totalDuration + t->length() - tl.at( i )->length();
+    double newS = transformDuration( l );
+    double oldS = transformDuration( m_totalDuration );
+    return newS - oldS;
+}
+
+double
+ConstraintTypes::PlaylistDuration::deltaS_delete( const Meta::TrackList& tl, const int i ) const
+{
+    int l = m_totalDuration - tl.at( i )->length();
+    double newS = transformDuration( l );
+    double oldS = transformDuration( m_totalDuration );
+    return newS - oldS;
+}
+
+double
+ConstraintTypes::PlaylistDuration::deltaS_swap( const Meta::TrackList&, const int, const int ) const
+{
+    return 0.0;
+}
+
+void
+ConstraintTypes::PlaylistDuration::insertTrack( const Meta::TrackList&, const Meta::TrackPtr t, const int )
+{
+    m_totalDuration += t->length();
+}
+
+void
+ConstraintTypes::PlaylistDuration::replaceTrack( const Meta::TrackList& tl, const Meta::TrackPtr t, const int i )
+{
+    m_totalDuration += t->length();
+    m_totalDuration -= tl.at( i )->length();
+}
+
+void
+ConstraintTypes::PlaylistDuration::deleteTrack( const Meta::TrackList& tl, const int i )
+{
+    m_totalDuration -= tl.at( i )->length();
+}
+
+void
+ConstraintTypes::PlaylistDuration::swapTracks( const Meta::TrackList&, const int, const int ) {}
+
+int
+ConstraintTypes::PlaylistDuration::suggestInitialPlaylistSize() const
+{
+    if ( m_comparison == CompareNumLessThan ) {
+        return m_duration / 300000;
+    } else if ( m_comparison == CompareNumGreaterThan ) {
+        return m_duration / 180000;
+    } else {
+        return m_duration / 240000;
+    }
+}
+
+ConstraintNode::Vote*
+ConstraintTypes::PlaylistDuration::vote( const Meta::TrackList& playlist, const Meta::TrackList& domain ) const
+{
+    ConstraintNode::Vote* v = 0;
+
+    if ( m_comparison == CompareNumLessThan ) {
+        if ( m_totalDuration > m_duration) {
+            int longestDuration = 0;
+            int longestPosition = -1;
+            for ( int i = 0; i < playlist.size(); i++ ) {
+                Meta::TrackPtr t = playlist.at( i );
+                if ( t->length() > longestDuration ) {
+                    longestDuration = t->length();
+                    longestPosition = i;
+                }
+            }
+
+            v = new ConstraintNode::Vote;
+            v->operation = ConstraintNode::OperationDelete;
+            v->place = longestPosition;
+        }
+    } else if ( m_comparison == CompareNumGreaterThan ) {
+        if ( m_totalDuration < m_duration) {
+            v = new ConstraintNode::Vote;
+            v->operation = ConstraintNode::OperationInsert;
+            v->place = KRandom::random() % ( playlist.size() + 1 );
+            v->track = domain.at( KRandom::random() % domain.size() );
+        }
+    } else if ( m_comparison == CompareNumEquals ) {
+        int deviation = qAbs( m_totalDuration - m_duration );
+        if ( m_totalDuration > m_duration ) {
+            int randomIdx = KRandom::random() % playlist.size();
+            if ( ( playlist.at( randomIdx )->length() / 2 ) < deviation ) {
+                v = new ConstraintNode::Vote;
+                v->operation = ConstraintNode::OperationDelete;
+                v->place = randomIdx;
+            }
+        } else {
+            Meta::TrackPtr randomTrack = domain.at( KRandom::random() % domain.size() );
+            if ( ( randomTrack->length() / 2 ) < deviation ) {
+                v = new ConstraintNode::Vote;
+                v->operation = ConstraintNode::OperationInsert;
+                v->place = KRandom::random() % ( playlist.size() + 1 );
+                v->track = randomTrack;
+            }
+        }
+    }
+
+    return v;
+}
+
+QString
+ConstraintTypes::PlaylistDuration::comparisonToString() const
+{
+    if ( m_comparison == CompareNumEquals ) {
+        return QString( i18nc("duration of playlist equals some time", "equals") );
+    } else if ( m_comparison == CompareNumGreaterThan ) {
+        return QString( i18n("longer than") );
+    } else if ( m_comparison == CompareNumLessThan ) {
+        return QString( i18n("shorter than") );
+    } else {
+        return QString( i18n("unknown comparison") );
+    }
+}
+
+double
+ConstraintTypes::PlaylistDuration::transformDuration( const qint64 l ) const
+{
+    double factor = m_strictness * 0.0003;
+    if ( m_comparison == CompareNumEquals ) {
+        return 4.0 / ( ( 1.0 + exp( factor*( double )( l - m_duration ) ) )*( 1.0 + exp( factor*( double )( m_duration - l ) ) ) );
+    } else if ( m_comparison == CompareNumLessThan ) {
+        return 1.0 / ( 1.0 + exp( factor*( double )( l - m_duration ) ) );
+    } else if ( m_comparison == CompareNumGreaterThan ) {
+        return 1.0 / ( 1.0 + exp( factor*( double )( m_duration - l ) ) );
+    }
+    return 1.0;
+}
+
+void
+ConstraintTypes::PlaylistDuration::setComparison( const int c )
+{
+    m_comparison = c;
+    emit dataChanged();
+}
+
+void
+ConstraintTypes::PlaylistDuration::setDuration( const int v )
+{
+    m_duration = v;
+    emit dataChanged();
+}
+
+void
+ConstraintTypes::PlaylistDuration::setStrictness( const int sv )
+{
+    m_strictness = static_cast<double>(sv)/10.0;
+}
+
+/******************************
+ * Edit Widget                *
+ ******************************/
+
+ConstraintTypes::PlaylistDurationEditWidget::PlaylistDurationEditWidget( const int duration,
+                                                                     const int comparison,
+                                                                     const int strictness ) : QWidget( 0 )
+{
+    ui.setupUi( this );
+
+    ui.timeEdit_Duration->setTime( QTime().addMSecs( duration ) );
+    ui.comboBox_Comparison->setCurrentIndex( comparison );
+    ui.slider_Strictness->setValue( strictness );
+}
+
+void
+ConstraintTypes::PlaylistDurationEditWidget::on_timeEdit_Duration_timeChanged( const QTime& t )
+{
+    emit durationChanged( QTime().msecsTo( t ) );
+    emit updated();
+}
+
+void
+ConstraintTypes::PlaylistDurationEditWidget::on_comboBox_Comparison_currentIndexChanged( const int v )
+{
+    emit comparisonChanged( v );
+    emit updated();
+}
+
+void
+ConstraintTypes::PlaylistDurationEditWidget::on_slider_Strictness_valueChanged( const int v )
+{
+    emit strictnessChanged( v );
+    emit updated();
+}
diff --git a/src/playlistgenerator/constraints/PlaylistDuration.h b/src/playlistgenerator/constraints/PlaylistDuration.h
new file mode 100644
index 0000000..992215c
--- /dev/null
+++ b/src/playlistgenerator/constraints/PlaylistDuration.h
@@ -0,0 +1,112 @@
+/****************************************************************************************
+ * Copyright (c) 2008-2010 Soren Harward <stharward at gmail.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, see <http://www.gnu.org/licenses/>.                           *
+ ****************************************************************************************/
+
+#ifndef APG_PLAYLISTDURATION_CONSTRAINT
+#define APG_PLAYLISTDURATION_CONSTRAINT
+
+#include "ui_PlaylistDurationEditWidget.h"
+
+#include "playlistgenerator/Constraint.h"
+
+#include <QString>
+
+class ConstraintFactoryEntry;
+class QWidget;
+
+namespace Collections {
+    class QueryMaker;
+}
+
+namespace ConstraintTypes {
+
+    /* This constraint derives its name from the fact that it specifies the
+     * duration (ie, total running time) of the Playlist. */
+
+    class PlaylistDuration : public Constraint {
+        Q_OBJECT
+
+        enum NumComparison { CompareNumLessThan, CompareNumEquals, CompareNumGreaterThan };
+
+        public:
+            static Constraint* createFromXml(QDomElement&, ConstraintNode*);
+            static Constraint* createNew(ConstraintNode*);
+            static ConstraintFactoryEntry* registerMe();
+
+            virtual QWidget* editWidget() const;
+            virtual void toXml(QDomDocument&, QDomElement&) const;
+
+            virtual QString getName() const;
+
+            virtual Collections::QueryMaker* initQueryMaker(Collections::QueryMaker*) const;
+            virtual double satisfaction(const Meta::TrackList&);
+            virtual double deltaS_insert(const Meta::TrackList&, const Meta::TrackPtr, const int) const;
+            virtual double deltaS_replace(const Meta::TrackList&, const Meta::TrackPtr, const int) const;
+            virtual double deltaS_delete(const Meta::TrackList&, const int) const;
+            virtual double deltaS_swap(const Meta::TrackList&, const int, const int) const;
+            virtual void insertTrack(const Meta::TrackList&, const Meta::TrackPtr, const int);
+            virtual void replaceTrack(const Meta::TrackList&, const Meta::TrackPtr, const int);
+            virtual void deleteTrack(const Meta::TrackList&, const int);
+            virtual void swapTracks(const Meta::TrackList&, const int, const int);
+            virtual int suggestInitialPlaylistSize() const;
+            ConstraintNode::Vote* vote( const Meta::TrackList&, const Meta::TrackList& ) const;
+
+        private slots:
+            void setComparison( const int );
+            void setDuration( const int );
+            void setStrictness( const int );
+
+        private:
+            PlaylistDuration(QDomElement&, ConstraintNode*);
+            PlaylistDuration(ConstraintNode*);
+
+            // constraint parameters
+            qint64 m_duration; // time in msec
+            int m_comparison;
+            double m_strictness;
+
+            // convenience functions
+            QString comparisonToString() const;
+
+            // internal mathematical functions
+            double transformDuration( const qint64 ) const;
+
+            // internal mathematical state data
+            qint64 m_totalDuration;
+    };
+
+    class PlaylistDurationEditWidget : public QWidget {
+        Q_OBJECT
+
+        public:
+            PlaylistDurationEditWidget( const int, const int, const int );
+
+        signals:
+            void updated();
+            void durationChanged( const int );
+            void comparisonChanged( const int );
+            void strictnessChanged( const int );
+
+        private slots:
+            void on_timeEdit_Duration_timeChanged( const QTime& );
+            void on_comboBox_Comparison_currentIndexChanged( const int );
+            void on_slider_Strictness_valueChanged( const int );
+
+        private:
+            Ui::PlaylistDurationEditWidget ui;
+    };
+} // namespace ConstraintTypes
+
+#endif
diff --git a/src/playlistgenerator/constraints/PlaylistDurationEditWidget.ui b/src/playlistgenerator/constraints/PlaylistDurationEditWidget.ui
new file mode 100644
index 0000000..f4b0c8d
--- /dev/null
+++ b/src/playlistgenerator/constraints/PlaylistDurationEditWidget.ui
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <author>Soren Harward <stharward at gmail.com></author>
+ <class>PlaylistDurationEditWidget</class>
+ <widget class="QWidget" name="PlaylistDurationEditWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>247</width>
+    <height>113</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string comment="KDE::DoNotExtract">Form</string>
+  </property>
+  <property name="whatsThis">
+   <string>An editor for a PlaylistDuration constraint.  The user can set the name of the constraint; the target duration and whether the playlist should be less than, equal to, or greater than that duration; and the strictness with which the APG should match that duration.</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Playlist Duration Constraint Settings</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0">
+       <layout class="QGridLayout" name="gridLayout">
+        <item row="0" column="0">
+         <widget class="QComboBox" name="comboBox_Comparison">
+          <property name="toolTip">
+           <string/>
+          </property>
+          <property name="whatsThis">
+           <string>Whether the duration of the generated playlist should be shorter than, equal to, or longer than the specified value.</string>
+          </property>
+          <property name="currentIndex">
+           <number>1</number>
+          </property>
+          <item>
+           <property name="text">
+            <string>shorter than</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>equal to</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>longer than</string>
+           </property>
+          </item>
+         </widget>
+        </item>
+        <item row="0" column="1">
+         <layout class="QHBoxLayout" name="horizontalLayout">
+          <item>
+           <widget class="QTimeEdit" name="timeEdit_Duration">
+            <property name="toolTip">
+             <string/>
+            </property>
+            <property name="whatsThis">
+             <string>The desired duration of the playlist, in hours, minutes, and seconds.  Note: because of Qt limitations, the maximum playlist duration that you can specify is 24 hours.</string>
+            </property>
+            <property name="displayFormat">
+             <string>h:mm:ss</string>
+            </property>
+            <property name="time">
+             <time>
+              <hour>0</hour>
+              <minute>0</minute>
+              <second>0</second>
+             </time>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <spacer name="horizontalSpacer">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>40</width>
+              <height>20</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+         </layout>
+        </item>
+        <item row="1" column="0">
+         <widget class="QLabel" name="label_Strictness">
+          <property name="whatsThis">
+           <string>How strict the APG should be about matching the playlist duration to the specified time.</string>
+          </property>
+          <property name="text">
+           <string>Match:</string>
+          </property>
+          <property name="buddy">
+           <cstring>slider_Strictness</cstring>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="1">
+         <layout class="QHBoxLayout" name="horizontalLayout_2">
+          <item>
+           <widget class="QLabel" name="label_Fuzzy">
+            <property name="text">
+             <string>fuzzy</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QSlider" name="slider_Strictness">
+            <property name="toolTip">
+             <string/>
+            </property>
+            <property name="whatsThis">
+             <string>How strict the APG should be about matching the playlist duration to the specified time.</string>
+            </property>
+            <property name="maximum">
+             <number>10</number>
+            </property>
+            <property name="value">
+             <number>8</number>
+            </property>
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLabel" name="label_Exact">
+            <property name="text">
+             <string>exact</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item row="2" column="1">
+         <spacer name="verticalSpacer">
+          <property name="orientation">
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>20</width>
+            <height>40</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>comboBox_Comparison</tabstop>
+  <tabstop>timeEdit_Duration</tabstop>
+  <tabstop>slider_Strictness</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/playlistgenerator/constraints/PlaylistLength.cpp b/src/playlistgenerator/constraints/PlaylistLength.cpp
index e6b03a4..58b076e 100644
--- a/src/playlistgenerator/constraints/PlaylistLength.cpp
+++ b/src/playlistgenerator/constraints/PlaylistLength.cpp
@@ -55,20 +55,25 @@ ConstraintFactoryEntry*
 ConstraintTypes::PlaylistLength::registerMe()
 {
     return new ConstraintFactoryEntry( "PlaylistLength",
-                                       i18n("Playlist Duration"),
-                                       i18n("Sets the preferred duration of the playlist"),
+                                       i18n("Playlist Length"),
+                                       i18n("Sets the preferred number of tracks in the playlist"),
                                        &PlaylistLength::createFromXml, &PlaylistLength::createNew );
 }
 
 ConstraintTypes::PlaylistLength::PlaylistLength( QDomElement& xmlelem, ConstraintNode* p )
         : Constraint( p )
 {
-    DEBUG_BLOCK
     QDomAttr a;
 
     a = xmlelem.attributeNode( "length" );
-    if ( !a.isNull() )
+    if ( !a.isNull() ) {
         m_length = a.value().toInt();
+        /* after 2.3.2, what was the PlaylistLength constraint became the
+         * PlaylistDuration constraint, so this works around the instance when
+         * a user loads an XML file generated with the old code -- sth*/
+        if ( m_length > 1000 )
+            m_length /= 240000;
+    }
 
     a = xmlelem.attributeNode( "comparison" );
     if ( !a.isNull() )
@@ -77,18 +82,14 @@ ConstraintTypes::PlaylistLength::PlaylistLength( QDomElement& xmlelem, Constrain
     a = xmlelem.attributeNode( "strictness" );
     if ( !a.isNull() )
         m_strictness = a.value().toDouble();
-
-    debug() << getName();
 }
 
 ConstraintTypes::PlaylistLength::PlaylistLength( ConstraintNode* p )
         : Constraint( p )
-        , m_length( 0 )
+        , m_length( 30 )
         , m_comparison( CompareNumEquals )
         , m_strictness( 1.0 )
 {
-    DEBUG_BLOCK
-    debug() << "new default PlaylistLength";
 }
 
 QWidget*
@@ -115,8 +116,8 @@ ConstraintTypes::PlaylistLength::toXml( QDomDocument& doc, QDomElement& elem ) c
 QString
 ConstraintTypes::PlaylistLength::getName() const
 {
-    QString v( i18n("Playlist duration %1 %2") );
-    return v.arg( comparisonToString() ).arg( QTime().addMSecs( m_length ).toString( "H:mm:ss" ) );
+    QString v( i18n("Playlist length: %1 %2 tracks") ); // FIXME: proper pluralization
+    return v.arg( comparisonToString() ).arg( m_length );
 }
 
 Collections::QueryMaker*
@@ -128,38 +129,26 @@ ConstraintTypes::PlaylistLength::initQueryMaker( Collections::QueryMaker* qm ) c
 double
 ConstraintTypes::PlaylistLength::satisfaction( const Meta::TrackList& tl )
 {
-    m_totalLength = 0;
-    foreach( Meta::TrackPtr t, tl ) {
-        m_totalLength += t->length();
-    }
-    return transformLength( m_totalLength );
+    m_totalLength = tl.size();
+    return penalty( m_totalLength );
 }
 
 double
 ConstraintTypes::PlaylistLength::deltaS_insert( const Meta::TrackList&, const Meta::TrackPtr t, const int ) const
 {
-    qint64 l = m_totalLength + t->length();
-    double newS = transformLength( l );
-    double oldS = transformLength( m_totalLength );
-    return newS - oldS;
+    return penalty( m_totalLength + 1 ) - penalty( m_totalLength );
 }
 
 double
-ConstraintTypes::PlaylistLength::deltaS_replace( const Meta::TrackList& tl, const Meta::TrackPtr t, const int i ) const
+ConstraintTypes::PlaylistLength::deltaS_replace( const Meta::TrackList&, const Meta::TrackPtr, const int ) const
 {
-    int l = m_totalLength + t->length() - tl.at( i )->length();
-    double newS = transformLength( l );
-    double oldS = transformLength( m_totalLength );
-    return newS - oldS;
+    return 0.0;
 }
 
 double
-ConstraintTypes::PlaylistLength::deltaS_delete( const Meta::TrackList& tl, const int i ) const
+ConstraintTypes::PlaylistLength::deltaS_delete( const Meta::TrackList&, const int ) const
 {
-    int l = m_totalLength - tl.at( i )->length();
-    double newS = transformLength( l );
-    double oldS = transformLength( m_totalLength );
-    return newS - oldS;
+    return penalty( m_totalLength - 1 ) - penalty( m_totalLength );
 }
 
 double
@@ -169,22 +158,18 @@ ConstraintTypes::PlaylistLength::deltaS_swap( const Meta::TrackList&, const int,
 }
 
 void
-ConstraintTypes::PlaylistLength::insertTrack( const Meta::TrackList&, const Meta::TrackPtr t, const int )
+ConstraintTypes::PlaylistLength::insertTrack( const Meta::TrackList&, const Meta::TrackPtr, const int )
 {
-    m_totalLength += t->length();
+    m_totalLength++;
 }
 
 void
-ConstraintTypes::PlaylistLength::replaceTrack( const Meta::TrackList& tl, const Meta::TrackPtr t, const int i )
-{
-    m_totalLength += t->length();
-    m_totalLength -= tl.at( i )->length();
-}
+ConstraintTypes::PlaylistLength::replaceTrack( const Meta::TrackList&, const Meta::TrackPtr, const int ) {}
 
 void
-ConstraintTypes::PlaylistLength::deleteTrack( const Meta::TrackList& tl, const int i )
+ConstraintTypes::PlaylistLength::deleteTrack( const Meta::TrackList&, const int )
 {
-    m_totalLength -= tl.at( i )->length();
+    m_totalLength--;
 }
 
 void
@@ -193,13 +178,7 @@ ConstraintTypes::PlaylistLength::swapTracks( const Meta::TrackList&, const int,
 int
 ConstraintTypes::PlaylistLength::suggestInitialPlaylistSize() const
 {
-    if ( m_comparison == CompareNumLessThan ) {
-        return m_length / 300000;
-    } else if ( m_comparison == CompareNumGreaterThan ) {
-        return m_length / 180000;
-    } else {
-        return m_length / 240000;
-    }
+    return m_length;
 }
 
 ConstraintNode::Vote*
@@ -207,48 +186,7 @@ ConstraintTypes::PlaylistLength::vote( const Meta::TrackList& playlist, const Me
 {
     ConstraintNode::Vote* v = 0;
 
-    if ( m_comparison == CompareNumLessThan ) {
-        if ( m_totalLength > m_length) {
-            int longestLength = 0;
-            int longestPosition = -1;
-            for ( int i = 0; i < playlist.size(); i++ ) {
-                Meta::TrackPtr t = playlist.at( i );
-                if ( t->length() > longestLength ) {
-                    longestLength = t->length();
-                    longestPosition = i;
-                }
-            }
-
-            v = new ConstraintNode::Vote;
-            v->operation = ConstraintNode::OperationDelete;
-            v->place = longestPosition;
-        }
-    } else if ( m_comparison == CompareNumGreaterThan ) {
-        if ( m_totalLength < m_length) {
-            v = new ConstraintNode::Vote;
-            v->operation = ConstraintNode::OperationInsert;
-            v->place = KRandom::random() % ( playlist.size() + 1 );
-            v->track = domain.at( KRandom::random() % domain.size() );
-        }
-    } else if ( m_comparison == CompareNumEquals ) {
-        int deviation = qAbs( m_totalLength - m_length );
-        if ( m_totalLength > m_length ) {
-            int randomIdx = KRandom::random() % playlist.size();
-            if ( ( playlist.at( randomIdx )->length() / 2 ) < deviation ) {
-                v = new ConstraintNode::Vote;
-                v->operation = ConstraintNode::OperationDelete;
-                v->place = randomIdx;
-            }
-        } else {
-            Meta::TrackPtr randomTrack = domain.at( KRandom::random() % domain.size() );
-            if ( ( randomTrack->length() / 2 ) < deviation ) {
-                v = new ConstraintNode::Vote;
-                v->operation = ConstraintNode::OperationInsert;
-                v->place = KRandom::random() % ( playlist.size() + 1 );
-                v->track = randomTrack;
-            }
-        }
-    }
+    //FIXME: needs to vote for addition or removal as appropriate
 
     return v;
 }
@@ -257,28 +195,36 @@ QString
 ConstraintTypes::PlaylistLength::comparisonToString() const
 {
     if ( m_comparison == CompareNumEquals ) {
-        return QString( i18nc("duration of playlist equals some time", "equals") );
+        return QString( i18nc("number of tracks in playlist equals some number", "equals") );
     } else if ( m_comparison == CompareNumGreaterThan ) {
-        return QString( i18n("longer than") );
+        return QString( i18n("more than") );
     } else if ( m_comparison == CompareNumLessThan ) {
-        return QString( i18n("shorter than") );
+        return QString( i18n("less than") );
     } else {
         return QString( i18n("unknown comparison") );
     }
 }
 
 double
-ConstraintTypes::PlaylistLength::transformLength( const qint64 l ) const
+ConstraintTypes::PlaylistLength::penalty( const int l ) const
 {
-    double factor = m_strictness * 0.0003;
     if ( m_comparison == CompareNumEquals ) {
-        return 4.0 / ( ( 1.0 + exp( factor*( double )( l - m_length ) ) )*( 1.0 + exp( factor*( double )( m_length - l ) ) ) );
-    } else if ( m_comparison == CompareNumLessThan ) {
-        return 1.0 / ( 1.0 + exp( factor*( double )( l - m_length ) ) );
+        return ( l == m_length ) ? 1.0 : transformLength( abs( l - m_length ) );
     } else if ( m_comparison == CompareNumGreaterThan ) {
-        return 1.0 / ( 1.0 + exp( factor*( double )( m_length - l ) ) );
+        return ( l > m_length ) ? 1.0 : transformLength( m_length - l );
+    } else if ( m_comparison == CompareNumLessThan ) {
+        return ( l < m_length ) ? 1.0 : transformLength( l - m_length );
+    } else {
+        return 0.0;
     }
-    return 1.0;
+}
+
+double
+ConstraintTypes::PlaylistLength::transformLength( const int delta ) const
+{
+    // Note: delta must be positive
+    const double w = 8.0;
+    return exp( -10.0 * ( 0.1 + m_strictness ) / w * ( delta + 1 ) );
 }
 
 void
@@ -289,9 +235,9 @@ ConstraintTypes::PlaylistLength::setComparison( const int c )
 }
 
 void
-ConstraintTypes::PlaylistLength::setLength( const int v )
+ConstraintTypes::PlaylistLength::setLength( const int l )
 {
-    m_length = v;
+    m_length = l;
     emit dataChanged();
 }
 
@@ -311,15 +257,15 @@ ConstraintTypes::PlaylistLengthEditWidget::PlaylistLengthEditWidget( const int l
 {
     ui.setupUi( this );
 
-    ui.timeEdit_Duration->setTime( QTime().addMSecs( length ) );
+    ui.spinBox_Length->setValue( length );
     ui.comboBox_Comparison->setCurrentIndex( comparison );
     ui.slider_Strictness->setValue( strictness );
 }
 
 void
-ConstraintTypes::PlaylistLengthEditWidget::on_timeEdit_Duration_timeChanged( const QTime& t )
+ConstraintTypes::PlaylistLengthEditWidget::on_spinBox_Length_valueChanged( const int l )
 {
-    emit lengthChanged( QTime().msecsTo( t ) );
+    emit lengthChanged( l );
     emit updated();
 }
 
diff --git a/src/playlistgenerator/constraints/PlaylistLength.h b/src/playlistgenerator/constraints/PlaylistLength.h
index d21ac71..adaab6b 100644
--- a/src/playlistgenerator/constraints/PlaylistLength.h
+++ b/src/playlistgenerator/constraints/PlaylistLength.h
@@ -33,7 +33,7 @@ namespace Collections {
 namespace ConstraintTypes {
 
     /* This constraint derives its name from the fact that it specifies the
-     * Length of the Playlist. */
+     * length (ie, number of tracks) of the Playlist. */
 
     class PlaylistLength : public Constraint {
         Q_OBJECT
@@ -41,25 +41,25 @@ namespace ConstraintTypes {
         enum NumComparison { CompareNumLessThan, CompareNumEquals, CompareNumGreaterThan };
 
         public:
-            static Constraint* createFromXml(QDomElement&, ConstraintNode*);
-            static Constraint* createNew(ConstraintNode*);
+            static Constraint* createFromXml( QDomElement&, ConstraintNode* );
+            static Constraint* createNew( ConstraintNode* );
             static ConstraintFactoryEntry* registerMe();
 
             virtual QWidget* editWidget() const;
-            virtual void toXml(QDomDocument&, QDomElement&) const;
+            virtual void toXml( QDomDocument&, QDomElement& ) const;
 
             virtual QString getName() const;
 
-            virtual Collections::QueryMaker* initQueryMaker(Collections::QueryMaker*) const;
-            virtual double satisfaction(const Meta::TrackList&);
-            virtual double deltaS_insert(const Meta::TrackList&, const Meta::TrackPtr, const int) const;
-            virtual double deltaS_replace(const Meta::TrackList&, const Meta::TrackPtr, const int) const;
-            virtual double deltaS_delete(const Meta::TrackList&, const int) const;
-            virtual double deltaS_swap(const Meta::TrackList&, const int, const int) const;
-            virtual void insertTrack(const Meta::TrackList&, const Meta::TrackPtr, const int);
-            virtual void replaceTrack(const Meta::TrackList&, const Meta::TrackPtr, const int);
-            virtual void deleteTrack(const Meta::TrackList&, const int);
-            virtual void swapTracks(const Meta::TrackList&, const int, const int);
+            virtual Collections::QueryMaker* initQueryMaker( Collections::QueryMaker* ) const;
+            virtual double satisfaction( const Meta::TrackList& );
+            virtual double deltaS_insert( const Meta::TrackList&, const Meta::TrackPtr, const int ) const;
+            virtual double deltaS_replace( const Meta::TrackList&, const Meta::TrackPtr, const int ) const;
+            virtual double deltaS_delete( const Meta::TrackList&, const int ) const;
+            virtual double deltaS_swap( const Meta::TrackList&, const int, const int ) const;
+            virtual void insertTrack( const Meta::TrackList&, const Meta::TrackPtr, const int );
+            virtual void replaceTrack( const Meta::TrackList&, const Meta::TrackPtr, const int );
+            virtual void deleteTrack( const Meta::TrackList&, const int );
+            virtual void swapTracks( const Meta::TrackList&, const int, const int );
             virtual int suggestInitialPlaylistSize() const;
             ConstraintNode::Vote* vote( const Meta::TrackList&, const Meta::TrackList& ) const;
 
@@ -69,11 +69,11 @@ namespace ConstraintTypes {
             void setStrictness( const int );
 
         private:
-            PlaylistLength(QDomElement&, ConstraintNode*);
-            PlaylistLength(ConstraintNode*);
+            PlaylistLength( QDomElement&, ConstraintNode* );
+            PlaylistLength( ConstraintNode* );
 
             // constraint parameters
-            qint64 m_length; // time in msec
+            int m_length;
             int m_comparison;
             double m_strictness;
 
@@ -81,10 +81,11 @@ namespace ConstraintTypes {
             QString comparisonToString() const;
 
             // internal mathematical functions
-            double transformLength( const qint64 ) const;
+            double penalty( const int ) const;
+            double transformLength( const int ) const;
 
             // internal mathematical state data
-            qint64 m_totalLength;
+            int m_totalLength;
     };
 
     class PlaylistLengthEditWidget : public QWidget {
@@ -100,7 +101,7 @@ namespace ConstraintTypes {
             void strictnessChanged( const int );
 
         private slots:
-            void on_timeEdit_Duration_timeChanged( const QTime& );
+            void on_spinBox_Length_valueChanged( const int );
             void on_comboBox_Comparison_currentIndexChanged( const int );
             void on_slider_Strictness_valueChanged( const int );
 
diff --git a/src/playlistgenerator/constraints/PlaylistLengthEditWidget.ui b/src/playlistgenerator/constraints/PlaylistLengthEditWidget.ui
index 38b9f8b..e56287b 100644
--- a/src/playlistgenerator/constraints/PlaylistLengthEditWidget.ui
+++ b/src/playlistgenerator/constraints/PlaylistLengthEditWidget.ui
@@ -7,7 +7,7 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>247</width>
+    <width>236</width>
     <height>113</height>
    </rect>
   </property>
@@ -15,13 +15,13 @@
    <string comment="KDE::DoNotExtract">Form</string>
   </property>
   <property name="whatsThis">
-   <string>An editor for a PlaylistLength constraint.  The user can set the name of the constraint; the target duration and whether the playlist should be less than, equal to, or greater than that duration; and the strictness with which the APG should match that duration.</string>
+   <string>An editor for a Playlist Length constraint.  The user can the target number of tracks and whether the playlist contain fewer than, equal to, or more than that number of tracks; and the strictness with which the APG should match the target number of tracks.</string>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QGroupBox" name="groupBox">
      <property name="title">
-      <string>Playlist Duration Constraint Settings</string>
+      <string>Playlist Length Constraint Settings</string>
      </property>
      <layout class="QGridLayout" name="gridLayout_2">
       <item row="0" column="0">
@@ -32,14 +32,14 @@
            <string/>
           </property>
           <property name="whatsThis">
-           <string>Whether the duration of the generated playlist should be shorter than, equal to, or longer than the specified value.</string>
+           <string>Whether the number of tracks in the generated playlist should be less than, equal to, or more than the specified value.</string>
           </property>
           <property name="currentIndex">
-           <number>1</number>
+           <number>0</number>
           </property>
           <item>
            <property name="text">
-            <string>shorter than</string>
+            <string>fewer than</string>
            </property>
           </item>
           <item>
@@ -49,7 +49,7 @@
           </item>
           <item>
            <property name="text">
-            <string>longer than</string>
+            <string>more than</string>
            </property>
           </item>
          </widget>
@@ -57,22 +57,15 @@
         <item row="0" column="1">
          <layout class="QHBoxLayout" name="horizontalLayout">
           <item>
-           <widget class="QTimeEdit" name="timeEdit_Duration">
-            <property name="toolTip">
-             <string/>
-            </property>
+           <widget class="QSpinBox" name="spinBox_Length">
             <property name="whatsThis">
-             <string>The desired duration of the playlist, in hours, minutes, and seconds.  Note: because of Qt limitations, the maximum playlist duration that you can specify is 24 hours.</string>
+             <string>The target number of tracks for the generated playlist.</string>
             </property>
-            <property name="displayFormat">
-             <string>h:mm:ss</string>
+            <property name="maximum">
+             <number>9999</number>
             </property>
-            <property name="time">
-             <time>
-              <hour>0</hour>
-              <minute>0</minute>
-              <second>0</second>
-             </time>
+            <property name="value">
+             <number>30</number>
             </property>
            </widget>
           </item>
@@ -111,6 +104,9 @@
             <property name="text">
              <string>fuzzy</string>
             </property>
+            <property name="buddy">
+             <cstring></cstring>
+            </property>
            </widget>
           </item>
           <item>
@@ -119,7 +115,7 @@
              <string/>
             </property>
             <property name="whatsThis">
-             <string>How strict the APG should be about matching the playlist duration to the specified time.</string>
+             <string>How strict the APG should be about matching the playlist length to the specified number of tracks.</string>
             </property>
             <property name="maximum">
              <number>10</number>
@@ -163,7 +159,6 @@
  </widget>
  <tabstops>
   <tabstop>comboBox_Comparison</tabstop>
-  <tabstop>timeEdit_Duration</tabstop>
   <tabstop>slider_Strictness</tabstop>
  </tabstops>
  <resources/>



More information about the Amarok mailing list