File indexing completed on 2024-04-28 03:40:31

0001 /***************************************************************************
0002  *   Copyright (C) 2002 by Gunnar Schmi Dt <kmouth@schmi-dt.de             *
0003  *             (C) 2015 by Jeremy Whiting <jpwhiting@kde.org>              *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
0019  ***************************************************************************/
0020 
0021 #include "phrasebookdialog.h"
0022 
0023 // include files for Qt
0024 #include <QClipboard>
0025 #include <QFileDialog>
0026 #include <QMimeData>
0027 #include <QStack>
0028 #include <QStandardPaths>
0029 
0030 // include files for KDE
0031 #include <KActionMenu>
0032 #include <KMessageBox>
0033 #include <KToolBarPopupAction>
0034 #include <KXMLGUIFactory>
0035 
0036 #include "phrasebook.h"
0037 
0038 #include <QDebug>
0039 #include <kwidgetsaddons_version.h>
0040 
0041 const int kTextColumn = 0;
0042 const int kShortcutColumn = 1;
0043 
0044 const QString kName = QStringLiteral("name");
0045 const QString kShortcut = QStringLiteral("shortcut");
0046 const QString kPhraseBook = QStringLiteral("phrasebook");
0047 const QString kPhrase = QStringLiteral("phrase");
0048 
0049 const QString kPhraseBookXML = QStringLiteral("<phrasebook name=\"%1\">\n%2</phrasebook>\n");
0050 const QString kPhraseXML = QStringLiteral("<phrase shortcut=\"%2\">%1</phrase>\n");
0051 const QString kWholeBookXML = QStringLiteral(
0052     "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
0053     "<!DOCTYPE phrasebook>\n"
0054     "<phrasebook>\n%1"
0055     "</phrasebook>");
0056 
0057 const QIcon kPhraseBookIcon = QIcon::fromTheme(kPhraseBook);
0058 const QIcon kPhraseIcon = QIcon::fromTheme(kPhrase);
0059 
0060 StandardPhraseBookInsertAction::StandardPhraseBookInsertAction(const QUrl &url,
0061                                                                const QString &name,
0062                                                                const QObject *receiver,
0063                                                                const char *slot,
0064                                                                KActionCollection *parent)
0065     : QAction(QIcon::fromTheme(QStringLiteral("phrasebook")), name, parent)
0066 {
0067     this->url = url;
0068     connect(this, SIGNAL(triggered(bool)), this, SLOT(slotActivated()));
0069     connect(this, SIGNAL(slotActivated(QUrl)), receiver, slot);
0070     parent->addAction(name, this);
0071 }
0072 
0073 StandardPhraseBookInsertAction::~StandardPhraseBookInsertAction()
0074 {
0075 }
0076 
0077 void StandardPhraseBookInsertAction::slotActivated()
0078 {
0079     Q_EMIT slotActivated(url);
0080 }
0081 
0082 namespace PhraseBookPrivate
0083 {
0084 PhraseBookDialog *instance = nullptr;
0085 }
0086 
0087 PhraseBookDialog::PhraseBookDialog()
0088     : KXmlGuiWindow(nullptr)
0089 {
0090     // Create model with 2 columns and no rows
0091     m_bookModel = new QStandardItemModel(0, 2, this);
0092     m_rootItem = m_bookModel->invisibleRootItem();
0093     m_bookModel->setHeaderData(kTextColumn, Qt::Horizontal, i18n("Phrase"));
0094     m_bookModel->setHeaderData(kShortcutColumn, Qt::Horizontal, i18n("Shortcut"));
0095 
0096     setObjectName(QStringLiteral("phraseEditDialog"));
0097     setWindowTitle(i18n("Phrase Book"));
0098     initGUI();
0099     initActions();
0100     initStandardPhraseBooks();
0101     QString standardBook = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("standard.phrasebook"));
0102     if (!standardBook.isNull() && !standardBook.isEmpty()) {
0103         QFile file(standardBook);
0104         file.open(QIODevice::ReadOnly);
0105         QDomDocument document;
0106         document.setContent(&file);
0107 
0108         const QDomNodeList nodes = document.documentElement().childNodes();
0109         for (int i = 0; i < nodes.count(); ++i) {
0110             deserializeBook(nodes.at(i), m_rootItem);
0111         }
0112 
0113         selectionChanged();
0114         phrasebookChanged = false;
0115         fileSave->setEnabled(false);
0116     }
0117 
0118     // Watch any changes in the model.
0119     connect(m_bookModel, &QAbstractItemModel::dataChanged, this, &PhraseBookDialog::slotModelChanged);
0120     connect(m_bookModel, &QAbstractItemModel::rowsInserted, this, &PhraseBookDialog::slotModelChanged);
0121     connect(m_bookModel, &QAbstractItemModel::rowsMoved, this, &PhraseBookDialog::slotModelChanged);
0122     connect(m_bookModel, &QAbstractItemModel::rowsRemoved, this, &PhraseBookDialog::slotModelChanged);
0123 
0124     // printer = 0;
0125     //  i18n("Edit Phrase Book")
0126 }
0127 
0128 PhraseBookDialog *PhraseBookDialog::get()
0129 {
0130     if (PhraseBookPrivate::instance == nullptr)
0131         PhraseBookPrivate::instance = new PhraseBookDialog();
0132     return PhraseBookPrivate::instance;
0133 }
0134 
0135 PhraseBookDialog::~PhraseBookDialog()
0136 {
0137     PhraseBookPrivate::instance = nullptr;
0138     // delete printer;
0139 }
0140 
0141 QStandardItem *PhraseBookDialog::deserializeBook(const QDomNode &node, QStandardItem *parent)
0142 {
0143     QString text;
0144     QString shortcut;
0145     bool isBook = (node.nodeName() == kPhraseBook);
0146     text = isBook ? node.attributes().namedItem(kName).nodeValue() : node.lastChild().nodeValue();
0147     shortcut = isBook ? QString() : node.attributes().namedItem(kShortcut).nodeValue();
0148 
0149     QStandardItem *item = isBook ? new QStandardItem(kPhraseBookIcon, text) : new QStandardItem(kPhraseIcon, text);
0150     QStandardItem *shortcutItem = new QStandardItem(shortcut);
0151 
0152     if (!isBook) {
0153         item->setDropEnabled(false);
0154         shortcutItem->setDropEnabled(false);
0155     }
0156     QList<QStandardItem *> items;
0157     items << item << shortcutItem;
0158     if (parent)
0159         parent->appendRow(items);
0160     else
0161         m_bookModel->appendRow(items);
0162 
0163     if (isBook) {
0164         // Iterate over the document creating QStandardItems as needed
0165         const QDomNodeList childNodes = node.childNodes();
0166         for (int i = 0; i < childNodes.count(); ++i) {
0167             const QDomNode child = childNodes.at(i);
0168             deserializeBook(child, item);
0169         }
0170     }
0171     return item;
0172 }
0173 
0174 QString PhraseBookDialog::serializeBook(const QModelIndex &index)
0175 {
0176     QString value;
0177     if (index.isValid()) {
0178         QStandardItem *item = m_bookModel->itemFromIndex(index);
0179         QStandardItem *shortcutItem = m_bookModel->itemFromIndex(index.sibling(index.row(), kShortcutColumn));
0180         bool isBook = item->isDropEnabled(); // We know it's a book if it's drop enabled
0181         if (isBook) {
0182             QString childrenText;
0183             for (int i = 0; i < item->rowCount(); ++i) {
0184                 childrenText += serializeBook(index.model()->index(i, 0, index));
0185             }
0186             value = kPhraseBookXML.arg(item->text(), childrenText);
0187         } else {
0188             value = kPhraseXML.arg(item->text(), shortcutItem->text());
0189         }
0190     } else {
0191         // Do the whole model, inside a <phrasebook> tag.
0192         int rows = m_bookModel->rowCount(QModelIndex());
0193         QString childrenText;
0194         for (int i = 0; i < rows; ++i) {
0195             childrenText += serializeBook(m_bookModel->index(i, 0));
0196         }
0197         value = kWholeBookXML.arg(childrenText);
0198     }
0199     return value;
0200 }
0201 
0202 void PhraseBookDialog::initGUI()
0203 {
0204     QWidget *page = new QWidget(this);
0205 
0206     m_ui = new Ui::PhraseBookDialog();
0207     m_ui->setupUi(page);
0208     setCentralWidget(page);
0209 
0210     m_ui->treeView->setModel(m_bookModel);
0211     connect(m_ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &PhraseBookDialog::selectionChanged);
0212     connect(m_ui->treeView, &QWidget::customContextMenuRequested, this, &PhraseBookDialog::contextMenuRequested);
0213     connectEditor();
0214 }
0215 
0216 void PhraseBookDialog::slotModelChanged()
0217 {
0218     phrasebookChanged = true;
0219     fileSave->setEnabled(true);
0220 }
0221 
0222 void PhraseBookDialog::initActions()
0223 {
0224     // The file menu
0225     fileNewPhrase = actionCollection()->addAction(QStringLiteral("file_new_phrase"));
0226     fileNewPhrase->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
0227     fileNewPhrase->setText(i18n("&New Phrase"));
0228     connect(fileNewPhrase, &QAction::triggered, this, &PhraseBookDialog::slotAddPhrase);
0229     fileNewPhrase->setToolTip(i18n("Adds a new phrase"));
0230     fileNewPhrase->setWhatsThis(i18n("Adds a new phrase"));
0231 
0232     fileNewBook = actionCollection()->addAction(QStringLiteral("file_new_book"));
0233     fileNewBook->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
0234     fileNewBook->setText(i18n("New Phrase &Book"));
0235     connect(fileNewBook, &QAction::triggered, this, &PhraseBookDialog::slotAddPhrasebook);
0236     fileNewBook->setToolTip(i18n("Adds a new phrase book into which other books and phrases can be placed"));
0237     fileNewBook->setWhatsThis(i18n("Adds a new phrase book into which other books and phrases can be placed"));
0238 
0239     fileSave = KStandardAction::save(this, &PhraseBookDialog::slotSave, actionCollection());
0240     fileSave->setToolTip(i18n("Saves the phrase book onto the hard disk"));
0241     fileSave->setWhatsThis(i18n("Saves the phrase book onto the hard disk"));
0242 
0243     fileImport = actionCollection()->addAction(QStringLiteral("file_import"));
0244     fileImport->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
0245     fileImport->setText(i18n("&Import..."));
0246     connect(fileImport, SIGNAL(triggered(bool)), this, SLOT(slotImportPhrasebook()));
0247     fileImport->setToolTip(i18n("Imports a file and adds its contents to the phrase book"));
0248     fileImport->setWhatsThis(i18n("Imports a file and adds its contents to the phrase book"));
0249 
0250     toolbarImport = new KToolBarPopupAction(QIcon::fromTheme(QStringLiteral("document-open")), i18n("&Import..."), this);
0251     actionCollection()->addAction(QStringLiteral("toolbar_import"), toolbarImport);
0252     connect(toolbarImport, SIGNAL(triggered(bool)), this, SLOT(slotImportPhrasebook()));
0253     toolbarImport->setToolTip(i18n("Imports a file and adds its contents to the phrase book"));
0254     toolbarImport->setWhatsThis(i18n("Imports a file and adds its contents to the phrase book"));
0255 
0256     fileImportStandardBook = actionCollection()->add<KActionMenu>(QStringLiteral("file_import_standard_book"));
0257     fileImportStandardBook->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
0258     fileImportStandardBook->setText(i18n("I&mport Standard Phrase Book"));
0259     fileImportStandardBook->setToolTip(i18n("Imports a standard phrase book and adds its contents to the phrase book"));
0260     fileImportStandardBook->setWhatsThis(i18n("Imports a standard phrase book and adds its contents to the phrase book"));
0261 
0262     fileExport = actionCollection()->addAction(QStringLiteral("file_export"));
0263     fileExport->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
0264     fileExport->setText(i18n("&Export..."));
0265     connect(fileExport, &QAction::triggered, this, &PhraseBookDialog::slotExportPhrasebook);
0266     fileExport->setToolTip(i18n("Exports the currently selected phrase(s) or phrase book(s) into a file"));
0267     fileExport->setWhatsThis(i18n("Exports the currently selected phrase(s) or phrase book(s) into a file"));
0268 
0269     // filePrint = KStandardAction::print(this, SLOT(slotPrint()), actionCollection());
0270     // filePrint->setToolTip(i18n("Prints the currently selected phrase(s) or phrase book(s)"));
0271     // filePrint->setWhatsThis (i18n("Prints the currently selected phrase(s) or phrase book(s)"));
0272 
0273     fileClose = KStandardAction::close(this, &PhraseBookDialog::close, actionCollection());
0274     fileClose->setToolTip(i18n("Closes the window"));
0275     fileClose->setWhatsThis(i18n("Closes the window"));
0276 
0277     // The edit menu
0278     editCut = KStandardAction::cut(this, &PhraseBookDialog::slotCut, actionCollection());
0279     editCut->setToolTip(i18n("Cuts the currently selected entries from the phrase book and puts it to the clipboard"));
0280     editCut->setWhatsThis(i18n("Cuts the currently selected entries from the phrase book and puts it to the clipboard"));
0281 
0282     editCopy = KStandardAction::copy(this, &PhraseBookDialog::slotCopy, actionCollection());
0283     editCopy->setToolTip(i18n("Copies the currently selected entry from the phrase book to the clipboard"));
0284     editCopy->setWhatsThis(i18n("Copies the currently selected entry from the phrase book to the clipboard"));
0285 
0286     editPaste = KStandardAction::paste(this, &PhraseBookDialog::slotPaste, actionCollection());
0287     editPaste->setToolTip(i18n("Pastes the clipboard contents to current position"));
0288     editPaste->setWhatsThis(i18n("Pastes the clipboard contents at the current cursor position into the edit field."));
0289 
0290     editDelete = actionCollection()->addAction(QStringLiteral("edit_delete"));
0291     editDelete->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
0292     editDelete->setText(i18n("&Delete"));
0293     connect(editDelete, &QAction::triggered, this, &PhraseBookDialog::slotRemove);
0294     editDelete->setToolTip(i18n("Deletes the selected entries from the phrase book"));
0295     editDelete->setWhatsThis(i18n("Deletes the selected entries from the phrase book"));
0296 
0297     // use the absolute path to your kmouthui.rc file for testing purpose in createGUI();
0298     createGUI(QStringLiteral("phrasebookdialogui.rc"));
0299 }
0300 
0301 void PhraseBookDialog::initStandardPhraseBooks()
0302 {
0303     StandardBookList bookPaths = PhraseBook::standardPhraseBooks();
0304 
0305     KActionMenu *parent = fileImportStandardBook;
0306     QStringList currentNamePath;
0307     currentNamePath << QStringLiteral("x");
0308     QStack<KActionMenu *> stack;
0309     StandardBookList::iterator it;
0310     for (it = bookPaths.begin(); it != bookPaths.end(); ++it) {
0311         QUrl url = QUrl::fromLocalFile((*it).filename);
0312 
0313         QString namePath = QStringLiteral("x") + (*it).path;
0314         QStringList dirs = namePath.split(QLatin1Char('/'));
0315 
0316         QStringList::iterator it1 = currentNamePath.begin();
0317         QStringList::iterator it2 = dirs.begin();
0318         for (; (it1 != currentNamePath.end()) && (it2 != dirs.end()) && (*it1 == *it2); ++it1, ++it2)
0319             ;
0320 
0321         for (; it1 != currentNamePath.end(); ++it1)
0322             parent = stack.pop();
0323         for (; it2 != dirs.end(); ++it2) {
0324             stack.push(parent);
0325             KActionMenu *newParent = actionCollection()->add<KActionMenu>(*it2);
0326             newParent->setText(*it2);
0327             parent->addAction(newParent);
0328             if (parent == fileImportStandardBook)
0329                 toolbarImport->popupMenu()->addAction(newParent);
0330             parent = newParent;
0331         }
0332         currentNamePath = dirs;
0333 
0334         QAction *book = new StandardPhraseBookInsertAction(url, (*it).name, this, SLOT(slotImportPhrasebook(QUrl)), actionCollection());
0335         parent->addAction(book);
0336         if (parent == fileImportStandardBook)
0337             toolbarImport->popupMenu()->addAction(book);
0338     }
0339 }
0340 
0341 void PhraseBookDialog::connectEditor()
0342 {
0343     connect(m_ui->lineEdit, &QLineEdit::textChanged, this, &PhraseBookDialog::slotTextChanged);
0344     connect(m_ui->noKey, &QAbstractButton::clicked, this, &PhraseBookDialog::slotNoKey);
0345     connect(m_ui->customKey, &QAbstractButton::clicked, this, &PhraseBookDialog::slotCustomKey);
0346     connect(m_ui->keyButton, &KKeySequenceWidget::keySequenceChanged, this, &PhraseBookDialog::slotKeySequenceChanged);
0347 }
0348 
0349 void PhraseBookDialog::disconnectEditor()
0350 {
0351     disconnect(m_ui->lineEdit, &QLineEdit::textChanged, this, &PhraseBookDialog::slotTextChanged);
0352     disconnect(m_ui->noKey, &QAbstractButton::clicked, this, &PhraseBookDialog::slotNoKey);
0353     disconnect(m_ui->customKey, &QAbstractButton::clicked, this, &PhraseBookDialog::slotCustomKey);
0354     disconnect(m_ui->keyButton, &KKeySequenceWidget::keySequenceChanged, this, &PhraseBookDialog::slotKeySequenceChanged);
0355 }
0356 
0357 void PhraseBookDialog::selectionChanged()
0358 {
0359     disconnectEditor();
0360     bool modified = phrasebookChanged;
0361     QModelIndex selected = m_ui->treeView->selectionModel()->currentIndex();
0362     QModelIndex shortcutIndex = selected.sibling(selected.row(), 1);
0363     QStandardItem *item = m_bookModel->itemFromIndex(selected);
0364     if (!m_ui->treeView->selectionModel()->hasSelection()) {
0365         m_ui->textLabel->setText(i18n("Text of the &phrase:"));
0366         m_ui->textLabel->setEnabled(false);
0367         m_ui->phrasebox->setEnabled(false);
0368         m_ui->lineEdit->setText(QLatin1String(""));
0369         m_ui->lineEdit->setEnabled(false);
0370         m_ui->shortcutLabel->setEnabled(false);
0371         m_ui->keyButton->clearKeySequence();
0372         m_ui->keyButton->setEnabled(false);
0373         m_ui->noKey->setChecked(false);
0374         m_ui->noKey->setEnabled(false);
0375         m_ui->customKey->setChecked(false);
0376         m_ui->customKey->setEnabled(false);
0377     } else if (!item->isDropEnabled()) {
0378         // It's a phrase, since drop isn't enabled
0379         m_ui->textLabel->setText(i18n("Text of the &phrase:"));
0380         m_ui->textLabel->setEnabled(true);
0381         m_ui->phrasebox->setEnabled(true);
0382         m_ui->lineEdit->setText(m_bookModel->data(selected, Qt::DisplayRole).toString());
0383         m_ui->lineEdit->setEnabled(true);
0384         m_ui->shortcutLabel->setEnabled(true);
0385         QString shortcut = m_bookModel->data(shortcutIndex, Qt::DisplayRole).toString();
0386         m_ui->keyButton->setKeySequence(QKeySequence(shortcut));
0387         if (shortcut.isEmpty() || shortcut.isNull()) {
0388             m_ui->noKey->setChecked(true);
0389         } else {
0390             m_ui->customKey->setChecked(true);
0391         }
0392         m_ui->keyButton->setEnabled(true);
0393         m_ui->noKey->setEnabled(true);
0394         m_ui->customKey->setEnabled(true);
0395     } else {
0396         // It's a book since drop is enabled
0397         m_ui->textLabel->setText(i18n("Name of the &phrase book:"));
0398         m_ui->textLabel->setEnabled(true);
0399         m_ui->phrasebox->setEnabled(true);
0400         m_ui->lineEdit->setText(m_bookModel->data(selected, Qt::DisplayRole).toString());
0401         m_ui->lineEdit->setEnabled(true);
0402         m_ui->shortcutLabel->setEnabled(false);
0403         m_ui->keyButton->clearKeySequence(); // Can't have a shortcut for a book
0404         m_ui->keyButton->setEnabled(false);
0405         m_ui->noKey->setChecked(false);
0406         m_ui->noKey->setEnabled(false);
0407         m_ui->customKey->setChecked(false);
0408         m_ui->customKey->setEnabled(false);
0409     }
0410     connectEditor();
0411     phrasebookChanged = modified;
0412 }
0413 
0414 bool PhraseBookDialog::queryClose()
0415 {
0416     if (phrasebookChanged) {
0417         int answer = KMessageBox::questionTwoActionsCancel(
0418             this,
0419             i18n("<qt>There are unsaved changes.<br />Do you want to apply the changes before closing the \"phrase book\" window or discard the changes?</qt>"),
0420             i18n("Closing \"Phrase Book\" Window"),
0421             KStandardGuiItem::apply(),
0422             KStandardGuiItem::discard(),
0423             KStandardGuiItem::cancel(),
0424             QStringLiteral("AutomaticSave"));
0425         if (answer == KMessageBox::ButtonCode::PrimaryAction) {
0426             slotSave();
0427             return true;
0428         }
0429         return (answer == KMessageBox::ButtonCode::SecondaryAction);
0430     }
0431     return true;
0432 }
0433 
0434 void PhraseBookDialog::slotTextChanged(const QString &s)
0435 {
0436     QModelIndex selected = m_ui->treeView->selectionModel()->currentIndex();
0437     QModelIndex textIndex = selected.sibling(selected.row(), kTextColumn);
0438     if (textIndex.isValid()) {
0439         m_bookModel->setData(textIndex, s);
0440     }
0441 }
0442 
0443 void PhraseBookDialog::slotNoKey()
0444 {
0445     m_ui->noKey->setChecked(true);
0446     m_ui->customKey->setChecked(false); // This shouldn't be needed because of the groupbox... FIXME
0447 
0448     QModelIndex selected = m_ui->treeView->selectionModel()->currentIndex();
0449     QModelIndex shortcutIndex = selected.sibling(selected.row(), kShortcutColumn);
0450     if (shortcutIndex.isValid())
0451         m_bookModel->setData(shortcutIndex, QString());
0452     m_ui->keyButton->clearKeySequence();
0453 }
0454 
0455 void PhraseBookDialog::slotCustomKey()
0456 {
0457     m_ui->keyButton->captureKeySequence();
0458 }
0459 
0460 void PhraseBookDialog::slotKeySequenceChanged(const QKeySequence &sequence)
0461 {
0462     if (sequence.isEmpty()) {
0463         slotNoKey();
0464     } else
0465         setShortcut(sequence);
0466 }
0467 
0468 void PhraseBookDialog::setShortcut(const QKeySequence &sequence)
0469 {
0470     // Check whether the shortcut is valid
0471     //   const QList<QKeySequence> cutList = cut.toList();
0472     //   for (int i = 0; i < cutList.count(); i++) {
0473     //      const QKeySequence& seq = cutList[i];
0474     //      //const KKey& key = seq.key(0);
0475     // #ifdef __GNUC__
0476     // #warning "kde 4 port it";
0477     // #endif
0478     // #if 0
0479     //      if (key.modFlags() == 0 && key.sym() < 0x3000
0480     //          && QChar(key.sym()).isLetterOrNumber())
0481     //      {
0482     //         QString s = i18n("In order to use the '%1' key as a shortcut, "
0483     //                          "it must be combined with the "
0484     //                          "Win, Alt, Ctrl, and/or Shift keys.", QChar(key.sym()));
0485     //         KMessageBox::error( this, s, i18n("Invalid Shortcut Key") );
0486     //         return;
0487     //      }
0488     // #endif
0489     //   }
0490     QModelIndex selected = m_ui->treeView->selectionModel()->currentIndex();
0491     QModelIndex shortcutIndex = selected.sibling(selected.row(), kShortcutColumn);
0492     if (shortcutIndex.isValid())
0493         m_bookModel->setData(shortcutIndex, sequence.toString());
0494     //// If key isn't already in use,
0495     //   // Update display
0496     m_ui->noKey->setChecked(false);
0497     m_ui->customKey->setChecked(true);
0498     m_ui->keyButton->setKeySequence(sequence);
0499 }
0500 
0501 void PhraseBookDialog::contextMenuRequested(const QPoint &pos)
0502 {
0503     QString name;
0504     if (m_ui->treeView->selectionModel()->hasSelection())
0505         name = QStringLiteral("phrasebook_popup_sel");
0506     else
0507         name = QStringLiteral("phrasebook_popup_nosel");
0508 
0509     QMenu *popup = (QMenu *)factory()->container(name, this);
0510     if (popup != nullptr) {
0511         popup->popup(m_ui->treeView->mapToGlobal(pos), nullptr);
0512     }
0513 }
0514 
0515 void PhraseBookDialog::slotRemove()
0516 {
0517     if (m_ui->treeView->selectionModel()->hasSelection()) {
0518         QList<QModelIndex> selected = m_ui->treeView->selectionModel()->selectedRows();
0519         std::sort(selected.begin(), selected.end());
0520         // Iterate over the rows backwards so we don't modify the .row of any indexes in selected.
0521         for (int i = selected.size() - 1; i >= 0; --i) {
0522             QModelIndex index = selected.at(i);
0523             m_bookModel->removeRows(index.row(), 1, index.parent());
0524         }
0525     }
0526 }
0527 
0528 void PhraseBookDialog::slotCut()
0529 {
0530     slotCopy();
0531     slotRemove();
0532 }
0533 
0534 void PhraseBookDialog::slotCopy()
0535 {
0536     const QList<QModelIndex> selected = m_ui->treeView->selectionModel()->selectedRows();
0537     QString xml;
0538     for (const QModelIndex index : selected) {
0539         xml += serializeBook(index);
0540     }
0541     QMimeData *data = new QMimeData();
0542     data->setText(xml);
0543     QApplication::clipboard()->setMimeData(data);
0544 }
0545 
0546 void PhraseBookDialog::slotPaste()
0547 {
0548     const QMimeData *data = QApplication::clipboard()->mimeData();
0549     QModelIndex index = m_ui->treeView->selectionModel()->currentIndex();
0550     QStandardItem *item = m_bookModel->itemFromIndex(index);
0551     while (item != nullptr && !item->isDropEnabled())
0552         item = item->parent();
0553     QDomDocument document;
0554     document.setContent(data->text());
0555     QDomNode node = document.documentElement();
0556     deserializeBook(node, item);
0557 }
0558 
0559 QModelIndex PhraseBookDialog::getCurrentParent()
0560 {
0561     QModelIndex currentIndex = m_ui->treeView->currentIndex();
0562     QStandardItem *item = m_bookModel->itemFromIndex(currentIndex);
0563     if (item != nullptr && !item->isDropEnabled()) // If it's not a book
0564         currentIndex = currentIndex.parent();
0565     return currentIndex;
0566 }
0567 
0568 void PhraseBookDialog::focusNewItem(const QModelIndex &parent, QStandardItem *item)
0569 {
0570     m_ui->treeView->expand(parent);
0571     QModelIndex newIndex = m_bookModel->indexFromItem(item);
0572     m_ui->treeView->setCurrentIndex(newIndex);
0573 
0574     m_ui->lineEdit->selectAll();
0575     m_ui->lineEdit->setFocus();
0576 }
0577 
0578 void PhraseBookDialog::slotAddPhrasebook()
0579 {
0580     QModelIndex parentIndex = getCurrentParent();
0581     QStandardItem *parent = m_bookModel->itemFromIndex(parentIndex);
0582     QStandardItem *item = new QStandardItem(kPhraseBookIcon, i18n("(New Phrase Book)"));
0583     QStandardItem *shortcutItem = new QStandardItem();
0584 
0585     QList<QStandardItem *> items;
0586     items << item << shortcutItem;
0587     if (parent)
0588         parent->appendRow(items);
0589     else
0590         m_bookModel->appendRow(items);
0591     focusNewItem(parentIndex, item);
0592 }
0593 
0594 void PhraseBookDialog::slotAddPhrase()
0595 {
0596     QModelIndex parentIndex = getCurrentParent();
0597     QStandardItem *parent = m_bookModel->itemFromIndex(parentIndex);
0598     QStandardItem *item = new QStandardItem(kPhraseIcon, i18n("(New Phrase)"));
0599     QStandardItem *shortcutItem = new QStandardItem();
0600 
0601     item->setDropEnabled(false);
0602     shortcutItem->setDropEnabled(false);
0603 
0604     QList<QStandardItem *> items;
0605     items << item << shortcutItem;
0606     if (parent)
0607         parent->appendRow(items);
0608     else
0609         m_bookModel->appendRow(items);
0610     focusNewItem(parentIndex, item);
0611 }
0612 
0613 void PhraseBookDialog::slotSave()
0614 {
0615     QString standardBook = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("standard.phrasebook"));
0616     if (!standardBook.isNull() && !standardBook.isEmpty()) {
0617         QFile file(standardBook);
0618         file.open(QIODevice::WriteOnly);
0619         file.write(serializeBook(QModelIndex()).toUtf8());
0620         file.close();
0621         Q_EMIT phrasebookConfirmed();
0622         phrasebookChanged = false;
0623         fileSave->setEnabled(false);
0624     }
0625 }
0626 
0627 void PhraseBookDialog::slotImportPhrasebook()
0628 {
0629     QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Import Phrasebook"), QUrl(), i18n("Phrase Books (*.phrasebook);Plain Text Files (*.txt);All Files (*)"));
0630 
0631     slotImportPhrasebook(url);
0632 }
0633 
0634 void PhraseBookDialog::slotImportPhrasebook(const QUrl &url)
0635 {
0636     if (!url.isEmpty()) {
0637         QModelIndex parentIndex = getCurrentParent();
0638         QStandardItem *parentItem = m_bookModel->itemFromIndex(parentIndex);
0639         QFile file(url.toLocalFile());
0640         if (file.open(QIODevice::ReadOnly)) {
0641             QDomDocument document;
0642             document.setContent(&file);
0643 
0644             QDomNode node = document.documentElement();
0645             QStandardItem *item = nullptr;
0646             if (node.hasAttributes()) {
0647                 // It has attributes, so add it directly
0648                 item = deserializeBook(node, parentItem);
0649             } else {
0650                 // It has no attributes, so add all it's children
0651                 QDomNodeList nodes = node.childNodes();
0652                 for (int i = 0; i < nodes.count(); ++i) {
0653                     QDomNode child = nodes.at(i);
0654                     item = deserializeBook(child, parentItem);
0655                 }
0656             }
0657             focusNewItem(parentIndex, item);
0658         } else {
0659             qDebug() << "Unable to open file " << url.toLocalFile();
0660             KMessageBox::error(this, i18n("There was an error loading file\n%1", url.toLocalFile()));
0661         }
0662     }
0663 }
0664 
0665 void PhraseBookDialog::slotExportPhrasebook()
0666 {
0667     // Get the current book or if a phrase is selected get it's parent book to export.
0668     QModelIndex parentIndex = getCurrentParent();
0669     QString content = kWholeBookXML.arg(serializeBook(parentIndex));
0670 
0671     QUrl url = QFileDialog::getSaveFileUrl(this, i18n("Export Phrase Book"), QUrl(), i18n("Phrase Books (*.phrasebook)"));
0672     QFile file(url.toLocalFile());
0673     if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
0674         QTextStream out(&file);
0675         out << content;
0676         file.close();
0677     } else {
0678         KMessageBox::error(this, i18n("There was an error saving file\n%1", url.toLocalFile()));
0679     }
0680 }
0681 
0682 // void PhraseBookDialog::slotPrint()
0683 //{
0684 //    if (printer == 0) {
0685 //      printer = new QPrinter();
0686 //    }
0687 
0688 //   QPrintDialog *printDialog = KdePrint::createPrintDialog(printer, this);
0689 
0690 //   if (printDialog->exec()) {
0691 //      PhraseBook book;
0692 //      //treeView->fillBook (&book, treeView->hasSelectedItems());
0693 
0694 //      book.print(printer);
0695 //   }
0696 //   delete printDialog;
0697 //}