diff options
Diffstat (limited to 'quanta/utility')
-rw-r--r-- | quanta/utility/Makefile.am | 20 | ||||
-rw-r--r-- | quanta/utility/myprocess.h | 36 | ||||
-rw-r--r-- | quanta/utility/newstuff.cpp | 144 | ||||
-rw-r--r-- | quanta/utility/newstuff.h | 121 | ||||
-rw-r--r-- | quanta/utility/qpevents.cpp | 403 | ||||
-rw-r--r-- | quanta/utility/qpevents.h | 90 | ||||
-rw-r--r-- | quanta/utility/quantabookmarks.cpp | 399 | ||||
-rw-r--r-- | quanta/utility/quantabookmarks.h | 99 | ||||
-rw-r--r-- | quanta/utility/quantacommon.cpp | 753 | ||||
-rw-r--r-- | quanta/utility/quantacommon.h | 225 | ||||
-rw-r--r-- | quanta/utility/quantanetaccess.cpp | 268 | ||||
-rw-r--r-- | quanta/utility/quantanetaccess.h | 86 | ||||
-rw-r--r-- | quanta/utility/resource.h | 59 | ||||
-rw-r--r-- | quanta/utility/tagaction.cpp | 1285 | ||||
-rw-r--r-- | quanta/utility/tagaction.h | 137 | ||||
-rw-r--r-- | quanta/utility/tagactionmanager.cpp | 83 | ||||
-rw-r--r-- | quanta/utility/tagactionmanager.h | 74 | ||||
-rw-r--r-- | quanta/utility/tagactionset.cpp | 1172 | ||||
-rw-r--r-- | quanta/utility/tagactionset.h | 161 | ||||
-rw-r--r-- | quanta/utility/toolbartabwidget.cpp | 351 | ||||
-rw-r--r-- | quanta/utility/toolbartabwidget.h | 111 | ||||
-rw-r--r-- | quanta/utility/toolbarxmlgui.cpp | 27 | ||||
-rw-r--r-- | quanta/utility/toolbarxmlgui.h | 32 |
23 files changed, 6136 insertions, 0 deletions
diff --git a/quanta/utility/Makefile.am b/quanta/utility/Makefile.am new file mode 100644 index 00000000..56522430 --- /dev/null +++ b/quanta/utility/Makefile.am @@ -0,0 +1,20 @@ +noinst_LTLIBRARIES = libutility.la + +METASOURCES = AUTO + +libutility_la_SOURCES = quantacommon.cpp tagaction.cpp toolbartabwidget.cpp \ + toolbarxmlgui.cpp newstuff.cpp quantanetaccess.cpp qpevents.cpp quantabookmarks.cpp \ + tagactionmanager.cpp tagactionset.cpp + +AM_CPPFLAGS = -I$(top_srcdir)/quanta/src \ + -I$(top_srcdir)/quanta/parsers \ + -I$(top_srcdir)/quanta/messages \ + -I$(top_srcdir)/quanta/dialogs/tagdialogs \ + -I$(top_srcdir)/quanta/parts/kafka \ + -I$(top_srcdir)/quanta/project \ + -I$(top_srcdir)/lib \ + $(KNEWSTUFF_INCLUDES) \ + $(KMDI_INCLUDES) $(all_includes) + +libutility_la_LDFLAGS = $(all_libraries) +noinst_HEADERS = qpevents.h tagactionmanager.h tagactionset.h diff --git a/quanta/utility/myprocess.h b/quanta/utility/myprocess.h new file mode 100644 index 00000000..a495cdf9 --- /dev/null +++ b/quanta/utility/myprocess.h @@ -0,0 +1,36 @@ +/*************************************************************************** + myprocess.h + ------------------- + begin : Jun 25 2003 + copyright : (C) 2003 by Andras Mantia <amantia@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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; version 2 of the License. * + * * + ***************************************************************************/ + +#ifndef MYPROCESS_H +#define MYPROCESS_H + +#include <kprocess.h> + +class MyProcess:public KProcess +{ + Q_OBJECT + + public: + MyProcess(); + virtual ~MyProcess() {}; + + protected: + virtual int commSetupDoneC(); +}; + + + +#endif + diff --git a/quanta/utility/newstuff.cpp b/quanta/utility/newstuff.cpp new file mode 100644 index 00000000..f6cbf397 --- /dev/null +++ b/quanta/utility/newstuff.cpp @@ -0,0 +1,144 @@ +/*************************************************************************** + newstuff.cpp - description + ------------------- + begin : Tue Jun 22 12:19:55 2004 + copyright : (C) 2004 by Andras Mantia <amantia@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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; version 2 of the License. * + * * + ***************************************************************************/ + //qt includes +#include <qfileinfo.h> + +//kde includes +#include <kglobal.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kstandarddirs.h> +#include <ktar.h> + +//app includes +#include "newstuff.h" +#include "dtds.h" +#include "resource.h" +#include "quantacommon.h" +#include "qextfileinfo.h" + +void QNewDTEPStuff::installResource() +{ + bool ok = true; + KTar tar(m_tarName, "application/x-gzip"); + if (tar.open(IO_ReadOnly)) + { + const KArchiveDirectory *directory = tar.directory(); + QString dtepDir =KGlobal::dirs()->saveLocation("data") + resourceDir + "dtep/"; + QString dtdName = (*directory->entries().at(0)); + if (dtdName.isEmpty()) + { + ok = false; + } else + { + directory->copyTo(dtepDir, true); + DTDs::ref()->slotLoadDTEP(dtepDir + dtdName, false); + } + tar.close(); + } else + ok = false; + if (!ok) + KMessageBox::error(parentWidget(), i18n("There was an error with the downloaded DTEP tarball file. Possible causes are damaged archive or invalid directory structure in the archive."), i18n("DTEP Installation Error")); +} + +QNewToolbarStuff::QNewToolbarStuff(const QString &type, QWidget *parentWidget) + :KNewStuffSecure(type, parentWidget) +{ + connect(this, SIGNAL(loadToolbarFile(const KURL&)), parentWidget, SLOT(slotLoadToolbarFile(const KURL&))); +} + + +void QNewToolbarStuff::installResource() +{ + KURL destURL = KURL::fromPathOrURL(KGlobal::dirs()->saveLocation("data") + resourceDir + "toolbars/" + QFileInfo(m_tarName).fileName()); + bool ok = true; + if (QuantaCommon::checkOverwrite(destURL, parentWidget())) + { + if (!QExtFileInfo::copy(KURL::fromPathOrURL(m_tarName), destURL, -1, true, false, parentWidget())) + ok = false; + else + { + if (KMessageBox::questionYesNo(parentWidget(), i18n("Do you want to load the newly downloaded toolbar?"), i18n("Load Toolbar"), i18n("Load"), KStdGuiItem::cancel()) == KMessageBox::Yes) + { + emit loadToolbarFile(destURL); + } + } + if (!ok) + KMessageBox::error(parentWidget(), i18n("There was an error with the downloaded toolbar tarball file. Possible causes are damaged archive or invalid directory structure in the archive."), i18n("Toolbar Installation Error")); + } +} + +QNewTemplateStuff::QNewTemplateStuff(const QString &type, QWidget *parentWidget) + :KNewStuffSecure(type, parentWidget) +{ + connect(this, SIGNAL(openFile(const KURL&)), parentWidget, SLOT(slotFileOpen(const KURL&))); +} + + +void QNewTemplateStuff::installResource() +{ + KURL destURL = KURL::fromPathOrURL(KGlobal::dirs()->saveLocation("data") + resourceDir + "templates/" + QFileInfo(m_tarName).fileName()); + bool ok = true; + if (QuantaCommon::checkOverwrite(destURL, parentWidget())) + { + if (!QExtFileInfo::copy(KURL::fromPathOrURL(m_tarName), destURL, -1, true, false, parentWidget())) + ok = false; + else + { + if (KMessageBox::questionYesNo(parentWidget(), i18n("Do you want to open the newly downloaded template?"), i18n("Open Template"), KStdGuiItem::open(), KStdGuiItem::cancel()) == KMessageBox::Yes) + { + emit openFile(destURL); + } + } + if (!ok) + KMessageBox::error(parentWidget(), i18n("There was an error with the downloaded template file."), i18n("Template Installation Error")); + } +} + +void QNewScriptStuff::installResource() +{ + bool ok = true; + KTar tar(m_tarName, "application/x-gzip"); + if (tar.open(IO_ReadOnly)) + { + const KArchiveDirectory *directory = tar.directory(); + QString scriptsDir =KGlobal::dirs()->saveLocation("data") + resourceDir + "scripts/"; + directory->copyTo(scriptsDir, true); + tar.close(); + } else + ok = false; + + if (!ok) + KMessageBox::error(parentWidget(), i18n("There was an error with the downloaded script tarball file. Possible causes are damaged archive or invalid directory structure in the archive."), i18n("Script Installation Error")); +} + +void QNewDocStuff::installResource() +{ + bool ok = true; + KTar tar(m_tarName, "application/x-gzip"); + if (tar.open(IO_ReadOnly)) + { + const KArchiveDirectory *directory = tar.directory(); + QString docDir =KGlobal::dirs()->saveLocation("data") + resourceDir + "doc/"; + directory->copyTo(docDir, true); + tar.close(); + } else + ok = false; + + if (!ok) + KMessageBox::error(parentWidget(), i18n("There was an error with the downloaded script tarball file. Possible causes are damaged archive or invalid directory structure in the archive."), i18n("Documentation Installation Error")); +} + +#include "newstuff.moc" diff --git a/quanta/utility/newstuff.h b/quanta/utility/newstuff.h new file mode 100644 index 00000000..dd2ad104 --- /dev/null +++ b/quanta/utility/newstuff.h @@ -0,0 +1,121 @@ +/*************************************************************************** + newstuff.h - description + ------------------- + begin : Tue Jun 22 12:19:55 2004 + copyright : (C) 2004 by Andras Mantia <amantia@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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; version 2 of the License. * + * * + ***************************************************************************/ + +#ifndef NEWSTUFF_H +#define NEWSTUFF_H + +//qt includes +#include <qobject.h> + +//kde includes +#include <knewstuff/knewstuffsecure.h> +/** +Makes possible downloading and installing a DTEP resource files from a server. + +@author Andras Mantia +*/ + +class KURL; + +class QNewDTEPStuff: public KNewStuffSecure +{ + Q_OBJECT + +public: + QNewDTEPStuff(const QString &type, QWidget *parentWidget=0) + :KNewStuffSecure(type, parentWidget){}; + ~QNewDTEPStuff() {}; + +private: + virtual void installResource(); +}; + +/** +Makes possible downloading and installing a Toolbar resource files from a server. + +@author Andras Mantia +*/ +class QNewToolbarStuff: public KNewStuffSecure +{ + Q_OBJECT + +public: + QNewToolbarStuff(const QString &type, QWidget *parentWidget=0); + ~QNewToolbarStuff() {}; + +signals: + void loadToolbarFile(const KURL&); + +private: + virtual void installResource(); +}; + +/** +Makes possible downloading and installing a template resource files from a server. + +@author Andras Mantia +*/ +class QNewTemplateStuff: public KNewStuffSecure +{ + Q_OBJECT + +public: + QNewTemplateStuff(const QString &type, QWidget *parentWidget=0); + ~QNewTemplateStuff() {}; + +signals: + void openFile(const KURL&); + +private: + virtual void installResource(); +}; + +/** +Makes possible downloading and installing a script resource files from a server. + +@author Andras Mantia +*/ +class QNewScriptStuff: public KNewStuffSecure +{ + Q_OBJECT + +public: + QNewScriptStuff(const QString &type, QWidget *parentWidget=0) + :KNewStuffSecure(type, parentWidget){}; + ~QNewScriptStuff() {}; + +private: + virtual void installResource(); +}; + +/** +Makes possible downloading and installing a documentation resource files from a server. + +@author Andras Mantia + */ +class QNewDocStuff: public KNewStuffSecure +{ + Q_OBJECT + + public: + QNewDocStuff(const QString &type, QWidget *parentWidget=0) + :KNewStuffSecure(type, parentWidget){}; + ~QNewDocStuff() {}; + + private: + virtual void installResource(); +}; + +#endif diff --git a/quanta/utility/qpevents.cpp b/quanta/utility/qpevents.cpp new file mode 100644 index 00000000..bb0d5db4 --- /dev/null +++ b/quanta/utility/qpevents.cpp @@ -0,0 +1,403 @@ +/*************************************************************************** + qpevents.cpp - description + ------------------- + begin : Sun Jul 11 2004 + copyright : (C) 2004 Andras Mantia <amantia@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +//qt includes +#include <qdatetime.h> +#include <qfile.h> +#include <qtextstream.h> + +//kde includes +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> + +//app includes +#include "qpevents.h" +#include "document.h" +#include "project.h" +#include "qextfileinfo.h" +#include "viewmanager.h" +#include "resource.h" +#include "quanta.h" +#include "tagaction.h" + +//TODO: Better create a class for each internal event action +QPEvents::QPEvents(QObject *parent, const char *name) + : QObject(parent, name) +{ + m_eventNames["before_save"] = i18n("Before Document Save"); + m_eventNames["after_save"] = i18n("After Document Save"); + m_eventNames["after_open"] = i18n("After Document Open"); + m_eventNames["before_close"] = i18n("Before Document Close"); + m_eventNames["after_close"] = i18n("After Document Close"); + m_eventNames["after_project_open"] = i18n("After Project Open"); + m_eventNames["before_project_close"] = i18n("Before Project Close"); + m_eventNames["after_project_close"] = i18n("After Project Close"); + m_eventNames["upload_requested"] = i18n("Upload Requested"); + m_eventNames["before_upload"] = i18n("Before Document Upload"); + m_eventNames["after_upload"] = i18n("After Document Upload"); + m_eventNames["after_project_add"] = i18n("After Addition to Project"); + m_eventNames["after_project_remove"] = i18n("After Removal From Project"); + m_eventNames["after_commit"] = i18n("After Commit to CVS"); + m_eventNames["after_update"] = i18n("After Update From CVS"); + m_eventNames["after_file_move"] = i18n("After Moving File Inside Project"); + m_eventNames["quanta_start"] = i18n("Quanta Start"); + m_eventNames["quanta_exit"] = i18n("Quanta Exit"); +// m_eventNames["after_multiple_save"] = i18n("After saving more files at once (like Save All)"); + + m_actionNames["email"] = i18n("Send Email"); + m_actionNames["log"] = i18n("Log Event"); + m_actionNames["script"] = i18n("Script Action"); + m_actionNames["action"] = i18n("Non-Script Action"); +} + + +QPEvents::~QPEvents() +{ +} + +void QPEvents::slotEventHappened(const QString& name, const QString& argument1, const QString& argument2) +{ + if (!quantaApp || !Project::ref()->eventsEnabled()) + return; + EventActions *events = Project::ref()->events(); + if (!events) return; + if (events->contains(name)) + { + m_eventName = name; + QValueList<EventAction> evList = (*events)[name]; + for (QValueList<EventAction>::Iterator it = evList.begin(); it != evList.end(); ++it) + { + EventAction ev = *it; + if (ev.type == EventAction::Internal) + { + if (KMessageBox::warningContinueCancel(0L, i18n("<qt>An internal action (<i>%1</i>) associated with an event (<i>%2</i>) will be executed. Do you want to allow the execution of this action?</qt>").arg(ev.action).arg(name), i18n("Event Triggered"), i18n("Execute"), "Warn about internal actions") == KMessageBox::Cancel) + return; + } else + { + if (KMessageBox::warningContinueCancel(0L, i18n("<qt>An external action (<i>%1</i>) associated with an event (<i>%2</i>) will be executed. Do you want to allow the execution of this action?</qt>").arg(ev.action).arg(name), i18n("Event Triggered"), i18n("Execute"), "Warn about external actions") == KMessageBox::Cancel) + return; + } + KURL url = KURL::fromPathOrURL(argument1); + KURL url2 = KURL::fromPathOrURL(argument2); + if (url.isValid()) + { + bool inProject = Project::ref()->contains(url); + if (inProject) + { + if (name == "upload_requested") + { + ev.arguments << i18n("An upload was initiated"); + ev.arguments << url.path(); + handleEvent(ev); + } + } + if (inProject && url2.isValid()) + { + if (name == "before_upload") + { + ev.arguments << i18n("About to upload a document"); + ev.arguments << url.path(); + ev.arguments << url2.path(); + handleEvent(ev); + } else + if (name == "after_upload") + { + ev.arguments << i18n("Document uploaded"); + ev.arguments << url.path(); + ev.arguments << url2.path(); + handleEvent(ev); + } else + if (name == "after_file_move") + { + ev.arguments << i18n("Document moved"); + ev.arguments << url.path(); + ev.arguments << url2.path(); + handleEvent(ev); + } + } else + { + QString relativePath = QExtFileInfo::toRelative(url, Project::ref()->projectBaseURL()).path(); + if (inProject && name == "after_save") + { + ev.arguments << i18n("Document saved"); + ev.arguments << relativePath; + handleEvent(ev); + } else + if (inProject && name == "before_save") + { + ev.arguments << i18n("About to save a document"); + ev.arguments << relativePath; + handleEvent(ev); + } else + if (inProject && name == "after_open") + { + ev.arguments << i18n("Document opened"); + ev.arguments << relativePath; + handleEvent(ev); + } else + if (inProject && name == "after_close") + { + ev.arguments << i18n("Document closed"); + ev.arguments << relativePath; + handleEvent(ev); + } else + if (inProject && name == "before_close") + { + ev.arguments << i18n("About to close a document"); + ev.arguments << relativePath; + handleEvent(ev); + } else + if (name == "after_project_open") + { + ev.arguments << i18n("Project opened"); + ev.arguments << url.path(); + handleEvent(ev); + } else + if (name == "after_project_close") + { + ev.arguments << i18n("Project closed"); + ev.arguments << url.path(); + handleEvent(ev); + } else + if (name == "before_project_close") + { + ev.arguments << i18n("About to close the project"); + ev.arguments << url.path(); + handleEvent(ev); + } else + if (name == "after_project_add") + { + ev.arguments << i18n("Document added to project"); + ev.arguments << url.path(); + handleEvent(ev); + } else + if (name == "after_project_remove") + { + ev.arguments << i18n("Document removed from project"); + ev.arguments << url.path(); + handleEvent(ev); + } + } + } else + if (name == "after_commit") + { + ev.arguments << i18n("Document committed"); + ev.arguments << argument1; + handleEvent(ev); + } else + if (name == "after_update") + { + ev.arguments << i18n("Document updated"); + ev.arguments << argument1; + handleEvent(ev); + } else + if (name == "quanta_start") + { + ev.arguments << i18n("Quanta has been started"); + ev.arguments << argument1; + handleEvent(ev); + } else + if (name == "quanta_exit") + { + ev.arguments << i18n("Quanta is shutting down"); + ev.arguments << argument1; + handleEvent(ev); + } + } + } + if (!m_eventNames.contains(name)) + KMessageBox::sorry(0L, i18n("<qt>Unsupported event <b>%1</b>.</qt>").arg(name), i18n("Event Handling Error")); +} + +bool QPEvents::handleEvent(const EventAction& ev) +{ + if (ev.type == EventAction::Internal) + { + if (ev.action == "email") + { + QString receiver = ev.arguments[0]; + TeamMember member; + if (receiver == "teamleader") + member = Project::ref()->teamLeader(); + else if (receiver.startsWith("subprojectleader-")) + { + QString s = receiver.remove("subprojectleader-"); + member = Project::ref()->subprojectLeader(s); + SubProject subProject; + QValueList<SubProject> *subprojects = Project::ref()->subprojects(); + for (uint i = 0 ; i < subprojects->count(); i++) + { + if ((*subprojects)[i].name == s) + { + subProject = (*subprojects)[i]; + break; + } + } + if (!subProject.location.isEmpty() && !ev.arguments[2].startsWith(subProject.location)) + { + kdDebug(24000) << ev.arguments[2] << " is not part of the " << subProject.name << "subproject \"" << subProject.location << "\". " << endl; + return true; + } + } + else if (receiver.startsWith("taskleader-")) + member = Project::ref()->taskLeader(receiver.remove("taskleader-")); + + QString body; + for (uint i = 2; i < ev.arguments.count(); i++) + body += ev.arguments[i] + "\n"; + kapp->invokeMailer(member.name + "<" + member.email + ">", "", "", ev.arguments[1], body, "", QStringList(), ""); + + return true; + } + if (ev.action == "log") + { + QString logFile = ev.arguments[0]; + KURL url = KURL::fromPathOrURL(logFile); + if (url.isValid() && !url.isLocalFile()) + { + KMessageBox::sorry(0L, i18n("Logging to remote files is not supported.")); + return false; + } + if (!logFile.startsWith("/")) + { + url = Project::ref()->projectBaseURL(); + url.addPath(logFile); + if (!url.isLocalFile()) + { + KMessageBox::sorry(0L, i18n("Logging to files inside a remote project is not supported.")); + return false; + } + } + QFile file(url.path()); + bool result; + if (ev.arguments[2] == "create_new") + result = file.open(IO_WriteOnly); + else + result = file.open(IO_WriteOnly | IO_Append); + if (result) + { + QTextStream stream(&file); + stream.setEncoding(QTextStream::UnicodeUTF8); + //Note: the log text should not be translated. + QString s = QDateTime::currentDateTime().toString(Qt::ISODate) + ": "; + s.append( "Event : " + m_eventName + " : "); + s.append( "Action: " + ev.action + " : "); + if (ev.arguments[1] == "full") + { + s.append( "Arguments: "); + for (uint i = 1; i < ev.arguments.count(); i++) + s.append(ev.arguments[i] + " | "); + } + s[s.length() - 1] = '\n'; + stream << s; + file.close(); + } + if (!result) + { + KMessageBox::sorry(0L, i18n("<qt>Logging failed. Check that you have write access to <i>%1</i>.").arg(url.path())); + return false; + } + } else + KMessageBox::sorry(0L, i18n("<qt>Unsupported internal event action : <b>%1</b>.</qt>").arg(ev.action)); + } else + if (ev.type == EventAction::External) + { + //KMessageBox::sorry(0L, i18n("External event actions are not yet supported.")); + if (ev.action == "script" || ev.action =="action") + { + QString name = ev.arguments[0]; + KAction *action = quantaApp->actionCollection()->action(name); + TagAction *tagAction = dynamic_cast<TagAction*>(action); + if (tagAction) + { + bool blocking = (ev.arguments[1] == "yes"); + EventAction event = ev; + event.arguments.remove(event.arguments.at(1)); + tagAction->addArguments(event.arguments); + tagAction->execute(blocking); + } + else + if (action) + { + action->activate(); + } else + KMessageBox::sorry(0L, i18n("<qt>The <b>%1</b> script action was not found on your system.</qt>").arg(name), i18n("Action Execution Error")); + } else + KMessageBox::sorry(0L, i18n("Unsupported external event action.")); + } else + KMessageBox::sorry(0L, i18n("Unknown event type.")); + return false; +} + +QString QPEvents::fullEventName(const QString &name) +{ + if (m_eventNames.contains(name)) + return m_eventNames[name]; + else + return name; +} + +QString QPEvents::fullActionName(const QString& name) +{ + if (m_actionNames.contains(name)) + return m_actionNames[name]; + else + return name; +} + +QString QPEvents::eventName(const QString &fullName) +{ + for (QMap<QString, QString>::ConstIterator it = m_eventNames.constBegin(); it != m_eventNames.constEnd(); ++it) + { + if (fullName == it.data()) + return it.key(); + } + return fullName; +} + +QString QPEvents::actionName(const QString &fullName) +{ + for (QMap<QString, QString>::ConstIterator it = m_actionNames.constBegin(); it != m_actionNames.constEnd(); ++it) + { + if (fullName == it.data()) + return it.key(); + } + return fullName; +} + +QStringList QPEvents::eventNames() +{ + QStringList names; + for (QMap<QString, QString>::ConstIterator it = m_eventNames.constBegin(); it != m_eventNames.constEnd(); ++it) + { + names << it.data(); + } + return names; +} + +QStringList QPEvents::actionNames() +{ + QStringList names; + for (QMap<QString, QString>::ConstIterator it = m_actionNames.constBegin(); it != m_actionNames.constEnd(); ++it) + { + names << it.data(); + } + return names; +} + +#include "qpevents.moc" diff --git a/quanta/utility/qpevents.h b/quanta/utility/qpevents.h new file mode 100644 index 00000000..83f6d37b --- /dev/null +++ b/quanta/utility/qpevents.h @@ -0,0 +1,90 @@ +/*************************************************************************** + qpevents.h - description + ------------------- + begin : Sun Jul 11 2004 + copyright : (C) 2004 Andras Mantia <amantia@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QPEVENTS_H +#define QPEVENTS_H + +#include <qobject.h> + +/** +@author Andras Mantia +*/ + +/** Describes an event action. */ +struct EventAction { + /** Possible event types */ + enum Types { + Internal = 0, + External = 1 + }; + /** The type of the event. See @ref Types */ + uint type; + /** the name of the action to be executed. In case of external events + this is the name of the script, in case of internal events it can be one of the + following: "email" + */ + QString action; + /** The arguments for the event action. It is different for each action. + */ + QStringList arguments; +}; + +/** The configured events. The key is the event name, the data is the event description. +For example: events["before_save"] points to the event data that needs to be used +before a file is saved. Possible key names are: before_save, after_save, after_open, +after_project_open, after_project_save, before_upload, after_upload, after_project_add, +after_project_remove, after_commit +*/ +typedef QMap<QString, QValueList<EventAction> > EventActions; + +class QPEvents : public QObject +{ +Q_OBJECT +public: + static QPEvents* const ref(QObject *parent = 0L) + { + static QPEvents *m_ref; + if (!m_ref) m_ref = new QPEvents(parent); + return m_ref; + } + ~QPEvents(); + QString fullEventName(const QString &name); + QString fullActionName(const QString &name); + QString eventName(const QString &fullName); + QString actionName(const QString &fullName); + QStringList eventNames(); + QStringList actionNames(); + +public slots: + /** Called when an event has happened */ + void slotEventHappened(const QString& name, const QString& argument1, const QString& argument2); + +private: + QPEvents(QObject *parent = 0, const char *name = 0); + /** Calls the action associated with an event. Returns true if the call succeeded, false + otherwise. The call might fail if: + - the action type is unknown + - the script cannot be found + - the user canceled the execution + */ + bool handleEvent(const EventAction& ev); + + QMap<QString, QString> m_eventNames; + QMap<QString, QString> m_actionNames; + QString m_eventName; +}; + +#endif diff --git a/quanta/utility/quantabookmarks.cpp b/quanta/utility/quantabookmarks.cpp new file mode 100644 index 00000000..ebca5843 --- /dev/null +++ b/quanta/utility/quantabookmarks.cpp @@ -0,0 +1,399 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002, 2003, 2004 Anders Lund <anders.lund@lund.tdcadsl.dk> + Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org> + Copyright (C) 2005 Andras Mantia <amantia@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "quantabookmarks.h" + +#include <kapplication.h> +#include <kconfig.h> +#include <klocale.h> +#include <kaction.h> +#include <kdebug.h> +#include <kpopupmenu.h> +#include <kstringhandler.h> +#include <ktexteditor/markinterface.h> +#include <ktexteditor/editinterface.h> +#include <ktexteditor/viewcursorinterface.h> + +#include <qregexp.h> +#include <qmemarray.h> +#include <qevent.h> + +#include "viewmanager.h" +#include "document.h" + +/** + Utility: selection sort + sort a QMemArray<uint> in ascending order. + max it the largest (zerobased) index to sort. + To sort the entire array: ssort( *array, array.size() -1 ); + This is only efficient if ran only once. +*/ +static void ssort( QMemArray<uint> &a, int max ) +{ + uint tmp, j, maxpos; + for ( uint h = max; h >= 1; h-- ) + { + maxpos = 0; + for ( j = 0; j <= h; j++ ) + maxpos = a[j] > a[maxpos] ? j : maxpos; + tmp = a[maxpos]; + a[maxpos] = a[h]; + a[h] = tmp; + } +} + +// TODO add a insort() or bubble_sort - more efficient for aboutToShow() ? + +QuantaBookmarks::QuantaBookmarks(ViewManager *parent,Sorting sort, bool onlyFromActualDocument ) + : QObject( parent, "bookmarks" ) + , m_sorting(sort) + , m_onlyFromActualDocument(onlyFromActualDocument) +{ + m_viewManager = parent; + _tries=0; + m_bookmarksMenu = 0L; + m_doc = 0L; +} + +QuantaBookmarks::~QuantaBookmarks() +{ +} + +void QuantaBookmarks::createActions( KActionCollection* ac ) +{ + m_bookmarksMenu = (new KActionMenu(i18n("&Bookmarks"), ac, "bookmarks"))->popupMenu(); + init(ac); +} + +void QuantaBookmarks::init(KActionCollection* ac) +{ + m_bookmarkToggle = new KToggleAction( + i18n("Set &Bookmark"), "bookmark", CTRL+Key_B, + this, SLOT(toggleBookmark()), + ac, "bookmarks_toggle" ); + m_bookmarkToggle->setWhatsThis(i18n("If a line has no bookmark then add one, otherwise remove it.")); + m_bookmarkToggle->setCheckedState( i18n("Clear &Bookmark") ); + + m_bookmarkClear = new KAction( + i18n("Clear &All Bookmarks"), 0, + this, SLOT(clearBookmarks()), + ac, "bookmarks_clear"); + m_bookmarkClear->setWhatsThis(i18n("Remove all bookmarks of the current document.")); + + m_goNext = new KAction( + i18n("Next Bookmark"), "next", ALT + Key_PageDown, + this, SLOT(goNext()), + ac, "bookmarks_next"); + m_goNext->setWhatsThis(i18n("Go to the next bookmark.")); + + m_goPrevious = new KAction( + i18n("Previous Bookmark"), "previous", ALT + Key_PageUp, + this, SLOT(goPrevious()), + ac, "bookmarks_previous"); + m_goPrevious->setWhatsThis(i18n("Go to the previous bookmark.")); + + //connect the aboutToShow() and aboutToHide() signals with + //the bookmarkMenuAboutToShow() and bookmarkMenuAboutToHide() slots + connect( m_bookmarksMenu, SIGNAL(aboutToShow()), this, SLOT(bookmarkMenuAboutToShow())); + connect( m_bookmarksMenu, SIGNAL(aboutToHide()), this, SLOT(bookmarkMenuAboutToHide()) ); + + marksChanged (); +} + +void QuantaBookmarks::setBookmarksMenu(QPopupMenu* bookmarksMenu) + +{ + m_bookmarksMenu = bookmarksMenu; + init(); +} + +void QuantaBookmarks::toggleBookmark () +{ + Document *doc = m_doc; + if (!doc) + doc = m_viewManager->activeDocument(); + if (doc && doc->markIf) + { + uint mark = doc->markIf->mark(doc->viewCursorIf->cursorLine()); + if( mark & KTextEditor::MarkInterface::markType01 ) + doc->markIf->removeMark(doc->viewCursorIf->cursorLine(), + KTextEditor::MarkInterface::markType01 ); + else + doc->markIf->addMark(doc->viewCursorIf->cursorLine(), + KTextEditor::MarkInterface::markType01 ); + } + marksChanged(); +} + +void QuantaBookmarks::clearBookmarks () +{ + Document *doc = m_viewManager->activeDocument(); + if (doc && doc->markIf) + { + QPtrList<KTextEditor::Mark> m = doc->markIf->marks(); + for (uint i=0; i < m.count(); i++) + doc->markIf->removeMark( m.at(i)->line, KTextEditor::MarkInterface::markType01 ); + + // just to be sure ;) + marksChanged (); + } +} + +int QuantaBookmarks::insertBookmarks(QPopupMenu& menu, Document *doc, bool insertNavigationItems ) +{ + int insertedItems = 0; + if (doc->markIf) + { + uint line = doc->viewCursorIf->cursorLine(); + const QRegExp re("&(?!&)"); + int idx( -1 ); + int old_menu_count = menu.count(); + KTextEditor::Mark *next = 0; + KTextEditor::Mark *prev = 0; + + QPtrList<KTextEditor::Mark> m = doc->markIf->marks(); + QMemArray<uint> sortArray( m.count() ); + QPtrListIterator<KTextEditor::Mark> it( m ); + + if ( it.count() > 0 && insertNavigationItems) + menu.insertSeparator(); + + for( int i = 0; *it; ++it) + { + if( (*it)->type & KTextEditor::MarkInterface::markType01 ) + { + QString bText = KStringHandler::rEmSqueeze + ( doc->editIf->textLine( (*it)->line ), + menu.fontMetrics(), 32 ); + bText.replace(re, "&&"); // kill undesired accellerators! + bText.replace('\t', ' '); // kill tabs, as they are interpreted as shortcuts + + if ( m_sorting == Position ) + { + sortArray[i] = (*it)->line; + ssort( sortArray, i ); + idx = sortArray.find( (*it)->line ); + if (insertNavigationItems) + idx += 3; + i++; + } + + menu.insertItem( + QString("%1 - \"%2\"").arg( (*it)->line+1 ).arg( bText ), + 0, (*it)->line, idx ); + insertedItems++; + + if ( (*it)->line < line ) + { + if ( ! prev || prev->line < (*it)->line ) + prev = (*it); + } + + else if ( (*it)->line > line ) + { + if ( ! next || next->line > (*it)->line ) + next = (*it); + } + } + } + + if (insertNavigationItems) + { + idx = ++old_menu_count; + if ( next ) + { + m_goNext->setText( i18n("&Next: %1 - \"%2\"").arg( next->line + 1 ) + .arg( KStringHandler::rsqueeze( doc->editIf->textLine( next->line ), 24 ) ) ); + m_goNext->plug( &menu, idx ); + idx++; + } + if ( prev ) + { + m_goPrevious->setText( i18n("&Previous: %1 - \"%2\"").arg(prev->line + 1 ) + .arg( KStringHandler::rsqueeze( doc->editIf->textLine( prev->line ), 24 ) ) ); + m_goPrevious->plug( &menu, idx ); + idx++; + } + if ( next || prev ) + menu.insertSeparator( idx ); + } + connect(&menu, SIGNAL(activated(int)), this, SLOT(gotoLineNumber(int))); + } + return insertedItems; +} + +void QuantaBookmarks::bookmarkMenuAboutToShow() +{ + KConfig *config = kapp->config(); + if (config->hasGroup("Kate View Defaults")) + { + config->setGroup("Kate View Defaults"); + m_sorting = config->readNumEntry("Bookmark Menu Sorting", 0) == 0 ? Position : Creation; + } + for (uint i = 0; i < m_othersMenuList.count(); i++) + { + delete m_othersMenuList[i]; + } + m_othersMenuList.clear(); + m_others.clear(); + m_bookmarksMenu->clear(); + marksChanged(); + + Document *doc = m_doc; + if (!doc) + doc = m_viewManager->activeDocument(); + QValueList<Document*> openedDocuments = m_viewManager->openedDocuments(); + if (doc && doc->markIf) + { + QPtrList<KTextEditor::Mark> m = doc->markIf->marks(); + + if (!m_onlyFromActualDocument) + { + m_bookmarkToggle->setChecked( doc->markIf->mark( doc->viewCursorIf->cursorLine() ) + & KTextEditor::MarkInterface::markType01 ); + m_bookmarkToggle->plug( m_bookmarksMenu ); + m_bookmarkClear->plug( m_bookmarksMenu ); + } + + insertBookmarks(*m_bookmarksMenu, doc, !m_onlyFromActualDocument); + if (openedDocuments.count() > 1 && !m_onlyFromActualDocument) + m_bookmarksMenu->insertSeparator(); + } + if (!m_onlyFromActualDocument) + { + int i = 0; + for (QValueList<Document*>::Iterator it = openedDocuments.begin(); it != openedDocuments.end(); ++it) + { + if (*it != doc) + { + QPopupMenu *menu = new QPopupMenu(m_bookmarksMenu); + m_bookmarksMenu->insertItem((*it)->url().fileName(), menu); + if (insertBookmarks(*menu, *it, false) > 0) + { + m_othersMenuList.append(menu); + m_others.append(*it); + i++; + } else + delete menu; + } + } + } +} + +/* + Make sure next/prev actions are plugged, and have a clean text +*/ +void QuantaBookmarks::bookmarkMenuAboutToHide() +{ + m_bookmarkToggle->plug( m_bookmarksMenu ); + m_bookmarkClear->plug( m_bookmarksMenu ); + m_goNext->setText( i18n("Next Bookmark") ); + m_goNext->plug( m_bookmarksMenu ); + m_goPrevious->setText( i18n("Previous Bookmark") ); + m_goPrevious->plug( m_bookmarksMenu ); +} + +void QuantaBookmarks::goNext() +{ + Document *doc = m_doc; + if (!doc) + doc = m_viewManager->activeDocument(); + if (doc && doc->markIf) + { + QPtrList<KTextEditor::Mark> m = doc->markIf->marks(); + if (m.isEmpty()) + return; + + uint line = doc->viewCursorIf->cursorLine(); + int found = -1; + + for (uint z=0; z < m.count(); z++) + if ( (m.at(z)->line > line) && ((found == -1) || (uint(found) > m.at(z)->line)) ) + found = m.at(z)->line; + + if (found != -1) + doc->viewCursorIf->setCursorPositionReal(found, 0); + } +} + +void QuantaBookmarks::goPrevious() +{ + Document *doc = m_doc; + if (!doc) + doc = m_viewManager->activeDocument(); + if (doc && doc->markIf) + { + QPtrList<KTextEditor::Mark> m = doc->markIf->marks(); + if (m.isEmpty()) + return; + + uint line = doc->viewCursorIf->cursorLine(); + int found = -1; + + for (uint z=0; z < m.count(); z++) + if ((m.at(z)->line < line) && ((found == -1) || (uint(found) < m.at(z)->line))) + found = m.at(z)->line; + + if (found != -1) + doc->viewCursorIf->setCursorPositionReal(found, 0); + } +} + +void QuantaBookmarks::gotoLineNumber(int line) +{ + Document *doc = m_doc; + if (!doc) + doc = m_viewManager->activeDocument(); + const QObject *s = sender(); + for (uint i = 0; i < m_othersMenuList.count(); i++) + { + if (s == m_othersMenuList[i]) + { + doc = m_others[i]; + break; + } + } + if (doc) + { + if (doc->isUntitled()) + { + emit gotoFileAndLine("file:" + doc->url().path(), line, 0); + } else + { + emit gotoFileAndLine(doc->url().url(), line, 0); + } + } +} + + +void QuantaBookmarks::marksChanged () +{ + Document *doc = m_doc; + if (!doc) + doc = m_viewManager->activeDocument(); + if (doc && doc->markIf) + { + m_bookmarkClear->setEnabled( !doc->markIf->marks().isEmpty() ); + } +} + +#include "quantabookmarks.moc" + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/quanta/utility/quantabookmarks.h b/quanta/utility/quantabookmarks.h new file mode 100644 index 00000000..10c0aad4 --- /dev/null +++ b/quanta/utility/quantabookmarks.h @@ -0,0 +1,99 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002, 2003 Anders Lund <anders.lund@lund.tdcadsl.dk> + Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org> + Copyright (C) 2005 Andras Mantia <amantia@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/* Extended bookmark manager. Code taken from the Kate part and adapted to +Quanta, so it works over multiple document */ + +#ifndef QUANTABOOKMARKS_H +#define QUANTABOOKMARKS_H + +#include <qobject.h> +#include <qptrlist.h> + +namespace KTextEditor { class Mark; } + +class ViewManager; +class Document; + +class KAction; +class KToggleAction; +class KActionCollection; +class QPopupMenu; +class QMenuData; + + + +class QuantaBookmarks : public QObject +{ + Q_OBJECT + + public: + enum Sorting { Position, Creation }; + QuantaBookmarks(ViewManager *parent, Sorting sort=Position, bool onlyFromActualDocument = false ); + virtual ~QuantaBookmarks(); + + void createActions( KActionCollection* ); + void setBookmarksMenu(QPopupMenu* bookmarksMenu); + + QuantaBookmarks::Sorting sorting() { return m_sorting; }; + void setSorting( Sorting s ) { m_sorting = s; }; + void setDocument(Document *doc) {m_doc = doc;} + + protected: + int insertBookmarks(QPopupMenu& menu, Document *doc, bool insertNavigationItems = true); + void init(KActionCollection* ac = 0L); + + private slots: + void toggleBookmark(); + void clearBookmarks(); + + void bookmarkMenuAboutToShow(); + void bookmarkMenuAboutToHide(); + + void goNext(); + void goPrevious(); + void gotoLineNumber(int line); + + void marksChanged (); + + signals: + void gotoFileAndLine(const QString&, int, int); + + private: + KToggleAction* m_bookmarkToggle; + KAction* m_bookmarkClear; + KAction* m_goNext; + KAction* m_goPrevious; + + Sorting m_sorting; + QPopupMenu* m_bookmarksMenu; + QValueList<QPopupMenu*> m_othersMenuList; + QValueList<Document*> m_others; + ViewManager* m_viewManager; + Document *m_doc; + bool m_onlyFromActualDocument; + + uint _tries; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; +// vim: noet ts=2 diff --git a/quanta/utility/quantacommon.cpp b/quanta/utility/quantacommon.cpp new file mode 100644 index 00000000..7cc7fb1b --- /dev/null +++ b/quanta/utility/quantacommon.cpp @@ -0,0 +1,753 @@ +/*************************************************************************** + quantacommon.cpp - description + ------------------- + begin : Sat Jul 27 2002 + copyright : (C) 2002, 2003 by Andras Mantia <amantia@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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; version 2 of the License. * + * * + ***************************************************************************/ + +//system includes +#include <sys/types.h> +#include <unistd.h> + +//qt includes +#include <qstringlist.h> +#include <qdict.h> +#include <qdir.h> +#include <qdom.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qwidget.h> + +//kde includes +#include <kapplication.h> +#include <dcopref.h> +#include <kurl.h> +#include <kdirwatch.h> +#include <kmimetype.h> +#include <kstandarddirs.h> +#include <kmessagebox.h> +#include <kio/netaccess.h> +#include <klocale.h> +#include <kprotocolinfo.h> +#include <kprogress.h> +#include <ktempdir.h> +#include <ktempfile.h> + +//remove the below ones when KQPasteAction is removed +#include <dcopclient.h> +#include <kdebug.h> +#include <kpopupmenu.h> +#include <ktoolbar.h> +#include <ktoolbarbutton.h> +#include <kstringhandler.h> +#include <qwhatsthis.h> +#include <qtimer.h> +#include <qclipboard.h> +#include <qdatastream.h> + +#include "qextfileinfo.h" +#include "quantacommon.h" +#include "tag.h" +#include "dtds.h" +//#include "resource.h" + + +QConfig qConfig; //holds the main configuration settings +QString tmpDir; + +QRegExp scriptBeginRx; +QRegExp scriptEndRx; + +Node *baseNode; +Parser *parser; +KDirWatch *fileWatcher; +KProgress *progressBar; + +QString toolbarExtension = ".toolbar.tgz"; +QRegExp newLineRx("\\n"); +QStringList charList; //hold the list of the &char; chars. See the data/chars file. +QMap<int, QString> replacementMap; +QPtrList<KTempFile> tempFileList; +QPtrList<KTempDir> tempDirList; +bool typingInProgress; + +QuantaCommon::QuantaCommon(){ +} + +QuantaCommon::~QuantaCommon(){ +} + +/** convert tag to upper or lower case */ +QString QuantaCommon::tagCase( const QString& tag) +{ + QString sTag = tag; + + switch (qConfig.tagCase) + { + case 1: sTag = tag.lower(); + break; + case 2: sTag = tag.upper(); + } + return sTag; +} + +/** convert attr of tag to upper or lower case */ +QString QuantaCommon::attrCase( const QString& attr) +{ + QString sAttr = attr; + + switch (qConfig.attrCase) + { + case 1: sAttr = attr.lower(); + break; + case 2: sAttr = attr.upper(); + } + return sAttr; +} + +/** returns the attribute value in quoted form, by taking care of the +quotation setting*/ +QString QuantaCommon::quoteAttributeValue(const QString& value) +{ + QString quote = qConfig.attrValueQuotation; + return quote + value + quote; +} + +/** Set's up the url correctly from urlString. */ +void QuantaCommon::setUrl(KURL &url, const QString& urlString) +{ + KURL oldUrl = url; + url = urlString; + if (!KProtocolInfo::isKnownProtocol(url)) + { + url = oldUrl; + url.setPath(urlString); + if (url.protocol().isEmpty()) + url.setProtocol("file"); + } +} + +/** No descriptions */ +bool QuantaCommon::isSingleTag(const QString& dtdName, const QString& tag) +{ + bool single = false; + + //!doctype is a common tag to all DTDs not listed in the tagsList + if(tag.lower() == "!doctype" || tag.lower() == "?xml") + return true; + + const DTDStruct* dtd = DTDs::ref()->find(dtdName); + if (dtd && !tag.isEmpty()) + { + QString searchForTag = (dtd->caseSensitive) ? tag : tag.upper(); + QTag* qtag = dtd->tagsList->find(searchForTag); + if (qtag) + single = qtag->isSingle(); + } + + return single; +} + +/** No descriptions */ +bool QuantaCommon::isOptionalTag(const QString& dtdName, const QString& tag) +{ + bool optional = false; + + const DTDStruct* dtd = DTDs::ref()->find(dtdName); + if (dtd && !tag.isEmpty()) + { + QString searchForTag = (dtd->caseSensitive) ? tag : tag.upper(); + QTag* qtag = dtd->tagsList->find(searchForTag); + if (qtag) + optional = qtag->isOptional(); + } + + return optional; +} +/** No descriptions */ +bool QuantaCommon::isKnownTag(const QString& dtdName, const QString& tag) +{ + bool known = false; + + const DTDStruct* dtd = DTDs::ref()->find(dtdName); + if (dtd && !tag.isEmpty()) + { + QString searchForTag = (dtd->caseSensitive) ? tag : tag.upper(); + if (dtd->tagsList->find(searchForTag)) + known = true; + } + + return known; +} + +AttributeList* QuantaCommon::tagAttributes(const QString& dtdName, const QString& tag) +{ + AttributeList* attrs = 0L; + + const DTDStruct* dtd = DTDs::ref()->find(dtdName); + if (dtd && !tag.isEmpty()) + { + QString searchForTag = (dtd->caseSensitive) ? tag : tag.upper(); + QTag* qtag = dtd->tagsList->find(searchForTag); + if (qtag) + attrs = qtag->attributes(); + } + + return attrs; +} + +/** Returns the QTag object for the tag "tag" from the DTD named "dtdname". */ +QTag* QuantaCommon::tagFromDTD(const QString& dtdName, const QString& tag) +{ + const DTDStruct* dtd = DTDs::ref()->find(dtdName); + return tagFromDTD(dtd, tag); +} + +/** Returns the QTag object for the tag "tag" from the DTD. */ +QTag* QuantaCommon::tagFromDTD(const DTDStruct *dtd, const QString& tag) +{ + QTag *qtag = 0; + if (dtd && !tag.isEmpty()) + { + QString searchForTag = (dtd->caseSensitive) ? tag : tag.upper(); + qtag = dtd->tagsList->find(searchForTag); + } + + return qtag; +} + +/** Returns the QTag object for the node "node" from node's DTD. */ +QTag* QuantaCommon::tagFromDTD(Node *node) +{ + if(!node || !node->tag) + return 0L; + + return tagFromDTD(node->tag->dtd(), node->tag->name); +} + +/** Returns an XML style string containing the GUI for attributes. */ +QString QuantaCommon::xmlFromAttributes(AttributeList* attributes) +{ + QString xmlStr; + QTextStream stream( &xmlStr, IO_WriteOnly ); + stream.setEncoding(QTextStream::UnicodeUTF8); + if (attributes) + { + int row = 0; + for ( uint i = 0; i< attributes->count();i++) + { + Attribute *attribute = attributes->at(i); + QString name = attribute->name.left(1).upper()+attribute->name.right(attribute->name.length()-1); + stream << " <attr name=\"" + attribute->name +"\" type=\""+attribute->type+"\""; + if (!attribute->defaultValue.isEmpty()) + stream << " defaultValue=\"" + attribute->defaultValue + "\""; + if (!attribute->status.isEmpty()) + stream << " status=\"" + attribute->status + "\""; + stream << ">" << endl; + stream << " <text>" << name << "</text>" << endl; + if (attribute->type != "check") + { + stream << " <textlocation col=\"0\" row=\"" << row << "\" />" << endl; + } + stream << " <location col=\"1\" row=\"" << row << "\" />" << endl; + + if (attribute->type == "list") + { + stream << " <items>" << endl; + for (uint j = 0; j < attribute->values.count(); j++) + { + stream << " <item>" << attribute->values[j] << "</item>" << endl; + } + stream << " </items>" << endl; + } + stream << " </attr>" << endl << endl ; + row++; + } //for + } //if + + return xmlStr; +} + + /** Returns 0 if the (line,col) is inside the area specified by the other +arguments, -1 if it is before the area and 1 if it is after. */ +int QuantaCommon::isBetween(int line, int col, int bLine, int bCol, int eLine, +int eCol){ + int pos = 0; + if (line < bLine || (line == bLine && (col < bCol) )) pos = -1; //it is before + if (line > eLine || (line == eLine && (col > eCol) )) pos = 1; //it is after + + return pos; +} + +/** Returns a pointer to a KStandardDirs object usable for plugin searchup. type +is the plugin binary type (exe or lib). The returned pointer must be deleted by +the caller!! */ +KStandardDirs* QuantaCommon::pluginDirs(const char *type) +{ + KStandardDirs *dirs = new KStandardDirs(); + dirs->addKDEDefaults(); + for (uint i = 0; i < qConfig.pluginSearchPaths.count(); i++) + { + dirs->addResourceDir(type, qConfig.pluginSearchPaths[i]); + } + return dirs; +} +/** Return true, if the url belong to the mimetype group. */ +bool QuantaCommon::checkMimeGroup(const KURL& url, const QString& group) +{ + KMimeType::List list = KMimeType::allMimeTypes(); + KMimeType::List::iterator it; + bool status = false; + KMimeType::Ptr mime = KMimeType::findByURL(url); + QString mimetype = mime->name(); + mimetype = mimetype.section('/',-1); + for ( it = list.begin(); it != list.end(); ++it ) + { + if ( ((*it)->name().contains(group)) && ((*it)->name().find(mimetype) != -1) +) { + status = true; + break; + } + } + + if (!status && group == "text") + { + if (url.isLocalFile()) + { + KMimeType::Format f = KMimeType::findFormatByFileContent(url.path()); + if (f.text && f.compression == KMimeType::Format::NoCompression) + status = true; + } else + { + QVariant v = mime->property("X-KDE-text"); + if (v.isValid()) + status = v.toBool(); + } + } + if (!status && group == "text" && mimetype == "x-zerosize") + status = true; + + return status; +} + +/** Return true, if the url has the mimetype type. */ +bool QuantaCommon::checkMimeType(const KURL& url, const QString& type) +{ + bool status = false; + QString mimetype = KMimeType::findByURL(url)->name(); + mimetype = mimetype.section('/',-1); + if (mimetype == type) status = true; + + return status; +} + +/** Return true, if the url has exactly the mimetype type. */ +bool QuantaCommon::checkExactMimeType(const KURL& url, const QString& type) +{ + bool status = false; + QString mimetype = KMimeType::findByURL(url)->name(); + if (mimetype == type) status = true; + + return status; +} + +/** Returns the url without the filename. */ +KURL QuantaCommon::convertToPath(const KURL& url) +{ + KURL result = url; + result.setFileName(""); + result.adjustPath(1); + return result; +} + +/** Return a string to be used when an url is saved to the project file. + Returns url.url() if it's an absolute url and + url.path() if the url is relative */ +QString QuantaCommon::qUrl(const KURL &url) +{ + QString result = url.path(); + if (url.path().startsWith("/")) result = url.url(); + + return result; +} +/** No descriptions */ +void QuantaCommon::dirCreationError(QWidget *widget, const KURL& url) +{ + KMessageBox::error(widget, i18n("<qt>Cannot create folder<br><b>%1</b>.<br>Check that you have write permission in the parent folder or that the connection to<br><b>%2</b><br> is valid.</qt>") + .arg(url.prettyURL(0, KURL::StripFileProtocol)) + .arg(url.protocol()+"://"+url.user()+"@"+url.host()));} + +/** +Adds the backslash before the special chars (like ?, *, . ) so the returned +string can be used in regular expressions.*/ +QString QuantaCommon::makeRxCompatible(const QString& s) +{ + const uint max = 7; + const QRegExp rxs[max]={QRegExp("\\?"), + QRegExp("\\*"), + QRegExp("\\."), + QRegExp("\\^"), + QRegExp("\\$"), + QRegExp("\\{"), + QRegExp("\\}") + }; + const QString strs[max]={QString("\\?"), + QString("\\*"), + QString("\\."), + QString("\\^"), + QString("\\$"), + QString("\\{"), + QString("\\}") + }; + QString str = s; + for (uint i = 0; i < max - 1; i++) + { + str.replace(rxs[i], strs[i]); + } + + return str; +} + +/** Returns the translated a_str in English. A "back-translation" useful e.g in case of CSS elements selected from a listbox. */ +QString QuantaCommon::i18n2normal(const QString& a_str) +{ +//TODO: a QMap lookup would be faster, but we need a pre-built QMap<QString,QString> + const int keywordNum = 15 *5; + const QString keywords[keywordNum] = + {"normal", "italic", "oblique", "serif", "sans-serif", + "cursive", "fantasy", "monospace", "small-caps", "lighter", + "bold", "bolder", "xx-small", "x-small", "small", + "medium", "large", "x-large", "xx-large", "smaller", + "larger", "repeat", "repeat-x", "repeat-y", "no-repeat", + "scroll", "fixed", "top", "center", "bottom", + "left", "right", "none", "underline", "overline" + "line-through", "blibk", "justify","baseline", "sub", + "super", "text-top","text-bottom","capitalize","uppercase", + "lowercase","thin", "thick", "[length value]","dotted", + "dashed", "solid", "double", "groove", "ridge", + "inset", "outset", "block", "inline", "list-item", + "none", "pre", "nowrap", "disc", "circle", + "square", "decimal", "lower-roman","upper-roman","lower-alpha", + "upper-alpha","inside","outside", "auto", "both" }; + QString str = a_str; + if (!a_str.isEmpty()) + { + for (int i = 0; i < keywordNum; i++) + { + if (!keywords[i].isEmpty() && a_str == i18n(keywords[i].utf8())) + { + str = keywords[i]; + break; + } + } + } + return str; +} + +static const QChar space(' '); + +void QuantaCommon::removeCommentsAndQuotes(QString &str, const DTDStruct *dtd) +{ + //Replace all the commented strings and the escaped quotation marks (\", \') + // with spaces so they will not mess up our parsing + int pos = 0; + int l; + QString s; + while (pos != -1) + { + pos = dtd->commentsStartRx.search(str, pos); + if (pos != -1) + { + s = dtd->commentsStartRx.cap(); + if (s == "\\\"" || s == "\\'") + { + int i = pos; + int slahNum = 0; + while (i > 0 && str[i] == '\\') + { + slahNum++; + i--; + } + if (slahNum % 2 == 0) + { + pos++; + } else + { + str[pos] = space; + str[pos+1] = space; + pos += 2; + } + } else + { + s = dtd->comments[s]; + l = str.find(s, pos); + l = (l == -1) ? str.length() : l; + for (int i = pos; i < l ; i++) + { + str[i] = space; + } + pos = l + s.length(); + } + } + } + + //Now replace the quoted strings with spaces + QRegExp strRx("(\"[^\"]*\"|'[^']*')"); + pos = 0; + while (pos != -1) + { + pos = strRx.search(str, pos); + if (pos != -1) + { + l = strRx.matchedLength(); + for (int i = pos; i < pos + l ; i++) + { + str[i] = space; + } + pos += l; + } + } + +} + +bool QuantaCommon::insideCommentsOrQuotes(int position, const QString &string, const DTDStruct *dtd) +{ + //Return true if position is inside a commented or quoted string + QString str = string; + int pos = 0; + int l; + QString s; + while (pos != -1) + { + pos = dtd->commentsStartRx.search(str, pos); + if (pos == position) + return true; + if (pos != -1) + { + s = dtd->commentsStartRx.cap(); + if (s == "\\\"" || s == "\\'") + { + int i = pos; + int slahNum = 0; + while (i > 0 && str[i] == '\\') + { + slahNum++; + i--; + } + if (slahNum % 2 == 0) + { + pos++; + } else + { + str[pos] = space; + str[pos+1] = space; + pos += 2; + } + } else + { + s = dtd->comments[s]; + l = str.find(s, pos); + l = (l == -1) ? str.length() : l; + for (int i = pos; i < l ; i++) + { + str[i] = space; + if (i == position) + return true; + } + pos = l + s.length(); + } + } + } + + //Now replace the quoted strings with spaces + const QRegExp strRx("(\"[^\"]*\"|'[^']*')"); + pos = 0; + while (pos != -1) + { + pos = strRx.search(str, pos); + if (pos != -1) + { + l = strRx.matchedLength(); + for (int i = pos; i < pos + l ; i++) + { + str[i] = space; + if (i == position) + return true; + } + pos += l; + } + } + + return false; +} + +DCOPReply QuantaCommon::callDCOPMethod(const QString& interface, const QString& method, const QString& arguments) +{ + QStringList argumentList = QStringList::split(",", arguments, true); + QString app = "quanta"; + if (!kapp->inherits("KUniqueApplication")) + { + pid_t pid = ::getpid(); + app += QString("-%1").arg(pid); + } + DCOPRef quantaRef(app.utf8(), interface.utf8()); + DCOPReply reply; + int argumentCount = argumentList.count(); + if (argumentCount == 0) + { + reply = quantaRef.call(method.utf8()); + } + else if (argumentCount == 1) + { + reply = quantaRef.call(method.utf8(), argumentList[0]); + } + else if (argumentCount == 2) + reply = quantaRef.call(method.utf8(), argumentList[0], argumentList[1]); + else if (argumentCount == 3) + reply = quantaRef.call(method.utf8(), argumentList[0], argumentList[1], argumentList[2]); + else if (argumentCount == 4) + reply = quantaRef.call(method.utf8(), argumentList[0], argumentList[1], argumentList[2], argumentList[3]); + else if (argumentCount == 5) + reply = quantaRef.call(method.utf8(), argumentList[0], argumentList[1], argumentList[2], argumentList[3], argumentList[4]); + else if (argumentCount == 6) + reply = quantaRef.call(method.utf8(), argumentList[0], argumentList[1], argumentList[2], argumentList[3], argumentList[4], argumentList[5]); + else if (argumentCount == 7) + reply = quantaRef.call(method.utf8(), argumentList[0], argumentList[1], argumentList[2], argumentList[3], argumentList[4], argumentList[5], argumentList[6]); + else if (argumentCount == 8) + reply = quantaRef.call(method.utf8(), argumentList[0], argumentList[1], argumentList[2], argumentList[3], argumentList[4], argumentList[5], argumentList[6], argumentList[7]); + + return reply; +} + +void QuantaCommon::normalizeStructure(QString f,QStringList& l) +{ + f.remove("\t"); + f.remove("\n"); + f.remove("\r"); + + while(f.contains("<")) + { + QString z(f); + z.truncate(z.find(">")+1); + z.remove(0,z.find("<")); + f.remove(0,f.find(">")+1); + l.append(z); + } +} + +bool QuantaCommon::closesTag(Tag *tag1, Tag *tag2) +{ + if (tag1->nameSpace.isEmpty()) + { + if (!tag2->nameSpace.isEmpty()) + return false; //namespace missmatch + QString tag1Name = tag1->dtd()->caseSensitive ? tag1->name : tag1->name.upper(); + QString tag2Name = tag2->dtd()->caseSensitive ? tag2->name : tag2->name.upper(); + if ("/" + tag1Name != tag2Name) + return false; //not the closing tag + } else + { + if (tag2->nameSpace.isEmpty()) + return false; //namespace missmatch + QString tag1Name = tag1->dtd()->caseSensitive ? (tag1->nameSpace + tag1->name) : (tag1->nameSpace.upper() + tag1->name.upper()); + QString tag2Name = tag2->dtd()->caseSensitive ? (tag2->nameSpace + tag2->name) : (tag2->nameSpace.upper() + tag2->name.upper()); + if ("/" + tag1Name != tag2Name) + return false; //namespace missmatch or not the closing tag + } + return true; +} + +bool QuantaCommon::closesTag(QString namespaceName, QString tagName, bool caseSensitive, + QString namespaceName2, QString tagName2, bool caseSensitive2) +{ + QString tag1Name, tag2Name; + if(namespaceName.isEmpty()) + { + if(!namespaceName.isEmpty()) + return false;//namespace missmatch + tag1Name = caseSensitive ? tagName : tagName.upper(); + tag2Name = caseSensitive2 ? tagName2 : tagName2.upper(); + if("/" + tag1Name != tag2Name) + return false;//not the closing tag + } + else + { + if(namespaceName2.isEmpty()) + return false;//namespace missmatch + tag1Name = caseSensitive ? (namespaceName + tagName) : (namespaceName.upper() + + tagName.upper()); + tag2Name = caseSensitive2 ? (namespaceName2 + tagName2) : (namespaceName2.upper() + + tagName2.upper()); + if("/" + tag1Name != tag2Name) + return false; //namespace missmatch or not the closing tag + } + return true; +} + +int QuantaCommon::denyBinaryInsert(QWidget *window) +{ + int result = KMessageBox::questionYesNo(window, i18n("The file type is not recognized. \ + Opening binary files may confuse Quanta.\n Are you sure you want to open this file?"), + i18n("Unknown Type"), KStdGuiItem::open(), i18n("Do Not Open"), "Open Everything"); + return result; +} + +bool QuantaCommon::checkOverwrite(const KURL& url, QWidget *window) +{ + bool result = true; + + if (QExtFileInfo::exists(url, false, window)) + { + if (KMessageBox::warningContinueCancel(window, + i18n( "<qt>The file <b>%1</b> already exists.<br>Do you want to overwrite it?</qt>" ).arg(url.prettyURL(0, KURL::StripFileProtocol)), QString::null, i18n("Overwrite")) == KMessageBox::Cancel) + result = false; + } + + return result; +} + +QStringList QuantaCommon::readPathListEntry(KConfig *config, const QString &pKey) +{ + QStringList list = config->readPathListEntry(pKey); + QStringList::Iterator end = list.end(); + for (QStringList::Iterator it = list.begin(); it != end; ++it) + { + KURL u = KURL::fromPathOrURL(*it); + if (u.isValid() && u.isLocalFile()) + { + u.setPath(QExtFileInfo::canonicalPath(u.path())); + *it = u.url(); + } + } + return list; +} + +QString QuantaCommon::readPathEntry(KConfig *config, const QString &pKey) +{ + QString path = config->readPathEntry(pKey); + KURL u = KURL::fromPathOrURL(path); + if (u.isValid() && u.isLocalFile()) + { + u.setPath(QExtFileInfo::canonicalPath(u.path())); + path = u.url(); + } + return path; +} + +QString QuantaCommon::encodedChar(uint code) +{ + + if (replacementMap.contains(code)) + return QString("%1;").arg(replacementMap[code]); + else + return QString("&#%1;").arg(code); +} + diff --git a/quanta/utility/quantacommon.h b/quanta/utility/quantacommon.h new file mode 100644 index 00000000..a88276b7 --- /dev/null +++ b/quanta/utility/quantacommon.h @@ -0,0 +1,225 @@ +/*************************************************************************** + quantacommon.h - description + ------------------- + begin : Sat Jul 27 2002 + copyright : (C) 2002, 2003 by Andras Mantia <amantia@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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; version 2 of the License. * + * * + ***************************************************************************/ + +#ifndef QUANTACOMMON_H +#define QUANTACOMMON_H + +#include <kdeversion.h> + +#include "qtag.h" +#include "node.h" +#include "parser.h" + +/**Some common, mostly static functions. + *@author Andras Mantia + */ + +#define DEFAULT_DTD QString("-//W3C//DTD HTML 4.01 Transitional//EN") + +class QString; +class DCOPReply; +class KURL; +class KStandardDirs; +class QWidget; +class Tag; + +class KConfig; +class KPopupMenu; + +/** Describes one abbreviation group */ +class Abbreviation{ +public: +/*A list with abbreviations in the for of: <template templatename, code> */ + QMap<QString, QString> abbreviations; + QStringList dteps; +}; + + +//Quanta main configuration structure +class QConfig{ +public: + //Tag style options + uint tagCase; + uint attrCase; + QChar attrValueQuotation; + bool closeOptionalTags; + bool closeTags; + bool updateClosingTags; + + //editor settings + bool useAutoCompletion; + bool enableDTDToolbar; + QString defaultEncoding; + + //parser options + bool instantUpdate; + bool showEmptyNodes; + bool showClosingTags; + uint refreshFrequency; + QString defaultDocType; + uint expandLevel; + bool showDTDSelectDialog; + QString showCloseButtons; ///< can be Disabled, ShowAlways or ShowDelayed + uint toolviewTabs; ///< how does the toolview tabs look like + + //kafka sync options + bool quantaRefreshOnFocus; + int quantaRefreshDelay; + bool kafkaRefreshOnFocus; + int kafkaRefreshDelay; + + //kafka indentation options + bool inlineNodeIndentation; + + //environment options + QString globalDataDir; //not stored, initialized on app startup + QStringList pluginSearchPaths; //global but read from plugins.rc + QString markupMimeTypes; + QString scriptMimeTypes; + QString imageMimeTypes; + QString textMimeTypes; + QString previewPosition; + QString docPosition; + QString windowLayout; + uint autosaveInterval; + QString backupDirPath; + QString quantaPID; + bool showHiddenFiles; ///< show hidden files in files treeview? + bool saveTrees; ///< save tree status for local trees? + QMap<QString, Abbreviation> abbreviations; ///< the abbreviation groups + bool replaceAccented; ///< replace or not the accented characters + bool replaceNotInEncoding; ///< replace characters with their entity number if they cannot be saved in the current encoding of the document. + bool smartTagInsertion; //enable/disable smartTagInsertion + }; + +typedef struct DirInfo{ + QString mimeType; + QString preText; + QString postText; + bool usePrePostText; + }; + +/**Some common, mostly static functions. + *@author Andras Mantia + */ + +class QuantaCommon { +public: + QuantaCommon(); + ~QuantaCommon(); + + /** convert tag to upper or lower case */ + static QString tagCase( const QString& tag); + /** convert tag to upper or lower case */ + static QString attrCase( const QString& attr); +/** returns the attribute value in quoted form, by taking care of the +quotation setting*/ + static QString quoteAttributeValue(const QString& value); + /** Set's up the url correctly from urlString. */ + static void setUrl(KURL &url, const QString& urlString); + /** No descriptions */ + static bool isSingleTag(const QString& dtdName, const QString& tag); + /** No descriptions */ + static bool isOptionalTag(const QString& dtdName, const QString& tag); + /** No descriptions */ + static bool isKnownTag(const QString& dtdName, const QString& tag); + /** No descriptions */ + static AttributeList* tagAttributes(const QString& dtdName, const QString& tag); + /** Returns the QTag object for the tag "tag" from the DTD named "dtdname". */ + static QTag* tagFromDTD(const QString& dtdName, const QString& tag); + /** Returns the QTag object for the tag "tag" from the DTD. */ + static QTag* tagFromDTD(const DTDStruct* dtd, const QString& tag); + /** Returns the QTag object for the node "node" from node's DTD. */ + static QTag* tagFromDTD(Node *node); + /** Returns an XML style string containing the GUI for attributes. */ + static QString xmlFromAttributes(AttributeList* attributes); + /** Returns 0 if the (line,col) is inside the area specified by the other arguments, + -1 if it is before the area and 1 if it is after. */ + static int isBetween(int line, int col, int bLine, int bCol, int eLine, int eCol); + /** Returns a pointer to a KStandardDirs object usable for plugin searchup. type is the plugin binary type (exe or lib). The returned +pointer must be deleted by the caller!! */ + static KStandardDirs* pluginDirs(const char *type); + + /** Return true, if the url has the mimetype starting with type. */ + static bool checkMimeGroup(const KURL& url, const QString& type); + /** Return true, if the url has the mimetype type. */ + static bool checkMimeType(const KURL& url, const QString& type); + /** Return true, if the url has exactly the mimetype type. */ + static bool checkExactMimeType(const KURL& url, const QString& type); + /** Returns the url without the filename. */ + static KURL convertToPath(const KURL& url); + /** Return a string to be used when an url is saved to the project file. + Returns url.url() if it's an absolute url and + url.path() if the url is relative */ + static QString qUrl(const KURL& url); + /** No descriptions */ + static void dirCreationError(QWidget *widget, const KURL& url); + /** + Adds the backslash before the special chars (like ?, *, . ) so the returned string can be used in regular expressions. + */ + static QString makeRxCompatible(const QString& s); + /** Returns the translated a_str in English. A "back-translation" useful e.g in case of CSS elements selected from a listbox. */ + static QString i18n2normal(const QString& a_str); + +/** No descriptions */ + static void normalizeStructure(QString f,QStringList& l); + /**Returns true if tag2 is the closing pair of tag1. It's namespace aware.*/ + static bool closesTag(Tag *tag1, Tag *tag2); + static bool closesTag(QString namespaceName, QString tagName, bool caseSensitive, + QString namespaceName2, QString tagName2, bool caseSensitive2); + static int denyBinaryInsert(QWidget *window); + static void removeCommentsAndQuotes(QString& str, const DTDStruct* dtd); + static bool insideCommentsOrQuotes(int position, const QString &string, const DTDStruct *dtd); + /** Calls a Quanta DCOP method. + * @param interface the DCOP interface the method belongs to + * @param method the DCOP method name (with the argument types) + * @param arguments comma separated list of argument + * @return the return value of the DCOP caller + */ + static DCOPReply callDCOPMethod(const QString& interface, const QString& method, const QString& arguments); + /** Checks if url exists and shows a question about overwriting it. + * @param url the url to check + * @return true if the user answered yes, false otherwise. + */ + static bool checkOverwrite(const KURL& url, QWidget *window); + + /** + * Same as KConfigBase::readPathListEntry, but resolves symlinks + * @param config + * @param pKey + * @return + */ + static QStringList readPathListEntry(KConfig *config, const QString &pKey); + + /** + * Same as KConfigBase::readPathEntry, but resolves symlinks + * @param config + * @param pKey + * @return + */ + static QString readPathEntry(KConfig *config, const QString &pKey); + + /** + * Returns the HTML encoding string for character with the specified code. + * If no such literal encoding is known (like ä), the numeric encoding + * is returned: &#code; + * @param code the numeric code of the character + * @return the encoded string + */ + static QString encodedChar(uint code); + +}; + +#endif diff --git a/quanta/utility/quantanetaccess.cpp b/quanta/utility/quantanetaccess.cpp new file mode 100644 index 00000000..095f9e08 --- /dev/null +++ b/quanta/utility/quantanetaccess.cpp @@ -0,0 +1,268 @@ +/*************************************************************************** + quantanetaccess.h - description + ------------------- + begin : Jun 21 2004 + copyright : (C) 2004 by Jens Herden <jhe@kdewebdev.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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; version 2 of the License. * + * * + ***************************************************************************/ + +#include <qstring.h> +#include <qwidget.h> +#include <qdir.h> + +#include <kio/netaccess.h> +#include <kurl.h> +#include <kmessagebox.h> +#include <klocale.h> +#include <kfileitem.h> +#include <kstringhandler.h> + +#include "qextfileinfo.h" +#include "quantanetaccess.h" +#include "project.h" + +bool QuantaNetAccess::upload(const QString& src, const KURL& target, QWidget* window, bool confirm) +{ + bool ok = KIO::NetAccess::upload(src, target, window); + if (ok) { + checkProjectInsert(target, window, confirm); + } else { + if (confirm) + errorMsg(window); + } + return ok; +} + + +bool QuantaNetAccess::file_copy( const KURL& src, const KURL& target, int permissions, + bool overwrite, bool resume, QWidget* window, bool confirm) +{ + bool ok = KIO::NetAccess::file_copy( src, target, permissions, overwrite, resume, window ); + if (ok) { + checkProjectInsert(target, window, confirm); + } else { + if (confirm) + errorMsg(window); + } + return ok; +} + + +bool QuantaNetAccess::file_move( const KURL& src, const KURL& target, int permissions, + bool overwrite, bool resume, QWidget* window, bool confirm) +{ + // don't ask if move is inside of the project + bool oldConfirm = confirm; + if ( Project::ref()->projectBaseURL().isParentOf(src) && + Project::ref()->projectBaseURL().isParentOf(target) ) + { + confirm = false; + } + if ( !checkProjectRemove(src, window, confirm)) { + return false; + } + bool ok = KIO::NetAccess::file_move( src, target, permissions, overwrite, resume, window ); + if (ok) { + checkProjectInsert(target, window, confirm); + } else { + if (oldConfirm) + errorMsg(window); + } + return ok; +} + + +bool QuantaNetAccess::dircopy( const KURL::List & srcList, const KURL & target, QWidget* window, bool confirm ) +{ + bool ok = KIO::NetAccess::dircopy( srcList, target, window ); + if (ok) { + KURL url; + for ( KURL::List::ConstIterator it = srcList.begin(); it != srcList.end(); ++it ) { + url = target; + url.adjustPath(+1); + url.setFileName((*it).fileName()); + checkProjectInsert(url, window, confirm); + } + } else { + if (confirm) + errorMsg(window); + } + return ok; +} + + +bool QuantaNetAccess::move( const KURL::List& srcList, const KURL& target, QWidget* window, bool confirm ) +{ + KURL targetURL = adjustURL(target); + bool oldConfirm = confirm; + bool moveInsideProject = false; + bool targetInProject = Project::ref()->projectBaseURL().isParentOf(targetURL); + KURL url; + // first we ask about the URLs in the list without actually removing them from the project + for ( KURL::List::ConstIterator it = srcList.begin(); it != srcList.end(); ++it ) { + //don't ask if move is inside of the project + url = adjustURL(*it); + if (targetInProject && Project::ref()->projectBaseURL().isParentOf(url) ) + { + confirm = false; + moveInsideProject = true; + } + if ( !checkProjectRemove(*it, window, confirm, false)) { + return false; + confirm = oldConfirm; + } + } + // all URLs are confirmed, we remove them from the project + for ( KURL::List::ConstIterator it = srcList.begin(); it != srcList.end(); ++it ) { + if ( Project::ref()->projectBaseURL().isParentOf(*it) ) + Project::ref()->slotRemove(*it); + } + bool ok = KIO::NetAccess::move( srcList, targetURL, window ); + if (ok) { + KURL url; + for ( KURL::List::ConstIterator it = srcList.begin(); it != srcList.end(); ++it ) { + url = target; + url.adjustPath(+1); + url.setFileName((*it).fileName()); + checkProjectInsert(url, window, confirm); + Project::ref()->urlMoved(*it, url); + } + } else { + if (confirm) + errorMsg(window); + } + return ok; +} + + +bool QuantaNetAccess::del( const KURL & url, QWidget* window, bool confirm ) +{ + if ( !checkProjectDel(url, window, confirm)) { + return false; + } + bool ok = KIO::NetAccess::del( url, window ); + if (!ok && confirm) + { + errorMsg(window); + } + return ok; +} + + +bool QuantaNetAccess::mkdir( const KURL & url, QWidget* window, int permissions, bool confirm ) +{ + KURL u = url; + u.adjustPath(-1); //some servers refuse to create directories ending with a slash + bool ok = KIO::NetAccess::mkdir( u, window, permissions ); + if (ok) { + checkProjectInsert(url, window, confirm); + } else { + if (confirm) + errorMsg(window); + } + return ok; +} + + +void QuantaNetAccess::checkProjectInsert(const KURL& target, QWidget* window, bool confirm) +{ + if ( !Project::ref()->hasProject()) return; + KURL saveUrl = adjustURL(target); + KURL baseURL = Project::ref()->projectBaseURL(); + if ( baseURL.isParentOf(saveUrl) && !Project::ref()->contains(saveUrl) ) + { + if (confirm) + { + QString nice = QExtFileInfo::toRelative(saveUrl, baseURL).path(); + nice = KStringHandler::lsqueeze(nice, 60); + if ( KMessageBox::Yes != KMessageBox::questionYesNo(window, i18n("<qt>Do you want to add <br><b>%1</b><br> to the project?</qt>").arg(nice), i18n("Add to Project"), KStdGuiItem::add(), i18n("Do Not Add"), "AddToProject") ) + { + return; + } + } + KFileItem fileItem(KFileItem::Unknown, KFileItem::Unknown, saveUrl); + if ( fileItem.isDir() ) + Project::ref()->slotAddDirectory(saveUrl, false); + else + Project::ref()->slotInsertFile(saveUrl); + } +} + + +bool QuantaNetAccess::checkProjectRemove(const KURL& src, QWidget* window, bool confirm, bool remove) +{ + if ( !Project::ref()->hasProject() ) return true; + KURL url = adjustURL(src); + KURL baseURL = Project::ref()->projectBaseURL(); + if ( baseURL.isParentOf(url) && Project::ref()->contains(url) ) + { + if (confirm) + { + QString nice = QExtFileInfo::toRelative(url, baseURL).path(); + nice = KStringHandler::lsqueeze(nice, 60); + if ( KMessageBox::Continue != KMessageBox::warningContinueCancel(window, i18n("<qt>Do you really want to remove <br><b>%1</b><br> from the project?</qt>").arg(nice), i18n("Remove From Project"), KStdGuiItem::remove(), "RemoveFromProject") ) + { + return false; + } + } + if (remove) + Project::ref()->slotRemove(url); + } + return true; +} + + +bool QuantaNetAccess::checkProjectDel(const KURL& src, QWidget* window, bool confirm) +{ + KURL url = adjustURL(src); + if ( Project::ref()->hasProject() ) + { + if ( Project::ref()->projectBaseURL().isParentOf(url) && Project::ref()->contains(url) ) + { + if (confirm) + { + QString nice = url.prettyURL(0, KURL::StripFileProtocol); + nice = KStringHandler::csqueeze(nice, 60); + if ( KMessageBox::Continue != KMessageBox::warningContinueCancel(window, i18n("<qt>Do you really want to delete <br><b>%1</b><br> and remove it from the project?</qt>").arg(nice), i18n("Delete & Remove From Project"), KStdGuiItem::del(), "DeleteAndRemoveFromProject") ) + { + return false; + } + } + Project::ref()->slotRemove(url); + return true; + } + } + // confirm normal delete if wanted + if (confirm) { + QString nice = url.prettyURL(0, KURL::StripFileProtocol); + nice = KStringHandler::csqueeze(nice, 60); + return (KMessageBox::Continue == KMessageBox::warningContinueCancel(window, i18n("<qt>Do you really want to delete <br><b>%1</b>?</qt>").arg(nice), i18n("Delete File or Folder"), KStdGuiItem::del(), "DeleteFileOrFolder") ); + } + return true; +} + +KURL QuantaNetAccess::adjustURL(const KURL &url) +{ + KURL u = url; + if ( u.isLocalFile() ) + { + QDir dir(u.path()); + u.setPath(dir.canonicalPath()); + } + return u; +} + + +void QuantaNetAccess::errorMsg(QWidget* window) +{ + QString msg = KIO::NetAccess::lastErrorString(); + if ( !msg.isEmpty()) + KMessageBox::sorry(window, msg); +} diff --git a/quanta/utility/quantanetaccess.h b/quanta/utility/quantanetaccess.h new file mode 100644 index 00000000..abdedc86 --- /dev/null +++ b/quanta/utility/quantanetaccess.h @@ -0,0 +1,86 @@ +/*************************************************************************** + quantanetaccess.h - description + ------------------- + begin : Sat Jul 27 2002 + copyright : (C) 2004 by Jens Herden <jhe@kdewebdev.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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; version 2 of the License. * + * * + ***************************************************************************/ + +#ifndef QUANTANETACCESS_H +#define QUANTANETACCESS_H + + +/** @short helperclass to track changes in the project + + This class be used as replacement of KIO::NetAccess (look there for details). + Every possible impact on the project will be monitored and signaled to the project. + + If you don't want user interaction use the extra parameter confirm + + If the user don't accept the removal from the project no action happens + and the caller gets false as result. +*/ + +class QuantaNetAccess +{ +public: + + static bool upload(const QString& src, const KURL& target, QWidget* window, bool confirm = true); + + static bool copy( const KURL & src, const KURL & target, QWidget* window, bool confirm = true ) + { + return file_copy( src, target, -1, false /*not overwrite*/, false, window, confirm ); + } + + static bool file_copy( const KURL& src, const KURL& target, int permissions, + bool overwrite, bool resume, QWidget* window, bool confirm = true ); + + static bool file_move( const KURL& src, const KURL& target, int permissions, + bool overwrite, bool resume, QWidget* window, bool confirm = true ); + + static bool dircopy( const KURL & src, const KURL & target, QWidget* window, bool confirm = true ) + { + KURL::List srcList; + srcList.append( src ); + return dircopy( srcList, target, window, confirm ); + }; + + static bool dircopy( const KURL::List & srcList, const KURL & target, QWidget* window, bool confirm = true ); + + static bool move( const KURL& src, const KURL& target, QWidget* window, bool confirm = true ) + { + KURL::List srcList; + srcList.append( src ); + return move( srcList, target, window, confirm ); + } + + static bool move( const KURL::List& srcList, const KURL& target, QWidget* window, bool confirm = true ); + + static bool del( const KURL & url, QWidget* window, bool confirm = true ); + + static bool mkdir( const KURL & url, QWidget* window, int permissions, bool confirm = true ); + + static void checkProjectInsert(const KURL& target, QWidget* window, bool confirm = true); + + static bool checkProjectRemove(const KURL& target, QWidget* window, bool confirm = true, bool remove = true); + + static bool checkProjectDel(const KURL& target, QWidget* window, bool confirm = true); + + static KURL adjustURL(const KURL &url); + + static void errorMsg(QWidget* window = 0); + +private: + QuantaNetAccess() {}; + ~QuantaNetAccess() {}; + +}; + +#endif diff --git a/quanta/utility/resource.h b/quanta/utility/resource.h new file mode 100644 index 00000000..3f09946e --- /dev/null +++ b/quanta/utility/resource.h @@ -0,0 +1,59 @@ +/*************************************************************************** + resource.h - description + ------------------- + begin : ÷ÔÒ íÁÊ 9 13:29:57 EEST 2000 + copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev & Eric Laffoon <pdima@users.sourceforge.net,yshurik@linuxfan.com,sequitur@easystreet.com> + (C) 2001-2003 Andras Mantia <amantia@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef RESOURCE_H +#define RESOURCE_H + + +class KDirWatch; +class KProgress; +class QuantaApp; +class QStringList; +class KTempFile; +class KTempDir; + +class Node; +class Parser; +struct QConfig; +struct DTDStruct; + + +extern QConfig qConfig; +extern QString tmpDir; + +extern QRegExp scriptBeginRx; +extern QRegExp scriptEndRx; + +extern Node *baseNode; +extern Parser *parser; + +extern KDirWatch *fileWatcher; + +extern QString toolbarExtension; +extern QRegExp newLineRx; +extern QStringList charList; //hold the list of the &char; chars. See the data/chars file. +extern QPtrList<KTempFile> tempFileList; +extern QPtrList<KTempDir> tempDirList; + +extern KProgress *progressBar; +extern QuantaApp *quantaApp; +extern const QString resourceDir; +extern bool typingInProgress; + +extern int nodeNum; + +#endif // RESOURCE_H diff --git a/quanta/utility/tagaction.cpp b/quanta/utility/tagaction.cpp new file mode 100644 index 00000000..98bcf87c --- /dev/null +++ b/quanta/utility/tagaction.cpp @@ -0,0 +1,1285 @@ +/*************************************************************************** + tagaction.cpp - description + ------------------- + begin : ? + copyright : (C) ? Dmitry Poplavsky + (C) 2002-2005 Andras Mantia <amantia@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +//other includes +#include <sys/types.h> +#include <unistd.h> + + +//qt includes +#include <qdir.h> +#include <qdom.h> +#include <qfile.h> +#include <qtimer.h> + +//kde includes +#include <kapplication.h> +#include <kdebug.h> +#include <kprocess.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kshortcut.h> +#include <kstandarddirs.h> +#include <ktempfile.h> +#include <ktexteditor/document.h> +#include <ktexteditor/viewcursorinterface.h> +#include <ktexteditor/editinterface.h> +#include <ktexteditor/selectioninterface.h> +#include <ktexteditor/selectioninterfaceext.h> + +//app includes +#include "tagaction.h" +#include "myprocess.h" +#include "document.h" +#include "quantaview.h" +#include "quanta.h" +// #include "quantadoc.h" +#include "tagdialog.h" +#include "messageoutput.h" +#include "quantacommon.h" +#include "resource.h" +#include "qextfileinfo.h" +#include "undoredo.h" +#include "kafkacommon.h" +#include "wkafkapart.h" +#include "cursors.h" +#include "tag.h" +#include "project.h" + +#include "viewmanager.h" + +MyProcess::MyProcess():KProcess() +{ +} + +int MyProcess::commSetupDoneC() +{ + ::setpgid(pid_, 0); + return KProcess::commSetupDoneC(); +} + +TagAction::TagAction( QDomElement *element, KMainWindow *parentMainWindow, bool toggle) + : KToggleAction(element->attribute("text").isEmpty() ? QString("") : i18n(element->attribute("text").utf8()), + KShortcut(element->attribute("shortcut")), 0, 0, parentMainWindow->actionCollection(), element->attribute("name")), + //disable toggle now m_toggle(toggle) + m_toggle(false) +{ + setToolTip(element->attribute("tooltip")); + m_parentMainWindow = parentMainWindow; + m_modified = false; + m_useInputFile = false; + m_useOutputFile = false; + tag = element->cloneNode().toElement(); + QString s = tag.attribute("icon"); + if (!QFileInfo(s).exists()) + { + s = QFileInfo(s).fileName(); + } + setIcon(s); + m_file = 0L; + loopStarted = false; +#if KDE_VERSION >= KDE_MAKE_VERSION(3,4,0) + connect(this, SIGNAL(activated(KAction::ActivationReason, Qt::ButtonState)), + SLOT(slotActionActivated(KAction::ActivationReason, Qt::ButtonState))); +#else + connect(this, SIGNAL(activated()), SLOT(slotActionActivated())); +#endif + connect(this, SIGNAL(showMessage(const QString&, bool)), m_parentMainWindow, SIGNAL(showMessage(const QString&, bool))); + connect(this, SIGNAL(clearMessages()), m_parentMainWindow, SIGNAL(clearMessages())); + connect(this, SIGNAL(showMessagesView()), m_parentMainWindow, SLOT(slotShowMessagesView())); + connect(this, SIGNAL(createNewFile()), m_parentMainWindow, SLOT(slotFileNew())); +} + +TagAction::~TagAction() +{ +} + +QString TagAction::type() +{ + return tag.attribute("type",""); +} + +#if KDE_VERSION >= KDE_MAKE_VERSION(3,4,0) +bool TagAction::slotActionActivated(KAction::ActivationReason reason, Qt::ButtonState /*state*/) +{ + QuantaView *view = ViewManager::ref()->activeView(); + if ( !view || !view->document()) + return false; + + unsigned int line, col; + Document *w = view->document(); + w->viewCursorIf->cursorPositionReal(&line, &col); + NodeModifsSet* modifs = new NodeModifsSet(); + + QString space; + space.fill( ' ', col); + + QString type = tag.attribute("type",""); + + if ( type == "tag" && view->hadLastFocus() == QuantaView::VPLFocus && toggable()) + { + KafkaWidget* kafka_widget = KafkaDocument::ref()->getKafkaWidget(); + QString tag_name = XMLTagName(); + + NodeSelectionInd selection; + selection.fillWithVPLCursorSelection(); + + Node* start_node = 0, *end_node = 0, *current_node = 0; + int start_offset = 0, end_offset = 0, current_offset = 0; + QString scope; + if(kafka_widget->hasSelection()) + { + // get selection + start_node = kafkaCommon::getNodeFromLocation(selection.cursorNode()); + end_node = kafkaCommon::getNodeFromLocation(selection.cursorNodeEndSel()); + current_node = end_node; + start_offset = selection.cursorOffset(); + end_offset = selection.cursorOffsetEndSel(); + current_offset = end_offset; + } + else + { + current_node = kafkaCommon::getNodeFromLocation(selection.cursorNode()); + Q_ASSERT(current_node); + if (current_node) + { + current_offset = selection.cursorOffset(); + + start_node = end_node = current_node; + start_offset = end_offset = current_offset; + + QTag* tag_description = QuantaCommon::tagFromDTD(KafkaDocument::ref()->getCurrentDoc()->defaultDTD(), XMLTagName()); + scope = tag_description->scope(); + // Q_ASSERT(!scope.isNull()); + if(scope.isNull()) + scope = "word"; // FIXME temporary + + if(scope.lower() == "word") + { + // Apply/deapply the tag in the word + if(kafkaCommon::isBetweenWords(current_node, current_offset)) + { + kafkaCommon::getStartOfWord(start_node, start_offset); + kafkaCommon::getEndOfWord(end_node, end_offset); + } + } + else if(scope.lower() == "paragraph") + { + kafkaCommon::getStartOfParagraph(start_node, start_offset); + kafkaCommon::getEndOfParagraph(end_node, end_offset); + } + else if(reason != KAction::EmulatedActivation) // is between words: save the state and return + { + if(!toggled()) + quantaApp->insertTagActionPoolItem(name()); + else + quantaApp->removeTagActionPoolItem(name()); + + return true; + } + } + } + Q_ASSERT(start_node && end_node); + +/* kdDebug(23100) << "start node string: " << start_node->tag->tagStr() << endl; + kdDebug(23100) << "start node offset: " << start_offset << endl; + kdDebug(23100) << "start node string length: " << start_node->tag->tagStr().length() << endl; */ + if (!start_node || !end_node) + return true; //FIXME: AndraS: don't crash + if(scope != "paragraph") { + start_node = kafkaCommon::getCorrectStartNode(start_node, start_offset); + end_node = kafkaCommon::getCorrectEndNode(end_node, end_offset); + if (!start_node || !end_node) + return true; //FIXME: AndraS: don't crash + } + NodeSelection cursor_holder; + cursor_holder.setCursorNode(current_node); + cursor_holder.setCursorOffset(current_offset); + + int inside_tag = kafkaCommon::isInsideTag(start_node, end_node, tag_name); + if(inside_tag == -1) + { + applyTagInSelection(start_node, start_offset, end_node, end_offset, cursor_holder, modifs); + } + else if(inside_tag == 1) + { + QString attribute_name(tag.attribute("attribute_name", QString())); + QString attribute_value(tag.attribute("attribute_value", QString())); + + // special case + if(!attribute_name.isEmpty() && !attribute_value.isEmpty()) + { + Node* tag_parent = kafkaCommon::hasParent(start_node, end_node, tag_name); + + Node* aux1 = start_node->previousSibling(); + while(aux1->tag->type == Tag::Empty) + aux1 = aux1->previousSibling(); + Node* aux2 = end_node->nextSibling(); + while(aux2->tag->type == Tag::Empty) + aux2 = aux2->nextSibling(); + + if(aux1 == tag_parent && aux2 == tag_parent->getClosingNode()) + { + if(tag_parent->tag->attributeValue(attribute_name, true) == attribute_value) + kafkaCommon::editNodeAttribute(tag_parent, attribute_name, QString(), modifs); + else + kafkaCommon::editNodeAttribute(tag_parent, attribute_name, attribute_value, modifs); + } + else + applyTagInSelection(start_node, start_offset, end_node, end_offset, cursor_holder, modifs); + } + else + deapplyTagInSelection(start_node, start_offset, end_node, end_offset, cursor_holder, modifs); + } + else + { + applyTagInMixedSelection(start_node, start_offset, end_node, end_offset, cursor_holder, modifs); + } + w->docUndoRedo->addNewModifsSet(modifs, undoRedo::NodeTreeModif, &cursor_holder); + KafkaDocument::ref()->getKafkaWidget()->setCurrentNode(cursor_holder.cursorNode(), cursor_holder.cursorOffset()); + return true; + } + + if ( type == "tag" ) { + QDomElement otag = (tag.namedItem("tag")).toElement(); + QDomElement xtag = (tag.namedItem("xtag")).toElement(); + + QString attr = otag.text(); + if ( attr[0] == '<' ) + attr.remove(0,1); + if ( attr.right(1) == ">" ) + attr.remove( attr.length()-1, 1 ); + attr = attr.stripWhiteSpace(); + int i = 0; + while ( !attr[i].isSpace() && !attr[i].isNull() ) i++; + QString name = attr.left(i); + attr = attr.remove(0,i).stripWhiteSpace(); + + if (otag.attribute("useDialog","false") == "true" && QuantaCommon::isKnownTag(w->defaultDTD()->name, name)) + { + view->insertNewTag(name, attr, xtag.attribute("inLine","true") == "true"); + } + else + { + QString s1 = QuantaCommon::tagCase(name); + if (otag.text().left(1) == "<") s1 = "<"+s1; + if (!attr.isEmpty()) + s1 += " "+QuantaCommon::attrCase(attr); + if (otag.text().right(1) == ">") + { + QTag *dtdTag = QuantaCommon::tagFromDTD(w->defaultDTD(), name); + if ( w->defaultDTD()->singleTagStyle == "xml" && dtdTag && + (dtdTag->isSingle() || (!qConfig.closeOptionalTags && dtdTag->isOptional())) + ) + { + s1.append(" /"); + } + + s1.append(">"); + } + + QString s2; + if ( xtag.attribute("use","false") == "true" ) + { + if (qConfig.closeTags) + s2 = QuantaCommon::tagCase(xtag.text()); + if ( xtag.attribute("inLine","true") == "true" ) + { + /** FIXME this is quick and temporary */ + view->insertOutputInTheNodeTree(s1, s2); + } + else + { + view->insertOutputInTheNodeTree(s1, s2); + } + } + else + view->insertOutputInTheNodeTree(s1, s2); + } + } + + if (view->hadLastFocus() != QuantaView::VPLFocus) + { + + + if ( type == "text" ) + w->insertTag( tag.namedItem("text").toElement().text() ); + + if ( type == "script" ) + { + proc = new MyProcess(); + proc->setWorkingDirectory(quantaApp->projectBaseURL().path()); + + QDomElement script = tag.namedItem("script").toElement(); + QString command = script.text(); + + + if ( !w->isUntitled() ) { + QString fname = w->url().url(); + if ( w->url().protocol() == "file") + fname = w->url().path(); + command.replace("%f", fname ); + } + + pid_t pid = ::getpid(); + if (kapp->inherits("KUniqueApplication")) + { + command.replace("%pid", QString("unique %1").arg(pid)); + } else + { + command.replace("%pid", QString("%1").arg(pid)); + } + QString buffer; + QString inputType = script.attribute("input","none"); + + if ( inputType == "current" ) { + buffer = w->editIf->text(); + } else + if ( inputType == "selected" && w->selectionIf) { + buffer = w->selectionIf->selection(); + } + command.replace("%input", buffer); + command = command.stripWhiteSpace(); + int pos = command.find(' '); + QString args; + if (pos != -1) + { + args = command.mid(pos+1); + command = command.left(pos); + } + if (command.startsWith("~")) + { + command = command.mid(1); + command.prepend(QDir::homeDirPath()); + } + + *proc << command.stripWhiteSpace(); + args = args.stripWhiteSpace(); + if (!args.isEmpty()) + { + pos = 0; + while (pos != -1 ) + { + pos = args.find("%scriptdir"); + QString scriptname; + if (pos != -1) + { + int begin = args.findRev('"', pos); + int end = -1; + if (begin == -1) + { + begin = args.findRev('\'', pos); + if (begin != -1) + end = args.find('\'', pos); + } else + { + end = args.find('"', pos); + } + if (begin == -1 || end != -1) + { + begin = args.findRev(' ', pos); + if (begin == -1) + begin = 0; + end = args.find(' ', pos); + if (end == -1) + end = args.length(); + } + scriptname = args.mid(begin, end - begin).stripWhiteSpace(); + scriptname.replace("%scriptdir","scripts"); + // kdDebug(24000) << "Script name is: |" << scriptname << "|" << endl; + scriptname = " " + locate("appdata", scriptname); + // kdDebug(24000) << "Script found at: " << scriptname << endl; + args.replace(begin, end - begin, scriptname); + // kdDebug(24000) << "Modified argument list: " << args << endl; + } + } + int pos = args.find("%projectbase"); + if (pos != -1) + { + QString s; + if (Project::ref()->hasProject()) + s = Project::ref()->projectBaseURL().url(); + args.replace("%projectbase", s); + } + QStringList argsList1 = QStringList::split(' ', args); + QStringList argsList; + for (uint i = 0; i < argsList1.count(); i++) + { + if (argsList1[i] == "%userarguments") + { + for (uint j = 0; j < m_argsList.count(); j++) + { + argsList.append(m_argsList[j]); + } + } else + argsList.append(argsList1[i]); + } + m_argsList.clear(); + *proc << argsList; + } + firstOutput = true; + firstError = true; + + connect( proc, SIGNAL(receivedStdout( KProcess*,char*,int)), this, + SLOT( slotGetScriptOutput(KProcess*,char*,int))); + connect( proc, SIGNAL(receivedStderr( KProcess*,char*,int)), this, + SLOT( slotGetScriptError(KProcess*,char*,int))); + connect( proc, SIGNAL(processExited( KProcess*)), this, + SLOT( slotProcessExited(KProcess*))); + + + + if (!m_useOutputFile) + scriptOutputDest = script.attribute("output","none"); + else + scriptOutputDest = "file"; + scriptErrorDest = script.attribute("error","none"); + if (scriptOutputDest == "message") + { + emit showMessagesView(); + } + + if (m_useInputFile) + { + *proc << m_inputFileName; + } + + if (proc->start(KProcess::NotifyOnExit, KProcess::All)) + { + emit clearMessages(); + emit showMessage(i18n("The \"%1\" script started.\n").arg(actionText()), false); + if (!m_useInputFile) + { + if ( inputType == "current" || inputType == "selected" ) + { + proc->writeStdin( buffer.local8Bit(), buffer.length() ); + } + } + proc->closeStdin(); + } else + { + KMessageBox::error(m_parentMainWindow, i18n("<qt>There was an error running <b>%1</b>.<br>Check that you have the <i>%2</i> executable installed and it is accessible.</qt>").arg(command + " " + args).arg(command), i18n("Script Not Found")); + ViewManager::ref()->activeView()->setFocus(); + if (loopStarted) + { + qApp->exit_loop(); + loopStarted = false; + } + return false; + } + } + } + return true; +} +#else + // hack to compile. moc doesn't check the "#ifdef" at the declaration and the compiler complains + // of no matching function. +bool TagAction::slotActionActivated(KAction::ActivationReason /*reason*/, Qt::ButtonState /*state*/) +{return true;} +#endif + +bool TagAction::slotActionActivated() +{ + QuantaView *view = ViewManager::ref()->activeView(); + if ( !view || !view->document()) + return false; + + QString space=""; + QString output; + unsigned int line, col; + + Document *w = view->document(); + w->viewCursorIf->cursorPositionReal(&line, &col); + space.fill( ' ', col); + + QString type = tag.attribute("type",""); + + if ( type == "tag" ) { + QDomElement otag = (tag.namedItem("tag")).toElement(); + QDomElement xtag = (tag.namedItem("xtag")).toElement(); + + QString attr = otag.text(); + if ( attr[0] == '<' ) + attr.remove(0,1); + if ( attr.right(1) == ">" ) + attr.remove( attr.length()-1, 1 ); + attr = attr.stripWhiteSpace(); + int i = 0; + while ( !attr[i].isSpace() && !attr[i].isNull() ) i++; + QString name = attr.left(i); + attr = attr.remove(0,i).stripWhiteSpace(); + + if (otag.attribute("useDialog","false") == "true" && QuantaCommon::isKnownTag(w->defaultDTD()->name, name)) + { + view->insertNewTag(name, attr, xtag.attribute("inLine","true") == "true"); + } + else + { + QString s1 = QuantaCommon::tagCase(name); + if (otag.text().left(1) == "<") s1 = "<"+s1; + if (!attr.isEmpty()) + s1 += " "+QuantaCommon::attrCase(attr); + if (otag.text().right(1) == ">") + { + QTag *dtdTag = QuantaCommon::tagFromDTD(w->defaultDTD(), name); + if ( w->defaultDTD()->singleTagStyle == "xml" && dtdTag && + (dtdTag->isSingle() || (!qConfig.closeOptionalTags && dtdTag->isOptional())) + ) + { + s1.append(" /"); + } + + s1.append(">"); + } + + QString s2; + if ( xtag.attribute("use","false") == "true" ) + { + if (qConfig.closeTags) + s2 = QuantaCommon::tagCase(xtag.text()); + if ( xtag.attribute("inLine","true") == "true" ) + { + /** FIXME this is quick and temporary */ + view->insertOutputInTheNodeTree(s1, s2); + } + else + { + view->insertOutputInTheNodeTree(s1, s2); + } + } + else + view->insertOutputInTheNodeTree(s1, s2); + } + } + + if (view->hadLastFocus() != QuantaView::VPLFocus) + { + + + if ( type == "text" ) + w->insertTag( tag.namedItem("text").toElement().text() ); + + if ( type == "script" ) + { + proc = new MyProcess(); + proc->setWorkingDirectory(quantaApp->projectBaseURL().path()); + + QDomElement script = tag.namedItem("script").toElement(); + QString command = script.text(); + + + if ( !w->isUntitled() ) { + QString fname = w->url().url(); + if ( w->url().protocol() == "file") + fname = w->url().path(); + command.replace("%f", fname ); + } + + pid_t pid = ::getpid(); + if (kapp->inherits("KUniqueApplication")) + { + command.replace("%pid", QString("unique %1").arg(pid)); + } else + { + command.replace("%pid", QString("%1").arg(pid)); + } + QString buffer; + QString inputType = script.attribute("input","none"); + + if ( inputType == "current" ) { + buffer = w->editIf->text(); + } else + if ( inputType == "selected" && w->selectionIf) { + buffer = w->selectionIf->selection(); + } + command.replace("%input", buffer); + command = command.stripWhiteSpace(); + int pos = command.find(' '); + QString args; + if (pos != -1) + { + args = command.mid(pos+1); + command = command.left(pos); + } + if (command.startsWith("~")) + { + command = command.mid(1); + command.prepend(QDir::homeDirPath()); + } + + *proc << command.stripWhiteSpace(); + args = args.stripWhiteSpace(); + if (!args.isEmpty()) + { + pos = 0; + while (pos != -1 ) + { + pos = args.find("%scriptdir"); + QString scriptname; + if (pos != -1) + { + int begin = args.findRev('"', pos); + int end = -1; + if (begin == -1) + { + begin = args.findRev('\'', pos); + if (begin != -1) + end = args.find('\'', pos); + } else + { + end = args.find('"', pos); + } + if (begin == -1 || end != -1) + { + begin = args.findRev(' ', pos); + if (begin == -1) + begin = 0; + end = args.find(' ', pos); + if (end == -1) + end = args.length(); + } + scriptname = args.mid(begin, end - begin).stripWhiteSpace(); + scriptname.replace("%scriptdir","scripts"); + // kdDebug(24000) << "Script name is: |" << scriptname << "|" << endl; + scriptname = " " + locate("appdata", scriptname); + // kdDebug(24000) << "Script found at: " << scriptname << endl; + args.replace(begin, end - begin, scriptname); + // kdDebug(24000) << "Modified argument list: " << args << endl; + } + } + int pos = args.find("%projectbase"); + if (pos != -1) + { + QString s; + if (Project::ref()->hasProject()) + s = Project::ref()->projectBaseURL().url(); + args.replace("%projectbase", s); + } + QStringList argsList1 = QStringList::split(' ', args); + QStringList argsList; + for (uint i = 0; i < argsList1.count(); i++) + { + if (argsList1[i] == "%userarguments") + { + for (uint j = 0; j < m_argsList.count(); j++) + { + argsList.append(m_argsList[j]); + } + } else + argsList.append(argsList1[i]); + } + m_argsList.clear(); + *proc << argsList; + } + firstOutput = true; + firstError = true; + + connect( proc, SIGNAL(receivedStdout( KProcess*,char*,int)), this, + SLOT( slotGetScriptOutput(KProcess*,char*,int))); + connect( proc, SIGNAL(receivedStderr( KProcess*,char*,int)), this, + SLOT( slotGetScriptError(KProcess*,char*,int))); + connect( proc, SIGNAL(processExited( KProcess*)), this, + SLOT( slotProcessExited(KProcess*))); + + + + if (!m_useOutputFile) + scriptOutputDest = script.attribute("output","none"); + else + scriptOutputDest = "file"; + scriptErrorDest = script.attribute("error","none"); + if (scriptOutputDest == "message") + { + emit showMessagesView(); + } + + if (m_useInputFile) + { + *proc << m_inputFileName; + } + + if (proc->start(KProcess::NotifyOnExit, KProcess::All)) + { + emit clearMessages(); + emit showMessage(i18n("The \"%1\" script started.\n").arg(actionText()), false); + if (!m_useInputFile) + { + if ( inputType == "current" || inputType == "selected" ) + { + proc->writeStdin( buffer.local8Bit(), buffer.length() ); + } + } + proc->closeStdin(); + } else + { + KMessageBox::error(m_parentMainWindow, i18n("<qt>There was an error running <b>%1</b>.<br>Check that you have the <i>%2</i> executable installed and it is accessible.</qt>").arg(command + " " + args).arg(command), i18n("Script Not Found")); + ViewManager::ref()->activeView()->setFocus(); + if (loopStarted) + { + qApp->exit_loop(); + loopStarted = false; + } + return false; + } + } + } + return true; +} + +void TagAction::slotGetScriptOutput( KProcess *, char *buffer, int buflen ) +{ + QCString tmp( buffer, buflen + 1 ); + QString text( QString::fromLocal8Bit(tmp) ); +// kdDebug(24000) << "Script output received: |" << text << "|" << endl; + Document *w = ViewManager::ref()->activeDocument(); + if (!w) + { + kdDebug(24000) << "Document not found." << endl; + return; + } + if ( scriptOutputDest == "cursor" ) + { + w->insertTag( text ); + } else + if ( scriptOutputDest == "selection" ) + { + if ( firstOutput ) + { + int line = dynamic_cast<KTextEditor::SelectionInterfaceExt*>(w->doc())->selEndLine(); + int col = dynamic_cast<KTextEditor::SelectionInterfaceExt*>(w->doc())->selEndCol(); + w->viewCursorIf->setCursorPositionReal(line, col); + if (w->selectionIf) + w->selectionIf->removeSelectedText(); + } + w->insertTag( text ); + } else + if ( scriptOutputDest == "replace" ) + { + if ( firstOutput ) + w->editIf->clear(); + w->insertTag( text ); + } else + if ( scriptOutputDest == "new" ) + { + if (firstOutput) + { + emit createNewFile(); + w = ViewManager::ref()->activeDocument(); + } + w->insertTag( text ); + } else + if ( scriptOutputDest == "message" ) + { + if ( firstOutput ) + { + emit showMessagesView(); + emit showMessage(i18n("The \"%1\" script output:\n").arg(actionText()), false); + } + emit showMessage(text, true); + } else + if ( scriptOutputDest == "file" && m_file) + { + if (!m_file->isOpen()) + m_file->open(IO_ReadWrite); + m_file->writeBlock(buffer, buflen); + } + + firstOutput = false; +} + +void TagAction::slotGetScriptError( KProcess *, char *buffer, int buflen ) +{ + Document *w = ViewManager::ref()->activeDocument(); + QCString tmp( buffer, buflen + 1 ); + QString text( QString::fromLocal8Bit(tmp) ); + + if ( scriptErrorDest == "merge" ) + { + scriptErrorDest = scriptOutputDest; + firstError = firstOutput; + } + if ( scriptErrorDest == "cursor" ) + w->insertTag( text ); + else + if ( scriptErrorDest == "selection" ) + { + if ( firstError ) + { + int line = dynamic_cast<KTextEditor::SelectionInterfaceExt*>(w->doc())->selEndLine(); + int col = dynamic_cast<KTextEditor::SelectionInterfaceExt*>(w->doc())->selEndCol(); + w->viewCursorIf->setCursorPositionReal(line, col); + if (w->selectionIf) + w->selectionIf->removeSelectedText(); + } + w->insertTag( text ); + } else + if ( scriptErrorDest == "replace" ) + { + if ( firstError ) + w->editIf->clear(); + w->insertTag( text ); + } else + if ( scriptErrorDest == "new" ) + { + if (firstError) + { + emit createNewFile(); + w = ViewManager::ref()->activeDocument(); + } + w->insertTag( text ); + } else + if ( scriptErrorDest == "message" ) + { + if ( firstError ) + { + emit showMessagesView(); + emit showMessage(i18n("The \"%1\" script output:\n").arg(actionText()), false); + } + emit showMessage(text, true); + } + + firstError = false; +} + +void TagAction::scriptDone() +{ + delete proc; + proc = 0; +} + +void TagAction::setOutputFile(QFile* file) +{ + m_file = file; +} + +void TagAction::setInputFileName(const QString& fileName) +{ + m_inputFileName = fileName; +} + +QString TagAction::actionText() +{ + QString t = tag.attribute("text"); + int pos = t.find('&'); + if (pos < (int)t.length()-1 && t[pos+1] != '&') + return t.remove(pos, 1); + else + return t; +} + +QString TagAction::XMLTagName() const +{ + if(tag.attribute("type","").lower() != "tag") + return QString(); + + QDomElement otag = (tag.namedItem("tag")).toElement(); + QDomElement xtag = (tag.namedItem("xtag")).toElement(); + + QString attr = otag.text(); + if ( attr[0] == '<' ) + attr.remove(0,1); + if ( attr.right(1) == ">" ) + attr.remove( attr.length()-1, 1 ); + attr = attr.stripWhiteSpace(); + int i = 0; + while ( !attr[i].isSpace() && !attr[i].isNull() ) + ++i; + QString name = attr.left(i); + + return name; +} + +QString TagAction::openXMLTagString() const +{ + QString name = XMLTagName(); + + QDomElement otag = (tag.namedItem("tag")).toElement(); + QDomElement xtag = (tag.namedItem("xtag")).toElement(); + + QString attr = otag.text(); + if ( attr[0] == '<' ) + attr.remove(0,1); + if ( attr.right(1) == ">" ) + attr.remove( attr.length()-1, 1 ); + attr = attr.stripWhiteSpace(); + attr.remove(0, name.length()); + + QString s1 = QuantaCommon::tagCase(name); + if (otag.text().left(1) == "<") s1 = "<"+s1; + if (!attr.isEmpty()) + s1 += " "+QuantaCommon::attrCase(attr); + if (otag.text().right(1) == ">") + { + Document* w = ViewManager::ref()->activeView()->document(); + QTag *dtdTag = QuantaCommon::tagFromDTD(w->defaultDTD(), name); + if ( w->defaultDTD()->singleTagStyle == "xml" && dtdTag && + (dtdTag->isSingle() || (!qConfig.closeOptionalTags && dtdTag->isOptional())) + ) + { + s1.append(" /"); + } + + s1.append(">"); + } + + return s1; +} + +QString TagAction::closeXMLTagString() const +{ + QString s2; + QDomElement xtag = (tag.namedItem("xtag")).toElement(); + if ( xtag.attribute("use","false") == "true" ) + { + if (qConfig.closeTags) + s2 = QuantaCommon::tagCase(xtag.text()); + } + return s2; +} + +void TagAction::slotActivated() +{ +// if(m_toggle) + KToggleAction::slotActivated(); +//Andras: Disable toggle behavior. It is just too broken. + setChecked(false); +/* + if(!m_toggle) + setChecked(!isChecked()); +*/ +} + +void TagAction::slotProcessExited(KProcess *process) +{ + if (loopStarted) + { + qApp->exit_loop(); + loopStarted = false; + } + emit showMessage(i18n("The \"%1\" script has exited.").arg(actionText()), false); + delete process; +} + +void TagAction::addArguments(const QStringList &arguments) +{ + m_argsList = arguments; +} + +void TagAction::execute(bool blocking) +{ + m_useInputFile = false; + m_useOutputFile = false; + if (blocking) + { + m_useInputFile = !m_inputFileName.isEmpty(); + m_useOutputFile = (m_file); + if (slotActionActivated()) + { + //To avoid lock-ups, start a timer. + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(slotTimeout())); + timer->start(180*1000, true); + QExtFileInfo internalFileInfo; + loopStarted = true; + m_killCount = 0; + internalFileInfo.enter_loop(); + delete timer; + m_useInputFile = false; + m_useOutputFile = false; + } + } else + slotActionActivated(); +} + +/** Timeout occurred while waiting for some network function to return. */ +void TagAction::slotTimeout() +{ + if ((m_killCount == 0) && (KMessageBox::questionYesNo(m_parentMainWindow, i18n("<qt>The filtering action <b>%1</b> seems to be locked.<br>Do you want to terminate it?</qt>").arg(actionText()), i18n("Action Not Responding"), i18n("Terminate"), i18n("Keep Running")) == KMessageBox::Yes)) + { + if (::kill(-proc->pid(), SIGTERM)) + { + m_killCount++; + return; + } + } + if (m_killCount > 0) + { + ::kill(-proc->pid(), SIGKILL); + if (loopStarted) + { + qApp->exit_loop(); + loopStarted = false; + } + return; + } + timer->start(180*1000, true); +} + +void TagAction::applyTagInSelection(Node* start_node, int start_offset, Node* end_node, int end_offset, + NodeSelection& selection, NodeModifsSet* modifs) const +{ + QuantaView *view = ViewManager::ref()->activeView(); + Document* w = view->document(); + + Q_ASSERT(view->hadLastFocus() == QuantaView::VPLFocus); + Q_ASSERT(toggable()); + + QString tag_name = XMLTagName(); + Q_ASSERT(kafkaCommon::isInsideTag(start_node, end_node, tag_name) == -1); + + QString open_tag = openXMLTagString(); + + //We build the node from the tag name + Node* node = kafkaCommon::createNode("", "", Tag::XmlTag, w); + node->tag->parse(open_tag, w); + node->tag->name = QuantaCommon::tagCase(node->tag->name); + node->tag->single = QuantaCommon::isSingleTag(w->defaultDTD()->name, + node->tag->name); + + long cursor_offset = selection.cursorOffset(); + + Node* nodeCursor = start_node; + Node* nodeParent = start_node; + if (nodeParent->tag->type == Tag::Text) + nodeParent = nodeParent->parent; + + //Checking if at least one parent of node can have a Text Node as child, otherwise + //it is impossible for the + //user to add this node. In that case, try to insert the Node in the closest parent accepting it. + //e.g. TR : a normal insertion would require to have the caret in the TABLE Node, but it is + //impossible + QTag* nodeQTag = QuantaCommon::tagFromDTD(w->defaultDTD(), node->tag->name); + if (!nodeQTag) return; + + bool specialTagInsertion = false; + QPtrList<QTag> qTagList = nodeQTag->parents(); + QTag* qTag = 0; + for (qTag = qTagList.first(); qTag; qTag = qTagList.next()) + { + if (qTag->isChild("#text", false)) + break; + if (qTag == qTagList.getLast()) + specialTagInsertion = true; + } + + bool nodeTreeModified = false; + + if (specialTagInsertion) // Attention: not smartTagInsertion + { + //let's try to insert this node in the closest parent accepting it. + while (nodeParent) + { + QTag* nodeParentQTag = QuantaCommon::tagFromDTD(w->defaultDTD(), nodeParent->tag->name); + if (nodeParentQTag && nodeParentQTag->isChild(node)) + { + nodeCursor = kafkaCommon::createMandatoryNodeSubtree(node, w); + start_offset = 0; + kafkaCommon::insertNodeSubtree(node, nodeParent, 0L, 0L, modifs); + nodeTreeModified = true; + break; + } + nodeParent = nodeParent->parent; + } + } + else if(!nodeQTag->isSingle()) + { + //If some text is selected in kafka, surround the selection with the new Node. + if(!start_node|| !end_node) + return; + nodeTreeModified = kafkaCommon::DTDinsertRemoveNode(node, start_node, start_offset, + end_node, end_offset, w, &nodeCursor, cursor_offset, modifs); + } + +// w->docUndoRedo->addNewModifsSet(modifs, undoRedo::NodeTreeModif); + + // FIXME Set the cursor right: the selection can be inverted + if(KafkaDocument::ref()->getKafkaWidget()->hasSelection()) + { + nodeCursor = end_node; + cursor_offset = end_node->tag->tagStr().length(); + } + + selection.setCursorNode(nodeCursor); + selection.setCursorOffset(cursor_offset); + + //Now update the VPL cursor position +// KafkaDocument::ref()->getKafkaWidget()->setCurrentNode(nodeCursor, cursor_offset); + + if (!nodeTreeModified) + quantaApp->slotStatusMsg(i18n("Cannot insert the tag: invalid location.")); +} + +void TagAction::applyTagInMixedSelection(Node* start_node, int start_offset, Node* end_node, int end_offset, + NodeSelection& selection, NodeModifsSet* modifs) const +{ + Q_ASSERT(start_node != end_node); + + QString const tag_name = XMLTagName(); + + // FIXME o pai pode ser do endNode. nao sei se esta merda eh precisa +/* Node* tag_parent = kafkaCommon::hasParent(start_node, tag_name); + Q_ASSERT(tag_parent);*/ + + QuantaView *view = ViewManager::ref()->activeView(); + Document* w = view->document(); + + // Set start and end nodes to the correct node + start_node = kafkaCommon::getCorrectStartNode(start_node, start_offset); + end_node = kafkaCommon::getCorrectEndNode(end_node, end_offset); + + // look for commonParent + QValueList<int> commonParentStartChildLocation; + QValueList<int> commonParentEndChildLocation; + + Node* commonParent = kafkaCommon::DTDGetCommonParent(start_node, end_node, commonParentStartChildLocation, commonParentEndChildLocation, 0); + if(!commonParent) return; + + Node* cursor_node = selection.cursorNode(); + long cursor_offset = selection.cursorOffset(); + kafkaCommon::splitStartAndEndNodeSubtree(start_node, start_offset, end_node, end_offset, commonParent, + commonParentStartChildLocation, commonParentEndChildLocation, + selection, 0, modifs); + + Q_ASSERT(start_node != end_node); + +// kafkaCommon::coutTree(baseNode, 3); + + //We build the node from the tag name + QString const open_tag_string = openXMLTagString(); + Node* new_node = kafkaCommon::createNode("", "", Tag::XmlTag, w); + new_node->tag->parse(open_tag_string, w); + new_node->tag->name = QuantaCommon::tagCase(new_node->tag->name); + new_node->tag->single = QuantaCommon::isSingleTag(w->defaultDTD()->name, + new_node->tag->name); + + Q_ASSERT(new_node->tag->type == Tag::XmlTag); + + Node* commonParentStartChild = kafkaCommon::getNodeFromLocation(commonParentStartChildLocation, commonParent); + Node* commonParentEndChild = kafkaCommon::getNodeFromLocation(commonParentEndChildLocation, commonParent); +// if(!commonParentStartChild) + commonParentStartChild = kafkaCommon::getCommonParentChild(start_node, commonParent); +/* if(!commonParentEndChild) + commonParentEndChild = kafkaCommon::getCommonParentChild(end_node, commonParent);*/ + + // insert the node, child of commonParent and commonParentStartChild as nextSibling + kafkaCommon::insertNode(new_node, commonParent, commonParentStartChild, commonParentStartChild, modifs); + + // move commonParentStartChild and commonParentEndChild inside new_node + kafkaCommon::moveNode(commonParentStartChild, new_node, 0, selection, modifs, true, true); + if(commonParentEndChild) + kafkaCommon::moveNode(commonParentEndChild, new_node, 0, selection, modifs, true, true); + + // FIXME Set the cursor right: the selection can be inverted + if(KafkaDocument::ref()->getKafkaWidget()->hasSelection()) + { + /*Node* */cursor_node = end_node; + /*int */cursor_offset = end_node->tag->tagStr().length(); + selection.setCursorNode(cursor_node); + selection.setCursorOffset(cursor_offset); + } + cursor_node = selection.cursorNode(); + cursor_offset = selection.cursorOffset(); + + Q_ASSERT(new_node->getClosingNode()); + + // FIXME remove possible equal tags inside the main surrounding tag + kafkaCommon::mergeInlineNode(new_node, new_node->getClosingNode(), &cursor_node, cursor_offset, modifs); + selection.setCursorNode(cursor_node); + selection.setCursorOffset(cursor_offset); + + //Now update the VPL cursor position +// KafkaDocument::ref()->getKafkaWidget()->setCurrentNode(cursor_node, cursor_offset); +} + +void TagAction::deapplyTagInSelection(Node* start_node, int start_offset, Node* end_node, int end_offset, + NodeSelection& selection, NodeModifsSet* modifs) const +{ +// QuantaView *view = ViewManager::ref()->activeView(); +// Document* w = view->document(); + + QString const tag_name = XMLTagName(); + + // Set start and end nodes to the correct node + start_node = kafkaCommon::getCorrectStartNode(start_node, start_offset); + end_node = kafkaCommon::getCorrectEndNode(end_node, end_offset); + + // look for commonParent + QValueList<int> commonParentStartChildLocation; + QValueList<int> commonParentEndChildLocation; + + Node* commonParent = kafkaCommon::DTDGetCommonParent(start_node, end_node, commonParentStartChildLocation, commonParentEndChildLocation, 0); + if(!commonParent) return; + +/* Node* cursor_node = selection.cursorNode(); + int cursor_offset = selection.cursorOffset();*/ + kafkaCommon::splitStartAndEndNodeSubtree(start_node, start_offset, end_node, end_offset, commonParent, + commonParentStartChildLocation, commonParentEndChildLocation, + selection, /*cursor_node, cursor_offset, */0, modifs); + +// kafkaCommon::coutTree(baseNode, 3); + + Node* tag_parent = kafkaCommon::hasParent(start_node, end_node, tag_name); + Q_ASSERT(tag_parent); + + QString attribute_name(tag.attribute("attribute_name", QString())); + QString attribute_value(tag.attribute("attribute_value", QString())); + + if(!attribute_name.isEmpty() && !attribute_value.isEmpty()) + { + kafkaCommon::editNodeAttribute(tag_parent, attribute_name, QString(), modifs); + } + + else + { + Node* common_parent_start_child = kafkaCommon::getCommonParentChild(start_node, tag_parent); + Node* common_parent_end_child = kafkaCommon::getCommonParentChild(end_node, tag_parent); + + Node* parent_of_tag_parent = tag_parent->parent; + if(common_parent_end_child == common_parent_start_child) + common_parent_end_child = 0; + if(!common_parent_start_child) + common_parent_start_child = kafkaCommon::getCommonParentChild(start_node, commonParent); + kafkaCommon::moveNode(common_parent_start_child, parent_of_tag_parent, tag_parent, selection, modifs, true, true); + + if(common_parent_end_child) + kafkaCommon::moveNode(common_parent_end_child, parent_of_tag_parent, tag_parent, selection, modifs, true, true); + + // Remove tag_parent node subtree if empty + if(!tag_parent->hasChildNodes()) + kafkaCommon::extractAndDeleteNode(tag_parent, modifs); + + // FIXME Set the cursor right: the selection can be inverted + if(KafkaDocument::ref()->getKafkaWidget()->hasSelection()) + { + Node* cursor_node = end_node; + int cursor_offset = end_node->tag->tagStr().length(); + selection.setCursorNode(cursor_node); + selection.setCursorOffset(cursor_offset); + } + //Now update the VPL cursor position +// KafkaDocument::ref()->getKafkaWidget()->setCurrentNode(cursor_node, cursor_offset); + } +} + +// void TagAction::deapplyTagInMixedSelection(Node* start_node, int start_offset, Node* end_node, int end_offset, NodeModifsSet* modifs) const +// { +// +// } + + +#include "tagaction.moc" +#include "myprocess.moc" diff --git a/quanta/utility/tagaction.h b/quanta/utility/tagaction.h new file mode 100644 index 00000000..addb35cf --- /dev/null +++ b/quanta/utility/tagaction.h @@ -0,0 +1,137 @@ +/*************************************************************************** + tagaction.h - description + ------------------- + begin : ? + copyright : (C) ? Dmitry Poplavsky + (C) 2002-2005 Andras Mantia <amantia@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef TAGACTION_H +#define TAGACTION_H + +#include <kdeversion.h> +#include <kaction.h> +#include <kactioncollection.h> + +#include <qstring.h> +#include <qdom.h> + +class KMainWindow; +class QuantaView; +class KProcess; +class QDomElement; +class QFile; +class QTimer; +class MessageOutput; +class Node; +class NodeModifsSet; +class NodeSelection; + +/** + * An action for inserting an XML tag. + * + * @author Dmitry Poplavsky, dima@kde.org + * Andras Mantia, amantia@kde.org + * Paulo Moura Guedes, moura@kdewebdev.org + */ + +class TagAction : public KToggleAction +{ + Q_OBJECT + +public: + /** + * Create an insert from dom element. + * @param toggle If set to true, the class behaves like a KToggleAction; + * Else it behaves like a KAction. This avoids the multi-inheritance problem. + */ + TagAction(QDomElement *element, KMainWindow *parentMainWindow, bool toggle = false); + virtual ~TagAction(); + + QDomElement data() const { return tag; } + void setModified(bool modified) { m_modified = modified;} + bool isModified() const {return m_modified;} + void setOutputFile(QFile* file); + void setInputFileName(const QString& fileName); + void addArguments(const QStringList& arguments); + bool toggable() const {return tag.attribute("toggable") == "true";} + bool toggled() const {return isChecked();} + + /** Activates the action. + @param blocking in case of script actions, the script is run in blocking mode, if this argument is true + */ + void execute(bool blocking); + QString type(); + /** + * Remove accelerator from tag action name * + */ + QString actionText(); + + QString XMLTagName() const; + QString openXMLTagString() const; + QString closeXMLTagString() const; + +signals: + void showMessage(const QString& msg, bool append); + void clearMessages(); + void showMessagesView(); + void createNewFile(); + +public slots: + virtual void slotActivated(); + + /** + * We need this information in order to know if queued actions should be applied. + * It's public because it can be activated by other classes if there are any queued actions. + */ + virtual bool slotActionActivated(KAction::ActivationReason reason, Qt::ButtonState state); + +protected slots: + virtual void slotGetScriptOutput( KProcess *, char *buffer, int buflen ); + virtual void slotGetScriptError( KProcess *, char *buffer, int buflen ); + virtual void scriptDone(); + void slotTimeout(); + void slotProcessExited(KProcess *); + virtual bool slotActionActivated(); + +private: + void applyTagInSelection(Node* start_node, int start_offset, Node* end_node, int end_offset, + NodeSelection& selection, NodeModifsSet* modifs) const; + void applyTagInMixedSelection(Node* start_node, int start_offset, Node* end_node, int end_offset, + NodeSelection& selection, NodeModifsSet* modifs) const; + void deapplyTagInSelection(Node* start_node, int start_offset, Node* end_node, int end_offset, + NodeSelection& selection, NodeModifsSet* modifs) const; + void deapplyTagInMixedSelection(Node* start_node, int start_offset, Node* end_node, int end_offset, + NodeSelection& selection, NodeModifsSet* modifs) const; + +private: + KProcess *proc; + bool firstError; + bool firstOutput; + bool m_modified; + bool loopStarted; + bool m_useInputFile; + bool m_useOutputFile; + QString scriptOutputDest; + QString scriptErrorDest; + QTimer* timer; + QDomElement tag; + QFile* m_file; + KMainWindow *m_parentMainWindow; + QString m_inputFileName; + QStringList m_argsList; + uint m_killCount; + bool const m_toggle; +}; + + +#endif // TAGACTION_H diff --git a/quanta/utility/tagactionmanager.cpp b/quanta/utility/tagactionmanager.cpp new file mode 100644 index 00000000..21183073 --- /dev/null +++ b/quanta/utility/tagactionmanager.cpp @@ -0,0 +1,83 @@ +/*************************************************************************** + tagactionmanager.cpp + ------------------- + + copyright : (C) 2004 - Paulo Moura Guedes + email : moura@kdewebdev.org + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <kstaticdeleter.h> +#include <kactioncollection.h> +#include <kaction.h> +#include <khtmlview.h> +#include <klocale.h> +#include <dom/dom_node.h> + +#include <qwidget.h> + +#include "tagactionmanager.h" +#include "tagactionset.h" +#include "resource.h" +#include "wkafkapart.h" + + +TagActionManager* TagActionManager::s_mSelf = 0; +static KStaticDeleter<TagActionManager> staticDeleter; + + +TagActionManager::TagActionManager() + : m_actionCollection(0) +{ + s_mSelf = this; + initActions(KafkaDocument::ref()->getKafkaWidget()->view()); +} + +TagActionManager::~TagActionManager() +{} + +TagActionManager* TagActionManager::self() +{ + if (!s_mSelf) + { + staticDeleter.setObject(s_mSelf, new TagActionManager()); + } + + return s_mSelf; +} + +void TagActionManager::initActions(QWidget* parent) +{ + Q_ASSERT(parent); + + m_actionCollection = new KActionCollection(parent); + + TagActionSet* general(new TagActionSet()); + general->initActions(parent); + m_tagActionSets.append(general); + + TableTagActionSet* table(new TableTagActionSet()); + table->initActions(parent); + m_tagActionSets.append(table); +} + +void TagActionManager::fillWithTagActions(QWidget* widget, DOM::Node const& node) +{ + TagActionSetAbstract* tagActionSet = 0; + for(tagActionSet = m_tagActionSets.first(); tagActionSet; tagActionSet = m_tagActionSets.next()) + tagActionSet->fillWithTagActions(widget, node); +} + +bool TagActionManager::canIndentDTD(QString const& dtd) +{ + return (dtd.contains("HTML", false) || + dtd.contains("XML", false)); +} diff --git a/quanta/utility/tagactionmanager.h b/quanta/utility/tagactionmanager.h new file mode 100644 index 00000000..533c3b39 --- /dev/null +++ b/quanta/utility/tagactionmanager.h @@ -0,0 +1,74 @@ +/*************************************************************************** + tagactionmanager.h + ------------------- + + copyright : (C) 2004 - Paulo Moura Guedes + email : moura@kdewebdev.org + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef TAGACTIONMANAGER_H +#define TAGACTIONMANAGER_H + +class KActionCollection; +namespace DOM +{ +class Node; +} + +#include <qptrlist.h> +class QWidget; + +class TagActionSetAbstract; +class TagActionSet; +class TableTagActionSet; + + +/** + * @author Paulo Moura Guedes + * This class is a singleton. +*/ +class TagActionManager +{ +public: + static TagActionManager* self(); + ~TagActionManager(); + + /** + * This method is used to fill context menus with apropriated actions for node. + * If you want to plug a single action to some widget use actionCollection(). + * @param widget The widget in wich the actions will be pluged. + * @param node The context/current node. + */ + void fillWithTagActions(QWidget* widget, DOM::Node const& node); + + static bool canIndentDTD(QString const& dtd); + + KActionCollection* actionCollection() const + { + return m_actionCollection; + } + +private: + TagActionManager(); + TagActionManager(TagActionManager const&) + {} + + void initActions(QWidget* parent); + +private: + static TagActionManager* s_mSelf; + + KActionCollection* m_actionCollection; + QPtrList<TagActionSetAbstract> m_tagActionSets; +}; + +#endif diff --git a/quanta/utility/tagactionset.cpp b/quanta/utility/tagactionset.cpp new file mode 100644 index 00000000..691ad182 --- /dev/null +++ b/quanta/utility/tagactionset.cpp @@ -0,0 +1,1172 @@ +/*************************************************************************** + tagactionset.cpp + ------------------- + + copyright : (C) 2004 - Paulo Moura Guedes + email : moura@kdewebdev.org + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <kapplication.h> +#include <kconfig.h> +#include <kactioncollection.h> +#include <kactionclasses.h> +#include <dom/dom_node.h> +#include <dom/dom_string.h> +#include <klocale.h> + +#include <qwidget.h> + +#include "tagactionset.h" +#include "tagactionmanager.h" +#include "kafkacommon.h" +#include "cursors.h" +#include "undoredo.h" +#include "wkafkapart.h" +#include "node.h" +#include "quantaview.h" +#include "viewmanager.h" +#include "tag.h" +#include "quantacommon.h" +#include "document.h" +#include "resource.h" + + +TagActionSetAbstract::TagActionSetAbstract(QObject *parent, const char *name) + : QObject(parent, name), m_currentNode(0) +{} + +TagActionSetAbstract::~TagActionSetAbstract() +{} + +Node* TagActionSetAbstract::parentTag(Node* node, QString const& tagName) +{ + Q_ASSERT(node); + + Node* aux = node; + while(aux && aux->nodeName().lower() != tagName) + aux = aux->parent; + + return aux; +} + +Node* TagActionSetAbstract::firstChildTag(Node* startParentNode, QString const& tagName) +{ + Node* aux = startParentNode; + while(aux && aux->nodeName().lower() != tagName) + { + aux = aux->nextSibling(); + if(!startParentNode->hasForChild(aux)) + return 0; + } + + return aux; +} + +bool TagActionSetAbstract::fillWithTagActions(QWidget* /*widget*/, DOM::Node const& node) +{ + m_currentDomNode = node; + m_currentNode = KafkaDocument::ref()->getNode(m_currentDomNode); + + return m_currentNode; +} + +//_____________________________________________________________________________ + +TagActionSet::TagActionSet(QObject *parent, const char *name) + : TagActionSetAbstract(parent, name), m_separator(0) +{ + m_separator = new KActionSeparator(); +} + +bool TagActionSet::isInTagContext() const +{ + return true; +} + +void TagActionSet::initActionMenus(QWidget* /*widget*/) +{ +} + +void TagActionSet::initActions(QWidget* /*parent*/) +{ + KActionCollection* ac(TagActionManager::self()->actionCollection()); + + QString actionName = "apply_source_indentation"; + new KAction(i18n("Apply Source Indentation"), 0, this, + SLOT(slotApplySourceIndentation()), + ac, actionName); + + actionName = "copy_div_element"; + new KAction(i18n("Copy DIV Area"), 0, this, + SLOT(slotCopyDivElement()), + ac, actionName); + + actionName = "cut_div_element"; + new KAction(i18n("Cut DIV Area"), 0, this, + SLOT(slotCutDivElement()), + ac, actionName); +} + +bool TagActionSet::fillWithTagActions(QWidget* widget, DOM::Node const& node) +{ + bool validNode = TagActionSetAbstract::fillWithTagActions(widget, node); + + if(!validNode || !isInTagContext()) + { + unplugAllActions(widget); + return false; + } + + m_separator->unplugAll(); + + KActionCollection* ac(TagActionManager::self()->actionCollection()); + + KAction* copyDivAction = ac->action("copy_div_element"); + Q_ASSERT(copyDivAction); + KAction* cutDivAction = ac->action("cut_div_element"); + Q_ASSERT(cutDivAction); + + if(/*!KafkaDocument::ref()->getKafkaWidget()->hasSelection() && */isInDivArea()) + { + if(!copyDivAction->isPlugged(widget)) + copyDivAction->plug(widget); + + if(!cutDivAction->isPlugged(widget)) + cutDivAction->plug(widget); + + m_separator->plug(widget); + } + else + { + copyDivAction->unplug(widget); + cutDivAction->unplug(widget); + } + +// KAction* applySourceIndentationAction = ac->action("apply_source_indentation"); +// Q_ASSERT(applySourceIndentationAction); +// +// applySourceIndentationAction->unplug(widget); // to keep things in order +// applySourceIndentationAction->plug(widget); +// +// m_separator->plug(widget); + + return true; +} + +void TagActionSet::unplugAllActions(QWidget* widget) const +{ + KActionCollection* ac(TagActionManager::self()->actionCollection()); + + m_separator->unplugAll(); + + KAction* applySourceIndentationAction = ac->action("apply_source_indentation"); + Q_ASSERT(applySourceIndentationAction); + applySourceIndentationAction->unplug(widget); + + KAction* copyDivAction = ac->action("copy_div_element"); + Q_ASSERT(copyDivAction); + copyDivAction->unplug(widget); + + KAction* cutDivAction = ac->action("cut_div_element"); + Q_ASSERT(cutDivAction); + cutDivAction->unplug(widget); +} + +void TagActionSet::slotApplySourceIndentation() +{ + QuantaView* view = ViewManager::ref()->activeView(); + NodeModifsSet *modifs = new NodeModifsSet(); + + KConfig* config = kapp->config(); + config->setGroup("Kate Document Defaults"); + int indentationWidth = config->readNumEntry("Indentation Width", 4); + + //Once the changes have been made, we will generate the "clean" string for Text Nodes only, and + //we will add the empty indentation Nodes. + int eLine, eCol; + Node* node = baseNode; + while(node) + { + if(/*!node->tag->cleanStrBuilt() && */node->tag->type == Tag::Text) + { + if(!node->insideSpecial) + { + node->tag->setStr(KafkaDocument::ref()->generateCodeFromNode(node, 0, 0, eLine, eCol, false)); + node->tag->setCleanStrBuilt(true); + } + } + if(/*!node->tag->indentationDone() && */!node->insideSpecial) + { + kafkaCommon::fitIndentationNodes(kafkaCommon::getPrevNodeNE(node), node, modifs); + bool goUp = false; + kafkaCommon::fitIndentationNodes(node, kafkaCommon::getNextNodeNE(node, goUp), modifs); + kafkaCommon::applyIndentation(node, indentationWidth, 0, modifs, qConfig.inlineNodeIndentation); + } + node = node->nextSibling(); + } + + view->document()->docUndoRedo->addNewModifsSet(modifs, undoRedo::NodeTreeModif); +} + +bool TagActionSet::isInDivArea() const +{ + Q_ASSERT(m_currentNode); + + return parentTag(m_currentNode, "div"); +} + +void TagActionSet::slotCopyDivElement() +{ + Q_ASSERT(m_currentNode); + + Node* divNode = parentTag(m_currentNode, "div"); + Q_ASSERT(divNode); + + Node* divClosingNode = divNode->getClosingNode(); + if(!divClosingNode) + { + kdError(25001) << "DIV element without closing node: " << divNode << endl; + return; + } + + KafkaDocument::ref()->slotCopy(divNode, 0, divClosingNode, 0, QString()); +} + +void TagActionSet::slotCutDivElement() +{ + Q_ASSERT(m_currentNode); + + Node* divNode = parentTag(m_currentNode, "div"); + Q_ASSERT(divNode); + + Node* divClosingNode = divNode->getClosingNode(); + if(!divClosingNode) + { + kdError(25001) << "DIV element without closing node: " << divNode << endl; + return; + } + + NodeSelectionInd selection_ind; + selection_ind.fillWithVPLCursorSelection(); + + int cursorOffset = selection_ind.cursorOffset(); + + KafkaDocument::ref()->slotCut(divNode, 0, divClosingNode, 0, &m_currentNode, cursorOffset, QString()); + +} + +//_____________________________________________________________________________ + +TableTagActionSet::TableTagActionSet(QObject *parent, const char *name) + : TagActionSetAbstract(parent, name), m_separator(0), m_tableActionMenu_0(0), m_insertActionMenu_1(0) +{ + m_separator = new KActionSeparator(); +} + +bool TableTagActionSet::isInTagContext() const +{ + return parentTag(m_currentNode, "table"); +} + +void TableTagActionSet::initActionMenus(QWidget* widget) +{ + Q_ASSERT(!m_tableActionMenu_0); + + m_tableActionMenu_0 = new KActionMenu(i18n("Table..."), widget); + m_insertActionMenu_1 = new KActionMenu(i18n("Insert..."), m_tableActionMenu_0); + m_removeActionMenu_1 = new KActionMenu(i18n("Remove..."), m_tableActionMenu_0); +} + + +void TableTagActionSet::initActions(QWidget* parent) +{ + if(!m_tableActionMenu_0) + initActionMenus(parent); + + KActionCollection* ac(TagActionManager::self()->actionCollection()); + + // Insert___________________________________________________________________________ + + QString actionName = "insert_table"; + //m_actionNames += actionName; + new KAction(i18n("Table..."), 0, this, + SLOT(slotInsertTable()), + ac, actionName); + + actionName = "insert_row_above"; + //m_actionNames += actionName; + new KAction(i18n("Row Above"), 0, this, + SLOT(slotInsertRowAbove()), + ac, actionName); + + actionName = "insert_row_below"; + //m_actionNames += actionName; + new KAction(i18n("Row Below"), 0, this, + SLOT(slotInsertRowBelow()), + ac, actionName); + + actionName = "insert_column_left"; + //m_actionNames += actionName; + new KAction(i18n("Column Left"), 0, this, + SLOT(slotInsertColumnLeft()), + ac, actionName); + + actionName = "insert_column_right"; + //m_actionNames += actionName; + new KAction(i18n("Column Right"), 0, this, + SLOT(slotInsertColumnRight()), + ac, actionName); + + // Remove___________________________________________________________________________ + + actionName = "remove_table"; + //m_actionNames += actionName; + new KAction(i18n("Table"), 0, this, + SLOT(slotRemoveTable()), + ac, actionName); + + actionName = "remove_rows"; + //m_actionNames += actionName; + new KAction(i18n("Row(s)"), 0, this, + SLOT(slotRemoveRows()), + ac, actionName); + + actionName = "remove_columns"; + //m_actionNames += actionName; + new KAction(i18n("Column(s)"), 0, this, + SLOT(slotRemoveColumns()), + ac, actionName); + + actionName = "remove_cells"; + //m_actionNames += actionName; + new KAction(i18n("Cell(s)"), 0, this, + SLOT(slotRemoveCells()), + ac, actionName); + + actionName = "remove_cells_content"; + //m_actionNames += actionName; + new KAction(i18n("Cell(s) Content"), 0, this, + SLOT(slotRemoveCellsContent()), + ac, actionName); + + // Merge___________________________________________________________________________ + + actionName = "merge_selected_cells"; + //m_actionNames += actionName; + new KAction(i18n("Merge Selected Cells"), 0, this, + SLOT(slotMergeSelectedCells()), + ac, actionName); +} + +bool TableTagActionSet::fillWithTagActions(QWidget* widget, DOM::Node const& node) +{ + bool validNode = TagActionSetAbstract::fillWithTagActions(widget, node); + + if(!validNode || !isInTagContext(/*node*/)) + { + unplugAllActions(widget); + return false; + } + + m_separator->unplugAll(); + + KActionCollection* ac(TagActionManager::self()->actionCollection()); + + // Table + bool emptyTableActionMenu_0 = true; + + // Insert _____________________________________________________________________ + + // Insert + bool emptyInsertActionMenu_1 = true; + + // Insert Table + KAction* insertTableAction = ac->action("insert_table"); + Q_ASSERT(insertTableAction); + + m_insertActionMenu_1->remove(insertTableAction); + if(canInsertTable()) + { + emptyTableActionMenu_0 = emptyInsertActionMenu_1 = false; + m_insertActionMenu_1->insert(insertTableAction); + + m_insertActionMenu_1->insert(m_separator); + } + // Insert Row Above + KAction* insertRowAboveAction = ac->action("insert_row_above"); + Q_ASSERT(insertRowAboveAction); + + m_insertActionMenu_1->remove(insertRowAboveAction); + if(canInsertRowAbove()) + { + emptyTableActionMenu_0 = emptyInsertActionMenu_1 = false; + m_insertActionMenu_1->insert(insertRowAboveAction); + + //m_insertActionMenu_1->insert(m_separator); + } + // Insert Row Below + KAction* insertRowBelowAction = ac->action("insert_row_below"); + Q_ASSERT(insertRowBelowAction); + + m_insertActionMenu_1->remove(insertRowBelowAction); + if(canInsertRowBelow()) + { + emptyTableActionMenu_0 = emptyInsertActionMenu_1 = false; + m_insertActionMenu_1->insert(insertRowBelowAction); + + m_insertActionMenu_1->insert(m_separator); + } + // Insert Column Left + KAction* insertColumnLeftAction = ac->action("insert_column_left"); + Q_ASSERT(insertColumnLeftAction); + + m_insertActionMenu_1->remove(insertColumnLeftAction); + if(canInsertColumnLeft()) + { + emptyTableActionMenu_0 = emptyInsertActionMenu_1 = false; + m_insertActionMenu_1->insert(insertColumnLeftAction); + + //m_insertActionMenu_1->insert(m_separator); + } + // Insert Column Right + KAction* insertColumnRightAction = ac->action("insert_column_right"); + Q_ASSERT(insertColumnRightAction); + + m_insertActionMenu_1->remove(insertColumnRightAction); + if(canInsertColumnRight()) + { + emptyTableActionMenu_0 = emptyInsertActionMenu_1 = false; + m_insertActionMenu_1->insert(insertColumnRightAction); + + m_insertActionMenu_1->insert(m_separator); + } + // Remove _____________________________________________________________________ + + // Remove + bool emptyRemoveActionMenu_1 = true; + + // Remove Table + KAction* removeTableAction = ac->action("remove_table"); + Q_ASSERT(removeTableAction); + + m_removeActionMenu_1->remove(removeTableAction); + if(canRemoveTable()) + { + emptyTableActionMenu_0 = emptyRemoveActionMenu_1 = false; + m_removeActionMenu_1->insert(removeTableAction); + + m_removeActionMenu_1->insert(m_separator); + } + // Remove Row(s) + KAction* removeRowsAction = ac->action("remove_rows"); + Q_ASSERT(removeRowsAction); + + m_removeActionMenu_1->remove(removeRowsAction); + if(canRemoveRows()) + { + emptyTableActionMenu_0 = emptyRemoveActionMenu_1 = false; + m_removeActionMenu_1->insert(removeRowsAction); + + //m_removeActionMenu_1->insert(m_separator); + } + // Remove Column(s) + KAction* removeColumnsAction = ac->action("remove_columns"); + Q_ASSERT(removeColumnsAction); + + m_removeActionMenu_1->remove(removeColumnsAction); + if(canRemoveColumns()) + { + emptyTableActionMenu_0 = emptyRemoveActionMenu_1 = false; + m_removeActionMenu_1->insert(removeColumnsAction); + + //m_removeActionMenu_1->insert(m_separator); + } +/* // Remove Cell(s) + KAction* removeCellsAction = ac->action("remove_cells"); + Q_ASSERT(removeCellsAction); + + m_removeActionMenu_1->remove(removeCellsAction); + if(canRemoveCells()) + { + emptyTableActionMenu_0 = emptyRemoveActionMenu_1 = false; + m_removeActionMenu_1->insert(removeCellsAction); + + //m_removeActionMenu_1->insert(m_separator); + }*/ + // Remove Cell(s) Content + KAction* removeCellsContentAction = ac->action("remove_cells_content"); + Q_ASSERT(removeCellsContentAction); + + m_removeActionMenu_1->remove(removeCellsContentAction); + if(canRemoveCellsContent()) + { + emptyTableActionMenu_0 = emptyRemoveActionMenu_1 = false; + m_removeActionMenu_1->insert(removeCellsContentAction); + + //m_removeActionMenu_1->insert(m_separator); + } + // Remove _____________________________________________________________________ + + // Merge +// bool emptyRemoveActionMenu_1 = true; + + // _____________________________________________________________________________ + + // Table + m_tableActionMenu_0->unplug(widget); + if(!emptyTableActionMenu_0) + { + m_tableActionMenu_0->plug(widget); + + m_tableActionMenu_0->remove(m_insertActionMenu_1); + if(!emptyInsertActionMenu_1) + m_tableActionMenu_0->insert(m_insertActionMenu_1); + + m_tableActionMenu_0->remove(m_removeActionMenu_1); + if(!emptyRemoveActionMenu_1) + m_tableActionMenu_0->insert(m_removeActionMenu_1); + + m_tableActionMenu_0->insert(m_separator); + } + // Merge selected cells + KAction* mergeSelectedCellsAction = ac->action("merge_selected_cells"); + Q_ASSERT(mergeSelectedCellsAction); + + m_tableActionMenu_0->remove(mergeSelectedCellsAction); + if(canMergeSelectedCells()) + { + emptyTableActionMenu_0 = false; + m_tableActionMenu_0->insert(mergeSelectedCellsAction); + +// m_removeActionMenu_1->insert(m_separator); + } + + return true; +} + +void TableTagActionSet::unplugAllActions(QWidget* widget) const +{ + m_separator->unplugAll(); + m_tableActionMenu_0->unplug(widget); +} + +// Insert _____________________________________________________________________ + +bool TableTagActionSet::canInsertTable() const +{ + return false; + // return isInTagContext(currentDomNode()); // TODO Implement slotInsertTable +} + +void TableTagActionSet::slotInsertTable() +{ + Q_ASSERT(m_currentNode); + +} + +bool TableTagActionSet::canInsertRowAbove() const +{ + return isInTagContext() && parentTag(m_currentNode, "tbody"); +} + +void TableTagActionSet::slotInsertRowAbove() +{ + Q_ASSERT(m_currentNode); + + Node* nearRow = parentTag(m_currentNode, "tr"); + + if(!nearRow) + return; + + Node* nodeParent= nearRow->parent; + + QuantaView* view = ViewManager::ref()->activeView(); + NodeModifsSet *modifs = new NodeModifsSet(); + Node* nodeToInsert = buildEmptyRowSubtree(); + + kafkaCommon::insertNodeSubtree(nodeToInsert, nodeParent, nearRow, modifs); + + view->document()->docUndoRedo->addNewModifsSet(modifs, undoRedo::NodeTreeModif); + +// kafkaCommon::coutTree(baseNode, 3); +} + +bool TableTagActionSet::canInsertRowBelow() const +{ + return isInTagContext(); +} + +void TableTagActionSet::slotInsertRowBelow() +{ + Q_ASSERT(m_currentNode); + + Node* nearRow = 0; + Node* aux = parentTag(m_currentNode, "thead"); + if(aux) + nearRow= firstChildTag(tableStart(), "tr"); + else + nearRow = parentTag(m_currentNode, "tr"); + + if(!nearRow) + return; + + Node* nodeParent= nearRow->parent; + Node* nextSibling = nearRow->SNext(); + + QuantaView* view = ViewManager::ref()->activeView(); + NodeModifsSet *modifs = new NodeModifsSet(); + Node* nodeToInsert = buildEmptyRowSubtree(); + + kafkaCommon::insertNodeSubtree(nodeToInsert, nodeParent, nextSibling, modifs); + + view->document()->docUndoRedo->addNewModifsSet(modifs, undoRedo::NodeTreeModif); + +// kafkaCommon::coutTree(baseNode, 3); +} + +bool TableTagActionSet::canInsertColumnLeft() const +{ + return isInTagContext(); +} + +void TableTagActionSet::slotInsertColumnLeft() +{ + Q_ASSERT(m_currentNode); + + QuantaView* view = ViewManager::ref()->activeView(); + NodeModifsSet *modifs = new NodeModifsSet(); + + Node* nodeToInsertInBody = buildEmptyTBodyCellSubtree(); + Node* nodeToInsertInHead = buildEmptyTHeadCellSubtree(); + Q_ASSERT(nodeToInsertInBody); + Q_ASSERT(nodeToInsertInHead); + + int const _currentColumnIndex = currentColumnIndex(); + + // thead + Node* trChild = firstChildTag(firstChildTag(tableStart(), "thead"), "tr"); + while(trChild) + { + Node* thChild = firstChildTag(trChild, "th"); + for(int i = 0; (i != _currentColumnIndex && thChild); ++i) + thChild = thChild->SNext(); + + kafkaCommon::insertNodeSubtree(nodeToInsertInHead, trChild, thChild, modifs); + + nodeToInsertInHead = kafkaCommon::duplicateNodeSubtree(nodeToInsertInHead); + + trChild = trChild->SNext(); + } + + // tbody + trChild = firstChildTag(firstChildTag(tableStart(), "tbody"), "tr"); + while(trChild) + { + Node* tdChild = firstChildTag(trChild, "td"); + for(int i = 0; (i != _currentColumnIndex && tdChild); ++i) + tdChild = tdChild->SNext(); + + kafkaCommon::insertNodeSubtree(nodeToInsertInBody, trChild, tdChild, modifs); + + nodeToInsertInBody = kafkaCommon::duplicateNodeSubtree(nodeToInsertInBody); + + trChild = trChild->SNext(); + } + + view->document()->docUndoRedo->addNewModifsSet(modifs, undoRedo::NodeTreeModif); + +// kafkaCommon::coutTree(baseNode, 3); +} + +bool TableTagActionSet::canInsertColumnRight() const +{ + return isInTagContext(); +} + +void TableTagActionSet::slotInsertColumnRight() +{ + Q_ASSERT(m_currentNode); + + QuantaView* view = ViewManager::ref()->activeView(); + NodeModifsSet *modifs = new NodeModifsSet(); + + Node* nodeToInsertInBody = buildEmptyTBodyCellSubtree(); + Node* nodeToInsertInHead = buildEmptyTHeadCellSubtree(); + Q_ASSERT(nodeToInsertInBody); + Q_ASSERT(nodeToInsertInHead); + + int const _currentColumnIndex = currentColumnIndex(); + + // thead + Node* trChild = firstChildTag(firstChildTag(tableStart(), "thead"), "tr"); + while(trChild) + { + Node* thChild = firstChildTag(trChild, "th"); + for(int i = 0; (i != _currentColumnIndex + 1 && thChild); ++i) + thChild = thChild->SNext(); + + kafkaCommon::insertNodeSubtree(nodeToInsertInHead, trChild, thChild, modifs); + + nodeToInsertInHead = kafkaCommon::duplicateNodeSubtree(nodeToInsertInHead); + + trChild = trChild->SNext(); + } + + // tbody + trChild = firstChildTag(firstChildTag(tableStart(), "tbody"), "tr"); + while(trChild) + { + Node* tdChild = firstChildTag(trChild, "td"); + for(int i = 0; (i != _currentColumnIndex + 1 && tdChild); ++i) + tdChild = tdChild->SNext(); + + kafkaCommon::insertNodeSubtree(nodeToInsertInBody, trChild, tdChild, modifs); + + nodeToInsertInBody = kafkaCommon::duplicateNodeSubtree(nodeToInsertInBody); + + trChild = trChild->SNext(); + } + + view->document()->docUndoRedo->addNewModifsSet(modifs, undoRedo::NodeTreeModif); + +// kafkaCommon::coutTree(baseNode, 3); +} + +// Remove ________________________________________________________________ + +bool TableTagActionSet::canRemoveTable() const +{ + return isInTagContext(); +} + +void TableTagActionSet::slotRemoveTable() +{ + Q_ASSERT(m_currentNode); + + QuantaView* view = ViewManager::ref()->activeView(); + NodeModifsSet *modifs = new NodeModifsSet(); + + Node* _tableStart = tableStart(); + + kafkaCommon::extractAndDeleteNode(_tableStart, modifs, true, true); + + view->document()->docUndoRedo->addNewModifsSet(modifs, undoRedo::NodeTreeModif); + +// kafkaCommon::coutTree(baseNode, 3); +} + +bool TableTagActionSet::canRemoveRows() const +{ + return isInTagContext(); +} + +void TableTagActionSet::slotRemoveRows() +{ + Q_ASSERT(m_currentNode); + + QuantaView* view = ViewManager::ref()->activeView(); + NodeModifsSet *modifs = new NodeModifsSet(); + + NodeSelectionInd selection; + selection.fillWithVPLCursorSelection(); + + if(!selection.hasSelection()) + { + Node* nearTr = parentTag(m_currentNode, "tr"); + kafkaCommon::extractAndDeleteNode(nearTr, modifs, true, true); + } + else + { + Node* startSelection = kafkaCommon::getNodeFromLocation(selection.cursorNode()); + Node* endSelection = kafkaCommon::getNodeFromLocation(selection.cursorNodeEndSel()); + + Node* startTr = parentTag(startSelection, "tr"); + Node* endTr = parentTag(endSelection, "tr"); + + Node* iteratorNode = startTr; + bool loop(true); + while(iteratorNode && loop) + { + // the check has to be done before extract + if(iteratorNode == endTr) + loop = false; + + Node* aux = iteratorNode; + iteratorNode = iteratorNode->SNext(); + + kafkaCommon::extractAndDeleteNode(aux, modifs, true, true); + } + } + + view->document()->docUndoRedo->addNewModifsSet(modifs, undoRedo::NodeTreeModif); + +// kafkaCommon::coutTree(baseNode, 3); +} + +bool TableTagActionSet::canRemoveColumns() const +{ + return isInTagContext(); +} + +void TableTagActionSet::slotRemoveColumns() +{ + Q_ASSERT(m_currentNode); + + QuantaView* view = ViewManager::ref()->activeView(); + NodeModifsSet *modifs = new NodeModifsSet(); + + NodeSelectionInd selection; + selection.fillWithVPLCursorSelection(); + + if(!selection.hasSelection()) + { + int const _currentColumnIndex = currentColumnIndex(); + removeColumn(_currentColumnIndex, modifs); + } + else + { + Node* startSelection = kafkaCommon::getNodeFromLocation(selection.cursorNode()); + Node* endSelection = kafkaCommon::getNodeFromLocation(selection.cursorNodeEndSel()); + + int startColumnIndex = columnIndex(startSelection); + int endColumnIndex = columnIndex(endSelection); + int numberOfColumnsSelected = endColumnIndex - startColumnIndex + 1; + + if(startColumnIndex == -1 || endColumnIndex == -1) + return; + + m_currentNode = parentTag(m_currentNode, "tbody"); // m_currentNode will become invalid + for(int i = 0; i != numberOfColumnsSelected; ++i) + removeColumn(startColumnIndex, modifs); + } + + view->document()->docUndoRedo->addNewModifsSet(modifs, undoRedo::NodeTreeModif); + +// kafkaCommon::coutTree(baseNode, 3); +} + +bool TableTagActionSet::canRemoveCells() const +{ + return isInTagContext(); +} + +void TableTagActionSet::slotRemoveCells() +{} + +bool TableTagActionSet::canRemoveCellsContent() const +{ + return isInTagContext(); +} + +void TableTagActionSet::slotRemoveCellsContent() +{ + Q_ASSERT(m_currentNode); + + QuantaView* view = ViewManager::ref()->activeView(); + NodeModifsSet *modifs = new NodeModifsSet(); + + NodeSelectionInd selection; + selection.fillWithVPLCursorSelection(); + + if(!selection.hasSelection()) + { + Node* aux = m_currentNode; + m_currentNode = parentTag(m_currentNode, "tbody"); + + Node* nearTd = parentTag(aux, "td"); + clearCellContents(nearTd, modifs); + } + else + { + Node* startSelection = kafkaCommon::getNodeFromLocation(selection.cursorNode()); + Node* endSelection = kafkaCommon::getNodeFromLocation(selection.cursorNodeEndSel()); + + Node* startTd = parentTag(startSelection, "td"); + Node* endTd = parentTag(endSelection, "td"); + + if(!startTd || !endTd) + return; + + } + + view->document()->docUndoRedo->addNewModifsSet(modifs, undoRedo::NodeTreeModif); + +// kafkaCommon::coutTree(baseNode, 3); +} + +// Merge ________________________________________________________________ + +bool TableTagActionSet::canMergeSelectedCells() const +{ + if(!KafkaDocument::ref()->getKafkaWidget()->hasSelection()) + return false; + + NodeSelectionInd selection; + selection.fillWithVPLCursorSelection(); + QValueList<int> start = selection.cursorNode(); + QValueList<int> end = selection.cursorNodeEndSel(); + + return start != end; +} + +void TableTagActionSet::slotMergeSelectedCells() +{ + Q_ASSERT(m_currentNode); + + QuantaView* view = ViewManager::ref()->activeView(); + NodeModifsSet *modifs = new NodeModifsSet(); + + NodeSelectionInd selection; + selection.fillWithVPLCursorSelection(); + + Q_ASSERT(selection.hasSelection()); + + Node* startSelection = kafkaCommon::getNodeFromLocation(selection.cursorNode()); + Node* endSelection = kafkaCommon::getNodeFromLocation(selection.cursorNodeEndSel()); + + Node* startTd = parentTag(startSelection, "td"); + Node* endTd = parentTag(endSelection, "td"); + + Node* nodeIterator = startTd->SNext(); + Node* stopNode = endTd->SNext(); + int count = 1; + while(nodeIterator && nodeIterator != stopNode) + { + Node* aux = nodeIterator; + nodeIterator = nodeIterator->SNext(); + + Node* child = aux->firstChild(); + while(child) + { + Node* next = child->next; + kafkaCommon::moveNode(child, startTd, 0, modifs); + child = next; + } + + kafkaCommon::extractAndDeleteNode(aux, modifs); + + ++count; + } + + if(count == 1) + return; + + kafkaCommon::editNodeAttribute(startTd, "colspan", QString::number(count), modifs); + kafkaCommon::editNodeAttribute(startTd, "rowspan", "1", modifs); + + view->document()->docUndoRedo->addNewModifsSet(modifs, undoRedo::NodeTreeModif); + +// kafkaCommon::coutTree(baseNode, 3); +} + +//_____________________________________________________________________________ + +Node* TableTagActionSet::tableStart() const +{ + Q_ASSERT(isInTagContext()); + Q_ASSERT(m_currentNode); + + return parentTag(m_currentNode, "table"); +} + +int TableTagActionSet::numberOfColumns() const +{ + Node* _tableStart = tableStart(); + if(!_tableStart) + return -1; + + Node* firstTd = firstChildTag(_tableStart, "td"); + + if(!firstTd) + return -1; + + int count(0); + Node* aux = firstTd; + while(aux) + { + ++count; + aux = aux->SNext(); + } + + kdDebug(23100) << "Number of columns: " << count << endl; + return count; +} + +int TableTagActionSet::currentColumnIndex() const +{ + return columnIndex(m_currentNode); +/* Node* nearTd = parentTag(m_currentNode, "td"); + if(!nearTd) + return -1; + + Node* _tableStart = tableStart(); + if(!_tableStart) + return -1; + + Node* firstTd = firstChildTag(parentTag(m_currentNode, "tr"), "td"); + //Node* firstTd = firstChildTag(_tableStart, "td"); + if(!firstTd) + return -1; + + int count(0); + Node* aux = firstTd; + while(aux && aux != nearTd) + { + ++count; + aux = aux->SNext(); + } + + if(!aux) + count = -1; + return count;*/ +} + +int TableTagActionSet::columnIndex(Node* node) const +{ + Node* nearTd = parentTag(node, "td"); + if(!nearTd) + return -1; + + Node* _tableStart = tableStart(); + if(!_tableStart) + return -1; + + Node* firstTd = firstChildTag(parentTag(node, "tr"), "td"); + if(!firstTd) + return -1; + + int count(0); + Node* aux = firstTd; + while(aux && aux != nearTd) + { + ++count; + aux = aux->SNext(); + } + + if(!aux) + count = -1; + return count; +} + +Node* TableTagActionSet::buildEmptyRowSubtree() const +{ + QuantaView* view = ViewManager::ref()->activeView(); + + Node* nodeToInsert = kafkaCommon::createNode("", "", Tag::XmlTag, view->document()); + nodeToInsert->tag->parse("<tr>", view->document()); + kafkaCommon::createMandatoryNodeSubtree(nodeToInsert, view->document()); + // now we have: <tr><td></td></tr> + + //Let's -> <tr><td><br></td></tr> + Node* brNode = kafkaCommon::createNode("", "", Tag::XmlTag, view->document()); + brNode->tag->parse("<br>", view->document()); + Node* tdNode = nodeToInsert->child; + kafkaCommon::insertNode(brNode, tdNode, 0, 0); + + int _numberOfColumns = numberOfColumns(); + + if(_numberOfColumns == -1) + return 0; + + for(int i = 1; i != _numberOfColumns; ++i) + { + Node* duplicatedTdSubtree = kafkaCommon::duplicateNodeSubtree(tdNode); + kafkaCommon::insertNodeSubtree(duplicatedTdSubtree, nodeToInsert, 0, 0); + } + + kafkaCommon::coutTree(nodeToInsert, 3); + + return nodeToInsert; +} + +Node* TableTagActionSet::buildEmptyTBodyCellSubtree() const +{ + QuantaView* view = ViewManager::ref()->activeView(); + + // build tree -> <td><br></td> + Node* nodeToInsert = kafkaCommon::createNode("", "", Tag::XmlTag, view->document()); + nodeToInsert->tag->parse("<td>", view->document()); + Node* brNode = kafkaCommon::createNode("", "", Tag::XmlTag, view->document()); + brNode->tag->parse("<br>", view->document()); + kafkaCommon::insertNode(brNode, nodeToInsert, 0, 0); + + return nodeToInsert; +} + +Node* TableTagActionSet::buildEmptyTHeadCellSubtree() const +{ + QuantaView* view = ViewManager::ref()->activeView(); + + // build tree -> <td><br></td> + Node* nodeToInsert = kafkaCommon::createNode("", "", Tag::XmlTag, view->document()); + nodeToInsert->tag->parse("<th>", view->document()); + Node* brNode = kafkaCommon::createNode("", "", Tag::XmlTag, view->document()); + brNode->tag->parse("<br>", view->document()); + kafkaCommon::insertNode(brNode, nodeToInsert, 0, 0); + + return nodeToInsert; +} + +void TableTagActionSet::removeColumn(int _currentColumnIndex, NodeModifsSet* modifs) +{ + Q_ASSERT(m_currentNode); + Q_ASSERT(_currentColumnIndex >= 0); + Q_ASSERT(modifs); + + // thead + Node* trChild = firstChildTag(firstChildTag(tableStart(), "thead"), "tr"); + while(trChild) + { + Node* thChild = firstChildTag(trChild, "th"); + for(int i = 0; (i != _currentColumnIndex && thChild); ++i) + thChild = thChild->SNext(); + + kafkaCommon::extractAndDeleteNode(thChild, modifs, true, true); + + trChild = trChild->SNext(); + } + + // tbody + trChild = firstChildTag(firstChildTag(tableStart(), "tbody"), "tr"); + while(trChild) + { + Node* tdChild = firstChildTag(trChild, "td"); + for(int i = 0; (i != _currentColumnIndex && tdChild); ++i) + tdChild = tdChild->SNext(); + + kafkaCommon::extractAndDeleteNode(tdChild, modifs, true, true); + + trChild = trChild->SNext(); + } +} + +void TableTagActionSet::clearCellContents(Node* tdNode, NodeModifsSet* modifs) +{ + if (!tdNode) + return; + Node* tdChild = tdNode->child; + + if(!tdChild) + return; + + while(tdChild) + { + Node* aux = tdChild; + tdChild = tdChild->next; + + kafkaCommon::extractAndDeleteNode(aux, modifs, true, false); + } +} + +//_____________________________________________________________________________ + +#include "tagactionset.moc" diff --git a/quanta/utility/tagactionset.h b/quanta/utility/tagactionset.h new file mode 100644 index 00000000..6bb2448f --- /dev/null +++ b/quanta/utility/tagactionset.h @@ -0,0 +1,161 @@ +/*************************************************************************** + tagactionset.h + ------------------- + + copyright : (C) 2004 - Paulo Moura Guedes + email : moura@kdewebdev.org + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef TAGACTIONSET_H +#define TAGACTIONSET_H + +namespace DOM +{ +class Node; +} +class KActionSeparator; + +#include <qobject.h> +class QWidget; +class KActionSet; +class KActionMenu; + +class Node; +class NodeModifsSet; + +/** +@author Paulo Moura Guedes +*/ +class TagActionSetAbstract : public QObject +{ + Q_OBJECT +public: + TagActionSetAbstract(QObject *parent = 0, const char *name = 0); + virtual ~TagActionSetAbstract(); + + DOM::Node const& currentDomNode() const {return m_currentDomNode;} + + static Node* parentTag(Node* node, QString const& tagName); + static Node* firstChildTag(Node* node, QString const& tagName); + + virtual bool isInTagContext(/*DOM::Node const& node*/) const = 0; + virtual void initActions(QWidget* parent) = 0; + virtual bool fillWithTagActions(QWidget* widget, DOM::Node const& node); + virtual void unplugAllActions(QWidget* widget) const = 0; + +protected: + virtual void initActionMenus(QWidget* widget) = 0; + +protected: + Node* m_currentNode; + +private: + DOM::Node m_currentDomNode; +}; + + +class TagActionSet : public TagActionSetAbstract +{ + Q_OBJECT +public: + TagActionSet(QObject *parent = 0, const char *name = 0); + virtual ~TagActionSet() {} + + virtual bool isInTagContext() const; + virtual void initActions(QWidget* parent); + virtual bool fillWithTagActions(QWidget* widget, DOM::Node const& node); + virtual void unplugAllActions(QWidget* widget) const; + +public slots: + void slotApplySourceIndentation(); + void slotCopyDivElement(); + void slotCutDivElement(); + +protected: + virtual void initActionMenus(QWidget* widget); + +private: + bool isInDivArea() const; + +private: + KActionSeparator* m_separator; +}; + + +class TableTagActionSet : public TagActionSetAbstract +{ + Q_OBJECT +public: + TableTagActionSet(QObject *parent = 0, const char *name = 0); + virtual ~TableTagActionSet() + {} + + virtual bool isInTagContext(/*DOM::Node const& node*/) const; + virtual void initActions(QWidget* parent); + virtual bool fillWithTagActions(QWidget* widget, DOM::Node const& node); + virtual void unplugAllActions(QWidget* widget) const; + +public slots: + // Insert + void slotInsertTable(); + void slotInsertRowAbove(); + void slotInsertRowBelow(); + void slotInsertColumnLeft(); + void slotInsertColumnRight(); + // Remove + void slotRemoveTable(); + void slotRemoveRows(); + void slotRemoveColumns(); + void slotRemoveCells(); + void slotRemoveCellsContent(); + // Merge + void slotMergeSelectedCells(); + +protected: + virtual void initActionMenus(QWidget* widget); + +private: + // Insert + bool canInsertTable() const; + bool canInsertRowAbove() const; + bool canInsertRowBelow() const; + bool canInsertColumnLeft() const; + bool canInsertColumnRight() const; + // Remove + bool canRemoveTable() const; + bool canRemoveRows() const; + bool canRemoveColumns() const; + bool canRemoveCells() const; + bool canRemoveCellsContent() const; + // Merge + bool canMergeSelectedCells() const; + + Node* tableStart() const; + int numberOfColumns() const; + int currentColumnIndex() const; + int columnIndex(Node* node) const; + Node* buildEmptyRowSubtree() const; + Node* buildEmptyTBodyCellSubtree() const; + Node* buildEmptyTHeadCellSubtree() const; + + void removeColumn(int index, NodeModifsSet* modifs); + void clearCellContents(Node* tdNode, NodeModifsSet* modifs); + +private: + KActionSeparator* m_separator; + + KActionMenu* m_tableActionMenu_0; + KActionMenu* m_insertActionMenu_1; + KActionMenu* m_removeActionMenu_1; +}; + +#endif diff --git a/quanta/utility/toolbartabwidget.cpp b/quanta/utility/toolbartabwidget.cpp new file mode 100644 index 00000000..03c6e86b --- /dev/null +++ b/quanta/utility/toolbartabwidget.cpp @@ -0,0 +1,351 @@ +/*************************************************************************** + toolbartabwidget.cpp + --------------------- + copyright : (C) 2003 by Andras Mantia <amantia@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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; version 2 of the License. * + * * + ***************************************************************************/ + +//qt includes +#include <qevent.h> +#include <qlayout.h> +#include <qobjectlist.h> +#include <qpoint.h> +#include <qtabbar.h> +#include <qwidgetstack.h> +#include <qtabwidget.h> +#include <qfontmetrics.h> + +//kde includes +#include <kaction.h> +#include <kaccelmanager.h> +#include <kdeversion.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpopupmenu.h> +#include <ktoolbar.h> +#include <ktoolbarbutton.h> +#include <kdebug.h> +#include <kglobalsettings.h> + +//app includes +#include "toolbartabwidget.h" + +ToolbarTabWidget::ToolbarTabWidget(QWidget * parent, const char * name, WFlags f) +:QTabWidget(parent, name, f) +{ + m_popupMenu = new KPopupMenu(this); + m_popupMenu->insertTitle(i18n("Toolbar Menu"), 1); + m_popupMenu->insertItem(i18n("New Action..."), parent, SLOT(slotNewAction())); + m_popupMenu->insertSeparator(); + m_popupMenu->insertItem(i18n("New Toolbar..."), parent, SLOT(slotAddToolbar())); + m_popupMenu->insertItem(i18n("Remove Toolbar"), this, SLOT(slotRemoveToolbar())); + m_popupMenu->insertItem(i18n("Rename Toolbar..."), this, SLOT(slotRenameToolbar())); + m_popupMenu->insertItem(SmallIconSet("configure_toolbars"), i18n("Configure Toolbars..."), this, SLOT(slotEditToolbar())); + + connect(this, SIGNAL(removeToolbar(const QString&)), + parent, SLOT(slotRemoveToolbar(const QString&))); + connect(this, SIGNAL(renameToolbar(const QString&)), + parent, SLOT(slotRenameToolbar(const QString&))); + connect(this, SIGNAL(editToolbar(const QString&)), + parent, SLOT(slotConfigureToolbars(const QString&))); + connect(this, SIGNAL(newAction()), + parent, SLOT(slotNewAction())); + connect(this, SIGNAL(addToolbar()), + parent, SLOT(slotAddToolbar())); + KAcceleratorManager::setNoAccel(this); +} + +void ToolbarTabWidget::insertTab(QWidget *child, const QString &label, const QString &id) +{ + if (child->inherits("KToolBar") && child->parentWidget()) + { + QTabWidget::insertTab(child->parentWidget(), label); + toolbarList.insert(id, child); + } +} + +QWidget* ToolbarTabWidget::page(int index) +{ + QWidget *w = QTabWidget::page(index); + + for (QMap<QString, QWidget*>::Iterator it = toolbarList.begin(); it != toolbarList.end(); ++it) + { + if (it.data()->parentWidget() == w) + { + w = *it; + break; + } + } + return w; +} + +QString ToolbarTabWidget::id(QWidget *w) const +{ + QString idStr; + for (QMap<QString, QWidget*>::ConstIterator it = toolbarList.constBegin(); it != toolbarList.constEnd(); ++it) + { + if (it.data()->parentWidget() == w) + { + idStr = it.key(); + break; + } + } + return idStr; +} + +QString ToolbarTabWidget::id(int index) const +{ + QWidget *w = QTabWidget::page(index); + QString idStr; + for (QMap<QString, QWidget*>::ConstIterator it = toolbarList.constBegin(); it != toolbarList.constEnd(); ++it) + { + if (it.data()->parentWidget() == w) + { + idStr = it.key(); + break; + } + } + return idStr; +} + +QWidget* ToolbarTabWidget::page(const QString& id) +{ + QWidget *w = toolbarList.find(id).data(); + return w; +} + +void ToolbarTabWidget::removePage(QWidget * w) +{ + QWidget *parent = w->parentWidget(); + if (w->inherits("KToolBar") && parent) + { + QTabWidget::removePage(parent); + for (QMap<QString, QWidget*>::ConstIterator it = toolbarList.constBegin(); it != toolbarList.constEnd(); ++it) + { + if (it.data() == w) + { + toolbarList.remove(it.key()); + break; + } + } + delete parent; + } +} + +void ToolbarTabWidget::slotRemoveToolbar() +{ + emit removeToolbar(tabUnderMouse.lower()); +} + +void ToolbarTabWidget::slotRenameToolbar() +{ + emit renameToolbar(tabUnderMouse.lower()); +} + +void ToolbarTabWidget::slotEditToolbar() +{ + emit editToolbar(tabUnderMouseLabel + " <quanta>"); +} + +void ToolbarTabWidget::mousePressEvent ( QMouseEvent * e ) +{ + if (e->button() == Qt::RightButton) + { + QPoint p = e->globalPos(); + QTab *tab = 0L; + QWidget *pageW = 0L; + for (int i =0; i < tabBar()->count(); i++) + { + tab = tabBar()->tabAt(i); + pageW = page(i); + QRect r = tab->rect(); + QPoint p1 = mapToGlobal(r.topLeft()); + QPoint p2 = mapToGlobal(r.bottomRight()); + if (QRect(p1, p2).contains(p)) + break; + else + tab = 0L; + } + tabUnderMouseLabel = tab ? tab->text() : label(currentPageIndex()); + if (!pageW) + pageW = currentPage(); + for (QMap<QString, QWidget*>::Iterator it = toolbarList.begin(); it != toolbarList.end(); ++it) + { + if (it.data()->parentWidget() == pageW) + { + tabUnderMouse = it.key(); + break; + } + } + m_popupMenu->changeTitle(1, i18n("Toolbar Menu") + " - " + i18n(tabUnderMouseLabel.utf8())); + m_popupMenu->popup(p); + } +} + + +void ToolbarTabWidget::resizeEvent(QResizeEvent *e) +{ + QWidget::resizeEvent(e); + QWidget *tb; + for (QMap<QString, QWidget*>::Iterator it = toolbarList.begin(); it != toolbarList.end(); ++it) + { + tb = it.data(); + tb->resize(QSize(width(), tb->height())); + } + int i = currentPageIndex(); + if (i > 0) + { + setCurrentPage(i -1); + } else + if (i+1 < count()) + { + setCurrentPage(i + 1); + } + setCurrentPage(i); +} + +int ToolbarTabWidget::tabHeight() const +{ + int height = tabBar()->height(); + if (height < 2) + { + height = QFontMetrics(KGlobalSettings::generalFont()).height() + 12; + } + return height; +} + + +QuantaToolBar::QuantaToolBar(QWidget *parent, const char *name, bool honor_style, bool readConfig) +:KToolBar (parent, name=0, honor_style, readConfig) +{ + m_popupMenu = new KPopupMenu(this); + m_toolbarTab = dynamic_cast<ToolbarTabWidget*>(parent->parentWidget()); + currentActionName = ""; + m_iconTextMenu = new KPopupMenu(this); + m_iconTextMenu->setCheckable(true); + m_iconTextMenu->insertItem(i18n("Icons Only"), 0); + m_iconTextMenu->insertItem(i18n("Text Only"), 1); + m_iconTextMenu->insertItem(i18n("Text Alongside Icons"), 2); + m_iconTextMenu->insertItem(i18n("Text Under Icons"), 3); + connect(m_iconTextMenu, SIGNAL(activated(int)), SLOT(slotIconTextChanged(int))); + connect(m_iconTextMenu, SIGNAL(aboutToShow()), SLOT(slotIconTextMenuAboutToShow())); + setIconText(ToolbarTabWidget::ref()->iconText(), false); +} + +void QuantaToolBar::slotIconTextMenuAboutToShow() +{ + m_iconTextMenu->setItemChecked(0, false); + m_iconTextMenu->setItemChecked(1, false); + m_iconTextMenu->setItemChecked(2, false); + m_iconTextMenu->setItemChecked(3, false); + switch (ToolbarTabWidget::ref()->iconText()) + { + case IconOnly: m_iconTextMenu->setItemChecked(0, true); + break; + case TextOnly: m_iconTextMenu->setItemChecked(1, true); + break; + case IconTextRight: m_iconTextMenu->setItemChecked(2, true); + break; + case IconTextBottom: m_iconTextMenu->setItemChecked(3, true); + break; + } +} + +void QuantaToolBar::slotIconTextChanged(int id) +{ + ToolbarTabWidget *toolbarTab = ToolbarTabWidget::ref(); + int width = toolbarTab->width(); + int bigHeight = iconSize() + QFontMetrics(KGlobalSettings::toolBarFont()).height() + 10; + int normalHeight = iconSize() + 10; + for (int i = 0; i < toolbarTab->count(); i++) + { + QuantaToolBar *tb = static_cast<QuantaToolBar*>(toolbarTab->page(i)); + switch (id) + { + case 0: tb->setIconText(IconOnly); + tb->setGeometry(0,0, width, normalHeight); + break; + case 1: tb->setIconText(TextOnly); + tb->setGeometry(0,0, width, normalHeight); + break; + case 2: tb->setIconText(IconTextRight); + tb->setGeometry(0,0, width, normalHeight); + break; + case 3: tb->setIconText(IconTextBottom); + tb->setGeometry(0,0, width, bigHeight); + break; + } + } + toolbarTab->setIconText(iconText()); + if (id == 3) + { + toolbarTab->setFixedHeight(toolbarTab->tabHeight() + height() + 3); + } else + { + toolbarTab->setFixedHeight(toolbarTab->tabHeight() + height() + 3); + } +} + +void QuantaToolBar::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == Qt::RightButton) + { + m_popupMenu->clear(); + QPoint p = e->globalPos(); + if (m_toolbarTab) + { + m_toolbarTab->tabUnderMouse = m_toolbarTab->id(m_toolbarTab->currentPageIndex()); + m_toolbarTab->tabUnderMouseLabel = m_toolbarTab->label(m_toolbarTab->currentPageIndex()); + m_popupMenu->insertTitle(i18n("Toolbar Menu") + " - " + + i18n(m_toolbarTab->tabUnderMouseLabel.utf8())); + m_popupMenu->insertItem(i18n("New Action..."), m_toolbarTab, SIGNAL(newAction())); + QObjectList* childrenList = queryList("KToolBarButton"); + for (uint i = 0; i < childrenList->count(); i++) + { + KToolBarButton *w = static_cast<KToolBarButton*>(childrenList->at(i)); + QPoint p1 = w->parentWidget()->mapToGlobal(w->pos()); + QPoint p2 = QPoint(p1.x() + w->width(), p1.y()+w->height()); + if (QRect(p1, p2).contains(p)) + { + currentActionName = w->textLabel(); + QString actionName = currentActionName; + m_popupMenu->insertItem(i18n("Remove Action - %1").arg(actionName.replace('&',"&&")), this, SLOT(slotRemoveAction())); + m_popupMenu->insertItem(i18n("Edit Action - %1").arg(actionName), this, SLOT(slotEditAction())); + break; + } + } + m_popupMenu->insertSeparator(); + m_popupMenu->insertItem(i18n("New Toolbar..."), m_toolbarTab, SIGNAL(addToolbar())); + m_popupMenu->insertItem(i18n("Remove Toolbar"), m_toolbarTab, SLOT(slotRemoveToolbar())); + m_popupMenu->insertItem(i18n("Rename Toolbar..."), m_toolbarTab, SLOT(slotRenameToolbar())); + m_popupMenu->insertSeparator(); + m_popupMenu->insertItem( i18n("Text Position"), m_iconTextMenu); + m_popupMenu->insertItem(SmallIconSet("configure_toolbars"), i18n("Configure Toolbars..."), m_toolbarTab, SLOT(slotEditToolbar())); + } + m_popupMenu->popup(p); + } +} + +void QuantaToolBar::slotEditAction() +{ + emit editAction(currentActionName); +} + +void QuantaToolBar::slotRemoveAction() +{ + if ( KMessageBox::warningContinueCancel(this, i18n("<qt>Are you sure you want to remove the <b>%1</b> action?</qt>").arg(currentActionName),QString::null,KStdGuiItem::del()) == KMessageBox::Continue ) + { + emit removeAction(m_toolbarTab->tabUnderMouse, currentActionName); + } +} + + +#include "toolbartabwidget.moc" diff --git a/quanta/utility/toolbartabwidget.h b/quanta/utility/toolbartabwidget.h new file mode 100644 index 00000000..aa800ae6 --- /dev/null +++ b/quanta/utility/toolbartabwidget.h @@ -0,0 +1,111 @@ +/*************************************************************************** + toolbartabwidget.h + --------------------- + copyright : (C) 2003 by Andras Mantia <amantia@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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; version 2 of the License. * + * * + ***************************************************************************/ + +#ifndef TOOLBARTABWIDGET_H +#define TOOLBARTABWIDGET_H + +#include <qwidget.h> +#include <qmap.h> + +class QTabWidget; +class KPopupMenu; +class QWidgetStack; +class QTabBar; +class KToolBar; + +class ToolbarTabWidget: public QTabWidget +{ + Q_OBJECT +public: + QWidgetStack *m_widgetStack; + + static ToolbarTabWidget* const ref(QWidget *parent = 0L, const char *name = 0L) + { + static ToolbarTabWidget *m_ref; + if (!m_ref) m_ref = new ToolbarTabWidget (parent, name); + return m_ref; + } + virtual ~ToolbarTabWidget(){}; + + virtual void insertTab(QWidget *child, const QString &label, const QString &id); + virtual QWidget* page(int index); + virtual QWidget* page(const QString& id); + int tabHeight() const; + QString id(QWidget *w) const; + QString id(int index) const; + KToolBar::IconText iconText() const {return m_iconText;} + void setIconText(KToolBar::IconText mode) {m_iconText = mode; emit iconTextModeChanged();} + +public slots: + virtual void removePage(QWidget * w ); + virtual void resizeEvent(QResizeEvent *); + +private slots: + void slotRemoveToolbar(); + void slotRenameToolbar(); + void slotEditToolbar(); + +signals: + void removeToolbar(const QString&); + void renameToolbar(const QString&); + void editToolbar(const QString&); + void newAction(); + void addToolbar(); + void iconTextModeChanged(); + +public: + QString tabUnderMouse; + QString tabUnderMouseLabel; + +protected: + virtual void mousePressEvent ( QMouseEvent * e ); + + KPopupMenu *m_popupMenu; + QTabBar *m_tabBar; + QMap<QString, QWidget*> toolbarList; + +private: + ToolbarTabWidget(QWidget * parent = 0, const char * name = 0, WFlags f = 0); + KToolBar::IconText m_iconText; + +}; + +class QuantaToolBar: public KToolBar +{ + Q_OBJECT + +public: + QuantaToolBar (QWidget *parent, const char *name=0, bool honor_style=FALSE, bool readConfig=TRUE); + virtual ~QuantaToolBar() {}; + +private slots: + void slotEditAction(); + void slotRemoveAction(); + void slotIconTextChanged(int id); + void slotIconTextMenuAboutToShow(); + +signals: + void removeAction(const QString&, const QString&); + void editAction(const QString&); + +protected: + virtual void mousePressEvent ( QMouseEvent * e ); + + KPopupMenu *m_popupMenu; + KPopupMenu *m_iconTextMenu; + ToolbarTabWidget *m_toolbarTab; + QString currentActionName; +}; + +#endif diff --git a/quanta/utility/toolbarxmlgui.cpp b/quanta/utility/toolbarxmlgui.cpp new file mode 100644 index 00000000..1d7d1558 --- /dev/null +++ b/quanta/utility/toolbarxmlgui.cpp @@ -0,0 +1,27 @@ +/*************************************************************************** + toolbarxmlgui.cpp - description + ------------------- + begin : Sat Jul 20 2002 + copyright : (C) 2002 by Andras Mantia <amantia@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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; version 2 of the License. * + * * + ***************************************************************************/ + +#include "toolbarxmlgui.h" + +ToolbarXMLGUI::ToolbarXMLGUI(const QString& xmlFile) + : KXMLGUIClient() +{ + setLocalXMLFile(xmlFile); + setXMLFile(xmlFile, false); +} + + +ToolbarXMLGUI::~ToolbarXMLGUI(){ +} diff --git a/quanta/utility/toolbarxmlgui.h b/quanta/utility/toolbarxmlgui.h new file mode 100644 index 00000000..74ac04cf --- /dev/null +++ b/quanta/utility/toolbarxmlgui.h @@ -0,0 +1,32 @@ +/*************************************************************************** + toolbarxmlgui.h - description + ------------------- + begin : Sat Jul 20 2002 + copyright : (C) 2002 by Andras Mantia <amantia@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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; version 2 of the License. * + * * + ***************************************************************************/ + + +#ifndef TOOLBARXMLGUI_H +#define TOOLBARXMLGUI_H + +#include <kxmlguiclient.h> + +/** + *@author Andras Mantia + */ + +class ToolbarXMLGUI : public KXMLGUIClient { +public: + ToolbarXMLGUI(const QString& xmlFile); + ~ToolbarXMLGUI(); +}; + +#endif |