KParts
browseropenorsavequestion.cpp
Go to the documentation of this file.
00001 /* 00002 Copyright (c) 2009, 2010 David Faure <faure@kde.org> 00003 00004 This library is free software; you can redistribute it and/or modify 00005 it under the terms of the GNU Lesser General Public License as published by 00006 the Free Software Foundation; either version 2 of the License or ( at 00007 your option ) version 3 or, at the discretion of KDE e.V. ( which shall 00008 act as a proxy as in section 14 of the GPLv3 ), any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Lesser General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "browseropenorsavequestion.h" 00022 #include <kdebug.h> 00023 #include <kaction.h> 00024 #include <kfileitemactions.h> 00025 #include <kpushbutton.h> 00026 #include <kmenu.h> 00027 #include <ksqueezedtextlabel.h> 00028 #include <knotification.h> 00029 #include <kdialog.h> 00030 #include <kmimetypetrader.h> 00031 #include <kstandardguiitem.h> 00032 #include <kguiitem.h> 00033 #include <kmessagebox.h> 00034 #include <kmimetype.h> 00035 #include <QStyle> 00036 #include <QStyleOption> 00037 #include <QVBoxLayout> 00038 #include <QCheckBox> 00039 #include <QLabel> 00040 00041 using namespace KParts; 00042 Q_DECLARE_METATYPE(KService::Ptr) 00043 00044 class KParts::BrowserOpenOrSaveQuestionPrivate : public KDialog 00045 { 00046 Q_OBJECT 00047 public: 00048 // Mapping to KDialog button codes 00049 static const KDialog::ButtonCode Save = KDialog::Yes; 00050 static const KDialog::ButtonCode OpenDefault = KDialog::User2; 00051 static const KDialog::ButtonCode OpenWith = KDialog::User1; 00052 static const KDialog::ButtonCode Cancel = KDialog::Cancel; 00053 00054 BrowserOpenOrSaveQuestionPrivate(QWidget* parent, const KUrl& url, const QString& mimeType) 00055 : KDialog(parent), url(url), mimeType(mimeType), 00056 features(0) 00057 { 00058 // Use askSave or askEmbedOrSave from filetypesrc 00059 dontAskConfig = KSharedConfig::openConfig("filetypesrc", KConfig::NoGlobals); 00060 00061 setCaption(url.host()); 00062 setButtons(Save | OpenDefault | OpenWith | Cancel); 00063 setObjectName("questionYesNoCancel"); 00064 setButtonGuiItem(Save, KStandardGuiItem::saveAs()); 00065 setButtonGuiItem(Cancel, KStandardGuiItem::cancel()); 00066 setDefaultButton(Save); 00067 00068 QVBoxLayout *mainLayout = new QVBoxLayout(mainWidget()); 00069 mainLayout->setSpacing(KDialog::spacingHint() * 2); // provide extra spacing 00070 mainLayout->setMargin(0); 00071 00072 QHBoxLayout *hLayout = new QHBoxLayout(); 00073 hLayout->setMargin(0); 00074 hLayout->setSpacing(-1); // use default spacing 00075 mainLayout->addLayout(hLayout, 5); 00076 00077 QLabel *iconLabel = new QLabel(mainWidget()); 00078 QStyleOption option; 00079 option.initFrom(this); 00080 KIcon icon("dialog-information"); 00081 iconLabel->setPixmap(icon.pixmap(style()->pixelMetric(QStyle::PM_MessageBoxIconSize, &option, this))); 00082 00083 hLayout->addWidget(iconLabel, 0, Qt::AlignCenter); 00084 hLayout->addSpacing(KDialog::spacingHint()); 00085 00086 QVBoxLayout* textVLayout = new QVBoxLayout; 00087 questionLabel = new KSqueezedTextLabel(mainWidget()); 00088 textVLayout->addWidget(questionLabel); 00089 00090 fileNameLabel = new QLabel(mainWidget()); 00091 fileNameLabel->hide(); 00092 textVLayout->addWidget(fileNameLabel); 00093 00094 mime = KMimeType::mimeType(mimeType, KMimeType::ResolveAliases); 00095 QString mimeDescription = mimeType; 00096 if (mime) { // The mime-type is known so display the comment instead of mime-type 00097 mimeDescription = mime->comment(); 00098 } 00099 QLabel* mimeTypeLabel = new QLabel(mainWidget()); 00100 mimeTypeLabel->setText(i18nc("@label Type of file", "Type: %1", mimeDescription)); 00101 textVLayout->addWidget(mimeTypeLabel); 00102 00103 hLayout->addLayout(textVLayout,5); 00104 00105 mainLayout->addStretch(15); 00106 dontAskAgainCheckBox = new QCheckBox(mainWidget()); 00107 dontAskAgainCheckBox->setText(i18nc("@label:checkbox", "Remember action for files of this type")); 00108 mainLayout->addWidget(dontAskAgainCheckBox); 00109 } 00110 00111 bool autoEmbedMimeType(int flags); 00112 00113 int executeDialog(const QString& dontShowAgainName) 00114 { 00115 KConfigGroup cg(dontAskConfig, "Notification Messages"); // group name comes from KMessageBox 00116 const QString dontAsk = cg.readEntry(dontShowAgainName, QString()).toLower(); 00117 if (dontAsk == "yes" || dontAsk == "true") { 00118 return Save; 00119 } else if (dontAsk == "no" || dontAsk == "false") { 00120 return OpenDefault; 00121 } 00122 00123 KNotification::event("messageQuestion", // why does KMessageBox uses Information for questionYesNoCancel? 00124 questionLabel->text(), // also include mimetype? 00125 QPixmap(), 00126 window()); 00127 const int result = exec(); 00128 00129 if (dontAskAgainCheckBox->isChecked()) { 00130 cg.writeEntry(dontShowAgainName, result == Save); 00131 cg.sync(); 00132 } 00133 return result; 00134 } 00135 00136 void showService(KService::Ptr selectedService) 00137 { 00138 KGuiItem openItem(i18nc("@label:button", "&Open with %1", selectedService->name()), selectedService->icon()); 00139 setButtonGuiItem(OpenWith, openItem); 00140 } 00141 00142 KUrl url; 00143 QString mimeType; 00144 KMimeType::Ptr mime; 00145 KService::Ptr selectedService; 00146 KSqueezedTextLabel* questionLabel; 00147 BrowserOpenOrSaveQuestion::Features features; 00148 QLabel* fileNameLabel; 00149 00150 protected: 00151 virtual void slotButtonClicked(int buttonId) 00152 { 00153 if (buttonId != OpenDefault) 00154 selectedService = 0; 00155 KPushButton* button = KDialog::button(KDialog::ButtonCode(buttonId)); 00156 if (button && !button->menu()) { 00157 done(buttonId); 00158 } 00159 } 00160 private: 00161 QCheckBox* dontAskAgainCheckBox; 00162 KSharedConfig::Ptr dontAskConfig; 00163 00164 public Q_SLOTS: 00165 void slotAppSelected(QAction* action) 00166 { 00167 selectedService = action->data().value<KService::Ptr>(); 00168 //showService(selectedService); 00169 done(OpenDefault); 00170 } 00171 }; 00172 00173 00174 BrowserOpenOrSaveQuestion::BrowserOpenOrSaveQuestion(QWidget* parent, const KUrl& url, const QString& mimeType) 00175 : d(new BrowserOpenOrSaveQuestionPrivate(parent, url, mimeType)) 00176 { 00177 } 00178 00179 BrowserOpenOrSaveQuestion::~BrowserOpenOrSaveQuestion() 00180 { 00181 delete d; 00182 } 00183 00184 static KAction* createAppAction(const KService::Ptr& service, QObject* parent) 00185 { 00186 QString actionName(service->name().replace('&', "&&")); 00187 actionName = i18nc("@action:inmenu", "Open &with %1", actionName); 00188 00189 KAction *act = new KAction(parent); 00190 act->setIcon(KIcon(service->icon())); 00191 act->setText(actionName); 00192 act->setData(QVariant::fromValue(service)); 00193 return act; 00194 } 00195 00196 BrowserOpenOrSaveQuestion::Result BrowserOpenOrSaveQuestion::askOpenOrSave() 00197 { 00198 d->questionLabel->setText(i18nc("@info", "Open '%1'?", d->url.pathOrUrl())); 00199 d->showButton(BrowserOpenOrSaveQuestionPrivate::OpenWith, false); 00200 00201 KGuiItem openWithDialogItem(i18nc("@label:button", "&Open with..."), "document-open"); 00202 00203 // I thought about using KFileItemActions, but we don't want a submenu, nor the slots.... 00204 // and we want no menu at all if there's only one offer. 00205 // TODO: we probably need a setTraderConstraint(), to exclude the current application? 00206 const KService::List apps = KFileItemActions::associatedApplications(QStringList() << d->mimeType, 00207 QString() /* TODO trader constraint */); 00208 if (apps.isEmpty()) { 00209 d->setButtonGuiItem(BrowserOpenOrSaveQuestionPrivate::OpenDefault, openWithDialogItem); 00210 } else { 00211 KService::Ptr offer = apps.first(); 00212 KGuiItem openItem(i18nc("@label:button", "&Open with %1", offer->name()), offer->icon()); 00213 d->setButtonGuiItem(BrowserOpenOrSaveQuestionPrivate::OpenDefault, openItem); 00214 if (d->features & ServiceSelection) { 00215 // OpenDefault shall use this service 00216 d->selectedService = apps.first(); 00217 d->showButton(BrowserOpenOrSaveQuestionPrivate::OpenWith, true); 00218 KMenu* menu = new KMenu(d); 00219 if (apps.count() > 1) { 00220 // Provide an additional button with a menu of associated apps 00221 KGuiItem openWithItem(i18nc("@label:button", "&Open with"), "document-open"); 00222 d->setButtonGuiItem(BrowserOpenOrSaveQuestionPrivate::OpenWith, openWithItem); 00223 d->setButtonMenu(BrowserOpenOrSaveQuestionPrivate::OpenWith, menu, KDialog::InstantPopup); 00224 QObject::connect(menu, SIGNAL(triggered(QAction*)), d, SLOT(slotAppSelected(QAction*))); 00225 for (KService::List::const_iterator it = apps.begin(); it != apps.end(); ++it) { 00226 KAction* act = createAppAction(*it, d); 00227 menu->addAction(act); 00228 } 00229 KAction* openWithDialogAction = new KAction(d); 00230 openWithDialogAction->setIcon(KIcon("document-open")); 00231 openWithDialogAction->setText(openWithDialogItem.text()); 00232 menu->addAction(openWithDialogAction); 00233 } else { 00234 // Only one associated app, already offered by the other menu -> add "Open With..." button 00235 d->setButtonGuiItem(BrowserOpenOrSaveQuestionPrivate::OpenWith, openWithDialogItem); 00236 } 00237 } else { 00238 kDebug() << "Not using new feature ServiceSelection; port the caller to BrowserOpenOrSaveQuestion::setFeature(ServiceSelection)"; 00239 //kDebug() << kBacktrace(); 00240 } 00241 } 00242 00243 // KEEP IN SYNC with kdebase/runtime/keditfiletype/filetypedetails.cpp!!! 00244 const QString dontAskAgain = QLatin1String("askSave") + d->mimeType; 00245 00246 const int choice = d->executeDialog(dontAskAgain); 00247 return choice == BrowserOpenOrSaveQuestionPrivate::Save ? Save 00248 : (choice == BrowserOpenOrSaveQuestionPrivate::Cancel ? Cancel : Open); 00249 } 00250 00251 KService::Ptr BrowserOpenOrSaveQuestion::selectedService() const 00252 { 00253 return d->selectedService; 00254 } 00255 00256 bool BrowserOpenOrSaveQuestionPrivate::autoEmbedMimeType(int flags) 00257 { 00258 // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC 00259 // NOTE: Keep this function in sync with 00260 // kdebase/runtime/keditfiletype/filetypedetails.cpp 00261 // FileTypeDetails::updateAskSave() 00262 00263 // Don't ask for: 00264 // - html (even new tabs would ask, due to about:blank!) 00265 // - dirs obviously (though not common over HTTP :), 00266 // - images (reasoning: no need to save, most of the time, because fast to see) 00267 // e.g. postscript is different, because takes longer to read, so 00268 // it's more likely that the user might want to save it. 00269 // - multipart/* ("server push", see kmultipart) 00270 // KEEP IN SYNC!!! 00271 if (flags != (int)BrowserRun::AttachmentDisposition && mime && ( 00272 mime->is("text/html") || 00273 mime->is("application/xml") || 00274 mime->is("inode/directory") || 00275 mimeType.startsWith(QLatin1String("image")) || 00276 mime->is("multipart/x-mixed-replace") || 00277 mime->is("multipart/replace"))) 00278 return true; 00279 return false; 00280 } 00281 00282 BrowserOpenOrSaveQuestion::Result BrowserOpenOrSaveQuestion::askEmbedOrSave(int flags) 00283 { 00284 if (d->autoEmbedMimeType(flags)) 00285 return Embed; 00286 00287 // don't use KStandardGuiItem::open() here which has trailing ellipsis! 00288 d->setButtonGuiItem(BrowserOpenOrSaveQuestionPrivate::OpenDefault, KGuiItem(i18nc("@label:button", "&Open"), "document-open")); 00289 d->showButton(BrowserOpenOrSaveQuestionPrivate::OpenWith, false); 00290 00291 d->questionLabel->setText(i18nc("@info", "Open '%1'?", d->url.pathOrUrl())); 00292 00293 const QString dontAskAgain = QLatin1String("askEmbedOrSave")+ d->mimeType; // KEEP IN SYNC!!! 00294 // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC 00295 00296 const int choice = d->executeDialog(dontAskAgain); 00297 return choice == BrowserOpenOrSaveQuestionPrivate::Save ? Save 00298 : (choice == BrowserOpenOrSaveQuestionPrivate::Cancel ? Cancel : Embed); 00299 } 00300 00301 void BrowserOpenOrSaveQuestion::setFeatures(Features features) 00302 { 00303 d->features = features; 00304 } 00305 00306 void BrowserOpenOrSaveQuestion::setSuggestedFileName(const QString& suggestedFileName) 00307 { 00308 if (!suggestedFileName.isEmpty()) { 00309 d->fileNameLabel->setText(i18nc("@label File name", "Name: %1", suggestedFileName)); 00310 d->fileNameLabel->setWhatsThis(i18nc("@info:whatsthis", "This is the file name suggested by the server")); 00311 d->fileNameLabel->show(); 00312 } 00313 } 00314 00315 #include "browseropenorsavequestion.moc"
KDE 4.6 API Reference