summaryrefslogtreecommitdiffstats
path: root/src/kfeedback.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/kfeedback.cpp')
-rw-r--r--src/kfeedback.cpp504
1 files changed, 504 insertions, 0 deletions
diff --git a/src/kfeedback.cpp b/src/kfeedback.cpp
new file mode 100644
index 0000000..e1eefac
--- /dev/null
+++ b/src/kfeedback.cpp
@@ -0,0 +1,504 @@
+
+/*
+ * File name: kfeedback.cpp
+ * Summary: User feedback form
+ * License: LGPL - See file COPYING.LIB for details.
+ * Author: Stefan Hundhammer <sh@suse.de>
+ *
+ * Updated: 2004-11-23
+ */
+
+
+#include <qheader.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qglobal.h>
+#include <qmultilineedit.h>
+#include <qhbox.h>
+
+#include <kglobal.h>
+#include <kapp.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kaboutdata.h>
+#include <kiconloader.h>
+#include <kurl.h>
+
+#include "kfeedback.h"
+
+
+KFeedbackDialog::KFeedbackDialog( const QString & feedbackMailAddress,
+ const QString & helpTopic )
+ : KDialogBase( Plain, // dialogFace
+ i18n( "Feedback" ), // caption
+ Apply | Cancel
+ | ( helpTopic.isEmpty() ? 0 : (int) Help ), // buttonMask
+ Apply ) // defaultButton
+{
+ QVBoxLayout * layout = new QVBoxLayout( plainPage(), 0, spacingHint() );
+ setButtonApply( KGuiItem( i18n( "&Mail this..." ) ) );
+
+ if ( ! helpTopic.isEmpty() )
+ setHelp( helpTopic );
+
+ _form = new KFeedbackForm( feedbackMailAddress, plainPage() );
+ Q_CHECK_PTR( _form );
+
+ layout->addWidget( _form );
+ checkSendButton();
+
+ connect( this, SIGNAL( applyClicked() ),
+ _form, SLOT ( sendMail() ) );
+
+ connect( _form, SIGNAL( mailSent() ),
+ this, SLOT ( hide() ) );
+
+ connect( _form, SIGNAL( mailSent() ),
+ this, SIGNAL( mailSent() ) );
+
+ connect( _form, SIGNAL( checkComplete() ),
+ this, SLOT ( checkSendButton() ) );
+}
+
+
+KFeedbackDialog::~KFeedbackDialog()
+{
+ // NOP
+}
+
+
+void
+KFeedbackDialog::checkSendButton()
+{
+ enableButtonApply( _form->readyToSend() );
+}
+
+
+
+
+
+KFeedbackForm::KFeedbackForm( const QString & feedbackMailAddress,
+ QWidget * parent )
+ : QVBox( parent )
+ , _feedbackMailAddress( feedbackMailAddress )
+{
+ //
+ // Explanation above the question list
+ //
+
+ QLabel * label = new QLabel( i18n( "<p><b>Please tell us your opinion about this program.</b></p>"
+ "<p>You will be able to review everything in your mailer "
+ "before any mail is sent.<br>"
+ "Nothing will be sent behind your back.</p>"
+ ), this );
+ //
+ // Question list
+ //
+
+ _questionList = new KFeedbackQuestionList( this );
+ Q_CHECK_PTR( _questionList );
+
+ connect( _questionList, SIGNAL( checkComplete() ),
+ this, SLOT ( slotCheckComplete() ) );
+
+
+ //
+ // Explanation below the question list
+ //
+
+ QHBox * hbox = new QHBox( this );
+ Q_CHECK_PTR( hbox );
+
+ QSizePolicy pol( QSizePolicy::Fixed, QSizePolicy::Fixed ); // hor / vert
+
+ label = new QLabel( i18n( "Questions marked with " ), hbox );
+ Q_CHECK_PTR( label );
+ label->setSizePolicy( pol );
+
+ label = new QLabel( hbox );
+ Q_CHECK_PTR( label );
+ label->setPixmap( KGlobal::iconLoader()->loadIcon( "edit", KIcon::Small ) );
+ label->setSizePolicy( pol );
+
+ label = new QLabel( i18n( " must be answered before a mail can be sent.") , hbox );
+ Q_CHECK_PTR( label );
+ label->setSizePolicy( pol );
+
+ new QWidget( hbox ); // Fill any leftover space to the right.
+
+
+ //
+ // Free-text comment field
+ //
+
+ label = new QLabel( "\n" + i18n( "&Additional Comments:" ), this ); Q_CHECK_PTR( label );
+ _comment = new QMultiLineEdit( this ); Q_CHECK_PTR( _comment );
+
+ label->setBuddy( _comment );
+#if (QT_VERSION < 300)
+ _comment->setFixedVisibleLines( 5 );
+#endif
+ _comment->setWordWrap( QMultiLineEdit::FixedColumnWidth );
+ _comment->setWrapColumnOrWidth( 70 );
+}
+
+
+KFeedbackForm::~KFeedbackForm()
+{
+ // NOP
+}
+
+
+void
+KFeedbackForm::sendMail()
+{
+ //
+ // Build mail subject
+ //
+
+ QString subject;
+
+ const KAboutData * aboutData = KGlobal::instance()->aboutData();
+
+ if ( aboutData )
+ subject = aboutData->programName() + "-" + aboutData->version();
+ else
+ subject = kapp->name();
+
+ subject = "[kde-feedback] " + subject + " user feedback";
+
+
+ //
+ // Build mail body
+ //
+
+ QString body = subject + "\n\n"
+ + formatComment()
+ + _questionList->result();
+
+
+ //
+ // Build "mailto:" URL from all this
+ //
+
+ KURL mail;
+ mail.setProtocol( "mailto" );
+ mail.setPath( _feedbackMailAddress );
+ mail.setQuery( "?subject=" + KURL::encode_string( subject ) +
+ "&body=" + KURL::encode_string( body ) );
+
+ // TODO: Check for maximum command line length.
+ //
+ // The hard part with this is how to get this from all that 'autoconf'
+ // stuff into 'config.h' or some other include file without hardcoding
+ // anything - this is too system dependent.
+
+
+ //
+ // Actually send mail
+ //
+
+ kapp->invokeMailer( mail );
+
+ emit mailSent();
+}
+
+
+void
+KFeedbackForm::slotCheckComplete()
+{
+ emit checkComplete();
+}
+
+
+QString
+KFeedbackForm::formatComment()
+{
+ QString result = _comment->text();
+
+ if ( ! result.isEmpty() )
+ {
+ result = "<comment>\n" + result + "\n</comment>\n\n";
+ }
+
+ return result;
+}
+
+
+bool
+KFeedbackForm::readyToSend()
+{
+ return _questionList->isComplete();
+}
+
+
+
+
+
+
+KFeedbackQuestionList::KFeedbackQuestionList( QWidget *parent )
+ : QListView( parent )
+{
+ addColumn( "" );
+ header()->hide();
+}
+
+
+KFeedbackQuestionList::~KFeedbackQuestionList()
+{
+ // NOP
+}
+
+
+bool
+KFeedbackQuestionList::isComplete()
+{
+ KFeedbackQuestion * question = firstQuestion();
+
+ while ( question )
+ {
+ if ( question->isRequired() && ! question->isAnswered() )
+ return false;
+
+ question = question->nextQuestion();
+ }
+
+ return true;
+}
+
+
+QString
+KFeedbackQuestionList::result()
+{
+ QString res;
+ KFeedbackQuestion * question = firstQuestion();
+
+ while ( question )
+ {
+ res += question->result();
+
+ question = question->nextQuestion();
+ }
+
+ return res;
+}
+
+
+KFeedbackQuestion *
+KFeedbackQuestionList::addQuestion( const QString & text,
+ const QString & id,
+ bool exclusiveAnswer,
+ bool required )
+{
+ KFeedbackQuestion * question = new KFeedbackQuestion( this, text, id,
+ exclusiveAnswer,
+ required );
+ Q_CHECK_PTR( question );
+
+ return question;
+}
+
+
+void
+KFeedbackQuestionList::addYesNoQuestion( const QString & text,
+ const QString & id,
+ bool required )
+{
+
+ KFeedbackQuestion * question = new KFeedbackQuestion( this, text, id,
+ true, // exclusive
+ required );
+ Q_CHECK_PTR( question );
+ question->addAnswer( i18n( "yes" ), "yes" );
+ question->addAnswer( i18n( "no" ), "no" );
+}
+
+
+void
+KFeedbackQuestionList::questionAnswered()
+{
+ emit checkComplete();
+}
+
+void
+KFeedbackQuestionList::questionAdded( KFeedbackQuestion * question)
+{
+ if ( question->isRequired() )
+ emit checkComplete();
+}
+
+
+
+
+
+static int nextNo = 0;
+
+KFeedbackQuestion::KFeedbackQuestion( KFeedbackQuestionList * parent,
+ const QString & text,
+ const QString & id,
+ bool exclusiveAnswer,
+ bool required,
+ bool open )
+ : QCheckListItem( parent, text )
+ , _id( id )
+ , _exclusiveAnswer( exclusiveAnswer )
+ , _required( required )
+{
+ if ( required )
+ {
+ setPixmap( 0, KGlobal::iconLoader()->loadIcon( "edit", KIcon::Small ) );
+ }
+
+ setOpen( open );
+ _no = nextNo++;
+
+ parent->questionAdded( this );
+}
+
+
+void
+KFeedbackQuestion::addAnswer( const QString & text,
+ const QString & id )
+{
+ new KFeedbackAnswer( this, text, id, _exclusiveAnswer );
+}
+
+
+bool
+KFeedbackQuestion::isAnswered()
+{
+ if ( ! _exclusiveAnswer )
+ {
+ /**
+ * If any number of answers is permitted for this question, this
+ * question is always considered to be answered.
+ **/
+
+ return true;
+ }
+
+
+ /**
+ * If this question requires an exclusive answer, exactly one of them
+ * should be checked. We don't need to bother about more than one being
+ * checked here - QListView does that for us.
+ **/
+
+ KFeedbackAnswer *answer = firstAnswer();
+
+ while ( answer )
+ {
+ if ( answer->isChecked() )
+ return true;
+
+ answer = answer->nextAnswer();
+ }
+
+ return false;
+}
+
+
+QString
+KFeedbackQuestion::result()
+{
+ QString res;
+ int answers = 0;
+
+ KFeedbackAnswer *answer = firstAnswer();
+
+ while ( answer )
+ {
+ if ( answer->isChecked() )
+ {
+ res += _id + "=\"" + answer->id() + "\"\n";
+ answers++;
+ }
+
+ answer = answer->nextAnswer();
+ }
+
+ if ( answers > 1 )
+ {
+ res = "\n" + res + "\n";
+ }
+
+ return res;
+}
+
+
+QString
+KFeedbackQuestion::text()
+{
+ return QCheckListItem::text(0);
+}
+
+
+QString
+KFeedbackQuestion::key( int, bool ) const
+{
+ QString no;
+ no.sprintf( "%08d", _no );
+
+ return no;
+}
+
+
+KFeedbackQuestionList *
+KFeedbackQuestion::questionList() const
+{
+ return dynamic_cast<KFeedbackQuestionList *>( listView() );
+}
+
+
+
+
+
+
+
+KFeedbackAnswer::KFeedbackAnswer( KFeedbackQuestion * parent,
+ const QString & text,
+ const QString & id,
+ bool exclusive )
+ : QCheckListItem( parent,
+ text,
+ exclusive
+ ? QCheckListItem::RadioButton
+ : QCheckListItem::CheckBox )
+ , _id( id )
+{
+ _no = nextNo++;
+}
+
+
+QString
+KFeedbackAnswer::text()
+{
+ return QCheckListItem::text(0);
+}
+
+
+QString
+KFeedbackAnswer::key( int, bool ) const
+{
+ QString no;
+ no.sprintf( "%08d", _no );
+
+ return no;
+}
+
+
+void
+KFeedbackAnswer::stateChange( bool newState )
+{
+ if ( newState && question()->isRequired() )
+ {
+ KFeedbackQuestionList * list = question()->questionList();
+
+ if ( list )
+ list->questionAnswered();
+ }
+}
+
+
+
+// EOF