diff options
Diffstat (limited to 'src/kfeedback.cpp')
-rw-r--r-- | src/kfeedback.cpp | 504 |
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 |