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 mimeTypeLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); 00102 textVLayout->addWidget(mimeTypeLabel); 00103 00104 hLayout->addLayout(textVLayout,5); 00105 00106 mainLayout->addStretch(15); 00107 dontAskAgainCheckBox = new QCheckBox(mainWidget()); 00108 dontAskAgainCheckBox->setText(i18nc("@label:checkbox", "Remember action for files of this type")); 00109 mainLayout->addWidget(dontAskAgainCheckBox); 00110 } 00111 00112 bool autoEmbedMimeType(int flags); 00113 00114 int executeDialog(const QString& dontShowAgainName) 00115 { 00116 KConfigGroup cg(dontAskConfig, "Notification Messages"); // group name comes from KMessageBox 00117 const QString dontAsk = cg.readEntry(dontShowAgainName, QString()).toLower(); 00118 if (dontAsk == "yes" || dontAsk == "true") { 00119 return Save; 00120 } else if (dontAsk == "no" || dontAsk == "false") { 00121 return OpenDefault; 00122 } 00123 00124 KNotification::event("messageQuestion", // why does KMessageBox uses Information for questionYesNoCancel? 00125 questionLabel->text(), // also include mimetype? 00126 QPixmap(), 00127 window()); 00128 const int result = exec(); 00129 00130 if (dontAskAgainCheckBox->isChecked()) { 00131 cg.writeEntry(dontShowAgainName, result == Save); 00132 cg.sync(); 00133 } 00134 return result; 00135 } 00136 00137 void showService(KService::Ptr selectedService) 00138 { 00139 KGuiItem openItem(i18nc("@label:button", "&Open with %1", selectedService->name()), selectedService->icon()); 00140 setButtonGuiItem(OpenWith, openItem); 00141 } 00142 00143 KUrl url; 00144 QString mimeType; 00145 KMimeType::Ptr mime; 00146 KService::Ptr selectedService; 00147 KSqueezedTextLabel* questionLabel; 00148 BrowserOpenOrSaveQuestion::Features features; 00149 QLabel* fileNameLabel; 00150 00151 protected: 00152 virtual void slotButtonClicked(int buttonId) 00153 { 00154 if (buttonId != OpenDefault) 00155 selectedService = 0; 00156 KPushButton* button = KDialog::button(KDialog::ButtonCode(buttonId)); 00157 if (button && !button->menu()) { 00158 done(buttonId); 00159 } 00160 } 00161 private: 00162 QCheckBox* dontAskAgainCheckBox; 00163 KSharedConfig::Ptr dontAskConfig; 00164 00165 public Q_SLOTS: 00166 void slotAppSelected(QAction* action) 00167 { 00168 selectedService = action->data().value<KService::Ptr>(); 00169 //showService(selectedService); 00170 done(OpenDefault); 00171 } 00172 }; 00173 00174 00175 BrowserOpenOrSaveQuestion::BrowserOpenOrSaveQuestion(QWidget* parent, const KUrl& url, const QString& mimeType) 00176 : d(new BrowserOpenOrSaveQuestionPrivate(parent, url, mimeType)) 00177 { 00178 } 00179 00180 BrowserOpenOrSaveQuestion::~BrowserOpenOrSaveQuestion() 00181 { 00182 delete d; 00183 } 00184 00185 static KAction* createAppAction(const KService::Ptr& service, QObject* parent) 00186 { 00187 QString actionName(service->name().replace('&', "&&")); 00188 actionName = i18nc("@action:inmenu", "Open &with %1", actionName); 00189 00190 KAction *act = new KAction(parent); 00191 act->setIcon(KIcon(service->icon())); 00192 act->setText(actionName); 00193 act->setData(QVariant::fromValue(service)); 00194 return act; 00195 } 00196 00197 BrowserOpenOrSaveQuestion::Result BrowserOpenOrSaveQuestion::askOpenOrSave() 00198 { 00199 d->questionLabel->setText(i18nc("@info", "Open '%1'?", d->url.pathOrUrl())); 00200 d->questionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); 00201 d->showButton(BrowserOpenOrSaveQuestionPrivate::OpenWith, false); 00202 00203 KGuiItem openWithDialogItem(i18nc("@label:button", "&Open with..."), "document-open"); 00204 00205 // I thought about using KFileItemActions, but we don't want a submenu, nor the slots.... 00206 // and we want no menu at all if there's only one offer. 00207 // TODO: we probably need a setTraderConstraint(), to exclude the current application? 00208 const KService::List apps = KFileItemActions::associatedApplications(QStringList() << d->mimeType, 00209 QString() /* TODO trader constraint */); 00210 if (apps.isEmpty()) { 00211 d->setButtonGuiItem(BrowserOpenOrSaveQuestionPrivate::OpenDefault, openWithDialogItem); 00212 } else { 00213 KService::Ptr offer = apps.first(); 00214 KGuiItem openItem(i18nc("@label:button", "&Open with %1", offer->name()), offer->icon()); 00215 d->setButtonGuiItem(BrowserOpenOrSaveQuestionPrivate::OpenDefault, openItem); 00216 if (d->features & ServiceSelection) { 00217 // OpenDefault shall use this service 00218 d->selectedService = apps.first(); 00219 d->showButton(BrowserOpenOrSaveQuestionPrivate::OpenWith, true); 00220 KMenu* menu = new KMenu(d); 00221 if (apps.count() > 1) { 00222 // Provide an additional button with a menu of associated apps 00223 KGuiItem openWithItem(i18nc("@label:button", "&Open with"), "document-open"); 00224 d->setButtonGuiItem(BrowserOpenOrSaveQuestionPrivate::OpenWith, openWithItem); 00225 d->setButtonMenu(BrowserOpenOrSaveQuestionPrivate::OpenWith, menu, KDialog::InstantPopup); 00226 QObject::connect(menu, SIGNAL(triggered(QAction*)), d, SLOT(slotAppSelected(QAction*))); 00227 for (KService::List::const_iterator it = apps.begin(); it != apps.end(); ++it) { 00228 KAction* act = createAppAction(*it, d); 00229 menu->addAction(act); 00230 } 00231 KAction* openWithDialogAction = new KAction(d); 00232 openWithDialogAction->setIcon(KIcon("document-open")); 00233 openWithDialogAction->setText(openWithDialogItem.text()); 00234 menu->addAction(openWithDialogAction); 00235 } else { 00236 // Only one associated app, already offered by the other menu -> add "Open With..." button 00237 d->setButtonGuiItem(BrowserOpenOrSaveQuestionPrivate::OpenWith, openWithDialogItem); 00238 } 00239 } else { 00240 kDebug() << "Not using new feature ServiceSelection; port the caller to BrowserOpenOrSaveQuestion::setFeature(ServiceSelection)"; 00241 //kDebug() << kBacktrace(); 00242 } 00243 } 00244 00245 // KEEP IN SYNC with kdebase/runtime/keditfiletype/filetypedetails.cpp!!! 00246 const QString dontAskAgain = QLatin1String("askSave") + d->mimeType; 00247 00248 const int choice = d->executeDialog(dontAskAgain); 00249 return choice == BrowserOpenOrSaveQuestionPrivate::Save ? Save 00250 : (choice == BrowserOpenOrSaveQuestionPrivate::Cancel ? Cancel : Open); 00251 } 00252 00253 KService::Ptr BrowserOpenOrSaveQuestion::selectedService() const 00254 { 00255 return d->selectedService; 00256 } 00257 00258 bool BrowserOpenOrSaveQuestionPrivate::autoEmbedMimeType(int flags) 00259 { 00260 // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC 00261 // NOTE: Keep this function in sync with 00262 // kdebase/runtime/keditfiletype/filetypedetails.cpp 00263 // FileTypeDetails::updateAskSave() 00264 00265 // Don't ask for: 00266 // - html (even new tabs would ask, due to about:blank!) 00267 // - dirs obviously (though not common over HTTP :), 00268 // - images (reasoning: no need to save, most of the time, because fast to see) 00269 // e.g. postscript is different, because takes longer to read, so 00270 // it's more likely that the user might want to save it. 00271 // - multipart/* ("server push", see kmultipart) 00272 // KEEP IN SYNC!!! 00273 if (flags != (int)BrowserRun::AttachmentDisposition && mime && ( 00274 mime->is("text/html") || 00275 mime->is("application/xml") || 00276 mime->is("inode/directory") || 00277 mimeType.startsWith(QLatin1String("image")) || 00278 mime->is("multipart/x-mixed-replace") || 00279 mime->is("multipart/replace"))) 00280 return true; 00281 return false; 00282 } 00283 00284 BrowserOpenOrSaveQuestion::Result BrowserOpenOrSaveQuestion::askEmbedOrSave(int flags) 00285 { 00286 if (d->autoEmbedMimeType(flags)) 00287 return Embed; 00288 00289 // don't use KStandardGuiItem::open() here which has trailing ellipsis! 00290 d->setButtonGuiItem(BrowserOpenOrSaveQuestionPrivate::OpenDefault, KGuiItem(i18nc("@label:button", "&Open"), "document-open")); 00291 d->showButton(BrowserOpenOrSaveQuestionPrivate::OpenWith, false); 00292 00293 d->questionLabel->setText(i18nc("@info", "Open '%1'?", d->url.pathOrUrl())); 00294 d->questionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); 00295 00296 const QString dontAskAgain = QLatin1String("askEmbedOrSave")+ d->mimeType; // KEEP IN SYNC!!! 00297 // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC 00298 00299 const int choice = d->executeDialog(dontAskAgain); 00300 return choice == BrowserOpenOrSaveQuestionPrivate::Save ? Save 00301 : (choice == BrowserOpenOrSaveQuestionPrivate::Cancel ? Cancel : Embed); 00302 } 00303 00304 void BrowserOpenOrSaveQuestion::setFeatures(Features features) 00305 { 00306 d->features = features; 00307 } 00308 00309 void BrowserOpenOrSaveQuestion::setSuggestedFileName(const QString& suggestedFileName) 00310 { 00311 if (suggestedFileName.isEmpty()) { 00312 return; 00313 } 00314 00315 d->fileNameLabel->setText(i18nc("@label File name", "Name: %1", suggestedFileName)); 00316 d->fileNameLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); 00317 d->fileNameLabel->setWhatsThis(i18nc("@info:whatsthis", "This is the file name suggested by the server")); 00318 d->fileNameLabel->show(); 00319 00320 // If the current mime-type is the default mime-type, then attempt to 00321 // determine the "real" mimetype from the file name. 00322 if (d->mimeType == KMimeType::defaultMimeType()) { 00323 const KMimeType::Ptr mimePtr = KMimeType::findByUrl(suggestedFileName); 00324 d->mimeType = mimePtr->name(); 00325 } 00326 } 00327 00328 #include "browseropenorsavequestion.moc"
KDE 4.7 API Reference