/* * Copyright 2008-2009 Ben Boeckel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ // Header include #include "SigmodrUI.h" // Sigmodr includes #include "SigmodrPreferences.h" #include "SigmodrPreferencesWidget.h" // Sigmodr tree includes #include // Sigmodr widget includes #include // Sigmod includes #include #include // KDE includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Qt includes #include #include #include #include #include using namespace Sigmod; using namespace Sigmodr::Widgets; using namespace Sigmodr::Tree; using namespace Sigmodr; SigmodrUI::SigmodrUI(QWidget* parent) : KXmlGuiWindow(parent) { setupUi(this); setupActions(); setAcceptDrops(true); buttonApply->setIcon(KIcon("dialog-ok-apply")); buttonReset->setIcon(KIcon("edit-undo")); buttonValidate->setIcon(KIcon("script-error")); boxButtons->setEnabled(false); buttonValidate->setEnabled(false); splitter->setSizes(QList() << SigmodrPreferences::treeWidth() << SigmodrPreferences::panelWidth()); connect(buttonApply, SIGNAL(clicked()), this, SLOT(update())); KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); if (args && args->count()) { for(int i = 0; i < args->count(); ++i) openGame(args->url(i)); args->clear(); } else if (SigmodrPreferences::reloadOnStart()) { QStringList urls = SigmodrPreferences::openFiles(); foreach (const QString& url, urls) openGame(KUrl(url)); } setAutoSaveSettings("MainWindow", true); } SigmodrUI::~SigmodrUI() { closeAllGames(true); SigmodrPreferences::self()->writeConfig(); KGlobal::config()->sync(); } void SigmodrUI::dragEnterEvent(QDragEnterEvent* event) { const QMimeData* data = event->mimeData(); if (data->hasFormat("application/x-sigmod+xml") || data->hasFormat("text/uri-list")) event->acceptProposedAction(); } void SigmodrUI::dropEvent(QDropEvent* event) { const QMimeData* data = event->mimeData(); bool loaded = false; event->setDropAction(Qt::CopyAction); if (data->hasFormat("application/x-sigmod+xml")) { QDomDocument xml; QString error; int line; int column; if (xml.setContent(data->data("application/x-sigmod+xml"), &error, &line, &column)) { if (xml.doctype().name() == "Game") { treeSigmod->addGame(new Game(xml.documentElement())); loaded = true; } else KMessageBox::error(this, QString("The file is not a Sigmod. The doctype was reported to be %1").arg(xml.doctype().name()), "Invalid Sigmod"); } else KMessageBox::error(this, QString("%1 at line %2, column %3").arg(error).arg(line).arg(column), "XML Error"); } else if (data->hasFormat("text/uri-list")) { QList urls = data->urls(); foreach (const QUrl& url, urls) loaded = openGame(url); } if (loaded) event->acceptProposedAction(); } void SigmodrUI::update() { treeSigmod->update(treeSigmod->currentIndex()); } void SigmodrUI::closeEvent(QCloseEvent* event) { KRecentFilesAction *recent = qobject_cast(actionCollection()->action(KStandardAction::name(KStandardAction::OpenRecent))); recent->saveEntries(KGlobal::config()->group("Recent Files")); if (closeAllGames()) event->accept(); else event->ignore(); } void SigmodrUI::resizeEvent(QResizeEvent* event) { event->accept(); on_splitter_splitterMoved(); } void SigmodrUI::setChangedTitle(const bool changed) { setCaption(treeSigmod->description(m_editedIndex), changed); } void SigmodrUI::setDirty(const bool dirty) { setDirty(dirty, m_editedIndex); setChangedTitle(dirty); actionCollection()->action("file_save")->setEnabled(dirty); } void SigmodrUI::setDirty(const bool dirty, const QModelIndex& index) { treeSigmod->setDirty(treeSigmod->game(index), dirty); } void SigmodrUI::newGame() { Game* game = new Game; treeSigmod->addGame(game); } void SigmodrUI::openGame() { KUrl::List urls = KFileDialog::getOpenUrls(KUrl("kfiledialog:///sigmod"), "*.smod|Sigmod files", this); foreach (const KUrl& url, urls) { if (!url.isEmpty()) openGame(url); } } bool SigmodrUI::openGame(const KUrl& url) { bool opened = false; if (treeSigmod->isOpen(url)) { KMessageBox::error(this, "File is already opened", "Sigmod error"); return false; } if (url.isValid()) { if (url.isLocalFile()) opened = openGame(url.path()); else { QString temp; if (KIO::NetAccess::download(url, temp, this)) { opened = openGame(temp, true); KIO::NetAccess::removeTempFile(temp); } else KMessageBox::error(this, KIO::NetAccess::lastErrorString(), "KIO Error"); } } else KMessageBox::error(this, "The URL is not valid", "Malformed URL"); if (opened) qobject_cast(actionCollection()->action(KStandardAction::name(KStandardAction::OpenRecent)))->addUrl(url); return opened; } bool SigmodrUI::openGame(const QString& path, const bool isRemote) { QFile file(path); if (file.open(QIODevice::ReadOnly)) { QDomDocument xml; QString error; int line; int column; if (xml.setContent(&file, &error, &line, &column)) { if (xml.doctype().name() == "Game") { treeSigmod->addGame(new Game(xml.documentElement()), isRemote ? KUrl() : KUrl(path)); return true; } else KMessageBox::error(this, QString("The file is not a Sigmod. The doctype was reported to be %1").arg(xml.doctype().name()), "Invalid Sigmod"); } else KMessageBox::error(this, QString("%1 at line %2, column %3").arg(error).arg(line).arg(column), "XML Error"); file.close(); } else KMessageBox::error(this, file.errorString(), "File Error"); return false; } void SigmodrUI::saveGame() { saveGame(treeSigmod->game(treeSigmod->currentIndex())); } void SigmodrUI::saveGame(const Game* game) { const KUrl url = treeSigmod->url(game); if (url.isEmpty()) saveAsGame(game); else saveGame(game, url); } bool SigmodrUI::saveGame(const Game* game, const KUrl& url) { if (game) { if (url.isLocalFile()) { QFile file(url.path()); if (file.open(QIODevice::WriteOnly)) { file.write(Object::xml(game).toByteArray()); treeSigmod->setDirty(game, false); file.close(); return true; } else KMessageBox::error(this, "Error saving the Sigmod!", "Save error"); } else { KTemporaryFile temp; if (temp.open()) { temp.write(Object::xml(game).toByteArray()); if (KIO::NetAccess::upload(temp.fileName(), url, this)) { treeSigmod->setDirty(game, false); return true; } else KMessageBox::error(this, KIO::NetAccess::lastErrorString(), "KIO Error"); } else KMessageBox::error(this, "Error saving the Sigmod!", "Save error"); temp.close(); } } return false; } void SigmodrUI::saveAsGame() { saveAsGame(treeSigmod->game(treeSigmod->currentIndex())); } void SigmodrUI::saveAsGame(const Game* game) { if (game) { KUrl url = KFileDialog::getSaveUrl(KUrl("kfiledialog:///sigmod"), "*.smod|Sigmod files", this, "Open Sigmod"); if (!url.isEmpty()) { if (saveGame(game, url)) treeSigmod->setUrl(game, url); } } } void SigmodrUI::downloadGame() { KNS::Engine engine(this); if (engine.init("sigmod.knsrc")) engine.downloadDialogModal(); } void SigmodrUI::uploadGame() { uploadGame(treeSigmod->game(treeSigmod->currentIndex())); } void SigmodrUI::uploadGame(const Game* game) { KUrl url = treeSigmod->url(game); if (url == KUrl()) { KMessageBox::error(this, "The Sigmod has not been saved!", "Upload error"); return; } if (!url.isLocalFile()) { KMessageBox::error(this, "The Sigmod is not local!", "Upload error"); return; } KNS::Engine engine(this); if (engine.init("sigmod.knsrc")) { if (!engine.uploadDialogModal(url.path())) KMessageBox::error(this, "An error occurred attempting to upload!", "Upload error"); } } bool SigmodrUI::closeWidget() { bool closed = true; if (formPanel->widget() && boxButtons->isEnabled()) { switch (KMessageBox::questionYesNoCancel(this, "You have unsaved changes, would you like to save them?", QString("Unsaved %1").arg(qobject_cast(formPanel->widget())->object()->className()))) { case KMessageBox::Yes: qobject_cast(formPanel->widget())->apply(); break; case KMessageBox::No: qobject_cast(formPanel->widget())->discard(); break; case KMessageBox::Cancel: closed = false; break; } } if (closed) { delete formPanel->takeWidget(); m_editedIndex = QModelIndex(); formPanel->setWidget(NULL); boxButtons->setEnabled(false); buttonValidate->setEnabled(false); } return closed; } void SigmodrUI::closeGame() { closeGame(treeSigmod->game(treeSigmod->currentIndex())); setChangedTitle(treeSigmod->dirty()); } bool SigmodrUI::closeGame(const Game* game, const bool force) { if (game) { const ObjectUI* widget = qobject_cast(formPanel->widget()); if (widget && (game == widget->object()->game()) && !closeWidget()) return false; if (treeSigmod->dirty(game)) { int result; if (force) result = KMessageBox::questionYesNo(this, "You have unsaved changes, would you like to save them?", "Unsaved Sigmod"); else result = KMessageBox::questionYesNoCancel(this, "You have unsaved changes, would you like to save them?", "Unsaved Sigmod"); switch (result) { case KMessageBox::Yes: saveGame(game); case KMessageBox::No: break; case KMessageBox::Cancel: return false; } } treeSigmod->deleteGame(game); } return true; } bool SigmodrUI::closeAllGames(const bool force) { QList games = treeSigmod->openedGames(); if (games.size()) SigmodrPreferences::setOpenFiles(treeSigmod->urls()); for (int i = 0; i < games.size(); ) { if (closeGame(games[i], force)) games.removeAt(i); else ++i; } if (!games.size() || force) close(); return !games.size(); } void SigmodrUI::copyObject() { // m_clipboard = treeSigmod->copy(treeSigmod->currentIndex()); } void SigmodrUI::pasteObject() { // treeSigmod->paste(treeSigmod->currentIndex(), m_clipboard); } void SigmodrUI::preferences() { if (KConfigDialog::showDialog("sigmodr-prefs")) return; KConfigDialog* dialog = new KConfigDialog(this, "sigmodr-prefs", SigmodrPreferences::self()); SigmodrPreferencesWidget* widget = new SigmodrPreferencesWidget; dialog->addPage(widget, i18nc("General settings tab", "General"), "configure"); connect(dialog, SIGNAL(okClicked()), widget, SLOT(save())); dialog->exec(); } void SigmodrUI::toggleMenubar() { if (menuBar()->isVisible()) menuBar()->hide(); else menuBar()->show(); } void SigmodrUI::on_splitter_splitterMoved() { SigmodrPreferences::setTreeWidth(splitter->sizes()[0]); SigmodrPreferences::setPanelWidth(splitter->sizes()[1]); } void SigmodrUI::on_treeSigmod_clicked(const QModelIndex& index) { if (!(treeSigmod->model()->flags(index) & Qt::ItemIsEnabled)) return; switch (index.column()) { case 1: { ObjectUI* editor = treeSigmod->editorWidget(index); if (editor && closeWidget()) { connect(editor, SIGNAL(changed(bool)), boxButtons, SLOT(setEnabled(bool))); connect(editor, SIGNAL(saved()), this, SLOT(setDirty())); // TODO: Better way? connect(editor, SIGNAL(saved()), treeSigmod, SLOT(doItemsLayout())); connect(buttonApply, SIGNAL(clicked()), editor, SLOT(apply())); connect(buttonReset, SIGNAL(clicked()), editor, SLOT(discard())); connect(buttonValidate, SIGNAL(clicked()), editor, SLOT(validate())); m_editedIndex = index; setChangedTitle(treeSigmod->dirty(treeSigmod->game(index))); buttonValidate->setEnabled(true); formPanel->setWidget(editor); formPanel->show(); } break; } case 2: if ((m_editedIndex.internalId() != index.internalId()) || closeWidget()) { if (treeSigmod->model()->removeRow(index.row(), index.parent())) setDirty(true, index); } break; case 3: if (treeSigmod->model()->insertRow(treeSigmod->model()->rowCount(index), index)) setDirty(true, index); break; default: break; } } void SigmodrUI::setupActions() { KStandardAction::openNew(this, SLOT(newGame()), actionCollection()); KStandardAction::open(this, SLOT(openGame()), actionCollection()); KRecentFilesAction* recent = KStandardAction::openRecent(this, SLOT(openGame(const KUrl&)), actionCollection()); recent->loadEntries(KGlobal::config()->group("Recent Files")); KStandardAction::save(this, SLOT(saveGame()), actionCollection()); KStandardAction::saveAs(this, SLOT(saveAsGame()), actionCollection()); KStandardAction::close(this, SLOT(closeGame()), actionCollection()); KStandardAction::quit(this, SLOT(closeAllGames()), actionCollection()); KStandardAction::cut(actionCollection()); KStandardAction::copy(actionCollection()); KStandardAction::paste(actionCollection()); KAction* download = actionCollection()->addAction("download_sigmod"); download->setText(i18n("Download")); download->setIcon(KIcon("arrow-down-double")); download->setShortcut(Qt::CTRL + Qt::Key_D); connect(download, SIGNAL(triggered()), this, SLOT(downloadGame())); KAction* upload = actionCollection()->addAction("upload_sigmod"); upload->setText(i18n("Upload")); upload->setIcon(KIcon("arrow-up-double")); upload->setShortcut(Qt::CTRL + Qt::Key_U); upload->setEnabled(false); connect(upload, SIGNAL(triggered()), this, SLOT(uploadGame())); KStandardAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection()); KStandardAction::configureToolbars(this, SLOT(configureToolbars()), actionCollection()); createStandardStatusBarAction(); setStandardToolBarMenuEnabled(true); KStandardAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), actionCollection()); KStandardAction::preferences(this, SLOT(preferences()), actionCollection()); setHelpMenuEnabled(true); createGUI(); }