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>
30 #include <KActionCollection>
31 #include "ktnef_debug.h"
32 #include <KEditToolBar>
33 #include <KLocalizedString>
35 #include <KMessageBox>
37 #include <KShortcutsDialog>
38 #include <KStandardAction>
39 #include <QTemporaryFile>
40 #include <QStandardPaths>
44 #include <KRecentFilesAction>
45 #include <KConfigGroup>
47 #include <QContextMenuEvent>
51 #include <KSharedConfig>
52 #include <QMimeDatabase>
54 #include <QFileDialog>
57 KTNEFMain::KTNEFMain(QWidget
*parent
)
58 : KXmlGuiWindow(parent
)
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/"));
78 setStandardToolBarMenuEnabled(true);
80 createStandardStatusBarAction();
82 setupGUI(Keys
| Save
| Create
, QStringLiteral("ktnefui.rc"));
84 setAutoSaveSettings();
87 KTNEFMain::~KTNEFMain()
93 void KTNEFMain::setupActions()
95 KStandardAction::quit(this, SLOT(close()), actionCollection());
98 KStandardAction::keyBindings(this, SLOT(slotConfigureKeys()), actionCollection());
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());
107 KStandardAction::open(this, SLOT(openFile()), actionCollection());
109 mOpenRecentFileAction
= KStandardAction::openRecent(this, SLOT(openRecentFile(QUrl
)), actionCollection());
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);
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);
207 "Unable to open file \"%1\".", filename
));
209 addRecentFile(QUrl::fromLocalFile(filename
));
210 QList
<KTNEFAttach
*> list
= mParser
->message()->attachmentList();
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()) {
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
);
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...";
252 db
.mimeTypeForFile(url
.path(), QMimeDatabase::MatchExtension
).name();
253 qCDebug(KTNEFAPPS_LOG
) << "Detected mime type: " << mimename
;
255 qCDebug(KTNEFAPPS_LOG
) << "Mime type from attachment object: " << mimename
;
258 KRun::runUrl(url
, mimename
, this, true);
260 KMessageBox::information(
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
);
282 void KTNEFMain::viewFileAs()
284 if (!mView
->getSelection().isEmpty()) {
286 list
.append(QUrl::fromLocalFile(extractTemp(mView
->getSelection().at(0))));
288 if (!list
.isEmpty()) {
289 KRun::displayOpenWithDialog(list
, this);
292 KMessageBox::information(
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()) {
313 void KTNEFMain::extractAllFiles()
315 QString dir
= QFileDialog::getExistingDirectory(this, QString(), mLastDir
);
316 if (!dir
.isEmpty()) {
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
)) {
327 "Unable to extract file \"%1\".", (*it
)->name()));
334 void KTNEFMain::propertiesFile()
336 KTNEFAttach
*attach
= mView
->getSelection().at(0);
337 AttachPropertyDialog
dlg(this);
338 dlg
.setAttachment(attach
);
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
)
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
)) {
406 "Unable to extract file \"%1\".", (*it
)->name()));
412 /* This breaks the saveMainWindowSettings stuff....
413 void KTNEFMain::closeEvent(QCloseEvent *e)
418 void KTNEFMain::contextMenuEvent(QContextMenuEvent
*event
)
420 QList
<KTNEFAttach
*> list
= mView
->getSelection();
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);
444 } else if (a
== extractTo
) {
446 } else if (a
== prop
) {
453 void KTNEFMain::viewDoubleClicked(QTreeWidgetItem
*item
)
455 if (item
&& item
->isSelected()) {
460 void KTNEFMain::viewDragRequested(const QList
<KTNEFAttach
*> &list
)
463 QList
<KTNEFAttach
*>::ConstIterator
end(list
.constEnd());
464 for (QList
<KTNEFAttach
*>::ConstIterator it
= list
.constBegin();
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
);
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());
500 void KTNEFMain::slotShowMessageText()
502 if (!mParser
->message()) {
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"));
510 tmpFile
->setPermissions(QFile::ReadUser
);
511 tmpFile
->write(rtf
.toLocal8Bit());
514 KRun::runUrl(QUrl::fromLocalFile(tmpFile
->fileName()), QStringLiteral("text/rtf"), this, true);
520 "The message does not contain any Rich Text data."));
524 void KTNEFMain::slotSaveMessageText()
526 if (!mParser
->message()) {
530 QString rtf
= mParser
->message()->rtfString();
531 QString filename
= QFileDialog::getSaveFileName(this, QString(), QString(), QString());
532 if (!filename
.isEmpty()) {
534 if (f
.open(QIODevice::WriteOnly
)) {
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
));
554 KRun::runService(*offer
, lst
, this, false);
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("&&")));
565 actionName
= i18n("Open &with %1", actionName
);
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
));
578 void KTNEFMain::createOpenWithMenu(QMenu
*topMenu
)
580 if (mView
->getSelection().isEmpty()) {
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...");
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
>();