SVN_SILENT made messages (.desktop file) - always resolve ours
[kdepim.git] / ktnef / ktnefmain.cpp
blob65c67d8b6c81a9cf668c12adf0d24a5aa884d8a5
1 /*
2 This file is part of KTnef.
4 Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be>
5 Copyright (c) 2012 Allen Winter <winter@kde.org>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software Foundation,
14 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 #include "ktnefmain.h"
18 #include "attachpropertydialog.h"
19 #include "ktnefview.h"
20 #include "messagepropertydialog.h"
22 #include <KTNEF/KTNEFAttach>
23 #include <KTNEF/KTNEFMessage>
24 #include <KTNEF/KTNEFParser>
25 #include <KTNEF/KTNEFProperty>
27 #include <KFileItemActions>
28 #include <kservice.h>
29 #include <QAction>
30 #include <KActionCollection>
31 #include "ktnef_debug.h"
32 #include <KEditToolBar>
33 #include <KLocalizedString>
34 #include <QMenu>
35 #include <KMessageBox>
36 #include <KRun>
37 #include <KShortcutsDialog>
38 #include <KStandardAction>
39 #include <QTemporaryFile>
40 #include <QStandardPaths>
41 #include <QUrl>
42 #include <QIcon>
44 #include <KRecentFilesAction>
45 #include <KConfigGroup>
47 #include <QContextMenuEvent>
48 #include <QDir>
49 #include <QDrag>
50 #include <QMimeData>
51 #include <KSharedConfig>
52 #include <QMimeDatabase>
53 #include <QMimeType>
54 #include <QFileDialog>
55 #include <QStatusBar>
57 KTNEFMain::KTNEFMain(QWidget *parent)
58 : KXmlGuiWindow(parent)
60 setupActions();
61 setupStatusbar();
63 setupTNEF();
65 KConfigGroup config(KSharedConfig::openConfig(), "Settings");
66 mDefaultDir = config.readPathEntry("defaultdir", QStringLiteral("/tmp/"));
68 config = KConfigGroup(KSharedConfig::openConfig(), "Recent Files");
69 mOpenRecentFileAction->loadEntries(config);
71 mLastDir = mDefaultDir;
73 // create personal temp extract dir
74 QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QLatin1String("/ktnef/"));
76 resize(430, 350);
78 setStandardToolBarMenuEnabled(true);
80 createStandardStatusBarAction();
82 setupGUI(Keys | Save | Create, QStringLiteral("ktnefui.rc"));
84 setAutoSaveSettings();
87 KTNEFMain::~KTNEFMain()
89 delete mParser;
90 cleanup();
93 void KTNEFMain::setupActions()
95 KStandardAction::quit(this, SLOT(close()), actionCollection());
97 QAction *action =
98 KStandardAction::keyBindings(this, SLOT(slotConfigureKeys()), actionCollection());
99 action->setWhatsThis(
100 i18nc("@info:whatsthis",
101 "You will be presented with a dialog where you can configure "
102 "the application-wide shortcuts."));
104 KStandardAction::configureToolbars(this, SLOT(slotEditToolbars()), actionCollection());
106 // File menu
107 KStandardAction::open(this, SLOT(openFile()), actionCollection());
109 mOpenRecentFileAction = KStandardAction::openRecent(this, SLOT(openRecentFile(QUrl)), actionCollection());
111 // Action menu
112 QAction *openAction = actionCollection()->addAction(QStringLiteral("view_file"));
113 openAction->setText(i18nc("@action:inmenu", "View"));
114 openAction->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
115 connect(openAction, &QAction::triggered, this, &KTNEFMain::viewFile);
117 QAction *openAsAction = actionCollection()->addAction(QStringLiteral("view_file_as"));
118 openAsAction->setText(i18nc("@action:inmenu", "View With..."));
119 connect(openAsAction, &QAction::triggered, this, &KTNEFMain::viewFileAs);
121 QAction *extractAction = actionCollection()->addAction(QStringLiteral("extract_file"));
122 extractAction->setText(i18nc("@action:inmenu", "Extract"));
123 connect(extractAction, &QAction::triggered, this, &KTNEFMain::extractFile);
125 QAction *extractToAction = actionCollection()->addAction(QStringLiteral("extract_file_to"));
126 extractToAction->setText(i18nc("@action:inmenu", "Extract To..."));
127 extractToAction->setIcon(QIcon::fromTheme(QStringLiteral("archive-extract")));
128 connect(extractToAction, &QAction::triggered, this, &KTNEFMain::extractFileTo);
130 QAction *extractAllToAction = actionCollection()->addAction(QStringLiteral("extract_all_files"));
131 extractAllToAction->setText(i18nc("@action:inmenu", "Extract All To..."));
132 extractAllToAction->setIcon(QIcon::fromTheme(QStringLiteral("archive-extract")));
133 connect(extractAllToAction, &QAction::triggered, this, &KTNEFMain::extractAllFiles);
135 QAction *filePropsAction = actionCollection()->addAction(QStringLiteral("properties_file"));
136 filePropsAction->setText(i18nc("@action:inmenu", "Properties"));
137 filePropsAction->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
138 connect(filePropsAction, &QAction::triggered, this, &KTNEFMain::propertiesFile);
140 QAction *messPropsAction = actionCollection()->addAction(QStringLiteral("msg_properties"));
141 messPropsAction->setText(i18nc("@action:inmenu", "Message Properties"));
142 connect(messPropsAction, &QAction::triggered, this, &KTNEFMain::slotShowMessageProperties);
144 QAction *messShowAction = actionCollection()->addAction(QStringLiteral("msg_text"));
145 messShowAction->setText(i18nc("@action:inmenu", "Show Message Text"));
146 messShowAction->setIcon(QIcon::fromTheme(QStringLiteral("document-preview-archive")));
147 connect(messShowAction, &QAction::triggered, this, &KTNEFMain::slotShowMessageText);
149 QAction *messSaveAction = actionCollection()->addAction(QStringLiteral("msg_save"));
150 messSaveAction->setText(i18nc("@action:inmenu", "Save Message Text As..."));
151 messSaveAction->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
152 connect(messSaveAction, &QAction::triggered, this, &KTNEFMain::slotSaveMessageText);
154 actionCollection()->action(QStringLiteral("view_file"))->setEnabled(false);
155 actionCollection()->action(QStringLiteral("view_file_as"))->setEnabled(false);
156 actionCollection()->action(QStringLiteral("extract_file"))->setEnabled(false);
157 actionCollection()->action(QStringLiteral("extract_file_to"))->setEnabled(false);
158 actionCollection()->action(QStringLiteral("extract_all_files"))->setEnabled(false);
159 actionCollection()->action(QStringLiteral("properties_file"))->setEnabled(false);
161 // Options menu
162 QAction *defFolderAction = actionCollection()->addAction(QStringLiteral("options_default_dir"));
163 defFolderAction->setText(i18nc("@action:inmenu", "Default Folder..."));
164 defFolderAction->setIcon(QIcon::fromTheme(QStringLiteral("folder-open")));
165 connect(defFolderAction, &QAction::triggered, this, &KTNEFMain::optionDefaultDir);
169 void KTNEFMain::slotConfigureKeys()
171 KShortcutsDialog::configure(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this);
174 void KTNEFMain::setupStatusbar()
176 statusBar()->showMessage(i18nc("@info:status", "No file loaded"));
179 void KTNEFMain::setupTNEF()
181 mView = new KTNEFView(this);
182 mView->setAllColumnsShowFocus(true);
183 mParser = new KTNEFParser;
185 setCentralWidget(mView);
187 connect(mView, &QTreeWidget::itemSelectionChanged,
188 this, &KTNEFMain::viewSelectionChanged);
190 connect(mView, &QTreeWidget::itemDoubleClicked,
191 this, &KTNEFMain::viewDoubleClicked);
193 //PORTME: connect( mView, SIGNAL(dragRequested(QList<KTNEFAttach*>)),
194 //PORTME: SLOT(viewDragRequested(QList<KTNEFAttach*>)) );
197 void KTNEFMain::loadFile(const QString &filename)
199 mFilename = filename;
200 setCaption(mFilename);
201 if (!mParser->openFile(filename)) {
202 mView->setAttachments(QList<KTNEFAttach *>());
203 enableExtractAll(false);
204 KMessageBox::error(
205 this,
206 i18nc("@info",
207 "Unable to open file \"%1\".", filename));
208 } else {
209 addRecentFile(QUrl::fromLocalFile(filename));
210 QList<KTNEFAttach *> list = mParser->message()->attachmentList();
211 QString msg;
212 msg = i18ncp("@info:status",
213 "%1 attachment found", "%1 attachments found", list.count());
214 statusBar()->showMessage(msg);
215 mView->setAttachments(list);
216 enableExtractAll((list.count() > 0));
217 enableSingleAction(false);
221 void KTNEFMain::openFile()
223 QString filename = QFileDialog::getOpenFileName(this, i18nc("@title:window", "Open TNEF File"));
224 if (!filename.isEmpty()) {
225 loadFile(filename);
229 void KTNEFMain::openRecentFile(const QUrl &url)
231 loadFile(url.path());
234 void KTNEFMain::addRecentFile(const QUrl &url)
236 mOpenRecentFileAction->addUrl(url);
237 KConfigGroup config(KSharedConfig::openConfig(), "Recent Files");
238 mOpenRecentFileAction->saveEntries(config);
239 config.sync();
242 void KTNEFMain::viewFile()
244 if (!mView->getSelection().isEmpty()) {
245 KTNEFAttach *attach = mView->getSelection().at(0);
246 QUrl url = QUrl::fromLocalFile(extractTemp(attach));
247 QString mimename(attach->mimeTag());
249 if (mimename.isEmpty() || mimename == QLatin1String("application/octet-stream")) {
250 qCDebug(KTNEFAPPS_LOG) << "No mime type found in attachment object, trying to guess...";
251 QMimeDatabase db;
252 db.mimeTypeForFile(url.path(), QMimeDatabase::MatchExtension).name();
253 qCDebug(KTNEFAPPS_LOG) << "Detected mime type: " << mimename;
254 } else {
255 qCDebug(KTNEFAPPS_LOG) << "Mime type from attachment object: " << mimename;
258 KRun::runUrl(url, mimename, this, true);
259 } else {
260 KMessageBox::information(
261 this,
262 i18nc("@info",
263 "There is no file selected. Please select a file an try again."));
268 QString KTNEFMain::extractTemp(KTNEFAttach *att)
270 QString dir = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QLatin1String("/ktnef/");
271 mParser->extractFileTo(att->name(), dir);
272 QString filename = att->fileName();
273 // falling back to internal TNEF attachement name if no filename is given for the attached file
274 // this follows the logic of KTNEFParser::extractFileTo(...)
275 if (filename.isEmpty()) {
276 filename = att->name();
278 dir.append(filename);
279 return dir;
282 void KTNEFMain::viewFileAs()
284 if (!mView->getSelection().isEmpty()) {
285 QList<QUrl> list;
286 list.append(QUrl::fromLocalFile(extractTemp(mView->getSelection().at(0))));
288 if (!list.isEmpty()) {
289 KRun::displayOpenWithDialog(list, this);
291 } else {
292 KMessageBox::information(
293 this,
294 i18nc("@info",
295 "There is no file selected. Please select a file an try again."));
299 void KTNEFMain::extractFile()
301 extractTo(mDefaultDir);
304 void KTNEFMain::extractFileTo()
306 QString dir = QFileDialog::getExistingDirectory(this, QString(), mLastDir);
307 if (!dir.isEmpty()) {
308 extractTo(dir);
309 mLastDir = dir;
313 void KTNEFMain::extractAllFiles()
315 QString dir = QFileDialog::getExistingDirectory(this, QString(), mLastDir);
316 if (!dir.isEmpty()) {
317 mLastDir = dir;
318 dir.append(QLatin1String("/"));
319 QList<KTNEFAttach *> list = mParser->message()->attachmentList();
320 QList<KTNEFAttach *>::ConstIterator it;
321 QList<KTNEFAttach *>::ConstIterator end(list.constEnd());
322 for (it = list.constBegin(); it != end; ++it) {
323 if (!mParser->extractFileTo((*it)->name(), dir)) {
324 KMessageBox::error(
325 this,
326 i18nc("@info",
327 "Unable to extract file \"%1\".", (*it)->name()));
328 return;
334 void KTNEFMain::propertiesFile()
336 KTNEFAttach *attach = mView->getSelection().at(0);
337 AttachPropertyDialog dlg(this);
338 dlg.setAttachment(attach);
339 dlg.exec();
342 void KTNEFMain::optionDefaultDir()
344 const QString dirname = QFileDialog::getExistingDirectory(this, QString(), mDefaultDir);
345 if (!dirname.isEmpty()) {
346 mDefaultDir = dirname;
348 KConfigGroup config(KSharedConfig::openConfig(), "Settings");
349 config.writePathEntry("defaultdir", mDefaultDir);
353 void KTNEFMain::viewSelectionChanged()
355 const QList<KTNEFAttach *> list = mView->getSelection();
356 const int nbItem = list.count();
357 const bool on1 = (nbItem == 1);
358 const bool on2 = (nbItem > 0);
360 actionCollection()->action(QStringLiteral("view_file"))->setEnabled(on1);
361 actionCollection()->action(QStringLiteral("view_file_as"))->setEnabled(on1);
362 actionCollection()->action(QStringLiteral("properties_file"))->setEnabled(on1);
364 actionCollection()->action(QStringLiteral("extract_file"))->setEnabled(on2);
365 actionCollection()->action(QStringLiteral("extract_file_to"))->setEnabled(on2);
368 void KTNEFMain::enableExtractAll(bool on)
370 if (!on) {
371 enableSingleAction(false);
374 actionCollection()->action(QStringLiteral("extract_all_files"))->setEnabled(on);
377 void KTNEFMain::enableSingleAction(bool on)
379 actionCollection()->action(QStringLiteral("extract_file"))->setEnabled(on);
380 actionCollection()->action(QStringLiteral("extract_file_to"))->setEnabled(on);
381 actionCollection()->action(QStringLiteral("view_file"))->setEnabled(on);
382 actionCollection()->action(QStringLiteral("view_file_as"))->setEnabled(on);
383 actionCollection()->action(QStringLiteral("properties_file"))->setEnabled(on);
386 void KTNEFMain::cleanup()
388 QDir d(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QLatin1String("/ktnef/"));
389 d.removeRecursively();
392 void KTNEFMain::extractTo(const QString &dirname)
394 QString dir = dirname;
395 if (dir.right(1) != QLatin1String("/")) {
396 dir.append(QLatin1String("/"));
398 QList<KTNEFAttach *>list = mView->getSelection();
399 QList<KTNEFAttach *>::ConstIterator it;
400 QList<KTNEFAttach *>::ConstIterator end(list.constEnd());
401 for (it = list.constBegin(); it != end; ++it) {
402 if (!mParser->extractFileTo((*it)->name(), dir)) {
403 KMessageBox::error(
404 this,
405 i18nc("@info",
406 "Unable to extract file \"%1\".", (*it)->name()));
407 return;
412 /* This breaks the saveMainWindowSettings stuff....
413 void KTNEFMain::closeEvent(QCloseEvent *e)
415 e->accept();
418 void KTNEFMain::contextMenuEvent(QContextMenuEvent *event)
420 QList<KTNEFAttach *> list = mView->getSelection();
421 if (!list.count()) {
422 return;
425 QAction *prop = 0;
426 QMenu *menu = new QMenu();
427 if (list.count() == 1) {
428 createOpenWithMenu(menu);
429 menu->addSeparator();
431 QAction *extract = menu->addAction(i18nc("@action:inmenu", "Extract"));
432 QAction *extractTo = menu->addAction(QIcon::fromTheme(QStringLiteral("archive-extract")),
433 i18nc("@action:inmenu", "Extract To..."));
434 if (list.count() == 1) {
435 menu->addSeparator();
436 prop = menu->addAction(QIcon::fromTheme(QStringLiteral("document-properties")),
437 i18nc("@action:inmenu", "Properties"));
440 QAction *a = menu->exec(event->globalPos(), 0);
441 if (a) {
442 if (a == extract) {
443 extractFile();
444 } else if (a == extractTo) {
445 extractFileTo();
446 } else if (a == prop) {
447 propertiesFile();
450 delete menu;
453 void KTNEFMain::viewDoubleClicked(QTreeWidgetItem *item)
455 if (item && item->isSelected()) {
456 viewFile();
460 void KTNEFMain::viewDragRequested(const QList<KTNEFAttach *> &list)
462 QList<QUrl> urlList;
463 QList<KTNEFAttach *>::ConstIterator end(list.constEnd());
464 for (QList<KTNEFAttach *>::ConstIterator it = list.constBegin();
465 it != end; ++it) {
466 urlList << QUrl::fromLocalFile(extractTemp(*it));
469 if (!list.isEmpty()) {
470 QMimeData *mimeData = new QMimeData;
471 mimeData->setUrls(urlList);
473 QDrag *drag = new QDrag(this);
474 drag->setMimeData(mimeData);
478 void KTNEFMain::slotEditToolbars()
480 KConfigGroup grp = KSharedConfig::openConfig()->group("MainWindow");
481 saveMainWindowSettings(grp);
483 KEditToolBar dlg(factory());
484 connect(&dlg, &KEditToolBar::newToolBarConfig, this, &KTNEFMain::slotNewToolbarConfig);
485 dlg.exec();
488 void KTNEFMain::slotNewToolbarConfig()
490 createGUI(QStringLiteral("ktnefui.rc"));
491 applyMainWindowSettings(KSharedConfig::openConfig()->group("MainWindow"));
494 void KTNEFMain::slotShowMessageProperties()
496 MessagePropertyDialog dlg(this, mParser->message());
497 dlg.exec();
500 void KTNEFMain::slotShowMessageText()
502 if (!mParser->message()) {
503 return;
506 QString rtf = mParser->message()->rtfString();
507 if (!rtf.isEmpty()) {
508 QTemporaryFile *tmpFile = new QTemporaryFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QLatin1String("/ktnef/") + QLatin1String("ktnef_XXXXXX.rtf"));
509 tmpFile->open();
510 tmpFile->setPermissions(QFile::ReadUser);
511 tmpFile->write(rtf.toLocal8Bit());
512 tmpFile->close();
514 KRun::runUrl(QUrl::fromLocalFile(tmpFile->fileName()), QStringLiteral("text/rtf"), this, true);
515 delete tmpFile;
516 } else {
517 KMessageBox::error(
518 this,
519 i18nc("@info",
520 "The message does not contain any Rich Text data."));
524 void KTNEFMain::slotSaveMessageText()
526 if (!mParser->message()) {
527 return;
530 QString rtf = mParser->message()->rtfString();
531 QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), QString());
532 if (!filename.isEmpty()) {
533 QFile f(filename);
534 if (f.open(QIODevice::WriteOnly)) {
535 QTextStream t(&f);
536 t << rtf;
537 } else {
538 KMessageBox::error(
539 this,
540 i18nc("@info",
541 "Unable to open file \"%1\" for writing, check file permissions.", filename));
546 void KTNEFMain::openWith(const KService::Ptr &offer)
548 if (!mView->getSelection().isEmpty()) {
549 KTNEFAttach *attach = mView->getSelection().at(0);
550 QUrl url = QUrl::fromLocalFile(QLatin1String("file:") + extractTemp(attach));
551 QList<QUrl> lst;
552 lst.append(url);
553 if (offer) {
554 KRun::runService(*offer, lst, this, false);
555 } else {
556 KRun::displayOpenWithDialog(lst, this, false);
561 QAction *KTNEFMain::createAppAction(const KService::Ptr &service, bool singleOffer, QActionGroup *actionGroup, QObject *parent)
563 QString actionName(service->name().replace(QLatin1Char('&'), QStringLiteral("&&")));
564 if (singleOffer) {
565 actionName = i18n("Open &with %1", actionName);
566 } else {
567 actionName = i18nc("@item:inmenu Open With, %1 is application name", "%1", actionName);
570 QAction *act = new QAction(parent);
571 act->setIcon(QIcon::fromTheme(service->icon()));
572 act->setText(actionName);
573 actionGroup->addAction(act);
574 act->setData(QVariant::fromValue(service));
575 return act;
578 void KTNEFMain::createOpenWithMenu(QMenu *topMenu)
580 if (mView->getSelection().isEmpty()) {
581 return;
583 KTNEFAttach *attach = mView->getSelection().at(0);
584 QString mimename(attach->mimeTag());
586 const KService::List offers = KFileItemActions::associatedApplications(QStringList() << mimename, QString());
587 if (!offers.isEmpty()) {
588 QMenu *menu = topMenu;
589 QActionGroup *actionGroup = new QActionGroup(menu);
590 connect(actionGroup, &QActionGroup::triggered, this, &KTNEFMain::slotOpenWithAction);
592 if (offers.count() > 1) { // submenu 'open with'
593 menu = new QMenu(i18nc("@title:menu", "&Open With"), topMenu);
594 menu->menuAction()->setObjectName(QStringLiteral("openWith_submenu")); // for the unittest
595 topMenu->addMenu(menu);
598 KService::List::ConstIterator it = offers.constBegin();
599 KService::List::ConstIterator end = offers.constEnd();
600 for (; it != end; ++it) {
601 QAction *act = createAppAction(*it,
602 // no submenu -> prefix single offer
603 menu == topMenu, actionGroup, menu);
604 menu->addAction(act);
607 QString openWithActionName;
608 if (menu != topMenu) { // submenu
609 menu->addSeparator();
610 openWithActionName = i18nc("@action:inmenu Open With", "&Other...");
611 } else {
612 openWithActionName = i18nc("@title:menu", "&Open With...");
614 QAction *openWithAct = new QAction(menu);
615 openWithAct->setText(openWithActionName);
616 connect(openWithAct, &QAction::triggered, this, &KTNEFMain::viewFileAs);
617 menu->addAction(openWithAct);
618 } else { // no app offers -> Open With...
619 QAction *act = new QAction(topMenu);
620 act->setText(i18nc("@title:menu", "&Open With..."));
621 connect(act, &QAction::triggered, this, &KTNEFMain::viewFileAs);
622 topMenu->addAction(act);
626 void KTNEFMain::slotOpenWithAction(QAction *act)
628 KService::Ptr app = act->data().value<KService::Ptr>();
630 openWith(app);