1 /***************************************************************************
2 * Copyright (C) 2006 by Sebastien Laout *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU Library General Public License as *
7 * published by the Free Software Foundation; either version 2 of the *
8 * License, or (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU Library General Public *
16 * License along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
19 ***************************************************************************/
21 #include <kapplication.h>
22 #include <kaboutdata.h>
25 #include <kiconloader.h>
26 #include <kaboutdata.h>
29 #include <kmessagebox.h>
31 #include <qtoolbutton.h>
33 #include <Q3HBoxLayout>
34 #include <Q3GridLayout>
36 #include <Q3VBoxLayout>
37 #include <kpushbutton.h>
38 #include <qcheckbox.h>
39 #include <qradiobutton.h>
40 #include <q3buttongroup.h>
41 #include <q3vgroupbox.h>
43 #include <q3popupmenu.h>
44 #include <q3textedit.h>
47 #include <kdialogbase.h>
50 #include <kinputdialog.h>
51 #include <qvalidator.h>
60 #include "likeback_private.h"
62 /****************************************/
63 /********** class LikeBackBar: **********/
64 /****************************************/
66 LikeBackBar::LikeBackBar(LikeBack
*likeBack
)
67 : QWidget(0, "LikeBackBar", Qt::WX11BypassWM
| Qt::WStyle_NoBorder
| Qt::WNoAutoErase
| Qt::WStyle_StaysOnTop
| Qt::WStyle_NoBorder
| Qt::Qt::WGroupLeader
)
68 , m_likeBack(likeBack
)
70 Q3HBoxLayout
*layout
= new Q3HBoxLayout(this);
72 QIcon likeIconSet
= kapp
->iconLoader()->loadIconSet("likeback_like", KIcon::Small
);
73 QIcon dislikeIconSet
= kapp
->iconLoader()->loadIconSet("likeback_dislike", KIcon::Small
);
74 QIcon bugIconSet
= kapp
->iconLoader()->loadIconSet("likeback_bug", KIcon::Small
);
75 QIcon featureIconSet
= kapp
->iconLoader()->loadIconSet("likeback_feature", KIcon::Small
);
77 m_likeButton
= new QToolButton(this, "likeback_like");
78 m_likeButton
->setIconSet(likeIconSet
);
79 m_likeButton
->setTextLabel("<p>" + i18n("Send application developers a comment about something you like"));
80 m_likeButton
->setAutoRaise(true);
81 connect( m_likeButton
, SIGNAL(clicked()), this, SLOT(clickedLike()) );
82 layout
->add(m_likeButton
);
84 m_dislikeButton
= new QToolButton(this, "likeback_dislike");
85 m_dislikeButton
->setIconSet(dislikeIconSet
);
86 m_dislikeButton
->setTextLabel("<p>" + i18n("Send application developers a comment about something you dislike"));
87 m_dislikeButton
->setAutoRaise(true);
88 connect( m_dislikeButton
, SIGNAL(clicked()), this, SLOT(clickedDislike()) );
89 layout
->add(m_dislikeButton
);
91 m_bugButton
= new QToolButton(this, "likeback_bug");
92 m_bugButton
->setIconSet(bugIconSet
);
93 m_bugButton
->setTextLabel("<p>" + i18n("Send application developers a comment about an improper behavior of the application"));
94 m_bugButton
->setAutoRaise(true);
95 connect( m_bugButton
, SIGNAL(clicked()), this, SLOT(clickedBug()) );
96 layout
->add(m_bugButton
);
98 m_featureButton
= new QToolButton(this, "likeback_feature");
99 m_featureButton
->setIconSet(featureIconSet
);
100 m_featureButton
->setTextLabel("<p>" + i18n("Send application developers a comment about a new feature you desire"));
101 m_featureButton
->setAutoRaise(true);
102 connect( m_featureButton
, SIGNAL(clicked()), this, SLOT(clickedFeature()) );
103 layout
->add(m_featureButton
);
105 connect( &m_timer
, SIGNAL(timeout()), this, SLOT(autoMove()) );
107 LikeBack::Button buttons
= likeBack
->buttons();
108 m_likeButton
->setShown( buttons
& LikeBack::Like
);
109 m_dislikeButton
->setShown( buttons
& LikeBack::Dislike
);
110 m_bugButton
->setShown( buttons
& LikeBack::Bug
);
111 m_featureButton
->setShown( buttons
& LikeBack::Feature
);
114 LikeBackBar::~LikeBackBar()
118 void LikeBackBar::startTimer()
123 void LikeBackBar::stopTimer()
128 void LikeBackBar::autoMove()
130 static QWidget
*lastWindow
= 0;
132 QWidget
*window
= kapp
->activeWindow();
133 // When a Kicker applet has the focus, like the Commandline QLineEdit,
134 // the systemtray icon indicates to be the current window and the LikeBack is shown next to the system tray icon.
135 // It's obviously bad ;-) :
136 bool shouldShow
= (m_likeBack
->userWantsToShowBar() && m_likeBack
->enabledBar() && window
&& !window
->inherits("KSystemTray"));
138 //move(window->x() + window->width() - 100 - width(), window->y());
139 //move(window->x() + window->width() - 100 - width(), window->mapToGlobal(QPoint(0, 0)).y() - height());
140 move(window
->mapToGlobal(QPoint(0, 0)).x() + window
->width() - width(), window
->mapToGlobal(QPoint(0, 0)).y() + 1);
142 if (window
!= lastWindow
&& m_likeBack
->windowNamesListing() != LikeBack::NoListing
) {
143 if (qstricmp(window
->name(), "") == 0 || qstricmp(window
->name(), "unnamed") == 0) {
144 std::cout
<< "===== LikeBack ===== UNNAMED ACTIVE WINDOW OF TYPE " << window
->className() << " ======" << LikeBack::activeWindowPath() << std::endl
;
145 } else if (m_likeBack
->windowNamesListing() == LikeBack::AllWindows
) {
146 std::cout
<< "LikeBack: Active Window: " << LikeBack::activeWindowPath() << std::endl
;
152 // Show or hide the bar accordingly:
153 if (shouldShow
&& !isShown()) {
155 } else if (!shouldShow
&& isShown()) {
160 void LikeBackBar::clickedLike()
162 m_likeBack
->execCommentDialog(LikeBack::Like
);
165 void LikeBackBar::clickedDislike()
167 m_likeBack
->execCommentDialog(LikeBack::Dislike
);
170 void LikeBackBar::clickedBug()
172 m_likeBack
->execCommentDialog(LikeBack::Bug
);
175 void LikeBackBar::clickedFeature()
177 m_likeBack
->execCommentDialog(LikeBack::Feature
);
180 /********************************************/
181 /********** class LikeBackPrivate: **********/
182 /********************************************/
184 LikeBackPrivate::LikeBackPrivate()
188 , buttons(LikeBack::DefaultButtons
)
193 , acceptedLanguagesMessage()
194 , windowListing(LikeBack::NoListing
)
202 LikeBackPrivate::~LikeBackPrivate()
211 /*************************************/
212 /********** class LikeBack: **********/
213 /*************************************/
215 LikeBack::LikeBack(Button buttons
, bool showBarByDefault
, KConfig
*config
, const KAboutData
*aboutData
)
218 // Initialize properties (1/2):
219 d
= new LikeBackPrivate();
220 d
->buttons
= buttons
;
222 d
->aboutData
= aboutData
;
223 d
->showBarByDefault
= showBarByDefault
;
225 // Use default KApplication config and aboutData if not provided:
227 d
->config
= kapp
->config();
228 if (d
->aboutData
== 0)
229 d
->aboutData
= kapp
->aboutData();
231 // Initialize properties (2/2) [Needs aboutData to be set]:
232 d
->showBar
= userWantsToShowBar();
234 // Fetch the KControl user email address as a default one:
235 if (!emailAddressAlreadyProvided())
238 // Initialize the button-bar:
239 d
->bar
= new LikeBackBar(this);
240 d
->bar
->resize(d
->bar
->sizeHint());
242 // Show the information message if it is the first time, and if the button-bar is shown:
243 static const char *messageShown
= "LikeBack_starting_information";
244 if (d
->showBar
&& KMessageBox::shouldBeShownContinue(messageShown
)) {
245 showInformationMessage();
246 KMessageBox::saveDontShowAgainContinue(messageShown
);
249 // Show the bar if that's wanted by the developer or the user:
251 QTimer::singleShot( 0, d
->bar
, SLOT(startTimer()) );
255 // Alex: Oh, it drove me nuts
256 d
->buttons
= (Button
) ( 0); showInformationMessage();
257 d
->buttons
= (Button
) ( Feature
); showInformationMessage();
258 d
->buttons
= (Button
) ( Bug
); showInformationMessage();
259 d
->buttons
= (Button
) ( Bug
| Feature
); showInformationMessage();
260 d
->buttons
= (Button
) ( Dislike
); showInformationMessage();
261 d
->buttons
= (Button
) ( Dislike
| Feature
); showInformationMessage();
262 d
->buttons
= (Button
) ( Dislike
| Bug
); showInformationMessage();
263 d
->buttons
= (Button
) ( Dislike
| Bug
| Feature
); showInformationMessage();
264 d
->buttons
= (Button
) (Like
); showInformationMessage();
265 d
->buttons
= (Button
) (Like
| Feature
); showInformationMessage();
266 d
->buttons
= (Button
) (Like
| Bug
); showInformationMessage();
267 d
->buttons
= (Button
) (Like
| Bug
| Feature
); showInformationMessage();
268 d
->buttons
= (Button
) (Like
| Dislike
); showInformationMessage();
269 d
->buttons
= (Button
) (Like
| Dislike
| Feature
); showInformationMessage();
270 d
->buttons
= (Button
) (Like
| Dislike
| Bug
); showInformationMessage();
271 d
->buttons
= (Button
) (Like
| Dislike
| Bug
| Feature
); showInformationMessage();
276 LikeBack::~LikeBack()
281 void LikeBack::setWindowNamesListing(WindowListing windowListing
)
283 d
->windowListing
= windowListing
;
286 LikeBack::WindowListing
LikeBack::windowNamesListing()
288 return d
->windowListing
;
291 void LikeBack::setAcceptedLanguages(const QStringList
&locales
, const QString
&message
)
293 d
->acceptedLocales
= locales
;
294 d
->acceptedLanguagesMessage
= message
;
297 QStringList
LikeBack::acceptedLocales()
299 return d
->acceptedLocales
;
302 QString
LikeBack::acceptedLanguagesMessage()
304 return d
->acceptedLanguagesMessage
;
307 void LikeBack::setServer(const QString
&hostName
, const QString
&remotePath
, Q_UINT16 hostPort
)
309 d
->hostName
= hostName
;
310 d
->remotePath
= remotePath
;
311 d
->hostPort
= hostPort
;
314 QString
LikeBack::hostName()
319 QString
LikeBack::remotePath()
321 return d
->remotePath
;
324 Q_UINT16
LikeBack::hostPort()
329 void LikeBack::disableBar()
332 if (d
->bar
&& d
->disabledCount
> 0) {
338 void LikeBack::enableBar()
341 if (d
->disabledCount
< 0)
342 std::cerr
<< "===== LikeBack ===== Enabled more times than it was disabled. Please refer to the disableBar() documentation for more information and hints." << std::endl
;
343 if (d
->bar
&& d
->disabledCount
<= 0) {
344 d
->bar
->startTimer();
348 bool LikeBack::enabledBar()
350 return d
->disabledCount
<= 0;
353 void LikeBack::execCommentDialog(Button type
, const QString
&initialComment
, const QString
&windowPath
, const QString
&context
)
356 LikeBackDialog
dialog(type
, initialComment
, windowPath
, context
, this);
361 void LikeBack::execCommentDialogFromHelp()
363 execCommentDialog(AllButtons
, /*initialComment=*/"", /*windowPath=*/"HelpMenuAction");
366 LikeBack::Button
LikeBack::buttons()
371 const KAboutData
* LikeBack::aboutData()
376 KConfig
* LikeBack::config()
381 KAction
* LikeBack::sendACommentAction(KActionCollection
*parent
)
384 d
->action
= new KAction(
385 i18n("&Send a Comment to Developers"), /*icon=*/"mail_new", /*shortcut=*/"",
386 this, SLOT(execCommentDialog()),
387 parent
, "likeback_send_a_comment"
393 bool LikeBack::userWantsToShowBar()
395 // Store the button-bar per version, so it can be disabled by the developer for the final version:
396 d
->config
->setGroup("LikeBack");
397 return d
->config
->readBoolEntry("userWantToShowBarForVersion_" + d
->aboutData
->version(), d
->showBarByDefault
);
400 void LikeBack::setUserWantsToShowBar(bool showBar
)
402 if (showBar
== d
->showBar
)
405 d
->showBar
= showBar
;
407 // Store the button-bar per version, so it can be disabled by the developer for the final version:
408 d
->config
->setGroup("LikeBack");
409 d
->config
->writeEntry("userWantToShowBarForVersion_" + d
->aboutData
->version(), showBar
);
410 d
->config
->sync(); // Make sure the option is saved, even if the application crashes after that.
413 d
->bar
->startTimer();
416 void LikeBack::showInformationMessage()
418 // Load and register the images needed by the message:
419 QPixmap likeIcon
= kapp
->iconLoader()->loadIcon("likeback_like", KIcon::Small
);
420 QPixmap dislikeIcon
= kapp
->iconLoader()->loadIcon("likeback_dislike", KIcon::Small
);
421 QPixmap bugIcon
= kapp
->iconLoader()->loadIcon("likeback_bug", KIcon::Small
);
422 QPixmap featureIcon
= kapp
->iconLoader()->loadIcon("likeback_feature", KIcon::Small
);
423 Q3MimeSourceFactory::defaultFactory()->setPixmap("likeback_icon_like", likeIcon
);
424 Q3MimeSourceFactory::defaultFactory()->setPixmap("likeback_icon_dislike", dislikeIcon
);
425 Q3MimeSourceFactory::defaultFactory()->setPixmap("likeback_icon_bug", bugIcon
);
426 Q3MimeSourceFactory::defaultFactory()->setPixmap("likeback_icon_feature", featureIcon
);
428 // Show a message reflecting the allowed types of comment:
429 Button buttons
= d
->buttons
;
430 int nbButtons
= (buttons
& Like
? 1 : 0) +
431 (buttons
& Dislike
? 1 : 0) +
432 (buttons
& Bug
? 1 : 0) +
433 (buttons
& Feature
? 1 : 0);
434 KMessageBox::information(0,
435 "<p><b>" + (isDevelopmentVersion(d
->aboutData
->version()) ?
436 i18n("Welcome to this testing version of %1.") :
437 i18n("Welcome to %1.")
438 ).arg(d
->aboutData
->programName()) + "</b></p>"
439 "<p>" + i18n("To help us improve it, your comments are important.") + "</p>"
441 ((buttons
& LikeBack::Like
) && (buttons
& LikeBack::Dislike
) ?
442 i18n("Each time you have a great or frustrating experience, "
443 "please click the appropriate face below the window title-bar, "
444 "briefly describe what you like or dislike and click Send.")
445 : (buttons
& LikeBack::Like
?
446 i18n("Each time you have a great experience, "
447 "please click the smiling face below the window title-bar, "
448 "briefly describe what you like and click Send.")
449 : (buttons
& LikeBack::Dislike
?
450 i18n("Each time you have a frustrating experience, "
451 "please click the frowning face below the window title-bar, "
452 "briefly describe what you dislike and click Send.")
456 (buttons
& LikeBack::Bug
?
458 (buttons
& (LikeBack::Like
| LikeBack::Dislike
) ?
459 i18n("Follow the same principle to quickly report a bug: "
460 "just click the broken-object icon in the top-right corner of the window, describe it and click Send.")
462 i18n("Each time you discover a bug in the application, "
463 "please click the broken-object icon below the window title-bar, "
464 "briefly describe the mis-behaviour and click Send.")
467 "<p>" + i18n("Example:", "Examples:", nbButtons
) + "</p>" +
468 (buttons
& LikeBack::Like
?
469 "<p><img source=\"likeback_icon_like\"> " +
470 i18n("<b>I like</b> the new artwork. Very refreshing.") + "</p>"
472 (buttons
& LikeBack::Dislike
?
473 "<p><img source=\"likeback_icon_dislike\"> " +
474 i18n("<b>I dislike</b> the welcome page of that assistant. Too time consuming.") + "</p>"
476 (buttons
& LikeBack::Bug
?
477 "<p><img source=\"likeback_icon_bug\"> " +
478 i18n("<b>The application has an improper behaviour</b> when clicking the Add button. Nothing happens.") + "</p>"
480 (buttons
& LikeBack::Feature
?
481 "<p><img source=\"likeback_icon_feature\"> " +
482 i18n("<b>I desire a new feature</b> allowing me to send my work by email.") + "</p>"
485 i18n("Help Improve the Application"));
487 // Reset the images from the factory:
488 Q3MimeSourceFactory::defaultFactory()->setData("likeback_icon_like", 0L);
489 Q3MimeSourceFactory::defaultFactory()->setData("likeback_icon_dislike", 0L);
490 Q3MimeSourceFactory::defaultFactory()->setData("likeback_icon_bug", 0L);
491 Q3MimeSourceFactory::defaultFactory()->setData("likeback_icon_feature", 0L);
494 QString
LikeBack::activeWindowPath()
496 // Compute the window hierarchy (from the latest to the oldest):
497 QStringList windowNames
;
498 QWidget
*window
= kapp
->activeWindow();
500 QString name
= window
->name();
501 // Append the class name to the window name if it is unnamed:
502 if (name
== "unnamed")
503 name
+= QString(":") + window
->className();
504 windowNames
.append(name
);
505 window
= dynamic_cast<QWidget
*>(window
->parent());
508 // Create the string of windows starting by the end (from the oldest to the latest):
510 for (int i
= ((int)windowNames
.count()) - 1; i
>= 0; i
--) {
511 if (windowPath
.isEmpty())
512 windowPath
= windowNames
[i
];
514 windowPath
+= QString("~~") + windowNames
[i
];
517 // Finally return the computed path:
521 bool LikeBack::emailAddressAlreadyProvided()
523 d
->config
->setGroup("LikeBack");
524 return d
->config
->readBoolEntry("emailAlreadyAsked", false);
527 QString
LikeBack::emailAddress()
529 if (!emailAddressAlreadyProvided())
532 d
->config
->setGroup("LikeBack");
533 return d
->config
->readEntry("emailAddress", "");
536 void LikeBack::setEmailAddress(const QString
&address
, bool userProvided
)
538 d
->config
->setGroup("LikeBack");
539 d
->config
->writeEntry("emailAddress", address
);
540 d
->config
->writeEntry("emailAlreadyAsked", userProvided
|| emailAddressAlreadyProvided());
541 d
->config
->sync(); // Make sure the option is saved, even if the application crashes after that.
544 void LikeBack::askEmailAddress()
546 d
->config
->setGroup("LikeBack");
548 QString currentEmailAddress
= d
->config
->readEntry("emailAddress", "");
549 if (!emailAddressAlreadyProvided() && !d
->fetchedEmail
.isEmpty())
550 currentEmailAddress
= d
->fetchedEmail
;
554 QString emailExpString
= "[\\w-\\.]+@[\\w-\\.]+\\.[\\w]+";
555 //QString namedEmailExpString = "[.]*[ \\t]+<" + emailExpString + '>';
556 //QRegExp emailExp("^(|" + emailExpString + '|' + namedEmailExpString + ")$");
557 QRegExp
emailExp("^(|" + emailExpString
+ ")$");
558 QRegExpValidator
emailValidator(emailExp
, this);
561 QString email
= KInputDialog::getText(
562 i18n("Email Address"),
563 "<p><b>" + i18n("Please provide your email address.") + "</b></p>" +
564 "<p>" + i18n("It will only be used to contact you back if more information is needed about your comments, ask you how to reproduce the bugs you report, send bug corrections for you to test, etc.") + "</p>" +
565 "<p>" + i18n("The email address is optional. If you do not provide any, your comments will be sent anonymously.") + "</p>",
566 currentEmailAddress
, &ok
, kapp
->activeWindow(), /*name=*/(const char*)0, &emailValidator
);
570 setEmailAddress(email
);
573 // FIXME: Should be moved to KAboutData? Cigogne will also need it.
574 bool LikeBack::isDevelopmentVersion(const QString
&version
)
576 return version
.find("alpha", /*index=*/0, /*caseSensitive=*/false) != -1 ||
577 version
.find("beta", /*index=*/0, /*caseSensitive=*/false) != -1 ||
578 version
.find("rc", /*index=*/0, /*caseSensitive=*/false) != -1 ||
579 version
.find("svn", /*index=*/0, /*caseSensitive=*/false) != -1 ||
580 version
.find("cvs", /*index=*/0, /*caseSensitive=*/false) != -1;
584 * Code from KBugReport::slotConfigureEmail() in kdeui/kbugreport.cpp:
586 /*void LikeBack::beginFetchingEmail()
590 m_process = new KProcess();
591 *m_process << QString::fromLatin1("kcmshell") << QString::fromLatin1("kcm_useraccount");
592 connect( m_process, SIGNAL(processExited(KProcess*)), SLOT(fetchUserEmail()) );
593 if (!m_process->start()) {
594 kdDebug() << "Couldn't start kcmshell.." << endl;
599 // m_configureEmail->setEnabled(false);
603 * Code from KBugReport::slotSetFrom() in kdeui/kbugreport.cpp:
605 void LikeBack::fetchUserEmail()
609 // m_configureEmail->setEnabled(true);
611 // ### KDE4: why oh why is KEmailSettings in kio?
612 KConfig
emailConf( QString::fromLatin1("emaildefaults") );
614 // find out the default profile
615 emailConf
.setGroup(QString::fromLatin1("Defaults"));
616 QString profile
= QString::fromLatin1("PROFILE_");
617 profile
+= emailConf
.readEntry(QString::fromLatin1("Profile"), QString::fromLatin1("Default"));
619 emailConf
.setGroup(profile
);
620 QString fromaddr
= emailConf
.readEntry(QString::fromLatin1("EmailAddress"));
621 if (fromaddr
.isEmpty()) {
623 p
= getpwuid(getuid());
624 d
->fetchedEmail
= QString::fromLatin1(p
->pw_name
);
626 QString name
= emailConf
.readEntry(QString::fromLatin1("FullName"));
628 d
->fetchedEmail
= /*name + QString::fromLatin1(" <") +*/ fromaddr
/*+ QString::fromLatin1(">")*/;
630 // m_from->setText( fromaddr );
633 /*******************************************/
634 /********** class LikeBackDialog: **********/
635 /*******************************************/
637 LikeBackDialog::LikeBackDialog(LikeBack::Button reason
, const QString
&initialComment
, const QString
&windowPath
, const QString
&context
, LikeBack
*likeBack
)
638 : KDialogBase(KDialogBase::Swallow
, i18n("Send a Comment to Developers"), KDialogBase::Ok
| KDialogBase::Cancel
| KDialogBase::Default
,
639 KDialogBase::Ok
, kapp
->activeWindow(), /*name=*/"_likeback_feedback_window_", /*modal=*/true, /*separator=*/true)
640 , m_likeBack(likeBack
)
641 , m_windowPath(windowPath
)
644 // If no specific "reason" is provided, choose the first one:
645 if (reason
== LikeBack::AllButtons
) {
646 LikeBack::Button buttons
= m_likeBack
->buttons();
648 if (firstButton
== 0 && (buttons
& LikeBack::Like
)) firstButton
= LikeBack::Like
;
649 if (firstButton
== 0 && (buttons
& LikeBack::Dislike
)) firstButton
= LikeBack::Dislike
;
650 if (firstButton
== 0 && (buttons
& LikeBack::Bug
)) firstButton
= LikeBack::Bug
;
651 if (firstButton
== 0 && (buttons
& LikeBack::Feature
)) firstButton
= LikeBack::Feature
;
652 reason
= (LikeBack::Button
) firstButton
;
655 // If no window path is provided, get the current active window path:
656 if (m_windowPath
.isEmpty())
657 m_windowPath
= LikeBack::activeWindowPath();
659 QWidget
*page
= new QWidget(this);
660 Q3VBoxLayout
*pageLayout
= new Q3VBoxLayout(page
, /*margin=*/0, spacingHint());
662 // The introduction message:
663 QLabel
*introduction
= new QLabel(introductionText(), page
);
664 pageLayout
->addWidget(introduction
);
666 // The comment group:
667 m_group
= new Q3ButtonGroup(0);//i18n("Send Application Developers a Comment About:"), page);
668 Q3VGroupBox
*box
= new Q3VGroupBox(i18n("Send Application Developers a Comment About:"), page
);
669 pageLayout
->addWidget(box
);
671 // The radio buttons:
672 QWidget
*buttons
= new QWidget(box
);
673 Q3GridLayout
*buttonsGrid
= new Q3GridLayout(buttons
, /*nbRows=*/4, /*nbColumns=*/2, /*margin=*/0, spacingHint());
674 if (m_likeBack
->buttons() & LikeBack::Like
) {
675 QPixmap likePixmap
= kapp
->iconLoader()->loadIcon("likeback_like", KIcon::NoGroup
, 16, KIcon::DefaultState
, 0L, true);
676 QLabel
*likeIcon
= new QLabel(buttons
);
677 likeIcon
->setPixmap(likePixmap
);
678 likeIcon
->setSizePolicy(QSizePolicy::Fixed
, QSizePolicy::Fixed
);
679 QRadioButton
*likeButton
= new QRadioButton(i18n("Something you &like"), buttons
);
680 buttonsGrid
->addWidget(likeIcon
, /*row=*/0, /*column=*/0);
681 buttonsGrid
->addWidget(likeButton
, /*row=*/0, /*column=*/1);
682 m_group
->insert(likeButton
, LikeBack::Like
);
684 if (m_likeBack
->buttons() & LikeBack::Dislike
) {
685 QPixmap dislikePixmap
= kapp
->iconLoader()->loadIcon("likeback_dislike", KIcon::NoGroup
, 16, KIcon::DefaultState
, 0L, true);
686 QLabel
*dislikeIcon
= new QLabel(buttons
);
687 dislikeIcon
->setPixmap(dislikePixmap
);
688 dislikeIcon
->setSizePolicy(QSizePolicy::Fixed
, QSizePolicy::Fixed
);
689 QRadioButton
*dislikeButton
= new QRadioButton(i18n("Something you &dislike"), buttons
);
690 buttonsGrid
->addWidget(dislikeIcon
, /*row=*/1, /*column=*/0);
691 buttonsGrid
->addWidget(dislikeButton
, /*row=*/1, /*column=*/1);
692 m_group
->insert(dislikeButton
, LikeBack::Dislike
);
694 if (m_likeBack
->buttons() & LikeBack::Bug
) {
695 QPixmap bugPixmap
= kapp
->iconLoader()->loadIcon("likeback_bug", KIcon::NoGroup
, 16, KIcon::DefaultState
, 0L, true);
696 QLabel
*bugIcon
= new QLabel(buttons
);
697 bugIcon
->setPixmap(bugPixmap
);
698 bugIcon
->setSizePolicy(QSizePolicy::Fixed
, QSizePolicy::Fixed
);
699 QRadioButton
*bugButton
= new QRadioButton(i18n("An improper &behavior of this application"), buttons
);
700 buttonsGrid
->addWidget(bugIcon
, /*row=*/2, /*column=*/0);
701 buttonsGrid
->addWidget(bugButton
, /*row=*/2, /*column=*/1);
702 m_group
->insert(bugButton
, LikeBack::Bug
);
704 if (m_likeBack
->buttons() & LikeBack::Feature
) {
705 QPixmap featurePixmap
= kapp
->iconLoader()->loadIcon("likeback_feature", KIcon::NoGroup
, 16, KIcon::DefaultState
, 0L, true);
706 QLabel
*featureIcon
= new QLabel(buttons
);
707 featureIcon
->setPixmap(featurePixmap
);
708 featureIcon
->setSizePolicy(QSizePolicy::Fixed
, QSizePolicy::Fixed
);
709 QRadioButton
*featureButton
= new QRadioButton(i18n("A new &feature you desire"), buttons
);
710 buttonsGrid
->addWidget(featureIcon
, /*row=*/3, /*column=*/0);
711 buttonsGrid
->addWidget(featureButton
, /*row=*/3, /*column=*/1);
712 m_group
->insert(featureButton
, LikeBack::Feature
);
714 m_group
->setButton(reason
);
716 // The comment text box:
717 m_comment
= new Q3TextEdit(box
);
718 m_comment
->setTabChangesFocus(true);
719 m_comment
->setTextFormat(Q3TextEdit::PlainText
);
720 m_comment
->setText(initialComment
);
722 m_showButtons
= new QCheckBox(i18n("Show comment buttons below &window titlebars"), page
);
723 m_showButtons
->setChecked(m_likeBack
->userWantsToShowBar());
724 pageLayout
->addWidget(m_showButtons
);
725 connect( m_showButtons
, SIGNAL(stateChanged(int)), this, SLOT(changeButtonBarVisible()) );
727 setButtonOK(KGuiItem(i18n("&Send Comment"), "mail_send"));
728 enableButtonOK(false);
729 connect( m_comment
, SIGNAL(textChanged()), this, SLOT(commentChanged()) );
731 setButtonGuiItem(Default
, KGuiItem(i18n("&Email Address..."), "mail_generic"));
733 resize(QSize(kapp
->desktop()->width() * 1 / 2, kapp
->desktop()->height() * 3 / 5).expandedTo(sizeHint()));
735 QAction
*sendShortcut
= new QAction(this);
736 sendShortcut
->setAccel(QString("Ctrl+Return"));
737 connect( sendShortcut
, SIGNAL(activated()), actionButton(Ok
), SLOT(animateClick()) );
742 LikeBackDialog::~LikeBackDialog()
746 QString
LikeBackDialog::introductionText()
748 QString text
= "<p>" + i18n("Please provide a brief description of your opinion of %1.").arg(m_likeBack
->aboutData()->programName()) + " ";
750 QString languagesMessage
= "";
751 if (!m_likeBack
->acceptedLocales().isEmpty() && !m_likeBack
->acceptedLanguagesMessage().isEmpty()) {
752 languagesMessage
= m_likeBack
->acceptedLanguagesMessage();
753 QStringList locales
= m_likeBack
->acceptedLocales();
754 for (QStringList::Iterator it
= locales
.begin(); it
!= locales
.end(); ++it
) {
755 QString locale
= *it
;
756 if (KGlobal::locale()->language().startsWith(locale
))
757 languagesMessage
= "";
760 if (!KGlobal::locale()->language().startsWith("en"))
761 languagesMessage
= i18n("Please write in English.");
764 if (!languagesMessage
.isEmpty())
765 // TODO: Replace the URL with a localized one:
766 text
+= languagesMessage
+ " " +
767 i18n("You may be able to use an <a href=\"%1\">online translation tool</a>.")
768 .arg("http://www.google.com/language_tools?hl=" + KGlobal::locale()->language())
771 // If both "I Like" and "I Dislike" buttons are shown and one is clicked:
772 if ((m_likeBack
->buttons() & LikeBack::Like
) && (m_likeBack
->buttons() & LikeBack::Dislike
))
773 text
+= i18n("To make the comments you send more useful in improving this application, try to send the same amount of positive and negative comments.") + " ";
775 if (!(m_likeBack
->buttons() & LikeBack::Feature
))
776 text
+= i18n("Do <b>not</b> ask for new features: your requests will be ignored.");
781 void LikeBackDialog::polish()
783 KDialogBase::polish();
784 m_comment
->setFocus();
787 void LikeBackDialog::slotDefault()
789 m_likeBack
->askEmailAddress();
792 void LikeBackDialog::slotOk()
797 void LikeBackDialog::changeButtonBarVisible()
799 m_likeBack
->setUserWantsToShowBar(m_showButtons
->isChecked());
802 void LikeBackDialog::commentChanged()
804 QPushButton
*sendButton
= actionButton(Ok
);
805 sendButton
->setEnabled(!m_comment
->text().isEmpty());
808 void LikeBackDialog::send()
810 QString emailAddress
= m_likeBack
->emailAddress();
812 int reason
= m_group
->selectedId();
813 QString type
= (reason
== LikeBack::Like
? "Like" : (reason
== LikeBack::Dislike
? "Dislike" : (reason
== LikeBack::Bug
? "Bug" : "Feature")));
815 "protocol=" + KURL::encode_string("1.0") + '&' +
816 "type=" + KURL::encode_string(type
) + '&' +
817 "version=" + KURL::encode_string(m_likeBack
->aboutData()->version()) + '&' +
818 "locale=" + KURL::encode_string(KGlobal::locale()->language()) + '&' +
819 "window=" + KURL::encode_string(m_windowPath
) + '&' +
820 "context=" + KURL::encode_string(m_context
) + '&' +
821 "comment=" + KURL::encode_string(m_comment
->text()) + '&' +
822 "email=" + KURL::encode_string(emailAddress
);
823 Q3Http
*http
= new Q3Http(m_likeBack
->hostName(), m_likeBack
->hostPort());
825 std::cout
<< "http://" << m_likeBack
->hostName() << ":" << m_likeBack
->hostPort() << m_likeBack
->remotePath() << std::endl
;
826 std::cout
<< data
<< std::endl
;
827 connect( http
, SIGNAL(requestFinished(int, bool)), this, SLOT(requestFinished(int, bool)) );
829 Q3HttpRequestHeader
header("POST", m_likeBack
->remotePath());
830 header
.setValue("Host", m_likeBack
->hostName());
831 header
.setValue("Content-Type", "application/x-www-form-urlencoded");
832 http
->setHost(m_likeBack
->hostName());
833 http
->request(header
, data
.utf8());
835 m_comment
->setEnabled(false);
838 void LikeBackDialog::requestFinished(int /*id*/, bool error
)
840 // TODO: Save to file if error (connection not present at the moment)
841 m_comment
->setEnabled(true);
842 m_likeBack
->disableBar();
844 KMessageBox::error(this, i18n("<p>Error while trying to send the report.</p><p>Please retry later.</p>"), i18n("Transfer Error"));
846 KMessageBox::information(
848 i18n("<p>Your comment has been sent successfully. It will help improve the application.</p><p>Thanks for your time.</p>"),
853 m_likeBack
->enableBar();
855 KDialogBase::slotOk();
858 #include "likeback_private.moc.cpp"
859 #include "likeback.moc"