diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | e9ae80694875f869892f13f4fcaf1170a00dea41 (patch) | |
tree | aa2f8d8a217e2d376224c8d46b7397b68d35de2d /quanta/src | |
download | tdewebdev-e9ae80694875f869892f13f4fcaf1170a00dea41.tar.gz tdewebdev-e9ae80694875f869892f13f4fcaf1170a00dea41.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdewebdev@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'quanta/src')
28 files changed, 16610 insertions, 0 deletions
diff --git a/quanta/src/Makefile.am b/quanta/src/Makefile.am new file mode 100644 index 00000000..5b8a47d9 --- /dev/null +++ b/quanta/src/Makefile.am @@ -0,0 +1,74 @@ +if BUILD_WYSIWYG +LIB_KAFKA = $(top_builddir)/quanta/parts/kafka/libkafkalibrary.la +KAFKAINCLUDE = -I$(top_srcdir)/quanta/parts/kafka -I$(top_builddir)/quanta/parts/kafka +endif + +if include_cvsservice +CVSSERVICE_INCLUDE = -I$(top_srcdir)/quanta/components/cvsservice +LIB_CVSSERVICE = $(top_builddir)/quanta/components/cvsservice/libcvsservice.la -lcvsservice +endif + +bin_PROGRAMS = quanta + +quanta_SOURCES = quanta_init.cpp quantaview.cpp quantadoc.cpp main.cpp document.cpp kqapp.cpp quanta.cpp dcopwindowmanagerif.skel dcopsettingsif.skel dcopquantaif.skel dcopsettings.cpp dtds.cpp dcopquanta.cpp viewmanager.cpp + + +quanta_LDADD = $(top_builddir)/quanta/project/libproject.la \ + $(top_builddir)/quanta/plugins/libplugins.la \ + $(top_builddir)/quanta/parsers/libparser.la \ + $(top_builddir)/quanta/parsers/dtd/libdtdparser.la \ + $(top_builddir)/quanta/treeviews/libtreeviews.la \ + $(top_builddir)/quanta/dialogs/libdialogs.la \ + $(top_builddir)/quanta/components/debugger/libdebuggermanager.la \ + $(top_builddir)/quanta/dialogs/tagdialogs/libtagdialogs.la \ + $(top_builddir)/quanta/dialogs/settings/libsettingsdialogs.la \ + $(top_builddir)/quanta/messages/libmessages.la \ + $(top_builddir)/quanta/components/framewizard/libframewizard.la \ + $(top_builddir)/quanta/components/csseditor/libcsseditor.la \ + $(top_builddir)/quanta/components/tableeditor/libtableeditor.la \ + $(top_builddir)/quanta/parts/preview/libpreview.la \ + $(top_builddir)/quanta/utility/libutility.la \ + $(top_builddir)/lib/libquantamodule.la \ + -lkatepartinterfaces $(LIB_KNEWSTUFF) $(LIB_KAFKA) $(LIB_CVSSERVICE) \ + $(LIB_KMDI) $(LIBXML_LIBS) $(LIBXSLT_LIBS) $(LIB_KABC) $(LIB_KSPELL) $(LIB_KHTML) +# $(top_builddir)/lib/compatibility/libcompat.la + +AM_CPPFLAGS = -I$(top_srcdir)/quanta/dialogs \ + -I$(top_srcdir)/quanta/plugins \ + -I$(top_srcdir)/quanta/components/framewizard \ + -I$(top_srcdir)/quanta/components/csseditor \ + -I$(top_srcdir)/quanta/components/debugger \ + -I$(top_srcdir)/quanta/components/tableeditor \ + -I$(top_srcdir)/quanta/dialogs/tagdialogs \ + -I$(top_srcdir)/quanta/dialogs/settings \ + -I$(top_srcdir)/quanta/utility \ + -I$(top_srcdir)/quanta/treeviews \ + -I$(top_srcdir)/quanta/project \ + -I$(top_srcdir)/quanta/parsers \ + -I$(top_srcdir)/quanta/parsers/dtd \ + -I$(top_srcdir)/quanta/parts/preview \ + -I$(top_srcdir)/quanta/parts/kafka \ + -I$(top_srcdir)/quanta/messages \ + -I$(top_srcdir)/lib \ + -I$(top_builddir)/quanta/dialogs \ + -I$(top_builddir)/quanta/dialogs/tagdialogs \ + -I$(top_builddir)/quanta/dialogs/settings \ + -I$(top_builddir)/quanta/components/csseditor \ + -I$(top_builddir)/quanta/components/framewizard \ + -I$(top_builddir)/quanta/components/tableeditor \ + -I$(top_builddir)/quanta/plugins \ + $(KAFKAINCLUDE) $(CVSSERVICE_INCLUDE) $(KMDI_INCLUDES) \ + $(KNEWSTUFF_INCLUDES) $(all_includes) +# -I$(top_srcdir)/lib/compatibility + +quanta_METASOURCES = AUTO + +# the library search path. +quanta_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +# this is where the kdelnk file will go +xdg_apps_DATA = quanta.desktop + +kdemimedir = $(kde_mimedir)/application +kdemime_DATA = x-webprj.desktop + diff --git a/quanta/src/dcopquanta.cpp b/quanta/src/dcopquanta.cpp new file mode 100644 index 00000000..55ff068e --- /dev/null +++ b/quanta/src/dcopquanta.cpp @@ -0,0 +1,113 @@ +/*************************************************************************** + dcopquanta.cpp - description + ------------------- + begin : Thu Mar 4 2004 + copyright : (C) 2004 Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +//kde includes + +//qt includes +#include <qregexp.h> + +//app includes +#include "dcopquanta.h" +#include "node.h" + +extern GroupElementMapList globalGroupMap; + +DCOPQuanta::DCOPQuanta() : DCOPObject("QuantaIf") +{ +} + +QStringList DCOPQuanta::selectors(const QString& tag) +{ + const QRegExp rx("\\.|\\#|\\:"); + QStringList selectorList; + GroupElementMapList::Iterator it; + for ( it = globalGroupMap.begin(); it != globalGroupMap.end(); ++it ) + { + QString key = it.key(); + if (key.startsWith("Selectors|")) + { + QString selectorName = key.mid(10); + int index = selectorName.find(':'); + if (index != -1) + selectorName = selectorName.mid(0, index); + QString tmpStr; + index = selectorName.find(rx); + if (index != -1) + { + tmpStr = selectorName.left(index).lower(); + } else + { + tmpStr = selectorName; + } + if (tmpStr.isEmpty() || tag.lower() == tmpStr || tmpStr == "*") + { + tmpStr = selectorName.mid(index + 1).replace('.',' '); + if (!tmpStr.isEmpty() && !selectorList.contains(tmpStr)) + selectorList << tmpStr; + } + } + } + return selectorList; +} + +QStringList DCOPQuanta::idSelectors() +{ + QStringList selectorList; + GroupElementMapList::Iterator it; + for ( it = globalGroupMap.begin(); it != globalGroupMap.end(); ++it ) + { + QString key = it.key(); + if (key.startsWith("Selectors|")) + { + QString selectorName = key.mid(10); + QString tmpStr; + if (selectorName.startsWith("#")) + { + selectorList << selectorName.mid(1); + } + } + } + return selectorList; +} + +QStringList DCOPQuanta::groupElements(const QString& group) +{ + QStringList elementList; + GroupElementMapList::Iterator it; + for ( it = globalGroupMap.begin(); it != globalGroupMap.end(); ++it ) + { + QString key = it.key(); + if (key.startsWith(group + "|")) + { + QString name = key.mid(10); + int index = name.find(':'); + if (index != -1) + name = name.mid(0, index); + QString tmpStr; + index = name.find("|"); + if (index != -1) + { + tmpStr = name.left(index).lower(); + } else + { + tmpStr = name; + } + + elementList << tmpStr; + } + } + return elementList; +} diff --git a/quanta/src/dcopquanta.h b/quanta/src/dcopquanta.h new file mode 100644 index 00000000..5c92f06f --- /dev/null +++ b/quanta/src/dcopquanta.h @@ -0,0 +1,34 @@ +/*************************************************************************** + dcopquanta.h - description + ------------------- + begin : Thu Mar 4 2004 + copyright : (C) 2004 Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 DCOPQUANTA_H +#define DCOPQUANTA_H + +#include "dcopquantaif.h" + +class DCOPQuanta : public DCOPQuantaIf +{ + +public: + DCOPQuanta(); + ~DCOPQuanta() {}; + + virtual QStringList selectors(const QString& tag); + virtual QStringList idSelectors(); + virtual QStringList groupElements(const QString &group); +}; + +#endif diff --git a/quanta/src/dcopquantaif.h b/quanta/src/dcopquantaif.h new file mode 100644 index 00000000..f0ec1818 --- /dev/null +++ b/quanta/src/dcopquantaif.h @@ -0,0 +1,33 @@ +/*************************************************************************** + dcopquantaif.h - description + ------------------- + begin : Thu Mar 4 2004 + copyright : (C) 2004 Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 DCOPQUANTAIF_H +#define DCOPQUANTAIF_H +#include <dcopobject.h> +#include <qstringlist.h> + +class DCOPQuantaIf : virtual public DCOPObject +{ + K_DCOP + + k_dcop: + + virtual QStringList selectors(const QString &tag) = 0; + virtual QStringList idSelectors() = 0; + virtual QStringList groupElements(const QString &group) = 0; +}; + +#endif diff --git a/quanta/src/dcopsettings.cpp b/quanta/src/dcopsettings.cpp new file mode 100644 index 00000000..b47e6c39 --- /dev/null +++ b/quanta/src/dcopsettings.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** + dcopsettings.cpp - description + ------------------- + begin : Thu Jan 29 2004 + copyright : (C) 2004 Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 "dcopsettings.h" +#include "quanta.h" +#include "resource.h" +#include "quantacommon.h" +#include "project.h" +#include "dtds.h" + +DCOPSettings::DCOPSettings() : DCOPObject("SettingsIf") +{ +} + +QString DCOPSettings::encoding() +{ + QString encoding = quantaApp->defaultEncoding(); + encoding.replace("iso ", "iso-"); //it's said that "iso-8859-x" is the valid format + encoding.replace("utf", "utf-"); //it's said that "utf-x" is the valid format + encoding.replace("cp ", "windows-"); + return encoding; +} + +QString DCOPSettings::dtep() +{ + if (Project::ref()->hasProject()) + { + return DTDs::ref()->getDTDNickNameFromName(Project::ref()->defaultDTD()); + } + else + return DTDs::ref()->getDTDNickNameFromName(qConfig.defaultDocType); +} + +QString DCOPSettings::quotationChar() +{ + return qConfig.attrValueQuotation; +} + diff --git a/quanta/src/dcopsettings.h b/quanta/src/dcopsettings.h new file mode 100644 index 00000000..62dc52b0 --- /dev/null +++ b/quanta/src/dcopsettings.h @@ -0,0 +1,35 @@ +/*************************************************************************** + dcopsettings.h - description + ------------------- + begin : Thu Jan 29 2004 + copyright : (C) 2004 Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 DCOPSETTINGS_H +#define DCOPSETTINGS_H + + +#include "dcopsettingsif.h" + +class DCOPSettings : public DCOPSettingsIf +{ + +public: + DCOPSettings(); + ~DCOPSettings() {}; + + virtual QString encoding(); + virtual QString dtep(); + virtual QString quotationChar(); +}; + +#endif diff --git a/quanta/src/dcopsettingsif.h b/quanta/src/dcopsettingsif.h new file mode 100644 index 00000000..7a811bc2 --- /dev/null +++ b/quanta/src/dcopsettingsif.h @@ -0,0 +1,34 @@ +/*************************************************************************** + dcopsettingsif.h - description + ------------------- + begin : Thu Jan 29 2004 + copyright : (C) 2003 by Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 DCOPSETTINGSIF_H +#define DCOPSETTINGSIF_H + +#include <dcopobject.h> +#include <qstringlist.h> + +class DCOPSettingsIf : virtual public DCOPObject +{ + K_DCOP + + k_dcop: + + virtual QString encoding() = 0; + virtual QString dtep() = 0; + virtual QString quotationChar() = 0; +}; + +#endif + diff --git a/quanta/src/dcopwindowmanagerif.h b/quanta/src/dcopwindowmanagerif.h new file mode 100644 index 00000000..d0f8a908 --- /dev/null +++ b/quanta/src/dcopwindowmanagerif.h @@ -0,0 +1,46 @@ +/*************************************************************************** + dcopwindowmanagerif.h - description + ------------------- + begin : Wed Feb 5 2003 + copyright : (C) 2003 by Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 DCOPWINDOWMANAGERIF_H +#define DCOPWINDOWMANAGERIF_H + +#include <dcopobject.h> +#include <qstringlist.h> + +class DCOPWindowManagerIf : virtual public DCOPObject +{ + K_DCOP + + k_dcop: + + virtual int currentEditorIfNum() const = 0; + virtual QString currentURL() const = 0; + virtual QString projectURL() const = 0; + virtual QStringList openedURLs() const = 0; + virtual QStringList tagAreas(const QString& tag, bool includeCoordinates, bool skipFoundContent) const = 0; + virtual void newCursorPosition(const QString &file, int lineNumber, int columnNumber) = 0; + virtual void newDebuggerPosition(const QString &file, int lineNumber) = 0; + virtual void openFile(const QString &file, int lineNumber, int columnNumber) = 0; + virtual QString saveCurrentFile() = 0; + virtual void setDtep(const QString& dtepName, bool convert) = 0; + virtual QString documentFolderForURL(const QString &url) = 0; + virtual QString urlWithPreviewPrefix(const QString &url) = 0; + virtual void addFileToProject(const QString &url) = 0; + virtual void addFolderToProject(const QString &url) = 0; + virtual void uploadURL(const QString &url, const QString& profile, bool markOnly) = 0; +}; + +#endif + diff --git a/quanta/src/document.cpp b/quanta/src/document.cpp new file mode 100644 index 00000000..0c3c30ab --- /dev/null +++ b/quanta/src/document.cpp @@ -0,0 +1,3192 @@ +/*************************************************************************** + document.cpp - description + ------------------- + begin : Tue Jun 6 2000 + copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev & Eric Laffoon <[email protected],[email protected],[email protected]> + (C) 2001-2004 Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 <list> +#include <cctype> +#include <cstdlib> +#include <stdlib.h> + +//QT includes +#include <qcheckbox.h> +#include <qdir.h> +#include <qeventloop.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qlineedit.h> +#include <qtextcodec.h> +#include <qtextstream.h> +#include <qregexp.h> +#include <qradiobutton.h> + +// KDE includes +#include <kapplication.h> +#include <kwin.h> +#include <klocale.h> +#include <kaction.h> +#include <kactionclasses.h> +#include <kdialogbase.h> +#include <kiconloader.h> +#include <kmdcodec.h> +#include <kmessagebox.h> +#include <ktempfile.h> +#include <kdirwatch.h> +#include <kdebug.h> +#include <kprogress.h> +#include <kio/netaccess.h> +#include <kstandarddirs.h> + +#include <ktexteditor/document.h> +#include <ktexteditor/view.h> +#include <ktexteditor/cursorinterface.h> +#include <ktexteditor/clipboardinterface.h> +#include <ktexteditor/codecompletioninterface.h> +#include <ktexteditor/configinterface.h> +#include <ktexteditor/editinterface.h> +#include <ktexteditor/editinterfaceext.h> +#include <ktexteditor/encodinginterface.h> +#include <ktexteditor/selectioninterface.h> +#include <ktexteditor/selectioninterfaceext.h> +#include <ktexteditor/viewcursorinterface.h> +#include <ktexteditor/wordwrapinterface.h> +#include <ktexteditor/markinterfaceextension.h> + +#include <kate/view.h> + +#include "tag.h" +#include "quantacommon.h" +#include "document.h" +#include "resource.h" +#include "dirtydlg.h" +#include "dirtydialog.h" +#include "casewidget.h" +#include "project.h" +#include "dtdselectdialog.h" + +#include "quanta.h" +#include "quantaview.h" +#include "structtreeview.h" +#include "qextfileinfo.h" +#include "viewmanager.h" +#include "messageoutput.h" + +#include "undoredo.h" + +#include "dtds.h" + +#include "sagroupparser.h" + +#define STEP 1 + +extern GroupElementMapList globalGroupMap; + +Document::Document(KTextEditor::Document *doc, + QWidget *parent, const char *name, WFlags f ) + : QWidget(parent, name, f) +{ + m_dirty = false; + busy = false; + changed = false; + m_md5sum = ""; + m_doc = doc; + m_view = 0L; //needed, because createView() calls processEvents() and the "this" may be deleted before m_view gets a value => crash on delete m_view; + m_view = m_doc->createView(parent, 0L); + completionInProgress = false; + argHintVisible = false; + completionRequested = false; + userTagList.setAutoDelete(true); + + // remove the unwanted actions + KAction *a = m_view->actionCollection()->action( "file_export" ); + if (a) + m_view->actionCollection()->take(a); + a = m_view->actionCollection()->action( "file_save" ); + if (a) + m_view->actionCollection()->take(a); + a = m_view->actionCollection()->action( "file_save_as" ); + if (a) + m_view->actionCollection()->take(a); + a = m_view->actionCollection()->action( "file_reload" ); + if (a) + m_view->actionCollection()->take(a); + a = m_view->actionCollection()->action( "edit_undo" ); + if (a) + m_view->actionCollection()->take(a); + a = m_view->actionCollection()->action( "edit_redo" ); + if (a) + m_view->actionCollection()->take(a); + //because they are not implemented in VPL + + a = m_view->actionCollection()->action( "edit_copy" ); + if (a) + m_view->actionCollection()->take(a); + a = m_view->actionCollection()->action( "edit_cut" ); + if (a) + m_view->actionCollection()->take(a); + a = m_view->actionCollection()->action( "edit_paste" ); + if (a) + m_view->actionCollection()->take(a); + + //conflicting shortcuts, so change them + a = m_view->actionCollection()->action("view_border"); + if (a) + a->setShortcut(Qt::SHIFT + Qt::Key_F9); + + a = m_view->actionCollection()->action("view_folding_markers"); + if (a) + a->setShortcut(Qt::SHIFT + Qt::Key_F11); + + KActionMenu *bookmarkAction = dynamic_cast<KActionMenu*>(m_view->actionCollection()->action( "bookmarks" )); + if (bookmarkAction) + { + m_view->actionCollection()->take(bookmarkAction); + //kdDebug(24000) << "Bookmarks found!" << endl; + //bookmarkAction->insert(quantaApp->actionCollection()->action( "file_quit" )); + } + + editIf = dynamic_cast<KTextEditor::EditInterface *>(m_doc); + editIfExt = dynamic_cast<KTextEditor::EditInterfaceExt *>(m_doc); + encodingIf = dynamic_cast<KTextEditor::EncodingInterface*>(m_doc); + m_encoding = quantaApp->defaultEncoding(); + if (encodingIf) + m_encoding = encodingIf->encoding(); + if (m_encoding.isEmpty()) + m_encoding = "utf8"; //final fallback + m_codec = QTextCodec::codecForName(m_encoding); + + selectionIf = dynamic_cast<KTextEditor::SelectionInterface *>(m_doc); + selectionIfExt = dynamic_cast<KTextEditor::SelectionInterfaceExt *>(m_doc); + configIf = dynamic_cast<KTextEditor::ConfigInterface*>(m_doc); + if (configIf) + configIf->readConfig(); + viewCursorIf = dynamic_cast<KTextEditor::ViewCursorInterface *>(m_view); + codeCompletionIf = dynamic_cast<KTextEditor::CodeCompletionInterface *>(m_view); + markIf = dynamic_cast<KTextEditor::MarkInterface *>(m_doc); + KTextEditor::MarkInterfaceExtension* iface = dynamic_cast<KTextEditor::MarkInterfaceExtension*>(m_doc); + if (iface) + { + iface->setPixmap(KTextEditor::MarkInterface::markType07, SmallIcon("stop")); + iface->setPixmap(KTextEditor::MarkInterface::markType02, SmallIcon("debug_breakpoint")); + iface->setDescription(KTextEditor::MarkInterface::markType02, i18n("Breakpoint")); + iface->setPixmap(KTextEditor::MarkInterface::markType05, SmallIcon("debug_currentline")); + iface->setDescription(KTextEditor::MarkInterface::markType08, i18n("Annotation")); + iface->setPixmap(KTextEditor::MarkInterface::markType08, SmallIcon("stamp")); + + // This is allows user to set breakpoints and bookmarks by clicking or rightclicking on the icon border. + iface->setMarksUserChangable(KTextEditor::MarkInterface::markType01 + KTextEditor::MarkInterface::markType02); + + } + + tempFile = 0; + m_tempFileName = QString::null; + dtdName = Project::ref()->defaultDTD(); + reparseEnabled = true; + repaintEnabled = true; + delayedTextChangedEnabled = true; + docUndoRedo = new undoRedo(this); + + //path of the backup copy file of the document + m_backupPathValue = QString::null; + + connect( m_doc, SIGNAL(charactersInteractivelyInserted (int ,int ,const QString&)), + this, SLOT(slotCharactersInserted(int ,int ,const QString&)) ); + + connect( m_view, SIGNAL(completionAborted()), + this, SLOT( slotCompletionAborted()) ); + + connect( m_view, SIGNAL(completionDone(KTextEditor::CompletionEntry)), + this, SLOT( slotCompletionDone(KTextEditor::CompletionEntry)) ); + + connect( m_view, SIGNAL(filterInsertString(KTextEditor::CompletionEntry*,QString *)), + this, SLOT( slotFilterCompletion(KTextEditor::CompletionEntry*,QString *)) ); + connect( m_doc, SIGNAL(textChanged()), SLOT(slotTextChanged())); + + connect(m_view, SIGNAL(gotFocus(Kate::View*)), SIGNAL(editorGotFocus())); + + connect(fileWatcher, SIGNAL(dirty(const QString&)), SLOT(slotFileDirty(const QString&))); + +// connect(m_doc, SIGNAL(marksChanged()), this, SLOT(slotMarksChanged())); + connect(m_doc, SIGNAL(markChanged(KTextEditor::Mark, KTextEditor::MarkInterfaceExtension::MarkChangeAction)), this, SLOT(slotMarkChanged(KTextEditor::Mark, KTextEditor::MarkInterfaceExtension::MarkChangeAction))); + +} + +Document::~Document() +{ + if (configIf) + configIf->writeConfig(); + parser->clearGroups(); + // kdDebug(24000) << "Document::~ Document: " << this << endl; + m_doc->closeURL(false); //TODO: Workaround for a Kate bug. Remove when KDE < 3.2.0 support is dropped. + delete m_doc; +} + +void Document::setUntitledUrl(const QString &url) +{ + untitledUrl = url; + openURL(KURL()); +} + +bool Document::isUntitled() +{ + return (m_doc->url().url().isEmpty()) ? true : false; +} + +KURL Document::url() +{ + return ( isUntitled() ) ? KURL("file:"+untitledUrl) : m_doc->url(); +} + +// kwrite addons + +void Document::insertTag(const QString &s1, const QString &s2) +{ + QString selection; + + if (selectionIf && selectionIf->hasSelection()) + { + reparseEnabled = false; + selection = selectionIf->selection(); + selectionIf->removeSelectedText(); + reparseEnabled = true; + } + insertText(s1 + selection); + insertText(s2, false); // don't adjust cursor, thereby leaving it in the middle of tag +} + + +/** Change the current tag's attributes with those from dict */ +void Document::changeTag(Tag *tag, QDict<QString> *dict ) +{ + tag->modifyAttributes(dict); + QString tagStr = tag->toString(); + + reparseEnabled = false; + int bLine, bCol, eLine, eCol; + tag->beginPos(bLine,bCol); + tag->endPos(eLine,eCol); + editIf->removeText(bLine, bCol, eLine, eCol+1); + viewCursorIf->setCursorPositionReal((uint)bLine, (uint)bCol); + insertText(tagStr); +} + +/**Change the namespace in a tag. Add if it's not present, or remove if the +namespace argument is empty*/ +void Document::changeTagNamespace(Tag *tag, const QString& nameSpace) +{ + int bl, bc; + int nl, nc; + if (!tag->nameSpace.isEmpty()) + { + tag->beginPos(bl, bc); + if (tag->type == Tag::XmlTagEnd) + bc++; + tag->namePos(nl, nc); + reparseEnabled = false; + editIf->removeText(bl, bc + 1, nl, nc); + reparseEnabled = true; + } else + { + tag->beginPos(bl, bc); + if (tag->type == Tag::XmlTagEnd) + bc++; + } + if (!nameSpace.isEmpty()) + { + viewCursorIf->setCursorPositionReal((uint)bl, (uint)(bc + 1)); + insertText(nameSpace + ":", true, false); + } + slotDelayedTextChanged(true); + quantaApp->slotNewLineColumn(); +} + +/**Change the attr value of the called attrName to attrValue*/ +void Document::changeTagAttribute(Tag *tag, const QString& attrName, const QString& attrValue) +{ + QString value; + int line, col; + int index = tag->attributeIndex(attrName); + if (index != -1) + { + int endCol; + value = tag->attributeValue(index); + if (value == attrValue) + return; + int aLine, aCol; + tag->attributeNamePos(index, aLine, aCol); + tag->attributeValuePos(index, line, col); + if (line == aLine && col == aCol) + { + col += tag->attribute(index).length(); + value = QString("=") + qConfig.attrValueQuotation + attrValue + qConfig.attrValueQuotation; + } else + { + endCol = col + value.length(); + if (attrValue.isEmpty()) + { + tag->attributeNamePos(index, line, col); + endCol++; + } + reparseEnabled = false; + QString textLine = editIf->textLine(line); + while (col > 1 && textLine[col-1].isSpace()) + col--; + + editIf->removeText(line, col, line, endCol); + reparseEnabled = true; + value = attrValue; + } + } else + { + index = tag->attrCount() - 1; + if (tag->attribute(index) == "/") + { + tag->attributeNamePos(index, line, col); + col--; + } else + { + tag->endPos(line, col); + } + if (attrValue.isEmpty()) + { + value = ""; + } else + { + value = " " + QuantaCommon::attrCase(attrName) + "=" + qConfig.attrValueQuotation + attrValue + qConfig.attrValueQuotation; + } + } + if (!value.isEmpty()) + { + viewCursorIf->setCursorPositionReal((uint)line, (uint)col); + insertText(value); + } + quantaApp->slotNewLineColumn(); + + //else +// slotDelayedTextChanged(); +} + + +void Document::selectText(int x1, int y1, int x2, int y2 ) +{ + if (selectionIf) + selectionIf->setSelection(x1, y1, x2, y2); +} + + +void Document::replaceSelected(const QString &s) +{ + if (selectionIf) + { + unsigned int line, col; + + viewCursorIf->cursorPositionReal(&line, &col); + reparseEnabled = false; + selectionIf->removeSelectedText(); + reparseEnabled = true; + editIf->insertText(line, col, s); + } +} + +void Document::insertFile(const KURL& url) +{ + QString fileName; + if (url.isLocalFile()) + { + fileName = url.path(); + } else + { + if (!KIO::NetAccess::download(url, fileName, this)) + { + KMessageBox::error(this, i18n("<qt>Cannot download <b>%1</b>.</qt>").arg( url.prettyURL(0, KURL::StripFileProtocol))); + return; + } + } + QFile file(fileName); + if (file.open(IO_ReadOnly)) + { + QTextStream stream( &file ); + stream.setEncoding(QTextStream::UnicodeUTF8); + insertText(stream.read()); + file.close(); + } else + KMessageBox::error(this, i18n("<qt>Cannot open <b>%1</b> for reading.</qt>").arg(url.prettyURL(0, KURL::StripFileProtocol))); +} + +/** Inserts text at the current cursor position */ +void Document::insertText(const QString &a_text, bool adjustCursor, bool reparse) +{ + QString text = a_text; + if(text.isEmpty()) + return; + + reparseEnabled = false; + unsigned int line, col; + + viewCursorIf->cursorPositionReal(&line, &col); + Node *n = parser->nodeAt(line, col, true); + if (n && n->tag->dtd()->family != Xml) + { + int bLine, bCol; + n->tag->beginPos(bLine, bCol); + QString s = this->text(bLine, bCol, line, col); + bool insideQuotes = false; + for (int i = 0 ; i < (int)s.length() - 1; i++) + { + if (s[i] == '"' && (i == 0 || s[i-1] != '\\')) + insideQuotes = !insideQuotes; + } + int eLine, eCol; + n->tag->endPos(eLine, eCol); + s = this->text(line + 1, col, eLine, eCol); + bool closeQuotationFound = false; + for (int i = 0 ; i < (int)s.length() - 1; i++) + { + if (s[i] == '"' && (i == 0 || s[i-1] != '\\')) + { + closeQuotationFound = true; + break; + } + } + if (insideQuotes && closeQuotationFound) + { + text.replace("\\\"", "\""); + text.replace("\"", "\\\""); + } + } + + editIf->insertText(line, col, text); + + // calculate new cursor position + // counts the words and whitespace of the text so we can place the + // cursor correctly and quickly with the viewCursorInterace, avoiding + // the KTexEditor::insertText method + if(adjustCursor) + { + unsigned textLength = text.length(); + unsigned int wordWrapAt = 80; + bool noWordWrap = false; + KTextEditor::WordWrapInterface *wordWrapIf = dynamic_cast<KTextEditor::WordWrapInterface *>(m_doc); + if (wordWrapIf) + { + wordWrapAt = wordWrapIf->wordWrapAt(); + noWordWrap = !(wordWrapIf->wordWrap()); + } + uint i=0, j=0; + int wordLength; + const char *ascii = text.latin1(); // use ascii for maximum speed + bool lineLock =false; + + while(i < textLength) + { + if(ascii[i] == '\n') // add a line, first column + { + ++line; col=0; ++i; lineLock = false; + } + else if(ascii[i] == '\r') + { + col = 0; ++i; + } + else if(!noWordWrap && !(isspace(ascii[i]))) // new word, see if it wraps + { + // TOO SLOW int wordLength = (text.mid(i).section(QRegExp("[ \t\r\n]"), 0, 0).length()); + wordLength = -1; + for(j = i+1;ascii[j];++j) // get word size, ascii is MUCH faster + { + if(isspace(ascii[j])) + { + wordLength = j-i; + break; + } + } + if(wordLength == -1) + wordLength = (textLength)-i; + + if((wordLength+col) > wordWrapAt) + { + if(col && !lineLock) // wraps onto new line unless locked by kate + { + col=0; + ++line; + } + } + col += wordLength; + i += wordLength; + if(wordLength > (int) wordWrapAt) + lineLock = true; // words > wordWrapAt lock the rest of the line + } + else // whitespace + { + ++col; ++i; + if(!noWordWrap) + if(col > wordWrapAt && !lineLock) // wrap like words + { + col -= wordWrapAt; + ++line; + } + } + } + } + viewCursorIf->setCursorPositionReal(line, col); + reparseEnabled = true; + if (reparse) + { + baseNode = parser->rebuild(this); + if (qConfig.instantUpdate && quantaApp->structTreeVisible()) + { + typingInProgress = false; + StructTreeView::ref()->slotReparse(this, baseNode , qConfig.expandLevel); + } + quantaApp->updateTreeViews(); + } +} + +bool Document::insertChildTags(QTag *tag, QTag *lastTag) +{ + bool childInserted = false; + if (!tag || tag == lastTag) //avoid infinite recursion + { + return false; + } + QMap<QString, bool>::Iterator it; + for (it = tag->childTags.begin(); it != tag->childTags.end(); ++it) + { + if (it.data()) + { + childInserted = true; + QTag *childTag = QuantaCommon::tagFromDTD(tag->parentDTD, it.key()); + QString tagStr =QuantaCommon::tagCase(it.key()); + if ( tag->parentDTD->singleTagStyle == "xml" && + ( childTag->isSingle() || + (childTag->isOptional() && !qConfig.closeOptionalTags)) ) + { + insertText("<" +tagStr + "/>", true, false); + } else + { + insertText("<" +tagStr + ">", true, false); + } + QString closingStr; + if (insertChildTags(childTag, tag)) + { + closingStr = ""; + } + if ( (!childTag->isSingle() && !childTag->isOptional() && qConfig.closeTags) || + (childTag->isOptional() && qConfig.closeOptionalTags) ) + { + insertText(closingStr + "</" + tagStr + ">", true, false); + } + } + } + return childInserted; +} + +/** Get the view of the document */ +KTextEditor::View* Document::view() +{ + return m_view; +} + +/** Get the KTextEditor::Document of the document */ +KTextEditor::Document* Document::doc() +{ + return m_doc; +} + +/** Returns true if the document was modified. */ +bool Document::isModified() +{ + bool modified = false; + if (m_doc) + modified = m_doc->isModified(); + + return modified; +} + +void Document::setModified(bool flag) +{ + if (m_doc) + m_doc->setModified(flag); +} + +void Document::createTempFile() +{ + closeTempFile(); + tempFile = new KTempFile(tmpDir); + tempFile->setAutoDelete(true); + m_tempFileName = QFileInfo(*(tempFile->file())).filePath(); + QString encoding = quantaApp->defaultEncoding(); + if (encodingIf) + encoding = encodingIf->encoding(); + if (encoding.isEmpty()) + encoding = "utf8"; //final fallback + tempFile->textStream()->setCodec(QTextCodec::codecForName(encoding)); + * (tempFile->textStream()) << editIf->text(); + + m_tempFileName = QFileInfo(*(tempFile->file())).filePath(); + tempFile->close(); +// kdDebug(24000) << "Creating tempfile " << m_tempFileName << " for " << url() << endl; +} + +void Document::closeTempFile() +{ + if (tempFile != 0) + { + delete tempFile; + tempFile = 0L; + } + if (QFileInfo(m_tempFileName).exists()) + QFile::remove(m_tempFileName); + + m_tempFileName = QString::null; +} + +QString Document::tempFileName() +{ + return m_tempFileName; +} + + +/** This will return the current tag name at the given position. + It will work even if the tag has not been completed yet. An + empty string will be returned if no tag is found. +*/ +QString Document::getTagNameAt(int line, int col ) +{ + QString name = ""; + QString textLine = editIf->textLine(line); + textLine = textLine.left(col); + while (line >= 0) + { + QuantaCommon::removeCommentsAndQuotes(textLine, completionDTD); + int pos = textLine.findRev("<"); + int pos2 = textLine.findRev(">"); + if (pos != -1 && pos2 < pos) + { + textLine.remove(0, pos + 1); + pos = 0; + while (pos < (int)textLine.length() && + !textLine[pos].isSpace() && + textLine[pos] != '>') + pos++; + name = textLine.left(pos).stripWhiteSpace(); + pos = name.find(":"); + if (pos != -1) + name = name.mid(pos + 1); + break; + } else + { + if (pos2 == -1) + { + line--; + if (line >= 0) + textLine = editIf->textLine(line); + } else + { + name = ""; + break; + } + } + } + + return name; +} + +/** Show the code completions passed in as an argument */ +void Document::showCodeCompletions( QValueList<KTextEditor::CompletionEntry> *completions ) { + bool reparse = reparseEnabled; + reparseEnabled = false; + codeCompletionIf->showCompletionBox( *completions, false ); + reparseEnabled = reparse; + argHintVisible = false; + delete completions; +} + +/** Once the completed text has been inserted into the document we + want to update the cursor position. +*/ +void Document::slotCompletionDone( KTextEditor::CompletionEntry completion ) +{ + unsigned int line,col; + completionInProgress = false; + argHintVisible = false; + viewCursorIf->cursorPositionReal(&line,&col); + const DTDStruct* dtd = currentDTD(); +/* if (completion.type == "charCompletion") + { + m_lastCompletionList = getCharacterCompletions(completion.userdata); + QTimer::singleShot(0, this, SLOT(slotDelayedShowCodeCompletion())); + } else*/ + if (completion.type == "attribute") + { + viewCursorIf->setCursorPositionReal(line,col-1); + if (dtd) + { + QTag *tag = QuantaCommon::tagFromDTD(dtd,completion.userdata); + if (tag) + { + m_lastCompletionList = getAttributeValueCompletions(tag->name(), completion.text); + QTimer::singleShot(0, this, SLOT(slotDelayedShowCodeCompletion())); + } + } + } else + if (completion.type == "attributeValue") + { + viewCursorIf->setCursorPositionReal(line, col); + } else + if (completion.type == "doctypeList") + { + viewCursorIf->setCursorPositionReal(line,col+1); + } else + if (completion.type == "script") + { + viewCursorIf->setCursorPositionReal(line,col); + if (dtd) + { + m_lastLine = line; + m_lastCol = col - 1; + QTimer::singleShot(0, this, SLOT(slotDelayedScriptAutoCompletion())); + } + } +} + +void Document::slotDelayedScriptAutoCompletion() +{ + scriptAutoCompletion(m_lastLine, m_lastCol, ""); +} + +void Document::slotDelayedShowCodeCompletion() +{ + showCodeCompletions(m_lastCompletionList); +} + +/** This is called when the user selects a completion. We + can filter this completion to allow more intelligent + code compeltions +*/ +void Document::slotFilterCompletion( KTextEditor::CompletionEntry *completion ,QString *string ) +{ + kdDebug(24000) << *string << endl; + kdDebug(24000) << completion->userdata << endl; + int pos = completion->userdata.find("|"); + QString s = completion->userdata.left(pos); + completion->userdata.remove(0,pos+1); + string->remove(0, s.length()); + kdDebug(24000) << *string << endl; + kdDebug(24000) << completion->userdata << endl; + if (completion->type == "charCompletion") + { + *string = completion->userdata; + uint line, col; + viewCursorIf->cursorPositionReal(&line, &col); + QString s2 = editIf->textLine(line).left(col); + kdDebug(24000) << s2 << endl; + int pos = s2.findRev('&'); + if (pos != -1) + { + s2 = s2.mid(pos + 1); + string->remove(s2); + } + string->append(";"); + kdDebug(24000) << *string << endl; + } else + if ( completion->type == "attributeValue") + { + uint line, col; + viewCursorIf->cursorPositionReal(&line, &col); + QString textLine = editIf->textLine(line); + QChar tagSeparator = completionDTD->tagSeparator; + if (tagSeparator == '\'' || tagSeparator =='"') + tagSeparator = qConfig.attrValueQuotation; + if (textLine[col] != tagSeparator) + string->append(tagSeparator); + } else + if ( completion->type == "attribute" ) + { + string->append("="+QString(qConfig.attrValueQuotation)+QString(qConfig.attrValueQuotation)); + } else + if (completion->type == "doctypeList") + { + s = *string; + string->remove(0, string->length()); + QString s2 = QString("public \""+DTDs::ref()->getDTDNameFromNickName(s)+"\""); + const DTDStruct *dtd = DTDs::ref()->find(DTDs::ref()->getDTDNameFromNickName(s)); + if (dtd && !dtd->url.isEmpty()) + { + s2 += " \""+dtd->url+"\""; + } + string->append(QuantaCommon::attrCase(s2)); + } else + if (completion->type == "script") + { + string->append(completionDTD->attrAutoCompleteAfter); + } +} + +void Document::slotReplaceChar() +{ + reparseEnabled = false; + editIf->removeText(m_replaceLine, m_replaceCol, m_replaceLine, m_replaceCol+1); + insertText(m_replaceStr, true, false); +} +/** Called when a user types in a character. From this we can show possibile + completions based on what they are trying to input. +*/ +void Document::slotCharactersInserted(int line, int column, const QString& string) +{ + if (qConfig.replaceNotInEncoding) + { + if (encodingIf) + { + QString encoding = encodingIf->encoding(); + if (encoding != m_encoding) + { + m_encoding = encoding; + m_codec = QTextCodec::codecForName(encoding); + } + if (!m_codec->canEncode(string[0])) + { + m_replaceLine = line; + m_replaceCol = column; + m_replaceStr = QuantaCommon::encodedChar(string[0].unicode()); + QTimer::singleShot(0, this, SLOT(slotReplaceChar())); + return; + } + } + } + if (qConfig.replaceAccented) + { + uint c = string[0].unicode(); + if (c > 191) + { + m_replaceLine = line; + m_replaceCol = column; + m_replaceStr = QuantaCommon::encodedChar(c); + QTimer::singleShot(0, this, SLOT(slotReplaceChar())); + return; + } + } + + + if ( (string == ">") || + (string == "<") ) + { + slotDelayedTextChanged(true); + } + bool handled = false; + if (qConfig.useAutoCompletion) + { + if (completionInProgress) + { + handleCodeCompletion(); + } else + { + completionDTD = currentDTD(); + if (completionDTD->family == Xml) + { + handled = xmlAutoCompletion(line, column, string); + } + if (completionDTD->family == Script) + { + handled = scriptAutoCompletion(line, column, string); + if (!handled && string == ">") + { + Node *node = parser->nodeAt(line, column, false); + if (node && node->tag->validXMLTag && node->tag->type == Tag::ScriptTag) + { + column++; + editIf->insertText(line, column, "</" + node->tag->name + ">"); + viewCursorIf->setCursorPositionReal( line, column ); + } + } + handled = true; + } + + if (!handled) + { + const DTDStruct *lastDTD = completionDTD; + completionDTD = defaultDTD(); + if (lastDTD != completionDTD && completionDTD->family == Xml) + { + handled = xmlAutoCompletion(line, column, string); + } +/*TODO: Can the default DTD be a script? + if (dtd->family == Script) + { + scriptAutoCompletion(dtd, line, column, string); + } +*/ + } + } + } +} + +/** Called whenever a user inputs text in an XML type document. + Returns true if the code completionw as handled. +*/ +bool Document::xmlAutoCompletion(int line, int column, const QString & string) +{ + QTag *tag; + QString tagName; + bool handled = false; + tagName = getTagNameAt(line, column); + tag = QuantaCommon::tagFromDTD(completionDTD, tagName); + if (!tag && !tagName.isEmpty()) + tag = userTagList.find(tagName.lower()); + + QString s = editIf->textLine(line).left(column + 1); + bool namespacecompletion = false; + if (!tagName.isEmpty() && string ==":" && s.endsWith("<" + tagName + ":")) + namespacecompletion = true; + int i = column; + while (i > 0 && s[i].isSpace()) + i--; + s = s.left(i + 1); + + if ( !tag || tagName.isEmpty() || namespacecompletion) //we are outside of any tag + { + + if (s.endsWith(completionDTD->tagAutoCompleteAfter) || + namespacecompletion) // a tag is started, either with < or <namespace: + { + //we need to complete a tag name + showCodeCompletions( getTagCompletions(line, column + 1) ); + handled = true; + } else + if (string == ">" && !tagName.isEmpty() && tagName[0] != '!' && tagName[0] != '?' && + tagName[0] != '/' && !tagName.endsWith("/") && !s.endsWith("/>") && + qConfig.closeTags && + currentDTD(true)->family == Xml) //close unknown tags + { + //add closing tag if wanted + column++; + editIf->insertText(line, column, "</" + tagName + ">"); + docUndoRedo->dontAddModifsSet(2); + viewCursorIf->setCursorPositionReal( line, column ); + handled = true; + } else + if (string == "/" && s.endsWith("</") && tagName.isEmpty()) + { + Node *node = parser->nodeAt(line, column, false); + if (node && node->parent ) + { + node = node->parent; + if (node->tag->type == Tag::XmlTag && (!node->next || !QuantaCommon::closesTag(node->tag, node->next->tag))) + { + QString name = node->tag->name; + name = name.left(name.find(" | ")); + if (!node->tag->nameSpace.isEmpty()) + name.prepend(node->tag->nameSpace + ":"); + editIf->insertText(line, column + 1, name + ">"); + docUndoRedo->dontAddModifsSet(2); + viewCursorIf->setCursorPositionReal( line, column + name.length() + 2); + handled = true; + } + } + } + } + else // we are inside of a tag + { + if ( string == ">" && tagName[0] != '/' && !tagName.endsWith("/") && + !s.endsWith("/>") && tag) + { + if ( tag->parentDTD->singleTagStyle == "xml" && + (tag->isSingle() || (!qConfig.closeOptionalTags && tag->isOptional())) + ) + { + editIf->insertText(line, column, " /"); + docUndoRedo->dontAddModifsSet(2); + viewCursorIf->setCursorPositionReal( line, column+3 ); + handled = true; + } + if ( ( !tag->isSingle() && !tag->isOptional() && qConfig.closeTags) || + ( tag->isOptional() && qConfig.closeOptionalTags ) ) + { + //add closing tag if wanted + Node *node = parser->nodeAt(line, column, false); + if (node && (!node->next || !QuantaCommon::closesTag(node->tag, node->next->tag))) + { + if (node && !node->tag->nameSpace.isEmpty()) + tagName.prepend(node->tag->nameSpace + ":"); + column++; + editIf->insertText(line, column, "</" + tagName + ">"); + docUndoRedo->dontAddModifsSet(2); + viewCursorIf->setCursorPositionReal( line, column ); + handled = true; + } + } + if (!tag->childTags.isEmpty()) + { + reparseEnabled = false; + // insertText("\n", false, false); + insertChildTags(tag); + reparseEnabled = true; + baseNode = parser->rebuild(this); + if (qConfig.instantUpdate && quantaApp->structTreeVisible()) + { + typingInProgress = false; + StructTreeView::ref()->slotReparse(this, baseNode , qConfig.expandLevel); + } + } + } + else if ( string == " " ) + { + QString textLine = editIf->textLine(line); + if (!QuantaCommon::insideCommentsOrQuotes(column, textLine, completionDTD)) + { + showCodeCompletions(getAttributeCompletions(tagName, "")); + handled = true; + } + } + else if ( string[0] == qConfig.attrValueQuotation ) + { + //we need to find the attribute name + QString textLine = editIf->textLine(line).left(column-1); + QString attribute = textLine.mid(textLine.findRev(' ')+1); + if (attribute == "style" && completionDTD->insideDTDs.contains("css")) + { + completionDTD = DTDs::ref()->find("text/css"); + completionRequested = true; + return scriptAutoCompletion(line, column + 1, string); + } + showCodeCompletions( getAttributeValueCompletions(tagName, attribute) ); + handled = true; + } + } // else - we are inside of a tag + if (!handled) + { + //check if we are inside a style attribute, and use css autocompletion if we are + QString textLine = editIf->textLine(line); + textLine = textLine.left(column); + int pos = textLine.findRev('"'); + if (pos != -1) + { + pos = textLine.findRev(' ', pos); + if (pos != -1) + { + textLine = textLine.mid(pos + 1); + pos = textLine.find('='); + if (pos != -1) + { + QString attribute = textLine.left(pos); + if (attribute == "style" && completionDTD->insideDTDs.contains("css")) + { + completionDTD = DTDs::ref()->find("text/css"); + completionRequested = true; + return scriptAutoCompletion(line, column + 1, string); + } + } + } + } + QString s = editIf->textLine(line).left(column + 1); + pos = s.findRev('&'); + if (pos != -1) + { + //complete character codes + s = s.mid(pos + 1); + showCodeCompletions(getCharacterCompletions(s)); + handled = true; + } + } + return handled; +} + +/** Return a list of possible variable name completions */ +QValueList<KTextEditor::CompletionEntry>* Document::getGroupCompletions(Node *node, const StructTreeGroup& group, int line, int col) +{ + QValueList<KTextEditor::CompletionEntry> *completions = new QValueList<KTextEditor::CompletionEntry>(); + KTextEditor::CompletionEntry completion; + + completion.type = "variable"; + + QString textLine = editIf->textLine(line).left(col); + QString word = findWordRev(textLine); + if (!group.removeFromAutoCompleteWordRx.pattern().isEmpty()) + word.remove(group.removeFromAutoCompleteWordRx); + completion.userdata = word + "|"; + GroupElementMapList::Iterator it; + QString str = group.name; + str.append("|"); + str.append(word); + for ( it = globalGroupMap.begin(); it != globalGroupMap.end(); ++it ) + { + if (it.key().startsWith(str) && it.key() != str ) + { + GroupElementList elementList = it.data(); + for (uint i = 0; i < elementList.count(); i++) + { + if (elementList[i]->parentNode == 0L || elementList[i]->global) + { + completion.text = it.key().section('|', -1).stripWhiteSpace(); + completions->append(completion); + break; + } else + { + Node *n = node; + while (n && n != elementList[i]->parentNode) + { + n = n->parent; + } + if (n == elementList[i]->parentNode) + { + completion.text = it.key().section('|', -1).stripWhiteSpace(); + completions->append(completion); + break; + } + } + } + } + } + IncludedGroupElementsMap elements = parser->includedMap; + IncludedGroupElementsMap::Iterator it2; + for ( it2 = elements.begin(); it2 != elements.end(); ++it2 ) + { + QStringList list = it2.data()[group.name].keys(); + list.sort(); + for (uint i = 0; i < list.count(); i++) + { + if (list[i].startsWith(word) && list[i] != word) + { + completion.text = list[i].stripWhiteSpace(); + completions->append(completion); + } + } + } + + + return completions; +} + +bool Document::isDerivatedFrom(const QString& className, const QString &baseClass) +{ + if (className.isEmpty() || !completionDTD->classInheritance.contains(className)) + return false; + + QString parentClass = completionDTD->classInheritance[className]; + int result = 0; + do { + if (parentClass == baseClass) + result = 1; //className extends baseClass + else + { + if (completionDTD->classInheritance.contains(parentClass)) + parentClass = completionDTD->classInheritance[parentClass]; + else + result = -1;//nothing was found in the inheritance list + } + } while (result == 0); + + return (result == 1); +} + + +/** Return a list of possible tag name completions */ +QValueList<KTextEditor::CompletionEntry>* Document::getTagCompletions(int line, int col) +{ + QValueList<KTextEditor::CompletionEntry> *completions = new QValueList<KTextEditor::CompletionEntry>(); + KTextEditor::CompletionEntry completion; + switch (completionDTD->family) + { + case Xml: completion.type = "tag"; + break; + case Script: + completion.type = "script"; + break; + } + Node *node = parser->nodeAt(line, col); + if (node && node->tag->type != Tag::XmlTag) + node = node->parent; + if (node && node->tag->type != Tag::XmlTag) + node = 0L; + QTag *parentQTag= 0L; + if (node && node->parent) + parentQTag = QuantaCommon::tagFromDTD(node->parent); + QString textLine = editIf->textLine(line).left(col); + QString word = findWordRev(textLine, completionDTD).upper(); + QString classStr = ""; + QString objStr; + if (completionDTD->classGroupIndex != -1 && completionDTD->objectGroupIndex != -1) + { + textLine = textLine.left(textLine.length() - word.length()); + int pos = completionDTD->memberAutoCompleteAfter.searchRev(textLine); + if (pos != -1) + { + textLine = textLine.left(pos); + QRegExp *r = &(completionDTD->structTreeGroups[completionDTD->classGroupIndex].usageRx); + pos = r->searchRev(textLine); + if (pos != -1) + { + objStr = r->cap(1); + if (objStr == "this") + { + QString parentGroupStr = ""; + bool classFound = false; + parser->synchParseInDetail(); + Node *n = parser->nodeAt(line, col); + while (n && !classFound) + { + //Need to parser for groups, as the node tree is rebuilt before + //autocompletion and none of the node has links to group elements + //at this position. + SAGroupParser *gParser = new SAGroupParser(0L, this, n, n->nextSibling(), true, false, false); + gParser->slotParseForScriptGroup(); + GroupElementList::Iterator it = n->m_groupElements.begin(); + while (it != n->m_groupElements.end()) + { + GroupElement *e = *it; + if (parentGroupStr.isEmpty() && e->group->appendToTags) + { + parentGroupStr = e->group->parentGroup; + } + if (!parentGroupStr.isEmpty() && e->group->name == parentGroupStr) + { + classStr = e->tag->name; + classFound = true; + } + //detach the groupelement from the node + e->node = 0L; + e->group = 0L; + e->deleted = true; + it = n->m_groupElements.erase(it); + } + delete gParser; + n = n->parent; + } + } else + { + GroupElementList groupElementList = globalGroupMap[completionDTD->structTreeGroups[completionDTD->objectGroupIndex].name + "|" + objStr]; + for (GroupElementList::Iterator it = groupElementList.begin(); it != groupElementList.end(); ++it) + { + if (!(*it)->tag) + continue; +#ifdef DEBUG_PARSER + kdDebug(24000) << "GroupElement: " << (*it) << " " << (*it)->tag->area().bLine << " " << (*it)->tag->area().bCol << " "<< (*it)->tag->area().eLine << " "<< (*it)->tag->area().eCol << " " << (*it)->tag->tagStr() << " " << (*it)->type << endl; +#endif + if (!(*it)->type.isEmpty()) + { + classStr = (*it)->type; + break; + } + } + } + } + } + if ((!objStr.isEmpty() || !completionRequested) && classStr.isEmpty()) //the class cannot be identified for the object or there is no object. + return completions; + } + completion.userdata = word + "|"; + QStringList tagNameList; + QMap<QString, QString> comments; + //A QMap to hold the completion type (function/string/class/etc) + QMap<QString, QString> type; + QString tagName; + QDictIterator<QTag> it(*(completionDTD->tagsList)); + int i = 0; + for( ; it.current(); ++it ) + { + QTag *tag = it.current(); + if ((tag->type != "entity") && (tag->className == classStr || + isDerivatedFrom(classStr, tag->className))) + { + tagName = tag->name(); + if (!tagName.isEmpty() && tagName.upper().startsWith(word)) + { + if (!parentQTag || (parentQTag && parentQTag->isChild(tagName))) + { + tagName = tag->name() + QString("%1").arg(i, 10); + tagNameList += tagName; + comments.insert(tagName, tag->comment); + i++; + } + } + } + } + + QDictIterator<QTag> it2(userTagList); + for( ; it2.current(); ++it2 ) + { + QTag *tag = it2.current(); + if ((tag->className == classStr || + isDerivatedFrom(classStr, tag->className)) && tag->name().upper().startsWith(word)) + { + tagName = tag->name() + QString("%1").arg(i, 10); + tagNameList += tagName; + comments.insert(tagName, tag->comment); + + // If the completion family is script, then we want to update the tag type + // it appears we use "script" for adding the completionDTD->attrAutoCompleteAfter when we run the slotFilterCompletion + // so we will continue to use that for functions (they need the attribute added), but variables get a new type - and we do not + // have to auto-complete them + if(completionDTD->family==Script) + { + if(tag->type=="variable") + type.insert(tagName, tag->type); + else if(tag->type=="function") + type.insert(tagName, "script"); + + // We add the type to the comment variable, so it displays on the screen, giving the user some feedback + if(comments[tagName].length()) + comments[tagName] = tag->type + "\n" + comments[tagName]; + else + comments[tagName] = tag->type + comments[tagName]; + } + i++; + } + } + + tagNameList.sort(); + // tagNameList is sorted above to sort the completions by name alphabetically + // Now we want to sort the completions by their types. + // We only want to do this if we are completing Script DTDs + // We are going to use a couple of iterators to sort the list by Type + // Type Sorting is as follows: 0:Other, 1:Variables, 2: Functions (script) + QValueList<KTextEditor::CompletionEntry>::Iterator otherIt=completions->begin(); + QValueList<KTextEditor::CompletionEntry>::Iterator variableIt=completions->begin(); + for (uint i = 0; i < tagNameList.count(); i++) + { + if (completionDTD->family == Xml) + completion.text = QuantaCommon::tagCase(tagNameList[i]); + else + completion.text = tagNameList[i]; + completion.text = completion.text.left(completion.text.length() - 10).stripWhiteSpace(); + completion.comment = comments[tagNameList[i]]; + + if(completionDTD->family==Script) + { + // Here we actually append the completion type + completion.type = type[tagNameList[i]]; + // And here is out sorting... + if(completion.type.contains("variable")) + { + // Insert after the last variable + variableIt++; + variableIt = completions->insert(variableIt, completion); + } + else + { + if(completion.type.contains("script")) + { + //Scripts can go at the end of the list + completions->append(completion); + } + else + { + // Other types go first, after the last other type + otherIt++; + otherIt = completions->insert(otherIt, completion); + // If we have no variables in the list, we need to point variableIt to otherIt, so they will go after the 'others' + if((*variableIt).text.length()==0) + variableIt=otherIt; + } + } + } + else + completions->append( completion ); + } + +// completionInProgress = true; + + return completions; +} + +/** Return a list of valid attributes for the given tag */ +QValueList<KTextEditor::CompletionEntry>* Document::getAttributeCompletions(const QString& tagName, const QString& a_startsWith ) +{ + QValueList<KTextEditor::CompletionEntry> *completions = new QValueList<KTextEditor::CompletionEntry>(); + KTextEditor::CompletionEntry completion; + QTag *tag = QuantaCommon::tagFromDTD(completionDTD, tagName); + if (!tag) + { + tag = userTagList.find(tagName.lower()); + } + QString startsWith = a_startsWith.upper(); + if (tag) + { + switch (completionDTD->family) + { + case Xml: + { + completion.type = "attribute"; + completion.userdata = startsWith+"|"+tag->name(); + + //list specified attributes for this tag + AttributeList *list = tag->attributes(); + QValueList<KTextEditor::CompletionEntry> tempCompletions; + QStringList nameList; + for (uint i = 0; i < list->count(); i++) + { + QString item = list->at(i)->name; + if (item.upper().startsWith(startsWith)) + { + completion.text = QuantaCommon::attrCase(item); + completion.comment = list->at(i)->type; + tempCompletions.append( completion ); + nameList.append(completion.text); + } + } + + //list common attributes for this tag + for (QStringList::Iterator it = tag->commonGroups.begin(); it != tag->commonGroups.end(); ++it) + { + AttributeList *attrs = tag->parentDTD->commonAttrs->find(*it); + for (uint j = 0; j < attrs->count(); j++) + { + QString name = attrs->at(j)->name; + if (name.upper().startsWith(startsWith)) + { + completion.text = QuantaCommon::attrCase(name); + completion.comment = attrs->at(j)->type; + tempCompletions.append( completion ); + nameList.append(completion.text); + } + } + } + + if (tag->name().contains("!doctype",false)) //special case, list all the known document types + { + QStringList nickNames = DTDs::ref()->nickNameList(true); + for ( QStringList::Iterator it = nickNames.begin(); it != nickNames.end(); ++it ) + { + completion.type = "doctypeList"; + completion.text = *it; + tempCompletions.append(completion); + nameList.append(completion.text); + } + } + //below isn't fast, but enough here. May be better with QMap<QString, KTextEditor::CompletionEntry> + nameList.sort(); + for ( QStringList::Iterator it = nameList.begin(); it != nameList.end(); ++it ) + { + for (QValueList<KTextEditor::CompletionEntry>::Iterator compIt = tempCompletions.begin(); compIt != tempCompletions.end(); ++compIt) + { + if ( (*compIt).text == *it) + { + completions->append(*compIt); + break; + } + } + } + break; + } + case Script: + { + completion.userdata = startsWith+"|"+tag->name(); + completion.type = "script"; + AttributeList *list = tag->attributes(); + for (uint i = 0; i < list->count(); i++) + { + QString item = list->at(i)->name; + completion.text = item; + completion.comment = list->at(i)->type; + completions->append( completion ); + } + } + } + } // if (tag) + +// completionInProgress = true; + return completions; +} + +/** Return a list of valid attribute values for the given tag and attribute */ +QValueList<KTextEditor::CompletionEntry>* Document::getAttributeValueCompletions(const QString& tagName, const QString& attribute, const QString& startsWith ) +{ + QValueList<KTextEditor::CompletionEntry> *completions = new QValueList<KTextEditor::CompletionEntry>(); + + KTextEditor::CompletionEntry completion; + completion.type = "attributeValue"; + completion.userdata = startsWith+"|"+tagName + "," + attribute; + + bool deleteValues; + QStringList *values = tagAttributeValues(completionDTD->name,tagName, attribute, deleteValues); + if (attribute.lower() == "class") + { + if (!values) + { + values = new QStringList(quantaApp->selectors(tagName)); + deleteValues = true; + } + } else + if (attribute.lower() == "id") + { + if (!values) + { + values = new QStringList(quantaApp->idSelectors()); + deleteValues = true; + } + } + if (values) + { + for ( QStringList::Iterator it = values->begin(); it != values->end(); ++it ) + { + completion.text = *it; + if (completion.text.startsWith(startsWith)) + { + completions->append( completion ); + } + } + } + if (deleteValues) + delete values; + int andSignPos = startsWith.find('&'); + if (andSignPos != -1) + { + QValueList<KTextEditor::CompletionEntry> *charCompletions = getCharacterCompletions(startsWith.mid(andSignPos + 1)); + *completions += *charCompletions; + delete charCompletions; + } + +// completionInProgress = true; + return completions; +} + +/** Return a list of character completions (like ...) */ +QValueList<KTextEditor::CompletionEntry>* Document::getCharacterCompletions(const QString& startsWith) +{ + QValueList<KTextEditor::CompletionEntry> *completions = 0L; + QMap<QString, KTextEditor::CompletionEntry> completionMap; + + //first search for entities defined in the document + const DTDStruct *dtdDTD = DTDs::ref()->find("dtd"); + if (dtdDTD) + { + StructTreeGroup group; + for (uint j = 0; j < dtdDTD->structTreeGroups.count(); j++) + { + group = dtdDTD->structTreeGroups[j]; + if (!group.autoCompleteAfterRx.pattern().isEmpty() && + group.autoCompleteAfterRx.search("&") != -1) + { + uint line, col; + viewCursorIf->cursorPositionReal(&line, &col); + Node *node = parser->nodeAt(line, col, false); + completions = getGroupCompletions(node, group, line, col); + for (uint i = 0; i < completions->count(); i++) + { + (*completions)[i].type = "charCompletion"; + (*completions)[i].userdata = (*completions)[i].text; + completionMap[(*completions)[i].text] = (*completions)[i]; + } + break; + } + } + } + + if (!completions) + completions = new QValueList<KTextEditor::CompletionEntry>(); + + KTextEditor::CompletionEntry completion; + completion.type = "charCompletion"; + //add the entities from the tag files + QDictIterator<QTag> it(*(completionDTD->tagsList)); + for( ; it.current(); ++it ) + { + QTag *tag = it.current(); + if (tag->type == "entity") + { + QString tagName = tag->name(true); + if (tagName.upper().startsWith(startsWith.upper()) || startsWith.isEmpty()) + { + completion.text = tagName; + completion.userdata = tagName; + completions->append( completion ); + completionMap[tagName] = completion; + } + } + } + + QValueList<KTextEditor::CompletionEntry> *completions2 = new QValueList<KTextEditor::CompletionEntry>(); + for (QMap<QString, KTextEditor::CompletionEntry>::ConstIterator it = completionMap.constBegin(); it != completionMap.constEnd(); ++it) + { + completions2->append(it.data()); + } + delete completions; + completions = completions2; + + for ( QStringList::Iterator it = charList.begin(); it != charList.end(); ++it ) + { + completion.text = *it; + int begin = completion.text.find("(&") + 2; + if (begin == 1) + continue; + int length = completion.text.find(";)") - begin + 1; + QString s = completion.text.mid(begin, length - 1); + completion.text = s + " : " + completion.text.left(begin -2) + " - " + completion.text.mid(begin + length + 1); + if (s.startsWith(startsWith)) + { + completion.userdata = s.mid(startsWith.length()); + completions->append( completion ); + } + } + + return completions; +} + +/** Returns the DTD identifier for the document */ +QString Document::getDTDIdentifier() +{ + return dtdName; +} + +/** Sets the DTD identifier */ +void Document::setDTDIdentifier(const QString &id) +{ + dtdName = id.lower(); + m_groupsForDTEPs.clear(); +} + +/** Get a pointer to the current active DTD. If fallback is true, this always gives back a valid and known DTD pointer: the active, the document specified and in last case the application default document type. */ +const DTDStruct* Document::currentDTD(bool fallback) +{ + uint line, col; + viewCursorIf->cursorPositionReal(&line, &col); + + const DTDStruct *dtd = parser->currentDTD(line, col); + + if (fallback && !dtd) return defaultDTD(); + + return dtd; +} + +/** Get a pointer to the default DTD (document, or app). */ +const DTDStruct* Document::defaultDTD() const +{ + const DTDStruct* dtd = DTDs::ref()->find(dtdName); + if (!dtd) dtd = DTDs::ref()->find(Project::ref()->defaultDTD()); + if (!dtd) dtd = DTDs::ref()->find(qConfig.defaultDocType); //this will always exists + + return dtd; +} + +/** Find the DTD name for a part of the document. */ +QString Document::findDTDName(Tag **tag) +{ + //Do some magic to find the document type + int endLine = editIf->numLines(); + QString foundText = ""; + int pos = 0; + int i = 0; + int line, startPos; + QString text; + do + { + text = editIf->textLine(i); + //search for !DOCTYPE tags + pos = text.find("!doctype",0,false); + if (pos != -1) //parse the found !DOCTYPE tag + { + int bl, bc, el, ec; + line = i; + bl = line; + startPos = text.findRev('<',pos); + while (startPos == -1 && line >=0) + { + text = editIf->textLine(line); + startPos = text.findRev('<'); + bl = line; + line--; + } + if (startPos == -1) + { + i++; + continue; + } + bc = startPos; + line = i; + text = editIf->textLine(i); + startPos = text.find('>',pos); + el = line; + while (startPos == -1 && line < endLine) + { + text = editIf->textLine(line); + startPos = text.find('>'); + el = line; + line++; + } + if (startPos == -1) + { + i++; + continue; + } + ec = startPos + 1; + *tag = new Tag(); + (*tag)->setTagPosition(bl, bc, el, ec); + text = this->text(bl, bc, el, ec); + (*tag)->parse(text, this); + (*tag)->type = Tag::XmlTag; + text.replace("\\\"", "\""); + pos = text.find("public",0,false); + if (pos == -1) //if no PUBLIC info, use the word after !DOCTYPE as the doc.type + { + foundText = (*tag)->attribute(0); + } else + { //use the quoted string after PUBLIC as doc. type + pos = text.find("\"", pos+1); + if (pos !=-1) + { + int endPos = text.find("\"",pos+1); + foundText = text.mid(pos+1, endPos-pos-1); + } + } + break; + } + i++; + } while (i < endLine); + + return foundText.lower(); +} + +/** Called whenever a user inputs text in a script type document. */ +bool Document::scriptAutoCompletion(int line, int column, const QString& insertedString) +{ + bool handled = false; + Node *node = parser->nodeAt(line, column); + if (!node) //happens in some cases in CSS + return false; + if (node->tag->type == Tag::Comment) + return true; //nothing to do + const DTDStruct *dtd = node->tag->dtd(); + if (node->prev) + node = node->prev; + else + if (node->parent) + node = node->parent; + + int bl, bc; + node->tag->beginPos(bl, bc); + QString s = text(bl, bc, line, column); + if (QuantaCommon::insideCommentsOrQuotes(s.length() -1, s, dtd)) + return true; //again, nothing to do + QString s2 = s; + int i = s.length() - 1; + while (i > 0 && s[i].isSpace()) + i--; + while (i > 0 && (s[i].isLetterOrNumber() || s[i] == '_' || + (completionDTD->minusAllowedInWord && s[i] == '-') ) ) + i--; + QString startStr = s.mid(i + 1).stripWhiteSpace(); + s = s.left(i + 1); + if (s[i] == completionDTD->attributeSeparator) + { + while (i > 0 && s[i] != completionDTD->attrAutoCompleteAfter) + i--; + s = s.left(i + 1); + } else + if (s[i] == completionDTD->tagSeparator) + { + while (i > 0 && s[i] != completionDTD->tagAutoCompleteAfter) + i--; + s = s.left(i + 1); + } + + if ( s[i] == completionDTD->attrAutoCompleteAfter || + s[i] == completionDTD->attributeSeparator ) //if we need to list the arguments of a function + { + QString textLine = s.left(i); + QString word = findWordRev(textLine, completionDTD); + QValueList<QTag *> tags; + if (!word.isEmpty()) + { + tags.append(userTagList.find(word.lower())); + QDictIterator<QTag> it(*(completionDTD->tagsList)); + for( ; it.current(); ++it ) + { + if (it.currentKey() == word) + tags.append(it.current()); + } + } + QStringList argList; + for (QValueList<QTag*>::ConstIterator it = tags.constBegin(); it != tags.constEnd(); ++it) + { + QTag *tag = *it; + if (!tag) + continue; + QString arguments; + if (tag->type != "property") + { + for (int i =0; i < tag->attributeCount(); i++) + { + Attribute* attr = tag->attributeAt(i); + if (attr->status == "optional") + { + arguments = arguments + "["+attr->type +" "+attr->name +"], "; + } else + { + arguments = arguments + attr->type +" "+attr->name +", "; + } + } + arguments = tag->returnType +" "+tag->name() + "("+arguments.left(arguments.length()-2)+")"; + argList.append(arguments); + codeCompletionIf->showArgHint(argList, "()" , completionDTD->attributeSeparator); + argHintVisible = true; + } else + { + if (hintRequested) + { + arguments = tag->name() + ": " + tag->attributeAt(0)->name + ";"; + argList.append(arguments); + codeCompletionIf->showArgHint(argList, ":;" , completionDTD->attributeSeparator); + } else + showCodeCompletions( getAttributeValueCompletions(tag->name(), tag->attributeAt(0)->name, startStr)); + } + + handled = true; + } + } else + { + StructTreeGroup group; + for (uint j = 0; j < completionDTD->structTreeGroups.count(); j++) + { + group = completionDTD->structTreeGroups[j]; + if (!group.autoCompleteAfterRx.pattern().isEmpty() && + ( group.autoCompleteAfterRx.search(s2) != -1|| + group.autoCompleteAfterRx.search(s) != -1) ) + { + Node *node = parser->nodeAt(line, column, false); + showCodeCompletions(getGroupCompletions(node, group, line, column + 1)); + handled = true; + break; + } + } + } + if ( !handled && !argHintVisible && + (completionRequested || + (s[i] == completionDTD->tagAutoCompleteAfter && (insertedString == " " || (insertedString[0] == completionDTD->tagAutoCompleteAfter && !completionDTD->requestSpaceBeforeTagAutoCompletion))) || + completionDTD->tagAutoCompleteAfter == '\1' || (!completionDTD->memberAutoCompleteAfter.pattern().isEmpty() && completionDTD->memberAutoCompleteAfter.searchRev(s) != -1)) + ) + { + showCodeCompletions(getTagCompletions(line, column + 1)); + handled = true; + } + return handled; +} + +/** Retrives the text from the specified rectangle. The KTextEditor::EditInterface::text seems to not +work correctly. */ +QString Document::text(int bLine, int bCol, int eLine, int eCol) const +{ + if (bLine > eLine) + { + int tmp = bLine; + bLine = eLine; + eLine = tmp; + tmp = bCol; + bCol = eCol; + eCol = tmp; + } + QString t = editIf->textLine(bLine); + if (bLine == eLine) + { + return t.mid(bCol, eCol-bCol +1); + } + t.remove(0, bCol); + t.append("\n"); +//TODO: This is slow if the area is big. We need to speed it up!! + for (int i = bLine+1; i < eLine ; i++) + { + t.append(editIf->textLine(i)+"\n"); + } + t = t+editIf->textLine(eLine).left(eCol+1); + return t; +} + +//TODO: profile which one is used more often and time critical places and use +//that one as the default and call from that one the other version +QString Document::text(const AreaStruct &area) const +{ + return text(area.bLine, area.bCol, area.eLine, area.eCol); +} + +QString Document::find(const QRegExp& regExp, int sLine, int sCol, int& fbLine, int&fbCol, int &feLine, int&feCol) +{ + + QRegExp rx = regExp; + QString foundText = ""; + int maxLine = editIf->numLines(); + QString textToSearch = text(sLine, sCol, sLine, editIf->lineLength(sLine)); + int pos; + int line = sLine; + do + { + pos = rx.search(textToSearch); + if (pos == -1) + { +/* if (line + STEP < maxLine) + { + line += STEP; + textToSearch.append("\n"+text(line - STEP + 1, 0, line, editIf->lineLength(line))); + } else*/ + { + line ++; + if (line < maxLine) textToSearch.append("\n"+editIf->textLine(line)); + } + } + } while (line < maxLine && pos == -1); +// pos = rx.search(text(sLine, sCol, maxLine -1, 100)); + if (pos != -1) + { + foundText = rx.cap(); + QString s = textToSearch.left(pos); + int linesUntilFound = s.contains("\n"); + fbLine = sLine + linesUntilFound; + fbCol = s.length()-s.findRev("\n")-1; + int linesInFound = foundText.contains("\n"); + feCol = foundText.length()-foundText.findRev("\n")-2; + feLine = fbLine + linesInFound; + if (linesUntilFound == 0) + { + fbCol = fbCol + sCol; + } + if (linesInFound == 0) + { + feCol = feCol + fbCol; + } + if (fbCol < 0) fbCol = 0; + if (feCol < 0) feCol = 0; +/* + s = text(fbLine, fbCol, feLine, feCol); + if (s != foundText) //debug, error + { + KMessageBox::error(this,"Found: "+foundText+"\nRead: "+s); + } +*/ + } + + return foundText; +} + +QString Document::findRev(const QRegExp& regExp, int sLine, int sCol, int& fbLine, int&fbCol, int &feLine, int&feCol) +{ + QRegExp rx = regExp; + QString foundText = ""; + int pos = -1; + int line = sLine; + QString textToSearch = text(sLine, 0, sLine, sCol); + do + { + pos = rx.searchRev(textToSearch); + if (pos == -1) + { +/* if (line - STEP >= 0) + { + textToSearch.prepend(text(line - STEP, 0, line - 1, editIf->lineLength(line-1)) + "\n"); + line -= STEP; + } else */ + { + line--; + if (line >=0) textToSearch.prepend(editIf->textLine(line) + "\n"); + } + } + } while (line >=0 && pos == -1); + if (pos != -1) + { + foundText = rx.cap(); + fbLine = line; + fbCol = pos; + int linesInFound = foundText.contains("\n"); + feCol = foundText.length()-foundText.findRev("\n")-2; + feLine = fbLine + linesInFound; + if (linesInFound == 0) + { + feCol = feCol + fbCol; + } + if (fbCol < 0) fbCol = 0; + if (feCol < 0) feCol = 0; +/* + QString s = text(fbLine, fbCol, feLine, feCol); + if (s != foundText) //debug, error + { + KMessageBox::error(this,"FindRev\nFound: "+foundText+"\nRead: "+s); + } +*/ + } + + return foundText; +} + +/** Code completion was requested by the user. */ +void Document::codeCompletionRequested() +{ + completionRequested = true; + completionInProgress = false; + argHintVisible = false; + hintRequested = false; + handleCodeCompletion(); + completionRequested = false; +} + +void Document::handleCodeCompletion() +{ + slotDelayedTextChanged(true); + bool handled = false; + uint line, col; + viewCursorIf->cursorPositionReal(&line, &col); + completionDTD = currentDTD(); + if (completionDTD->family == Xml) + { + handled = xmlCodeCompletion(line, col); + } + if (completionDTD->family == Script) + { + if (completionDTD->tagAutoCompleteAfter == '\0') + completionDTD->tagAutoCompleteAfter = '\1'; + handled = scriptAutoCompletion(line, col - 1, ""); + if (completionDTD->tagAutoCompleteAfter == '\1') + completionDTD->tagAutoCompleteAfter = '\0'; +/* if (!handled) + { + completionDTD = defaultDTD(); + QString s = text(line, 0, line, col).stripWhiteSpace(); + if (s.findRev("<") != -1) + { + //showCodeCompletions(getTagCompletions(line, col + 1)); + + handled = true; + } + }*/ + } + if (!handled) + { + completionDTD = defaultDTD(); + if (completionDTD->family == Xml) + { + // xmlCodeCompletion(line, col); + xmlAutoCompletion(line, col, " "); + } + } + + completionInProgress = true; +} + +/** Bring up the code completion tooltip. */ +void Document::codeCompletionHintRequested() +{ + completionRequested = true; + slotDelayedTextChanged(true); + uint line, col; + viewCursorIf->cursorPositionReal(&line, &col); + completionDTD = currentDTD(); + if (completionDTD->family == Script) + { +// QString textLine = editIf->textLine(line).left(col); +// int pos = textLine.findRev("("); +// int pos2 = textLine.findRev(")"); + //if (pos > pos2 ) + hintRequested = true; + scriptAutoCompletion(line, col - 1, ""); + } + completionRequested = false; +} + +QString Document::currentWord() +{ + uint line, col; + viewCursorIf->cursorPositionReal(&line, &col); + QString textLine = editIf->textLine(line); + int startPos = textLine.findRev(QRegExp("\\W"), col); + int endPos = textLine.find(QRegExp("\\W"), col); + if (startPos == -1) + startPos = 0; + else + startPos++; + if (endPos == -1) + endPos = textLine.length(); + return textLine.mid(startPos, endPos - startPos); +} + +/** Find the word until the first word boundary backwards */ +QString Document::findWordRev(const QString& textToSearch, const DTDStruct *dtd) +{ + QString t = textToSearch; + while (t.endsWith(" ")) + t = t.left(t.length()-1); + int startPos = -1; + int pos; + bool end = false; + do{ + pos = t.findRev(QRegExp("\\W"), startPos); + if (t[pos] == '_' || + (dtd && dtd->minusAllowedInWord && t[pos] == '-')) + { + startPos = pos - t.length()-1; + end = false; + } else + { + end = true; + } + } while (!end); + return t.remove(0,pos+1); +} + + +/** Invoke code completion dialog for XML like tags according to the position (line, col), using DTD dtd. */ +bool Document::xmlCodeCompletion(int line, int col) +{ + bool handled = false; + Node *node = parser->nodeAt(line, col); + if (node && node->tag && node->tag->type == Tag::XmlTag ) + { + Tag *tag = node->tag; + int bLine, bCol; + tag->beginPos(bLine, bCol); + QString s; + int index; + QString tagName = tag->name.section('|', 0, 0).stripWhiteSpace(); + int nameCol = bCol + tagName.length() + 1; + if (!tag->nameSpace.isEmpty()) + nameCol += 1 + tag->nameSpace.length(); + if (col > bCol && col <= nameCol) //we are inside a tag name, so show the possible tags + { + showCodeCompletions( getTagCompletions(line, col) ); + handled = true; + } else + { + index = tag->valueIndexAtPos(line,col); + if (index != -1) //inside a value + { + s = tag->attribute(index); + if (s == "style" && completionDTD->insideDTDs.contains("css")) + { + completionDTD = DTDs::ref()->find("text/css"); + return scriptAutoCompletion(line, col, ""); + } else + { + tag->attributeValuePos(index, bLine, bCol); + s = tag->attributeValue(index).left(col - bCol); + showCodeCompletions( getAttributeValueCompletions(tagName, tag->attribute(index), s) ); + handled = true; + } + } else + { + index = tag->attributeIndexAtPos(line,col); + s = text(line,col,line,col); + if (index != -1 || s ==" " || s==">" || s == "/") //inside an attribute or between attributes + { + if (index !=-1) + { + tag->attributeNamePos(index, bLine, bCol); + s = tag->attribute(index).left(col - bCol); + } else + { + s = text(line, 0, line, col -1); + s = s.section(' ', -1); + } + showCodeCompletions( getAttributeCompletions(tagName, s) ); + handled = true; + } + } + } + } + if (!handled) + { + QString s = editIf->textLine(line).left(col); + int pos = s.findRev('&'); + if (pos != -1) + { + s = s.mid(pos + 1); + if (!s.stripWhiteSpace().isEmpty()) + { + //complete character codes + showCodeCompletions(getCharacterCompletions(s)); + handled = true; + } + } + } + return handled; +} + +void Document::slotCompletionAborted() +{ + completionInProgress = false; + argHintVisible = false; +} + +/** Ask for user confirmation if the file was changed outside. */ +void Document::checkDirtyStatus() +{ + QString fileName; + if (url().isLocalFile()) + fileName = url().path(); + if (m_dirty) + { + createTempFile(); + if (!fileName.isEmpty()) + { + QDateTime modifTime = QFileInfo(fileName).lastModified(); + if (modifTime == m_modifTime) + m_dirty = false; + } + if (m_dirty) + { + if (m_md5sum.isEmpty()) + { + QFile f(fileName); + if (f.open(IO_ReadOnly)) + { + const char* c = ""; + KMD5 context(c); + context.reset(); + context.update(f); + m_md5sum = context.hexDigest(); + f.close(); + } + m_dirty = false; + } else + { + //check if the file is changed, also by file content. Might help to reduce + //unwanted warning on NFS + QFile f(fileName); + if (f.open(IO_ReadOnly)) + { + QString md5sum; + const char* c = ""; + KMD5 context(c); + context.reset(); + context.update(f); + md5sum = context.hexDigest(); + kdDebug(24000) << "MD5 sum of current doc: " << m_md5sum << endl; + kdDebug(24000) << "MD5 sum of doc on disc : " << md5sum << endl; + if (md5sum == m_md5sum) + { + m_dirty = false; + } + f.close(); + } + } + } + if (m_dirty) + { + DirtyDlg *dlg = new DirtyDlg(url().path(), m_tempFileName, false, this); + DirtyDialog *w = static_cast<DirtyDialog*>(dlg->mainWidget()); + QString kompareStr = KStandardDirs::findExe("kompare"); + if (kompareStr.isEmpty()) + { + w->buttonCompare->setEnabled(false); + w->buttonLoad->setChecked(true); + } + if (dlg->exec()) + { + m_doc->setModified(false); + openURL(url()); + } + m_modifTime = QFileInfo(fileName).lastModified(); + delete dlg; + } + closeTempFile(); + m_dirty = false; + } +} + +/** Save the document and reset the dirty status. */ +void Document::save() +{ + if (url().isLocalFile()) + { + QString fileName; + fileName = url().path(); + fileWatcher->removeFile(fileName); +// kdDebug(24000) << "removeFile[save]: " << fileName << endl; + m_doc->save(); + m_dirty = false; + m_modifTime = QFileInfo(fileName).lastModified(); + fileWatcher->addFile(fileName); +// kdDebug(24000) << "addFile[save]: " << fileName << endl; + } else + { + m_doc->save(); + m_dirty = false; + } +// kdDebug(24000) << "Document " << url() << " saved." << endl; +} + +bool Document::saveAs(const KURL& url) +{ + bool result = m_doc->saveAs(url); + if (result) + { + m_md5sum = ""; + if (url.isLocalFile()) + { + QFile f(url.path()); + if (f.open(IO_ReadOnly)) + { + const char* c = ""; + KMD5 context(c); + context.reset(); + context.update(f); + m_md5sum = context.hexDigest(); + f.close(); + } + } + } + return result; +} + +void Document::enableGroupsForDTEP(const QString& dtepName, bool enable) +{ + if (m_groupsForDTEPs.isEmpty()) + m_groupsForDTEPs = m_DTEPList; + if (enable) + { + if (m_groupsForDTEPs.contains(dtepName) == 0) + m_groupsForDTEPs.append(dtepName); + } else + { + m_groupsForDTEPs.remove(dtepName); + } +} + +void Document::resetGroupsForDTEPList() +{ + m_groupsForDTEPs.clear(); +} + +/** Returns true if the number of " (excluding \") inside text is even. */ +bool Document::evenQuotes(const QString &text) +{ + int num = text.contains(QRegExp("[^\\\\]\"")); + + return (num /2 *2 == num); +} + +void Document::slotTextChanged() +{ + changed = true; + parser->setSAParserEnabled(false); //disable special area parsing if the text was changed. + if (reparseEnabled && delayedTextChangedEnabled) + { + kdDebug(24000) << "Delayed text changed called." << endl; + //delay the handling, otherwise we may get wrong values for (line,column) + QTimer::singleShot(0, this, SLOT(slotDelayedTextChanged())); + delayedTextChangedEnabled = false; + } +} + +void Document::slotDelayedTextChanged(bool forced) +{ + if (!forced && typingInProgress) + { + kdDebug(24000) << "Reparsing delayed!" << endl; + parser->setParsingNeeded(true); + QTimer::singleShot(1000, this, SLOT(slotDelayedTextChanged())); + reparseEnabled = false; + delayedTextChangedEnabled = false; + return; + } + + uint line, column; + QString oldNodeName = ""; + Node *node; + Node *currentNode = 0L; //holds a copy of the node which is at (line,column) + Node *previousNode = 0L;//holds a copy of the node before currentNode + if (qConfig.updateClosingTags) + { + viewCursorIf->cursorPositionReal(&line, &column); + node = parser->nodeAt(line, column, false); + if (node && + ((node->tag->type == Tag::XmlTag && !node->tag->single) || + node->tag->type == Tag::XmlTagEnd) + ) + { + Tag *tag; + tag = new Tag(*node->tag); + currentNode = new Node(0L); + currentNode->removeAll = false; + currentNode->tag = tag; + + node = node->previousSibling(); + if (node) + { + tag = new Tag(*node->tag); + previousNode = new Node(0L); + previousNode->removeAll = false; + previousNode->tag = tag; + } + } + } + parser->setSAParserEnabled(true); //enable special area parsing, it was disabled in slotTextChanged() + baseNode = parser->rebuild(this); + if (qConfig.updateClosingTags && currentNode) + { + viewCursorIf->cursorPositionReal(&line, &column); + node = parser->nodeAt(line, column, false); + if (node && + node->tag->nameSpace + node->tag->name != currentNode->tag->nameSpace + currentNode->tag->name && + ((node->tag->type == Tag::XmlTag && !node->tag->single) || node->tag->type == Tag::XmlTagEnd) && node->tag->validXMLTag) + { + int bl, bc, bl2, bc2; + node->tag->beginPos(bl, bc); + currentNode->tag->beginPos(bl2,bc2); + if ( (bl != bl2 || bc !=bc2) && previousNode) + { + previousNode->tag->beginPos(bl2, bc2); + Node::deleteNode(currentNode); + currentNode = previousNode; + previousNode = 0L; + } else + { + Node::deleteNode(previousNode); + previousNode = 0L; + } + if (bl == bl2 && bc == bc2 && + ((node->tag->type == Tag::XmlTag && !node->tag->single) || currentNode->tag->type == Tag::XmlTagEnd)) + { + QString newName = node->tag->name; + bool updateClosing = (currentNode->tag->type == Tag::XmlTag) && !newName.startsWith("!"); + int num = 1; + if (!node->tag->nameSpace.isEmpty()) + newName.prepend(node->tag->nameSpace + ":"); + if (updateClosing) + node = node->nextSibling(); + else + node = node->previousSibling(); + while (node) + { + if (node->tag->validXMLTag && ((node->tag->type == Tag::XmlTag && !node->tag->single) || node->tag->type == Tag::XmlTagEnd)) + { + if (node->tag->nameSpace + node->tag->name == currentNode->tag->nameSpace + currentNode->tag->name ) + { + num++; + } + if ( (updateClosing && QuantaCommon::closesTag(currentNode->tag, node->tag)) || + (!updateClosing && QuantaCommon::closesTag(node->tag, currentNode->tag)) ) + { + num--; + } + if (num == 0) + { + reparseEnabled = false; + node->tag->beginPos(bl, bc); + bc++; + if(editIfExt) + editIfExt->editBegin(); + int len = node->tag->name.length(); + if (!node->tag->nameSpace.isEmpty()) + len += 1 + node->tag->nameSpace.length(); + editIf->removeText(bl, bc, bl, bc + len); + if (updateClosing) + { + editIf->insertText(bl, bc, "/"+newName); + } else + { + editIf->insertText(bl, bc, newName.mid(1)); + if (bl == (int)line) + { + column += (newName.length() - currentNode->tag->name.length()); + } + } + if(editIfExt) + editIfExt->editEnd(); + viewCursorIf->setCursorPositionReal(bl, bc); + docUndoRedo->mergeNextModifsSet(); + baseNode = parser->parse(this, true); + viewCursorIf->setCursorPositionReal(line, column); + reparseEnabled = true; + break; + } + } + if (updateClosing) + node = node->nextSibling(); + else + node = node->previousSibling(); + } + } + } + Node::deleteNode(currentNode); + Node::deleteNode(previousNode); + } + + quantaApp->slotNewLineColumn(); + if (qConfig.instantUpdate && quantaApp->structTreeVisible()) + { + typingInProgress = false; + StructTreeView::ref()->slotReparse(this, baseNode , qConfig.expandLevel); + } + reparseEnabled = true; + delayedTextChangedEnabled = true; +} + +/** Returns list of values for attribute */ +QStringList* Document::tagAttributeValues(const QString& dtdName, const QString& tag, const QString &attribute, bool &deleteResult) +{ + QStringList *values = 0L; + deleteResult = true; + const DTDStruct* dtd = DTDs::ref()->find(dtdName); + if (dtd) + { + QString searchForAttr = (dtd->caseSensitive) ? attribute : attribute.upper(); + AttributeList* attrs = QuantaCommon::tagAttributes(dtdName, tag); + if (attrs) + { + Attribute *attr; + KURL u; + KURL base = url(); + base.setPath(base.directory(false,false)); + QString s; + for ( attr = attrs->first(); attr; attr = attrs->next() ) + { + QString attrName = (dtd->caseSensitive) ? attr->name : attr->name.upper(); + if (attrName == searchForAttr) + { + if (attr->type == "url") { + Project *project = Project::ref(); + if (project->hasProject()) + { + values = new QStringList(project->fileNameList()); + for (uint i = 0; i < values->count(); i++) + { + u = (*values)[i]; + u = QExtFileInfo::toRelative(u, base); + (*values)[i] = u.path(); + } + values->remove(values->at(0)); + values->append("mailto:" + project->email()); + } else + { + QDir dir = QDir(url().directory()); + values = new QStringList(dir.entryList()); + } + break; + } else { + values = &attr->values; + deleteResult = false; + break; + } + } + } + } + } + return values; +} + +bool Document::hasChanged() +{ + bool b = changed; + changed = false; + return b; +} + +void Document::setChanged(bool newStatus) +{ + changed = newStatus; +} + +void Document::paste() +{ + reparseEnabled = false; + dynamic_cast<KTextEditor::ClipboardInterface*>(view())->paste(); + reparseEnabled = true; + baseNode = parser->rebuild(this); +} + +/** returns all the areas that are between tag and it's closing pair */ +QStringList Document::tagAreas(const QString& tag, bool includeCoordinates, bool skipFoundContent) +{ + Node *node = baseNode; + int bl, bc, el, ec; + QStringList result; + + while (node) + { + if (node->tag->type == Tag::XmlTag) + { + if ( (node->tag->dtd()->caseSensitive && node->tag->name == tag) || + (!node->tag->dtd()->caseSensitive && node->tag->name.lower() == tag.lower()) ) + { + node->tag->beginPos(bl, bc); + if (node->next) + node->next->tag->endPos(el, ec); + else + { + el = editIf->numLines()-1; + ec = editIf->lineLength(el); + } + QString s = text(bl, bc, el, ec); + if (includeCoordinates) + { + s.prepend(QString("%1,%2,%3,%4\n").arg(bl).arg(bc).arg(el).arg(ec)); + } + result += s; + if (skipFoundContent) + node = node->next; + else + node = node->nextSibling(); + } else + node = node->nextSibling(); + } else + node = node->nextSibling(); + } + + return result; +} + +void Document::activateRepaintView(bool activation) +{ + repaintEnabled = activation; + m_view->setUpdatesEnabled(activation); +} + +void Document::setErrorMark(int line) +{ + if (!markIf) + return; + markIf->addMark(line, KTextEditor::MarkInterface::markType07); +} + +void Document::clearErrorMarks() +{ + if (!markIf) + return; + QPtrList<KTextEditor::Mark> marks = markIf->marks(); + KTextEditor::Mark* mark; + for (mark = marks.first(); mark; mark = marks.next()) + { + if (mark->type & KTextEditor::MarkInterface::markType07) + markIf->removeMark(mark->line, KTextEditor::MarkInterface::markType07); + } +} + +QString Document::backupPathEntryValue() +{ + return m_backupPathValue; +} + +void Document::setBackupPathEntryValue(const QString& ev) +{ + m_backupPathValue = ev; +} + +/** if the document is modified then backup it and insert an entry in quantarc */ +void Document::createBackup(KConfig* config) +{ + if (isModified()) + { + if (isUntitled()) + { + m_backupPathValue = qConfig.backupDirPath + untitledUrl + "." + hashFilePath("file:///" + untitledUrl) + "U"; + } else + { + m_backupPathValue = qConfig.backupDirPath + url().fileName() + "." + hashFilePath(url().url()); + } + QString backupPathValueURL = KURL::fromPathOrURL(m_backupPathValue).url(); + + //the encoding used for the current document + QString encoding = quantaApp->defaultEncoding(); + if (encodingIf) + encoding = encodingIf->encoding(); + if (encoding.isEmpty()) + encoding = "utf8"; //final fallback + + //creates an entry string in quantarc if it does not exist yet + config->setGroup("General Options"); + QStringList backedupFilesEntryList = QuantaCommon::readPathListEntry(config, "List of backedup files"); //the files that were backedup + QStringList autosavedFilesEntryList = QuantaCommon::readPathListEntry(config, "List of autosaved files"); //the list of actual backup files inside $KDEHOME/share/apps/quanta/backups + if (!autosavedFilesEntryList.contains(backupPathValueURL)) //not yet backed up, add an entry for this file + { + autosavedFilesEntryList.append(backupPathValueURL); + config->writePathEntry("List of autosaved files", autosavedFilesEntryList); + if (!isUntitled()) + backedupFilesEntryList.append(KURL::fromPathOrURL(url().path() + "." + qConfig.quantaPID).url()); + else + backedupFilesEntryList.append(url().url() + "." + qConfig.quantaPID); + config->writePathEntry("List of backedup files", backedupFilesEntryList); + config->sync(); + } + + //creates a copy of this specific document + QFile file(m_backupPathValue); + if (file.open(IO_WriteOnly)) + { + QTextStream stream(&file); + stream.setCodec(QTextCodec::codecForName(encoding)); + stream << editIf->text(); + file.close(); + } + } +} +/** if there is no more need for a backup copy then remove it */ +void Document::removeBackup(KConfig *config) +{ + QString backupPathValueURL = KURL::fromPathOrURL(m_backupPathValue).url(); + + config->reparseConfiguration(); + config->setGroup("General Options"); + + QStringList backedupFilesEntryList = QuantaCommon::readPathListEntry(config, "List of backedup files"); + QStringList autosavedFilesEntryList = QuantaCommon::readPathListEntry(config, "List of autosaved files"); + + autosavedFilesEntryList.remove(backupPathValueURL); + config->writePathEntry("List of autosaved files", autosavedFilesEntryList); + backedupFilesEntryList.remove(KURL::fromPathOrURL(url().path() + "." + qConfig.quantaPID).url()); + config->writePathEntry("List of backedup files", backedupFilesEntryList); + config->sync(); + + if(QFile::exists(m_backupPathValue)) + QFile::remove(m_backupPathValue); +} +/** creates a string by hashing a bit the path string of this document */ +QString Document::hashFilePath(const QString& p) +{ + switch(p.length()) + { + case 1: { + int c = int(p[0]); + return QString::number(c, 10) + "P" + qConfig.quantaPID; + } + + case 2: { + int c = int(p[1]) * 2; + return QString::number(c, 10) + "P" + qConfig.quantaPID; + } + + default: { + int sign = 1, + sum = 0; + uint plen = p.length(); + for (uint i = 0; i+1 < plen; i++) + { + sum += int(p[i]) + int(p[i + 1]) * sign; + sign *= -1; + } + if( sum >= 0 ) + return QString::number(sum, 10) + "P" + qConfig.quantaPID; + else + return QString::number(sum*(-1), 10) + "N" + qConfig.quantaPID; + } + } +} + +void Document::convertCase() +{ + int tagCase = 0; + int attrCase = 0; + KDialogBase dlg(this, 0L, false, i18n("Change Tag & Attribute Case"), KDialogBase::Ok | KDialogBase::Cancel); + CaseWidget w(&dlg); + dlg.setMainWidget(&w); + const DTDStruct *dtd = defaultDTD(); + switch (qConfig.attrCase) + { + case 1: {w.lowerAttr->setChecked(true); break;} + case 2: {w.upperAttr->setChecked(true); break;} + default:{w.unchangedAttr->setChecked(true); break;} + } + switch (qConfig.tagCase) + { + case 1: {w.lowerTag->setChecked(true); break;} + case 2: {w.upperTag->setChecked(true); break;} + default:{w.unchangedTag->setChecked(true); break;} + } + + if (dlg.exec()) + { + KProgressDialog progressDlg(this, 0, i18n("Working...")); + progressDlg.setLabel(i18n("Changing tag and attribute case. This may take some time, depending on the document complexity.")); + progressDlg.setAllowCancel(false); + progressDlg.show(); + kapp->eventLoop()->processEvents( QEventLoop::ExcludeUserInput | QEventLoop::ExcludeSocketNotifiers); + KProgress *pBar = progressDlg.progressBar(); + pBar->setValue(0); + pBar->setTotalSteps(nodeNum); + pBar->setTextEnabled(true); + if (w.lowerTag->isChecked()) + tagCase = 1; + if (w.upperTag->isChecked()) + tagCase = 2; + if (w.lowerAttr->isChecked()) + attrCase = 1; + if (w.upperAttr->isChecked()) + attrCase = 2; + if (tagCase == 0 && attrCase == 0) + return; + reparseEnabled = false; + int bl, bc, ec; + uint line, col; + viewCursorIf->cursorPositionReal(&line, &col); + Node *node = baseNode; + while (node) + { + pBar->advance(1); + if (node->tag->dtd() == dtd) + { + if (tagCase !=0) + { + if(editIfExt) + editIfExt->editBegin(); + node->tag->namePos(bl, bc); + ec = bc + node->tag->name.length(); + editIf->removeText(bl, bc, bl, ec); + viewCursorIf->setCursorPositionReal(bl, bc); + QString newName = node->tag->name; + if (tagCase == 1) + newName = newName.lower(); + else if (tagCase == 2) + newName = newName.upper(); + editIf->insertText(bl, bc, newName); + if(editIfExt) + editIfExt->editEnd(); + } + if (attrCase != 0) + { + QString newName; + for (int i = 0; i < node->tag->attrCount(); i++) + { + if(editIfExt) + editIfExt->editBegin(); + node->tag->attributeNamePos(i, bl, bc); + newName = node->tag->attribute(i); + ec = bc + newName.length(); + editIf->removeText(bl, bc, bl, ec); + if (attrCase == 1) + newName = newName.lower(); + else if (attrCase == 2) + newName = newName.upper(); + editIf->insertText(bl, bc, newName); + if(editIfExt) + editIfExt->editEnd(); + } + } + } + node = node->nextSibling(); + } + reparseEnabled = true; + viewCursorIf->setCursorPositionReal(line, col); + quantaApp->reparse(true); + } +} + +void Document::open(const KURL &url, const QString &encoding) +{ + if (encodingIf) + { + encodingIf->setEncoding(encoding); + m_encoding = encoding; + m_codec = QTextCodec::codecForName(m_encoding); + } + connect(m_doc, SIGNAL(completed()), this, SLOT(slotOpeningCompleted())); + connect(m_doc, SIGNAL(canceled(const QString&)), this, SLOT(slotOpeningFailed(const QString&))); + if (!openURL(url)) + slotOpeningFailed(QString::null); + if (!url.isLocalFile()) + { + QExtFileInfo internalFileInfo; + internalFileInfo.enter_loop(); + } +} + +void Document::slotOpeningCompleted() +{ + KURL u = url(); + if (!u.isLocalFile()) + { + m_modifTime = QDateTime(); + qApp->exit_loop(); + } + else + { + fileWatcher->addFile(u.path()); + m_modifTime = QFileInfo(u.path()).lastModified(); +// kdDebug(24000) << "addFile[Document::open]: " << u.path() << endl; + } + disconnect(m_doc, SIGNAL(completed()), this, SLOT(slotOpeningCompleted())); + disconnect(m_doc, SIGNAL(canceled(const QString&)), this, SLOT(slotOpeningFailed(const QString&))); + m_dirty = false; + m_view->setFocus(); + processDTD(); + emit openingCompleted(u); +} + +void Document::slotOpeningFailed(const QString &errorMessage) +{ + m_md5sum = ""; + Q_UNUSED(errorMessage); //TODO: append the error message to our own error message + if (!url().isLocalFile()) + qApp->exit_loop(); + disconnect(m_doc, SIGNAL(completed()), this, SLOT(slotOpeningCompleted())); + disconnect(m_doc, SIGNAL(canceled(const QString&)), this, SLOT(slotOpeningFailed(const QString&))); + emit openingFailed(url()); +} + +void Document::processDTD(const QString& documentType) +{ + QString foundName; + QString projectDTD = Project::ref()->defaultDTD(); + setDTDIdentifier(projectDTD); + Tag *tag = 0L; + if (documentType.isEmpty()) + { + foundName = findDTDName(&tag); //look up the whole file for DTD definition + bool found = false; + if (!foundName.isEmpty()) //!DOCTYPE found in file + { + KDialogBase dlg(this, 0L, true, i18n("DTD Selector"), KDialogBase::Ok | KDialogBase::Cancel); + DTDSelectDialog *dtdWidget = new DTDSelectDialog(&dlg); + dlg.setMainWidget(dtdWidget); + QStringList lst = DTDs::ref()->nickNameList(true); + QString foundNickName = DTDs::ref()->getDTDNickNameFromName(foundName); + for (uint i = 0; i < lst.count(); i++) + { + dtdWidget->dtdCombo->insertItem(lst[i]); + if (lst[i] == foundNickName) + { + setDTDIdentifier(foundName); + found =true; + } + } + + if (!DTDs::ref()->find(foundName)) + { + //try to find the closest matching DTD + QString s = foundName.lower(); + uint spaceNum = s.contains(' '); + QStringList dtdList = DTDs::ref()->nameList(); + QStringList lastDtdList; + for (uint i = 0; i <= spaceNum && !dtdList.empty(); i++) + { + lastDtdList = dtdList; + QStringList::Iterator strIt = dtdList.begin(); + while (strIt != dtdList.end()) + { + if (!(*strIt).startsWith(s.section(' ', 0, i))) + { + strIt = dtdList.remove(strIt); + } else + { + ++strIt; + } + } + } + dtdList = lastDtdList; + for (uint i = 0; i <= spaceNum && !dtdList.empty(); i++) + { + lastDtdList = dtdList; + QStringList::Iterator strIt = dtdList.begin(); + while (strIt != dtdList.end()) + { + if (!(*strIt).endsWith(s.section(' ', -(i+1), -1))) + { + strIt = dtdList.remove(strIt); + } else + { + ++strIt; + } + } + } + if (lastDtdList.count() == 1 || lastDtdList[0].startsWith(s.section(' ', 0, 0))) + { + projectDTD = lastDtdList[0]; + } + } + +// dlg->dtdCombo->insertItem(i18n("Create New DTD Info")); + dtdWidget->messageLabel->setText(i18n("This DTD is not known for Quanta. Choose a DTD or create a new one.")); + dtdWidget->currentDTD->setText(DTDs::ref()->getDTDNickNameFromName(foundName)); + QString projectDTDNickName = DTDs::ref()->getDTDNickNameFromName(projectDTD); + for (int i = 0; i < dtdWidget->dtdCombo->count(); i++) + { + if (dtdWidget->dtdCombo->text(i) == projectDTDNickName) + { + dtdWidget->dtdCombo->setCurrentItem(i); + break; + } + } + if (!found && qConfig.showDTDSelectDialog) + { + quantaApp->slotHideSplash(); + if (dlg.exec()) + { + qConfig.showDTDSelectDialog = !dtdWidget->useClosestMatching->isChecked(); + setDTDIdentifier(DTDs::ref()->getDTDNameFromNickName(dtdWidget->dtdCombo->currentText())); + const DTDStruct *dtd = DTDs::ref()->find(dtdName); + if (dtdWidget->convertDTD->isChecked() && dtd->family == Xml) + { + int bLine, bCol, eLine, eCol; + tag->beginPos(bLine,bCol); + tag->endPos(eLine,eCol); + editIf->removeText(bLine, bCol, eLine, eCol+1); + viewCursorIf->setCursorPositionReal((uint)bLine, (uint)bCol); + insertText("<!DOCTYPE" + dtd->doctypeStr +">"); + } + } + } + } else //DOCTYPE not found in file + { + KURL u = url(); + QString dtdId = DTDs::ref()->DTDforURL(u)->name; +// if (dtdId == "empty") + { + const DTDStruct * dtd = DTDs::ref()->find(projectDTD); + if (DTDs::canHandle(dtd, u)) + dtdId = projectDTD; + else + { + dtd = DTDs::ref()->find(qConfig.defaultDocType); + if (DTDs::canHandle(dtd, u)) + dtdId = qConfig.defaultDocType; + } + } + setDTDIdentifier(dtdId); + } + } else //dtdName is read from the method's parameter + { + setDTDIdentifier(documentType); + } + + if (!isUntitled()) + { + quantaApp->messageOutput()->showMessage(i18n("\"%1\" is used for \"%2\".\n").arg(DTDs::ref()->getDTDNickNameFromName(dtdName)).arg(url().prettyURL(0, KURL::StripFileProtocol))); + } + quantaApp->slotLoadToolbarForDTD(dtdName); + StructTreeView::ref()->useOpenLevelSetting = true; + delete tag; +} + + +/** Called when a file on the disk has changed. */ +void Document::slotFileDirty(const QString& fileName) +{ + if ( url().path() == fileName && !dirty() ) + { + setDirtyStatus(true); + if (this == ViewManager::ref()->activeDocument()) + { + checkDirtyStatus(); + } + } +} + +void Document::slotMarkChanged(KTextEditor::Mark mark, KTextEditor::MarkInterfaceExtension::MarkChangeAction action) +{ + if(mark.type & KTextEditor::MarkInterface::markType02) + { + if(action == KTextEditor::MarkInterfaceExtension::MarkRemoved) + emit breakpointUnmarked(this, mark.line); + else + emit breakpointMarked(this, mark.line); + } +} + +void Document::resetDTEPs() +{ + m_DTEPList.clear(); + m_DTEPList.append(defaultDTD()->name); +} + +void Document::addDTEP(const QString &dtepName) +{ + if (m_DTEPList.contains(dtepName) == 0) + { + m_DTEPList.append(dtepName); + } +} + +QStringList Document::groupsForDTEPs() +{ + if (m_groupsForDTEPs.isEmpty()) + return m_DTEPList; + else + return m_groupsForDTEPs; +} + +QString Document::annotationText(uint line) +{ + QMap<uint, QPair<QString, QString> >::Iterator it = m_annotations.find(line); + if (it != m_annotations.end()) + return it.data().first; + else + return QString::null; +} + +void Document::setAnnotationText(uint line, const QString& text) +{ + if (text.isEmpty()) + { + m_annotations.remove(line); + if (markIf) + markIf->removeMark(line, KTextEditor::MarkInterface::markType08); + } else + { + m_annotations.insert(line, qMakePair(text, QString(""))); + if (markIf) + markIf->setMark(line, KTextEditor::MarkInterface::markType08); + uint line, column; + viewCursorIf->cursorPositionReal(&line, &column); + viewCursorIf->setCursorPositionReal(line, 0); + const DTDStruct *dtd = currentDTD(true); + QString commentBegin = ""; + QString commentEnd = ""; + for (QMap<QString, QString>::ConstIterator it = dtd->comments.constBegin(); it != dtd->comments.constEnd(); ++it) + { + commentBegin = it.key(); + commentEnd = it.data(); + if (commentEnd != "\n") + break; + } + if (commentBegin.isEmpty()) + { + if (dtd->family == Xml) + { + commentBegin = "<!--"; + commentEnd = "-->"; + } else + { + commentBegin = "/*"; + commentEnd = "*/"; + } + } + QString s = "@annotation: " + text; + s.prepend(commentBegin + " "); + s.append(" " + commentEnd + "\n"); + insertText(s, true, true); + emit showAnnotation(line, "", qMakePair(text, QString(""))); + } +} + +void Document::addAnnotation(uint line, const QPair<QString, QString>& annotation) +{ + m_annotations.insert(line, annotation); + if (markIf) + markIf->setMark(line, KTextEditor::MarkInterface::markType08); + emit showAnnotation(line, "", annotation); +} + +void Document::clearAnnotations() +{ + if (markIf) + { + QPtrList<KTextEditor::Mark> m = markIf->marks(); + for (uint i=0; i < m.count(); i++) + markIf->removeMark( m.at(i)->line, KTextEditor::MarkInterface::markType08 ); + } + m_annotations.clear(); +} + +bool Document::openURL(const KURL& url) +{ + m_md5sum = ""; + if (url.isLocalFile()) + { + QFile f(url.path()); + if (f.open(IO_ReadOnly)) + { + const char* c = ""; + KMD5 context(c); + context.reset(); + context.update(f); + m_md5sum = context.hexDigest(); + f.close(); + } + } + return m_doc->openURL(url); +} + +#include "document.moc" diff --git a/quanta/src/document.h b/quanta/src/document.h new file mode 100644 index 00000000..7b6ef151 --- /dev/null +++ b/quanta/src/document.h @@ -0,0 +1,352 @@ +/*************************************************************************** + document.h - description + ------------------- + begin : Tue Jun 6 2000 + copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev & Eric Laffoon <[email protected],[email protected],[email protected]> + (C) 2001-2004 Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 DOCUMENT_H +#define DOCUMENT_H + +//qt includes +#include <qdatetime.h> +#include <qdict.h> +#include <qmap.h> +#include <qwidget.h> + +#include <kurl.h> +#include <ktexteditor/markinterfaceextension.h> + +//own includes +#include "qtag.h" + +/** + *@author Dmitry Poplavsky & Alexander Yakovlev & Eric Laffoon & Andras Mantia + */ + +class QDomDocument; +class QEvent; +class QFocusEvent; +class QTextCodec; +class QStringList; +class KConfig; +class KTempFile; +class KURL; +class Tag; +class Node; +class Project; +class undoRedo; +struct AreaStruct; +struct DTDStruct; + +namespace KTextEditor +{ + class CodeCompletionInterface; + class CompletionEntry; + class ConfigInterface; + class Document; + class EditInterface; + class EditInterfaceExt; + class EncodingInterface; + class MarkInterface; + class SelectionInterface; + class SelectionInterfaceExt; + class View; + class ViewCursorInterface; + class Mark; +} + +class Document : public QWidget{ + Q_OBJECT + +public: + Document(KTextEditor::Document *doc, + QWidget *parent = 0, const char *name = 0, WFlags f=0); + ~Document(); + + KURL url(); + + bool isUntitled(); + void setUntitledUrl(const QString &url); + /** Returns tag name at specified position */ + QString getTagNameAt(int line, int col ); + + void selectText(int x1, int y1, int x2, int y2 ); + void replaceSelected(const QString &s); + + /** insert tag in document */ + void insertTag(const QString &s1, const QString &s2 = QString::null); + /** Change the current tag's attributes with those from dict */ + void changeTag(Tag *tag, QDict<QString> *dict ); + /**Change the attr value of the called attrName to attrValue*/ + void changeTagAttribute(Tag *tag, const QString& attrName, const QString&attrValue); + /**Change the namespace in a tag. Add if it's not present, or remove if the + namespace argument is empty*/ + void changeTagNamespace(Tag *tag, const QString& nameSpace); + /** Insert the content of the url into the document. */ + void insertFile(const KURL& url); + /** Inserts text at the current cursor position */ + void insertText(const QString &text, bool adjustCursor = true, bool reparse = true); + /** Recursively insert the mandatory childs of tag. Returns true if a child was + inserted.*/ + bool insertChildTags(QTag *tag, QTag* lastTag = 0L); + + QPoint getGlobalCursorPos(); + QString find(const QRegExp& rx, int sLine, int sCol, int& fbLine, int&fbCol, int &feLine, int&feCol); + QString findRev(const QRegExp& rx, int sLine, int sCol, int& fbLine, int&fbCol, int &feLine, int&feCol); + /** Get the view of the document */ + KTextEditor::View* view(); + /** Get the KTextEditor::Document of the document */ + KTextEditor::Document* doc(); + /** Sets the modifiedFlag value. */ + void setModified(bool flag); + /** Returns true if the document was modified. */ + bool isModified(); + + /** Creates a temporary file where the editor content is saved. + */ + void createTempFile(); + /** Closes and removes the temporary file. */ + void closeTempFile(); + /** Returns the name of the temporary file, QString::null if no temporary file exists. */ + QString tempFileName(); + + /** Returns the DTD identifier for the document */ + QString getDTDIdentifier(); + /** Sets the DTD identifier */ + void setDTDIdentifier(const QString &id); + /** Get a pointer to the current active DTD. If fallback is true, this always gives back a valid and known DTD pointer: the active, the document specified and in last case the application default document type. */ + const DTDStruct* currentDTD(bool fallback = true); + /** Get a pointer to the default DTD (document, or app). */ + const DTDStruct* defaultDTD() const; + /** Find the DTD name for a part of the document. */ + QString findDTDName(Tag **tag); + /** Retrives the text from the specified rectangle. The KTextEditor::EditInterface::text seems to not +work correctly. */ + QString text(int bLine, int bCol, int eLine, int eCol) const; + /** Same as the above, but using AreaStruct as an argument */ + QString text(const AreaStruct &area) const; + /** Code completion was requested by the user. */ + void codeCompletionRequested(); + /** Bring up the code completion tooltip. */ + void codeCompletionHintRequested(); + /** Returns the dirty status. */ + bool dirty() const {return m_dirty;}; + void setDirtyStatus(bool status) {m_dirty = status;}; + /** Ask for user confirmation if the file was changed outside. */ + void checkDirtyStatus(); + /** Save the document and reset the dirty status. */ + void save(); + /** Save the document under a new name and calculate the new md5sum. */ + bool saveAs(const KURL& url); + /** Enable or disable the visibility of groups for a DTEP.*/ + void enableGroupsForDTEP(const QString& dtepName, bool enable = true); + /** Clears the selected DTEP list */ + void resetGroupsForDTEPList(); + /** Find the word until the first word boundary backwards */ + QString findWordRev(const QString& textToSearch, const DTDStruct *dtd = 0L); + /** Returns the changed status since the last query. Resets changed.*/ + bool hasChanged(); + /** Sets the changed status.*/ + void setChanged(bool newStatus); + /** Paste the contents of clipboard into the document */ + void paste(); + + /** disable/enable the parser*/ + void activateParser(bool activation) {reparseEnabled = activation;} + bool parserActivated() {return reparseEnabled;} + + /** returns all the areas that are between tag and it's closing pair */ + QStringList tagAreas(const QString &tag, bool includeCoordinates, bool skipFoundContent); + + /** disable/enable the repaint of the Kate view */ + void activateRepaintView(bool activation); + bool RepaintViewActivated() {return repaintEnabled;} + + void setErrorMark(int line); + void clearErrorMarks(); + void convertCase(); + + /** returns the word under the cursor */ + QString currentWord(); + /** Opens the url. The url must be valid and the file pointed to it must exists. */ + void open(const KURL &url, const QString &encoding); + /** + * Opens a file in the editor part. + * @param url + */ + bool openURL(const KURL& url); + /** Reads the DTD info from the file, tries to find the correct DTD and builds the tag/attribute list from the DTD file. */ + void processDTD(const QString& documentType = QString::null); + + /** Resets the list of DTEPs found in the document */ + void resetDTEPs(); + /** Adds a DTEP to the list of DTEPs present in the document */ + void addDTEP(const QString &dtepName); + /** Returns the list of DTEPs that should appear in the structure tree. By default + this is the list of DTEPs present in the document, but the user can turn on/ + off them with the help of RMB->Show Groups For in the structure tree */ + QStringList groupsForDTEPs(); + + bool busy; + + KTextEditor::ViewCursorInterface *viewCursorIf; + KTextEditor::SelectionInterface *selectionIf; + KTextEditor::SelectionInterfaceExt *selectionIfExt; + KTextEditor::EditInterface *editIf; + KTextEditor::EncodingInterface *encodingIf; + KTextEditor::EditInterfaceExt *editIfExt; + KTextEditor::CodeCompletionInterface *codeCompletionIf; + KTextEditor::ConfigInterface* configIf; + KTextEditor::MarkInterface* markIf; + + /** Hold the list of user tags (real or not, like functions) that are in the document*/ + QTagList userTagList; + /** The undo/redo stack */ + undoRedo *docUndoRedo; + + bool isBackedUp(); + /** Creates an automatic backup copy for the crash recovering mechanism */ + void createBackup(KConfig* config); + /** No descriptions */ + QString backupPathEntryValue(); + /** No descriptions */ + void setBackupPathEntryValue(const QString& ev); + /** Removes automatic backup copies */ + void removeBackup(KConfig *config); + /** create a string using document path string */ + static QString hashFilePath(const QString& p); + QString annotationText(uint line); + void setAnnotationText(uint line, const QString& text); + QMap<uint, QPair<QString, QString> > annotations() {return m_annotations;} + void addAnnotation(uint line, const QPair<QString, QString>& annotation); + void clearAnnotations(); + +public slots: + + /** Called after a completion is inserted */ + void slotCompletionDone( KTextEditor::CompletionEntry completion ); + /** Called when a user selects a completion, we then can modify it */ + void slotFilterCompletion(KTextEditor::CompletionEntry*,QString *); + /** Called whenever a user inputs text */ + void slotCharactersInserted(int ,int ,const QString&); + /** Called when the code completion is aborted.*/ + void slotCompletionAborted(); + /** Called whenever the text in the document is changed. */ + void slotTextChanged(); + /** Handle the text changed events. Usually called from slotTextChanged, + but it's possible to force the handling by calling manually and setting + forced to true. */ + void slotDelayedTextChanged(bool forced = false); + void slotDelayedScriptAutoCompletion(); + void slotDelayedShowCodeCompletion(); + +signals: + /** Emitted when the internal text editor got the focus */ + void editorGotFocus(); + void openingFailed(const KURL &url); + void openingCompleted(const KURL &url); + + void breakpointMarked(Document*, int); + void breakpointUnmarked(Document*, int); + void showAnnotation(uint, const QString&, const QPair<QString, QString>&); + +private slots: + void slotReplaceChar(); + void slotOpeningCompleted(); + void slotOpeningFailed(const QString &errorMessage); + /** Called when a file on the disk has changed. */ + void slotFileDirty(const QString& fileName); + + void slotMarkChanged(KTextEditor::Mark mark, KTextEditor::MarkInterfaceExtension::MarkChangeAction action); +private: + /** + * Finds the beginning of a tag in the document, starting from a position. + * @param position start to look from this position backwards + * @return the position of the starting character or an empty QPoint if not found + */ + QPoint findTagBeginning(const QPoint& position); + QPoint findTagEnd(const QPoint& position); + + + + QMap<uint, QPair<QString, QString> > m_annotations; + QString untitledUrl; + int m_replaceLine; + int m_replaceCol; + QString m_replaceStr; + + KTextEditor::Document *m_doc; + KTextEditor::View *m_view; + + KTempFile *tempFile; + QString m_tempFileName; + QDateTime m_modifTime; + /* path of the backup copy file of the document */ + QString m_backupPathValue; + QString dtdName; + QString m_encoding; + QTextCodec *m_codec; +/*The DTD valid in the place where the completion was invoked.*/ + const DTDStruct *completionDTD; + + bool changed; + bool completionInProgress; + bool completionRequested; ///< true if the code completion was explicitely requested by the user + bool argHintVisible; + bool hintRequested; + bool reparseEnabled; + bool repaintEnabled; + bool delayedTextChangedEnabled; + /** True if the document is dirty (has been modified outside). */ + bool m_dirty; + QString m_md5sum; + Project *m_project; + /** Parse the document according to this DTD. */ + QStringList m_groupsForDTEPs; ///< The list of the DTEPs for which the groups should appear in the structure tree + QStringList m_DTEPList; ///< The list of all DTEPs found in the document + //stores the data after an autocompletion. Used when bringing up the + //autocompletion box delayed with the singleshot timer (workaround for + //a bug: the box is not showing up if it is called from slotCompletionDone) + int m_lastLine, m_lastCol; + QValueList<KTextEditor::CompletionEntry>* m_lastCompletionList; + + /** Get list of possibile variable name completions */ + QValueList<KTextEditor::CompletionEntry>* getGroupCompletions(Node *node, const StructTreeGroup& groupName, int line, int col); + /** Get list of possibile tag name completions */ + QValueList<KTextEditor::CompletionEntry>* getTagCompletions(int line, int col); + /** Get list of possibile tag attribute completions */ + QValueList<KTextEditor::CompletionEntry>* getAttributeCompletions(const QString& tagName,const QString& startsWith=QString::null); + /** Get list of possibile tag attribute value completions */ + QValueList<KTextEditor::CompletionEntry>* getAttributeValueCompletions(const QString& tagName, const QString& attribute, const QString& startsWith=QString::null); + /** Get list of possibile completions in normal text input (nt creating a tag) */ + QValueList<KTextEditor::CompletionEntry>* getCharacterCompletions(const QString& starstWith=QString::null); + /** Invoke code completion dialog for XML like tags according to the position (line, col), using DTD dtd. */ + bool xmlCodeCompletion(int line, int col); + /** Returns list of values for attribute. If deleteResult is true after the call, + the caller must delete the returned list. */ + QStringList* tagAttributeValues(const QString& dtdName, const QString& tag, const QString& attribute, bool &deleteResult); + /** Brings up list of code completions */ + void showCodeCompletions( QValueList<KTextEditor::CompletionEntry> *completions ); + /** Called whenever a user inputs text in an XML type document. */ + bool xmlAutoCompletion(int , int , const QString & ); + /** Called whenever a user inputs text in a script type document. */ + bool scriptAutoCompletion(int line, int col, const QString &insertedString); + /** Returns true if the number of " (excluding \") inside text is even. */ + bool evenQuotes(const QString &text); + void handleCodeCompletion(); + bool isDerivatedFrom(const QString& className, const QString &baseClass); +}; + +#endif + diff --git a/quanta/src/dtds.cpp b/quanta/src/dtds.cpp new file mode 100644 index 00000000..44fba0ab --- /dev/null +++ b/quanta/src/dtds.cpp @@ -0,0 +1,1103 @@ +/*************************************************************************** + dtds.cpp + ------------------- + begin : 12.02.2004 (extract from quanta_init and others) + + copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev <[email protected],[email protected]> + (C) 2001-2003 by Andras Mantia <[email protected]> + (C) 2000, 2003 by Eric Laffoon <[email protected]> + (C) 2004 by Jens Herden <jhe at epost.de> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 <qfile.h> +#include <qextfileinfo.h> +#include <qdom.h> +#include <qcursor.h> +#include <qtimer.h> + +// include files for KDE +#include <kapplication.h> +#include <kcombobox.h> +#include <kconfig.h> +#include <kdialogbase.h> +#include <kurl.h> +#include <kurlrequester.h> +#include <kglobal.h> +#include <kmessagebox.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <kfiledialog.h> + + +// application specific includes +#include "quantacommon.h" +#include "resource.h" +#include "dtdparser.h" +#include "document.h" +#include "viewmanager.h" +#include "loadentitydlgs.h" + +#include "dtds.h" + +/** filename for the desciption of the dtd */ +const QString m_rcFilename("description.rc"); + +/** + This constructor reads the dictionary of known dtd's, the attributes and tags will be loaded + on the first access to one dtd. +*/ +DTDs::DTDs(QObject *parent) + :QObject(parent) +{ + connect(this, SIGNAL(hideSplash()), parent, SLOT(slotHideSplash())); + connect(this, SIGNAL(enableIdleTimer(bool)), parent, SLOT(slotEnableIdleTimer(bool))); + connect(this, SIGNAL(loadToolbarForDTD(const QString&)), parent, SLOT(slotLoadToolbarForDTD(const QString&))); +// kdDebug(24000) << "dtds::dtds" << endl; + m_dict = new QDict<DTDStruct>(119, false); //optimized for max 119 DTD. This should be enough. + m_dict->setAutoDelete(true); + m_doc = new QDomDocument(); + + QString localKDEdir = KGlobal::instance()->dirs()->localkdedir(); + QStringList tagsResourceDirs = KGlobal::instance()->dirs()->findDirs("appdata", "dtep"); + QStringList tagsDirs; + QStringList::ConstIterator end = tagsResourceDirs.constEnd(); + for ( QStringList::ConstIterator it = tagsResourceDirs.constBegin(); it != end; ++it ) + { + if ((*it).startsWith(localKDEdir)) + { + QDir dir(*it); + dir.setFilter(QDir::Dirs); + QStringList subDirs = dir.entryList(); + QStringList::ConstIterator subitEnd = subDirs.constEnd(); + for ( QStringList::ConstIterator subit = subDirs.constBegin(); subit != subitEnd; ++subit ) + { +// kdDebug(24000) << "dtds::dtds add:" << *it + *subit+"/" << endl; + if ((*subit != ".") && (*subit != "..")) + tagsDirs += *it + *subit + "/"; + } + } + } + for ( QStringList::ConstIterator it = tagsResourceDirs.constBegin(); it != end; ++it ) + { + if (!(*it).startsWith(localKDEdir)) + { + QDir dir(*it); + dir.setFilter(QDir::Dirs); + QStringList subDirs = dir.entryList(); + QStringList::ConstIterator subitEnd = subDirs.constEnd(); + for ( QStringList::ConstIterator subit = subDirs.constBegin(); subit != subitEnd; ++subit ) + { +// kdDebug(24000) << "dtds::dtds add2:" << *it + *subit+"/" << endl; + if ((*subit != ".") && (*subit != "..")) + tagsDirs += *it + *subit + "/"; + } + } + } +// kdDebug(24000) << tagsDirs.count() << " folders found." << endl; + QTime t; + t.start(); + QStringList::ConstIterator tagsDirsEnd = tagsDirs.constEnd(); + for ( QStringList::ConstIterator it = tagsDirs.constBegin(); it != tagsDirsEnd; ++it ) + { +// kdDebug(24000) << "read:" << *it << endl; + readTagDir(*it, false); // read all tags, but only short form + } + kdDebug(24000) << "DTD reading: " << t.elapsed() << endl; +//load the mimetypes from the insideDTDs + QDictIterator<DTDStruct> it(*m_dict); + for( ; it.current(); ++it ) + { + DTDStruct * dtd = it.current(); + for (uint i = 0; i < dtd->insideDTDs.count(); i++) + { + const DTDStruct *insideDTD = m_dict->find(dtd->insideDTDs[i]); // search but don't load + if (!insideDTD) + insideDTD = m_dict->find(getDTDNameFromNickName(dtd->insideDTDs[i])); // search but don't load + if (insideDTD && !insideDTD->toplevel) + dtd->mimeTypes += insideDTD->mimeTypes; + } + } + + // kdDebug(24000) << "dtds::dtds constructed" << endl; +} + +DTDs::~DTDs() +{ + QDictIterator<DTDStruct> it(*m_dict); + for( ; it.current(); ++it ) + { + removeDTD (it.current()); + } + delete m_dict; + delete m_doc; +} + + + +void DTDs::removeDTD(DTDStruct *dtd) +{ + if (dtd) + { + delete dtd->tagsList; + dtd->tagsList = 0L; + delete dtd->commonAttrs; + dtd->commonAttrs = 0L; + m_dict->remove(dtd->name.lower()); + } +} + + +/** Reads the tag files and the description.rc from tagDir in order to + build up the internal DTD and tag structures. */ +bool DTDs::readTagDir(const QString &dirName, bool loadAll) +{ + // kdDebug(24000) << "dtds::readTagDir:" << dirName << " all:" << loadAll << endl; + QString tmpStr = dirName + m_rcFilename; + if (!QFile::exists(tmpStr)) + return false; + KConfig *dtdConfig = new KConfig(tmpStr, true); + dtdConfig->setGroup("General"); + QString dtdName = dtdConfig->readEntry("Name", "Unknown"); + if (m_dict->find(dtdName.lower())) + { + delete dtdConfig; + kdDebug(24000) << "dtds::readTagDir from " << dirName + << " canceled, DTD " << dtdName << " found in memory" << endl; + return false; + } + + //read the general DTD info + DTDStruct *dtd = new DTDStruct; + dtd->fileName = tmpStr; + dtd->name = dtdName; + dtd->nickName = dtdConfig->readEntry("NickName", dtdName); + dtd->mimeTypes = dtdConfig->readListEntry("MimeTypes"); + for (uint i = 0; i < dtd->mimeTypes.count(); i++) + dtd->mimeTypes[i] = dtd->mimeTypes[i].stripWhiteSpace(); + dtd->family = dtdConfig->readNumEntry("Family", Xml); + if (dtd->family != Xml) + dtd->toplevel = dtdConfig->readBoolEntry("TopLevel", false); + else + dtd->toplevel = true; + dtd->tagsList = 0L; + dtd->commonAttrs = 0L; + + //Read the areas that define the areas + dtdConfig->setGroup("Parsing rules"); + QStringList definitionAreaBorders = dtdConfig->readListEntry("AreaBorders"); + for (uint i = 0; i < definitionAreaBorders.count(); i++) + { + QStringList tmpStrList = QStringList::split(" ", definitionAreaBorders[i].stripWhiteSpace()); + dtd->definitionAreas[tmpStrList[0].stripWhiteSpace()] = tmpStrList[1].stripWhiteSpace(); + } + //Read the tags that define this DTD + QStringList tmpStrList = dtdConfig->readListEntry("Tags"); + for (uint i = 0; i < tmpStrList.count(); i++) + { + tmpStr = tmpStrList[i].stripWhiteSpace(); + int pos = tmpStr.find('('); + dtd->definitionTags[tmpStr.left(pos).stripWhiteSpace()] = tmpStr.mid(pos+1, tmpStr.findRev(')')-pos-1).stripWhiteSpace(); + } + //Which DTD can be present in this one? + dtd->insideDTDs = dtdConfig->readListEntry("MayContain"); + for (uint i = 0; i < dtd->insideDTDs.count(); i++) + { + dtd->insideDTDs[i] = dtd->insideDTDs[i].stripWhiteSpace().lower(); + } + + + m_dict->insert(dtdName.lower(), dtd); //insert the structure into the dictionary + delete dtdConfig; + + if (!loadAll) + { + dtd->loaded = false; + return true; + } + + dtd->loaded = readTagDir2(dtd); + return dtd->loaded; +} + + +/** Reads the tag files and the description.rc from dtd in order to + build up the internal DTD and tag structures. + */ +bool DTDs::readTagDir2(DTDStruct *dtd) +{ +// kdDebug(24000) << "dtds::readTagDir2:" << dtd->name << " at " << dtd->fileName << endl; + + if (!QFile::exists(dtd->fileName)) return false; + + kapp->setOverrideCursor( QCursor(Qt::WaitCursor) ); + + KConfig *dtdConfig = new KConfig(dtd->fileName, true); + + //read the general DTD info + dtdConfig->setGroup("General"); + dtd->commonAttrs = new AttributeListDict(); + dtd->commonAttrs->setAutoDelete(true); + + bool caseSensitive = dtdConfig->readBoolEntry("CaseSensitive"); + dtd->url = dtdConfig->readEntry("URL"); + dtd->doctypeStr = dtdConfig->readEntry("DoctypeString"); + if (dtd->doctypeStr.isEmpty()) + { + dtd->doctypeStr = "PUBLIC \"" + dtd->name + "\""; + if (!dtd->url.isEmpty()) + dtd->doctypeStr += " \"" + dtd->url + "\""; + } + dtd->doctypeStr.prepend(' '); + dtd->inheritsTagsFrom = dtdConfig->readEntry("Inherits").lower(); + dtd->documentation = dtdConfig->readEntry("Documentation").lower(); + + dtd->defaultExtension = dtdConfig->readEntry("DefaultExtension"); + dtd->caseSensitive = caseSensitive; + int numOfTags = 0; + QTagList *tagList = new QTagList(119, false); //max 119 tag in a DTD + tagList->setAutoDelete(true); + //read all the tag files + KURL dirURL(dtd->fileName); + dirURL.setFileName(""); + QString dirName = dirURL.path(1); + if (QFile::exists(dirName + "common.tag")) + readTagFile(dirName + "common.tag", dtd, 0L); + //bool idleTimerStatus = quantaApp->slotEnableIdleTimer(false); + emit enableIdleTimer(false); + KURL::List files = QExtFileInfo::allFilesRelative(dirURL, "*.tag", 0L); + //quantaApp->slotEnableIdleTimer(idleTimerStatus); + emit enableIdleTimer(true); + QString tmpStr; + KURL::List::ConstIterator end_f = files.constEnd(); + for ( KURL::List::ConstIterator it_f = files.constBegin(); it_f != end_f; ++it_f ) + { + tmpStr = (*it_f).path(-1); + if (!tmpStr.isEmpty()) + { + tmpStr.prepend(dirName); + if (!tmpStr.endsWith("/common.tag")) + numOfTags += readTagFile(tmpStr, dtd, tagList); + } + } + + //read the toolbars + dtdConfig->setGroup("Toolbars"); + tmpStr = QuantaCommon::readPathEntry(dtdConfig, "Location"); //holds the location of the toolbars + if (!tmpStr.endsWith("/") && !tmpStr.isEmpty()) + { + tmpStr.append("/"); + } + dtd->toolbars = dtdConfig->readListEntry("Names"); + for (uint i = 0; i < dtd->toolbars.count(); i++) + { + dtd->toolbars[i] = tmpStr + dtd->toolbars[i].stripWhiteSpace() + toolbarExtension; + } + + //read the extra tags and their attributes + dtdConfig->setGroup("Extra tags"); + dtd->defaultAttrType = dtdConfig->readEntry("DefaultAttrType","input"); + QStrList extraTagsList; + dtdConfig->readListEntry("List",extraTagsList); + QString option; + QStrList optionsList; + QStrList attrList; + for (uint i = 0 ; i < extraTagsList.count(); i++) + { + QTag *tag = new QTag(); + tag->setName(QString(extraTagsList.at(i)).stripWhiteSpace()); + + tmpStr = (dtd->caseSensitive) ? tag->name() : tag->name().upper(); + if (tagList->find(tmpStr)) //the tag is already defined in a .tag file + { + delete tag; + continue; //skip this tag + } + tag->parentDTD = dtd; + //read the possible stopping tags + QStrList stoppingTags; + dtdConfig->readListEntry(tag->name() + "_stoppingtags",stoppingTags); + for (uint j = 0; j < stoppingTags.count(); j++) + { + QString stopTag = QString(stoppingTags.at(j)).stripWhiteSpace(); + if (!dtd->caseSensitive) stopTag = stopTag.upper(); + tag->stoppingTags.append(stopTag); + } + //read the possible tag options + optionsList.clear(); + dtdConfig->readListEntry(tag->name() + "_options",optionsList); + for (uint j = 0; j < optionsList.count(); j++) + { + option = QString(optionsList.at(j)).stripWhiteSpace(); + QDictIterator<AttributeList> it(*(dtd->commonAttrs)); + for( ; it.current(); ++it ) + { + tmpStr = "has" + QString(it.currentKey()).stripWhiteSpace(); + if (option == tmpStr) + { + tag->commonGroups += QString(it.currentKey()).stripWhiteSpace(); + } + } + if (option == "single") + { + tag->setSingle(true); + } + if (option == "optional") + { + tag->setOptional(true); + } + } + attrList.clear(); + dtdConfig->readListEntry(tag->name(), attrList); + for (uint j = 0; j < attrList.count(); j++) + { + Attribute* attr = new Attribute; + attr->name = QString(attrList.at(j)).stripWhiteSpace(); + attr->type = dtd->defaultAttrType; + tag->addAttribute(attr); + delete attr; + } + if (caseSensitive) + { + tagList->insert(tag->name(),tag); //append the tag to the list for this DTD + } else + { + tagList->insert(tag->name().upper(),tag); + } + } + dtd->tagsList = tagList; + dtd->tagsList->setAutoDelete(true); + + + /**** Code for the new parser *****/ + + dtdConfig->setGroup("Parsing rules"); + bool appendCommonRules = dtdConfig->readBoolEntry("AppendCommonSpecialAreas", true); + //Read the special areas and area names + QString rxStr = ""; + if (dtd->family == Xml && appendCommonRules) + { + dtd->specialAreas["<?xml"] = "?>"; + dtd->specialAreaNames["<?xml"] = "XML PI"; + dtd->specialAreas["<!--"] = "-->"; + dtd->specialAreaNames["<!--"] = "comment"; +// dtd->specialAreas["<!"] = ">"; + // dtd->specialAreaNames["<!"] = "DTD"; + dtd->insideDTDs.append("dtd"); + tmpStr = "(<?xml)|(<!--)|(<!)|"; + rxStr = QuantaCommon::makeRxCompatible(tmpStr); + } + QStringList specialAreasList = dtdConfig->readListEntry("SpecialAreas"); + QStringList specialAreaNameList = dtdConfig->readListEntry("SpecialAreaNames"); + QStringList tmpStrList; + for (uint i = 0; i < specialAreasList.count(); i++) + { + if (!specialAreasList[i].stripWhiteSpace().isEmpty()) + { + tmpStrList = QStringList::split(" ",specialAreasList[i].stripWhiteSpace()); + tmpStr = tmpStrList[0].stripWhiteSpace(); + rxStr.append(QuantaCommon::makeRxCompatible(tmpStr)+"|"); + dtd->specialAreas[tmpStr] = tmpStrList[1].stripWhiteSpace(); + dtd->specialAreaNames[tmpStr] = specialAreaNameList[i]; + } + } + if (rxStr.isEmpty()) + { + dtd->specialAreaStartRx.setPattern(""); + } else + { + dtd->specialAreaStartRx.setPattern(rxStr.left(rxStr.length() - 1)); + } + //Read the special tags + tmpStrList = dtdConfig->readListEntry("SpecialTags"); + for (uint i = 0; i < tmpStrList.count(); i++) + { + tmpStr = tmpStrList[i].stripWhiteSpace(); + int pos = tmpStr.find('('); + dtd->specialTags[tmpStr.left(pos).stripWhiteSpace()] = tmpStr.mid(pos+1, tmpStr.findRev(')')-pos-1).stripWhiteSpace(); + } + + //static const QString quotationStr = "\\\\\"|\\\\'"; + rxStr = "\\\\\"|\\\\'|"; + QStringList commentsList = dtdConfig->readListEntry("Comments"); + if (dtd->family == Xml && appendCommonRules) + commentsList.append("<!-- -->"); + QString tmpStr2; + for (uint i = 0; i < commentsList.count(); i++) + { + tmpStrList = QStringList::split(" ",commentsList[i].stripWhiteSpace()); + tmpStr = tmpStrList[0].stripWhiteSpace(); + rxStr += QuantaCommon::makeRxCompatible(tmpStr); + rxStr += "|"; + tmpStr2 = tmpStrList[1].stripWhiteSpace(); + if (tmpStr2 == "EOL") + tmpStr2 = '\n'; + dtd->comments[tmpStr] = tmpStr2; + } + dtd->commentsStartRx.setPattern(rxStr.left(rxStr.length()-1)); + + /**** End of code for the new parser *****/ + + //read the definition of a structure, and the structure keywords + QStringList structKeywords = dtdConfig->readListEntry("StructKeywords",','); + if (structKeywords.count() !=0 ) + { + tmpStr = "\\b("; + for (uint i = 0; i < structKeywords.count(); i++) + { + tmpStr += structKeywords[i].stripWhiteSpace()+"|"; + } + tmpStr.truncate(tmpStr.length()-1); + tmpStr += ")\\b"; + } else + { + tmpStr = "\\b[\\d\\S\\w]+\\b"; + } + dtd->structKeywordsRx.setPattern(tmpStr); + + structKeywords = dtdConfig->readListEntry("LocalScopeKeywords",','); + if (structKeywords.count() !=0 ) + { + tmpStr = "\\b("; + for (uint i = 0; i < structKeywords.count(); i++) + { + tmpStr += structKeywords[i].stripWhiteSpace()+"|"; + } + tmpStr.truncate(tmpStr.length()-1); + tmpStr += ")\\b"; + } else + { + tmpStr = "\\b[\\d\\S\\w]+\\b"; + } + dtd->localScopeKeywordsRx.setPattern(tmpStr); + + dtd->structRx.setPattern(dtdConfig->readEntry("StructRx","\\{|\\}").stripWhiteSpace()); + dtd->structBeginStr = dtdConfig->readEntry("StructBeginStr","{").stripWhiteSpace(); + dtd->structEndStr = dtdConfig->readEntry("StructEndStr","}").stripWhiteSpace(); + + + dtdConfig->setGroup("Extra rules"); + dtd->minusAllowedInWord = dtdConfig->readBoolEntry("MinusAllowedInWord", false); + tmpStr = dtdConfig->readEntry("TagAutoCompleteAfter", "<").stripWhiteSpace(); + if (tmpStr.upper() == "NONE") + dtd->tagAutoCompleteAfter = '\0'; + else + if (tmpStr.upper() == "ALWAYS") + dtd->tagAutoCompleteAfter = '\1'; + else + dtd->tagAutoCompleteAfter = tmpStr.at(0); + dtd->requestSpaceBeforeTagAutoCompletion = dtdConfig->readBoolEntry("RequestSpaceBeforeTagAutoCompletion", false); + dtd->attrAutoCompleteAfter = dtdConfig->readEntry("AttributeAutoCompleteAfter","(").stripWhiteSpace().at(0); + dtd->attributeSeparator = dtdConfig->readEntry("AttributeSeparator").stripWhiteSpace().at(0); + if (dtd->attributeSeparator.isNull()) + { + dtd->attributeSeparator = (dtd->family == Xml) ? '\"' : ','; + } + dtd->tagSeparator = dtdConfig->readEntry("TagSeparator").stripWhiteSpace().at(0); + if (dtd->tagSeparator.isNull()) + dtd->tagSeparator = dtd->attributeSeparator; + + dtd->booleanAttributes = dtdConfig->readEntry("BooleanAttributes","extended"); + dtd->booleanTrue = dtdConfig->readEntry("BooleanTrue","true"); + dtd->booleanFalse = dtdConfig->readEntry("BooleanFalse","false"); + dtd->singleTagStyle = dtdConfig->readEntry("Single Tag Style", "xml").lower(); + dtd->variableGroupIndex = dtdConfig->readNumEntry("VariableGroupIndex", 0) - 1; + dtd->functionGroupIndex = dtdConfig->readNumEntry("FunctionGroupIndex", 0) - 1; + dtd->classGroupIndex = dtdConfig->readNumEntry("ClassGroupIndex", 0) - 1; + if (dtd->classGroupIndex != -1) + { + tmpStr = dtdConfig->readEntry("MemberAutoCompleteAfter").stripWhiteSpace(); + dtd->memberAutoCompleteAfter.setPattern(tmpStr); + } + dtd->objectGroupIndex = dtdConfig->readNumEntry("ObjectGroupIndex", 0) - 1; + + //read the definition of different structure groups, like links, images, functions + //classes, etc. + uint structGroupsCount = dtdConfig->readNumEntry("StructGroupsCount", 0); + if (structGroupsCount > MAX_STRUCTGROUPSCOUNT) + structGroupsCount = MAX_STRUCTGROUPSCOUNT; //max. 10 groups + + if (dtd->family == Script) + { + StructTreeGroup group; + QRegExp attrRx("\\([^\\)]*\\)"); + QString tagStr; + for (uint index = 1; index <= structGroupsCount; index++) + { + dtdConfig->setGroup(QString("StructGroup_%1").arg(index)); + //new code + group.name = dtdConfig->readEntry("Name").stripWhiteSpace(); + group.noName = dtdConfig->readEntry("No_Name").stripWhiteSpace(); + group.icon = dtdConfig->readEntry("Icon").stripWhiteSpace(); + tmpStr = dtdConfig->readEntry("DefinitionRx").stripWhiteSpace(); + group.definitionRx.setPattern(tmpStr); + tmpStr = dtdConfig->readEntry("UsageRx").stripWhiteSpace(); + group.usageRx.setPattern(tmpStr); + tmpStr = dtdConfig->readEntry("TypeRx").stripWhiteSpace(); + group.typeRx.setPattern(tmpStr); + group.hasDefinitionRx = !group.definitionRx.pattern().isEmpty(); + group.isMinimalDefinitionRx = dtdConfig->readBoolEntry("DefinitionRx_Minimal", false); + group.appendToTags = dtdConfig->readBoolEntry("AppendToTags", false); + group.parentGroup = dtdConfig->readEntry("ParentGroup").stripWhiteSpace(); + tagStr = dtdConfig->readEntry("TagType", "Text").stripWhiteSpace(); + if (tagStr == "XmlTag") + group.tagType = Tag::XmlTag; + else if (tagStr == "XmlTagEnd") + group.tagType = Tag::XmlTagEnd; + else if (tagStr == "Text") + group.tagType = Tag::Text; + else if (tagStr == "Comment") + group.tagType = Tag::Comment; + else if (tagStr == "CSS") + group.tagType = Tag::CSS; + else if (tagStr == "ScriptTag") + group.tagType = Tag::ScriptTag; + else if (tagStr == "ScriptStructureBegin") + group.tagType = Tag::ScriptStructureBegin; + else if (tagStr == "ScriptStructureEnd") + group.tagType = Tag::ScriptStructureEnd; + else group.tagType = -1; + tmpStr = dtdConfig->readEntry("AutoCompleteAfter").stripWhiteSpace(); + group.autoCompleteAfterRx.setPattern(tmpStr); + tmpStr = dtdConfig->readEntry("RemoveFromAutoCompleteWord").stripWhiteSpace(); + group.removeFromAutoCompleteWordRx.setPattern(tmpStr); + group.hasFileName = dtdConfig->readBoolEntry("HasFileName", false); + group.parseFile = dtdConfig->readBoolEntry("ParseFile", false); + tmpStr = dtdConfig->readEntry("FileNameRx").stripWhiteSpace(); + group.fileNameRx.setPattern(tmpStr); + dtd->structTreeGroups.append(group); + } + } else + { + XMLStructGroup group; + QRegExp attrRx("\\([^\\)]*\\)"); + QString tagName; + for (uint index = 1; index <= structGroupsCount; index++) + { + dtdConfig->setGroup(QString("StructGroup_%1").arg(index)); + group.name = dtdConfig->readEntry("Name").stripWhiteSpace(); + group.noName = dtdConfig->readEntry("No_Name").stripWhiteSpace(); + group.icon = dtdConfig->readEntry("Icon").stripWhiteSpace(); + group.appendToTags = dtdConfig->readBoolEntry("AppendToTags", false); + group.parentGroup = dtdConfig->readEntry("ParentGroup").stripWhiteSpace(); + QString tagStr = dtdConfig->readEntry("Tag").stripWhiteSpace(); + if (!tagStr.isEmpty()) + { + attrRx.search(tagStr); + tmpStr = attrRx.cap(); + tmpStrList = QStringList::split(',', tmpStr.mid(1, tmpStr.length()-2)); + tagName = tagStr.left(tagStr.find('(')).lower(); + group.attributes.clear(); + for (uint i = 0; i < tmpStrList.count(); i++) + group.attributes += tmpStrList[i].stripWhiteSpace(); + group.hasFileName = dtdConfig->readBoolEntry("HasFileName", false); + tmpStr = dtdConfig->readEntry("FileNameRx").stripWhiteSpace(); + group.fileNameRx.setPattern(tmpStr); + dtd->xmlStructTreeGroups.insert(tagName, group); + } + } + } + + delete dtdConfig; + dtd->loaded = true; + resolveInherited(dtd); + kapp->restoreOverrideCursor(); + return true; +} + + +void DTDs::resolveInherited (DTDStruct *dtd) +{ + //Resolve the inheritence + if (!dtd->inheritsTagsFrom.isEmpty()) + { + DTDStruct *parent = (DTDStruct *) find(dtd->inheritsTagsFrom); // this loads the dtd, if not present in memory + QDictIterator<QTag> tag_it(*(parent->tagsList)); + for ( ; tag_it.current(); ++tag_it) + { + QTag *tag = tag_it.current(); + QString searchForTag = (dtd->caseSensitive) ? tag->name() : tag->name().upper(); + if (!dtd->tagsList->find(searchForTag)) + { + QTag *newTag = new QTag(*tag); + dtd->tagsList->insert(searchForTag, newTag); + } + } + } + +//Read the pseudo DTD area definition strings (special area/tag string) +//from the DTD's which may be present in the DTD (May_Contain setting) + QMap<QString, QString>::ConstIterator mapIt; + QString specialAreaStartRxStr = dtd->specialAreaStartRx.pattern(); + if (!specialAreaStartRxStr.isEmpty()) + specialAreaStartRxStr += "|"; + for (uint i = 0; i < dtd->insideDTDs.count(); i++) + { + const DTDStruct *insideDTD = m_dict->find(dtd->insideDTDs[i]); // search but don't load + if (!insideDTD) + insideDTD = m_dict->find(getDTDNameFromNickName(dtd->insideDTDs[i])); // search but don't load + if (insideDTD) + { + for (mapIt = insideDTD->definitionAreas.begin(); mapIt != insideDTD->definitionAreas.end(); ++mapIt) + { + QString tmpStr = mapIt.key(); + dtd->specialAreas[tmpStr] = mapIt.data(); + dtd->specialAreaNames[tmpStr] = dtd->insideDTDs[i]; + specialAreaStartRxStr.append("(?:" + QuantaCommon::makeRxCompatible(tmpStr) + ")|"); + } + + for (mapIt = insideDTD->definitionTags.begin(); mapIt != insideDTD->definitionTags.end(); ++mapIt) + { + dtd->specialTags[mapIt.key()] = mapIt.data(); + } + } + dtd->specialAreaStartRx.setPattern(specialAreaStartRxStr.left(specialAreaStartRxStr.length() - 1)); + }; +} + + + +/** Reads the tags for the tag files. Returns the number of read tags. */ +uint DTDs::readTagFile(const QString& fileName, DTDStruct* parentDTD, QTagList *tagList) +{ +// kdDebug(24000) << "dtds::readTagFile:" << fileName << endl; + QFile f(fileName); + if (! f.exists()) + kdError() << "dtds::readTagFile file does not exist:" << fileName << endl; + else + { + bool result = f.open( IO_ReadOnly ); + if (! result) + kdError() << "dtds::readTagFile unable to open:" << fileName + << " Status: " << f.status() << endl; + } + QString errorMsg; + int errorLine, errorCol; + if (!m_doc->setContent( &f, &errorMsg, &errorLine, &errorCol )) + { + emit hideSplash(); + KMessageBox::error(0L, i18n("<qt>The DTD tag file %1 is not valid.<br> The error message is: <i>%2 in line %3, column %4.</i></qt>").arg(fileName).arg(errorMsg).arg(errorLine).arg(errorCol), + i18n("Invalid Tag File")); + kdWarning() << fileName << ": " << errorMsg << ": " << errorLine << "," << errorCol << endl; + } + + f.close(); + QDomNodeList nodeList = m_doc->elementsByTagName("tag"); + uint numOfTags = nodeList.count(); + for (uint i = 0; i < numOfTags; i++) + { + QDomNode n = nodeList.item(i); + QDomElement e = n.toElement(); + if (e.attribute("type") == "class") + { + QString extends = e.attribute("extends"); + QString name = e.attribute("name"); + if (!name.isEmpty() && !extends.isEmpty()) + parentDTD->classInheritance[name] = extends; + continue; + } + QTag *tag = new QTag(); + tag->setName(e.attribute("name")); + tag->setFileName(fileName); + tag->parentDTD = parentDTD; + bool common = false; + setAttributes(&n, tag, common); + if (common) + { + QString groupName = e.attribute("name"); + AttributeList *attrs = tag->attributes(); + attrs->setAutoDelete(false); + AttributeList *commonAttrList = new AttributeList; //no need to delete it + commonAttrList->setAutoDelete(true); + *commonAttrList = *attrs; + //delete tag; + parentDTD->commonAttrs->insert(groupName, commonAttrList); + } else + { + if (parentDTD->caseSensitive) + { + tagList->replace(tag->name(), tag); //append the tag to the list for this DTD + } else + { + tagList->replace(tag->name().upper(), tag); + } + } + } + return numOfTags; +} + + +/** + Parse the dom document and retrieve the tag attributes +*/ +void DTDs::setAttributes(QDomNode *dom, QTag* tag, bool &common) +{ + common = false; + Attribute *attr; + + QDomElement el = dom->toElement(); + QString tmpStr; + + tmpStr = el.attribute("common"); + if ((tmpStr != "1" && tmpStr != "yes")) //in case of common tags, we are not interested in these options + { + if (tag->parentDTD->commonAttrs) + { + QDictIterator<AttributeList> it(*(tag->parentDTD->commonAttrs)); + for( ; it.current(); ++it ) + { + QString lookForAttr = "has" + QString(it.currentKey()).stripWhiteSpace(); + tmpStr = el.attribute(lookForAttr); + if (tmpStr == "1" || tmpStr == "yes") + { + tag->commonGroups += QString(it.currentKey()).stripWhiteSpace(); + } + } + } + + tmpStr = el.attribute("single"); + if (tmpStr == "1" || tmpStr == "yes") + { + tag->setSingle(true); + } + + tmpStr = el.attribute("optional"); + if (tmpStr == "1" || tmpStr == "yes") + { + tag->setOptional(true); + } + + tmpStr = el.attribute("scope"); + tag->setScope(tmpStr); + + tag->type = el.attribute("type", "xmltag"); + tag->returnType = el.attribute("returnType", ""); + tag->className = el.attribute("class", ""); + tag->comment = el.attribute("comment", ""); + if (!tag->comment.isEmpty()) + tag->comment = " [" + i18n(tag->comment) + "] "; + tag->comment.prepend(el.attribute("version")); + } else + { + common = true; + } + QString attrList; + for ( QDomNode n = dom->firstChild(); !n.isNull(); n = n.nextSibling() ) + { + tmpStr = n.nodeName(); + if (tmpStr == "children") + { + QDomElement el = n.toElement(); + QDomElement item = el.firstChild().toElement(); + while ( !item.isNull() ) + { + tmpStr = item.tagName(); + if (tmpStr == "child") + { + QString childTag = item.attribute("name"); + if (!tag->parentDTD->caseSensitive) + childTag = childTag.upper(); + tag->childTags.insert(childTag, item.attribute("usage") == "required"); + } + item = item.nextSibling().toElement(); + } + } else + if (tmpStr == "stoppingtags") //read what tag can act as closing tag + { + QDomElement el = n.toElement(); + QDomElement item = el.firstChild().toElement(); + while ( !item.isNull() ) + { + if (item.tagName() == "stoppingtag") + { + QString stopTag = item.attribute("name"); + if (!tag->parentDTD->caseSensitive) + stopTag = stopTag.upper(); + tag->stoppingTags.append(stopTag); + } + item = item.nextSibling().toElement(); + } + } else + if (tmpStr == "attr") //an attribute + { + QDomElement el = n.toElement(); + attr = new Attribute; + attr->name = el.attribute("name"); + attr->source = el.attribute("source"); + attr->interface = el.attribute("interface"); + attr->method = el.attribute("method"); + attr->arguments = el.attribute("arguments"); + + attr->type = el.attribute("type",tag->parentDTD->defaultAttrType); + attr->defaultValue = el.attribute("defaultValue"); + attr->status = el.attribute("status"); + + if ( attr->type == "list" ) { + for ( QDomElement attrEl = el.firstChild().toElement(); !attrEl.isNull(); attrEl = attrEl.nextSibling().toElement() ) { + if ( attrEl.tagName() == "items" ) { + QDomElement item = attrEl.firstChild().toElement(); + while ( !item.isNull() ) { + attr->values.append( item.text() ); + item = item.nextSibling().toElement(); + } + } + } + } else if ( attr->type == "check" ) { + attr->values.append("true"); + attr->values.append("false"); + } else if ( attr->type == "color" ) { + attr->values.append("Black"); + attr->values.append("Silver"); + attr->values.append("Gray"); + attr->values.append("White"); + attr->values.append("Maroon"); + attr->values.append("Red"); + attr->values.append("Purple"); + attr->values.append("Fuchsia"); + attr->values.append("Green"); + attr->values.append("Lime"); + attr->values.append("Olive"); + attr->values.append("Yellow"); + attr->values.append("Navy"); + attr->values.append("Blue"); + attr->values.append("Teal"); + attr->values.append("Aqua"); + } else if ( attr->type == "url" ) { + //not treated yet + } else if ( attr->type == "input" ) { + //not treated yet + } + + if (tag->type == "function" || tag->type == "method") + { + if (attr->status == "optional") + { + attrList = attrList + "["+attr->type +" "+attr->name +"], "; + } else + { + attrList = attrList + attr->type +" "+attr->name +", "; + } + } + if (!attr->name.isEmpty()) + { + tag->addAttribute(attr); + } + delete attr; + } + } + if (!attrList.isEmpty()) + tag->comment.prepend(attrList.left(attrList.length() - 2) + "; "); +} + + +void DTDs::slotLoadDTD() +{ + KURL url = KFileDialog::getOpenURL("", i18n("*.dtd|DTD Definitions"), 0L); + if (!url.isEmpty()) + { + DTDParser dtdParser(url, KGlobal::dirs()->saveLocation("data") + resourceDir + "dtep"); + if (dtdParser.parse()) + { + QString dirName = dtdParser.dirName(); + KConfig dtdcfg(dirName + m_rcFilename, true); + dtdcfg.setGroup("General"); + QString dtdName = dtdcfg.readEntry("Name"); + QString nickName = dtdcfg.readEntry("NickName", dtdName); + DTDStruct * dtd = m_dict->find(dtdName) ; + if (dtd && + KMessageBox::warningYesNo(0L, i18n("<qt>Do you want to replace the existing <b>%1</b> DTD?</qt>").arg(nickName), QString::null, i18n("Replace"), i18n("Do Not Replace")) == KMessageBox::No) + { + return; + } + removeDTD(dtd); + if (readTagDir(dirName)) + { + QString family = dtdcfg.readEntry("Family", "1"); + Document *w = ViewManager::ref()->activeDocument(); + if (family == "1" && w && + KMessageBox::questionYesNo(0L, i18n("<qt>Use the newly loaded <b>%1</b> DTD for the current document?</qt>").arg(nickName), i18n("Change DTD"), i18n("Use"), i18n("Do Not Use")) == KMessageBox::Yes) + { + w->setDTDIdentifier(dtdName); + emit loadToolbarForDTD(w->getDTDIdentifier()); + emit forceReparse(); + } + } + } + } +} + +void DTDs::slotLoadDTEP(const QString &_dirName, bool askForAutoload) +{ + QString dirName = _dirName; + if (!dirName.endsWith("/")) + dirName += "/"; + KConfig dtdcfg(dirName + m_rcFilename, true); + dtdcfg.setGroup("General"); + QString dtdName = dtdcfg.readEntry("Name"); + QString nickName = dtdcfg.readEntry("NickName", dtdName); + DTDStruct * dtd = m_dict->find(dtdName) ; + if ( dtd && + KMessageBox::warningYesNo(0L, i18n("<qt>Do you want to replace the existing <b>%1</b> DTD?</qt>").arg(nickName), QString::null, i18n("Replace"), i18n("Do Not Replace")) == KMessageBox::No) + { + return; + } + removeDTD(dtd); + if (!readTagDir(dirName)) + { + KMessageBox::error(0L, i18n("<qt>Cannot read the DTEP from <b>%1</b>. Check that the folder contains a valid DTEP (<i>description.rc and *.tag files</i>).</qt>").arg(dirName), i18n("Error Loading DTEP")); + } else + { + QString family = dtdcfg.readEntry("Family", "1"); + if (askForAutoload && KMessageBox::questionYesNo(0L, i18n("<qt>Autoload the <b>%1</b> DTD in the future?</qt>").arg(nickName), QString::null, i18n("Load"), i18n("Do Not Load")) == KMessageBox::Yes) + { + KURL src; + src.setPath(dirName); + KURL target; + QString destDir = KGlobal::dirs()->saveLocation("data") + resourceDir + "dtep/"; + target.setPath(destDir + src.fileName()); + KIO::copy( src, target, false); //don't care about the result + } + Document *w = ViewManager::ref()->activeDocument(); + if (family == "1" && w && + KMessageBox::questionYesNo(0L, i18n("<qt>Use the newly loaded <b>%1</b> DTD for the current document?</qt>").arg(nickName), i18n("Change DTD"), i18n("Use"), i18n("Do Not Use")) == KMessageBox::Yes) + { + w->setDTDIdentifier(dtdName); + emit loadToolbarForDTD(w->getDTDIdentifier()); + emit forceReparse(); + } + } +} + +void DTDs::slotLoadEntities() +{ + KDialogBase dlg(0L, "loadentities", true, i18n("Load DTD Entities Into DTEP"), KDialogBase::Ok | KDialogBase::Cancel); + LoadEntityDlgS entitiesWidget(&dlg); + QStringList lst(DTDs::ref()->nickNameList(true)); + entitiesWidget.targetDTEPCombo->insertStringList(lst); + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + QString nickName = DTDs::ref()->getDTDNickNameFromName(w->getDTDIdentifier()); + entitiesWidget.targetDTEPCombo->setCurrentItem(lst.findIndex(nickName)); + } + dlg.setMainWidget(&entitiesWidget); + if (dlg.exec()) + { + DTDStruct * dtd = m_dict->find(getDTDNameFromNickName(entitiesWidget.targetDTEPCombo->currentText())); + DTDParser dtdParser(KURL::fromPathOrURL(entitiesWidget.sourceDTDRequester->url()), KGlobal::dirs()->saveLocation("data") + resourceDir + "dtep"); + QString dtdDir = QFileInfo(dtd->fileName).dirPath(); + if (dtdParser.parse(dtdDir, true)) + { + readTagFile(dtdDir + "/entities.tag", dtd, dtd->tagsList); + } + } +} + + +/** Returns the DTD name (identifier) corresponding to the DTD's nickname */ +QString DTDs::getDTDNameFromNickName(const QString& nickName) +{ + QDictIterator<DTDStruct> it(*m_dict); + for( ; it.current(); ++it ) + { + if (it.current()->nickName.lower() == nickName.lower()) + { + return it.current()->name; + } + } + return nickName; +} + +/** returns the known nick names */ +QStringList DTDs::nickNameList(bool topLevelOnly) +{ + QStringList nickList; + QDictIterator<DTDStruct> it(*m_dict); + for( ; it.current(); ++it ) + { + if (!topLevelOnly || it.current()->toplevel) + { + nickList << it.current()->nickName; + } + } + nickList.sort(); + return nickList; +} + + +/** returns the known names */ +QStringList DTDs::nameList(bool topLevelOnly) +{ + QStringList nameList; + QDictIterator<DTDStruct> it(*m_dict); + for( ; it.current(); ++it ) + { + if (!topLevelOnly || it.current()->toplevel) + { + nameList << it.current()->name; + } + } + nameList.sort(); + return nameList; +} + +QStringList DTDs::fileNameList(bool topLevelOnly) +{ + QStringList nameList; + QDictIterator<DTDStruct> it(*m_dict); + for( ; it.current(); ++it ) + { + if (!topLevelOnly || it.current()->toplevel) + { + nameList << (it.current()->name + "|" + it.current()->fileName); + } + } + return nameList; +} + + +const DTDStruct * DTDs::DTDforURL(const KURL &url) +{ + QValueList<DTDStruct*> foundList; + QDictIterator<DTDStruct> it(*m_dict); + for( ; it.current(); ++it ) + { + if (it.current()->toplevel && canHandle(it.current(), url)) + { + foundList.append(it.current()); + } + } + if (foundList.isEmpty()) + return find("empty"); + else + { + QString path = url.path(); + for (uint i = 0; i < foundList.count(); i++) + { + if (path.endsWith('.' + foundList[i]->defaultExtension)) + return foundList[i]; + } + return foundList[0]; + } +} + +bool DTDs::canHandle(const DTDStruct *dtd, const KURL &url) +{ + QString mimetype = KMimeType::findByURL(url)->name(); + if (dtd->mimeTypes.contains(mimetype)) + return true; + if (url.path().endsWith('.' + dtd->defaultExtension)) + return true; + return false; +} + +#include "dtds.moc" diff --git a/quanta/src/dtds.h b/quanta/src/dtds.h new file mode 100644 index 00000000..1e26f712 --- /dev/null +++ b/quanta/src/dtds.h @@ -0,0 +1,238 @@ +/*************************************************************************** + dtds.h - description + ------------------- + begin : 12.02.2004 (extract from quanta_init and others) + + copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev <[email protected],[email protected]> + (C) 2001-2003 by Andras Mantia <[email protected]> + (C) 2000, 2003 by Eric Laffoon <[email protected]> + (C) 2004 by Jens Herden <jhe at epost.de> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 DTDS_H +#define DTDS_H + +// application specific includes +#include "qtag.h" +#include "qobject.h" +#include "qfile.h" + +//qt includes +#include <qdict.h> + +//kde includes +#include <kdebug.h> + + +class QDomNode; +class QDomDocument; +class QTag; +class QString; +class QStringList; +class KConfig; +struct DTDStruct; + + +/** @short This class manages the DTD's for Quanta. + * + * The constructor only loads the necessary information from the description.rc file. + * The rest is later loaded on demand of the find() function. + */ +class DTDs : public QObject +{ + Q_OBJECT + +public: + + /** + * since this class is a singleton you must use this function to access it + * @return the class pointer + */ + static DTDs* ref(QObject *parent = 0L) + { + static DTDs *m_ref; + if (!m_ref) m_ref = new DTDs(parent); + return m_ref; + } + + + ~DTDs(); + + /** + * searchs a dtd and loads it from disk if not present in memory + * + * @param dtdName name of the dtd, will be converted to lowercase inside + * @return the found dtd structure + */ + const DTDStruct * find (const QString &dtdName) + { +// kdDebug(24000) << "dtds::find " << dtdName << endl; + DTDStruct *dtd = m_dict->find(dtdName.lower()) ; + if (dtd && ! dtd->loaded ) readTagDir2(dtd); + return dtd; + } + /** + * @param nickName nickname of the DTD + * @return the name (identifier) to the nickname. If the DTD is not found you get + * nickName back! + */ + QString getDTDNameFromNickName(const QString& nickName); + /** + * @param name name of the DTD + * @return the nickname to the name. If the DTD is not found you get + * name back! + */ + QString getDTDNickNameFromName(const QString& name) + { + DTDStruct *dtd = m_dict->find(name); + if ( dtd ) + return dtd->nickName; + else + return name; + }; + + /** + * creates a list of all available nicknames + * + * @param topLevelOnly true = only the DTD's which can be the main DTD of a document + * are included + * @return all known nick names + */ + QStringList nickNameList (bool topLevelOnly=false); + + /** + * creates a list of all available names + * + * @param topLevelOnly true = only the DTD's which can be the main DTD of a document + * are included + * @return all known names + */ + QStringList nameList (bool topLevelOnly=false); + /** + * creates a list with the path to the description.rc of all available DTEPs + * + * @param topLevelOnly true = only the DTD's which can be the main DTD of a document + * are included + * @return a list with the name and the path to the description.rc of all available DTEPs in form of + * DTEPName | path to description.rc + */ + QStringList fileNameList (bool topLevelOnly=false); + + + /** finds a dtd for a given url + * + * @param url look for this file + * @return the first DTD for this mimetype or the "empty" DTD if not found + */ + const DTDStruct * DTDforURL(const KURL &url); + + /** + * Checks if a DTD can handle the file or not. + * @param dtd the DTD to check against + * @param url the url pointing to the file + * @return true if it can handle + */ + static bool canHandle(const DTDStruct *dtd, const KURL &url); + +public slots: + + /** Load a DTD and convert to a DTEP */ + void slotLoadDTD(); + + /** Loads a DTEP from a directory + * @param dirName The directory containing the DTEP + * @param askForAutoLoad if true and the user answer with yes to the autoload question + * the DTEP will be copied to the local resource directory and will be autoloaded on + * startup + */ + void slotLoadDTEP(const QString& dirName, bool askForAutoLoad); + + /** + * Loads (replaces) the entities for a DTEP. + */ + void slotLoadEntities(); + +signals: + /** used to hide the splash screen */ + void hideSplash(); + + /** DTD has changed need reparse */ + void forceReparse(); + + /** Enable/disbale the idle timer*/ + void enableIdleTimer(bool); + + void loadToolbarForDTD(const QString&); + +private: + + /** The constructor is privat because we use singleton patter. + * If you need the class use DTDs::ref() for + * construction and reference + */ + DTDs(QObject *parent); + + + /** Reads the tag files and the description.rc from tagDir in order to + * build up the internal DTD and tag structures. + * + * @param dirName folder where dtd definition is located + * @param loadAll true = all information and tags will be loaded now (@ref readTagDir2 will be called) + * @return true = no error + */ + bool readTagDir(const QString &dirName, bool loadAll=true); + + /** Reads the tag files and the description.rc from tagDir in order to + * build up the internal DTD and tag structures. + * + * @param dtd the already preloaded dtd structure + * @return true = no error + */ + bool readTagDir2(DTDStruct *dtd); + + + /** helper for @ref readTagDir2 + * + * @param dtd the already preloaded dtd structure + */ + void resolveInherited (DTDStruct *dtd); + + /** Reads the tags from the tag files. + * + * @param fileName path of the tag file + * @param parentDTD the DTD where the tags belog to + * @param tagList the list where the tags are inserted + * @return the number of read tags. + */ + uint readTagFile(const QString& fileName, DTDStruct* parentDTD, QTagList *tagList); + + /** Parses the dom document and retrieve the tag attributes + * + * @param dom the DOM node containing the tag definition + * @param tag the QTag object that will be initialized by using the information in dom + * @param common will be true, if the tag is a just a list of common group attributes + */ + void setAttributes(QDomNode *dom, QTag* tag, bool &common); + /** removes dtd from dictonary and deletes all components + * + * @param dtd the dtd to delete + */ + void removeDTD(DTDStruct *dtd); + + /** helper to read the tag files */ + QDomDocument *m_doc; + + /** dictonary with references to all DTD's in memory */ + QDict<DTDStruct> *m_dict; +}; + + +#endif diff --git a/quanta/src/kqapp.cpp b/quanta/src/kqapp.cpp new file mode 100644 index 00000000..6e7f02df --- /dev/null +++ b/quanta/src/kqapp.cpp @@ -0,0 +1,233 @@ +/* + KQApplication.cpp + + KNode, the KDE newsreader + Copyright (c) 1999-2001 the KNode authors. + See file AUTHORS for details + + Rewritten for Quanta Plus: (C) 2002, 2003 Andras Mantia <[email protected]> + + 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. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US +*/ + +#include <qtimer.h> + +#include <kconfig.h> +#include <kdebug.h> +#include <kwin.h> +#include <kcmdlineargs.h> +#include <kurl.h> +#include <kiconloader.h> +#include <kstandarddirs.h> +#include <kglobalsettings.h> +#include <ksplashscreen.h> +#include <dcopclient.h> +#include <kdeversion.h> + +#include "config.h" +#include "quantacommon.h" +#include "project.h" +#include "quanta.h" +#include "quanta_init.h" +#include "kqapp.h" + +QuantaApp *quantaApp = 0L; //global pointer to the main application object +#define SPLASH_PICTURE "quantalogo" + +KSplash::KSplash() + : QFrame( 0L, QString("Quanta")+QUANTA_VERSION, + QWidget::WStyle_NoBorder | QWidget::WStyle_Customize | WX11BypassWM) +{ + QPixmap pm( UserIcon(SPLASH_PICTURE) ); + + setBackgroundPixmap(pm); + QRect desk = KGlobalSettings::splashScreenDesktopGeometry(); + setGeometry( desk.center().x()-225, desk.center().y()-150, 450, 300 ); + setLineWidth(0); + show(); +} + + +KSplash::~KSplash() +{ +} + + +KQApplication::KQApplication() + : KApplication() +{ + args = KCmdLineArgs::parsedArgs(); + splash = 0L; + + KGlobal::dirs()->addPrefix(PREFIX); + dcopClient()->registerAs("quanta", true); + +//FIXME: Do not hide the problem, but really fix the session restoration. +//I have no idea how to do it, but it may be broken because of delayed +//initialization in QuantaApp +/* if (isRestored()) + { + RESTORE(QuantaApp); + } + else */ + { + KConfig *config = kapp->config(); + config->setGroup("General Options"); + int mdiMode = config->readNumEntry("MDI mode", KMdi::IDEAlMode); + QString layout = config->readEntry("Window layout", "Default"); + if (layout == "Default" || args->isSet("resetlayout")) + { + mdiMode = KMdi::IDEAlMode; + config->writeEntry("MDI mode", KMdi::IDEAlMode); + config->writeEntry("Window layout", "Default"); + } + quantaApp = new QuantaApp(mdiMode); + config->setGroup("General Options"); + if (config->readBoolEntry("Show Splash", true) && args->isSet("logo")) + { + sp = new KSplashScreen(UserIcon(SPLASH_PICTURE)); + sp->show(); + connect(quantaApp, SIGNAL(showSplash(bool)), sp, SLOT(setShown(bool))); + QTimer::singleShot(10*1000, this, SLOT(slotSplashTimeout())); + } + setMainWidget(quantaApp); + slotInit(); + } +} + +void KQApplication::slotInit() +{ + KQApplicationPrivate::init(); +} + +KQApplication::~KQApplication() +{ +} + +void KQApplication::slotSplashTimeout() +{ + delete splash; + delete sp; + splash = 0L; + sp = 0L; +} + +KQUniqueApplication::KQUniqueApplication() + : KUniqueApplication() +{ + KGlobal::dirs()->addPrefix(PREFIX); + dcopClient()->registerAs("quanta", false); +} + +KQUniqueApplication::~KQUniqueApplication() +{ +} + + +int KQUniqueApplication::newInstance() +{ + args = KCmdLineArgs::parsedArgs(); + if (mainWidget()) + { + KWin::activateWindow( mainWidget()->winId() ); + quantaApp = static_cast<QuantaApp*>(mainWidget()); + quantaApp->setParserEnabled(false); + for (int i = 0; i < args->count(); i++) + { + quantaApp->slotFileOpen(args->url(i), quantaApp->defaultEncoding()); // load initial files + } + quantaApp->setParserEnabled(true); + quantaApp->reparse(true); + } + else + { + splash = 0L; + KConfig *config = kapp->config(); + config->setGroup("General Options"); + int mdiMode = config->readNumEntry("MDI mode", KMdi::IDEAlMode); + QString layout = config->readEntry("Window layout", "Default"); + if (layout == "Default" || args->isSet("resetlayout")) + { + mdiMode = KMdi::IDEAlMode; + config->writeEntry("Window layout", "Default"); + config->writeEntry("MDI mode", KMdi::IDEAlMode); + } + quantaApp = new QuantaApp(mdiMode); + config->setGroup("General Options"); + if (config->readBoolEntry("Show Splash", true) && args->isSet("logo")) + { + sp = new KSplashScreen(UserIcon(SPLASH_PICTURE)); + sp->show(); + connect(quantaApp, SIGNAL(showSplash(bool)), sp, SLOT(setShown(bool))); + QTimer::singleShot(10*1000, this, SLOT(slotSplashTimeout())); + } + setMainWidget(quantaApp); + slotInit(); + } + + return 0; +} + +void KQUniqueApplication::slotInit() +{ + KQApplicationPrivate::init(); +} + +void KQUniqueApplication::slotSplashTimeout() +{ + delete splash; + delete sp; + splash = 0L; + sp = 0L; +} + + +void KQApplicationPrivate::init() +{ + if (quantaApp->quantaStarted) + { + quantaApp->slotEnableIdleTimer(false); + quantaApp->setParserEnabled(false); //will be enabled in quantaApp->m_quantaInit->openLastFiles(); + quantaApp->m_quantaInit->initQuanta(); + quantaApp->show(); + + QString initialProject; + QStringList initialFiles; + for (int i = 0; i < args->count(); i++ ) + { + QString arg = args->url(i).url(); + + if(arg.findRev(QRegExp(".+\\.webprj")) != -1) + initialProject = arg; + else + initialFiles += arg; + } + quantaApp->m_quantaInit->loadInitialProject(initialProject); + //recoverCrashed manages the autosaved copies + quantaApp->m_quantaInit->recoverCrashed(initialFiles); + + for(QStringList::Iterator it = initialFiles.begin();it != initialFiles.end();++it) + { + KURL url; + QuantaCommon::setUrl(url, (*it)); + quantaApp->slotFileOpen(url, quantaApp->defaultEncoding()); // load initial files + } + quantaApp->m_quantaInit->openLastFiles(); + } + args->clear(); + delete splash; + splash = 0L; + delete sp; + sp = 0L; + delete quantaApp->m_quantaInit; + quantaApp->m_quantaInit = 0L; + quantaApp->slotEnableIdleTimer(true); +} + +#include "kqapp.moc" diff --git a/quanta/src/kqapp.h b/quanta/src/kqapp.h new file mode 100644 index 00000000..592d7cbc --- /dev/null +++ b/quanta/src/kqapp.h @@ -0,0 +1,79 @@ +/* + knapplication.h + + KNode, the KDE newsreader + Copyright (c) 1999-2001 the KNode authors. + See file AUTHORS for details + + Rewritten for Quanta Plus: (C) 2002, 2003 Andras Mantia <[email protected]> + + 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. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US +*/ + +#ifndef KQAPPLICATION_H +#define KQAPPLICATION_H + +#include <qframe.h> +#include <kuniqueapplication.h> + +class KCmdLineArgs; +class KSplashScreen; + +class KSplash : public QFrame +{ + Q_OBJECT + + public: + KSplash(); + ~KSplash(); +}; + +class KQApplicationPrivate +{ + public: + KQApplicationPrivate():splash(0L), sp(0L) {}; + ~KQApplicationPrivate() {}; + + protected: + void init(); + + KSplash *splash; + KCmdLineArgs *args; + KSplashScreen *sp; +}; + +class KQApplication : public KApplication, KQApplicationPrivate +{ + Q_OBJECT + + public: + KQApplication(); + ~KQApplication(); + public slots: + void slotInit(); + void slotSplashTimeout(); +}; + +class KQUniqueApplication : public KUniqueApplication, KQApplicationPrivate +{ + Q_OBJECT + + public: + KQUniqueApplication(); + ~KQUniqueApplication(); + + /** Create new instance of Quanta. Make the existing + main window active if Quanta is already running */ + int newInstance(); + public slots: + void slotInit(); + void slotSplashTimeout(); +}; + +#endif diff --git a/quanta/src/main.cpp b/quanta/src/main.cpp new file mode 100644 index 00000000..39672048 --- /dev/null +++ b/quanta/src/main.cpp @@ -0,0 +1,217 @@ +/*************************************************************************** + main.cpp - description + ------------------- + begin : ���� 9 13:29:57 EEST 2000 + copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev & Eric Laffoon <[email protected],[email protected],[email protected]> + (C) 2001-2002 Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 <stdlib.h> + +// kde includes +#include <klocale.h> +#include <kaboutdata.h> +#include <kiconloader.h> +#include <ksimpleconfig.h> +#include <kdebug.h> +#include <kcmdlineargs.h> +#include <dcopclient.h> +#include <dcopref.h> + +// qt includes +#include <qpixmap.h> +#include <qnetwork.h> +#include <qdom.h> +#include <qfile.h> +#include <qfileinfo.h> + +// app includes +#include "kqapp.h" +#include "quanta.h" + +static const char *description = + I18N_NOOP("Quanta Plus Web Development Environment"); +// INSERT A DESCRIPTION FOR YOUR APPLICATION HERE + +static const char *othertext = + I18N_NOOP("Our goal is to be nothing less than the best possible tool for\n \ +working with tagging and scripting languages.\ +\n\nQuanta Plus is not in any way affiliated with any commercial\n \ +versions of Quanta. \ +\n\nWe hope you enjoy Quanta Plus.\n\n"); + +static const char *developers = I18N_NOOP("The Quanta+ developers"); + +static KCmdLineOptions options[] = +{ + { "+[File]", I18N_NOOP("File to open"), 0 }, + { "unique", I18N_NOOP("Whether we start as a one-instance application"), 0 }, + { "nologo", I18N_NOOP("Do not show the nice logo during startup"), 0 }, + { "resetlayout", I18N_NOOP("Reset the layout of the user interface to the default"), 0}, + KCmdLineLastOption + // INSERT YOUR COMMANDLINE OPTIONS HERE +}; + +int main(int argc, char *argv[]) +{ + char *copyright = new char[200]; //this should be enough + strcpy(copyright, "(C) 2000-2006 - "); + strcat(copyright, developers); + KAboutData + aboutData( QUANTA_PACKAGE, I18N_NOOP("Quanta"), + QUANTA_VERSION, description, KAboutData::License_GPL_V2, + copyright, + othertext, + "http://kdewebdev.org" + ); + + //aboutData.otherText(&othertext); + + aboutData.addAuthor("Eric Laffoon",I18N_NOOP("Project Lead - public liaison"), "[email protected]"); + aboutData.addAuthor("Andras Mantia",I18N_NOOP("Program Lead - bug squisher"), "[email protected]"); + aboutData.addAuthor("Dmitry Poplavsky",I18N_NOOP("Inactive - left for commercial version"), "[email protected]"); + aboutData.addAuthor("Alexander Yakovlev",I18N_NOOP("Inactive - left for commercial version"), "[email protected]"); + + aboutData.addCredit("Michal Rudolf", + I18N_NOOP("Various fixes, table editor maintainer"), + "[email protected]"); + + aboutData.addCredit("Linus McCabe", + I18N_NOOP("Debugger interface and integration of the Gubed PHP debugger"), + "[email protected]"); + + aboutData.addCredit("Thiago Silva", + I18N_NOOP("Debugger interface"), + "[email protected]"); + + aboutData.addCredit("Chris Hornbaker", + I18N_NOOP("XML - compliance, tools & DTEPs"), + "[email protected]"); + + aboutData.addCredit("Dave Reddish", + I18N_NOOP("Template contributions"), + "[email protected]"); + + aboutData.addCredit("Doug Bezona", + I18N_NOOP("ColdFusion support"), + "[email protected]"); + + aboutData.addCredit("Mathieu Kooiman", + I18N_NOOP("Initial debugger work - advanced test"), + "[email protected]"); + + aboutData.addCredit("Richard Moore", + I18N_NOOP("Coding and tag dialog definition documentation and more"), + "[email protected]"); + + aboutData.addCredit("Marc Britton", + I18N_NOOP("Original plugin system, various fixes"), + "[email protected]"); + + aboutData.addCredit("Robert Nickel", + I18N_NOOP("Original documentation, many cool parsing scripts to automate \ndevelopment"), + "[email protected]"); + + aboutData.addCredit("Jason P. Hanley", + I18N_NOOP("Various fixes, foundational code for the old DTD parsing and other \nDTD related work"), + "[email protected]"); + + aboutData.addCredit("George Vilches", + I18N_NOOP("Tree based upload dialog"), + "[email protected]"); + + aboutData.addCredit("Ted Pibil", + I18N_NOOP("Addition and maintenance of DTDs"), + "[email protected]"); + + aboutData.addCredit("Nicolas Deschildre", + I18N_NOOP("Visual Page Layout part, new undo/redo system"), + "[email protected]"); + + aboutData.addCredit("Paulo Moura Guedes", + I18N_NOOP("VPL View"), + "[email protected]"); + + aboutData.addCredit("Luciano Gulmini", + I18N_NOOP("Frame wizard, CSS wizard"), + "[email protected]"); + + aboutData.addCredit("Emiliano Gulmini", + I18N_NOOP("Crash recovery"), + "[email protected]"); + + aboutData.addCredit("Jens Herden", + I18N_NOOP("Improvements to the treeview code; code review and cleanup"), + "[email protected]"); + + aboutData.addCredit("Andrea Bergia", + I18N_NOOP("Original CSS editor"), + "[email protected]"); + + aboutData.addCredit("Matthew Colton", + I18N_NOOP("Cool splash screen for many version releases of Quanta"), + "[email protected]"); + + aboutData.addCredit("Claus Hindsgaul", + I18N_NOOP("Danish translation"), + "[email protected]"); + + aboutData.addCredit("Dmitri Dmitrienko", + I18N_NOOP("Part of a code for the old PHP4 Debugger"), + "[email protected]"); + + aboutData.addCredit("Keith Isdale", + I18N_NOOP("XSLT tags"), + "[email protected]"); + + aboutData.addCredit("Lukas Masek", + I18N_NOOP("Splash screen and icon for 3.2"), + "[email protected]"); + + KCmdLineArgs::init( argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions( options ); // Add our own options. + + // this defeats the purpose of KCmdLineArgs, but there is no other + // way around, I'm afraid. + const char *uniq = "--unique"; + bool isUnique = false; + for ( int i = 1; i < argc; i++ ) { + if ( strcmp(argv[i], uniq) == 0 ) { + isUnique = true; + KUniqueApplication::addCmdLineOptions(); // before calling parsedArgs! + break; + } + } + + KApplication *app; + KApplication::disableAutoDcopRegistration(); + + if (isUnique) { + KUniqueApplication::dcopClient()->registerAs("quanta-foo"); + if (KUniqueApplication::dcopClient()->isApplicationRegistered("quanta")) + { + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + for (int i = 0; i < args->count(); i++) + { + QString s = args->url(i).url(); + DCOPRef("quanta", "WindowManagerIf").call("openFile(QString, int, int)", s, 1, 1); // Activate it + } + DCOPRef("quanta", QUANTA_PACKAGE).call("newInstance()"); + exit(0); + } + app = new KQUniqueApplication; + } else { + app = new KQApplication; + } + + return app->exec(); +} diff --git a/quanta/src/quanta.cpp b/quanta/src/quanta.cpp new file mode 100644 index 00000000..596dc7e3 --- /dev/null +++ b/quanta/src/quanta.cpp @@ -0,0 +1,5390 @@ +/*************************************************************************** + quanta.cpp - description + ------------------- + begin : ?? ??? 9 13:29:57 EEST 2000 + copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev & Eric Laffoon <[email protected],[email protected],[email protected]> + (C) 2001-2005 by Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 <time.h> + +// include files for QT +#include <qaction.h> +#include <qdragobject.h> +#include <qdir.h> +#include <qprinter.h> +#include <qpainter.h> +#include <qwidgetstack.h> +#include <qtabwidget.h> +#include <qfile.h> +#include <qlineedit.h> +#include <qcheckbox.h> +#include <qtabbar.h> +#include <qradiobutton.h> +#include <qimage.h> +#include <qtimer.h> +#include <qtextcodec.h> +#include <qtextstream.h> +#include <qtextedit.h> +#include <qiodevice.h> +#include <qcombobox.h> +#include <qdockarea.h> +#include <qdom.h> +#include <qspinbox.h> +#include <qeventloop.h> +#include <qfontmetrics.h> +#include <qclipboard.h> +#include <qptrlist.h> +#include <qbuffer.h> +#include <qdatetime.h> + + +// include files for KDE +#include <kapplication.h> +#include <kaboutdata.h> +#include <kaccelmanager.h> +#include <kbugreport.h> +#include <kcolordialog.h> +#include <kcombobox.h> +#include <kiconloader.h> +#include <kmessagebox.h> +#include <kencodingfiledialog.h> +#include <kfiledialog.h> +#include <kmenubar.h> +#include <klocale.h> +#include <kconfig.h> +#include <khtmlview.h> +#include <kdialogbase.h> +#include <kdeversion.h> +#include <kkeydialog.h> +#include <kinputdialog.h> +#include <klibloader.h> +#include <kdockwidget.h> +#include <kstatusbar.h> +#include <kpopupmenu.h> +#include <kpushbutton.h> +#include <kprocess.h> +#include <kprogress.h> +#include <ktempdir.h> +#include <ktempfile.h> +#include <ktextedit.h> +#include <kdebug.h> +#include <ktar.h> +#include <kedittoolbar.h> +#include <kaction.h> +#include <kcharsets.h> +#include <kdirwatch.h> +#include <kstandarddirs.h> +#include <ktabwidget.h> +#include <ktip.h> +#include <kmimetype.h> +#include <kparts/partmanager.h> +#include <kparts/part.h> +#include <kstringhandler.h> +#include <kstdguiitem.h> +#include <kurldrag.h> + +#include <ktexteditor/editinterface.h> +#include <ktexteditor/encodinginterface.h> +#include <ktexteditor/selectioninterface.h> +#include <ktexteditor/markinterface.h> +#include <ktexteditor/viewcursorinterface.h> +#include <ktexteditor/printinterface.h> +#include <ktexteditor/popupmenuinterface.h> +#include <ktexteditor/dynwordwrapinterface.h> +#include <ktexteditor/encodinginterface.h> +#include <ktexteditor/undointerface.h> +#include <ktexteditor/document.h> +#include <ktexteditor/view.h> +#include <ktexteditor/clipboardinterface.h> + +#include <kio/netaccess.h> + +#ifdef ENABLE_CVSSERVICE +#include "cvsservice.h" +#endif + + +// application specific includes +#include "document.h" +#include "quanta.h" +#include "quantaview.h" +#include "quantadoc.h" +#include "qextfileinfo.h" +#include "resource.h" + +#include "project.h" + +// For Kafka cut/copy/paste +#include "wkafkapart.h" + +#include "whtmlpart.h" + +#include "abbreviation.h" +#include "filemasks.h" +#include "styleoptionss.h" +#include "previewoptions.h" +#include "parseroptions.h" +#include "dtdselectdialog.h" +#include "donationdialog.h" +#include "fourbuttonmessagebox.h" +#include "specialchardialog.h" +#include "kafkasyncoptions.h" +#include "htmldocumentproperties.h" +#include "undoredo.h" + +#include "filestreeview.h" +#include "structtreeview.h" +#include "doctreeview.h" +#include "templatestreeview.h" +#include "tagattributetree.h" +#include "projecttreeview.h" +#include "scripttreeview.h" +#include "servertreeview.h" +#include "variableslistview.h" +#include "debuggerbreakpointview.h" + +#include "listdlg.h" +#include "tagdialog.h" +#include "tagmaildlg.h" +#include "tagmisc.h" +#include "tagquicklistdlg.h" +#include "tableeditor.h" + +#include "csseditor.h" +#include "cssselector.h" + +#include "framewizard.h" + +#include "debuggermanager.h" + +#include "parser.h" +#include "dtdparser.h" + +#include "annotationoutput.h" +#include "messageoutput.h" + +#include "dtepeditdlg.h" +#include "actionconfigdialog.h" +#include "toolbarxmlgui.h" +#include "tagaction.h" +#include "toolbartabwidget.h" +#include "dcopquanta.h" +#include "tagmiscdlg.h" + +#include "quantaplugininterface.h" +#include "quantaplugin.h" +#include "dtds.h" +#include "dcopsettings.h" +#include "quanta_init.h" +#include "viewmanager.h" +#include "debuggerui.h" +#include "newstuff.h" +#include "quantanetaccess.h" + +extern int NN; + +const QString resourceDir = QString(QUANTA_PACKAGE) + "/"; + +// from kfiledialog.cpp - avoid qt warning in STDERR (~/.xsessionerrors) +static void silenceQToolBar(QtMsgType, const char *){} + +QuantaApp::QuantaApp(int mdiMode) : DCOPObject("WindowManagerIf"), KMdiMainFrm( 0, "Quanta", (KMdi::MdiMode) mdiMode) + +{ + setStandardToolBarMenuEnabled(true); + createStandardStatusBarAction(); + m_quantaInit = new QuantaInit(this); + dcopSettings = new DCOPSettings; + dcopQuanta = new DCOPQuanta; + DTDs::ref(this); + quantaStarted = true; + tempFileList.setAutoDelete(true); + m_toolbarList.setAutoDelete(true); + userToolbarsCount = 0; + baseNode = 0L; + currentToolbarDTD = QString::null; + m_config=kapp->config(); + idleTimer = new QTimer(this); + connect(idleTimer, SIGNAL(timeout()), SLOT(slotIdleTimerExpired())); + m_idleTimerEnabled = true; + + qConfig.globalDataDir = KGlobal::dirs()->findResourceDir("data",resourceDir + "toolbar/quantalogo.png"); + if (qConfig.globalDataDir.isEmpty()) + { + quantaStarted = false; + kdWarning() << "***************************************************************************" << endl; + kdWarning() << i18n("Quanta data files were not found.") << endl; + kdWarning() << i18n("You may have forgotten to run \"make install\", or your KDEDIR, KDEDIRS or PATH are not set correctly.") << endl; + kdWarning() << "***************************************************************************" << endl; + QTimer::singleShot(20, kapp, SLOT(quit())); + return; + } + qConfig.enableDTDToolbar = true; + + // connect up signals from KXXsldbgPart + connectDCOPSignal(0, 0, "debuggerPositionChangedQString,int)", "newDebuggerPosition(QString,int)", false ); + connectDCOPSignal(0, 0, "editorPositionChanged(QString,int,int)", "newCursorPosition(QString,int,int)", false ); + connectDCOPSignal(0, 0, "openFile(QString,int,int)", "openFile(QString,int,int)", false); + + m_partManager = new KParts::PartManager(this); + // When the manager says the active part changes, + // the builder updates (recreates) the GUI + connect(m_partManager, SIGNAL(activePartChanged(KParts::Part * )), + this, SLOT(slotActivePartChanged(KParts::Part * ))); + connect(this, SIGNAL(dockWidgetHasUndocked(KDockWidget *)), this, SLOT(slotDockWidgetHasUndocked(KDockWidget *))); + connect(tabWidget(), SIGNAL(initiateDrag(QWidget *)), this, SLOT(slotTabDragged(QWidget*))); + + m_oldKTextEditor = 0L; + m_previewToolView = 0L; + m_documentationToolView = 0L; + m_previewedDocument = 0L; + m_previewVisible = false; + m_newDTEPStuff = 0L; + m_newToolbarStuff = 0L; + m_newTemplateStuff = 0L; + m_newScriptStuff = 0L; + m_newDocStuff = 0L; + m_debugger = 0L; + m_parserEnabled = true; + cursorLine = 0; + cursorCol = 0; + emit eventHappened("quanta_start", QDateTime::currentDateTime().toString(Qt::ISODate), QString::null); + setAcceptDrops(true); + tabWidget()->installEventFilter(this); +} + + +QuantaApp::~QuantaApp() +{ + delete m_newDTEPStuff; + m_newDTEPStuff = 0L; + delete m_newToolbarStuff; + m_newToolbarStuff = 0L; + delete m_newTemplateStuff; + m_newTemplateStuff = 0L; + delete m_newScriptStuff; + m_newScriptStuff = 0L; + delete m_newDTEPStuff; + m_newDocStuff = 0L; + // disconnect(m_htmlPart, SIGNAL(destroyed(QObject *))); + // disconnect(m_htmlPartDoc, SIGNAL(destroyed(QObject *))); + disconnect(this, SIGNAL(lastChildViewClosed()), ViewManager::ref(), SLOT(slotLastViewClosed())); + //kdDebug(24000) << "QuantaApp::~QuantaApp" << endl; +#ifdef ENABLE_CVSSERVICE + delete CVSService::ref(); +#endif + delete m_debugger; + m_debugger = 0L; + quantaApp = 0L; + delete m_doc; + m_doc = 0L; + kdDebug(24000) << "Node objects before delete :" << NN << " baseNode= " << baseNode << endl; + delete baseNode; + baseNode = 0; + delete parser; + parser = 0L; + delete idleTimer; + idleTimer = 0L; + delete m_actions; + m_actions = 0L; + cursorLine = 0; + cursorCol = 0; + tempFileList.clear(); + for (uint i = 0; i < tempDirList.count(); i++) + { + KIO::NetAccess::del(KURL().fromPathOrURL(tempDirList.at(i)->name()), this); + } + tempDirList.clear(); + QDictIterator<ToolbarEntry> iter(m_toolbarList); + ToolbarEntry *p_toolbar; + for( ; iter.current(); ++iter ) + { + p_toolbar = iter.current(); + delete p_toolbar->dom; + delete p_toolbar->menu; + delete p_toolbar->guiClient; + } + + m_toolbarList.clear(); + QStringList tmpDirs = KGlobal::dirs()->resourceDirs("tmp"); + tmpDir = tmpDirs[0]; + for (uint i = 0; i < tmpDirs.count(); i++) + { + if (tmpDirs[i].contains("kde-")) + tmpDir = tmpDirs[i]; + } + QString infoCss = tmpDir; + infoCss += "quanta/info.css"; + QFile::remove(infoCss); + QDir dir; + dir.rmdir(tmpDir + "quanta"); + + delete dcopSettings; + delete dcopQuanta; +// delete m_partManager; + + kdDebug(24000) << "Undeleted node objects :" << NN << endl; +} + +void QuantaApp::setTitle(const QString& title) +{ + QString s = title; + if (Project::ref()->hasProject()) + { + s = Project::ref()->projectName() + " : " + s; + } + setCaption(s); +} + +void QuantaApp::slotFileNew() +{ + m_doc->openDocument(KURL()); +} + +void QuantaApp::slotFileOpen() +{ + QString myEncoding = defaultEncoding(); + QString startDir; + Document *w = ViewManager::ref()->activeDocument(); + if (w && !w->isUntitled()) + startDir = w->url().url(); + else + startDir = Project::ref()->projectBaseURL().url(); + + KEncodingFileDialog::Result data; + data = KEncodingFileDialog::getOpenURLsAndEncoding(myEncoding, startDir, + "all/allfiles text/html text/xml application/x-php text/plain", this, i18n("Open File")); + slotFileOpen(data.URLs, data.encoding); +} + +void QuantaApp::slotFileOpen(const KURL::List &urls, const QString& encoding) +{ + m_doc->blockSignals(true); + m_parserEnabled = false; + for (KURL::List::ConstIterator i = urls.begin(); i != urls.end(); ++i) + { + if (!QExtFileInfo::exists(*i, true, this)) + { + KMessageBox::error(this, i18n("<qt>The file <b>%1</b> does not exist or is not a recognized mime type.</qt>").arg((*i).prettyURL(0, KURL::StripFileProtocol))); + + } else + { + if (QuantaCommon::checkMimeGroup(*i, "text") || + QuantaCommon::denyBinaryInsert(this) == KMessageBox::Yes) + slotFileOpen(*i, encoding); + } + } + m_doc->blockSignals(false); + m_parserEnabled = true; + reparse(true); + Document *w = ViewManager::ref()->activeDocument(); + if (w) + setTitle(w->url().prettyURL(0, KURL::StripFileProtocol)); +} + +void QuantaApp::slotFileOpen(const KURL &url) +{ + slotFileOpen(url, defaultEncoding()); +} + +void QuantaApp::slotFileOpen(const KURL &url, const QString& encoding) +{ + m_doc->openDocument(url, encoding); +} + +void QuantaApp::slotFileOpen(const KURL &url, const QString& encoding, bool readOnly) +{ + m_doc->openDocument(url, encoding, true, readOnly); +} + +void QuantaApp::slotFileOpenRecent(const KURL &url) +{ + if (!QExtFileInfo::exists(url, true, this)) + { + if (KMessageBox::questionYesNo(this, + i18n("The file %1 does not exist.\n Do you want to remove it from the list?").arg(url.prettyURL(0, KURL::StripFileProtocol)), QString::null, KStdGuiItem::del(), i18n("Keep")) + == KMessageBox::Yes) + { + fileRecent->removeURL(url); + } + } else + if (QuantaCommon::checkMimeGroup(url, "text") || + QuantaCommon::denyBinaryInsert(this) == KMessageBox::Yes) + { + slotFileOpen(url); + } + fileRecent->setCurrentItem(-1); + ViewManager::ref()->activeDocument()->view()->setFocus(); +} + +void QuantaApp::slotFileSave() +{ + QuantaView* view=ViewManager::ref()->activeView(); + Document *w = view->document(); + if (w) + { + w->checkDirtyStatus(); + if (w->isUntitled()) + slotFileSaveAs(); + else + { + if(ViewManager::ref()->activeView() && + ViewManager::ref()->activeView()->hadLastFocus() == QuantaView::VPLFocus) + w->docUndoRedo->reloadQuantaEditor(); + view->saveDocument(w->url()); + w->docUndoRedo->fileSaved(); + } + } +} + +bool QuantaApp::slotFileSaveAs(QuantaView *viewToSave) +{ + bool result = false; + QuantaView* view = viewToSave; + if (!view) + view = ViewManager::ref()->activeView(); + Document *w = view->document(); + if (w) + { + KURL oldURL = w->url(); + w->checkDirtyStatus(); + if (!w->isUntitled() && oldURL.isLocalFile()) + { + fileWatcher->removeFile(oldURL.path()); +// kdDebug(24000) << "removeFile[slotFileSaveAs]: " << oldURL.path() << endl; + } + + //FIXME: in katepart changing encoding saves the original file if it was modified, so it's useless in saveas... +// QString myEncoding = dynamic_cast<KTextEditor::EncodingInterface*>(w->doc())->encoding(); + + bool gotPath = false; + + KURL saveAsUrl; + + if (fTab->isVisible()) + { + saveAsUrl = fTab->currentURL(); + if (fTab->currentKFileTreeViewItem() && fTab->currentKFileTreeViewItem()->isDir()) + { + saveAsUrl.adjustPath(+1); + } + gotPath = true; + } else + if (ProjectTreeView::ref()->isVisible()) + { + saveAsUrl = ProjectTreeView::ref()->currentURL(); + if (ProjectTreeView::ref()->currentKFileTreeViewItem() && ProjectTreeView::ref()->currentKFileTreeViewItem()->isDir()) + { + saveAsUrl.adjustPath(+1); + } + gotPath = true; + } + if (!gotPath || saveAsUrl.isEmpty()) + { + if (w->isUntitled()) + { + saveAsUrl = Project::ref()->projectBaseURL(); + saveAsUrl.adjustPath(+1); + saveAsUrl.setFileName(oldURL.fileName()); + } else + saveAsUrl = oldURL; + } else + if (w->isUntitled() && !saveAsUrl.path().endsWith("/")) + { + saveAsUrl.setPath(saveAsUrl.directory(false, false) + oldURL.fileName()); + } + +//FIXME: in katepart changing encoding saves the original file if it was modified, so it's useless in saveas... + /* + KEncodingFileDialog::Result data; + data = KEncodingFileDialog::getSaveURLAndEncoding(myEncoding, saveAsUrl.url(), + "all/allfiles text/html text/xml application/x-php text/plain", this, i18n("Save File")); + KURL saveUrl = data.URLs[0]; + bool found; + QString encoding = KGlobal::charsets()->codecForName(data.encoding, found)->name(); + KTextEditor::EncodingInterface* encodingIf = dynamic_cast<KTextEditor::EncodingInterface*>(w->doc()); + if (encodingIf && encodingIf->encoding() != encoding) + encodingIf->setEncoding(encoding); + */ + KURL saveUrl = KFileDialog::getSaveURL(saveAsUrl.url(), + "all/allfiles text/html text/xml application/x-php text/plain", this, i18n("Save File")); + + if (QuantaCommon::checkOverwrite(saveUrl, this) && view->saveDocument(saveUrl)) + { + oldURL = saveUrl; + if (Project::ref()->hasProject() && !Project::ref()->contains(saveUrl) && + KMessageBox::Yes == KMessageBox::questionYesNo(0,i18n("<qt>Do you want to add the<br><b>%1</b><br>file to project?</qt>").arg(saveUrl.prettyURL(0, KURL::StripFileProtocol)), QString::null, KStdGuiItem::add(), i18n("Do Not Add")) + ) + { + if (saveUrl.isLocalFile()) + { + QDir dir(saveUrl.path()); + saveUrl.setPath(dir.canonicalPath()); + } + Project::ref()->insertFile(saveUrl, true); + } + if (view->hadLastFocus() == QuantaView::VPLFocus) + w->docUndoRedo->reloadQuantaEditor(); + + w->docUndoRedo->fileSaved(); + result = true; + } + if (oldURL.isLocalFile()) + { + fileWatcher->addFile(oldURL.path()); +// kdDebug(24000) << "addFile[slotFileSaveAs]: " << oldURL.path() << endl; + } + } + return result; +} + +void QuantaApp::saveAsTemplate(bool projectTemplate, bool selectionOnly) +{ + QuantaView *view = ViewManager::ref()->activeView(); + Document *w = view->document(); + if (!w) return; + + KURL url; + int query; + KURL projectTemplateURL; + w->checkDirtyStatus(); + QString localTemplateDir = locateLocal("data", resourceDir + "templates/"); + + do { + query = KMessageBox::Yes; + + if (projectTemplate) + { + url = KFileDialog::getSaveURL(Project::ref()->templateURL().url(), QString::null, this); + } else + { + url = KFileDialog::getSaveURL(locateLocal("data", resourceDir + "templates/"), QString::null, this); + } + + if (url.isEmpty()) return; + + if (Project::ref()->hasProject()) + projectTemplateURL = Project::ref()->templateURL(); + if ( ((projectTemplate) && (projectTemplateURL.isParentOf(url)) ) || + ((! projectTemplate) && (KURL(localTemplateDir).isParentOf(url))) ) + { + if (!QuantaCommon::checkOverwrite(url, this)) + query = KMessageBox::No; + } else + { + if (projectTemplate) + localTemplateDir = projectTemplateURL.path(1); + KMessageBox::sorry(this,i18n("You must save the templates in the following folder: \n\n%1").arg(localTemplateDir)); + query = KMessageBox::No; + } + } while (query != KMessageBox::Yes); + + if (query == KMessageBox::Cancel) return; + + if (selectionOnly && w->selectionIf) + { + KTempFile *tempFile = new KTempFile(tmpDir); + tempFile->setAutoDelete(true); + QString content; + content = w->selectionIf->selection(); + QTextStream stream(tempFile->file()); + stream.setEncoding(QTextStream::UnicodeUTF8); + stream << content; + tempFile->file()->flush(); + tempFile->close(); + if (!QExtFileInfo::copy(KURL::fromPathOrURL(tempFile->name()), url, -1, true, false, this)) + KMessageBox::error(this, i18n("<qt>There was an error while creating the template file.<br>Check that you have write access to <i>%1</i>.</qt>").arg(url.prettyURL(0, KURL::StripFileProtocol)), i18n("Template Creation Error")); + delete tempFile; + } else + { + view->saveDocument(url); + } + + if (projectTemplate) + Project::ref()->insertFile(url, true); + if(ViewManager::ref()->activeView() && + ViewManager::ref()->activeView()->hadLastFocus() == QuantaView::VPLFocus) + w->docUndoRedo->reloadQuantaEditor(); + + w->docUndoRedo->fileSaved(); +} + +void QuantaApp::slotFileSaveAsLocalTemplate() +{ + saveAsTemplate(false); +} + +void QuantaApp::slotFileSaveAsProjectTemplate() +{ + saveAsTemplate(true); +} + + +void QuantaApp::slotFileSaveSelectionAsLocalTemplate() +{ + saveAsTemplate(false, true); +} + +void QuantaApp::slotFileSaveSelectionAsProjectTemplate() +{ + saveAsTemplate(true, true); +} + +void QuantaApp::slotFileSaveAll() +{ + ViewManager::ref()->saveAll(); +} + +void QuantaApp::slotFileReload(QuantaView *view) +{ + if (!view) + view = ViewManager::ref()->activeView(); + Document *w = view->document(); + if (!w || w->isUntitled() || !view->saveModified()) + return; + w->setModified(false); + unsigned int line, col; + w->viewCursorIf->cursorPosition(&line, &col); + if (w->openURL(w->url())) + w->viewCursorIf->setCursorPosition(line, col); + reparse(true); +} + +void QuantaApp::slotFileReloadAll() +{ +//TODO: Implement it! +} + +void QuantaApp::slotFileClose(const KURL &url) +{ + QuantaView *view = ViewManager::ref()->isOpened(url); + if (view) + { + ViewManager::ref()->removeView(view); + } +} + + +void QuantaApp::slotFileCloseAll() +{ + ViewManager::ref()->closeAll(); + WHTMLPart *part = m_htmlPart; + part->closeURL(); + part->begin(Project::ref()->projectBaseURL()); + part->write(" "); + part->end(); + + slotNewStatus(); +} + +void QuantaApp::slotFileQuit() +{ + close(); +} + + +void QuantaApp::slotEditFindInFiles() +{ + QuantaPlugin *fileReplacePlugin = m_pluginInterface->plugin("KFileReplace"); + if (fileReplacePlugin) + fileReplacePlugin->run(); +} + + +void QuantaApp::slotHelpTip() +{ + KTipDialog::showTip(this, QString::null, true); +} + +void QuantaApp::slotStatusMsg(const QString &msg) +{ + statusbarTimer->stop(); + statusBar()->changeItem(" " + KStringHandler::cPixelSqueeze(msg, statusBar()->fontMetrics(), progressBar->x() - 20), IDS_STATUS); + statusBar()->repaint(); + kapp->processEvents(QEventLoop::ExcludeUserInput | QEventLoop::ExcludeSocketNotifiers); + statusbarTimer->start(10000, true); +} + +/** repaint preview */ +void QuantaApp::slotRepaintPreview() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + if (!m_previewVisible) return; + if (!m_previewToolView && qConfig.previewPosition != "Editor" ) return; + + m_previewedDocument = 0L; + previewCopyMade = false; + + KHTMLView *html = m_htmlPart->view(); + int xOffset = html->contentsX(), yOffset = html->contentsY(); + + m_htmlPart->closeURL(); + KParts::BrowserExtension *browserExtension = KParts::BrowserExtension::childObject(m_htmlPart); + KParts::URLArgs args(true, browserExtension->xOffset(), browserExtension->yOffset()); + browserExtension->setURLArgs( args ); + QString encoding = defaultEncoding(); + KTextEditor::EncodingInterface* encodingIf = dynamic_cast<KTextEditor::EncodingInterface*>(w->doc()); + if (encodingIf) + encoding = encodingIf->encoding(); + + KURL url; + m_htmlPart->setEncoding(encoding, true); + QStringList list; + if (m_noFramesPreview) + { + list = w->tagAreas("frameset", true, true); + if (list.isEmpty() || w->editIf->text().isEmpty()) + m_noFramesPreview = false; + else + { + m_htmlPart->closeURL(); + QStringList noframearea = w->tagAreas("noframes", false, true); + //find the frameset area + int bl, bc, el, ec; + QStringList l = QStringList::split('\n', list[0], true); + QStringList coordList = QStringList::split(',', l[0], true); + bl = coordList[0].toInt(); + bc = coordList[1].toInt(); + el = coordList[2].toInt(); + ec = coordList[3].toInt(); + QString noFramesText = w->text(0,0, bl, bc - 1); + noFramesText += noframearea[0]; + noFramesText += w->text(el, ec + 1, w->editIf->numLines() - 1, w->editIf->lineLength(w->editIf->numLines() - 1)); + noFramesText.replace(QRegExp("</?noframes[^>]*>", false), ""); + //kdDebug(24000) << "NOFRAMES: " << noFramesText << endl; + if (w->isUntitled()) + m_htmlPart->begin(Project::ref()->projectBaseURL(), xOffset, yOffset); + else + { + url = Project::ref()->urlWithPrefix(w->url()); + m_htmlPart->setPreviewedURL(url); + m_htmlPart->begin(url, xOffset, yOffset); + } + m_htmlPart->write(noFramesText); + m_htmlPart->end(); + } + } + + if (!m_noFramesPreview) + { + m_htmlPart->closeURL(); + QString text = w->editIf->text(); + if (text.isEmpty()) + { + text = i18n("<center><h3>The current document is empty...</h3></center>"); + } + if (w->isUntitled()) + { + m_htmlPart->begin(Project::ref()->projectBaseURL(), xOffset, yOffset); + m_htmlPart->write(text); + m_htmlPart->end(); + } else + { + m_previewedDocument = w; + url = Project::ref()->urlWithPrefix(w->url()); + m_htmlPart->setPreviewedURL(url); + KURL previewURL = w->url(); + previewURL.setFileName("preview-" + previewURL.fileName()); + //save the content to disk, so preview with prefix works + KTempFile *tmpFile = new KTempFile(tmpDir); + QString tempFileName = QFileInfo(*(tmpFile->file())).filePath(); + tmpFile->setAutoDelete(true); + QString encoding = quantaApp->defaultEncoding(); + KTextEditor::EncodingInterface* encodingIf = dynamic_cast<KTextEditor::EncodingInterface*>(w->doc()); + if (encodingIf) + encoding = encodingIf->encoding(); + if (encoding.isEmpty()) + encoding = "utf8"; //final fallback + tmpFile->textStream()->setCodec(QTextCodec::codecForName(encoding)); + *(tmpFile->textStream()) << w->editIf->text(); + tmpFile->close(); + if (!QExtFileInfo::copy(KURL::fromPathOrURL(tempFileName), previewURL, -1, true)) { + m_htmlPart->setPreviewedURL(KURL()); // copy failed, force the preview of the original + }; + delete tmpFile; + m_htmlPart->openURL(url); + m_htmlPart->addToHistory(url.url()); + } + } + m_htmlPart->show(); +} + +void QuantaApp::slotOpenFileInPreview(const KURL& a_url) +{ + WHTMLPart *part = m_htmlPart; + if (!part) + return; + slotShowPreviewWidget(true); + part->openURL(a_url); + part->show(); +} + +/** view image in preview */ +void QuantaApp::slotImageOpen(const KURL& url) +{ + slotShowPreviewWidget(true); + WHTMLPart *part = m_htmlPart; + QString text = "<html>\n<body>\n<div align=\"center\">\n<img src=\""; + text += url.fileName(); //TODO + text += "\">\n</div>\n</body>\n</html>\n"; + part->closeURL(); + KURL docURL = url; + docURL.setFileName("imagepreview.html"); + part->begin(docURL); + part->write(text); + part->end(); + + part->show(); +} + + +/** insert <img> tag for images or <a> for other */ +void QuantaApp::slotInsertTag(const KURL& url, DirInfo dirInfo) +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + KURL baseURL ; + if (w->isUntitled() ) + { + baseURL = Project::ref()->projectBaseURL(); + } else + { + baseURL = w->url(); + baseURL.setFileName(""); + } + KURL relURL = QExtFileInfo::toRelative(url, baseURL); + QString urlStr = relURL.url(); + if (relURL.protocol() == baseURL.protocol()) + urlStr = relURL.path(); + bool isImage = false; + + if (!dirInfo.preText.isEmpty() || !dirInfo.postText.isEmpty()) + { + w->insertTag(dirInfo.preText+urlStr+dirInfo.postText); + } else + { + QString mimetype = KMimeType::findByURL(url)->name(); + if (mimetype.contains("image")) + { + QString imgFileName; + KIO::NetAccess::download(url, imgFileName, this); + QImage img(imgFileName); + if (!img.isNull()) + { + QString width,height; + width.setNum(img.width()); + height.setNum(img.height()); + QString imgTag = QuantaCommon::tagCase("<img "); + imgTag += QuantaCommon::attrCase("src="); + imgTag += QuantaCommon::quoteAttributeValue(urlStr); + imgTag += QuantaCommon::attrCase(" width="); + imgTag += QuantaCommon::quoteAttributeValue(width); + imgTag += QuantaCommon::attrCase(" height="); + imgTag += QuantaCommon::quoteAttributeValue(height); + if (w->currentDTD(true)->singleTagStyle == "xml") + imgTag += " />"; + else + imgTag += ">"; + w->insertTag(imgTag); + isImage = true; + } + KIO::NetAccess::removeTempFile(imgFileName); + } + if (!isImage) + { + QString tag = QuantaCommon::tagCase("<a "); + tag += QuantaCommon::attrCase("href="); + tag += QuantaCommon::quoteAttributeValue(urlStr); + tag += ">"; + w->insertTag(tag, QuantaCommon::tagCase("</a>")); + } + } + w->view()->setFocus(); + } +} + +void QuantaApp::slotNewStatus() +{ + fileRecent->setEnabled(true); + actionCollection()->action("project_open_recent")->setEnabled(true); + QuantaView *view = ViewManager::ref()->activeView(); + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + setTitle( w->url().prettyURL(0, KURL::StripFileProtocol) ); + + if (w->doc()->isReadWrite()) + { + KToggleAction *a = dynamic_cast<KToggleAction*>(w->view()->actionCollection()->action("set_insert")); + if (a) + { + statusBar()->changeItem(a->isChecked() ? i18n(" OVR ") : i18n(" INS "),IDS_INS_OVR); + } + } + else + statusBar()->changeItem(i18n(" R/O "),IDS_INS_OVR); + statusBar()->changeItem(w->isModified() ? " * " : "",IDS_MODIFIED); + + saveAction->setEnabled(w->isModified()); + saveAllAction->setEnabled(ViewManager::ref()->isOneModified()); + + bool projectExists = Project::ref()->hasProject(); + + actionCollection()->action("toolbars_load_project")->setEnabled(projectExists); + actionCollection()->action("toolbars_save_project")->setEnabled(projectExists); + } + if (view) + { + view->updateTab(); + } +} + +void QuantaApp::slotOptionsConfigureKeys() +{ + Document *w = ViewManager::ref()->activeDocument(); + KKeyDialog dlg( false, this ); + QPtrList<KXMLGUIClient> toolbarGuiClients; + QDictIterator<ToolbarEntry> iter(m_toolbarList); + for( ; iter.current(); ++iter ) + { + toolbarGuiClients.append(iter.current()->guiClient); + } + QPtrList<KXMLGUIClient> clients = guiFactory()->clients(); + for( QPtrListIterator<KXMLGUIClient> it( clients ); + it.current(); ++it ) + { + if (toolbarGuiClients.contains(*it) <= 0) //no need to insert the collections of the toolbars as they are present in the main actionCollection + dlg.insert((*it)->actionCollection()); + } + if (dlg.configure() == KKeyDialog::Accepted) + { + // this is needed for when we have multiple embedded kateparts and change one of them. + // it also needs to be done to their views, as they too have actioncollections to update + if (const QPtrList<KParts::Part> * partlist = m_partManager->parts()) + { + QPtrListIterator<KParts::Part> it(*partlist); + while (KParts::Part* part = it.current()) + { + if (KTextEditor::Document *doc = dynamic_cast<KTextEditor::Document*>(part)) + { + KActionPtrList actionList = doc->actionCollection()->actions(); + KActionPtrList::Iterator actionIt; + if (!w || w->doc() != doc) + { + for ( actionIt = actionList.begin(); actionIt != actionList.end(); ++actionIt) + { + (*actionIt)->setShortcut((*actionIt)->shortcutDefault()); + } + } + doc->reloadXML(); + + QPtrList<KTextEditor::View> const & list = doc->views(); + QPtrListIterator<KTextEditor::View> itt( list ); + while (KTextEditor::View * view = itt.current()) + { + if (!w || w->view() != view) + { + actionList = view->actionCollection()->actions(); + for (actionIt = actionList.begin(); actionIt != actionList.end(); ++actionIt) + { + (*actionIt)->setShortcut((*actionIt)->shortcutDefault()); + } + } + view->reloadXML(); + ++itt; + } + } + ++it; + } + } + + QDomDocument doc; + doc.setContent(KXMLGUIFactory::readConfigFile(xmlFile(), instance())); + QDomNodeList nodeList = doc.elementsByTagName("ActionProperties"); + QDomNode node = nodeList.item(0).firstChild(); + while (!node.isNull()) + { + if (node.nodeName() == "Action") + { + TagAction *action = dynamic_cast<TagAction*>(actionCollection()->action(node.toElement().attribute("name"))); + if (action) + { + action->setModified(true); + QDomElement el = action->data(); + el.setAttribute("shortcut", action->shortcut().toString()); + el = node.toElement(); + node = node.nextSibling(); + el.parentNode().removeChild(el); + } else + { + node = node.nextSibling(); + } + } + } + } +} + +void QuantaApp::slotConfigureToolbars(const QString& defaultToolbar) +{ + currentPageIndex = ToolbarTabWidget::ref()->currentPageIndex(); + QDomNodeList nodeList; + ToolbarEntry *p_toolbar = 0L; + + saveMainWindowSettings(KGlobal::config(), autoSaveGroup()); + KEditToolbar *dlg; + if (defaultToolbar) + dlg = new KEditToolbar(defaultToolbar, factory(), this); + else + dlg = new KEditToolbar(factory(), this); + + KMenuBar *mb = menuBar(); + KActionCollection *ac = actionCollection(); + //remove the manually added menus BEFORE the dlg shows up + if (m_debugger->UI()) + { + m_debugger->UI()->hideMenu(); + } + for (uint i = 0 ; i < mb->count(); i++) + { + if (mb->text(mb->idAt(i)) == i18n("&Window")) + { + mb->removeItem(mb->idAt(i)); + break; + } + } + ToolbarTabWidget *tb = ToolbarTabWidget::ref(); + QString toolbarId; + for (int i = 0; i < tb->count(); i++) + { + toolbarId = tb->id(i); + p_toolbar = quantaApp->m_toolbarList[toolbarId]; + if (p_toolbar) + { + delete p_toolbar->menu; + p_toolbar->menu = 0L; + } + } + + connect(dlg, SIGNAL(newToolbarConfig()), SLOT(slotNewToolbarConfig())); + dlg->exec(); + delete dlg; + QPopupMenu *menu = 0L; + m_tagsMenu = static_cast<QPopupMenu*>(factory()->container("tags", this)); + QString toolbarName; + for (int i = 0; i < tb->count(); i++) + { + toolbarName = tb->label(i); + toolbarId = tb->id(i); + p_toolbar = quantaApp->m_toolbarList[toolbarId]; + if (p_toolbar) + { + menu = new QPopupMenu(m_tagsMenu); + nodeList = p_toolbar->guiClient->domDocument().elementsByTagName("Action"); + for (uint i = 0; i < nodeList.count(); i++) + { + KAction *action = ac->action(nodeList.item(i).toElement().attribute("name")); + if (action) + action->plug(menu); + } + + m_tagsMenu->insertItem(toolbarName,menu); + p_toolbar->menu = menu; + } + } + + //add back the menus + m_pluginInterface->setPluginMenu(static_cast<QPopupMenu*>(factory()->container("plugins", this))); + m_pluginInterface->buildPluginMenu(); + for (uint i = 0 ; i < mb->count(); i++) + { + if (mb->text(mb->idAt(i)) == i18n("&Settings")) + { + mb->insertItem(i18n("&Window"), windowMenu(), -1, i); + break; + } + } + if (m_debugger->UI()) + { + m_debugger->UI()->showMenu(); + } + tb->setCurrentPage(currentPageIndex); +} + +void QuantaApp::slotOptionsConfigureToolbars() +{ + slotConfigureToolbars(); +} + +void QuantaApp::slotNewToolbarConfig() +{ + applyMainWindowSettings(KGlobal::config(), autoSaveGroup()); + ToolbarTabWidget::ref()->setCurrentPage(currentPageIndex); +} + +void QuantaApp::slotShowMenuBar() +{ + if (menuBar()->isVisible()) + menuBar()->hide(); + else + menuBar()->show(); +} + +void QuantaApp::slotOptionsConfigureActions() +{ + ActionConfigDialog dlg(m_toolbarList, this, "actions_config_dlg", true); + dlg.exec(); +} + +void QuantaApp::slotPreviewOptions() +{ + KMessageBox::information(this, i18n("Changes made in the preview configuration dialog are global and have effect on every application using the KHTML part to display web pages, including Konqueror."), i18n("Warning"), "configure_preview_warning"); + KApplication::startServiceByDesktopName("quanta_preview_config"); +} + +void QuantaApp::slotOptions() +{ + KDialogBase *kd = new KDialogBase(KDialogBase::IconList, + i18n("Configure Quanta"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, this, "tabdialog"); + + // Tag Style options + QVBox *page=kd->addVBoxPage(i18n("Tag Style"), QString::null, BarIcon("kwrite", KIcon::SizeMedium)); + StyleOptionsS *styleOptionsS = new StyleOptionsS( (QWidget *)page); + + styleOptionsS->tagCase->setCurrentItem( qConfig.tagCase); + styleOptionsS->attributeCase->setCurrentItem( qConfig.attrCase); + styleOptionsS->attributeQuotation->setCurrentItem( (qConfig.attrValueQuotation == '"') ? 0 : 1); + styleOptionsS->tagAutoClose->setChecked( qConfig.closeTags ); + styleOptionsS->optionalTagAutoClose->setChecked( qConfig.closeOptionalTags ); + styleOptionsS->useAutoCompletion->setChecked( qConfig.useAutoCompletion ); + styleOptionsS->tagUpdateClosing->setChecked(qConfig.updateClosingTags); + styleOptionsS->replaceAccented->setChecked(qConfig.replaceAccented); + + // Environment options + //TODO FileMasks name is not good anymore + page=kd->addVBoxPage(i18n("Environment"), QString::null, UserIcon("files", KIcon::SizeMedium ) ); + FileMasks *fileMasks = new FileMasks((QWidget *)page); + + fileMasks->lineMarkup->setText( qConfig.markupMimeTypes ); + fileMasks->lineScript->setText( qConfig.scriptMimeTypes ); + fileMasks->lineImage->setText( qConfig.imageMimeTypes ); + fileMasks->lineText->setText( qConfig.textMimeTypes ); + fileMasks->showDTDSelectDialog->setChecked(qConfig.showDTDSelectDialog); + m_config->setGroup("General Options"); + fileMasks->showSplash->setChecked(m_config->readBoolEntry("Show Splash", true)); + fileMasks->reloadProject->setChecked(m_config->readBoolEntry("Reload Project", true)); + fileMasks->reloadFiles->setChecked(m_config->readBoolEntry("Reload Files", true)); + if(!m_config->readEntry("Autosave interval").isEmpty()) + fileMasks->sbAutoSave->setValue(m_config->readNumEntry("Autosave interval")); + //else default value 15 + + QStringList availableEncodingNames(KGlobal::charsets()->availableEncodingNames()); + fileMasks->encodingCombo->insertStringList( availableEncodingNames ); + QStringList::ConstIterator iter; + int iIndex = -1; + for (iter = availableEncodingNames.begin(); iter != availableEncodingNames.end(); ++iter) + { + ++iIndex; + if ((*iter).lower() == qConfig.defaultEncoding.lower()) + { + fileMasks->encodingCombo->setCurrentItem(iIndex); + break; + } + } + QStringList lst = DTDs::ref()->nickNameList(true); + uint pos = 0; + for (uint i = 0; i < lst.count(); i++) + { + fileMasks->defaultDTDCombo->insertItem(lst[i]); + if (lst[i] == DTDs::ref()->getDTDNickNameFromName(qConfig.defaultDocType.lower())) + pos = i; + } + fileMasks->defaultDTDCombo->setCurrentItem(pos); + + // Preview options + page=kd->addVBoxPage(i18n("User Interface"), QString::null, BarIcon("view_choose", KIcon::SizeMedium ) ); + PreviewOptions *uiOptions = new PreviewOptions( (QWidget *)page ); + + uiOptions->setPosition(qConfig.previewPosition); + uiOptions->setDocPosition(qConfig.docPosition); + uiOptions->setWindowLayout(qConfig.windowLayout); + uiOptions->setCloseButtons(qConfig.showCloseButtons); + uiOptions->setToolviewTabs(qConfig.toolviewTabs); + uiOptions->setHiddenFiles(qConfig.showHiddenFiles); + uiOptions->setSaveTrees(qConfig.saveTrees); + if (m_config->hasGroup("Notification Messages")) + { + m_config->setGroup("Notification Messages"); + uiOptions->warnBinaryOpening->setChecked(m_config->readEntry("Open Everything") != "yes"); + uiOptions->warnEventActions->setChecked((m_config->readEntry("Warn about internal actions", "yes") != "yes") && (m_config->readEntry("Warn about external actions", "yes") != "yes")); + uiOptions->warnAll->setChecked(false); + } else + { + uiOptions->warnAll->setChecked(true); + uiOptions->warnBinaryOpening->setChecked(true); + uiOptions->warnEventActions->setChecked(true); + } + //kafka options + page = kd->addVBoxPage(i18n("VPL View"), QString::null, UserIcon("vpl_text", KIcon::SizeMedium)); + KafkaSyncOptions *kafkaOptions = new KafkaSyncOptions( m_config, (QWidget *)page ); + + page=kd->addVBoxPage(i18n("Parser"), QString::null, BarIcon("kcmsystem", KIcon::SizeMedium ) ); + ParserOptions *parserOptions = new ParserOptions( m_config, (QWidget *)page ); + + parserOptions->refreshFrequency->setValue(qConfig.refreshFrequency); + parserOptions->instantUpdate->setChecked(qConfig.instantUpdate); + parserOptions->showEmptyNodes->setChecked(qConfig.showEmptyNodes); + parserOptions->showClosingTags->setChecked(qConfig.showClosingTags); + parserOptions->spinExpand->setValue(qConfig.expandLevel); + + page = kd->addVBoxPage(i18n("Abbreviations"), QString::null, BarIcon("fontsizeup", KIcon::SizeMedium)); + AbbreviationDlg *abbreviationOptions = new AbbreviationDlg((QWidget*)(page)); + + bool reloadTrees = false; + kd->adjustSize(); + if ( kd->exec() ) + { + bool uiRebuildNeeded = false; + bool previewSettingsChanged = false; + + qConfig.tagCase = styleOptionsS->tagCase->currentItem(); + qConfig.attrCase = styleOptionsS->attributeCase->currentItem(); + qConfig.attrValueQuotation = styleOptionsS->attributeQuotation->currentItem() == 0 ? '"': '\''; + qConfig.closeTags = styleOptionsS->tagAutoClose->isChecked(); + qConfig.closeOptionalTags = styleOptionsS->optionalTagAutoClose->isChecked(); + qConfig.useAutoCompletion = styleOptionsS->useAutoCompletion->isChecked(); + qConfig.updateClosingTags = styleOptionsS->tagUpdateClosing->isChecked(); + qConfig.replaceAccented = styleOptionsS->replaceAccented->isChecked(); + + qConfig.markupMimeTypes = fileMasks->lineMarkup->text(); + qConfig.scriptMimeTypes = fileMasks->lineScript->text(); + qConfig.imageMimeTypes = fileMasks->lineImage->text(); + qConfig.textMimeTypes = fileMasks->lineText->text(); + qConfig.showDTDSelectDialog = fileMasks->showDTDSelectDialog->isChecked(); + qConfig.autosaveInterval = fileMasks->sbAutoSave->value(); + autosaveTimer->start(60000 * qConfig.autosaveInterval, false); + m_config->setGroup("General Options"); + m_config->writeEntry("Show Splash", fileMasks->showSplash->isChecked()); + m_config->writeEntry("Reload Project", fileMasks->reloadProject->isChecked()); + m_config->writeEntry("Reload Files", fileMasks->reloadFiles->isChecked()); + + qConfig.defaultEncoding = fileMasks->encodingCombo->currentText(); + QString tmpStr = uiOptions->closeButtons(); + if (tmpStr != qConfig.showCloseButtons) + uiRebuildNeeded = true; + qConfig.showCloseButtons = tmpStr; + uint tmp = uiOptions->toolviewTabs(); + if (tmp != qConfig.toolviewTabs) + uiRebuildNeeded = true; + qConfig.toolviewTabs = tmp; + if (uiRebuildNeeded) + { + initTabWidget(); + } + reloadTrees = (qConfig.showHiddenFiles != uiOptions->hiddenFiles()); + qConfig.showHiddenFiles = uiOptions->hiddenFiles(); + qConfig.saveTrees = uiOptions->saveTrees(); + if (uiOptions->warnAll->isChecked()) + m_config->deleteGroup("Notification Messages"); + else + { + m_config->setGroup("Notification Messages"); + m_config->writeEntry("Open Everything", uiOptions->warnBinaryOpening->isChecked() ? "" : "yes"); + m_config->writeEntry("Warn about internal actions", uiOptions->warnEventActions->isChecked() ? "" : "yes"); + m_config->writeEntry("Warn about external actions", uiOptions->warnEventActions->isChecked() ? "" : "yes"); + } + + qConfig.showEmptyNodes = parserOptions->showEmptyNodes->isChecked(); + qConfig.showClosingTags = parserOptions->showClosingTags->isChecked(); + qConfig.instantUpdate = parserOptions->instantUpdate->isChecked(); + qConfig.refreshFrequency = parserOptions->refreshFrequency->value(); + if (!qConfig.instantUpdate && qConfig.refreshFrequency > 0) + { + refreshTimer->changeInterval(qConfig.refreshFrequency*1000); + } else + { + refreshTimer->stop(); + } + qConfig.expandLevel = parserOptions->spinExpand->value(); + parserOptions->updateConfig(); + + kafkaOptions->updateConfig(); + qConfig.quantaRefreshOnFocus = kafkaOptions->sourceFocusRefresh->isChecked(); + qConfig.quantaRefreshDelay = kafkaOptions->sourceDelay->value(); + qConfig.kafkaRefreshOnFocus = kafkaOptions->kafkaFocusRefresh->isChecked(); + qConfig.kafkaRefreshDelay = kafkaOptions->kafkaDelay->value(); + QuantaView *view = ViewManager::ref()->activeView(); + if (view && view->document()) + view->reloadUpdateTimers(); + /**(static_cast<HTMLEnhancer *>(quantaApp->view()->getKafkaInterface()->mainEnhancer))-> + showIconsForScripts(kafkaOptions->showScriptsIcon->isChecked());*/ + + + qConfig.defaultDocType = DTDs::ref()->getDTDNameFromNickName(fileMasks->defaultDTDCombo->currentText()); + + abbreviationOptions->saveTemplates(); + + tmpStr = uiOptions->position(); + if (tmpStr != qConfig.previewPosition) + { + if (m_previewVisible || m_previewToolView) + previewSettingsChanged = true; + slotShowPreviewWidget(false); + } + qConfig.previewPosition = tmpStr; + if (previewSettingsChanged) + slotShowPreviewWidget(true); + qConfig.docPosition = uiOptions->docPosition(); + qConfig.windowLayout = uiOptions->layout(); + + m_htmlPart->closeURL(); + m_htmlPart->begin( Project::ref()->projectBaseURL()); + m_htmlPart->write(" "); + m_htmlPart->end(); + + reparse(true); + slotNewStatus(); + } + + m_config->sync(); + + saveOptions(); + + delete kd; + if (reloadTrees) emit reloadAllTrees(); + +} + +void QuantaApp::slotShowPreviewWidget(bool show) +{ + QuantaView *view = ViewManager::ref()->activeView(); + if (!view) return; + if (m_previewVisible == show) return; + if (show) + { + if (qConfig.previewPosition == "Editor") + { + delete m_previewToolView; + m_previewToolView = 0L; + view->addCustomWidget(m_htmlPart->view(), QString::null); + } else + { + if (!m_previewToolView) + { + m_previewToolView= addToolWindow(m_htmlPart->view(), prevDockPosition(m_htmlPart->view(), KDockWidget::DockBottom), getMainDockWidget()); + connect(m_previewToolView->wrapperWidget(), SIGNAL(iMBeingClosed +()), this, SLOT(slotPreviewBeingClosed())); + } + m_htmlPart->view()->show(); + m_previewToolView->show(); + } + m_previewVisible = true; + slotRepaintPreview(); + } else + { + m_noFramesPreview = false; + m_previewVisible = false; + m_htmlPart->view()->reparent(this, 0, QPoint(), false); + m_htmlPart->view()->resize(0, 0); + m_htmlPart->view()->hide(); + if (qConfig.previewPosition == "Editor") + { + view->addCustomWidget(0L, QString::null); + delete m_previewToolView; + m_previewToolView = 0L; + } else + { + delete m_previewToolView; + m_previewToolView = 0L; + } + if (m_previewedDocument) + { + KURL url = m_previewedDocument->url(); + url.setFileName("preview-" + url.fileName()); + KIO::NetAccess::del(url, this); + Document *w = view->document(); + if (w) + w->view()->setFocus(); + } + } + + KToggleAction *ta = 0L; + int viewLayout = view->currentViewsLayout(); + if (viewLayout == QuantaView::SourceOnly) + ta = (KToggleAction *) actionCollection()->action( "show_quanta_editor" ); + else if (viewLayout == QuantaView::VPLOnly) + ta = (KToggleAction *) actionCollection()->action( "show_kafka_view" ); + else if (viewLayout == QuantaView::SourceAndVPL) + { + ta = (KToggleAction *) actionCollection()->action( "show_kafka_and_quanta" ); + } + if (ta) + { + ta->setChecked(!show); + } +} + +void QuantaApp::slotChangePreviewStatus() +{ + if (qConfig.previewPosition == "Editor") + { + slotShowPreviewWidget(false); + } else + if (m_previewToolView && m_htmlPart->view()->isVisible()) + { + //hiding the preview when it's in a toolview means that the current tab has changed, + //so we just repaint the content and restore the document on the disc. + m_previewVisible = true; + if (m_previewedDocument) + { + KURL url = m_previewedDocument->url(); + url.setFileName("preview-" + url.fileName()); + KIO::NetAccess::del(url, this); + } + slotRepaintPreview(); + m_previewedDocument = 0L; + Document *w = ViewManager::ref()->activeDocument(); + if (w) + w->view()->setFocus(); + } +} + +void QuantaApp::slotPreviewHasFocus(bool focus) +{ + if (m_previewToolView) + { + if (focus) + slotRepaintPreview(); + else + { + if (m_previewedDocument) + { + KURL url = m_previewedDocument->url(); + url.setFileName("preview-" + url.fileName()); + KIO::NetAccess::del(url, this); + } + m_previewedDocument = 0L; + } + } +} + +void QuantaApp::slotToggleShowPreview() +{ + Document *w =ViewManager::ref()->activeDocument(); + if (!w) + { + m_previewVisible = false; + return; + } + if (m_previewToolView) + { + m_previewVisible = m_htmlPart->view()->isVisible(); + } + slotShowPreviewWidget(!m_previewVisible); + m_noFramesPreview = false; +} + +void QuantaApp::slotShowNoFramesPreview() +{ + m_noFramesPreview = true; + slotToggleShowPreview(); +} + + +void QuantaApp::newCursorPosition(const QString &file, int lineNumber, int columnNumber) +{ + Q_UNUSED(file); + typingInProgress = true; + startIdleTimer(); + // updateTreeViews(); + QString linenumber; + linenumber = i18n("Line: %1 Col: %2").arg(lineNumber).arg(columnNumber); + statusBar()->changeItem(linenumber, IDS_STATUS_CLM); + statusBar()->changeItem(i18n(" R/O "),IDS_INS_OVR); + statusBar()->changeItem("",IDS_MODIFIED); +} + +void QuantaApp::newDebuggerPosition(const QString &file, int lineNumber) +{ + newCursorPosition(file, lineNumber, 0); +} + +void QuantaApp::openFile(const QString &file, int lineNumber, int columnNumber) +{ + gotoFileAndLine(file, lineNumber, columnNumber); + slotNewStatus(); +} + +void QuantaApp::slotNewLineColumn() +{ + typingInProgress = true; + startIdleTimer(); + // updateTreeViews(); + QString linenumber; + oldCursorLine = cursorLine; + oldCursorCol = cursorCol; + Document *w = ViewManager::ref()->activeDocument(); + if (w) + w->viewCursorIf->cursorPosition(&cursorLine, &cursorCol); + linenumber = i18n("Line: %1 Col: %2").arg(cursorLine+1).arg(cursorCol+1); + statusBar()->changeItem(linenumber, IDS_STATUS_CLM); +} + +void QuantaApp::updateTreeViews() +{ + QuantaView *view = ViewManager::ref()->activeView(); + if (!view) + return; + Document *w = view->document(); + if (w) + { + w->viewCursorIf->cursorPositionReal(&cursorLine, &cursorCol); + Node *node = parser->nodeAt(cursorLine, cursorCol); + if (node) + { + StructTreeView::ref()->showTagAtPos(node); + } + if(view->hadLastFocus() == QuantaView::SourceFocus) + aTab->setCurrentNode(node); + } +} + +void QuantaApp::slotIdleTimerExpired() +{ + if (idleTimer) + { + typingInProgress = false; + updateTreeViews(); + } +} + +void QuantaApp::startIdleTimer() +{ + if (m_idleTimerEnabled && idleTimer) + idleTimer->start(500, true); +} + +bool QuantaApp::slotEnableIdleTimer(bool enable) +{ + bool status = m_idleTimerEnabled; + if (enable) + startIdleTimer(); + else + if (idleTimer) + idleTimer->stop(); + m_idleTimerEnabled = enable; + return status; +} + +void QuantaApp::slotReparse() +{ + reparse(false); +} + +void QuantaApp::slotForceReparse() +{ + reparse(true); +} + +/** reparse current document and initialize node. */ +void QuantaApp::reparse(bool force) +{ + if (!parser || !m_parserEnabled) + return; + //temp +// if (!parser->activated()) return; + typingInProgress = false; + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + if (force) + { + baseNode = parser->parse(w, true); + } + + if (w->hasChanged() || force) + { + slotReloadStructTreeView(); + } + + if (force) + { + uint line, col; + w->viewCursorIf->cursorPositionReal(&line, &col); + Node *node = parser->nodeAt(line, col); + if (StructTreeView::ref()->isVisible() && node) + StructTreeView::ref()->showTagAtPos(node); + aTab->setCurrentNode(node); + } + } + + return; +} + +void QuantaApp::setCursorPosition( int row, int col ) +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + int numLines = w->editIf->numLines(); + + if ( row < numLines ) + w->viewCursorIf->setCursorPositionReal(row, col); + else + w->viewCursorIf->setCursorPositionReal(numLines - 1, col); + } +} + +void QuantaApp::gotoFileAndLine(const QString& filename, int line, int column) +{ + // First, check if we're already showing this file + Document *w = ViewManager::ref()->activeDocument(); + KURL currentfilename, newfilename; + if(w) + { + currentfilename = w->url(); + newfilename.setPath(filename); + } + + // If a filename is specified and that file is not already active, openn it + if (!filename.isEmpty() && !currentfilename.equals(filename)) + { + QuantaView* view = ViewManager::ref()->isOpened(KURL::fromPathOrURL(filename)); + // If it's already opened, just activate it + if (view) + { + view->activate(); + view->activated(); + } else + { + // Otherwise open it + m_doc->openDocument( KURL::fromPathOrURL( filename ) ); + } + } + // We have to do this again, in case activedocument changed since last check (ie a file was opened) + w = ViewManager::ref()->activeDocument(); + if (w) + { + int numLines = w->editIf->numLines(); + if ( numLines > line && line >= 0 ) + { + // Jump to the correct line/col + w->viewCursorIf->setCursorPositionReal(line, column); + } + w->view()->setFocus(); + } +} + +void QuantaApp::selectArea(int line1, int col1, int line2, int col2) +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + int numLines = w->editIf->numLines(); + + if ( line1 > numLines-1 ) + line1 = numLines-1; + + if ( line2 > numLines-1 ) + line2 = numLines-1; + + w->viewCursorIf->setCursorPositionReal(line2, col2); + if (w->selectionIf) + w->selectionIf->setSelection(line1, col1, line2, col2); + } +} + +void QuantaApp::openDoc(const QString& url) +{ + if (qConfig.docPosition == "Tab") + { + QuantaView *docView = ViewManager::ref()->documentationView(); + delete m_documentationToolView; + m_documentationToolView = 0L; + docView->activate(); + } else + { + QuantaView *docView = ViewManager::ref()->documentationView(false); + if (docView) + ViewManager::ref()->removeView(docView); + if (!m_documentationToolView) + m_documentationToolView= addToolWindow(m_htmlPartDoc->view(), prevDockPosition(m_htmlPartDoc->view(), KDockWidget::DockBottom), getMainDockWidget()); + m_htmlPartDoc->view()->show(); + m_documentationToolView->show(); + } + m_htmlPartDoc->view()->setFocus(); // activates the part + + QString urlStr = url; + if (urlStr.startsWith("/")) + urlStr.prepend("file:"); + KURL u(urlStr); + if (u == m_htmlPartDoc->url()) + return; + + m_htmlPartDoc->closeURL(); + m_htmlPartDoc->openURL(u); + m_htmlPartDoc->show(); + m_htmlPartDoc->addToHistory(urlStr); +} + +void QuantaApp::slotContextHelp() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + QString currentWord = ""; + parser->setSAParserEnabled(false); + reparse(true); + parser->setSAParserEnabled(true); + uint line, col; + w->viewCursorIf->cursorPositionReal(&line, &col); + Node *node = parser->nodeAt(line, col); + if (node && node->tag->type == Tag::XmlTag) + { + currentWord = node->tag->name; + } else + { + currentWord = w->currentWord(); + } + const DTDStruct *dtd = w->currentDTD(true); + QString *url = dTab->contextHelp(dtd->documentation + "|" + currentWord); + if (url) + openDoc(*url); + } +} + +void QuantaApp::slotShowMessagesView() +{ + makeDockVisible(dynamic_cast<KDockWidget*>(m_messageOutputView->wrapperWidget())); +} + +void QuantaApp::slotShowProblemsView() +{ + makeDockVisible(dynamic_cast<KDockWidget*>(m_problemsOutputView->wrapperWidget())); +} + +void QuantaApp::slotShowAnnotationView() +{ + makeDockVisible(dynamic_cast<KDockWidget*>(m_annotationOutputView->wrapperWidget())); +} + +QWidget* QuantaApp::createContainer( QWidget *parent, int index, const QDomElement &element, int &id ) +{ + + QString tabname = element.attribute( "i18ntabname", "" ); + QString idStr = element.attribute( "id", "" ); + + if ( element.tagName().lower() == "toolbar" && !tabname.isEmpty()) + { +//avoid QToolBar warning in the log + QtMsgHandler oldHandler = qInstallMsgHandler( silenceQToolBar ); + ToolbarTabWidget *toolbarTab = ToolbarTabWidget::ref(); + QWidget *w = new QWidget(toolbarTab, "ToolbarHoldingWidget" + element.attribute("name")); + QuantaToolBar *tb = new QuantaToolBar(w, element.attribute("name"), true, true); + tb->loadState(element); + tb->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + + //kdDebug(24000) << "tb->iconSize() " << tb->iconSize() << endl; + if (toolbarTab->iconText() == KToolBar::IconTextBottom) + { + tb->setGeometry(0,0, toolbarTab->width(), tb->iconSize() + QFontMetrics(KGlobalSettings::toolBarFont()).height() + 10); + toolbarTab->setFixedHeight(toolbarTab->tabHeight() + tb->height() + 3); + } else + { + tb->setGeometry(0,0, toolbarTab->width(), tb->iconSize() + 10); + toolbarTab->setFixedHeight(toolbarTab->tabHeight() + tb->height() + 3); + } +/* + kdDebug(24000) << "tb->height() " << tb->height() << endl; + kdDebug(24000) << "toolbarTab->height() " << toolbarTab->height() << endl; + kdDebug(24000) << "toolbarTab->tabHeight() " << toolbarTab->tabHeight() << endl; +*/ + toolbarTab->insertTab(tb, tabname, idStr); + qInstallMsgHandler( oldHandler ); + + connect(tb, SIGNAL(removeAction(const QString&, const QString&)), + SLOT(slotRemoveAction(const QString&, const QString&))); + connect(tb, SIGNAL(editAction(const QString&)), + SLOT(slotEditAction(const QString&))); + return tb; + } + + return KMainWindow::createContainer( parent, index, element, id ); + +} + +void QuantaApp::removeContainer( QWidget *container, QWidget *parent, QDomElement &element, int id ) +{ + if (dynamic_cast<QuantaToolBar*>(container)) + { + ToolbarTabWidget::ref()->removePage(container); + } + else + KMainWindow::removeContainer( container, parent, element, id ); +} + +void QuantaApp::slotBack() +{ + if (ViewManager::ref()->documentationView(false) == ViewManager::ref()->activeView()) + { + m_htmlPartDoc->back(); + } else + if (m_previewVisible && (!m_previewToolView || m_htmlPart->view()->hasFocus())) + { + m_htmlPart->back(); + } else + { + activatePrevWin(); + } +} + +void QuantaApp::slotForward() +{ + if (ViewManager::ref()->documentationView(false) == ViewManager::ref()->activeView()) + { + m_htmlPartDoc->forward(); + } else + if (m_previewVisible && (!m_previewToolView || m_htmlPart->view()->hasFocus())) + { + m_htmlPart->forward(); + } else + { + activateNextWin(); + } +} + +void QuantaApp::slotInsertFile(const KURL& url) +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + w->insertFile(url); + } +} + +void QuantaApp::slotContextMenuAboutToShow() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + QPopupMenu *popup = static_cast<QPopupMenu*>(factory()->container("popup_editor",this)); + QString name; + uint line, col; + int bl, bc, el, ec; + QString tagStr; + w->viewCursorIf->cursorPositionReal(&line, &col); + Node *node = parser->nodeAt(line, col, false); + if (node) + { + if (node->tag->dtd()->family == Script) + { + StructTreeGroup group; + uint count = node->tag->dtd()->structTreeGroups.count(); + for (uint i = 0; i < count; i++) + { + group = node->tag->dtd()->structTreeGroups[i]; + if (group.hasFileName) + { + if (!group.hasDefinitionRx ) + continue; + tagStr = node->tag->tagStr(); + int pos = 0; + while (pos != -1) + { + pos = group.definitionRx.search(node->tag->cleanStr, pos); + if (pos != -1) + { + QString cleanName = node->tag->cleanStr.mid(pos, group.definitionRx.matchedLength()); + name = tagStr.mid(pos, group.definitionRx.matchedLength()); + node->tag->beginPos(bl, bc); + QString tmpStr = tagStr.left(pos); + int newLines = tmpStr.contains('\n'); + bl += newLines; + int l = tmpStr.findRev('\n'); //the last EOL + bc = (l == -1) ? bc+pos : pos - l - 1; + newLines = name.contains('\n'); + l = name.length(); + el = bl + newLines; + ec = (newLines > 0) ? l - name.findRev('\n') : bc + l - 1; + pos += l; + int p = group.definitionRx.search(cleanName); + if (p != -1) + { + name = name.mid(p, group.definitionRx.matchedLength()); + } else + name = ""; + if (QuantaCommon::isBetween(line, col, bl, bc, el, ec) == 0) + { + break; + } else + { + name = ""; + } + } + } + name.remove(group.fileNameRx); + if (!name.isEmpty()) + break; + } + } + } else + { + QMap<QString, XMLStructGroup>::ConstIterator it = node->tag->dtd()->xmlStructTreeGroups.find(node->tag->name.lower()); + + if (it != node->tag->dtd()->xmlStructTreeGroups.constEnd()) + { + XMLStructGroup group = it.data(); + uint count = group.attributes.count(); + for (uint j = 0; j < count; j++ ) + if (node->tag->hasAttribute(group.attributes[j])) + { + name.append(node->tag->attributeValue(group.attributes[j])); + name.append(" | "); + } + name = name.left(name.length()-3); + name.remove('\n'); + } + } + } + KAction *action = actionCollection()->action("open_file_under_cursor"); + if (action) + { + if (!name.isEmpty()) + { + KURL baseUrl = QExtFileInfo::path(w->url()); + urlUnderCursor = baseUrl; + QuantaCommon::setUrl(urlUnderCursor, name.stripWhiteSpace()); + urlUnderCursor = QExtFileInfo::toAbsolute(urlUnderCursor, baseUrl); + action->setText(i18n("Open File: %1").arg(KStringHandler::lsqueeze(urlUnderCursor.prettyURL(0, KURL::StripFileProtocol), 80))); + action->setEnabled(true); + } else + { + action->setText(i18n("Open File: none")); + action->setEnabled(false); + } + } + action = actionCollection()->action("upload_file"); + if (action) + { + action->setEnabled(Project::ref()->contains(w->url())); + } + + // If we have a debugger session active... + if(debugger() && debugger()->hasClient()) + { + int startpos; + QString word; + + // If we have a selection made, thats what we want to use for watching, setting etc + if (w->selectionIf && w->selectionIf->hasSelection()) + { + word = w->selectionIf->selection(); + } + else + { + // Otherwise, find the word under the cursor + word = w->editIf->textLine(w->viewCursorIf->cursorLine()); + startpos = word.findRev(QRegExp("$|[^a-zA-Z0-9_]"), w->viewCursorIf->cursorColumn()); + + word.remove(0, startpos); + if(word.left(1) != "$") + word.remove(0, 1); + + word = word.left(word.find(QRegExp("[^a-zA-Z0-9_]"), 1)); + } + // If we have a linebreak, take everything before the break + startpos = word.find("\n"); + if(startpos > 0) + word = word.left(startpos); + + // Trim whitespace from the beginning and end of the string + word = word.stripWhiteSpace(); + + // now we have a word, possibly the name of a variable + popupWord = word; + + // The word we display in the popup will be cut off not to make an obeast pop up menu + if(word.length() > 23) + { + word.mid(20); + word += "..."; + } + + // If we have the addwatch action... + action = actionCollection()->action("debug_addwatch"); + if(action) + { + action->setText(i18n("Add Watch: '%1'").arg(word)); + action->setEnabled(!word.isEmpty()); + + if(!action->isPlugged(popup)) + action->plug(popup); + } + + // Dito for the set action + action = actionCollection()->action("debug_variable_set"); + if(action) + { + action->setText(i18n("Set Value of '%1'").arg(word)); + action->setEnabled(!word.isEmpty()); + + if(!action->isPlugged(popup)) + action->plug(popup); + } + + // Dito for the "break when" action + action = actionCollection()->action("debug_conditional_break"); + if(action) + { + action->setText(i18n("Break When '%1'...").arg(word)); + action->setEnabled(!word.isEmpty()); + + if(!action->isPlugged(popup)) + action->plug(popup); + } + } + else + { + action = actionCollection()->action("debug_addwatch"); + if(action && action->isPlugged(popup)) + action->unplug(popup); + action = actionCollection()->action("debug_variable_set"); + if(action && action->isPlugged(popup)) + action->unplug(popup); + action = actionCollection()->action("debug_conditional_break"); + if(action && action->isPlugged(popup)) + action->unplug(popup); + } + } + +} + +void QuantaApp::slotOpenFileUnderCursor() +{ + if (QExtFileInfo::exists(urlUnderCursor, true, this)) + { + if (QuantaCommon::checkMimeGroup(urlUnderCursor, "text" )) + { + slotFileOpen(urlUnderCursor, defaultEncoding()); + } + else if (QuantaCommon::checkMimeGroup(urlUnderCursor, "image" )) + { + slotShowPreviewWidget(true); + slotImageOpen(urlUnderCursor); + } + } else + { + KMessageBox::error(this, i18n("<qt>The file <b>%1</b> does not exist or is not a recognized mime type.</qt>").arg(urlUnderCursor.prettyURL(0, KURL::StripFileProtocol))); + + } +} + +/** Load an user toolbar file from the disk. */ +void QuantaApp::slotLoadToolbarFile(const KURL& url) +{ + QDictIterator<ToolbarEntry> it(m_toolbarList); + ToolbarEntry *p_toolbar; + while (it.current()) + { + p_toolbar = it.current(); + ++it; + if (url == p_toolbar->url) + return; + } + QDomDocument actionDom; + + QTextStream str; + str.setEncoding(QTextStream::UnicodeUTF8); + QString fileName = url.path(); + + if ( url.fileName().endsWith(toolbarExtension) ) + { + QDomDocument *toolbarDom = new QDomDocument(); +//extract the files from the archives + KTar tar(fileName); + if (tar.open(IO_ReadOnly)) + { + QString base = QFileInfo(fileName).baseName(); + KArchiveFile* file = (KArchiveFile *) tar.directory()->entry(base+".toolbar"); + if (file) + { + QIODevice *device = file->device(); + toolbarDom->setContent(device); + delete device; + } + file = (KArchiveFile *) tar.directory()->entry(base+".actions"); + if (file) + { + QIODevice *device = file->device(); + actionDom.setContent(device); + delete device; + } + + tar.close(); + } + if ( (toolbarDom->toString().isEmpty()) ) //|| (actionContent.isEmpty())) + { + KMessageBox::error(this, i18n("Cannot load the toolbars from the archive.\nCheck that the filenames inside the archives begin with the archive name.")); + delete toolbarDom; + return; + } + + QDomNodeList nodeList = toolbarDom->elementsByTagName("ToolBar"); + QString name = nodeList.item(0).cloneNode().toElement().attribute("tabname"); + + //search for another toolbar with the same name + QPtrList<KXMLGUIClient> xml_clients = guiFactory()->clients(); + QString newName = name; + QString i18nName = i18n(name.utf8()); + QString origName = name; + bool found = false; + bool nameModified = false; + int count = 2; + do + { + uint index = 0; + while (index < xml_clients.count()) + { + name = newName; + if (index == 0) + found = false; + nodeList = xml_clients.at(index)->domDocument().elementsByTagName("ToolBar"); + for (uint i = 0; i < nodeList.count(); i++) + { + if ((nodeList.item(i).cloneNode().toElement().attribute("name").lower() ) == name.lower()) + { + newName = origName + QString(" (%1)").arg(count); + i18nName = i18n(origName.utf8()) + QString(" (%1)").arg(count); + nameModified = true; + count++; + found = true; + break; + } + } + if (found) + { + index = 0; + } else + { + index++; + } + } + } while (name == newName && found); + name = newName; + + p_toolbar = new ToolbarEntry; + + QDomDocument *dom = new QDomDocument(); + dom->setContent(toolbarDom->toString()); + p_toolbar->dom = dom; + p_toolbar->nameModified = nameModified; + + QString s = i18nName.lower(); + QString toolbarId = s; + QRegExp rx("\\s|\\."); + toolbarId.replace(rx, "_"); + int n = 1; + while (m_toolbarList.find(toolbarId) != 0L) + { + toolbarId = s + QString("%1").arg(n); + toolbarId.replace(rx, "_"); + n++; + } + + + userToolbarsCount++; + + //Change the name also in the XML File -> create a temp XML file + KTempFile* tempFile = new KTempFile(tmpDir); + tempFile->setAutoDelete(true); + + nodeList = toolbarDom->elementsByTagName("ToolBar"); + QDomElement el = nodeList.item(0).toElement(); + el.setAttribute("name", name.lower()); + el.setAttribute("tabname", name); + el.setAttribute("i18ntabname", i18nName); + el.setAttribute("id", toolbarId); + nodeList = toolbarDom->elementsByTagName("text"); + el.firstChild().setNodeValue(name); + tempFile->textStream()->setEncoding(QTextStream::UnicodeUTF8); + * (tempFile->textStream()) << toolbarDom->toString(); + tempFile->close(); + + //create the new toolbar GUI from the temp file + ToolbarXMLGUI * toolbarGUI = new ToolbarXMLGUI(tempFile->name()); + + //setup the actions + nodeList = actionDom.elementsByTagName("action"); + for (uint i = 0; i < nodeList.count(); i++) + { + QDomNode node = nodeList.item(i).cloneNode(); + el = node.toElement(); + QString actionName = el.attribute("name"); + //if there is no such action yet, add to the available actions + if (!actionCollection()->action(actionName)) + { + bool toggable = (el.attribute("toggable", "") == "true"); + TagAction *tagAction = new TagAction(&el, this, toggable); + m_tagActions.append(tagAction); + + //add the actions to every toolbar xmlguiclient + QDictIterator<ToolbarEntry> it(m_toolbarList); + while (it.current()) + { + it.current()->guiClient->actionCollection()->insert(tagAction); + ++it; + } + + //Compatility code (read the action shortcuts from quantaui.rc) + //TODO: Remove after upgrade from 3.1 is not supported + if (oldShortcuts.contains(actionName)) + { + tagAction->setModified(true); + tagAction->data().setAttribute("shortcut", oldShortcuts[actionName]); + tagAction->setShortcut(KShortcut(oldShortcuts[actionName])); + } + } else + { + // kdDebug(24000) << "The action " << actionName << " is already present!" << endl; + TagAction *tagAction = dynamic_cast<TagAction*>(actionCollection()->action(actionName)); + if (tagAction) + tagAction->setModified(true); + } + } + + //add all actions to the xmlguiclient of this toolbar + for (uint i = 0 ; i < actionCollection()->count(); i++) + toolbarGUI->actionCollection()->insert(actionCollection()->action(i)); + + guiFactory()->addClient(toolbarGUI); + + //Plug in the actions & build the menu + QPopupMenu *menu = new QPopupMenu; + KAction *action; + nodeList = toolbarGUI->domDocument().elementsByTagName("Action"); + for (uint i = 0; i < nodeList.count(); i++) + { + action = actionCollection()->action(nodeList.item(i).cloneNode().toElement().attribute("name") ); + if (action) + { + action->plug(menu); + } + } + + m_tagsMenu->insertItem(i18nName, menu); + p_toolbar->menu = menu; + + tempFileList.append(tempFile); + p_toolbar->guiClient = toolbarGUI; + p_toolbar->name = name; + p_toolbar->id = toolbarId; + p_toolbar->url = url; + p_toolbar->visible = true; + p_toolbar->user = true; //TODO + m_toolbarList.insert(toolbarId, p_toolbar); + delete toolbarDom; + + slotToggleDTDToolbar(!allToolbarsHidden()); + } +} + +/** Show the toolbar which is in url. If it was not loaded yet, it loads the + toolbar from the file */ +void QuantaApp::showToolbarFile(const KURL &url) +{ + ToolbarEntry *p_toolbar = toolbarByURL(url); + if (!p_toolbar) + { + slotLoadToolbarFile(url); + p_toolbar = toolbarByURL(url); + if (p_toolbar) + { + p_toolbar->user = false; + userToolbarsCount--; + } + } else + { + QDomNodeList nodeList; + QPopupMenu *menu = new QPopupMenu; + KAction *action; + KActionCollection *ac = actionCollection(); + nodeList = p_toolbar->guiClient->domDocument().elementsByTagName("Action"); + for (uint i = 0; i < nodeList.count(); i++) + { + action = ac->action(nodeList.item(i).toElement().attribute("name") ); + if (action) + { + action->plug(menu); + } + } + m_tagsMenu->insertItem(i18n(p_toolbar->name.utf8()),menu); + p_toolbar->menu = menu; + p_toolbar->guiClient->reloadXML(); + guiFactory()->addClient(p_toolbar->guiClient); + p_toolbar->visible = true; + } +} + +/** Load an user toolbar from the disk. */ +void QuantaApp::slotLoadToolbar() +{ + KURL::List urls = KFileDialog::getOpenURLs(locateLocal("data",resourceDir + "toolbars/"), "*"+toolbarExtension, this); + if (!urls.isEmpty()) + { + for (KURL::List::ConstIterator it = urls.constBegin(); it != urls.constEnd(); ++it) + slotLoadToolbarFile(*it); + } +} + +/** Load an user toolbar from the disk. */ +void QuantaApp::slotLoadGlobalToolbar() +{ + KURL::List urls = KFileDialog::getOpenURLs(qConfig.globalDataDir +resourceDir + "toolbars/", "*"+toolbarExtension+"\n*", this); + if (!urls.isEmpty()) + { + for (KURL::List::ConstIterator it = urls.constBegin(); it != urls.constEnd(); ++it) + slotLoadToolbarFile(*it); + } +} + +KURL QuantaApp::saveToolbarToFile(const QString& toolbarName, const KURL& destFile) +{ + KURL tarFile = destFile; + + if (! destFile.fileName().endsWith(toolbarExtension)) + { + tarFile.setFileName(destFile.fileName() + toolbarExtension); + } + + QBuffer buffer; + buffer.open(IO_ReadWrite); + QString toolStr; + QTextStream toolStream(&toolStr, IO_ReadWrite); + toolStream.setEncoding(QTextStream::UnicodeUTF8); + + QBuffer buffer2; + buffer2.open(IO_WriteOnly); + QTextStream actStr(&buffer2); + actStr.setEncoding(QTextStream::UnicodeUTF8); + + QDomNodeList nodeList, nodeList2; + + toolStream << "<!DOCTYPE kpartgui SYSTEM \"kpartgui.dtd\">\n<kpartgui name=\"quanta\" version=\"2\">\n"; + actStr << QString("<!DOCTYPE actionsconfig>\n<actions>\n"); + +//look up the clients + QPtrList<KXMLGUIClient> xml_clients = factory()->clients(); + for (uint index = 0; index < xml_clients.count(); index++) + { + nodeList = xml_clients.at(index)->domDocument().elementsByTagName("ToolBar"); + for (uint i = 0; i < nodeList.count(); i++) + { + //find the actual toolbar in the XML GUI + if ((nodeList.item(i).cloneNode().toElement().attribute("id") ) == toolbarName) + { + + //find the actions registered to the toolbar + QDomNode n = nodeList.item(i).firstChild(); + while (! n.isNull()) + { + QDomElement e = n.toElement(); + if (e.tagName() == "Action") + { + TagAction *action = dynamic_cast<TagAction*>(actionCollection()->action(e.attribute("name"))); + if (action) + { + action->data().save(actStr,1); + action->setModified(false); + } + } else + if (e.tagName() == "_Separator_") + { + e.setTagName("Separator"); + } + n = n.nextSibling(); + } + QDomElement e = nodeList.item(0).toElement(); + QString i18nName = e.attribute("i18ntabname"); + QString id = e.attribute("id"); + e.removeAttribute("i18ntabname"); + e.removeAttribute("id"); + nodeList.item(i).save(toolStream,2); + e.setAttribute("i18ntabname", i18nName); + e.setAttribute("id", id); + } + } + } + toolStream << QString("\n</kpartgui>"); + actStr << QString("\n</actions>"); + //buffer.flush(); + + ToolbarEntry *p_toolbar = m_toolbarList[toolbarName]; + QDomDocument *oldDom = p_toolbar->dom; + QDomDocument *dom = new QDomDocument(); + QString s = toolStr; + QString error; + int el, ec; + if (!dom->setContent(s, &error, &el, &ec)) + kdError(24000) << QString("Error %1 at (%2, %3)").arg(error).arg(el).arg(ec)<<endl; + p_toolbar->dom = dom; + + QTextStream bufferStr(&buffer); + bufferStr.setEncoding(QTextStream::UnicodeUTF8); + bufferStr << toolStr; + buffer.close(); + buffer2.close(); + + KTempFile *tempFile = new KTempFile(tmpDir); + tempFile->setAutoDelete(true); + tempFile->close(); + KTar tar(tempFile->name(), "application/x-gzip"); + if (!tar.open(IO_WriteOnly)) + return KURL(); + if (!tar.writeFile(QFileInfo(tarFile.path()).baseName()+".toolbar", "user", "group", buffer.buffer().size(), buffer.buffer().data())) + return KURL(); + if (!tar.writeFile(QFileInfo(tarFile.path()).baseName()+".actions", "user", "group", buffer2.buffer().size(), buffer2.buffer().data())) + return KURL(); + tar.close(); + if (!QExtFileInfo::copy(KURL::fromPathOrURL(tempFile->name()), tarFile, -1, true, false, this)) + { + KMessageBox::error(this, i18n("<qt>An error happened while saving the <b>%1</b> toolbar.<br>" + "Check that you have write permissions for<br><b>%2</b>.<br><br>This might happen if you tried save to save a global toolbar as a simple user. Use <i>Save As</i> or <i>Toolbars->Save Toolbars->Save as Local Toolbar</i> in this case. </qt>").arg(p_toolbar->name).arg(tarFile.prettyURL(0, KURL::StripFileProtocol)), i18n("Toolbar Saving Error")); + tarFile = KURL(); + delete p_toolbar->dom; + p_toolbar->dom = oldDom; + } else + delete oldDom; + delete tempFile; + return tarFile; +} + +/** Saves a toolbar as local or project specific. */ +bool QuantaApp::saveToolbar(bool localToolbar, const QString& toolbarToSave, const KURL& destURL) +{ + int query; + KURL url; + KURL projectToolbarsURL; + QString toolbarName; + QString localToolbarsDir = locateLocal("data",resourceDir + "toolbars/"); + + if (toolbarToSave.isEmpty()) + { + ToolbarTabWidget *tb = ToolbarTabWidget::ref(); + + QStringList lst; + QStringList idLst; + int current=0; + for (int i = 0; i < tb->count(); i++) + { + lst << tb->label(i); + idLst << tb->id(i); + if ( tb->tabLabel(tb->currentPage()) == tb->label(i) ) current=i; + } + + bool ok = false; + QString res = KInputDialog::getItem( + i18n( "Save Toolbar" ), + i18n( "Please select a toolbar:" ), lst, current, false, &ok, this ); + if ( !ok ) + return false; + + for (uint i = 0; i < lst.count(); i++) + { + if (lst[i] == res) + { + toolbarName = idLst[i]; + break; + } + } + } else + { + toolbarName = toolbarToSave; + } + ToolbarEntry *p_toolbar = m_toolbarList[toolbarName]; + QString toolbarFileName = p_toolbar->url.fileName(false); + QString toolbarRelPath = p_toolbar->url.url(); + if (toolbarRelPath.startsWith("file://" + qConfig.globalDataDir)) + { + toolbarRelPath.remove("file://" + qConfig.globalDataDir + resourceDir + "toolbars/"); + toolbarRelPath.remove(toolbarFileName); + } + else + { + toolbarRelPath = ""; + } + toolbarFileName.remove(".toolbar.tgz"); + if (destURL.isEmpty()) + { + do { + query = KMessageBox::Yes; + + if (localToolbar) + { + url = KFileDialog::getSaveURL(localToolbarsDir + toolbarRelPath + toolbarFileName, "*"+toolbarExtension, this); + } else + { + url = KFileDialog::getSaveURL(Project::ref()->toolbarURL().url() + toolbarFileName, "*"+toolbarExtension, this); + } + + if (url.isEmpty()) + return false; + + if (Project::ref()->hasProject()) + projectToolbarsURL = Project::ref()->toolbarURL(); + if ( ((!localToolbar) && (projectToolbarsURL.isParentOf(url)) ) || + ((localToolbar) && (KURL(localToolbarsDir).isParentOf(url))) ) + { + if (!QuantaCommon::checkOverwrite(url, this)) + query = KMessageBox::No; + } else + { + if (!localToolbar) + localToolbarsDir = projectToolbarsURL.prettyURL(); + KMessageBox::sorry(0,i18n("<qt>You must save the toolbars to the following folder: <br><br><b>%1</b></qt>") + .arg(localToolbarsDir)); + query = KMessageBox::No; + } + } while (query != KMessageBox::Yes); + } else + { + url = destURL; + query = KMessageBox::Yes; + } + if( query != KMessageBox::Cancel ) + { + KURL tarName = saveToolbarToFile(toolbarName, url); + if (tarName.isEmpty()) + { + return false; + } + if (!localToolbar) + Project::ref()->insertFile(tarName, true); + } + return true; +} + +/** Saves a toolbar as localspecific. */ +void QuantaApp::slotSaveLocalToolbar() +{ + saveToolbar(true); +} +/** Saves a toolbar as project specific. */ +void QuantaApp::slotSaveProjectToolbar() +{ + saveToolbar(false); +} + +/** Adds a new, empty toolbar. */ +void QuantaApp::slotAddToolbar() +{ + bool ok; + QString name = KInputDialog::getText(i18n("New Toolbar"), i18n("Enter toolbar name:"), i18n("User_%1").arg(userToolbarsCount), &ok, this); + if (ok) + { + userToolbarsCount++; + + QString toolbarId = name; + int n = 1; + while (m_toolbarList.find(toolbarId) != 0L) + { + toolbarId = name + QString("%1").arg(n); + n++; + } + toolbarId = toolbarId.lower(); + + KTempFile* tempFile = new KTempFile(tmpDir); + tempFile->setAutoDelete(true); + tempFile->textStream()->setEncoding(QTextStream::UnicodeUTF8); + * (tempFile->textStream()) << QString("<!DOCTYPE kpartgui SYSTEM \"kpartgui.dtd\">\n<kpartgui name=\"quanta\" version=\"2\">\n<ToolBar name=\"%1\" tabname=\"%2\" i18ntabname=\"%3\" id=\"%4\">\n<text>%5</text>\n</ToolBar>\n</kpartgui>\n") + .arg(name.lower()).arg(name).arg(name).arg(toolbarId).arg(name); + tempFile->close(); + + ToolbarXMLGUI * toolbarGUI = new ToolbarXMLGUI(tempFile->name()); + +//add all actions to the xmlguiclient of this toolbar + for (uint i = 0 ; i < actionCollection()->count(); i++) + toolbarGUI->actionCollection()->insert(actionCollection()->action(i)); + + guiFactory()->addClient(toolbarGUI); + ToolbarTabWidget::ref()->setCurrentPage(ToolbarTabWidget::ref()->count()-1); + tempFileList.append(tempFile); + ToolbarEntry *p_toolbar = new ToolbarEntry; + p_toolbar->guiClient = toolbarGUI; + + QDomDocument *dom = new QDomDocument(toolbarGUI->domDocument()); + + p_toolbar->dom = dom; + p_toolbar->name = name; + p_toolbar->user = true; + p_toolbar->visible = true; + p_toolbar->nameModified = false; + p_toolbar->menu = new QPopupMenu; + p_toolbar->id = toolbarId; + m_tagsMenu->insertItem(p_toolbar->name, p_toolbar->menu); + m_toolbarList.insert(toolbarId, p_toolbar); + + slotToggleDTDToolbar(!allToolbarsHidden()); + } +} + + +/** Removes a user toolbar from the toolbars. */ +bool QuantaApp::slotRemoveToolbar() +{ + ToolbarTabWidget *tb = ToolbarTabWidget::ref(); + int i; + + QStringList lst; + QStringList idLst; + int current=0, j =0; + for (i = 0; i < tb->count(); i++) + { + lst << tb->label(i); + idLst << tb->id(i); + if ( tb->tabLabel(tb->currentPage()) == tb->label(i) ) current=j; + j++; + } + + bool ok = false; + QString res = KInputDialog::getItem( + i18n( "Remove Toolbar" ), + i18n( "Please select a toolbar:" ), lst, current, false, &ok, this ); + + if (ok) + { + QString id = res; + for (uint i = 0; i < lst.count(); i++) + { + if (lst[i] == res) + { + id = idLst[i]; + break; + } + } + return slotRemoveToolbar(id); + } else + return false; + +} + +QString QuantaApp::createToolbarTarball() +{ + ToolbarTabWidget *tb = ToolbarTabWidget::ref(); + + QStringList lst; + QStringList idLst; + int current = 0; + for (int i = 0; i < tb->count(); i++) + { + lst << tb->label(i); + idLst << tb->id(i); + if ( tb->tabLabel(tb->currentPage()) == tb->label(i) ) current=i; + } + + bool ok = false; + QString res = KInputDialog::getItem( + i18n( "Send Toolbar" ), + i18n( "Please select a toolbar:" ), lst, current, false, &ok, this ); + + if (!ok) + return QString::null; + + QString toolbarName = res; + for (uint i = 0; i < lst.count(); i++) + { + if (lst[i] == toolbarName) + { + toolbarName = idLst[i]; + break; + } + } + QString prefix="quanta"; + KTempDir* tempDir = new KTempDir(tmpDir); + tempDir->setAutoDelete(true); + tempDirList.append(tempDir); + QString tempFileName=tempDir->name() + toolbarName; + + KURL tempURL; + tempURL.setPath(tempFileName); + saveToolbarToFile(toolbarName, tempURL); + + return tempFileName + ".toolbar.tgz"; +} + +/** Sends a toolbar in mail. */ +void QuantaApp::slotSendToolbar() +{ + + QString tempFileName = createToolbarTarball(); + if (tempFileName.isNull()) + return; + + QStringList toolbarFile; + toolbarFile += tempFileName; + + TagMailDlg *mailDlg = new TagMailDlg( this, i18n("Send toolbar in email")); + QString toStr; + QString message = i18n("Hi,\n This is a Quanta Plus [http://quanta.kdewebdev.org] toolbar.\n\nHave fun.\n"); + QString titleStr; + QString subjectStr; + + mailDlg->TitleLabel->setText(i18n("Content:")); +/* mailDlg->titleEdit->setFixedHeight(60); + mailDlg->titleEdit->setVScrollBarMode(QTextEdit::Auto); + mailDlg->titleEdit->setHScrollBarMode(QTextEdit::Auto);*/ + if ( mailDlg->exec() ) { + if ( !mailDlg->lineEmail->text().isEmpty()) + { + toStr = mailDlg->lineEmail->text(); + subjectStr = (mailDlg->lineSubject->text().isEmpty())?i18n("Quanta Plus toolbar"):mailDlg->lineSubject->text(); + if ( !mailDlg->titleEdit->text().isEmpty()) + message = mailDlg->titleEdit->text(); + } else + { + KMessageBox::error(this,i18n("No destination address was specified.\n Sending is aborted."),i18n("Error Sending Email")); + delete mailDlg; + return; + } + + kapp->invokeMailer(toStr, QString::null, QString::null, subjectStr, message, QString::null, toolbarFile); + } + delete mailDlg; +} + +void QuantaApp::slotDownloadToolbar() +{ + if (!m_newToolbarStuff) + m_newToolbarStuff = new QNewToolbarStuff("quanta/toolbar", this); + m_newToolbarStuff->downloadResource(); +} + +void QuantaApp::slotUploadToolbar() +{ + QString tempFileName = createToolbarTarball(); + if (tempFileName.isNull()) + return; + if (!m_newToolbarStuff) + m_newToolbarStuff = new QNewToolbarStuff("quanta/toolbar", this); +// tempDirList.append(m_newToolbarStuff->uploadResource(tempFileName)); + m_newToolbarStuff->uploadResource(tempFileName); +} + +void QuantaApp::slotRenameToolbar() +{ + ToolbarTabWidget *tb = ToolbarTabWidget::ref(); + + QStringList lst; + QStringList idLst; + int current = 0; + for (int i = 0; i < tb->count(); i++) + { + lst << tb->label(i); + idLst << tb->id(i); + if ( tb->tabLabel(tb->currentPage()) == tb->label(i) ) current=i; + } + + bool ok = false; + QString res = KInputDialog::getItem( + i18n( "Rename Toolbar" ), + i18n( "Please select a toolbar:" ), lst, current, false, &ok, this ); + if (ok) + { + QString id = res; + for (uint i = 0; i < lst.count(); i++) + { + if (lst[i] == res) + { + id = idLst[i]; + break; + } + } + slotRenameToolbar(id); + } +} + +void QuantaApp::slotRenameToolbar(const QString& name) +{ + ToolbarEntry *p_toolbar = quantaApp->m_toolbarList[name]; + if (p_toolbar) + { + bool ok; + QString newName = KInputDialog::getText(i18n("Rename Toolbar"), i18n("Enter the new name:"), p_toolbar->name, &ok, this); + if (ok && newName != p_toolbar->name) + { + m_toolbarList.take(name); + p_toolbar->name = newName; + QDomElement el = p_toolbar->guiClient->domDocument().firstChild().firstChild().toElement(); + el.setAttribute("tabname", p_toolbar->name); + el.removeAttribute("i18ntabname"); + el.setAttribute("name", p_toolbar->name.lower()); + QDomNodeList nodeList = p_toolbar->guiClient->domDocument().elementsByTagName("text"); + nodeList.item(0).firstChild().setNodeValue(p_toolbar->name); + //Rename the _Separator_ tags back to Separator, so they are not treated + //as changes + nodeList = p_toolbar->guiClient->domDocument().elementsByTagName("_Separator_"); + for (uint i = 0; i < nodeList.count(); i++) + { + nodeList.item(i).toElement().setTagName("Separator"); + } + KXMLGUIFactory::saveConfigFile(p_toolbar->guiClient->domDocument(), + p_toolbar->guiClient->xmlFile(), p_toolbar->guiClient->instance()); + ToolbarTabWidget *tb = ToolbarTabWidget::ref(); + for (int i = 0; i < tb->count(); i++) + { + if (tb->id(i) == name) + { + tb->setTabLabel(tb->page(i)->parentWidget(), i18n(p_toolbar->name.utf8())); + m_tagsMenu->changeItem(m_tagsMenu->idAt(i + 2), i18n(p_toolbar->name.utf8())); + break; + } + } + m_toolbarList.insert(name, p_toolbar); + } + } +} + +/** Ask for save all the modified user toolbars. */ +bool QuantaApp::removeToolbars() +{ + QStringList names; + QDictIterator<ToolbarEntry> it(m_toolbarList); + for (;it.current();++it) + { + names += it.currentKey(); + } + for (QStringList::ConstIterator iter = names.constBegin(); iter != names.constEnd(); ++iter) + { + if (!slotRemoveToolbar(*iter)) + return false; + } + + QString s = "<!DOCTYPE actionsconfig>\n<actions>\n</actions>\n"; + m_actions->setContent(s); + TagAction *action; + for (uint i = 0; i < actionCollection()->count(); i++) + { + action = dynamic_cast<TagAction *>(actionCollection()->action(i)); + if (action) + { + QDomElement el = action->data(); + m_actions->firstChild().appendChild(el); + } + } + + QFile f(KGlobal::instance()->dirs()->saveLocation("data")+resourceDir + "actions.rc" ); + if (f.open( IO_ReadWrite | IO_Truncate )) + { + if (!m_actions->firstChild().firstChild().isNull()) + { + QTextStream qts(&f); + qts.setEncoding(QTextStream::UnicodeUTF8); + m_actions->save(qts,0); + f.close(); + } else + f.remove(); + } + + return true; +} + +void QuantaApp::slotDeleteAction(KAction *action) +{ +//remove all references to this action + QDomElement el = static_cast<TagAction*>(action)->data(); + QString text = el.attribute("text"); + QString actionName = action->name(); + + QPtrList<KXMLGUIClient> guiClients = factory()->clients(); + KXMLGUIClient *guiClient = 0; + QDomNodeList nodeList; + for (uint i = 0; i < guiClients.count(); i++) + { + guiClient = guiClients.at(i); + guiClient->domDocument().setContent(KXMLGUIFactory::readConfigFile( guiClient->xmlFile(), guiClient->instance() )); + nodeList = guiClient->domDocument().elementsByTagName("Action"); + for (uint j = 0; j < nodeList.count(); j++) + { + //we found a toolbar that contains the action + if (nodeList.item(j).toElement().attribute("name") == actionName) + { + nodeList.item(j).parentNode().removeChild(nodeList.item(j)); + KXMLGUIFactory::saveConfigFile(guiClient->domDocument(), guiClient->xmlFile()); + break; + } + } + guiClient->actionCollection()->take(action); + } + action->unplugAll(); + delete action; + action = 0L; +} + +void QuantaApp::slotRemoveAction(const QString& toolbarName, const QString& a_actionName) +{ + KAction *action = 0L; + QString actionName = a_actionName; + actionName.replace('&',"&&"); + KActionCollection *ac = actionCollection(); + uint actionCount = ac->count(); + QString str; + for (uint i = 0; i < actionCount; i++) + { + str = ac->action(i)->text(); + if (str == actionName || str.remove('&') == actionName) + { + action = ac->action(i); + break; + } + } + if (!action) //workaround for actionnames ending with "...". It's stripped from the end + //of the text when plugged into a toolbar. + { + actionName += "..."; + for (uint i = 0; i < actionCount; i++) + { + if (ac->action(i)->text() == actionName) + { + action = ac->action(i); + break; + } + } + } + + if (action) + { + ToolbarEntry *p_toolbar = quantaApp->m_toolbarList[toolbarName]; + if (p_toolbar) + { + QDomNode node = p_toolbar->guiClient->domDocument().firstChild().firstChild().firstChild(); + while (!node.isNull()) + { + if (node.nodeName() == "Action" && + node.toElement().attribute("name") == action->name()) + { + action->unplug(ToolbarTabWidget::ref()->page(toolbarName)); + action->unplug(p_toolbar->menu); + node.parentNode().removeChild(node); + } + node = node.nextSibling(); + } + KXMLGUIFactory::saveConfigFile(p_toolbar->guiClient->domDocument(), + p_toolbar->guiClient->xmlFile(), p_toolbar->guiClient->instance()); + } + } +} + +void QuantaApp::slotEditAction(const QString& actionName) +{ + ActionConfigDialog dlg(m_toolbarList, this, "actions_config_dlg", true, 0, actionName); + dlg.exec(); +} + +void QuantaApp::slotNewAction() +{ + ActionConfigDialog dlg(m_toolbarList, this, "actions_config_dlg"); + dlg.slotNewAction(); + dlg.exec(); +} + +void QuantaApp::slotAssignActionToScript(const KURL& a_scriptURL, const QString& a_interpreter) +{ + ActionConfigDialog dlg(m_toolbarList, this, "actions_config_dlg"); + QString name = a_scriptURL.fileName(); + name.truncate(name.length() - QFileInfo(name).extension().length() - 1); + dlg.createScriptAction(name, a_interpreter + " " + a_scriptURL.path()); + dlg.exec(); +} + +void QuantaApp::setDtep(const QString& dtepName, bool convert) +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + QString dtep = DTDs::ref()->getDTDNameFromNickName(dtepName); + if (!DTDs::ref()->find(dtep)) + return; + w->setDTDIdentifier(dtep); + const DTDStruct *dtd = DTDs::ref()->find(w->getDTDIdentifier()); + if (convert && dtd->family == Xml) + { + Tag *tag = 0L; + w->findDTDName(&tag); + if (tag) + { + int bLine, bCol, eLine, eCol; + tag->beginPos(bLine,bCol); + tag->endPos(eLine,eCol); + w->editIf->removeText(bLine, bCol, eLine, eCol+1); + w->viewCursorIf->setCursorPositionReal((uint)bLine, (uint)bCol); + w->insertText("<!DOCTYPE" + dtd->doctypeStr +">"); + delete tag; + } else + { + w->viewCursorIf->setCursorPositionReal(0,0); + w->insertText("<!DOCTYPE" + dtd->doctypeStr + ">\n"); + } + } + slotLoadToolbarForDTD(w->getDTDIdentifier()); + QuantaView *view = ViewManager::ref()->activeView(); + if (view) + view->activated(); + reparse(true); + } +} + +void QuantaApp::slotChangeDTD() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + KDialogBase dlg(this, 0L, true, i18n("DTD Selector"), KDialogBase::Ok | KDialogBase::Cancel); + DTDSelectDialog *dtdWidget = new DTDSelectDialog(&dlg); + dtdWidget->setMinimumHeight(130); + dlg.setMainWidget(dtdWidget); + int pos = -1; + int defaultIndex = 0; + + QString oldDtdName = w->getDTDIdentifier(); + QString defaultDocType = Project::ref()->defaultDTD(); + QStringList lst = DTDs::ref()->nickNameList(true); + + QString oldDtdNickName = DTDs::ref()->getDTDNickNameFromName(oldDtdName); + QString defaultDtdNickName = DTDs::ref()->getDTDNickNameFromName(defaultDocType); + for(uint i = 0; i < lst.count(); i++) + { + dtdWidget->dtdCombo->insertItem(lst[i]); + if (lst[i] == oldDtdNickName) pos = i; + if (lst[i] == defaultDtdNickName) defaultIndex = i; + } + + if (pos == -1) + pos = defaultIndex; + dtdWidget->dtdCombo->setCurrentItem(pos); + dtdWidget->messageLabel->setText(i18n("Change the current DTD.")); + dtdWidget->currentDTD->setText(DTDs::ref()->getDTDNickNameFromName(w->getDTDIdentifier())); + //dlg->useClosestMatching->setShown(false); + delete dtdWidget->useClosestMatching; + dtdWidget->useClosestMatching = 0L; + dtdWidget->adjustSize(); + if (dlg.exec()) + { + setDtep(dtdWidget->dtdCombo->currentText(), dtdWidget->convertDTD->isChecked()); + } + } +} + +void QuantaApp::slotEditDTD() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + QStringList lst(DTDs::ref()->nickNameList()); + QString nickName = DTDs::ref()->getDTDNickNameFromName(w->getDTDIdentifier()); + bool ok = false; + QString res = KInputDialog::getItem( + i18n( "Edit DTD" ), + i18n( "Please select a DTD:" ), lst, lst.findIndex(nickName), false, &ok, this ); + + QString s = i18n("Create a new DTEP description"); + s = i18n("Load DTEP description from disk"); + if (!ok) + return; + + QString dtdName = DTDs::ref()->getDTDNameFromNickName(res); + + KDialogBase editDlg(this, "edit_dtep", true, i18n("Configure DTEP"), KDialogBase::Ok | KDialogBase::Cancel); + DTEPEditDlg dtepDlg(DTDs::ref()->find(dtdName)->fileName, &editDlg); + editDlg.setMainWidget(&dtepDlg); + if (editDlg.exec()) + { + dtepDlg.saveResult(); + } + } +} + +void QuantaApp::focusInEvent(QFocusEvent* e) +{ + KDockMainWindow::focusInEvent(e); + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + w->view()->setFocus(); + w->checkDirtyStatus(); + } +} + +void QuantaApp::slotShowCompletion() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w && w->codeCompletionIf) + w->codeCompletionRequested(); +} + +void QuantaApp::slotShowCompletionHint() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w && w->codeCompletionIf) + w->codeCompletionHintRequested(); +} + +void QuantaApp::slotMakeDonation() +{ + DonationDialog *dlg = new DonationDialog(this); + dlg->closeButton->setIconSet(SmallIconSet("fileclose")); + connect(dlg->closeButton, SIGNAL(clicked()), dlg, SLOT(accept())); + dlg->exec(); + delete dlg; +} + +void QuantaApp::slotHelpHomepage() +{ + kapp->invokeBrowser("http://quanta.kdewebdev.org"); +} + +void QuantaApp::slotHelpUserList() +{ + kapp->invokeBrowser("http://mail.kde.org/mailman/listinfo/quanta"); +} + +/** Loads the toolbars for dtd named dtdName and unload the ones belonging to oldDtdName. */ +void QuantaApp::slotLoadToolbarForDTD(const QString& dtdName) +{ + const DTDStruct *oldDtd = 0L; + + if (!currentToolbarDTD.isEmpty()) + { + oldDtd = DTDs::ref()->find(currentToolbarDTD); + if (!oldDtd) + oldDtd = DTDs::ref()->find(Project::ref()->defaultDTD()); + } + QString fileName; + const DTDStruct *newDtd = DTDs::ref()->find(dtdName); + if (!newDtd) + { + newDtd = DTDs::ref()->find(Project::ref()->defaultDTD()); + if (!newDtd) + newDtd = DTDs::ref()->find(qConfig.defaultDocType); //extreme case + } + + ToolbarEntry *p_toolbar; + if (m_debugger->UI()) + { + p_toolbar = m_toolbarList["Debug"]; + if (p_toolbar) + { + guiFactory()->removeClient(p_toolbar->guiClient); + p_toolbar->visible = false; + delete p_toolbar->menu; + p_toolbar->menu = 0L; + } + } + if (newDtd != oldDtd) + { + KURL::List newToolbars; + for (uint i = 0; i < newDtd->toolbars.count(); i++) + { + KURL url; + //first load the local version if it exists + fileName = locateLocal("data", resourceDir + "toolbars/"+newDtd->toolbars[i]); + QuantaCommon::setUrl(url, fileName); + if (QExtFileInfo::exists(url, true, this)) + { + //showToolbarFile(url); + newToolbars += url; + } else + { + fileName = qConfig.globalDataDir + resourceDir + "toolbars/"+newDtd->toolbars[i]; + QuantaCommon::setUrl(url, fileName); + if (QExtFileInfo::exists(url, true, this)) + { + newToolbars += url;// showToolbarFile(url); + } + } + } + //remove the toolbars of the oldDtdName + if (!currentToolbarDTD.isEmpty()) + { + for (uint i = 0; i < oldDtd->toolbars.count(); i++) + { + KURL url; + QString fileName = qConfig.globalDataDir + resourceDir + "toolbars/"+oldDtd->toolbars[i]; + QuantaCommon::setUrl(url, fileName); + KURL urlLocal; + fileName = locateLocal("data", resourceDir + "toolbars/"+oldDtd->toolbars[i]); + QuantaCommon::setUrl(urlLocal, fileName); + if (newToolbars.contains(url) == 0) + { + QDictIterator<ToolbarEntry> iter(m_toolbarList); + for( ; iter.current(); ++iter ) + { + p_toolbar = iter.current(); + if (p_toolbar->url == url || p_toolbar->url == urlLocal) + { + guiFactory()->removeClient(p_toolbar->guiClient); + p_toolbar->visible = false; + delete p_toolbar->menu; + p_toolbar->menu = 0L; + break; + } + } + } else + { + newToolbars.remove(url); + } + } + } + + //Load the toolbars for dtdName + KURL::List::Iterator it; + for (it = newToolbars.begin(); it != newToolbars.end(); ++it) + { + showToolbarFile(*it); + } + ToolbarTabWidget::ref()->setCurrentPage(0); + } + currentToolbarDTD = newDtd->name; + slotToggleDTDToolbar(!allToolbarsHidden()); +} + +/** Remove the toolbar named "name". */ +bool QuantaApp::slotRemoveToolbar(const QString& a_name) +{ + QString name = a_name; // increase reference counter for this string + ToolbarEntry *p_toolbar = m_toolbarList[name]; + QRegExp i18ntabnameRx("\\si18ntabname=\"[^\"]*\""); + QRegExp idRx("\\sid=\"[^\"]*\""); + if (p_toolbar) + { + KXMLGUIClient* toolbarGUI = p_toolbar->guiClient; + + if (toolbarGUI) + { + KAction *action; + //Rename the _Separator_ tags back to Separator, so they are not treated + //as changes + QDomNodeList nodeList = toolbarGUI->domDocument().elementsByTagName("_Separator_"); + for (uint i = 0; i < nodeList.count(); i++) + { + nodeList.item(i).toElement().setTagName("Separator"); + } + //check if the actions on the toolbar were modified or not + bool actionsModified = false; + nodeList = toolbarGUI->domDocument().elementsByTagName("Action"); + for (uint i = 0; i < nodeList.count(); i++) + { + action = actionCollection()->action(nodeList.item(i).toElement().attribute("name")); + if (dynamic_cast<TagAction*>(action) && + dynamic_cast<TagAction*>(action)->isModified()) + { + actionsModified = true; + break; + } + } + + //check if the toolbar's XML GUI was modified or not + QString s1 = p_toolbar->dom->toString(); + QString s2 = toolbarGUI->domDocument().toString(); + s1.remove(i18ntabnameRx); + s2.remove(i18ntabnameRx); + s1.remove(idRx); + s2.remove(idRx); + if (p_toolbar->nameModified) + { + QRegExp tabnameRx("\\stabname=\"[^\"]*\""); + tabnameRx.search(s2); + QString name1 = tabnameRx.cap(); + name1.remove(" tab"); + QString name2 = name1; + name2.remove(QRegExp("[\\s]\\([0-9]+\\)")); + s2.replace(name1, name2); + s2.replace(name1.lower(), name2.lower()); + } + bool useToolbarGUI = true; + if ( s1 != s2 /*|| actionsModified */) + { + int result; + if (p_toolbar->url.isEmpty()) + { + result = KMessageBox::warningYesNoCancel(this, i18n("<qt>Toolbar <b>%1</b> is new and unsaved. Do you want to save it before it is removed?</qt>").arg(p_toolbar->name), + i18n("Save Toolbar"), KStdGuiItem::save(), KStdGuiItem::discard()); + } else + { + FourButtonMessageBox dlg(this, 0, true); + dlg.textLabel->setText(i18n("<qt>The toolbar <b>%1</b> was modified. Do you want to save it before it is removed?</qt>").arg(p_toolbar->name)); + dlg.setCaption(i18n("Save Toolbar")); + dlg.pixmapLabel->setPixmap(BarIcon("messagebox_info", KIcon::SizeMedium)); + dlg.exec(); + result = dlg.status(); + switch (result) + { + case -1: result = KMessageBox::Cancel; + break; + case 1: result = KMessageBox::Continue; //hack - this means Save + break; + case 2: result = KMessageBox::Yes; //hack - this means Save As + break; + case 3: result = KMessageBox::No; //this means Don't Save + break; + } + } + switch (result) + { + case KMessageBox::Yes: + { + bool local = true; + if (Project::ref()->hasProject() && p_toolbar->url.url().startsWith(Project::ref()->projectBaseURL().url())) local = false; + if (!saveToolbar(local, p_toolbar->id)) + return false; + break; + } + case KMessageBox::Continue: + { + bool local = true; + if (Project::ref()->hasProject() && p_toolbar->url.url().startsWith(Project::ref()->projectBaseURL().url())) local = false; + if (!saveToolbar(local, p_toolbar->id, p_toolbar->url)) + return false; + break; + } + case KMessageBox::No: + { + useToolbarGUI = false; + break; + } + case KMessageBox::Cancel: return false; + + } + } + + guiFactory()->removeClient(toolbarGUI); + delete p_toolbar->menu; +//unplug the actions and remove them if they are not used in other places + if (useToolbarGUI) + nodeList = toolbarGUI->domDocument().elementsByTagName("Action"); + else + nodeList = p_toolbar->dom->elementsByTagName("Action"); + for (uint i = 0; i < nodeList.count(); i++) + { + action = actionCollection()->action(nodeList.item(i).toElement().attribute("name")); + if (action && !action->isPlugged()) + { + if (dynamic_cast<TagAction*>(action) && + !dynamic_cast<TagAction*>(action)->isModified()) + { + //take out the action from every toolbar's xmlguiclient + //this avoid a crash when removing a toolbar + QDictIterator<ToolbarEntry> it(m_toolbarList); + while (it.current()) + { + it.current()->guiClient->actionCollection()->take(action); + ++it; + } + delete action; + } + } + } + delete p_toolbar->dom; + delete p_toolbar->guiClient; + m_toolbarList.remove(name); + } + } + + slotToggleDTDToolbar(!allToolbarsHidden()); + emit toolbarRemoved(name); + return true; +} + +/** Show or hide the DTD toolbar */ +void QuantaApp::slotToggleDTDToolbar(bool show) +{ + if (show) + { + ToolbarTabWidget::ref()->show(); + } else + { + ToolbarTabWidget::ref()->hide(); + } + qConfig.enableDTDToolbar = show; + if (ViewManager::ref()->activeView()) + ViewManager::ref()->activeView()->refreshWindow(); +} + +void QuantaApp::slotRefreshActiveWindow() +{ +//FIXME: Find a good way to redraw the editor view when the toolbar height +//changes +// if (ViewManager::ref()->activeView()) + //ViewManager::ref()->activeView()->activated(); +} + + +void QuantaApp::slotShowGroupsForDTEP(const QString& dtepName, bool show) +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + if (dtepName == "clear") + { + w->resetGroupsForDTEPList(); + } else + { + w->enableGroupsForDTEP(dtepName, show); + } + w->setChanged(true); + reparse(false); + } +} + +KURL QuantaApp::projectBaseURL() const +{ + return Project::ref()->projectBaseURL(); +} + +void QuantaApp::slotBuildPrjToolbarsMenu() +{ + static bool buildInProgress = false; + if (buildInProgress) + return; + KURL::List toolbarList; + if (Project::ref()->hasProject()) + { + buildInProgress = true; + toolbarList = QExtFileInfo::allFiles(Project::ref()->toolbarURL(), "*"+toolbarExtension, this); + buildInProgress = false; + projectToolbarFiles->setMaxItems(toolbarList.count()); + for (uint i = 0; i < toolbarList.count(); i++) + { + projectToolbarFiles->addURL(toolbarList[i]); + } + } else + { + projectToolbarFiles->clearURLList(); + } +} + +/** Returns the project (if there is one loaded) or global default encoding. */ +QString QuantaApp::defaultEncoding() +{ + QString encoding = qConfig.defaultEncoding; + if (Project::ref()->hasProject()) + { + encoding = Project::ref()->defaultEncoding(); + } + return encoding.lower(); +} + +void QuantaApp::slotGetUserToolbarFiles(KURL::List *list) +{ + ToolbarEntry *p_toolbar; + QDictIterator<ToolbarEntry> iter(m_toolbarList); + for( ; iter.current(); ++iter ) + { + p_toolbar = iter.current(); + if (p_toolbar->user && p_toolbar->visible) + { + list->append(p_toolbar->url); + } + } +} + +ToolbarEntry *QuantaApp::toolbarByURL(const KURL& url) +{ + ToolbarEntry *p_toolbar = 0L; + QDictIterator<ToolbarEntry> iter(m_toolbarList); + for( ; iter.current(); ++iter ) + { + p_toolbar = iter.current(); + if (p_toolbar->url == url) + { + return p_toolbar; + } + } + + return 0L; +} + + +/** Returns true if all toolbars are hidden, false otherwise. */ +bool QuantaApp::allToolbarsHidden() const +{ + bool result = true; + showDTDToolbar->setEnabled(false); + ToolbarEntry *p_toolbar = 0L; + QDictIterator<ToolbarEntry> iter(m_toolbarList); + for( ; iter.current(); ++iter ) + { + p_toolbar = iter.current(); + if (p_toolbar->visible) + { + showDTDToolbar->setEnabled(true); + result = false; + break; + } + } + + if (!showDTDToolbar->isChecked()) + result = true; + return result; +} + + +void QuantaApp::slotLoadDTEP() +{ + QString dirName = KFileDialog::getExistingDirectory(QString::null, 0, i18n("Select DTEP Directory")); + if (!dirName.isEmpty()) + { + DTDs::ref()->slotLoadDTEP(dirName, true); + } +} + +QString QuantaApp::createDTEPTarball() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + QStringList lst(DTDs::ref()->nickNameList()); + QString nickName = DTDs::ref()->getDTDNickNameFromName(w->getDTDIdentifier()); + bool ok = false; + QString res = KInputDialog::getItem( + i18n( "Send DTD" ), + i18n( "Please select a DTD:" ), lst, lst.findIndex(nickName), false, &ok, this ); + + if (!ok) + return QString::null; + + QString dtdName = DTDs::ref()->getDTDNameFromNickName(res); + + QString prefix="quanta"; + KTempDir* tempDir = new KTempDir(tmpDir); + tempDir->setAutoDelete(true); + tempDirList.append(tempDir); + QString tempFileName=tempDir->name() +"/"+ DTDs::ref()->getDTDNickNameFromName(dtdName).replace(QRegExp("\\s|\\."), "_") + ".tgz"; + + //pack the .tag files and the description.rc into a .tgz file + KTar tar(tempFileName, "application/x-gzip"); + tar.open(IO_WriteOnly); + + KURL dirURL; + dirURL.setPath(DTDs::ref()->find(dtdName)->fileName); + dirURL.setPath(dirURL.directory(false)); + + KURL::List files = QExtFileInfo::allFilesRelative(dirURL, "*", this); + for ( KURL::List::Iterator it_f = files.begin(); it_f != files.end(); ++it_f ) + { + QString name = (*it_f).fileName(); + + QFile file(dirURL.path()+name); + file.open(IO_ReadOnly); + QByteArray bArray = file.readAll(); + tar.writeFile(dirURL.fileName()+"/"+name, "user", "group", bArray.size(), bArray.data()); + file.close(); + + } + tar.close(); + return tempFileName; + } + return QString::null; +} + +void QuantaApp::slotEmailDTEP() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + QString tempFileName = createDTEPTarball(); + if (tempFileName.isNull()) + return; + QStringList dtdFile; + dtdFile += tempFileName; + + TagMailDlg *mailDlg = new TagMailDlg( this, i18n("Send DTEP in Email")); + QString toStr; + QString message = i18n("Hi,\n This is a Quanta Plus [http://quanta.kdewebdev.org] DTEP definition tarball.\n\nHave fun.\n"); + QString titleStr; + QString subjectStr; + + mailDlg->TitleLabel->setText(i18n("Content:")); +/* mailDlg->titleEdit->setFixedHeight(60); + mailDlg->titleEdit->setVScrollBarMode(QTextEdit::Auto); + mailDlg->titleEdit->setHScrollBarMode(QTextEdit::Auto);*/ + if ( mailDlg->exec() ) + { + if ( !mailDlg->lineEmail->text().isEmpty()) + { + toStr = mailDlg->lineEmail->text(); + subjectStr = (mailDlg->lineSubject->text().isEmpty())?i18n("Quanta Plus DTD"):mailDlg->lineSubject->text(); + if ( !mailDlg->titleEdit->text().isEmpty()) + message = mailDlg->titleEdit->text(); + } else + { + KMessageBox::error(this,i18n("No destination address was specified.\n Sending is aborted."),i18n("Error Sending Email")); + delete mailDlg; + return; + } + + kapp->invokeMailer(toStr, QString::null, QString::null, subjectStr, message, QString::null, dtdFile); + } + delete mailDlg; + } +} + +void QuantaApp::slotDownloadDTEP() +{ + if (!m_newDTEPStuff) + m_newDTEPStuff = new QNewDTEPStuff("quanta/dtep", this); + m_newDTEPStuff->downloadResource(); +} + +void QuantaApp::slotUploadDTEP() +{ + QString tempFileName = createDTEPTarball(); + if (tempFileName.isNull()) + return; + if (!m_newDTEPStuff) + m_newDTEPStuff = new QNewDTEPStuff("quanta/dtep", this); +// tempDirList.append(m_newDTEPStuff->uploadResource(tempFileName)); + m_newDTEPStuff->uploadResource(tempFileName); +} + +void QuantaApp::slotSmartTagInsertion() +{ + KAction *action = actionCollection()->action("smart_tag_insertion"); + if(!action) + return; + if(!ViewManager::ref()->activeDocument() || !ViewManager::ref()->activeView() || + ViewManager::ref()->activeDocument()->defaultDTD()->name.contains("HTML", false) == 0) + { + KMessageBox::error(this, "Smart Tag Insertion is available only for (X)HTML for the moment."); + qConfig.smartTagInsertion = false; + (static_cast<KToggleAction* >(action))->setChecked(false); + return; + } + qConfig.smartTagInsertion = (static_cast<KToggleAction* >(action))->isChecked(); +} + +void QuantaApp::slotDownloadTemplate() +{ + if (!m_newTemplateStuff) + m_newTemplateStuff = new QNewTemplateStuff("quanta/template", this); + m_newTemplateStuff->downloadResource(); +} + +void QuantaApp::slotUploadTemplate(const QString &fileName) +{ + if (!m_newTemplateStuff) + m_newTemplateStuff = new QNewTemplateStuff("quanta/template", this); +// tempDirList.append(m_newTemplateStuff->uploadResource(fileName)); + m_newTemplateStuff->uploadResource(fileName); +} + +void QuantaApp::slotDownloadScript() +{ + if (!m_newScriptStuff) + m_newScriptStuff = new QNewScriptStuff("quanta/script", this); + m_newScriptStuff->downloadResource(); +} + +void QuantaApp::slotUploadScript(const QString &fileName) +{ + if (!m_newScriptStuff) + m_newScriptStuff = new QNewScriptStuff("quanta/script", this); +// tempDirList.append(m_newScriptStuff->uploadResource(fileName)); + m_newScriptStuff->uploadResource(fileName); +} + +void QuantaApp::slotDownloadDoc() +{ + if (!m_newDocStuff) + { + m_newDocStuff = new QNewDocStuff("quanta/documentation", this); + connect(m_newDocStuff, SIGNAL(installFinished()), dTab, SLOT(slotRefreshTree())); + } + m_newDocStuff->downloadResource(); +} + +void QuantaApp::slotCodeFormatting() +{ + QuantaView *view = ViewManager::ref()->activeView(); + if(!view || !view->document() || (view->currentViewsLayout() != QuantaView::SourceOnly && + view->hadLastFocus() == QuantaView::VPLFocus)) + { + KMessageBox::error(this, i18n("Code formatting can only be done in the source view.")); + return; + } + view->document()->docUndoRedo->codeFormatting(); +} + +void QuantaApp::slotDocumentProperties() +{ + documentProperties(false); +} + +void QuantaApp::documentProperties(bool forceInsertionOfMinimalTree) +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + if (w->defaultDTD()->name.contains("HTML", false) == 0) + { + KMessageBox::information(this, i18n("The Document Properties Dialog is only for HTML and XHTML.")); + return; + } + htmlDocumentProperties htmlPropsDlg(this, forceInsertionOfMinimalTree); + htmlPropsDlg.exec(); + w->setModified(true); + } +} + +/** Returns the interface number for the currently active editor. */ +int QuantaApp::currentEditorIfNum() const +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + return w->editIf->editInterfaceNumber(); + } else + { + QuantaView * view = ViewManager::ref()->lastActiveEditorView(); + if (view) + return view->document()->editIf->editInterfaceNumber(); + else + return 0L; + } +} + +QString QuantaApp::projectURL() const +{ + return Project::ref()->projectBaseURL().url(); +} + +QStringList QuantaApp::openedURLs() const +{ + QStringList list; + QPtrListIterator<KMdiChildView> childIt(*m_pDocumentViews); + KMdiChildView *view; + QuantaView *qView; + while ( (view = childIt.current()) != 0 ) + { + ++childIt; + qView = dynamic_cast<QuantaView*>(view); + if (qView) + { + Document *w = qView->document(); + if ( w ) + { + list.prepend( QString("%1:%2").arg(w->editIf->editInterfaceNumber()).arg(w->url().url())); + } + } + } + + return list; +} + +void QuantaApp::slotExpandAbbreviation() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + const DTDStruct *dtd = w->currentDTD(); + uint line, col; + w->viewCursorIf->cursorPositionReal(&line, &col); + QString text = w->text(line, 0, line, col - 1); + text = w->findWordRev(text) + " "; + QString textToInsert; + QMap<QString, Abbreviation>::ConstIterator it; + for (it = qConfig.abbreviations.constBegin(); it != qConfig.abbreviations.constEnd(); ++it) + { + bool found = false; + Abbreviation abbrev = it.data(); + if (abbrev.dteps.contains(dtd->name)) + { + QMap<QString, QString>::ConstIterator it2; + for (it2 = abbrev.abbreviations.constBegin(); it2 != abbrev.abbreviations.constEnd(); ++it2) + { + if (it2.key().startsWith(text)) + { + textToInsert = it2.data(); + found = true; + break; + } + } + } + if (found) + break; + } + if (!textToInsert.isEmpty()) + { + w->activateParser(false); + w->editIf->removeText(line, col - text.length() + 1, line, col); + w->activateParser(true); + col -= (text.length() - 1); + int pos = textToInsert.find('|'); + if (pos != -1) + { + text = textToInsert.left(pos); + if (text.contains('\n')) + { + line += text.contains('\n'); + col = text.length() - text.findRev('\n') - 1; + } else + { + col += pos; + } + } + textToInsert.replace('|',""); + w->insertText(textToInsert, false); + w->viewCursorIf->setCursorPositionReal(line, col); + } + } +} + +void QuantaApp::slotUploadFile(QuantaView *view) +{ + if (!view) + view = ViewManager::ref()->activeView(); + bool quick = true; + if (KMessageBox::questionYesNo(this, i18n("Do you want to review the upload?"), i18n("Enable Quick Upload"), i18n("Review"), i18n("Do Not Review"), "EnableQuickUpload") == KMessageBox::Yes) + quick = false; + Project::ref()->slotUploadURL(view->document()->url(), "", quick, false); +} + + +void QuantaApp::slotUploadOpenedFiles() +{ +} + +void QuantaApp::slotDeleteFile(QuantaView *view) +{ + if (!view) + view = ViewManager::ref()->activeView(); + Document *w = view->document(); + KURL url = w->url(); + bool modified = w->isModified(); + w->setModified(false); //don't ask for save + if (QuantaNetAccess::del(url, this, true)) + { + ViewManager::ref()->removeView(view); + } else + w->setModified(modified); +} + + +bool QuantaApp::structTreeVisible() const +{ + return StructTreeView::ref()->isVisible(); +} + +QStringList QuantaApp::tagAreas(const QString &tag, bool includeCoordinates, bool skipFoundContent) const +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + return w->tagAreas(tag, includeCoordinates, skipFoundContent); + else + return QStringList(); +} + +QString QuantaApp::documentFolderForURL(const QString &url) +{ + KURL u = KURL::fromPathOrURL(url); + return Project::ref()->documentFolderForURL(u).url(); +} + +QString QuantaApp::urlWithPreviewPrefix(const QString &url) +{ + KURL u = KURL::fromPathOrURL(url); + return Project::ref()->urlWithPrefix(u).url(); +} + +void QuantaApp::addFileToProject(const QString &url) +{ + if (Project::ref()->hasProject()) + { + Project::ref()->slotInsertFile(KURL::fromPathOrURL(url)); + } +} + +void QuantaApp::addFolderToProject(const QString &url) +{ + if (Project::ref()->hasProject()) + { + Project::ref()->slotAddDirectory(KURL::fromPathOrURL(url), true); + } +} + +void QuantaApp::uploadURL(const QString &url, const QString& profile, bool markOnly) +{ + if (Project::ref()->hasProject()) + { + Project::ref()->slotUploadURL(url, profile, true, markOnly); + } +} + +void QuantaApp::slotAutosaveTimer() +{ + m_config->reparseConfiguration(); + QPtrListIterator<KMdiChildView> childIt(*m_pDocumentViews); + KMdiChildView *view; + QuantaView *qView; + while ( (view = childIt.current()) != 0 ) + { + ++childIt; + qView = dynamic_cast<QuantaView*>(view); + if (qView) + { + Document *w = qView->document(); + if ( w ) + { + w->createBackup(m_config); + } + } + } + +} + +/** Get script output */ +void QuantaApp::slotGetScriptOutput(KProcess* ,char* buf,int buflen) +{ + QCString tmp( buf, buflen + 1 ); + m_scriptOutput = QString::null; + m_scriptOutput = QString::fromLocal8Bit(tmp).remove(" "); +} + +/** Get script error*/ +void QuantaApp::slotGetScriptError(KProcess* ,char* buf,int buflen) +{ +//TODO: Implement some error handling? + Q_UNUSED(buf); + Q_UNUSED(buflen); +} +/** Notify when process exits*/ +void QuantaApp::slotProcessExited(KProcess* process) +{ + slotProcessTimeout(); + delete process; +} + +/** Timeout occurred while waiting for some network function to return. */ +void QuantaApp::slotProcessTimeout() +{ + if (m_loopStarted) + { + qApp->exit_loop(); + m_loopStarted = false; + } +} + +void QuantaApp::slotActivePartChanged(KParts::Part * part) +{ + if (m_oldKTextEditor && part) // if part == 0L the pointer m_oldKTextEditor is not useable + { + guiFactory()->removeClient(m_oldKTextEditor); + m_oldKTextEditor = 0L; + } + createGUI(part); + QWidget * activeWid = m_partManager->activeWidget(); + if ( activeWid && activeWid->inherits("KTextEditor::View")) + { + m_oldKTextEditor = dynamic_cast<KTextEditor::View *>(activeWid); + if (m_oldKTextEditor) + guiFactory()->addClient(m_oldKTextEditor); + } +} + +void QuantaApp::slotConvertCase() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + w->convertCase(); + } +} + +void QuantaApp::slotReloadStructTreeView(bool groupOnly) +{ + Document *w = ViewManager::ref()->activeDocument(); + if (StructTreeView::ref()->isVisible() && w) + { + StructTreeView::ref()->setParsingDTDs(w->groupsForDTEPs()); + int expandLevel = qConfig.expandLevel; + if (expandLevel == 0) + expandLevel = 40; + StructTreeView::ref()->slotReparse(w, baseNode, expandLevel, groupOnly); + } else + StructTreeView::ref()->slotReparse(0L, 0L, 0); //delete the tree + + if (!groupOnly && w) + { + m_annotationOutput->clearAnnotations(); + w->clearAnnotations(); + Node *node = baseNode; + while (node) + { + if (node->tag->type == Tag::Comment) + { + Node *n = node; + if (node->child) + { + n = node->child; + } + Tag *commentTag = n->tag; + QString text = commentTag->tagStr(); + int pos = text.find("@annotation"); + if (pos != -1) + { + pos += 11; + QString receiver; + if (text[pos] == '(') + { + int p = pos; + pos = text.find(')'); + if (pos != -1) + { + receiver = text.mid(p + 1, pos - p - 1); + pos += 2; + } + } else + pos++; + text = text.mid(pos).stripWhiteSpace(); + int l, c; + if (n->next) + n->next->tag->beginPos(l, c); + else + n->tag->endPos(l, c); + commentTag->write()->addAnnotation(l, qMakePair(text, receiver)); + } + } + node = node->nextSibling(); + } + } +} + +QString QuantaApp::saveCurrentFile() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) + return QString::null; + if (w->isModified()) + { + if ( KMessageBox::questionYesNo(this, + i18n("The file must be saved before external preview.\n" + "Do you want to save and preview?"), + i18n("Save Before Preview"), + KStdGuiItem::save(),KStdGuiItem::dontSave(), "AskForSaveBeforePreview") + == KMessageBox::Yes) + { + if (w->isUntitled()) + { + quantaApp->slotFileSaveAs(); + } + else + { + w->save(); + } + } else + { + return QString::null; + } + } + KURL url = Project::ref()->urlWithPrefix(w->url()); + return url.url(); +} + +void QuantaApp::slotReportBug() +{ + KAboutData aboutData( "quanta", I18N_NOOP("Quanta"), QUANTA_VERSION); + KBugReport bugReportDlg(this, true, &aboutData); + bugReportDlg.exec(); +} + +void QuantaApp::slotNewPart(KParts::Part *newPart, bool setActiv) +{ + m_partManager->addPart(newPart, setActiv); +} + + +bool QuantaApp::queryClose() +{ + if (m_quantaInit) + return false; //not loaded completely + bool canExit = true; + if (quantaStarted) + { + m_config->setGroup("General Options"); + QStringList urlStrList; + KURL::List urlList = ViewManager::ref()->openedFiles(); + KURL u; + for (KURL::List::Iterator it = urlList.begin(); it != urlList.end(); ++it) + { + KURL u = *it; + u.setPass(""); + urlStrList += u.url(); + } + m_config->writePathEntry("List of opened files", urlStrList); + QStringList encodings; + QValueList<Document*> documents = ViewManager::ref()->openedDocuments(); + for (QValueList<Document*>::ConstIterator it = documents.constBegin(); it != documents.constEnd(); ++it) + { + if (!(*it)->isUntitled()) + { + QString encoding = defaultEncoding(); + KTextEditor::EncodingInterface* encodingIf = dynamic_cast<KTextEditor::EncodingInterface*>((*it)->doc()); + if (encodingIf) + encoding = encodingIf->encoding(); + if (encoding.isEmpty()) + encoding = "utf8"; //final fallback + encodings += encoding; + } + } + m_config->writePathEntry("Encoding of opened files", encodings); + parser->setParsingEnabled(false); + canExit = ViewManager::ref()->closeAll(false); + if (canExit) + canExit = Project::ref()->queryClose(); + if (canExit) + canExit = removeToolbars(); + parser->setParsingEnabled(true); + } + if (canExit) + { + saveOptions(); + // kdDebug(24000) << "Quanta will exit" << endl; + emit eventHappened("quanta_exit", QDateTime::currentDateTime().toString(Qt::ISODate), QString::null); + } else + slotFileNew(); + return canExit; +} + +void QuantaApp::saveOptions() +{ + if (m_config) + { + m_config->setGroup ("General Options"); + + m_config->writeEntry("Geometry", size()); + + m_config->writeEntry("Show Toolbar", toolBar("mainToolBar")->isVisible()); + m_config->writeEntry("Show DTD Toolbar", showDTDToolbar->isChecked()); + + m_config->writeEntry("Markup mimetypes", qConfig.markupMimeTypes ); + m_config->writeEntry("Script mimetypes", qConfig.scriptMimeTypes ); + m_config->writeEntry("Image mimetypes", qConfig.imageMimeTypes ); + m_config->writeEntry("Text mimetypes", qConfig.textMimeTypes ); + + m_config->writeEntry("Capitals for tags", qConfig.tagCase); + m_config->writeEntry("Capitals for attr", qConfig.attrCase); + m_config->writeEntry("Attribute quotation", qConfig.attrValueQuotation=='"' ? "double":"single"); + m_config->writeEntry("Close tag if optional", qConfig.closeOptionalTags); + m_config->writeEntry("Close tags", qConfig.closeTags); + m_config->writeEntry("Auto completion", qConfig.useAutoCompletion); + m_config->writeEntry("Update Closing Tags", qConfig.updateClosingTags); + m_config->writeEntry("Replace Accented Chars", qConfig.replaceAccented); + m_config->writeEntry("Replace Chars Not In Current Encoding", qConfig.replaceNotInEncoding); + + m_config->writeEntry("Default encoding", qConfig.defaultEncoding); + m_config->writeEntry("Default DTD", qConfig.defaultDocType); + + m_config->writeEntry("Preview area", qConfig.previewPosition); + m_config->writeEntry("Documentation area", qConfig.docPosition); + + m_config->writeEntry("Smart Tag Insertion", qConfig.smartTagInsertion); + + m_config->writeEntry("Window layout", qConfig.windowLayout); + m_config->writeEntry("Follow Cursor", StructTreeView::ref()->followCursor() ); + //If user choose the timer interval, it needs to restart the timer too + m_config->writeEntry("Autosave interval", qConfig.autosaveInterval); + m_config->writePathEntry("Top folders", fTab->topURLList.toStringList()); + QStringList aliasList; + for (KURL::List::Iterator it2 = fTab->topURLList.begin(); it2 != fTab->topURLList.end(); ++it2) + { + aliasList.append(fTab->topURLAliases[(*it2).url()]); + } + m_config->writePathEntry("Top folder aliases", aliasList); + m_config->writeEntry("Version", QUANTA_VERSION); // version + m_config->writeEntry("Close Buttons", qConfig.showCloseButtons); + m_config->writeEntry("MDI mode", mdiMode()); + m_config->writeEntry("MDI style", qConfig.toolviewTabs); + m_config->writeEntry("IconTextMode", ToolbarTabWidget::ref()->iconText()); + + m_config->deleteGroup("RecentFiles"); + fileRecent->saveEntries(m_config); + m_config->writeEntry("Show Hidden Files", qConfig.showHiddenFiles); + m_config->writeEntry("Save Local Trees", qConfig.saveTrees); + + m_config->setGroup("Parser options"); + m_config->writeEntry("Instant Update", qConfig.instantUpdate); + m_config->writeEntry("Show Empty Nodes", qConfig.showEmptyNodes); + m_config->writeEntry("Show Closing Tags", qConfig.showClosingTags); + m_config->writeEntry("Refresh frequency", qConfig.refreshFrequency); + m_config->writeEntry("Expand Level", qConfig.expandLevel); + m_config->writeEntry("Show DTD Select Dialog", qConfig.showDTDSelectDialog); + + manager()->writeConfig(m_config); + saveMainWindowSettings(m_config); + writeDockConfig(m_config); + // save settings of treeviews + fTab->saveLayout( m_config, fTab->className() ); + ProjectTreeView::ref()->saveLayout(m_config, ProjectTreeView::ref()->className() ); + TemplatesTreeView::ref()->saveLayout(m_config, TemplatesTreeView::ref()->className() ); + scriptTab->saveLayout(m_config, scriptTab->className() ); + + m_config->sync(); + } +} + +void QuantaApp::statusBarTimeout() +{ + statusBar()->changeItem("", IDS_STATUS); +} + +QStringList QuantaApp::selectors(const QString &tag) +{ + return dcopQuanta->selectors(tag); +} + +QStringList QuantaApp::idSelectors() +{ + return dcopQuanta->idSelectors(); +} + +void QuantaApp::slotEditCurrentTag() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + if (parser->parsingNeeded()) + baseNode = parser->rebuild(w); + //avoid reparsing while the dialog is shown + typingInProgress = true; + slotEnableIdleTimer(false); + uint line,col; + w->viewCursorIf->cursorPositionReal(&line, &col); + Node *node = parser->nodeAt(line, col, false); + bool isUnknown = true; + QString tagName; + if (node && node->tag) + { + Tag *tag = new Tag(*node->tag); //create a copy, as a reparse might happen meantime and that would make node (and node->tag) invalid + tagName = tag->name; + if ( QuantaCommon::isKnownTag(tag->dtd()->name,tagName) ) + { + isUnknown = false; + QString selection; + if (w->selectionIf) + selection = w->selectionIf->selection(); + TagDialog *dlg = new TagDialog( QuantaCommon::tagFromDTD(tag->dtd(),tagName), tag, selection, ViewManager::ref()->activeView()->baseURL() ); + if (dlg->exec()) + { + w->changeTag(tag, dlg->getAttributes() ); + } + delete tag; + delete dlg; + } + } + if (isUnknown) + { + const DTDStruct *dtd = w->defaultDTD(); + if (dtd->family == Xml) + { + QString currentLine = w->editIf->textLine(line); + int sPos = currentLine.findRev('<', col); + if (sPos != -1) + { + int ePos = currentLine.find('>', col); + if (ePos != -1) + { + AreaStruct area(line, sPos, line, ePos); + Tag *tag = new Tag(area, w, dtd, true); + if ( QuantaCommon::isKnownTag(dtd->name, tag->name) ) + { + isUnknown = false; + QString selection; + if (w->selectionIf) + selection = w->selectionIf->selection(); + TagDialog *dlg = new TagDialog( QuantaCommon::tagFromDTD(dtd, tag->name), tag, selection, ViewManager::ref()->activeView()->baseURL() ); + if (dlg->exec()) + { + w->changeTag(tag, dlg->getAttributes() ); + } + delete dlg; + } + delete tag; + } + } + } + } + typingInProgress = false; + slotEnableIdleTimer(true); + if (isUnknown) + { + QString message = i18n("Unknown tag: %1").arg(tagName); + slotStatusMsg( message ); + } +} + +void QuantaApp::slotSelectTagArea(Node *node) +{ + int bLine, bCol, eLine, eCol; + Tag *tag = node->tag; + tag->beginPos(bLine, bCol); + if (tag->single || !node->next) + { + tag->endPos(eLine, eCol); + } else + if (tag->closingMissing && node->child) + { + Node *childNode = node->child; + while (childNode->child || childNode->next) + { + if (childNode->next) + { + childNode = childNode->next; + } else + { + childNode = childNode->child; + } + } + childNode->tag->endPos(eLine, eCol); + } else + { + node->next->tag->endPos(eLine, eCol); + } + quantaApp->selectArea(bLine, bCol, eLine, eCol + 1); +} + +void QuantaApp::slotSelectTagArea() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) + return; + uint line,col; + w->viewCursorIf->cursorPositionReal(&line, &col); + Node *node = parser->nodeAt(line, col); + slotSelectTagArea(node); +} + +void QuantaApp::slotFrameWizard() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) + return; + QStringList list = w->tagAreas("frameset", true, true); + bool framesetExists = !list.isEmpty(); + int bl, bc, el, ec; + bl = bc = el = ec = 0; + QStringList l; + QStringList l2; + QuantaCommon::normalizeStructure(list[0],l2); + if (framesetExists) + { + l = QStringList::split('\n',list[0],true); + QStringList coordList = QStringList::split(',', l[0], true); + bl = coordList[0].toInt(); + bc = coordList[1].toInt(); + el = coordList[2].toInt(); + ec = coordList[3].toInt(); + l.remove(l.begin()); + } + + FrameWizard dlg(this); + + if (!w->isUntitled()) + { + dlg.setSaved(true); + } + dlg.setMarkupLanguage(w->currentDTD(true)->name); + dlg.loadExistingFramesetStructure(l2); + + if ( dlg.exec() ) + { + QString tag = +QString("\n")+dlg.generateFramesetStructure()+QString("\n"); + if (framesetExists) + { + w->activateParser(false); + w->editIf->removeText(bl, bc, el, ec + 1); + w->viewCursorIf->setCursorPositionReal((uint)bl, (uint)bc); + w->activateParser(true); + } + w->insertTag(tag); + } +} + + +/** edit/insert CSS */ +void QuantaApp::slotInsertCSS() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + + uint line, col; + int bLine, bCol, eLine, eCol; + bLine = bCol = eLine = eCol = 0; + w->viewCursorIf->cursorPositionReal(&line, &col); + if (line == 0 && col == 0) + col++; + parser->rebuild(w); + Node *node = parser->nodeAt(line, col, false); + unsigned int lastLine = w->editIf->numLines() - 1; + unsigned int lastCol = w->editIf->lineLength(lastLine); + Node *styleNode = node; + + if (styleNode->tag->type == Tag::XmlTagEnd && styleNode->prev) + styleNode = styleNode->prev; + + while (styleNode && styleNode->parent && styleNode->tag->name.lower() != "style" && styleNode->tag->dtd()->name == "text/css") + styleNode = styleNode->parent; + + Node *parentNode = node; + if (parentNode->tag->type == Tag::XmlTagEnd && parentNode->prev) + parentNode = parentNode->prev; + else + while (parentNode && parentNode->parent && + parentNode->tag->type != Tag::XmlTag) + parentNode = parentNode->parent; + QString fullDocument = w->editIf->text().stripWhiteSpace(); + + if (styleNode->tag->name.lower() == "comment block" && styleNode->parent) { + if (styleNode->parent->tag->name.lower() == "style") { + styleNode = styleNode->parent; + } + } + + if (styleNode && styleNode->tag->name.lower() == "style" && styleNode->next) //inside <style> invoke the selector editor + { + styleNode->tag->endPos(bLine, bCol); + QString header(w->text(0, 0, bLine, bCol));// beginning part of the file + styleNode->next->tag->endPos(eLine, eCol); + QString footer("</style>" + w->text(eLine, eCol+1, lastLine, lastCol)); // ending part of the file + + styleNode->next->tag->beginPos(eLine, eCol); + QString styleTagContent(w->text(bLine, bCol+1, eLine, eCol-1).remove("<!--").remove("-->"));// <style></style> block content + kdDebug(24000) << "Style tag contains: " << endl << styleTagContent << endl; + CSSSelector *dlg = new CSSSelector; + + dlg->setCallingFrom("XHTML"); + QFileInfo fi(ViewManager::ref()->currentURL()); + dlg->setFileToPreview(projectBaseURL().path() + fi.baseName()); + + + dlg->setHeader(header); + dlg->setFooter(footer); + + dlg->loadCSSContent(styleTagContent); + if(!dlg->errorOnProcessingStylesheet()) + if( dlg->exec() ){ + w->activateParser(false); + w->editIf->removeText(bLine, bCol+1, eLine, eCol); + w->viewCursorIf->setCursorPositionReal((uint)bLine, (uint)bCol+1); + w->activateParser(true); + w->insertTag( /*"\n<!--" + */ dlg->generateFormattedStyleSection() /*+ "-->\n"*/); + } + delete dlg; + + } else + if (!node || w->currentDTD(true)->name == "text/css") + { + kdDebug(24000) << "[CSS editor] This is a pure CSS document" << endl; + + CSSSelector *dlg = new CSSSelector; + + dlg->setCallingFrom("CSS"); + + if (!fullDocument.isEmpty()) + dlg->loadCSSContent(fullDocument); + dlg->enableApplyToFile(); + if(!dlg->errorOnProcessingStylesheet()) + if (dlg->exec()) + { + w->activateParser(false); + w->editIf->clear(); + w->activateParser(true); + w->insertTag(dlg->generateFormattedStyleSection()); + } + delete dlg; + } else + if (parentNode && parentNode->tag->type == Tag::XmlTag) + { + kdDebug(24000) << "[CSS editor] We will add a style attribute to: " << parentNode->tag->name << endl; + CSSEditor *dlg = new CSSEditor(this); + QFileInfo fi(ViewManager::ref()->currentURL()); + dlg->setFileToPreview(projectBaseURL().path() + fi.baseName(),false); + + + parentNode->tag->beginPos(bLine, bCol); + parentNode->tag->endPos(eLine, eCol); + dlg->setFooter(">" + w->text(eLine, eCol + 1, lastLine, lastCol)); + + QString temp; + if (parentNode->tag->hasAttribute("style")) + { + dlg->setInlineStyleContent(parentNode->tag->attributeValue("style")); + Tag tempTag(*(parentNode->tag)); + tempTag.deleteAttribute("style"); + temp = tempTag.toString(); + + } else { + dlg->setInlineStyleContent(QString::null); + temp = parentNode->tag->toString(); + } + //using QString::mid sometimes generates strange results; maybe this is due to a (random) blank in temp + temp = temp.left(temp.length()-1);//remove > + temp = temp.right(temp.length()-1);//remove < + dlg->setHeader(w->text(0, 0, bLine, bCol) + temp); + + dlg->initialize(); + if( dlg->exec() ) + { + w->changeTagAttribute(parentNode->tag, "style", dlg->generateProperties()); + } + delete dlg; + } else + KMessageBox::sorry(this, i18n("The CSS Editor cannot be invoked here.\nTry to invoke it on a tag or on a style section.")); +} + +/** for <a href=mailto> tag */ +void QuantaApp::slotTagMail() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + + TagMailDlg *mailDlg = new TagMailDlg( this, i18n("Email Link (mailto)")); + + if ( mailDlg->exec() ) { + QString tag = QString(QuantaCommon::tagCase("<a")); + + if ( !QString(mailDlg->lineEmail->text()).isEmpty()) + { + tag += QuantaCommon::attrCase(" href=")+qConfig.attrValueQuotation+"mailto:"+mailDlg->lineEmail->text(); + if ( !QString(mailDlg->lineSubject->text()).isEmpty()) + tag += "?subject="+KURL::encode_string(mailDlg->lineSubject->text()); + tag += qConfig.attrValueQuotation; + } + + if ( !QString(mailDlg->titleEdit->text()).isEmpty()) + tag += QuantaCommon::attrCase(" title=")+qConfig.attrValueQuotation+mailDlg->titleEdit->text()+qConfig.attrValueQuotation; + tag += QString(">"); + w->insertTag(tag,QuantaCommon::tagCase("</a>")); + } + delete mailDlg; +} + +/** Add the starting and closing text for a +user specified tag. */ +void QuantaApp::slotTagMisc() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + + static QString element = ""; + static bool addClosingTag = true; + + TagMiscDlg * miscDlg = new TagMiscDlg( this, 0L, addClosingTag, element ); + + if ( miscDlg->exec() ) + { + QString tag; + element = miscDlg->elementTagName(); + element.remove('<'); + element.remove('>'); + if ( !element.isEmpty()) + { + tag += "<" + QuantaCommon::attrCase(element)+">"; + if ( (addClosingTag = miscDlg->addClosingTag()) == true) + { + w->insertTag(tag,QuantaCommon::tagCase( "</"+QuantaCommon::attrCase(element)+">")); + } else + { + w->insertTag(tag,QuantaCommon::tagCase("")); + } + } + } + delete miscDlg; +} + + +/** do quick list */ +void QuantaApp::slotTagQuickList() +{ + QString space =" " ; + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + + TagQuickListDlg *listDlg = new TagQuickListDlg(this,i18n("Generate List")); + if ( listDlg->exec() ) { + int i; + int n = listDlg->spinBoxRows->value(); + + QString tag; + if ( listDlg->radioOrdered->isChecked()) + tag = QString("<ol>\n")+space; + else tag = QString("<ul>\n")+space; + + for ( i=0;i<n;i++) + if ( qConfig.closeTags ) + tag += QString(" <li> </li>\n")+space; + else + tag += QString(" <li> \n")+space; + + if ( listDlg->radioOrdered->isChecked()) + tag += QString("</ol>"); + else tag += QString("</ul>"); + + w->insertTag( QuantaCommon::tagCase(tag)); + } + delete(listDlg); +} + +void QuantaApp::slotTagEditTable() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + baseNode = parser->rebuild(w); + QStringList list = w->tagAreas("table", true, false); + bool tableExists = false; + uint line, col; + w->viewCursorIf->cursorPositionReal(&line, &col); + int bl, bc, el, ec; + int bLine, bCol, eLine, eCol; + bLine = bCol = eLine = eCol = 0; + QStringList l; + QStringList l2; + for (QStringList::Iterator it = list.begin(); it != list.end(); ++it) + { + QuantaCommon::normalizeStructure(*it, l2); + l = QStringList::split('\n', *it, true); + QStringList coordList = QStringList::split(',', l[0], true); + bl = coordList[0].toInt(); + bc = coordList[1].toInt(); + el = coordList[2].toInt(); + ec = coordList[3].toInt(); + if (QuantaCommon::isBetween(line, col, bl, bc, el, ec) == 0) + { + tableExists = true; + bLine = bl; + bCol = bc; + eLine = el; + eCol = ec; + } + l.remove(l.begin()); + } + + TableEditor editor; + bool tableRead = true; + if (tableExists) + { + editor.setBaseURL(ViewManager::ref()->activeView()->baseURL()); + tableRead = editor.setTableArea(bLine, bCol, eLine, eCol, parser); + if (!tableRead) + { + KMessageBox::error(this, i18n("The table structure is invalid. Most probably you forgot to close some tags."), i18n("Cannot Read Table")); + } + } else + { + Node *node = parser->nodeAt(line, col); + const DTDStruct *dtd = w->defaultDTD(); + if (node) + dtd = node->tag->dtd(); + bLine = line; + bCol = col; + eLine = line; + eCol = col; + editor.createNewTable(w, dtd); + } + if (tableRead && editor.exec()) + { + QString tableString = editor.readModifiedTable(); + w->activateParser(false); +//#ifdef BUILD_KAFKAPART +// if(w->editIfExt) +// w->editIfExt->editBegin(); +//#endif + if (eLine != bLine || (eLine == bLine && eCol != bCol)) + w->editIf->removeText(bLine, bCol, eLine, eCol + 1); + w->viewCursorIf->setCursorPositionReal((uint)bLine, (uint)bCol); + w->insertText(tableString, false); +//#ifdef BUILD_KAFKAPART +// if(w->editIfExt) +// w->editIfExt->editEnd(); +//#endif + w->viewCursorIf->setCursorPositionReal(line, col); + reparse(true); + } +} + +/** Open color Dialog and insert color in the text */ +void QuantaApp::slotTagColor() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + QColor color; + + if (KColorDialog::getColor( color )) { + char c[8]; + sprintf(c,"#%2X%2X%2X",color.red(),color.green(),color.blue()); + for (int i=0;i<7;i++) if (c[i] == ' ') c[i] = '0'; + QString scolor = (char *)c; + w->insertTag(scolor); + } +} + +/** insert date */ +void QuantaApp::slotTagDate() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + time_t tektime; + time( &tektime); + QString stime = ctime( &tektime); + + w->insertTag( stime); +} + +/** for select form */ +void QuantaApp::slotTagSelect() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + w->insertTag(QuantaCommon::tagCase("<select")+ QuantaCommon::attrCase("name")+QuantaCommon::tagCase("=\"\"><option>"),QuantaCommon::tagCase("</select>")); +} + +void QuantaApp::slotViewInKFM() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + if (w->isModified()) + { + if ( KMessageBox::questionYesNo(this, + i18n("The file must be saved before external preview.\n" + "Do you want to save and preview?"), + i18n("Save Before Preview"), + KStdGuiItem::save(),KStdGuiItem::dontSave(), "AskForSaveBeforePreview") + == KMessageBox::Yes) + { + if (w->isUntitled()) + { + quantaApp->slotFileSaveAs(); + } + else + { + w->save(); + } + } else + { + return; + } + } + if ( !w->isUntitled() ) + { + KProcess *show = new KProcess(this); + KURL url = Project::ref()->urlWithPrefix(w->url()); + *show << "kfmclient" << "newTab" << url.url(); + show->start( KProcess::DontCare ); + } +} + +void QuantaApp::slotViewInLynx() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + if (w->isModified()) + { + if ( KMessageBox::questionYesNo(this, + i18n("The file must be saved before external preview.\n" + "Do you want to save and preview?"), + i18n("Save Before Preview"),KStdGuiItem::save(),KStdGuiItem::dontSave(), "AskForSaveBeforePreview") + == KMessageBox::Yes) + { + if (w->isUntitled()) + { + quantaApp->slotFileSaveAs(); + } + else + { + w->save(); + } + } else + { + return; + } + } + if ( !w->isUntitled() ) + { + KProcess *show = new KProcess(this); + KURL url = Project::ref()->urlWithPrefix(w->url()); + *show << "konsole" + << "--nohist" + << "--notoolbar" + << "--caption" + << "Lynx Preview - Quanta" + << "-e" + << "lynx" + << url.url(); + show->start( KProcess::DontCare ); + } +} + +/** insert clipboard contents (but quote them for HTML first) */ +void QuantaApp::slotPasteHTMLQuoted() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + QClipboard *cb = qApp->clipboard(); + QString text = cb->text(); + + if ( ( !text.isNull() ) && (!text.isEmpty() ) ) + { + text.replace( "&", "&" ); + text.replace( "<", "<" ); + text.replace( "\"", """ ); + text.replace( ">", ">" ); + +//TODO: Replace only the chars not present in the current encoding. + QString encoding = defaultEncoding(); + KTextEditor::EncodingInterface* encodingIf = dynamic_cast<KTextEditor::EncodingInterface*>(w->doc()); + if (encodingIf) + encoding = encodingIf->encoding(); + if (encoding != "UTF-8" && encoding != "UTF-16" && encoding != "ISO-10646-UCS-2") + { + for ( QStringList::Iterator it = charList.begin(); it != charList.end(); ++it ) + { + QString s = *it; + int begin = s.find("(&#") + 3; + if (begin == 1) + continue; + int length = s.find(";)") - begin + 1; + s = s.mid(begin, length - 1); + bool ok; + int code = s.toInt(&ok); + if (!ok || code < 191) + continue; + text.replace(QChar(code), QString("&#%1;").arg(s)); + } + } + unsigned int line, col; + w->viewCursorIf->cursorPositionReal(&line, &col); + w->editIf->insertText(line, col, text ); + } + } +} + +/** insert clipboard contents (but quote them as a URL first) */ +void QuantaApp::slotPasteURLEncoded() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + QClipboard *cb = qApp->clipboard(); + QString text = cb->text(); + + if ( ( !text.isNull() ) && (!text.isEmpty() ) ) + { + text = KURL::encode_string( text ); + unsigned int line, col; + w->viewCursorIf->cursorPositionReal(&line, &col); + w->editIf->insertText(line, col, text ); + } + } +} + + +/** Kate related slots. */ + +void QuantaApp::slotUndo () +{ + Document *w = ViewManager::ref()->activeDocument(); + if(ViewManager::ref()->activeView()->hadLastFocus() == QuantaView::VPLFocus && w) + { + /**MessageBox::information(this, i18n("VPL does not support this functionality yet."), + QString::null, "show undo unavailable");*/ + w->docUndoRedo->undo(); + return; + } + if (w) + { + bool updateClosing = qConfig.updateClosingTags; + qConfig.updateClosingTags = false; +//#ifdef BUILD_KAFKAPART +// write()->docUndoRedo.undo(false); +//#else + KTextEditor::UndoInterface* undoIf = dynamic_cast<KTextEditor::UndoInterface*>(w->doc()); + if (undoIf) + undoIf->undo(); +//#endif + qConfig.updateClosingTags = updateClosing; + } +} + +void QuantaApp::slotRedo () +{ + Document *w = ViewManager::ref()->activeDocument(); + if(ViewManager::ref()->activeView()->hadLastFocus() == QuantaView::VPLFocus) + { + /**KMessageBox::information(this, i18n("VPL does not support this functionality yet."), + QString::null, "show redo unavailable");*/ + w->docUndoRedo->redo(); + return; + } + if (w) + { + bool updateClosing = qConfig.updateClosingTags; + qConfig.updateClosingTags = false; +//#ifdef BUILD_KAFKAPART +// write()->docUndoRedo.redo(false); +//#else + KTextEditor::UndoInterface* undoIf = dynamic_cast<KTextEditor::UndoInterface*>(w->doc()); + if (undoIf) + undoIf->redo(); +//#endif + qConfig.updateClosingTags = updateClosing; + } +} + +/** insert special character */ +void QuantaApp::slotInsertChar() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (w) + { + SpecialCharDialog S( this, "special_char" ); + if (S.exec()) + w->insertTag(S.selection()); + } +} + +void QuantaApp::slotCut() +{ + QuantaView* view = ViewManager::ref()->activeView(); + Document *w = ViewManager::ref()->activeDocument(); + if(view && view->hadLastFocus() == QuantaView::VPLFocus) + { + /* + KMessageBox::information(this, i18n("Sorry, VPL does not support this functionality yet."), + QString::null, "show cut unavailable"); + */ + KafkaDocument::ref()->slotCut(); + return; + } + if(w) + { + KTextEditor::ClipboardInterface* clip = dynamic_cast<KTextEditor::ClipboardInterface*>(w->view()); + if(clip) + clip->cut(); + } +} + +void QuantaApp::slotCopy() +{ + QuantaView* view = ViewManager::ref()->activeView(); + Document *w = ViewManager::ref()->activeDocument(); + if(view && view->hadLastFocus() == QuantaView::VPLFocus) + { + //KMessageBox::information(this, i18n("Sorry, VPL does not support this functionality yet."), + //QString::null, "show copy unavailable"); + KafkaDocument::ref()->slotCopy(); + return; + } + if (w) + { + KTextEditor::ClipboardInterface* clip = dynamic_cast<KTextEditor::ClipboardInterface*>(w->view()); + if (clip) + clip->copy(); + } + if (m_htmlPart->view()->hasFocus()) + { + QString selection = m_htmlPart->selectedText(); + QClipboard *cb = QApplication::clipboard(); + cb->setText(selection, QClipboard::Clipboard); + } + else + if (m_htmlPartDoc->view()->hasFocus()) + { + QString selection = m_htmlPartDoc->selectedText(); + QClipboard *cb = QApplication::clipboard(); + cb->setText(selection, QClipboard::Clipboard); + } + +} + +void QuantaApp::slotPaste() +{ + QuantaView* view = ViewManager::ref()->activeView(); + Document *w = ViewManager::ref()->activeDocument(); + if(view && view->hadLastFocus() == QuantaView::VPLFocus) + { + //KMessageBox::information(this, i18n("Sorry, VPL does not support this functionality yet."), + //QString::null, "show paste unavailable"); + KafkaDocument::ref()->slotPaste(); + return; + } + if(w) + { + KTextEditor::ClipboardInterface* clip = dynamic_cast<KTextEditor::ClipboardInterface*>(w->view()); + if(clip) + clip->paste(); + } +} + +Node *QuantaApp::showTagDialogAndReturnNode(const QString &tag, const QString &attr) +{ + Node *n = 0L; + QuantaView *view = ViewManager::ref()->activeView(); + if(view && view->document()) + { + Document *w = view->document(); + + QString selection; + if(view->hadLastFocus() == QuantaView::VPLFocus) + selection = KafkaDocument::ref()->getKafkaWidget()->selectedText(); + + TagDialog *dlg = new TagDialog(QuantaCommon::tagFromDTD(w->getDTDIdentifier(),tag), selection, attr, ViewManager::ref()->activeView()->baseURL()); + if (dlg->exec()) + { + n= dlg->buildNode(w); + } + + delete dlg; + } + return n; +} + + +void QuantaApp::slotShowSourceEditor() +{ + if (!showVPLAction->isChecked() && !showSourceAction->isChecked() && !showVPLSourceAction->isChecked()) + showSourceAction->activate(); + else + ViewManager::ref()->activeView()->slotSetSourceLayout(); +} + +void QuantaApp::slotShowVPLAndSourceEditor() +{ + if (!showVPLAction->isChecked() && !showSourceAction->isChecked() && !showVPLSourceAction->isChecked()) + showSourceAction->activate(); + else + ViewManager::ref()->activeView()->slotSetSourceAndVPLLayout(); +} + +void QuantaApp::slotShowVPLOnly() +{ + if (!showVPLAction->isChecked() && !showSourceAction->isChecked() && !showVPLSourceAction->isChecked()) + showSourceAction->activate(); + else + ViewManager::ref()->activeView()->slotSetVPLOnlyLayout(); +} + +void QuantaApp::initTabWidget(bool closeButtonsOnly) +{ + KTabWidget *tab = tabWidget(); + KAcceleratorManager::setNoAccel(tab); + if (tab) + { + if (qConfig.showCloseButtons == "ShowAlways") + { + tab->setHoverCloseButton(true); + tab->setHoverCloseButtonDelayed(false); + } else + if (qConfig.showCloseButtons == "ShowDelayed") + { + tab->setHoverCloseButton(true); + tab->setHoverCloseButtonDelayed(true); + } else + { + tab->setHoverCloseButton(false); + } + if (!closeButtonsOnly) + { + tab->setTabReorderingEnabled(true); + tab->setTabPosition(QTabWidget::Bottom); + connect(tab, SIGNAL( contextMenu( QWidget *, const QPoint & ) ), ViewManager::ref(), SLOT(slotTabContextMenu( QWidget *, const QPoint & ) ) ); + connect(tab, SIGNAL(initiateTabMove(int, int)), this, SLOT(slotTabAboutToMove(int, int))); connect(tab, SIGNAL(movedTab(int, int)), this, SLOT(slotTabMoved(int, int))); setTabWidgetVisibility(KMdi::AlwaysShowTabs); + } + } + if (!closeButtonsOnly) + setToolviewStyle(qConfig.toolviewTabs); +} + +void QuantaApp::slotFileClosed(Document *w) +{ + if (w) + { + KURL url = w->url(); + if (Project::ref()->hasProject() && Project::ref()->contains(url)) + { + KURL u = QExtFileInfo::toRelative(url, Project::ref()->projectBaseURL()); + m_annotationOutput->writeAnnotations(QuantaCommon::qUrl(u), w->annotations()); + } + } +} + +void QuantaApp::slotCVSCommandExecuted(const QString& command, const QStringList& files) +{ + QString file; + for (uint i = 0; i < files.count(); i++) + { + file = files[i]; + if (Project::ref()->contains(KURL::fromPathOrURL(file))) + { + emit eventHappened("after_" + command, file, QString::null); + } + } +} + +//overridden KMdiMainFrm slots +void QuantaApp::closeActiveView() +{ + ViewManager::ref()->removeActiveView(); +} + +void QuantaApp::closeAllViews() +{ + ViewManager::ref()->closeAll(); +} + +void QuantaApp::resetDockLayout() +{ + QStringList groupList = m_config->groupList(); + for (QStringList::Iterator it = groupList.begin(); it != groupList.end(); ++it) + { + if ((*it).startsWith("dock_setting_default")) + { + m_config->deleteGroup(*it); + } + } + m_config->sync(); + QWidget *mainDockWidget = getMainDockWidget(); + addToolWindow(fTab, KDockWidget::DockLeft, mainDockWidget); + addToolWindow(ProjectTreeView::ref(), KDockWidget::DockLeft, mainDockWidget); + addToolWindow(TemplatesTreeView::ref(), KDockWidget::DockLeft, mainDockWidget); + addToolWindow(StructTreeView::ref(), KDockWidget::DockLeft, mainDockWidget); + addToolWindow(scriptTab, KDockWidget::DockLeft, mainDockWidget); + addToolWindow(dTab, KDockWidget::DockRight, mainDockWidget); + addToolWindow(aTab, KDockWidget::DockRight, mainDockWidget); + addToolWindow(m_messageOutput, KDockWidget::DockBottom, mainDockWidget); + addToolWindow(m_problemOutput, KDockWidget::DockBottom, mainDockWidget); + addToolWindow(m_annotationOutput, KDockWidget::DockBottom, mainDockWidget); + if (m_previewToolView) + m_previewToolView = addToolWindow(m_htmlPart->view(), KDockWidget::DockBottom, mainDockWidget); + if (m_documentationToolView) + m_documentationToolView= addToolWindow(m_htmlPartDoc->view(), KDockWidget::DockBottom, mainDockWidget); + for (QMap<QWidget*,KMdiToolViewAccessor*>::Iterator it = m_pToolViews->begin(); it != m_pToolViews->end(); ++it) + { + QWidget *widget = it.key(); + if (dynamic_cast<ServerTreeView*>(widget)) + addToolWindow(widget, KDockWidget::DockRight, mainDockWidget); + if (dynamic_cast<VariablesListView*>(widget)) + addToolWindow(widget, KDockWidget::DockLeft, mainDockWidget); + if (dynamic_cast<DebuggerBreakpointView*>(widget)) + addToolWindow(widget, KDockWidget::DockBottom, mainDockWidget); + } +} + +KDockWidget::DockPosition QuantaApp::prevDockPosition(QWidget* widget, KDockWidget::DockPosition def) +{ + QMap<KDockWidget::DockPosition,QString> maps; + QMap<QString,QString> map; + QString dock = widget->name(); + + // Which groups to search through + maps[KDockWidget::DockTop] = "dock_setting_default::KMdiDock::topDock"; + maps[KDockWidget::DockLeft] = "dock_setting_default::KMdiDock::leftDock"; + maps[KDockWidget::DockBottom] = "dock_setting_default::KMdiDock::bottomDock"; + maps[KDockWidget::DockRight] = "dock_setting_default::KMdiDock::rightDock"; + + // Loop the groups + for(QMap<KDockWidget::DockPosition,QString>::Iterator itmaps = maps.begin(); itmaps != maps.end(); ++itmaps ) + { + // Loop the items in the group + map = quantaApp->config()->entryMap(itmaps.data()); + for(QMap<QString,QString>::Iterator it = map.begin(); it != map.end(); ++it ) + { + // If we found it, return the key of the group + if(it.data() == dock) + return itmaps.key(); + } + } + return def; +} + +void QuantaApp::switchToToplevelMode() +{ + KMdiMainFrm::switchToToplevelMode(); + resetDockLayout(); + initTabWidget(); +} + +void QuantaApp::switchToChildframeMode() +{ + KMdiMainFrm::switchToChildframeMode(); + resetDockLayout(); + initTabWidget(); +} + +void QuantaApp::switchToIDEAlMode() +{ + KMdiMainFrm::switchToIDEAlMode(); + resetDockLayout(); + initTabWidget(); +} + +void QuantaApp::switchToTabPageMode() +{ + KMdiMainFrm::switchToTabPageMode(); + resetDockLayout(); + initTabWidget(); +} + +void QuantaApp::slotPreviewBeingClosed() +{ + m_previewVisible = false; + m_noFramesPreview = false; + m_previewToolView = 0L; //this automatically deleted, so set to 0L + } + +void QuantaApp::slotDockWidgetHasUndocked(KDockWidget *widget) +{ + if (m_previewToolView && m_previewToolView->wrapperWidget() == widget) + slotPreviewBeingClosed(); +} + +void QuantaApp::slotTabDragged(QWidget *widget) +{ + QuantaView *view = dynamic_cast<QuantaView*>(widget); + if (view && view->document()) + { + QString url = view->document()->url().url(); + QDragObject *d = new QTextDrag( url, this ); + d->dragCopy(); + } +} + +void QuantaApp::setTabToolTip(QWidget *w, const QString &toolTipStr) +{ + if (tabWidget()) + tabWidget()->setTabToolTip(w, toolTipStr); +} + +void QuantaApp::createPreviewPart() +{ + m_htmlPart = new WHTMLPart(this, "rightHTML", true); + m_htmlPart->view()->resize(0, 0); + m_htmlPart->view()->setIcon(UserIcon("preview")); + m_htmlPart->view()->setCaption(i18n("Preview")); + slotNewPart(m_htmlPart, false); + connect(m_htmlPart, SIGNAL(previewHasFocus(bool)), this, SLOT(slotPreviewHasFocus(bool))); + connect(m_htmlPart, SIGNAL(destroyed(QObject *)), this, SLOT(slotHTMLPartDeleted(QObject *))); + connect(m_htmlPart, SIGNAL(openFile(const KURL&, const QString&, bool)), this, SLOT(slotFileOpen(const KURL&, const QString&, bool))); + connect(m_htmlPart, SIGNAL(showPreview(bool)), this, SLOT(slotShowPreviewWidget(bool))); + +} + +void QuantaApp::createDocPart() +{ + m_htmlPartDoc = new WHTMLPart(this, "docHTML"); + m_htmlPartDoc->view()->resize(0, 0); + m_htmlPartDoc->view()->setIcon(SmallIcon("contents")); + m_htmlPartDoc->view()->setCaption(i18n("Documentation")); + slotNewPart(m_htmlPartDoc, false); + connect(m_htmlPartDoc, SIGNAL(destroyed(QObject *)), this, SLOT(slotHTMLPartDeleted(QObject *))); +} + +void QuantaApp::insertTagActionPoolItem(QString const& action_item) +{ + for(QStringList::Iterator it = m_tagActionPool.begin(); it != m_tagActionPool.end(); ++it) + if(action_item == *it) + return; + + m_tagActionPool += action_item; +} + +void QuantaApp::removeTagActionPoolItem(QString const& action_item) +{ + for(QStringList::Iterator it = m_tagActionPool.begin(); it != m_tagActionPool.end(); ++it) + { + if(action_item == *it) + { + m_tagActionPool.remove(it); + return; + } + } +} + +void QuantaApp::slotHTMLPartDeleted(QObject *object) +{ + if (object == m_htmlPart) + { + createPreviewPart(); + slotShowPreviewWidget(false); + } else + createDocPart(); +} + +void QuantaApp::slotTabMoved(int from, int to) +{ + KMdiChildView *view = m_pDocumentViews->at(from); + m_pDocumentViews->remove(from); + m_pDocumentViews->insert(to, view); + connect(this, SIGNAL(viewActivated (KMdiChildView *)), ViewManager::ref(), SLOT(slotViewActivated(KMdiChildView*))); +} + +void QuantaApp::slotTabAboutToMove(int from, int to) +{ + Q_UNUSED(from); + Q_UNUSED(to); + disconnect(this, SIGNAL(viewActivated (KMdiChildView *)), ViewManager::ref(), SLOT(slotViewActivated(KMdiChildView*))); +} + +QString QuantaApp::currentURL() const +{ + return ViewManager::ref()->currentURL(); +} + +void QuantaApp::slotAnnotate() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + uint line, column; + w->viewCursorIf->cursorPositionReal(&line, &column); + KDialogBase editDlg(this, "annotate", true, i18n("Annotate Document"), KDialogBase::Ok | KDialogBase::Cancel /*| KDialogBase::User1*/); + KTextEdit editor(&editDlg); + editor.setTextFormat(PlainText); + editor.setText(w->annotationText(line)); + editDlg.setMainWidget(&editor); + //editDlg.setButtonText(KDialogBase::User1, i18n("Clear")); + if (editDlg.exec()) + { + w->setAnnotationText(line, editor.text()); + } +} + +void QuantaApp::dropEvent(QDropEvent* event) +{ + if (KURLDrag::canDecode(event)) + { + KURL::List fileList; + KURLDrag::decode(event, fileList); + + if(fileList.empty()) + return; + + slotFileOpen(fileList, defaultEncoding()); + } +} + +void QuantaApp::dragEnterEvent( QDragEnterEvent *e) +{ + e->accept(); +} + +#include "quanta.moc" diff --git a/quanta/src/quanta.desktop b/quanta/src/quanta.desktop new file mode 100644 index 00000000..dcf8fbda --- /dev/null +++ b/quanta/src/quanta.desktop @@ -0,0 +1,95 @@ +[Desktop Entry] +Name=Quanta Plus +Name[br]=Quanta+ +Name[hi]=क्वांटा प्लस +Name[ne]=क्वान्टा प्लस +Name[sv]=Quanta plus +Name[ta]=குவாண்டா ப்ளஸ் +Exec=quanta %U +Icon=quanta +Type=Application +MimeType=text/html;application/x-webprj; +DocPath=quanta/index.html +GenericName=Web Development Environment +GenericName[bg]=Разработка на уеб приложения +GenericName[ca]=Entorn de desenvolupament web +GenericName[cs]=Vývojové prostředí pro web +GenericName[da]=Internetudviklingsomgivelser +GenericName[de]=Web-Entwicklungsumgebung +GenericName[el]=Περιβάλλον ανάπτυξης ιστοσελίδων +GenericName[es]=Entorno de desarrollo web +GenericName[et]=Veebiarenduskeskkond +GenericName[eu]=Web garapenerako ingurunea +GenericName[fa]=محیط توسعۀ وب +GenericName[fi]=WWW:n kehitysympäristö +GenericName[fr]=Environnement de développement Internet +GenericName[gl]=Entorno de desenvolvemento de páxinas web +GenericName[hi]=वेब डेवलपमेंट वातावरण +GenericName[hu]=Webfejlesztő környezet +GenericName[is]=Vefþróunarumhverfi +GenericName[it]=Ambiente per lo sviluppo Web +GenericName[ja]=ウェブ開発環境 +GenericName[ka]=ვებ დამუშავების გარსი +GenericName[lt]=Žiniatinklio kūrimo aplinka +GenericName[ms]=Persekitaran Pembangunan Web +GenericName[nds]=Ümgeven för Nett-Entwickeln +GenericName[ne]=वेब विकास परिवेश +GenericName[nl]=Omgeving voor webdevelopment +GenericName[pl]=Środowisko do tworzenia stron WWW +GenericName[pt]=Ambiente de Desenvolvimento Web +GenericName[pt_BR]=Ambiente de Desenvolvimento Web +GenericName[ru]=Среда web-разработки +GenericName[sk]=Prostredie na vývoj Web aplikácií +GenericName[sl]=Spletno razvijalno okolje +GenericName[sr]=Окружење за веб развој +GenericName[sr@Latn]=Okruženje za veb razvoj +GenericName[sv]=Webbutvecklingsmiljö +GenericName[ta]=இணைய மேம்பாட்டுச் சூழல் +GenericName[tg]=Фазои тайёр кардани-Web +GenericName[tr]=Web Geliştirme Ortamı +GenericName[uk]=Середовище веб-розробки +GenericName[zh_CN]=Web 开发环境 +GenericName[zh_HK]=網頁開發環境 +GenericName[zh_TW]=網頁開發環境 +Comment=Web Development Environment +Comment[bg]=Среда за разработка на уеб приложения +Comment[ca]=Entorn de desenvolupament web +Comment[cs]=Vývojové prostředí pro web +Comment[da]=Internetudviklingsomgivelser +Comment[de]=Web-Entwicklungsumgebung +Comment[el]=Περιβάλλον ανάπτυξης ιστοσελίδων +Comment[es]=Entorno de desarrollo web +Comment[et]=Veebiarenduskeskkond +Comment[eu]=Web garapenerako ingurunea +Comment[fa]=محیط توسعۀ وب +Comment[fi]=WWW:n kehitysympäristö +Comment[fr]=Environnement de développement Internet +Comment[gl]=Entorno de desenvolvemento de páxinas web +Comment[hi]=वेब डेवलपमेंट वातावरण +Comment[hu]=Webfejlesztő környezet +Comment[is]=Vefþróunarumhverfi +Comment[it]=Ambiente per lo sviluppo Web +Comment[ja]=ウェブ開発環境 +Comment[ka]=ვებ დამუშავების გარსი +Comment[lt]=Žiniatinklio kūrimo aplinka +Comment[ms]=Persekitaran Pembangunan Web +Comment[nds]=Ümgeven för Nett-Entwickeln +Comment[ne]=वेब विकास परिवेश +Comment[nl]=Omgeving voor webdevelopment +Comment[pl]=Środowisko do tworzenia stron WWW +Comment[pt]=Ambiente de desenvolvimento Web +Comment[pt_BR]=Ambiente de Desenvolvimento Web +Comment[ru]=Среда web-разработки +Comment[sk]=Prostredie na vývoj Web aplikácií +Comment[sl]=Spletno razvijalno okolje +Comment[sr]=Окружење за веб развој +Comment[sr@Latn]=Okruženje za veb razvoj +Comment[sv]=Webbutvecklingsmiljö +Comment[ta]=இணைய மேம்பாட்டுச் சூழல் +Comment[tg]=Фазои тайёр кардани-Web +Comment[tr]=Web Geliştirme Ortamı +Comment[uk]=Середовище веб-розробки +Comment[zh_CN]=Web 开发环境 +Comment[zh_HK]=網頁開發環境 +Comment[zh_TW]=網頁開發環境 +Categories=Qt;KDE;Development;WebDevelopment; diff --git a/quanta/src/quanta.h b/quanta/src/quanta.h new file mode 100644 index 00000000..5cee4c1f --- /dev/null +++ b/quanta/src/quanta.h @@ -0,0 +1,664 @@ +/*************************************************************************** + quanta.h - description + ------------------- + begin : ?? ??? 9 13:29:57 EEST 2000 + copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev & Eric Laffoon <[email protected],[email protected],[email protected]> + (C) 2001-2004 by Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 QUANTA_H +#define QUANTA_H + +#define QUANTA_PACKAGE "quanta" +#define QUANTA_VERSION "3.5.10" + +#define IDS_STATUS 1 +#define IDS_INS_OVR 2 +#define IDS_MODIFIED 3 +#define IDS_STATUS_CLM 4 +// Number 5 is used by the debugger, but not added until debugger is activated +#define IDS_STATUS_DEBUGGER 5 + +#define IDS_DEFAULT "Ready." + +// include files for Qt +#include <qmap.h> +#include <qdict.h> +#include <qvaluelist.h> +#include <qstrlist.h> +#include <qptrlist.h> + +// include files for KDE +#include <kdeversion.h> +#include <kdockwidget.h> +#include <kparts/browserextension.h> +#include <kparts/dockmainwindow.h> + +#include <kmdimainfrm.h> + +//app includes +#include "dcopwindowmanagerif.h" + +// forward declaration +class DCOPSettings; +class DCOPQuanta; + +class QuantaPluginInterface; + +class QuantaDoc; +class QuantaView; +class QNewDTEPStuff; +class QNewToolbarStuff; +class QNewTemplateStuff; +class QNewScriptStuff; +class QNewDocStuff; + +class QTabWidget; +class QWidgetStack; +class QListViewItem; + +class QPopupMenu; + +class WHTMLPart; +class KHTMLView; + +class KAction; +class KToggleAction; +class KProcess; +class KSelectAction; +class KRecentFilesAction; +class KToolBarPoupAction; +class KTextEdit; + +class TagAction; +class Node; +class Parser; +class DocTreeView; +class FilesTreeView; +class ScriptTreeView; +class EnhancedTagAttributeTree; +class Project; +class AnnotationOutput; +class MessageOutput; +class QDomDocument; +class Document; +class DebuggerManager; +class QuantaInit; +class KToolBarPopupAction; +class KTempFile; +class KParts::Part; +class KParts::PartManager; +namespace KTextEditor +{ + class Mark; + class View; +} + +struct DirInfo; +struct EventAction; +struct ToolbarEntry; + + +/** + * The base class for Quanta application windows. + */ +class QuantaApp : public KMdiMainFrm, public DCOPWindowManagerIf +{ + Q_OBJECT + +friend class QuantaInit; + +public: + QuantaApp(int mdiMode); + ~QuantaApp(); + + QuantaDoc *doc() const {return m_doc; } + QPopupMenu *tagsMenu() const {return m_tagsMenu;} + KConfig *config() const {return m_config;} + +//TODO: check if we really need these "get" methods (and get rid o get) + MessageOutput *messageOutput() const {return m_messageOutput;} + MessageOutput *problemOutput() const {return m_problemOutput;} + AnnotationOutput *annotationOutput() const {return m_annotationOutput;} + + DebuggerManager *debugger() const {return m_debugger;} + KParts::PartManager *partManager() {return m_partManager;} + + QWidget* createContainer(QWidget *parent, int index, const QDomElement &element, int &id ); + void removeContainer(QWidget *container, QWidget *parent, QDomElement &element, int id ); +/** Returns the project's base URL if it exists, the HOME dir if there is no project and no opened document (or the current opened document was not saved yet), and the base URL of the opened document, if it is saved somewhere. + + maps to the same function in Project*/ + KURL projectBaseURL() const; + + /** Returns the project (if there is one loaded) or global default encoding. */ + QString defaultEncoding(); + /** Returns the interface number for the currently active editor. */ + int currentEditorIfNum() const; + /** Return the URL of the currently active document */ + QString currentURL() const; + /** Return the URL of the currently project */ + QString projectURL() const; + /** Return the list of opened URLs and their editor interface numbers*/ + QStringList openedURLs() const; + QString saveCurrentFile(); + /** + * Sets the DTEP for the current document. + * @param dtepName the name (nickname or full name) of the DTEP + * @param convert if true, converts the !DOCTYPE line to the new DTEP + */ + void setDtep(const QString& dtepName, bool convert); + QStringList tagAreas(const QString& name, bool includeCoordinates, bool skipFoundContent) const; + QString documentFolderForURL(const QString &url); + QString urlWithPreviewPrefix(const QString &url); + void addFileToProject(const QString &url); + void addFolderToProject(const QString &url); + void uploadURL(const QString &url, const QString& profile, bool markOnly); /** Capture DCOP signals from KXsldbgPart or similar plugin */ + void newCursorPosition(const QString &file, int lineNumber, int columnNumber); + void newDebuggerPosition(const QString &file, int lineNumber); + void openFile(const QString &file, int lineNumber, int columnNumber); + + /** reparse current document and initialize node. */ + void reparse(bool force); + + bool structTreeVisible() const; + bool previewVisible() const {return m_previewVisible;} + + //return the old Cursor position + void oldCursorPos(uint &line, uint &col) {line = oldCursorLine; col = oldCursorCol;} + + QStringList selectors(const QString& tag); + QStringList idSelectors(); + WHTMLPart *documentationPart() {return m_htmlPartDoc;} + /** Show the toolbar which is in url. If it was not loaded yet, it loads the + toolbar from the file */ + void showToolbarFile(const KURL &url); + + void setParserEnabled(bool enabled) {m_parserEnabled = enabled;} + + /** tabs for left panel */ + DocTreeView *dTab; + EnhancedTagAttributeTree *aTab; + +/** + * Show a TagDialog of Node tag, with attrs attr. + * @param tag The name of the new Node to create. + * @param attr The string containing the attrs of the new Node to create. + * @return Returns a new Node created according to the contents of the TagDialog. + */ + Node *showTagDialogAndReturnNode(const QString &tag, const QString &attr = QString::null); + /** Returns the baseURL of the document. */ + KURL baseURL(); + + void startIdleTimer(); + + /** Called when a document was closed. Resets some variables. */ + void slotFileClosed(Document *w); + void setTabToolTip(QWidget *w, const QString& toolTipStr); + + void createPreviewPart(); + void createDocPart(); + + /** + * This is a pool for actions waiting for being applied (VPL only yet). + * For example, whee a user presses the bold action, it isn't immediately applied on the document. + * If the user presses a key right away the character is inserted inside the tags for the queued actions. + * If the user changes the place of the cursor, the actions waiting for being inserted are removed. + */ + QStringList const& tagActionPool() const {return m_tagActionPool;} + void insertTagActionPoolItem(QString const& action_item); + void removeTagActionPoolItem(QString const& action_item); + void removeAllTagActionPoolItems() {m_tagActionPool.clear();} + /** Updates the structure and attribute treeview. */ + void updateTreeViews(); + void setTitle(const QString&); + + QPtrList<TagAction> const& tagActions() const {return m_tagActions;} + + /** Clicked word or selected text for context sensitive menu in editor */ + QString popupWord; + +signals: // Signals + /** signal used to hide the splash screen */ + void showSplash(bool); + // trees need reload because hidden files option changed + void reloadAllTrees(); + + /** Emitted when some kind of event that can have associated actions has happened. */ + void eventHappened(const QString&, const QString&, const QString& ); + + void toolbarRemoved(const QString&); + + void showMessage(const QString&, bool); + void clearMessages(); + +public slots: + void slotFileNew(); + void slotFileOpen(); + void slotFileOpen(const KURL &url); + void slotFileOpen(const KURL &url, const QString &encoding); + void slotFileOpen(const KURL &url, const QString &encoding, bool readOnly); + void slotFileOpen(const KURL::List &urls, const QString& encoding); + void slotFileSave(); + bool slotFileSaveAs(QuantaView *viewToSave = 0L); + void slotFileSaveAsLocalTemplate(); + void slotFileSaveAsProjectTemplate(); + void slotFileSaveSelectionAsLocalTemplate(); + void slotFileSaveSelectionAsProjectTemplate(); + void slotFileSaveAll(); + void slotFileReload(QuantaView *view = 0L); + void slotFileReloadAll(); + /** Close the document specified in the parameter if it's opened */ + void slotFileClose(const KURL &url); + void slotFileCloseAll(); + void slotFileQuit(); + + void slotOpenFileInPreview(const KURL &url); + void slotImageOpen(const KURL &url); + void slotFileOpenRecent(const KURL &url); + void slotInsertTag(const KURL&, DirInfo); + + void slotEditFindInFiles(); + /// open url in documentation window + void openDoc(const QString& url); + + void slotContextHelp(); + + void slotBack(); + void slotForward(); + + void statusBarTimeout(); + /** Shows the message in the status bar. + WARNING: Don't use in place where nothing should happen until the function + exits (like in startup code, DTD reading, etc.) as it calls processEvents() and + unexpected things may happen. */ + void slotStatusMsg(const QString &text); + + void slotNewStatus(); + void slotNewLineColumn(); +// void slotUpdateStatus(QWidget*);FIXME: + + /** repaint preview */ + void slotRepaintPreview(); + /** toggles showing the preview */ + void slotToggleShowPreview(); + /** Shows the preview widget and repaints the preview or + hides the preview widget and restores the original document + */ + void slotShowPreviewWidget(bool show); + /** Called from the view manager when the status of the preview must change: + - hide if the preview was in the editor area + - update if the preview is in a toolview + */ + void slotChangePreviewStatus(); + /** Called when the preview widget got or lost the focus */ + void slotPreviewHasFocus(bool focus); + + void slotShowMessagesView(); + void slotShowProblemsView(); + void slotShowAnnotationView(); + + void slotContextMenuAboutToShow(); + + /** options slots */ + void slotShowMenuBar(); + void slotPreviewOptions(); + void slotOptions(); + void slotOptionsConfigureKeys(); + void slotOptionsConfigureToolbars(); + void slotNewToolbarConfig(); + /** Configure toolbars, show defaultToolbar by default */ + void slotConfigureToolbars(const QString& defaultToolbar = QString::null); + void slotOptionsConfigureActions(); + + void setCursorPosition(int row, int col ); + void gotoFileAndLine(const QString& filename, int line, int column); + + void selectArea(int line1, int col1, int line2, int col2); + + /** Insert the content of the url into the current document. */ + void slotInsertFile(const KURL& url); + /** Sends a toolbar in mail. */ + void slotSendToolbar(); + /** Removes a user toolbar from the toolbars. */ + bool slotRemoveToolbar(); + /** Adds a new, empty toolbar. */ + void slotAddToolbar(); + /** Saves a toolbar as local specific. */ + void slotSaveLocalToolbar(); + /** Saves a toolbar as project specific. */ + void slotSaveProjectToolbar(); + /** Loads the toolbars for dtd named dtdName and unload the ones belonging to oldDtdName. */ + void slotLoadToolbarForDTD(const QString& dtdName); + /** Load an user toolbar from the disk. */ + void slotLoadToolbarFile(const KURL& url); + /** Load an user toolbar from the disk. */ + void slotLoadToolbar(); + /** Load a global toolbar from the disk. */ + void slotLoadGlobalToolbar(); + /** Remove the toolbar named "name". */ + bool slotRemoveToolbar(const QString& name); + /** Rename the toolbar named "name". */ + void slotRenameToolbar(const QString& name); + /** Rename the toolbar. */ + void slotRenameToolbar(); + /** Delete an action */ + void slotDeleteAction(KAction *action); + /** Remove the action from toolbar*/ + void slotRemoveAction(const QString&, const QString& actionName); + /** Edit the action */ + void slotEditAction(const QString&); + /** Creates a new, empty action */ + void slotNewAction(); + /** Creates a script action for a_scriptURL using the a_interpreter as the script + interpreter application */ + void slotAssignActionToScript(const KURL&a_scriptURL, const QString& a_interpreter); + + /** Change the DTD/DTEP of the current document. */ + void slotChangeDTD(); + /** Edit a DTD/DTEP description. */ + void slotEditDTD(); + /** Called when the user explicetly requested the completion box.*/ + void slotShowCompletion(); + /** Called when the user explicetly requested the completion hint.*/ + void slotShowCompletionHint(); + /** Show or hide the groups for dtepName in the structure tree. + The special value of "clear" for dtepName means show groups + for all DTEPs found in the document.*/ + void slotShowGroupsForDTEP(const QString& dtepName, bool show); + /** Build the project specific toolbar menu. */ + void slotBuildPrjToolbarsMenu(); + + void slotReparse(); + void slotForceReparse(); + + void slotExpandAbbreviation(); + + /** Format the code of the document */ + void slotCodeFormatting(); + /** Show the Document Properties Dialog */ + void slotDocumentProperties(); + /** Show the Document Properties Dialog + * If forceInsertionOfMinimalTree and the user haven't modified something in the dialog, + * it will anyway insert a Minimal tree (HTML, BODY, ...) + */ + void documentProperties(bool forceInsertionOfMinimalTree = false); + /** No descriptions */ + void slotAutosaveTimer(); + + void slotHideSplash() {emit showSplash(false);} + + void slotConvertCase(); + + /** Reload the tree of the StructTreeView */ + void slotReloadStructTreeView(bool groupOnly = false); + + void slotReportBug(); + /** registers a new part in the partmanager */ + void slotNewPart(KParts::Part *newPart, bool setActiv); + + void slotUploadFile(QuantaView *view=0L); + void slotDeleteFile(QuantaView *view=0L); + + /** Called when the CVS command working on files was executed successfully. */ + void slotCVSCommandExecuted(const QString &command, const QStringList &files); + + /** Called when the preview or documentation part is deleted. */ + void slotHTMLPartDeleted(QObject *object); + + void slotRefreshActiveWindow(); + + bool slotEnableIdleTimer(bool enable); + +//Overridden KMdiMainFrm slots + virtual void closeAllViews(); + virtual void closeActiveView(); + virtual void switchToToplevelMode(); + virtual void switchToChildframeMode(); + virtual void switchToIDEAlMode(); + virtual void switchToTabPageMode(); + /** appends all visible user toolbar urls to the list */ + void slotGetUserToolbarFiles(KURL::List *list); + + // Get saved position of dock + KDockWidget::DockPosition prevDockPosition(QWidget* dock, KDockWidget::DockPosition def); + +protected slots: + void slotDockWidgetHasUndocked(KDockWidget *widget); + void slotPreviewBeingClosed(); + /** No descriptions */ + void slotMakeDonation(); + /** No descriptions */ + void slotHelpHomepage(); + /** Show or hide the DTD toolbar */ + void slotToggleDTDToolbar(bool show); + /** Loads a DTEP*/ + void slotLoadDTEP(); + /** Sends a DTEP in email */ + void slotEmailDTEP(); + /** Downloads a DTEP from the main server */ + void slotDownloadDTEP(); + /** Uploads a DTEP to the main server */ + void slotUploadDTEP(); + /** Downloads a toolbar from the main server */ + void slotDownloadToolbar(); + /** Uploads a toolbar to the main server */ + void slotUploadToolbar(); + /** Enable/Disable Smart Tag Insertion */ + void slotSmartTagInsertion(); + /** Downloads a template from the main server */ + void slotDownloadTemplate(); + /** Uploads a template to the main server */ + void slotUploadTemplate(const QString &fileName); + /** Downloads a script from the main server */ + void slotDownloadScript(); + /** Uploads a script to the main server */ + void slotUploadScript(const QString &fileName); + /** Downloads a documentation from the main server */ + void slotDownloadDoc(); + /** Shows tip of the day */ + void slotHelpTip(); + /** Show the user mailing list sign up */ + void slotHelpUserList(); + void slotOpenFileUnderCursor(); + void slotUploadOpenedFiles(); + /** Called after there was no user activity - cursor movement - for xx ms*/ + void slotIdleTimerExpired(); + void slotShowNoFramesPreview(); + /** Get script output */ + void slotGetScriptOutput(KProcess*, char*, int); + /** Get script error */ + void slotGetScriptError(KProcess*, char*, int); + /** Notify when process exits */ + void slotProcessExited(KProcess*); + /** External app execution timeout handling */ + void slotProcessTimeout(); + + /** connected to the part manager, activates a new part */ + void slotActivePartChanged(KParts::Part * ); + + void slotTagMail(); + void slotTagQuickList(); + void slotTagEditTable(); + void slotTagColor(); + void slotTagDate(); + void slotTagSelect(); + /** Add the starting and closing text for a + user specified tag. */ + void slotTagMisc(); + void slotEditCurrentTag(); + void slotSelectTagArea(); + void slotSelectTagArea(Node *node); + + void slotInsertCSS(); + void slotFrameWizard(); + void slotViewInKFM(); + void slotViewInLynx(); + + void slotPasteHTMLQuoted(); + void slotPasteURLEncoded(); + void slotInsertChar(); + +//Edit + void slotUndo (); + void slotRedo (); + void slotCut(); + void slotCopy(); + void slotPaste(); + + void slotShowSourceEditor(); + void slotShowVPLAndSourceEditor(); + void slotShowVPLOnly(); + void slotTabDragged(QWidget *widget); + void slotTabMoved(int from, int to); + void slotTabAboutToMove(int from, int to); + void slotAnnotate(); + +protected: + /** Create a DTEP tarball which can be uploaded or sent in email. Returns + * the name of the created file or QString::null if creation has failed. + */ + QString createDTEPTarball(); + + /** Create a toolbar tarball which can be uploaded or sent in email. Returns + * the name of the created file or QString::null if creation has failed. + */ + QString createToolbarTarball(); + + /** Ask for save all the modified user toolbars. */ + bool removeToolbars(); + /** Returns true if all toolbars are hidden, false otherwise. */ + bool allToolbarsHidden() const; + /** No descriptions */ + virtual void focusInEvent(QFocusEvent*); + void saveOptions(); + + virtual bool queryClose(); + void saveAsTemplate (bool projectTemplate, bool selectionOnly = false); + /** Saves a toolbar as local or project specific. */ + bool saveToolbar(bool localToolbar = true, const QString& toolbarToSave = QString::null, const KURL& destURL = KURL()); + /** Saves the toolbar and the actions. Returns the name of the actions file*/ + KURL saveToolbarToFile(const QString& toolbarName,const KURL& destFile); + /** Makes the tabwidget look and behave like we want. If closeButtonsOnly is true, + only the close button behavior is changed. */ + void initTabWidget(bool closeButtonsOnly = false); + + void dropEvent(QDropEvent *ev); + void dragEnterEvent ( QDragEnterEvent * ); + + void resetDockLayout(); + + ScriptTreeView *scriptTab; + +private: + FilesTreeView *fTab; + ToolbarEntry *toolbarByURL(const KURL& url); + /** Message output window */ + MessageOutput *m_messageOutput; + MessageOutput *m_problemOutput; + AnnotationOutput *m_annotationOutput; + KMdiToolViewAccessor* m_messageOutputView; + KMdiToolViewAccessor* m_problemsOutputView; + KMdiToolViewAccessor* m_annotationOutputView; + KMdiToolViewAccessor* m_previewToolView; + KMdiToolViewAccessor* m_documentationToolView; + Document *m_previewedDocument; + + // Debugger + DebuggerManager *m_debugger; + + QuantaPluginInterface *m_pluginInterface; + + QPopupMenu *m_tagsMenu; + + // config + KConfig *m_config; + + /** HTML class for preview */ + WHTMLPart *m_htmlPart; + WHTMLPart *m_htmlPartDoc; + + // DOC & VIEW + QuantaDoc *m_doc; + + /** parsered tree of document */ + QTimer *statusbarTimer; + + // ACTIONS + KRecentFilesAction *projectToolbarFiles; + + KToggleAction *showVPLAction, *showSourceAction, *showVPLSourceAction, *showDTDToolbar; + KToolBarPopupAction *showPreviewAction; + + KAction *saveAction, *saveAllAction; + + KAction *editTagAction, *selectTagAreaAction; + + QDomDocument* m_actions; + + QPtrList<KTextEditor::Mark> markList; + + int currentPageIndex; + uint userToolbarsCount; + bool previewCopyMade; + KTempFile *previewTmpFile; + + uint cursorLine; + uint cursorCol; + uint oldCursorLine; + uint oldCursorCol; + bool m_previewVisible; + bool m_noFramesPreview; + bool m_parserEnabled; ///< enables/disables reparsing. If false, even a forced reparse is ignored (used when opening multiple files) + + QString m_scriptOutput; + + QStringList m_tagActionPool; + QPtrList<TagAction> m_tagActions; + +protected: // Protected attributes + /** Timer to refresh the structure tree. */ + QTimer *refreshTimer; + /** Timer to detect idle periods. Every time the cursor moves the timer is + restarted.*/ + QTimer *idleTimer; + /** The toolbars for this DTD are currently shown to the user. */ + QString currentToolbarDTD; + KDockWidget *m_oldTreeViewWidget; + /** The ids of the widgets visible before doing the preview/documentation browsing */ + QValueList<int> previousWidgetList; + + /* Store the old shortcuts from the local quantaui.rc */ + QMap<QString, QString> oldShortcuts; + KURL urlUnderCursor; + QTimer *autosaveTimer; + DCOPSettings *dcopSettings; + DCOPQuanta *dcopQuanta; + KParts::PartManager *m_partManager; ///< the pointer to the part manager + QGuardedPtr<KTextEditor::View> m_oldKTextEditor; ///< remembers the last activated GUI + QNewDTEPStuff *m_newDTEPStuff; + QNewToolbarStuff *m_newToolbarStuff; + QNewTemplateStuff *m_newTemplateStuff; + QNewScriptStuff *m_newScriptStuff; + QNewDocStuff *m_newDocStuff; + QDict<ToolbarEntry> m_toolbarList; + +public: //TODO: check if it's worth to make a read method for them + KRecentFilesAction *fileRecent; + /** True when the whole quanta is initialized. */ + bool quantaStarted; + bool m_loopStarted; //true if an internal event loop has been started + bool m_idleTimerEnabled; + QuantaInit *m_quantaInit; ///< the pointer to all the init stuff +}; + +#endif // QUANTA_H diff --git a/quanta/src/quanta_be.desktop b/quanta/src/quanta_be.desktop new file mode 100644 index 00000000..46578125 --- /dev/null +++ b/quanta/src/quanta_be.desktop @@ -0,0 +1,94 @@ +[Desktop Entry] +Name=Quanta Plus BE +Name[br]=Quanta+ BE +Name[ne]=क्वान्टा प्लस बीई +Name[sv]=Quanta plus BE +Name[ta]=குவாண்டா ப்ளஸ் BE +GenericName=Web Development Environment +GenericName[bg]=Разработка на уеб приложения +GenericName[ca]=Entorn de desenvolupament web +GenericName[cs]=Vývojové prostředí pro web +GenericName[da]=Internetudviklingsomgivelser +GenericName[de]=Web-Entwicklungsumgebung +GenericName[el]=Περιβάλλον ανάπτυξης ιστοσελίδων +GenericName[es]=Entorno de desarrollo web +GenericName[et]=Veebiarenduskeskkond +GenericName[eu]=Web garapenerako ingurunea +GenericName[fa]=محیط توسعۀ وب +GenericName[fi]=WWW:n kehitysympäristö +GenericName[fr]=Environnement de développement Internet +GenericName[gl]=Entorno de desenvolvemento de páxinas web +GenericName[hi]=वेब डेवलपमेंट वातावरण +GenericName[hu]=Webfejlesztő környezet +GenericName[is]=Vefþróunarumhverfi +GenericName[it]=Ambiente per lo sviluppo Web +GenericName[ja]=ウェブ開発環境 +GenericName[ka]=ვებ დამუშავების გარსი +GenericName[lt]=Žiniatinklio kūrimo aplinka +GenericName[ms]=Persekitaran Pembangunan Web +GenericName[nds]=Ümgeven för Nett-Entwickeln +GenericName[ne]=वेब विकास परिवेश +GenericName[nl]=Omgeving voor webdevelopment +GenericName[pl]=Środowisko do tworzenia stron WWW +GenericName[pt]=Ambiente de Desenvolvimento Web +GenericName[pt_BR]=Ambiente de Desenvolvimento Web +GenericName[ru]=Среда web-разработки +GenericName[sk]=Prostredie na vývoj Web aplikácií +GenericName[sl]=Spletno razvijalno okolje +GenericName[sr]=Окружење за веб развој +GenericName[sr@Latn]=Okruženje za veb razvoj +GenericName[sv]=Webbutvecklingsmiljö +GenericName[ta]=இணைய மேம்பாட்டுச் சூழல் +GenericName[tg]=Фазои тайёр кардани-Web +GenericName[tr]=Web Geliştirme Ortamı +GenericName[uk]=Середовище веб-розробки +GenericName[zh_CN]=Web 开发环境 +GenericName[zh_HK]=網頁開發環境 +GenericName[zh_TW]=網頁開發環境 +Exec=quanta_be +Icon=quanta_be +Type=Application +MimeType=text/html;application/x-webprj +DocPath=quanta/index.html +Comment=Web Development Environment +Comment[bg]=Среда за разработка на уеб приложения +Comment[ca]=Entorn de desenvolupament web +Comment[cs]=Vývojové prostředí pro web +Comment[da]=Internetudviklingsomgivelser +Comment[de]=Web-Entwicklungsumgebung +Comment[el]=Περιβάλλον ανάπτυξης ιστοσελίδων +Comment[es]=Entorno de desarrollo web +Comment[et]=Veebiarenduskeskkond +Comment[eu]=Web garapenerako ingurunea +Comment[fa]=محیط توسعۀ وب +Comment[fi]=WWW:n kehitysympäristö +Comment[fr]=Environnement de développement Internet +Comment[gl]=Entorno de desenvolvemento de páxinas web +Comment[hi]=वेब डेवलपमेंट वातावरण +Comment[hu]=Webfejlesztő környezet +Comment[is]=Vefþróunarumhverfi +Comment[it]=Ambiente per lo sviluppo Web +Comment[ja]=ウェブ開発環境 +Comment[ka]=ვებ დამუშავების გარსი +Comment[lt]=Žiniatinklio kūrimo aplinka +Comment[ms]=Persekitaran Pembangunan Web +Comment[nds]=Ümgeven för Nett-Entwickeln +Comment[ne]=वेब विकास परिवेश +Comment[nl]=Omgeving voor webdevelopment +Comment[pl]=Środowisko do tworzenia stron WWW +Comment[pt]=Ambiente de desenvolvimento Web +Comment[pt_BR]=Ambiente de Desenvolvimento Web +Comment[ru]=Среда web-разработки +Comment[sk]=Prostredie na vývoj Web aplikácií +Comment[sl]=Spletno razvijalno okolje +Comment[sr]=Окружење за веб развој +Comment[sr@Latn]=Okruženje za veb razvoj +Comment[sv]=Webbutvecklingsmiljö +Comment[ta]=இணைய மேம்பாட்டுச் சூழல் +Comment[tg]=Фазои тайёр кардани-Web +Comment[tr]=Web Geliştirme Ortamı +Comment[uk]=Середовище веб-розробки +Comment[zh_CN]=Web 开发环境 +Comment[zh_HK]=網頁開發環境 +Comment[zh_TW]=網頁開發環境 +Categories=Qt;KDE;Development; diff --git a/quanta/src/quanta_init.cpp b/quanta/src/quanta_init.cpp new file mode 100644 index 00000000..91f9f043 --- /dev/null +++ b/quanta/src/quanta_init.cpp @@ -0,0 +1,1631 @@ +/*************************************************************************** + quanta.cpp - description + ------------------- + begin : ?? ??? 9 13:29:57 EEST 2000 + copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev <[email protected],[email protected]> + (C) 2001-2004 by Andras Mantia <[email protected]> + (C) 2000, 2003 by Eric Laffoon <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 files for QT +#include <qdir.h> +#include <qprinter.h> +#include <qpainter.h> +#include <qtabwidget.h> +#include <qwidgetstack.h> +#include <qlayout.h> +#include <qeventloop.h> +#include <qtimer.h> +#include <qdom.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qtextcodec.h> +#include <qpopupmenu.h> +#include <qdatetime.h> +#include <qradiobutton.h> + +// include files for KDE +#include <dcopclient.h> +#include <kaccel.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <kmessagebox.h> +#include <kfiledialog.h> +#include <kglobal.h> +#include <kmenubar.h> +#include <klocale.h> +#include <kconfig.h> +#include <khtmlview.h> +#include <kstandarddirs.h> +#include <kstatusbar.h> +#include <kaction.h> +#include <kstdaction.h> +#include <kpopupmenu.h> +#include <kprocess.h> +#include <kprogress.h> +#include <ktip.h> +#include <kio/netaccess.h> +#include <ktabwidget.h> +#include <kmultitabbar.h> +#include <ktexteditor/view.h> + +#include "wkafkapart.h" +#include "kafkacommon.h" +#include "undoredo.h" +#include <dom/dom_string.h> + +// application specific includes + +#include "quanta_init.h" +#include "quanta.h" +#include "viewmanager.h" +#include "quantadoc.h" +#include "resource.h" +#include "document.h" +#include "qextfileinfo.h" + +#include "project.h" +#include "debuggermanager.h" + +#include "wkafkapart.h" +#include "whtmlpart.h" +#include "annotationoutput.h" +#include "messageoutput.h" + +#include "tagaction.h" + +#include "filestreeview.h" +#include "projecttreeview.h" +#include "doctreeview.h" +#include "structtreeview.h" +#include "templatestreeview.h" +#include "tagattributetree.h" +#include "scripttreeview.h" +#include "toolbartabwidget.h" + +#ifdef ENABLE_CVSSERVICE +#include "cvsservice.h" +#endif + +#include "quantaplugin.h" +#include "quantaplugininterface.h" + + +#include "parser.h" +#include "filemasks.h" +#include "dirtydlg.h" +#include "dirtydialog.h" + +#include "dtds.h" +#include "qpevents.h" +#include "quantabookmarks.h" + +#include "tagactionmanager.h" +#include "tagactionset.h" + +extern QMap<int, QString> replacementMap; + +QuantaInit::QuantaInit(QuantaApp * quantaApp) + : QObject() +{ + m_quanta = quantaApp; + connect(this, SIGNAL(hideSplash()), m_quanta, SLOT(slotHideSplash())); +} + +QuantaInit::~QuantaInit() +{ +} + + +/** Delayed initialization. */ +void QuantaInit::initQuanta() +{ + m_config = quantaApp->m_config; + parser = new Parser(); + + QStringList tmpDirs = KGlobal::dirs()->resourceDirs("tmp"); + QDir dir; + tmpDir = tmpDirs[0]; + for (uint i = 0; i < tmpDirs.count(); i++) + { + if (tmpDirs[i].contains("kde-")) + tmpDir = tmpDirs[i]; + } + dir.mkdir(tmpDir + "quanta"); + tmpDir += "quanta/quanta"; + + scriptBeginRx.setCaseSensitive(false); + scriptBeginRx.setPattern("(<script)"); + scriptEndRx.setCaseSensitive(false); + scriptEndRx.setPattern("(/script>)"); + + initStatusBar(); + + //defaultDocType must be read before the Project object is created!! + m_config->setGroup("General Options"); + qConfig.defaultDocType = m_config->readEntry("Default DTD",DEFAULT_DTD); + if (! DTDs::ref()->find(qConfig.defaultDocType)) + qConfig.defaultDocType = DEFAULT_DTD; + DTDs::ref()->find("dtd"); //load on startup + + initView(); + initDocument(); // after initView because of init of treeViews + initProject(); + initActions(); + + + DTDs::ref(); // create the class, must be before readOptions() ! + readOptions(); + initPlugins(); // needs to be before createGUI because some actions are created inside + readAbbreviations(); + + // Initialize debugger + m_quanta->m_debugger = new DebuggerManager(m_quanta); + connect(Project::ref(), SIGNAL(newProjectLoaded(const QString &, const KURL &, const KURL &)), + m_quanta->m_debugger, SLOT(slotNewProjectLoaded(const QString &, const KURL &, const KURL &))); + connect(Project::ref(), SIGNAL(eventHappened(const QString &, const QString &, const QString &)), + m_quanta->m_debugger, SLOT(slotHandleEvent(const QString &, const QString &, const QString &))); + connect(m_quanta->m_debugger, SIGNAL(hideSplash()), m_quanta, SLOT(slotHideSplash())); + + //m_quanta->KDockMainWindow::createGUI( QString::null, false /* conserveMemory */ ); + m_quanta->createShellGUI(true); + + addToolTreeView(m_quanta->fTab, i18n("Files"), UserIcon("ftab"), KDockWidget::DockLeft); + addToolTreeView(m_quanta->dTab, i18n("Documentation"), BarIcon("contents"), KDockWidget::DockRight); + addToolTreeView(m_quanta->aTab, i18n("Attribute Editor"), UserIcon("tag_misc"), KDockWidget::DockRight); + addToolTreeView(ProjectTreeView::ref(), i18n("Project"), UserIcon("ptab"), KDockWidget::DockLeft); + addToolTreeView(TemplatesTreeView::ref(), i18n("Templates"), UserIcon("ttab"), KDockWidget::DockLeft); + addToolTreeView(StructTreeView::ref(), i18n("Document Structure"), BarIcon("view_sidetree"), KDockWidget::DockLeft); + addToolTreeView(m_quanta->scriptTab, i18n("Scripts"), BarIcon("run"), KDockWidget::DockLeft); + m_quanta->m_messageOutputView = addToolTreeView(m_quanta->m_messageOutput, i18n("Messages"), SmallIcon("openterm"), KDockWidget::DockBottom); + m_quanta->m_problemsOutputView = addToolTreeView(m_quanta->m_problemOutput, i18n("Problems"), SmallIcon("info"), KDockWidget::DockBottom); + m_quanta->m_annotationOutputView = addToolTreeView(m_quanta->m_annotationOutput, i18n("Annotations"), SmallIcon("stamp"), KDockWidget::DockBottom); + + // Restore the dock layout + m_config->setGroup ("General Options"); + QString layout = m_config->readEntry("Window layout", "Default"); + int mdiMode = m_config->readNumEntry("MDI mode", -1); + if (mdiMode != -1 && layout != "Default") + { + m_quanta->readDockConfig(m_config); //FIXME: This causes the visible widget construction on startup, but is needed to restore the window layout... + if (mdiMode != KMdi::IDEAlMode) + m_quanta->setToolviewStyle(qConfig.toolviewTabs); + } + + m_quanta->initTabWidget(true); + qConfig.windowLayout = "Custom"; + //FIXME: This is a hack to workaround the starting problem when we are in Toplevel mode. + //Without this, the editor becomes the child of the widget holding the menus and toolbars... + if (mdiMode == KMdi::ToplevelMode) + { + m_quanta->switchToChildframeMode(); + QTimer::singleShot(0, m_quanta, SLOT(switchToToplevelMode())); + } + + // Always hide debugger toolbar at this point + m_quanta->toolBar("debugger_toolbar")->hide(); + + m_quanta->m_pluginInterface->setPluginMenu(static_cast<QPopupMenu*>(m_quanta->factory()->container("plugins", m_quanta))); + m_quanta->m_pluginInterface->buildPluginMenu(); +//TODO: Remove after upgrade from 3.1 is not supported + QDomDocument doc; + doc.setContent(KXMLGUIFactory::readConfigFile(m_quanta->xmlFile(), m_quanta->instance())); + QDomNodeList nodeList = doc.elementsByTagName("ActionProperties"); + QDomNode node = nodeList.item(0).firstChild(); + while (!node.isNull()) + { + if (node.nodeName() == "Action") + { + QDomElement el = node.toElement(); + m_quanta->oldShortcuts.insert(el.attribute("name"), el.attribute("shortcut")); + node = node.nextSibling(); + el.parentNode().removeChild(el); + } else + { + node = node.nextSibling(); + } + } + + m_quanta->applyMainWindowSettings(m_config); + + m_quanta->m_tagsMenu = static_cast<QPopupMenu*>(m_quanta->factory()->container("tags", m_quanta)); + KMenuBar *mb = m_quanta->menuBar(); + for (uint i = 0 ; i < mb->count(); i++) + { + if (mb->text(mb->idAt(i)) == i18n("&Settings")) + { + mb->insertItem(i18n("&Window"), m_quanta->windowMenu(), -1, i); + break; + } + } + KActionMenu *toolviewMenu = (KActionMenu*)(m_quanta->actionCollection()->action("kmdi_toolview_menu")); + if (toolviewMenu) + toolviewMenu->plug(m_quanta->windowMenu()); + + QPopupMenu *toolbarsMenu = (QPopupMenu*)(m_quanta->guiFactory())->container("toolbars_load", m_quanta); + connect(toolbarsMenu, SIGNAL(aboutToShow()), m_quanta, SLOT(slotBuildPrjToolbarsMenu())); + + QPopupMenu *contextMenu = (QPopupMenu*)(m_quanta->guiFactory())->container("popup_editor", m_quanta); + connect(contextMenu, SIGNAL(aboutToShow()), m_quanta, SLOT(slotContextMenuAboutToShow())); + + connect(m_quanta->m_messageOutput, SIGNAL(clicked(const QString&, int, int)), + m_quanta, SLOT(gotoFileAndLine(const QString&, int, int))); + connect(m_quanta->m_problemOutput, SIGNAL(clicked(const QString&, int, int)), + m_quanta, SLOT(gotoFileAndLine(const QString&, int, int))); + connect(m_quanta->m_annotationOutput->currentFileAnnotations(), SIGNAL(clicked(const QString&, int, int)), + m_quanta, SLOT(gotoFileAndLine(const QString&, int, int))); + connect(m_quanta->m_annotationOutput, SIGNAL(clicked(const QString&, int, int)), + m_quanta, SLOT(gotoFileAndLine(const QString&, int, int))); + + m_quanta->slotFileNew(); + m_quanta->slotNewStatus(); + initToolBars(); + Project::ref()->setProjectToolbarVisible(m_quanta->factory()->container("project_toolbar", m_quanta)->isShown()); + Project::ref()->slotShowProjectToolbar(Project::ref()->hasProject()); + KTipDialog::showTip(m_quanta); + + //get the PID of this running instance + qConfig.quantaPID = QString::number(int(getpid()), 10); + qConfig.backupDirPath = KGlobal::instance()->dirs()->saveLocation("data", resourceDir + "backups/"); + + m_quanta->autosaveTimer = new QTimer(m_quanta); + connect(m_quanta->autosaveTimer, SIGNAL(timeout()), m_quanta, SLOT(slotAutosaveTimer())); + m_quanta->autosaveTimer->start(qConfig.autosaveInterval * 60000, false); + + connect(m_quanta->m_doc, SIGNAL(hideSplash()), m_quanta, SLOT(slotHideSplash())); + connect(parser, SIGNAL(rebuildStructureTree(bool)), + m_quanta, SLOT(slotReloadStructTreeView(bool))); + + // Read list of characters + QFile file(locate("appdata","chars")); + if ( file.open(IO_ReadOnly) ) { // file opened successfully + QTextStream t( &file ); // use a text stream + t.setEncoding(QTextStream::UnicodeUTF8); + QString s; + while (!t.eof()) + { + s = t.readLine(); + charList << i18n(s.utf8()); // line excluding '\n' + int begin = s.find("(&") + 1; + if (begin == 1) + continue; + int length = s.find(";)") - begin + 1; + QString s2 = s.mid(begin, length - 1); + replacementMap[s[0].unicode()] = s2; + } + file.close(); + } + + QString infoCss = tmpDir; + infoCss.replace(QRegExp("/quanta$"),""); + infoCss += "/info.css"; + QExtFileInfo::copy(KURL().fromPathOrURL(qConfig.globalDataDir + resourceDir + "scripts/info.css"), KURL().fromPathOrURL(infoCss)); + + checkRuntimeDependencies(); + ViewManager::ref()->activeDocument()->view()->setFocus(); + + m_quanta->refreshTimer = new QTimer(m_quanta); + connect(m_quanta->refreshTimer, SIGNAL(timeout()), m_quanta, SLOT(slotReparse())); + m_quanta->refreshTimer->start( qConfig.refreshFrequency*1000, false ); //update the structure tree every 5 seconds + if (qConfig.instantUpdate || qConfig.refreshFrequency == 0) + { + m_quanta->refreshTimer->stop(); + } + +} + +void QuantaInit::initToolBars() +{ + if (m_quanta->m_toolbarList.count() == 0) + m_quanta->slotLoadToolbarForDTD(Project::ref()->defaultDTD()); +} + +void QuantaInit::initStatusBar() +{ + m_quanta->statusbarTimer = new QTimer(m_quanta); + connect(m_quanta->statusbarTimer,SIGNAL(timeout()), + m_quanta, SLOT(statusBarTimeout())); + + progressBar = new KProgress(m_quanta->statusBar()); + progressBar->setTextEnabled(false); + progressBar->setMaximumHeight(progressBar->fontMetrics().height()); + progressBar->show(); + m_quanta->statusBar()->insertItem(i18n(IDS_DEFAULT),IDS_STATUS, 1); + m_quanta->statusBar()->addWidget(progressBar); + m_quanta->statusBar()->insertItem("", IDS_INS_OVR ); + m_quanta->statusBar()->insertFixedItem(" * ", IDS_MODIFIED ); + m_quanta->statusBar()->insertFixedItem(i18n("Line: 00000 Col: 000"), IDS_STATUS_CLM, true); + + m_quanta->statusBar()->changeItem("", IDS_INS_OVR); + m_quanta->statusBar()->changeItem("", IDS_MODIFIED); + m_quanta->statusBar()->changeItem("", IDS_STATUS_CLM); + m_quanta->statusBar()->setItemAlignment(IDS_STATUS, AlignLeft); +} + + +void QuantaInit::initDocument() +{ + m_quanta->m_doc = new QuantaDoc(0L); + connect(m_quanta->m_doc, SIGNAL(newStatus()), + m_quanta, SLOT(slotNewStatus())); +} + +void QuantaInit::initProject() +{ + Project *m_project = Project::ref(m_quanta); + + connect(m_project, SIGNAL(getTreeStatus(QStringList *)), + pTab, SLOT(slotGetTreeStatus(QStringList *))); + connect(m_project, SIGNAL(loadToolbarFile(const KURL &)), + m_quanta, SLOT(slotLoadToolbarFile(const KURL &))); + connect(m_project, SIGNAL(getUserToolbarFiles(KURL::List *)), + m_quanta, SLOT(slotGetUserToolbarFiles(KURL::List *))); + connect(m_project, SIGNAL(openFiles(const KURL::List &, const QString&)), + m_quanta, SLOT(slotFileOpen(const KURL::List &, const QString&))); + connect(m_project, SIGNAL(openFile(const KURL &, const QString&)), + m_quanta, SLOT(slotFileOpen(const KURL &, const QString&))); + connect(m_project, SIGNAL(closeFile(const KURL &)), + m_quanta, SLOT(slotFileClose(const KURL &))); + connect(m_project, SIGNAL(reloadTree(ProjectList *, bool, const QStringList &)), + pTab, SLOT(slotReloadTree(ProjectList *, bool, const QStringList &))); + connect(m_project, SIGNAL(closeFiles()), ViewManager::ref(), SLOT(closeAll())); + connect(m_project, SIGNAL(eventHappened(const QString&, const QString&, const QString& )), QPEvents::ref(m_quanta), SLOT(slotEventHappened(const QString&, const QString&, const QString& ))); + + connect(m_quanta->fTab, SIGNAL(insertDirInProject(const KURL&)), + m_project, SLOT(slotAddDirectory(const KURL&))); + + connect(m_quanta->fTab, SIGNAL(insertFileInProject(const KURL&)), + m_project, SLOT(slotInsertFile(const KURL&))); + + connect(TemplatesTreeView::ref(), SIGNAL(insertDirInProject(const KURL&)), + m_project, SLOT(slotAddDirectory(const KURL&))); + + connect(TemplatesTreeView::ref(), SIGNAL(insertFileInProject(const KURL&)), + m_project, SLOT(slotInsertFile(const KURL&))); + connect(TemplatesTreeView::ref(), SIGNAL(downloadTemplate()), + m_quanta, SLOT(slotDownloadTemplate())); + connect(TemplatesTreeView::ref(), SIGNAL(uploadTemplate(const QString&)), m_quanta, SLOT(slotUploadTemplate(const QString&))); + + // inform project if something was renamed + connect(pTab, SIGNAL(renamed(const KURL&, const KURL&)), + m_project, SLOT(slotRenamed(const KURL&, const KURL&))); + connect(m_quanta->fTab, SIGNAL(renamed(const KURL&, const KURL&)), + m_project, SLOT(slotRenamed(const KURL&, const KURL&))); + connect(tTab, SIGNAL(renamed(const KURL&, const KURL&)), + m_project, SLOT(slotRenamed(const KURL&, const KURL&))); + + connect(pTab, SIGNAL(insertToProject(const KURL&)), + m_project, SLOT(slotInsertFile(const KURL&))); + connect(pTab, SIGNAL(removeFromProject(const KURL&)), + m_project, SLOT(slotRemove(const KURL&))); + connect(pTab, SIGNAL(uploadSingleURL(const KURL&, const QString&, bool, bool)), + m_project, SLOT(slotUploadURL(const KURL&, const QString&, bool, bool))); + connect(pTab, SIGNAL(rescanProjectDir()), m_project, SLOT(slotRescanPrjDir())); + connect(pTab, SIGNAL(showProjectOptions()), m_project, SLOT(slotOptions())); + connect(pTab, SIGNAL(uploadProject()), m_project, SLOT(slotUpload())); + + connect(m_quanta->dTab, SIGNAL(reloadProjectDocs()), m_project, SLOT(slotReloadProjectDocs())); + connect(m_project, SIGNAL(reloadProjectDocs()), m_quanta->dTab, SLOT(slotReloadProjectDocs())); + connect(m_project, SIGNAL(addProjectDoc(const KURL&)), m_quanta->dTab, SLOT(slotAddProjectDoc(const KURL&))); + + connect(m_project, SIGNAL(enableMessageWidget()), + m_quanta, SLOT(slotShowMessagesView())); + + connect(m_project, SIGNAL(messages(const QString&)), + m_quanta->m_messageOutput, SLOT(showMessage(const QString&))); + + connect(m_project, SIGNAL(newStatus()), + m_quanta, SLOT(slotNewStatus())); + + connect(m_project, SIGNAL(newProjectLoaded(const QString &, const KURL &, const KURL &)), + TemplatesTreeView::ref(), SLOT(slotNewProjectLoaded(const QString &, const KURL &, const KURL &))); + connect(m_project, SIGNAL(newProjectLoaded(const QString &, const KURL &, const KURL &)), + pTab, SLOT(slotNewProjectLoaded(const QString &, const KURL &, const KURL &))); + connect(m_project, SIGNAL(newProjectLoaded(const QString &, const KURL &, const KURL &)), + m_quanta->fTab, SLOT(slotNewProjectLoaded(const QString &, const KURL &, const KURL &))); + connect(m_project, SIGNAL(newProjectLoaded(const QString &, const KURL &, const KURL &)), + m_quanta->annotationOutput(), SLOT(updateAnnotations())); + + connect(pTab, SIGNAL(changeFileDescription(const KURL&, const QString&)), + m_project, SLOT(slotFileDescChanged(const KURL&, const QString&))); + connect(pTab, SIGNAL(changeUploadStatus(const KURL&, int)), + m_project, SLOT(slotUploadStatusChanged(const KURL&, int))); + connect(pTab, SIGNAL(changeDocumentFolderStatus(const KURL&, bool)), + m_project, SLOT(slotChangeDocumentFolderStatus(const KURL&, bool))); + + connect(m_project, SIGNAL(hideSplash()), m_quanta, SLOT(slotHideSplash())); + + connect(m_project, SIGNAL(statusMsg(const QString &)), + m_quanta, SLOT(slotStatusMsg(const QString & ))); +} + +void QuantaInit::initView() +{ + ViewManager *m_viewManager = ViewManager::ref(m_quanta); + connect(m_quanta, SIGNAL(viewActivated (KMdiChildView *)), m_viewManager, SLOT(slotViewActivated(KMdiChildView*))); + connect(m_quanta, SIGNAL(lastChildViewClosed()), m_viewManager, SLOT(slotLastViewClosed())); +// connect(m_quanta, SIGNAL(viewDeactivated(KMdiChildView *)), m_viewManager, SLOT(slotViewDeactivated(KMdiChildView*))); + KafkaDocument *m_kafkaDocument = KafkaDocument::ref(0, 0, "KafkaPart"); + m_kafkaDocument->getKafkaWidget()->view()->setMinimumHeight(50); + m_kafkaDocument->readConfig(quantaApp->config()); + loadVPLConfig(); + ToolbarTabWidget *toolBarTab = ToolbarTabWidget::ref(quantaApp); + connect(toolBarTab, SIGNAL(iconTextModeChanged()), quantaApp, SLOT(slotRefreshActiveWindow())); + + //set the toolview and close button style before the GUI is created + m_config->setGroup("General Options"); + int iconTextMode = m_config->readNumEntry("IconTextMode", KToolBar::IconOnly); + toolBarTab->setIconText(KToolBar::IconText(iconTextMode)); + qConfig.toolviewTabs = m_config->readNumEntry("MDI style", KMdi::IconOnly); + m_quanta->initTabWidget(); + + m_quanta->setStandardMDIMenuEnabled(false); + m_quanta->setManagedDockPositionModeEnabled(true); + + m_quanta->fTab = new FilesTreeView(m_config, m_quanta, "filestree"); + m_quanta->aTab = new EnhancedTagAttributeTree(m_quanta, "TagAttributes"); + pTab = ProjectTreeView::ref(m_quanta, "Project"); + tTab = TemplatesTreeView::ref(m_quanta, "Templates"); // creates the treeview + m_quanta->dTab = new DocTreeView(m_quanta, "Docs"); + StructTreeView *sTab = StructTreeView::ref(m_quanta ,"Struct"); + m_quanta->scriptTab = new ScriptTreeView(m_quanta, "Scripts"); + + m_quanta->m_messageOutput = new MessageOutput(m_quanta, "Messages"); + m_quanta->m_messageOutput->setFocusPolicy(QWidget::NoFocus); + m_quanta->m_messageOutput->showMessage(i18n("Message Window...")); + connect(m_quanta, SIGNAL(showMessage(const QString&, bool)), m_quanta->m_messageOutput, SLOT(showMessage(const QString&, bool))); + connect(m_quanta, SIGNAL(clearMessages()), m_quanta->m_messageOutput, SLOT(clear())); + + m_quanta->m_problemOutput = new MessageOutput(m_quanta, "Problems"); + m_quanta->m_problemOutput->setFocusPolicy(QWidget::NoFocus); + m_quanta->m_annotationOutput = new AnnotationOutput(m_quanta, "Annotations"); + m_quanta->m_annotationOutput->setFocusPolicy(QWidget::NoFocus); + + m_quanta->createPreviewPart(); + m_quanta->createDocPart(); + + connect(m_quanta, SIGNAL(reloadAllTrees()), + m_quanta->fTab, SLOT(slotReloadAllTrees())); + + connect(pTab, SIGNAL(loadToolbarFile (const KURL&)), + m_quanta, SLOT(slotLoadToolbarFile(const KURL&))); + connect(m_viewManager, SIGNAL(viewActivated(const KURL&)), + pTab, SLOT(slotViewActivated(const KURL&))); + + connect(m_viewManager, SIGNAL(documentClosed(const KURL&)), + pTab, SLOT(slotDocumentClosed(const KURL&))); + connect(m_viewManager, SIGNAL(documentClosed(const KURL&)), + tTab, SLOT(slotDocumentClosed(const KURL&))); + connect(m_viewManager, SIGNAL(documentClosed(const KURL&)), + m_quanta->scriptTab, SLOT(slotDocumentClosed(const KURL&))); + connect(m_viewManager, SIGNAL(documentClosed(const KURL&)), + m_quanta->fTab, SLOT(slotDocumentClosed(const KURL&))); + + connect(tTab, SIGNAL(insertFile (const KURL &)), + m_quanta, SLOT(slotInsertFile(const KURL &))); + + connect(m_quanta->scriptTab, SIGNAL(openFileInPreview(const KURL &)), + m_quanta, SLOT(slotOpenFileInPreview(const KURL &))); + connect(m_quanta->scriptTab, SIGNAL(showPreviewWidget(bool)), + m_quanta, SLOT(slotShowPreviewWidget(bool))); + connect(m_quanta->scriptTab, SIGNAL(assignActionToScript(const KURL &, const QString&)), + m_quanta, SLOT(slotAssignActionToScript(const KURL &, const QString&))); + connect(m_quanta->scriptTab, SIGNAL(downloadScript()), m_quanta, SLOT(slotDownloadScript())); + connect(m_quanta->scriptTab, SIGNAL(uploadScript(const QString&)), m_quanta, SLOT(slotUploadScript(const QString&))); + connect(m_quanta->dTab, SIGNAL(downloadDoc()), m_quanta, SLOT(slotDownloadDoc())); + + connect(m_quanta->m_htmlPart, SIGNAL(onURL(const QString&)), + m_quanta, SLOT(slotStatusMsg(const QString&))); + connect(m_quanta->m_htmlPartDoc, SIGNAL(onURL(const QString&)), + m_quanta, SLOT(slotStatusMsg(const QString&))); + + connect(sTab, SIGNAL(newCursorPosition(int,int)), m_quanta, SLOT(setCursorPosition(int,int))); + connect(sTab, SIGNAL(selectArea(int,int,int,int)), m_quanta, SLOT( selectArea(int,int,int,int))); + connect(sTab, SIGNAL(selectTagArea(Node*)), m_quanta, SLOT(slotSelectTagArea(Node*))); + connect(sTab, SIGNAL(needReparse()), m_quanta, SLOT(slotForceReparse())); + connect(sTab, SIGNAL(showGroupsForDTEP(const QString&, bool)), m_quanta, SLOT(slotShowGroupsForDTEP(const QString&, bool))); + connect(sTab, SIGNAL(openFile(const KURL &)), + m_quanta, SLOT (slotFileOpen(const KURL &))); + connect(sTab, SIGNAL(openImage (const KURL&)), + m_quanta, SLOT(slotImageOpen(const KURL&))); + connect(sTab, SIGNAL(showProblemMessage(const QString&)), + m_quanta->m_problemOutput, SLOT(showMessage(const QString&))); + connect(sTab, SIGNAL(clearProblemOutput()), + m_quanta->m_problemOutput, SLOT(clear())); + connect(parser, SIGNAL(nodeTreeChanged()), sTab, SLOT(slotNodeTreeChanged())); + + connect(m_quanta->dTab, SIGNAL(openURL(const QString&)), m_quanta, SLOT(openDoc(const QString&))); + + connect(m_viewManager, SIGNAL(dragInsert(QDropEvent *)), tTab, SLOT(slotDragInsert(QDropEvent *))); + + qConfig.windowLayout = "Default"; +} + +KMdiToolViewAccessor* QuantaInit::addToolTreeView(QWidget *widget, const QString &name, const QPixmap &icon, KDockWidget::DockPosition position) +{ + widget->setIcon(icon); + widget->setCaption(name); + return m_quanta->addToolWindow(widget, m_quanta->prevDockPosition(widget, position), m_quanta->getMainDockWidget()); +} + +void QuantaInit::readOptions() +{ + m_config->setGroup("General Options"); + + qConfig.markupMimeTypes = m_config->readEntry("Markup mimetypes", qConfig.markupMimeTypes); + qConfig.scriptMimeTypes = m_config->readEntry("Script mimetypes", qConfig.scriptMimeTypes); + qConfig.imageMimeTypes = m_config->readEntry("Image mimetypes", qConfig.imageMimeTypes); + qConfig.textMimeTypes = m_config->readEntry("Text mimetypes", qConfig.textMimeTypes); + + qConfig.tagCase = m_config->readNumEntry("Capitals for tags", 0); + qConfig.attrCase = m_config->readNumEntry("Capitals for attr", 0); + qConfig.attrValueQuotation = (m_config->readEntry("Attribute quotation", "double") == "double" )? '"':'\''; + qConfig.closeOptionalTags = m_config->readBoolEntry("Close tag if optional", true); + qConfig.closeTags = m_config->readBoolEntry("Close tags", true); + qConfig.useAutoCompletion = m_config->readBoolEntry("Auto completion",true); + qConfig.updateClosingTags = m_config->readBoolEntry("Update Closing Tags", true); + qConfig.replaceAccented = m_config->readBoolEntry("Replace Accented Chars", false); + qConfig.replaceNotInEncoding = m_config->readBoolEntry("Replace Chars Not In Current Encoding", true); + + + qConfig.defaultEncoding = m_config->readEntry("Default encoding", "UTF8"); + + StructTreeView::ref()->setFollowCursor( m_config->readBoolEntry("Follow Cursor", true)); + + qConfig.previewPosition = m_config->readEntry("Preview area","Editor"); + qConfig.docPosition = m_config->readEntry("Documentation area","Tab"); + + qConfig.smartTagInsertion = m_config->readBoolEntry("Smart Tag Insertion", false); + KAction *action = quantaApp->actionCollection()->action("smart_tag_insertion"); + (static_cast<KToggleAction* >(action))->setChecked(qConfig.smartTagInsertion); + + QSize s(800,580); + m_quanta->resize( m_config->readSizeEntry("Geometry", &s)); + qConfig.autosaveInterval = m_config->readNumEntry("Autosave interval", 1); + + qConfig.enableDTDToolbar = m_config->readBoolEntry("Show DTD Toolbar",true); + m_quanta->showDTDToolbar->setChecked(qConfig.enableDTDToolbar); + qConfig.showCloseButtons = m_config->readEntry("Close Buttons", "ShowDelayed"); +// m_quanta->initTabWidget(true); + m_quanta->fileRecent ->loadEntries(m_config); + qConfig.showHiddenFiles = m_config->readBoolEntry("Show Hidden Files", true); + qConfig.saveTrees = m_config->readBoolEntry("Save Local Trees", true); + int maxRecentItems = m_config->readNumEntry("Recent Files Limit", 32); + m_quanta->fileRecent->setMaxItems(maxRecentItems); + + m_config->setGroup("Parser options"); + qConfig.showEmptyNodes = m_config->readBoolEntry("Show Empty Nodes", false); + qConfig.showClosingTags = m_config->readBoolEntry("Show Closing Tags", false); + qConfig.instantUpdate = m_config->readBoolEntry("Instant Update", false); + qConfig.refreshFrequency = m_config->readNumEntry("Refresh frequency",5); + qConfig.expandLevel = m_config->readNumEntry("Expand Level", 4); + qConfig.showDTDSelectDialog = m_config->readBoolEntry("Show DTD Select Dialog", true); + + m_quanta->m_previewVisible = false; + m_quanta->m_noFramesPreview = false; + + m_quanta->showVPLAction->setChecked( false ); + + +//KNewStuff config + m_config->setGroup("KNewStuff"); + QString str = m_config->readEntry("ProvidersUrl"); + if (str.isEmpty()) + { + m_config->writeEntry( "ProvidersUrl", "http://quanta.kdewebdev.org/newstuff/providers.xml" ); + m_config->sync(); + } + + + Project::ref()->readConfig(m_config); // project +} + +void QuantaInit::openLastFiles() +{ + + // Reload previously opened files only if setting allows + m_config->setGroup("General Options"); + if (!m_config->readBoolEntry("Reload Files", true)) + { + m_quanta->setParserEnabled(true); + m_quanta->reparse(true); + return; + } + // we need to check config + // because project now can be + // in load stage ( remote prj ) + m_config->setGroup("Projects"); + QString pu = QuantaCommon::readPathEntry(m_config, "Last Project"); + + KURL u; + QuantaCommon::setUrl(u, pu); + bool isPrj = true; + if (pu.isEmpty()) + isPrj = false; + if (!u.isValid()) + isPrj = false; + + m_config->setGroup("General Options"); + + QStringList urls = QuantaCommon::readPathListEntry(m_config, "List of opened files"); + QStringList encodings = QuantaCommon::readPathListEntry(m_config, "Encoding of opened files"); + m_quanta->m_doc->blockSignals(true); + m_quanta->setParserEnabled(false); + uint i = 0; + for ( QStringList::Iterator it = urls.begin(); it != urls.end(); ++it ) + { + KURL fu; + QuantaCommon::setUrl(fu, *it); + + if (!ViewManager::ref()->isOpened(fu) && (!isPrj || fu.isLocalFile())) + m_quanta->slotFileOpen(fu, encodings[i]); + i++; + } + m_config->sync(); + m_quanta->m_doc->blockSignals(false); + m_quanta->setParserEnabled(true); + m_quanta->reparse(true); + Document *w = ViewManager::ref()->activeDocument(); + if (w) //w==0 might happen on quick close on startup + { + m_quanta->setTitle(w->url().prettyURL(0, KURL::StripFileProtocol) ); +// m_quanta->slotUpdateStatus(w);//FIXME: + } +} + +/** Loads the initial project */ +void QuantaInit::loadInitialProject(const QString& url) +{ + if(url.isNull()) + { + if(runningQuantas() == 1) + { + // Get config + KConfig *config = m_quanta->config(); + config->setGroup("General Options"); + + // Reload last project if setting is enabled + Project::ref()->loadLastProject(config->readBoolEntry("Reload Project", true)); + } + } + else + Project::ref()->slotOpenProject(KURL( url )); +} + + +void QuantaInit::initActions() +{ + KActionCollection *ac = m_quanta->actionCollection(); + new KAction(i18n("Annotate..."), 0, m_quanta, SLOT(slotAnnotate()),ac, "annotate"); + m_quanta->editTagAction = new KAction( i18n( "&Edit Current Tag..." ), CTRL+Key_E, + m_quanta, SLOT( slotEditCurrentTag() ), + ac, "edit_current_tag" ); + m_quanta->selectTagAreaAction = new KAction( i18n( "&Select Current Tag Area" ), 0, + m_quanta, SLOT( slotSelectTagArea() ), + ac, "select_tag_area" ); + new KAction( i18n( "E&xpand Abbreviation" ), CTRL+SHIFT+Key_J, + m_quanta, SLOT( slotExpandAbbreviation() ), + ac, "expand_abbreviation" ); + + new KAction(i18n("&Report Bug..."), 0, m_quanta, SLOT(slotReportBug()), ac, "help_reportbug"); //needed, because quanta_be bugs should be reported for quanta + + //Kate actions + +//Edit menu + KStdAction::undo(m_quanta, SLOT(slotUndo()), ac); + KStdAction::redo(m_quanta, SLOT(slotRedo()), ac); + KStdAction::cut(m_quanta, SLOT(slotCut()), ac); + KStdAction::copy(m_quanta, SLOT(slotCopy()), ac) ; + KStdAction::pasteText(m_quanta, SLOT(slotPaste()), ac); + +//help + (void) new KAction(i18n("Ti&p of the Day"), "idea", "", m_quanta, + SLOT(slotHelpTip()), ac, "help_tip"); + + // File actions + // + KStdAction::openNew( m_quanta, SLOT( slotFileNew() ), ac); + KStdAction::open ( m_quanta, SLOT( slotFileOpen() ), ac, "file_open"); + (void) new KAction(i18n("Close Other Tabs"), 0, ViewManager::ref(), SLOT(slotCloseOtherTabs()), ac, "close_other_tabs"); + + m_quanta->fileRecent = KStdAction::openRecent(m_quanta, SLOT(slotFileOpenRecent(const KURL&)), + ac, "file_open_recent"); + m_quanta->fileRecent->setToolTip(i18n("Open / Open Recent")); + connect(m_quanta->fileRecent, SIGNAL(activated()), m_quanta, SLOT(slotFileOpen())); + + (void) new KAction( i18n( "Close All" ), 0, m_quanta, + SLOT( slotFileCloseAll() ), + ac, "file_close_all" ); + + m_quanta->saveAction = KStdAction::save(m_quanta, SLOT( slotFileSave() ), ac); + + KStdAction::saveAs( m_quanta, SLOT( slotFileSaveAs() ), ac ); + + m_quanta->saveAllAction = new KAction( i18n( "Save All..." ), "save_all", SHIFT+KStdAccel::shortcut(KStdAccel::Save).keyCodeQt(), + m_quanta, SLOT( slotFileSaveAll() ), + ac, "file_save_all" ); + (void) new KAction(i18n("Reloa&d"), "revert", SHIFT+Key_F5, m_quanta, + SLOT(slotFileReload()), ac, "file_reload"); +// (void) new KAction(i18n("Reload All "), 0, 0, m_quanta, +// SLOT(slotFileReloadAll()), ac, "file_reload_all"); + + (void) new KAction( i18n( "Save as Local Template..." ), 0, + m_quanta, SLOT( slotFileSaveAsLocalTemplate() ), + ac, "save_local_template" ); + + (void) new KAction( i18n( "Save Selection to Local Template File..." ), 0, + m_quanta, SLOT( slotFileSaveSelectionAsLocalTemplate() ), + ac, "save_selection_local_template" ); + + KStdAction::quit( m_quanta, SLOT( slotFileQuit() ), ac ); + + // Edit actions + + (void) new KAction( i18n( "Find in Files..." ), + SmallIcon("filefind"), CTRL+ALT+Key_F, + m_quanta, SLOT( slotEditFindInFiles() ), + ac, "find_in_files" ); + + KAction* aux = TagActionManager::self()->actionCollection()->action("apply_source_indentation"); + aux->setEnabled(false); + ac->insert(aux); + + // Tool actions + + (void) new KAction( i18n( "&Context Help..." ), CTRL+Key_H, + m_quanta, SLOT( slotContextHelp() ), + ac, "context_help" ); + + (void) new KAction( i18n( "&Quanta Homepage" ), 0, + m_quanta, SLOT( slotHelpHomepage() ), + ac, "help_homepage" ); + + (void) new KAction( i18n( "&User Mailing List" ), 0, + m_quanta, SLOT( slotHelpUserList() ), + ac, "help_userlist" ); + + (void) new KAction( i18n( "Make &Donation" ), 0, + m_quanta, SLOT( slotMakeDonation() ), + ac, "help_donation" ); + + (void) new KAction( i18n( "Tag &Attributes..." ), ALT+Key_Down, + m_quanta->m_doc, SLOT( slotAttribPopup() ), + ac, "tag_attributes" ); + + (void) new KAction( i18n( "&Change the DTD..." ), 0, + m_quanta, SLOT( slotChangeDTD() ), + ac, "change_dtd" ); + + (void) new KAction( i18n( "&Edit DTD Settings..." ), 0, + m_quanta, SLOT( slotEditDTD() ), + ac, "edit_dtd" ); + + (void) new KAction( i18n( "&Load && Convert DTD..." ), 0, + DTDs::ref(), SLOT( slotLoadDTD() ), + ac, "load_dtd" ); + (void) new KAction( i18n( "Load DTD E&ntities..." ), 0, + DTDs::ref(), SLOT( slotLoadEntities() ), + ac, "load_entities" ); + + (void) new KAction( i18n( "Load DTD &Package (DTEP)..." ), 0, + m_quanta, SLOT( slotLoadDTEP() ), + ac, "load_dtep" ); + + (void) new KAction( i18n( "Send DTD Package (DTEP) in E&mail..." ), "mail_send", 0, + m_quanta, SLOT( slotEmailDTEP() ), + ac, "send_dtep" ); + + (void) new KAction( i18n( "&Download DTD Package (DTEP)..." ), "network", 0, + m_quanta, SLOT( slotDownloadDTEP() ), + ac, "download_dtep" ); + (void) new KAction( i18n( "&Upload DTD Package (DTEP)..." ), "network", 0, + m_quanta, SLOT( slotUploadDTEP() ), + ac, "upload_dtep" ); +/* + (void) new KAction( i18n( "&Upload DTD Package (DTEP)..." ), 0, + m_quanta, SLOT( slotUploadDTEP() ), + ac, "send_dtep" ); +*/ + (void) new KAction( i18n( "&Document Properties" ), 0, + m_quanta, SLOT( slotDocumentProperties() ), + ac, "tools_document_properties" ); + + (void) new KAction ( i18n ("F&ormat XML Code"), 0, + m_quanta, SLOT( slotCodeFormatting() ), + ac, "tools_code_formatting"); + + (void) new KAction( i18n( "&Convert Tag && Attribute Case..."), 0, + m_quanta, SLOT(slotConvertCase()), + ac, "tools_change_case"); + + // View actions + + + m_quanta->showSourceAction = + new KToggleAction( i18n( "&Source Editor"), UserIcon ("view_text"), ALT+Key_F9, + m_quanta, SLOT( slotShowSourceEditor()), + ac, "show_quanta_editor"); + m_quanta->showSourceAction->setExclusiveGroup("view"); + + m_quanta->showVPLAction = + new KToggleAction( i18n( "&VPL Editor"), UserIcon ("vpl"), CTRL+SHIFT+Key_F9, + m_quanta, SLOT( slotShowVPLOnly() ), + ac, "show_kafka_view"); + m_quanta->showVPLAction->setExclusiveGroup("view"); + + m_quanta->showVPLSourceAction = + new KToggleAction( i18n("VPL && So&urce Editors"), UserIcon ("vpl_text"), Key_F9, + m_quanta, SLOT( slotShowVPLAndSourceEditor() ), + ac, "show_kafka_and_quanta"); + m_quanta->showVPLSourceAction->setExclusiveGroup("view"); + /**kafkaSelectAction = new KSelectAction(i18n("Main &View"), 0, ac,"show_kafka"); + QStringList list2; + list2.append(i18n("&Source Editor")); + list2.append(i18n("&VPL Editor (experimental)")); + list2.append(i18n("&Both Editors")); + kafkaSelectAction->setItems(list2); + connect(kafkaSelectAction, SIGNAL(activated(int)), m_quanta, SLOT(slotShowKafkaPartl(int)));*/ + + + (void) new KAction( i18n( "&Reload Preview" ), "reload", + KStdAccel::shortcut(KStdAccel::Reload).keyCodeQt(), + m_quanta, SLOT(slotRepaintPreview()), + ac, "reload" ); + + (void) new KAction( i18n( "&Previous File" ), "1leftarrow", KStdAccel::back(), + m_quanta, SLOT( slotBack() ), + ac, "previous_file" ); + + (void) new KAction( i18n( "&Next File" ), "1rightarrow", KStdAccel::forward(), + m_quanta, SLOT( slotForward() ), + ac, "next_file" ); + + // Options actions + // + + (void) new KAction( i18n( "Configure &Actions..." ), UserIcon("ball"),0, + m_quanta, SLOT( slotOptionsConfigureActions() ), + ac, "configure_actions" ); + + KStdAction::showMenubar(m_quanta, SLOT(slotShowMenuBar()), ac, "options_show_menubar"); + KStdAction::keyBindings(m_quanta, SLOT( slotOptionsConfigureKeys() ), ac, "configure_shortcuts"); + KStdAction::configureToolbars( m_quanta, SLOT( slotOptionsConfigureToolbars() ), ac, "options_configure_toolbars"); + KStdAction::preferences(m_quanta, SLOT( slotOptions() ), ac, "general_options"); + new KAction(i18n("Configure Pre&view..."), SmallIcon("konqueror"), 0, m_quanta, SLOT(slotPreviewOptions()), ac, "preview_options"); + + // Toolbars actions + m_quanta->projectToolbarFiles = new KRecentFilesAction(i18n("Load &Project Toolbar"),0, + m_quanta, SLOT(slotLoadToolbarFile(const KURL&)), + ac, "toolbars_load_project"); + + new KAction(i18n("Load &Global Toolbar..."), 0, m_quanta, SLOT(slotLoadGlobalToolbar()), ac, "toolbars_load_global"); + new KAction(i18n("Load &Local Toolbar..."), 0, m_quanta, SLOT(slotLoadToolbar()), ac, "toolbars_load_user"); + new KAction(i18n("Save as &Local Toolbar..."), 0, m_quanta, SLOT(slotSaveLocalToolbar()), ac, "toolbars_save_local"); + new KAction(i18n("Save as &Project Toolbar..."), 0, m_quanta, SLOT(slotSaveProjectToolbar()), ac, "toolbars_save_project"); + new KAction(i18n("&New User Toolbar..."), 0, m_quanta, SLOT(slotAddToolbar()), ac, "toolbars_add"); + new KAction(i18n("&Remove User Toolbar..."), 0, m_quanta, SLOT(slotRemoveToolbar()), ac, "toolbars_remove"); + new KAction(i18n("Re&name User Toolbar..."), 0, m_quanta, SLOT(slotRenameToolbar()), ac, "toolbars_rename"); + new KAction(i18n("Send Toolbar in E&mail..."), "mail_send", 0, m_quanta, SLOT(slotSendToolbar()), ac, "toolbars_send"); + new KAction(i18n("&Upload Toolbar..." ), "network", 0, m_quanta, SLOT(slotUploadToolbar()), ac, "toolbars_upload" ); + new KAction(i18n("&Download Toolbar..." ), "network", 0, m_quanta, SLOT(slotDownloadToolbar()), ac, "toolbars_download" ); + + KToggleAction *toggle = new KToggleAction( i18n("Smart Tag Insertion"), 0, ac, "smart_tag_insertion"); + connect(toggle, SIGNAL(toggled(bool)), m_quanta, SLOT(slotSmartTagInsertion())); + + m_quanta->showDTDToolbar=new KToggleAction(i18n("Show DTD Toolbar"), 0, ac, "view_dtd_toolbar"); + + connect(m_quanta->showDTDToolbar, SIGNAL(toggled(bool)), m_quanta, SLOT(slotToggleDTDToolbar(bool))); + m_quanta->showDTDToolbar->setCheckedState(i18n("Hide DTD Toolbar")); + + new KAction(i18n("Complete Text"), CTRL+Key_Space, + m_quanta, SLOT(slotShowCompletion()), ac,"show_completion"); + new KAction(i18n("Completion Hints"), CTRL+SHIFT+Key_Space, + m_quanta, SLOT(slotShowCompletionHint()), ac,"show_completion_hint"); + + KStdAction::back(m_quanta, SLOT( slotBack() ), ac, "w_back"); + KStdAction::forward(m_quanta, SLOT( slotForward() ), ac, "w_forward"); + + new KAction(i18n("Open File: none"), 0, m_quanta, SLOT(slotOpenFileUnderCursor()), ac, "open_file_under_cursor"); + new KAction(i18n("Upload..."), 0, m_quanta, SLOT(slotUploadFile()), ac, "upload_file"); + new KAction(i18n("Delete File"), 0, m_quanta, SLOT(slotDeleteFile()), ac, "delete_file"); + + QString ss = i18n("Upload Opened Project Files..."); +/* new KAction(i18n("Upload Opened Project Files"), 0, m_quanta, SLOT(slotUploadOpenedFiles()), ac, "upload_opened_files"); */ + + QString error; + int el, ec; + m_quanta->m_actions = new QDomDocument(); +//load the global actions + QFile f(qConfig.globalDataDir + resourceDir + "actions.rc"); + if ( f.open( IO_ReadOnly )) + { + if (m_quanta->m_actions->setContent(&f, &error, &el, &ec)) + { + QDomElement docElem = m_quanta->m_actions->documentElement(); + + QDomNode n = docElem.firstChild(); + while( !n.isNull() ) { + QDomElement e = n.toElement(); // try to convert the node to an element. + if( !e.isNull() ) { // the node was really an element. + bool toggable = (e.attribute("toggable", "") == "true"); + new TagAction(&e, m_quanta, toggable); + } + n = n.nextSibling(); + } + } else + kdError(24000) << QString("Error %1 at (%2, %3) in %4").arg(error).arg(el).arg(ec).arg(f.name()) << endl; + f.close(); + } + m_quanta->m_actions->clear(); +//read the user defined actions + QString s = locateLocal("appdata","actions.rc"); + if (!s.isEmpty()) + { + f.setName(s); + if ( f.open( IO_ReadOnly )) + { + if (m_quanta->m_actions->setContent(&f, &error, &el, &ec)) + { + QDomElement docElem = m_quanta->m_actions->documentElement(); + + QDomNode n = docElem.firstChild(); + while( !n.isNull() ) { + QDomElement e = n.toElement(); // try to convert the node to an element. + if( !e.isNull()) + { // the node was really an element. + delete ac->action(e.attribute("name")); + bool toggable = (e.attribute("toggable", "") == "true"); + new TagAction(&e, m_quanta, toggable); + } + n = n.nextSibling(); + } + } else + kdError(24000) << QString("Error %1 at (%2, %3) in %4").arg(error).arg(el).arg(ec).arg(f.name()) << endl; + f.close(); + } + } else + { + m_quanta->m_actions->setContent(s); + } + + // create the preview action + m_quanta->showPreviewAction = + new KToolBarPopupAction( i18n( "&Preview" ), "preview", Key_F6, + m_quanta, SLOT( slotToggleShowPreview() ), + ac, "show_preview" ); + + KAction *act = new KAction( i18n( "Preview Without Frames" ), "", 0, + m_quanta, SLOT(slotShowNoFramesPreview()), + ac, "show_preview_no_frames" ); + act->plug(m_quanta->showPreviewAction->popupMenu()); + + act = new KAction( i18n( "View with &Konqueror" ), "konqueror", Key_F12, + m_quanta, SLOT( slotViewInKFM() ), + ac, "view_with_konqueror" ); + act->plug(m_quanta->showPreviewAction->popupMenu()); + + + act = ac->action("view_with_firefox"); + if (act) + act->plug(m_quanta->showPreviewAction->popupMenu()); + + act = ac->action("view_with_mozilla"); + if (act) + act->plug(m_quanta->showPreviewAction->popupMenu()); + + act = ac->action("view_with_netscape"); + if (act) + act->plug(m_quanta->showPreviewAction->popupMenu()); + + act = ac->action("view_with_opera"); + if (act) + act->plug(m_quanta->showPreviewAction->popupMenu()); + + act = new KAction( i18n( "View with L&ynx" ), "terminal", 0, + m_quanta, SLOT( slotViewInLynx() ), + ac, "view_with_lynx" ); + act->plug(m_quanta->showPreviewAction->popupMenu()); + + + (void) new KAction( i18n( "Table Editor..." ), "quick_table", 0, + m_quanta, SLOT( slotTagEditTable() ), + ac, "tag_edit_table" ); + + (void) new KAction( i18n( "Quick List..." ), "quick_list", 0, + m_quanta, SLOT( slotTagQuickList() ), + ac, "tag_quick_list" ); + + (void) new KAction( i18n( "Color..." ), "colorize", CTRL+SHIFT+Key_C, + m_quanta, SLOT( slotTagColor() ), + ac, "tag_color" ); + + + (void) new KAction( i18n( "Email..." ), "tag_mail", 0, + m_quanta, SLOT( slotTagMail() ), + ac, "tag_mail" ); + + (void) new KAction( i18n( "Misc. Tag..." ), "tag_misc", CTRL+SHIFT+Key_T, + m_quanta, SLOT( slotTagMisc() ), + ac, "tag_misc" ); + + (void) new KAction( i18n( "Frame Wizard..." ), "frame", 0, + m_quanta, SLOT( slotFrameWizard() ), + ac, "tag_frame_wizard" ); + + (void) new KAction( i18n( "Paste &HTML Quoted" ), "editpaste", 0, + m_quanta, SLOT( slotPasteHTMLQuoted() ), + ac, "edit_paste_html_quoted" ); + + (void) new KAction( i18n( "Paste &URL Encoded" ), "editpaste", 0, + m_quanta, SLOT( slotPasteURLEncoded() ), + ac, "edit_paste_url_encoded" ); + + (void) new KAction( i18n( "Insert CSS..." ),"css", 0, + m_quanta, SLOT( slotInsertCSS() ), + ac, "insert_css" ); + + // special-character combo + KAction* char_action = new KAction( + i18n( "Insert Special Character" ), "charset", 0, + ac, "insert_char" ); + connect( char_action, SIGNAL(activated()), + m_quanta, SLOT(slotInsertChar()) ); + + connect(m_quanta, SIGNAL(eventHappened(const QString&, const QString&, const QString& )), QPEvents::ref(m_quanta), SLOT(slotEventHappened(const QString&, const QString&, const QString& ))); + connect(m_quanta->doc(), SIGNAL(eventHappened(const QString&, const QString&, const QString& )), QPEvents::ref(m_quanta), SLOT(slotEventHappened(const QString&, const QString&, const QString& ))); + connect(ViewManager::ref(), SIGNAL(eventHappened(const QString&, const QString&, const QString& )), QPEvents::ref(m_quanta), SLOT(slotEventHappened(const QString&, const QString&, const QString& ))); + + QuantaBookmarks *m_bookmarks = new QuantaBookmarks(ViewManager::ref(m_quanta)); + m_bookmarks->createActions(ac); + connect(m_bookmarks, SIGNAL(gotoFileAndLine(const QString&, int, int)), m_quanta, SLOT(gotoFileAndLine(const QString&, int, int))); +} + +/** Initialize the plugin architecture. */ +void QuantaInit::initPlugins() +{ + // TODO : read option from plugins.rc to see if we should validate the plugins + + m_quanta->m_pluginInterface = QuantaPluginInterface::ref(m_quanta); + + connect(m_quanta->m_pluginInterface, SIGNAL(hideSplash()), + m_quanta, SLOT(slotHideSplash())); + connect(m_quanta->m_pluginInterface, SIGNAL(statusMsg(const QString &)), + m_quanta, SLOT(slotStatusMsg(const QString & ))); + + m_quanta->m_pluginInterface->readConfig(); + if (!m_quanta->m_pluginInterface->pluginAvailable("KFileReplace")) + { + delete m_quanta->actionCollection()->action("find_in_files"); + } +} + + +void QuantaInit::recoverCrashed(QStringList& recoveredFileNameList) +{ + m_quanta->m_doc->blockSignals(true); + + execCommandPS("ps -C quanta -C quanta_be -o pid --no-headers"); + m_PIDlist = QStringList::split("\n", m_quanta->m_scriptOutput); + + m_config->setGroup("Projects"); + QString pu = QuantaCommon::readPathEntry(m_config, "Last Project"); + + KURL u; + QuantaCommon::setUrl(u, pu); + bool isPrj = true; + if (pu.isEmpty()) + isPrj = false; + if (!u.isValid()) + isPrj = false; + + m_config->reparseConfiguration(); + m_config->setGroup("General Options"); + + QStringList backedUpUrlsList = QuantaCommon::readPathListEntry(m_config, "List of backedup files"); + QStringList autosavedUrlsList = QuantaCommon::readPathListEntry(m_config, "List of autosaved files"); + + QStringList::ConstIterator backedUpUrlsEndIt = backedUpUrlsList.constEnd(); + for (QStringList::ConstIterator backedUpUrlsIt = backedUpUrlsList.constBegin(); + backedUpUrlsIt != backedUpUrlsEndIt; ++backedUpUrlsIt ) + { + // when quanta crashes and file autoreloading option is on + // then if user restarts quanta, the backup copies will reload + QString backedUpFileName = (*backedUpUrlsIt).left((*backedUpUrlsIt).findRev(".")); //the filename without the PID + bool notFound; + QString autosavedPath = searchPathListEntry(backedUpFileName, autosavedUrlsList, notFound); + if (!autosavedPath.isEmpty()) //the current item was autosaved and is not in use by another Quanta + { + KURL originalVersion; + KURL autosavedVersion; + QuantaCommon::setUrl(originalVersion, backedUpFileName); + QuantaCommon::setUrl(autosavedVersion, autosavedPath); + bool isUntitledDocument = false; + if (autosavedVersion.path().right(1) == "U") + isUntitledDocument = true; + if (!isPrj || originalVersion.isLocalFile()) + { + //find some information about local files + KIO::UDSEntry entry; + KIO::NetAccess::stat(originalVersion, entry, m_quanta); + KFileItem* item= new KFileItem(entry, originalVersion, false, true); + QString origTime = item->timeString(); + KIO::filesize_t origSize = item->size(); + delete item; + KIO::NetAccess::stat(autosavedVersion, entry, m_quanta); + item= new KFileItem(entry, autosavedVersion, false, true); + QString backupTime = item->timeString(); + KIO::filesize_t backupSize = item->size(); + delete item; + + if (QFileInfo(autosavedPath).exists()) //if the backup file exists + { + emit hideSplash(); + DirtyDlg *dlg = new DirtyDlg(autosavedVersion.path(), originalVersion.path(), false, m_quanta); + dlg->setCaption(i18n("Restore File")); + DirtyDialog *w = static_cast<DirtyDialog*>(dlg->mainWidget()); + w->textLabel->setText(i18n("<qt>A backup copy of a file was found:<br><br>" + "Original file: <b>%1</b><br>" + "Original file size: <b>%2</b><br>" + "Original file last modified on: <b>%3</b><br><br>" + "Backup file size: <b>%4</b><br>" + "Backup created on: <b>%5</b><br><br>" + "</qt>") + .arg(originalVersion.prettyURL(0, KURL::StripFileProtocol )) + .arg(KIO::convertSize(origSize)).arg(origTime) + .arg(KIO::convertSize(backupSize)).arg(backupTime)); + w->buttonLoad->setText(i18n("&Restore the file from backup")); + w->buttonIgnore->setText(i18n("Do ¬ restore the file from backup")); + delete w->warningLabel; + w->warningLabel = 0L; + w->setMinimumHeight(320); + dlg->adjustSize(); + if (KStandardDirs::findExe("kompare").isEmpty() || isUntitledDocument) + { + w->buttonCompare->setEnabled(false); + w->buttonLoad->setChecked(true); + } + if (dlg->exec()) + { + //backup the current version and restore it from the autosaved backup + KURL backupURL = originalVersion; + backupURL.setPath(backupURL.path() + "." + QString::number(getpid(),10) + ".backup"); + QExtFileInfo::copy(originalVersion, backupURL, -1, true, false, m_quanta); + QExtFileInfo::copy(autosavedVersion, originalVersion, -1, true, false, m_quanta); + //we save a list of autosaved file names so "KQApplicationPrivate::init()" + //can open them. If autosavedVersion.path().right(1) == "U" then we are recovering + //an untitled document + if(isUntitledDocument) + m_quanta->slotFileOpen(autosavedVersion, + m_quanta->defaultEncoding()); // load initial files + else + recoveredFileNameList += backedUpFileName; + } + delete dlg; + QFile::remove(autosavedPath); //we don't need the backup anymore + } + } + //remove the auto-backup file from the list + m_config->setGroup("General Options"); + QStringList autosavedFilesEntryList = QuantaCommon::readPathListEntry(m_config, "List of autosaved files"); + QStringList::Iterator entryIt = autosavedFilesEntryList.begin(); + while(entryIt != autosavedFilesEntryList.end()) + { + if ((*entryIt) == KURL::fromPathOrURL(autosavedPath).url()) + entryIt = autosavedFilesEntryList.remove(entryIt); + else + ++entryIt; + } + m_config->writePathEntry("List of autosaved files", autosavedFilesEntryList); + + autosavedUrlsList = autosavedFilesEntryList; + } + + if (notFound) + { + //remove processed items + m_config->setGroup("General Options"); + + QStringList backedupFilesEntryList = QuantaCommon::readPathListEntry(m_config, "List of backedup files"); + QStringList::Iterator entryIt = backedupFilesEntryList.begin(); + while (entryIt != backedupFilesEntryList.end()) + { + if ((*entryIt) == (*backedUpUrlsIt)) + entryIt = backedupFilesEntryList.remove(entryIt); + else + ++entryIt; + } + m_config->writePathEntry("List of backedup files", backedupFilesEntryList); + } + } + + //clean up auto-backup list, just in case of an old Quanta was used before + QStringList::Iterator entryIt = autosavedUrlsList.begin(); + while (entryIt != autosavedUrlsList.end()) + { + QString quPID = retrievePID((*entryIt)); + + //check if the file is opened by another running Quanta or not + bool isOrphan = true; + QStringList::ConstIterator PIDEndIt = m_PIDlist.constEnd(); + for (QStringList::ConstIterator PIDIt = m_PIDlist.constBegin(); PIDIt != PIDEndIt; ++PIDIt ) + { + if ((*PIDIt) == quPID && qConfig.quantaPID != quPID) + { + isOrphan = false; //the file is opened + break; + } + } + if (isOrphan) + entryIt = autosavedUrlsList.remove(entryIt); + else + ++entryIt; + } + m_config->writePathEntry("List of autosaved files", autosavedUrlsList); + + + +} + + void QuantaInit::execCommandPS(const QString& cmd) + { + + //We create a KProcess that executes the "ps" *nix command to get the PIDs of the + //other instances of quanta actually running + KProcess *execCommand = new KProcess(); + *(execCommand) << QStringList::split(" ",cmd); + + connect(execCommand, SIGNAL(receivedStdout(KProcess*,char*,int)), + m_quanta, SLOT(slotGetScriptOutput(KProcess*,char*,int))); + connect(execCommand, SIGNAL(receivedStderr(KProcess*,char*,int)), + m_quanta, SLOT(slotGetScriptError(KProcess*,char*,int))); + connect(execCommand, SIGNAL(processExited(KProcess*)), + m_quanta, SLOT(slotProcessExited(KProcess*))); + + if (!execCommand->start(KProcess::NotifyOnExit,KProcess::All)) + { + KMessageBox::error(m_quanta, i18n("Failed to query for running Quanta instances.")); + } + else + { + //To avoid lock-ups, start a timer. + QTimer *timer = new QTimer(m_quanta); + connect(timer, SIGNAL(timeout()), + m_quanta, SLOT(slotProcessTimeout())); + timer->start(180*1000, true); + QExtFileInfo internalFileInfo; + m_quanta->m_loopStarted = true; + internalFileInfo.enter_loop(); + delete timer; + } + } + + + QString QuantaInit::searchPathListEntry(const QString& url, const QStringList& autosavedUrlsList, bool ¬Found) +{ + QString backedUpUrlHashedPath = retrieveHashedPath('.' + Document::hashFilePath(url)); + notFound = true; + QStringList::ConstIterator autosavedUrlsEndIt = autosavedUrlsList.constEnd(); + for (QStringList::ConstIterator autosavedUrlsIt = autosavedUrlsList.constBegin(); + autosavedUrlsIt != autosavedUrlsEndIt; + ++autosavedUrlsIt) + { + QString quPID = retrievePID((*autosavedUrlsIt)); + + //check if the file is opened by another running Quanta or not + bool isOrphan = true; + QStringList::ConstIterator PIDEndIt = m_PIDlist.constEnd(); + for (QStringList::ConstIterator PIDIt = m_PIDlist.constBegin(); PIDIt != PIDEndIt; ++PIDIt ) + { + if ((*PIDIt) == quPID && qConfig.quantaPID != quPID) + { + isOrphan = false; //the file is opened + break; + } + } + + if (backedUpUrlHashedPath == retrieveHashedPath((*autosavedUrlsIt))) + { + notFound = false; + if (isOrphan) + return KURL::fromPathOrURL(*autosavedUrlsIt).path(); //the url was autosaved to this file + } + + } + return QString::null; +} + +/** Retrieves hashed path from the name of a backup file */ +QString QuantaInit::retrieveHashedPath(const QString& filename) +{ + int lastPoint = filename.findRev("."); + int Ppos = filename.find("P", lastPoint); + return filename.mid(lastPoint + 1, + Ppos - lastPoint); +} + + +/** Retrieves PID from the name of a backup file */ +QString QuantaInit::retrievePID(const QString& filename) +{ + QString strPID = QString::null; + strPID = filename.mid(filename.findRev("P") + 1); + + if (strPID.isEmpty()) + strPID = filename.mid(filename.findRev("N") + 1); + + if (strPID.endsWith("U")) + strPID = strPID.left(strPID.length() - 1); + + return strPID; +} + + +void QuantaInit::loadVPLConfig() +{ + //load the VPL options + m_config->setGroup("Kafka Synchronization options"); + qConfig.quantaRefreshOnFocus = (m_config->readEntry("Source refresh", "delay") == "focus"); + qConfig.quantaRefreshDelay = m_config->readNumEntry("Source refresh delay", 500); + qConfig.kafkaRefreshOnFocus = (m_config->readEntry("Kafka refresh", "focus") == "focus"); + qConfig.kafkaRefreshDelay = m_config->readNumEntry("Kafka refresh delay", 4000); + /**reloadUpdateTimers();*/ + + m_config->setGroup("Kafka Indentation options"); + qConfig.inlineNodeIndentation = m_config->readBoolEntry("Inline Node Indentation"); +} + +struct Dependency{ + QString name; + QString execName; + QString url; + QString description; + enum Type{ + Executable = 0, + Plugin + }; + Type type; +}; + +void QuantaInit::checkRuntimeDependencies() +{ + + QValueList<Dependency> dependencies; + Dependency dependency; + dependency.name = "Kommander"; + dependency.execName = "kmdr-executor"; + dependency.url = "http://kommander.kdewebdev.org"; + dependency.description = i18n("various script based dialogs including the Quick Start dialog"); + dependency.type = Dependency::Executable; + dependencies.append(dependency); + + dependency.name = "Tidy"; + dependency.execName = "tidy"; + dependency.url = "http://tidy.sourceforge.net"; + dependency.description = i18n("HTML syntax checking"); + dependency.type = Dependency::Executable; + dependencies.append(dependency); + + dependency.name = "Kompare"; + dependency.execName = "kompare"; + dependency.url = "http://bruggie.dnsalias.org/kompare"; + dependency.description = i18n("comparing of files by content"); + dependency.type = Dependency::Executable; + dependencies.append(dependency); + + + dependency.name = i18n("Control Center (kdebase)"); + dependency.execName = "kcmshell"; + dependency.url = "http://www.kde.org"; + dependency.description = i18n("preview browser configuration"); + dependency.type = Dependency::Executable; + dependencies.append(dependency); + + dependency.name = "GPG (OpenPGP)"; + dependency.execName = "gpg"; + dependency.url = "http://www.gnupg.de"; + dependency.description = i18n("preview browser configuration"); + dependency.type = Dependency::Executable; + dependencies.append(dependency); + + dependency.name = "KFileReplace"; + dependency.execName = "KFileReplace"; + dependency.url = "http://kfilereplace.kdewebdev.org"; + dependency.description = i18n("search and replace in files"); + dependency.type = Dependency::Plugin; + dependencies.append(dependency); + + dependency.name = "KXSLDbg"; + dependency.execName = "XSLT Debugger"; + dependency.url = "http://xsldbg.sourceforge.net/"; + dependency.description = i18n("XSLT debugging"); + dependency.type = Dependency::Plugin; + dependencies.append(dependency); + + + dependency.name = "KImageMapEditor"; + dependency.execName = "KImageMapEditor"; + dependency.url = "http://www.nongnu.org/kimagemap/"; + dependency.description = i18n("editing HTML image maps"); + dependency.type = Dependency::Plugin; + dependencies.append(dependency); + + + dependency.name = "KLinkStatus"; + dependency.execName = "Link Checker"; + dependency.url = "http://kde-apps.org/content/show.php?content=12318"; + dependency.description = i18n("link validity checking"); + dependency.type = Dependency::Plugin; + dependencies.append(dependency); + + dependency.name = "Cervisia"; + dependency.execName = "CVS Management (Cervisia)"; + dependency.url = "http://www.kde.org/apps/cervisia"; + dependency.description = i18n("CVS management plugin"); + dependency.type = Dependency::Plugin; + dependencies.append(dependency); + + QString errorStr; + QString stdErrorMsg = i18n("<br><b>- %1</b> [<i>%2</i>] - %3 will not be available;"); + for (QValueList<Dependency>::ConstIterator it = dependencies.constBegin(); it != dependencies.constEnd(); ++it) + { + dependency = *it; + if (dependency.type == Dependency::Executable) + { + if (KStandardDirs::findExe(dependency.execName).isNull()) + errorStr += QString(stdErrorMsg).arg(dependency.name).arg(dependency.url).arg(dependency.description); + + } else + if (dependency.type == Dependency::Plugin) + { + if (!QuantaPlugin::validatePlugin(m_quanta->m_pluginInterface->plugin(dependency.execName))) + errorStr += QString(stdErrorMsg).arg(dependency.name).arg(dependency.url).arg(dependency.description); + } + } + +#ifdef ENABLE_CVSSERVICE + QString error; + QCString appId; + + KApplication::startServiceByDesktopName("cvsservice", QStringList(), &error, + &appId); + if (appId.isEmpty()) + { + errorStr += QString(stdErrorMsg).arg("Cervisia (cvsservice)").arg("http://www.kde.org/apps/cervisia").arg(i18n("integrated CVS management")); + } else + { + CVSService::ref(m_quanta->actionCollection())->setAppId(appId); + connect(CVSService::ref(), SIGNAL(clearMessages()), m_quanta->m_messageOutput, SLOT(clear())); + connect(CVSService::ref(), SIGNAL(showMessage(const QString&, bool)), m_quanta->m_messageOutput, SLOT(showMessage(const QString&, bool))); + connect(CVSService::ref(), SIGNAL(commandExecuted(const QString&, const QStringList&)), m_quanta, SLOT(slotCVSCommandExecuted(const QString&, const QStringList&))); + //connect(CVSService::ref(), SIGNAL(statusMsg(const QString &)), m_quanta, SLOT(slotStatusMsg(const QString & ))); + m_quanta->fTab->plugCVSMenu(); + pTab->plugCVSMenu(); + } +#endif + + if (!errorStr.isEmpty()) + { + m_quanta->slotHideSplash(); + errorStr[errorStr.length() - 1] = '.'; + KMessageBox::information(m_quanta, "<qt>" + i18n("Some applications required for full functionality are missing:<br>") + errorStr + i18n("<br><br>You may download the applications from the specified locations.</qt>"), i18n("Missing Applications"), "RuntimeDependencyCheck"); + } +} + +void QuantaInit::readAbbreviations() +{ + QDomDocument doc; + QString groupName; + bool mainAbbrevFileFound = false; + QStringList mainFileList; + mainFileList << qConfig.globalDataDir + resourceDir + "abbreviations.xml"; + mainFileList << KGlobal::dirs()->saveLocation("data") + resourceDir + "abbreviations.xml"; + for (uint i = 0; i < mainFileList.count(); i++) + { + if (!QFile::exists(mainFileList[i])) + continue; + QFile file(mainFileList[i]); + if (file.open(IO_ReadOnly)) + { + if (doc.setContent(&file)) + { + QDomNodeList groupList = doc.elementsByTagName("Group"); + for (uint groupIdx = 0; groupIdx < groupList.count(); groupIdx++) + { + Abbreviation abbrev; + QDomElement el = groupList.item(groupIdx).toElement(); + groupName = el.attribute("name"); + QDomNodeList dtepList = el.elementsByTagName("DTEP"); + for (uint dtepListIdx = 0; dtepListIdx < dtepList.count(); dtepListIdx++) + { + abbrev.dteps.append(dtepList.item(dtepListIdx).toElement().attribute("name")); + } + QDomNodeList nodeList = el.elementsByTagName("Template"); + for (uint nodeIdx = 0; nodeIdx < nodeList.count(); nodeIdx++) + { + QDomElement e = nodeList.item(nodeIdx).toElement(); + abbrev.abbreviations.insert(e.attribute("name")+" "+e.attribute("description"), e.attribute("code")); + } + qConfig.abbreviations.insert(groupName, abbrev); + } + } + mainAbbrevFileFound = true; + file.close(); + } + } + if (mainAbbrevFileFound) return; +//Compatibility code: read the abbreviations files from the DTEP directories +//TODO: Remove when upgrade from 3.2 is not supported. + QStringList filenameList = DTDs::ref()->fileNameList(false); + QStringList::Iterator it; + for (it = filenameList.begin(); it != filenameList.end(); ++it) + { + int pos =(*it).find('|'); + QString dirName = (*it).mid(pos + 1); + QString dtepName = (*it).left(pos); + KURL dirURL(dirName); + dirURL.setFileName(""); + dirName = dirURL.path(1); + QString abbrevFile = dirName; + QString tmpStr = dirName; + QStringList resourceDirs = KGlobal::dirs()->resourceDirs("data"); + bool dirFound = false; + for (uint i = 0; i < resourceDirs.count(); i++) + { + if (tmpStr.startsWith(resourceDirs[i])) + { + dirFound = true; + tmpStr = tmpStr.right(tmpStr.length() - resourceDirs[i].length()); + break; + } + } + if (dirFound) + { + abbrevFile = KGlobal::dirs()->saveLocation("data", tmpStr) +"/"; + } + abbrevFile.append("abbreviations"); + if (!QFile::exists(abbrevFile)) + abbrevFile = dirName + "abbreviations"; + QFile f(abbrevFile); + if (f.open(IO_ReadOnly)) + { + if (doc.setContent(&f)) + { + Abbreviation abbrev; + QDomNodeList nodeList = doc.elementsByTagName("Template"); + for (uint i = 0; i < nodeList.count(); i++) + { + QDomElement e = nodeList.item(i).toElement(); + abbrev.abbreviations.insert(e.attribute("name")+" "+e.attribute("description"), e.attribute("code")); + } + abbrev.dteps.append(dtepName); + qConfig.abbreviations.insert(DTDs::ref()->getDTDNickNameFromName(dtepName), abbrev); + } + f.close(); + } + } +} + + +int QuantaInit::runningQuantas() +{ + QCStringList list = kapp->dcopClient()->registeredApplications(); + int i = 0; + for (QCStringList::iterator it = list.begin(); it != list.end(); ++it) + { + if (QString(*it).startsWith("quanta", false)) + ++i; + } + return i; +} + +#include "quanta_init.moc" diff --git a/quanta/src/quanta_init.h b/quanta/src/quanta_init.h new file mode 100644 index 00000000..8013e0bd --- /dev/null +++ b/quanta/src/quanta_init.h @@ -0,0 +1,99 @@ +/*************************************************************************** + quantaInit.h - description + ------------------- + begin : ?? ??? 9 13:29:57 EEST 2000 + copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev & Eric Laffoon <[email protected],[email protected],[email protected]> + (C) 2001-2003 by Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 QUANTAINIT_H +#define QUANTAINIT_H + + +// include files for Qt +#include <qobject.h> + +// include files for KDE +#include <kparts/dockmainwindow.h> + +//app includes + +class KMdiToolViewAccessor; + +class QuantaApp; +class ViewManager; +class ProjectTreeView; +class TemplatesTreeView; +class ScriptTreeView; +class KConfig; + +/** + * The base class for Quanta application windows. + */ +class QuantaInit : public QObject +{ + Q_OBJECT + +public: + QuantaInit(QuantaApp * quantaApp); + ~QuantaInit(); + + /** Loads the initial project */ + void loadInitialProject(const QString& url); + + /**Executes *nix ps command */ + void execCommandPS(const QString& cmd); + /** if there are backup files, asks user whether wants to restore them or to mantain the originals instead*/ + void recoverCrashed(QStringList& recoveredFileNameList); + /** Delayed initialization. */ + void initQuanta(); + void openLastFiles(); + void loadVPLConfig(); + +signals: // Signals + /** signal used to hide the splash screen */ + void hideSplash(); + +private: + QuantaApp * m_quanta; + void initToolBars(); + void readOptions(); + + void initActions(); + void initStatusBar(); + void initDocument(); + void initView(); + void initProject(); + void checkRuntimeDependencies(); + void readAbbreviations(); + + KMdiToolViewAccessor* addToolTreeView(QWidget *widget, const QString &name, const QPixmap &icon, KDockWidget::DockPosition position); + + /** Initialize the plugin architecture. */ + void initPlugins(); + + /** find where was url backed up in the list of autosaved urls*/ + QString searchPathListEntry(const QString& url, const QStringList& autosavedUrlsList, bool ¬Found); + /** Retrieves hashed path from the name of a backup file */ + QString retrieveHashedPath(const QString& filename); + /** Obtains PID from file extension */ + QString retrievePID(const QString& filename); + ProjectTreeView *pTab; + TemplatesTreeView *tTab; + // config + KConfig *m_config; + QStringList m_PIDlist; + /** @return number of Quanta instances registered by dcop */ + int runningQuantas(); +}; + +#endif // QUANTA_H diff --git a/quanta/src/quantadoc.cpp b/quanta/src/quantadoc.cpp new file mode 100644 index 00000000..9100432e --- /dev/null +++ b/quanta/src/quantadoc.cpp @@ -0,0 +1,375 @@ +/*************************************************************************** + quantadoc.cpp - description + ------------------- + begin : ���� 9 13:29:57 EEST 2000 + copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev & Eric Laffoon <[email protected],[email protected],[email protected]> + (C) 2001-2003 Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 files for Qt +#include <qdir.h> +#include <qfileinfo.h> +#include <qwidget.h> +#include <qtabwidget.h> +#include <qtabbar.h> +#include <qlayout.h> +#include <qdragobject.h> +#include <qobject.h> + + +// include files for KDE +#include <klocale.h> +#include <kaction.h> +#include <kconfig.h> +#include <kpopupmenu.h> +#include <kmessagebox.h> +#include <kdirwatch.h> +#include <kdeversion.h> +#include <kdebug.h> +#include <kiconloader.h> + +#include <ktexteditor/view.h> +#include <ktexteditor/configinterface.h> +#include <ktexteditor/highlightinginterface.h> +#include <ktexteditor/popupmenuinterface.h> +#include <ktexteditor/markinterface.h> +#include <ktexteditor/undointerface.h> +#include <ktexteditor/viewcursorinterface.h> +#include <ktexteditor/clipboardinterface.h> +#include <ktexteditor/selectioninterface.h> +#include <ktexteditor/encodinginterface.h> +#include <ktexteditor/dynwordwrapinterface.h> +#include <ktexteditor/editorchooser.h> +#include <ktexteditor/editinterface.h> + +#include <kparts/componentfactory.h> + +#include <klibloader.h> +#include <ktrader.h> + + +// application specific includes +#include "document.h" +#include "quanta.h" +#include "quantadoc.h" +#include "quantaview.h" +#include "viewmanager.h" + +#include "quantacommon.h" +#include "qextfileinfo.h" +#include "resource.h" +#include "debuggermanager.h" + +#include "project.h" +//#include "dtds.h" + +#include "undoredo.h" +#include "tagactionmanager.h" +#include "tagactionset.h" + +QuantaDoc::QuantaDoc(QWidget *parent, const char *name) : QObject(parent, name) +{ + fileWatcher = new KDirWatch(this); + + attribMenu = new KPopupMenu(); + attribMenu->insertTitle(i18n("Tag")); + connect( attribMenu, SIGNAL(activated(int)), this, SLOT(slotInsertAttrib(int))); +} + +QuantaDoc::~QuantaDoc() +{ + //kdDebug(24000) << "QuantaDoc::~QuantaDoc: " << endl; +} + + +bool QuantaDoc::newDocument( const KURL& url, bool switchToExisting ) +{ + bool newfile = false; + if ( url.url().isEmpty() ) newfile = true; + Document *w = 0L; + QuantaView *view = ViewManager::ref()->isOpened(url); + if (!view || newfile) + { + w = ViewManager::ref()->activeDocument(); + if (w && !newfile && !w->isModified() && w->isUntitled() && !w->busy) + ViewManager::ref()->removeActiveView(false); + +/* + // no modi and new -> we can remove !!!! + w = ViewManager::ref()->activeDocument(); + if (w && !w->isModified() && + w->isUntitled() && !w->busy) + { + //workaround for some strange Katepart behavior. If there is a highlighting mode + //selected and new content is loaded, the highlighting is reset to None. To avoid this + //remove the untitled document and create a new one, where we don't set the + //highlighting + ViewManager::ref()->removeActiveView(false); + w = ViewManager::ref()->activeDocument(); + + if (w && !w->isModified() && w->isUntitled() && !w->busy) + return true; + } +*/ + // now we can create new kwrite + ViewManager::ref()->createNewDocument(); + view = ViewManager::ref()->activeView(); + + if (Project::ref()->hasProject()) + view->document()->processDTD(Project::ref()->defaultDTD()); + else + view->document()->processDTD(qConfig.defaultDocType); + } + else // select opened + if (switchToExisting) + { + view->document()->checkDirtyStatus(); + if (view != ViewManager::ref()->activeView()) + { + view->activate(); + view->activated(); + } + return false; // don't need loadURL + } + + return true; +} + +void QuantaDoc::openDocument(const KURL& urlToOpen, const QString &a_encoding, + bool switchToExisting, bool readOnly) +{ + bool idleTimerStatus = quantaApp->slotEnableIdleTimer(false); + KURL url = urlToOpen; + if (url.isLocalFile()) + { + QString path = QDir(url.path()).canonicalPath(); + if (!path.isEmpty()) + url.setPath(path); + } + QString encoding = a_encoding; + if (!newDocument(url, switchToExisting)) + { + quantaApp->slotEnableIdleTimer(idleTimerStatus); + return; + } + Document *w = ViewManager::ref()->activeDocument(); + if (readOnly) + { + //might work only with Kate part + KAction *writeLockAction = w->view()->actionCollection()->action("tools_toggle_write_lock"); + if (writeLockAction) + writeLockAction->activate(); + } + if (!url.isEmpty()) + { + if (QExtFileInfo::exists(url, true, quantaApp)) + { + if (encoding.isEmpty()) + encoding = quantaApp->defaultEncoding(); + w->disconnect(SIGNAL(openingFailed(const KURL&))); + connect(w, SIGNAL(openingFailed(const KURL&)), this, SLOT(slotOpeningFailed(const KURL&))); + w->disconnect(SIGNAL(openingCompleted(const KURL&))); + connect(w, SIGNAL(openingCompleted(const KURL&)), this, SLOT(slotOpeningCompleted(const KURL&))); + w->open(url, encoding); + quantaApp->setTitle(url.prettyURL(0, KURL::StripFileProtocol)); + } + else + { + slotOpeningFailed(url); + } + } else + { + quantaApp->reparse(true); + KTextEditor::HighlightingInterface* highlightIf = dynamic_cast<KTextEditor::HighlightingInterface*>(w->doc()); + if (highlightIf) + { + QString hlName; + int htmlIdx = -1, xmlIdx = -1; + for (uint i = 0; i < highlightIf->hlModeCount(); i++) + { + hlName = highlightIf->hlModeName(i); + if (hlName == "HTML") + htmlIdx = i; + if (hlName == "XML") + xmlIdx = i; + } + const DTDStruct *dtd = w->defaultDTD(); + if (dtd->family == 1) + { + if (dtd->singleTagStyle == "xml") + highlightIf->setHlMode(xmlIdx); + else + highlightIf->setHlMode(htmlIdx); + } + } + emit newStatus(); + } + quantaApp->slotEnableIdleTimer(idleTimerStatus); +} + +void QuantaDoc::slotOpeningFailed(const KURL &url) +{ + Q_UNUSED(url); + bool signalStatus = signalsBlocked(); + blockSignals(false); + emit hideSplash(); + //Seems to be not needed anymore since KDE 3.2, but keep until it's completely verified +/* + KMessageBox::error(quantaApp, i18n("<qt>Cannot open document <b>%1</b>.</qt>").arg(url.prettyURL(0, KURL::StripFileProtocol))); +*/ + ViewManager::ref()->removeActiveView(); + blockSignals(signalStatus); +} + +void QuantaDoc::slotOpeningCompleted(const KURL &url) +{ + Document *w = ViewManager::ref()->activeDocument(); + Project::ref()->loadBookmarks(w->url(), dynamic_cast<KTextEditor::MarkInterface*>(w->doc())); + + quantaApp->fileRecent->addURL(url); + quantaApp->slotRepaintPreview(); + quantaApp->reparse(true); + if (url.isLocalFile()) + quantaApp->debugger()->fileOpened(url.prettyURL(0, KURL::StripFileProtocol)); + quantaApp->slotNewStatus(); + quantaApp->setTitle(url.prettyURL(0, KURL::StripFileProtocol)); + Project::ref()->loadCursorPosition(w->url(), dynamic_cast<KTextEditor::ViewCursorInterface*>(w->view())); + emit eventHappened("after_open", url.url(), QString::null); + + bool flag = TagActionManager::canIndentDTD(w->defaultDTD()->name); + quantaApp->actionCollection()->action("apply_source_indentation")->setEnabled(flag); +} + +/** show popup menu with list of attributes for current tag */ +void QuantaDoc::slotAttribPopup() +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + + attribMenu->clear(); + uint line, col; + w->viewCursorIf->cursorPositionReal(&line, &col); + + Node *node = parser->nodeAt(line, col, false); + if (node && node->tag) + { + Tag *tag = node->tag; + QString tagName = tag->name; + QStrIList attrList = QStrIList(); + QString name; + + for (int i=0; i < tag->attrCount(); i++ ) + attrList.append( tag->attribute(i) ); + + if ( QuantaCommon::isKnownTag(w->getDTDIdentifier(),tagName) ) + { + QString caption = i18n("Attributes of <%1>").arg(tagName); + attribMenu->insertTitle( caption ); + + AttributeList *list = QuantaCommon::tagAttributes(w->getDTDIdentifier(),tagName ); + uint menuId = 0; + for ( uint i = 0; i < list->count(); i++ ) + { + name = list->at(i)->name; + attribMenu->insertItem( name , i);//list->findIndex(*item) ); + if (attrList.contains(name)) + { + attribMenu->setItemEnabled( i , false ); + } + menuId++; + } + + QTag* qtag = QuantaCommon::tagFromDTD(w->getDTDIdentifier(), tagName); + for (QStringList::Iterator it = qtag->commonGroups.begin(); it != qtag->commonGroups.end(); ++it) + { + QPopupMenu* popUpMenu = new QPopupMenu(attribMenu, (*it).latin1()); + AttributeList *attrs = qtag->parentDTD->commonAttrs->find(*it); + for (uint j = 0; j < attrs->count(); j++) + { + name = attrs->at(j)->name; + popUpMenu->insertItem(name, ++menuId); + if (attrList.contains(name)) + { + popUpMenu->setItemEnabled( menuId , false ); + } + } + connect( popUpMenu, SIGNAL(activated(int)), this, SLOT(slotInsertAttrib(int))); + attribMenu->insertItem(*it, popUpMenu); + } + + if (menuId > 0) // don't show empty menu, may be core dumped + { + attribMenu->setActiveItem( 0); + + QPoint globalPos = w->view()->mapToGlobal(w->viewCursorIf->cursorCoordinates()); + QFont font = w->view()->font(); + globalPos.setY(globalPos.y() + QFontMetrics(font).height()); + attribMenu->exec(globalPos); + } + } + else { + QString message = i18n("Unknown tag: %1").arg(tagName); + quantaApp->slotStatusMsg( message ); + } + } +} + +void QuantaDoc::slotInsertAttrib( int id ) +{ + Document *w = ViewManager::ref()->activeDocument(); + if (!w) return; + uint line, col; + w->viewCursorIf->cursorPositionReal(&line, &col); + Node *node = parser->nodeAt(line, col); + if (node && node->tag) + { + Tag *tag = node->tag; + QString tagName = tag->name; + if ( QuantaCommon::isKnownTag(w->getDTDIdentifier(), tagName) ) + { + int menuId; + AttributeList *list = QuantaCommon::tagAttributes(w->getDTDIdentifier(), tagName); + menuId = list->count(); + QString attrStr; + if (id <= menuId) + { + attrStr = list->at(id)->name; + } else + { + QTag* qtag = QuantaCommon::tagFromDTD(w->getDTDIdentifier(), tagName); + for (QStringList::Iterator it = qtag->commonGroups.begin(); it != qtag->commonGroups.end(); ++it) + { + AttributeList *attrs = qtag->parentDTD->commonAttrs->find(*it); + menuId += attrs->count(); + if (id <= menuId) + { + attrStr = attrs->at(id - (menuId - attrs->count()) -1)->name; + break; + } + } + } + //now insert the new attribute into the tag + int el, ec; + tag->endPos(el, ec); + w->viewCursorIf->setCursorPositionReal( el, ec ); + w->insertTag( " " + QuantaCommon::attrCase(attrStr) + "="+qConfig.attrValueQuotation, qConfig.attrValueQuotation ); + } + + delete attribMenu; + attribMenu = new KPopupMenu(); + attribMenu->insertTitle(i18n("Tag")); + connect( attribMenu, SIGNAL(activated(int)), this, SLOT(slotInsertAttrib(int))); + } +} + + +#include "quantadoc.moc" diff --git a/quanta/src/quantadoc.h b/quanta/src/quantadoc.h new file mode 100644 index 00000000..f825d72e --- /dev/null +++ b/quanta/src/quantadoc.h @@ -0,0 +1,59 @@ +/*************************************************************************** + quantadoc.h - description + ------------------- + begin : ���� 9 13:29:57 EEST 2000 + copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev & Eric Laffoon <[email protected],[email protected],[email protected]> + (C) 2001-2003 Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 QUANTADOC_H +#define QUANTADOC_H + +// include files for QT + +// forward declaration of the Quanta classes +class Document; +class KConfig; +class KURL; + +class QuantaDoc : public QObject +{ + Q_OBJECT + +public: + + QuantaDoc(QWidget *parent, const char *name=0); + ~QuantaDoc(); + + bool newDocument(const KURL&, bool switchToExisting = true); + void openDocument(const KURL&, const QString& a_encoding = QString::null, bool switchToExisting = true, bool readOnly = false); + +public slots: + /** close documents. */ + /** show popup menu with list of attributes for current tag */ + void slotInsertAttrib( int id ); + void slotAttribPopup(); + + void slotOpeningCompleted(const KURL &url); + void slotOpeningFailed(const KURL &url); + +signals: + void newStatus(); + void hideSplash(); + void eventHappened(const QString&, const QString&, const QString& ); + +private: + KPopupMenu *attribMenu; + bool m_eventLoopStarted; +}; + +#endif // QUANTADOC_H diff --git a/quanta/src/quantaview.cpp b/quanta/src/quantaview.cpp new file mode 100644 index 00000000..39cff3bc --- /dev/null +++ b/quanta/src/quantaview.cpp @@ -0,0 +1,1185 @@ +/*************************************************************************** + quantaview.cpp - description + ------------------- + begin : ���� 9 13:29:57 EEST 2000 + copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev & Eric Laffoon <[email protected],[email protected],[email protected]> + (C) 2001-2005 Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 files for Qt +#include <qprinter.h> +#include <qpainter.h> +#include <qtabbar.h> +#include <qtabwidget.h> +#include <qtimer.h> +#include <qlayout.h> +#include <qwidgetstack.h> +#include <qdom.h> +#include <qfile.h> +#include <qevent.h> +#include <qwidget.h> +#include <qsplitter.h> +#include <qpoint.h> +#include <qscrollview.h> + +// include files for KDE +#include <kaction.h> +#include <kdebug.h> +#include <kdirwatch.h> +#include <khtmlview.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <kmenubar.h> +#include <kiconloader.h> +#include <kmessagebox.h> +#include <kurldrag.h> +#include <kdeversion.h> +#include <kparts/partmanager.h> +#include <kstatusbar.h> + +#include "undoredo.h" +#include "kafkacommon.h" +#include "wkafkapart.h" + +#include <ktexteditor/document.h> +#include <ktexteditor/selectioninterface.h> +#include <ktexteditor/selectioninterfaceext.h> +#include <ktexteditor/view.h> +#include <ktexteditor/viewcursorinterface.h> + +// application specific includes +#include "document.h" +#include "resource.h" +#include "quantaview.h" +#include "quanta.h" +#include "quantacommon.h" +#include "qextfileinfo.h" +#include "viewmanager.h" + +#include "tagaction.h" +#include "toolbartabwidget.h" +#include "quantaplugin.h" +#include "project.h" +#include "structtreeview.h" + +#include "tagdialog.h" + +extern int NN; +extern QValueList<Node*> nodes; + +QuantaView::QuantaView(QWidget *parent, const char *name, const QString &caption ) + : KMdiChildView(parent, name) + , m_document(0L) + , m_plugin(0L) + , m_customWidget(0L) + , m_kafkaDocument(0L) + , m_currentFocus(SourceFocus) +{ + setMDICaption(caption); +//Connect the VPL update timers + connect(&m_sourceUpdateTimer, SIGNAL(timeout()), this, SLOT(sourceUpdateTimerTimeout())); + connect(&m_VPLUpdateTimer, SIGNAL(timeout()), this, SLOT(VPLUpdateTimerTimeout())); + +//create the source and VPL holding widgets + m_documentArea = new QWidget(this); + +//get the reference to the user toolbar holding widget + ToolbarTabWidget *m_toolbarTab = ToolbarTabWidget::ref(); + m_toolbarTab->reparent(this, 0, QPoint(), true); + m_toolbarTab ->setFocusPolicy( QWidget::NoFocus ); + +//create a splitter to separate the VPL and document area + m_splitter = new QSplitter(Qt::Vertical, this); +//place the widgets in a grid + m_viewLayout = new QGridLayout(this, 2, 0); + m_viewLayout->setRowStretch(0, 0); + m_viewLayout->setRowStretch(1,1); + m_viewLayout->addWidget( m_toolbarTab, 0, 0); + m_viewLayout->addWidget( m_documentArea, 1, 0); + + m_documentArea->show(); + + setAcceptDrops(true); // [MB02] Accept drops on the view +} + +QuantaView::~QuantaView() +{ + // quantaApp is undefined if the destructor of QuantaApp is active + if (quantaApp) + quantaApp->slotFileClosed(m_document); + if (m_document) + { + m_document->view()->reparent(0L, 0, QPoint(), false); + if (quantaApp) + emit documentClosed(m_document->url()); + } + delete m_document; + m_document = 0L; +} + +bool QuantaView::mayRemove() +{ + emit hidePreview(); + if (m_plugin) + { + m_plugin->unload(false); + } else + { + bool unmodifiedUntitled = false; + if (m_document && m_document->isUntitled() && !m_document->isModified()) + unmodifiedUntitled = true; + if (m_customWidget) + m_customWidget->reparent(0L, 0, QPoint(), false); + if (!saveModified()) + return false; + slotSetSourceLayout(); + if (static_cast<QuantaView *>(quantaApp->activeWindow()) == this) + { + parser->setSAParserEnabled(false); + kdDebug(24000) << "Node objects before delete = " << NN << " ; list count = " << nodes.count() << endl; + Node::deleteNode(baseNode); + baseNode = 0L; + kdDebug(24000) << "Node objects after delete = " << NN << " ; list count = " << nodes.count() << endl; + QValueList<Node*> nList = nodes; +/* for (QValueList<Node*>::ConstIterator it = nList.constBegin(); it != nList.constEnd(); ++it) + Node::deleteNode(*it); + kdDebug(24000) << "Node objects after cleanup = " << NN << " ; list count = " << nodes.count() << endl;*/ + } + if (m_document) + { + KURL url = m_document->url(); + Project::ref()->saveBookmarks(url, dynamic_cast<KTextEditor::MarkInterface*>(m_document->doc())); + if (!unmodifiedUntitled) + emit eventHappened("before_close", url.url(), QString::null); + m_currentViewsLayout = -1; +// m_document->closeTempFile(); + if (!m_document->isUntitled() && url.isLocalFile()) + { + fileWatcher->removeFile(url.path()); +// kdDebug(24000) << "removeFile[mayRemove]: " << url.path() << endl; + } + Project::ref()->saveCursorPosition(url, dynamic_cast<KTextEditor::ViewCursorInterface*>(m_document->view())); + + quantaApp->menuBar()->activateItemAt(-1); + quantaApp->guiFactory()->removeClient(m_document->view()); + if (!unmodifiedUntitled) + emit eventHappened("after_close", url.url(), QString::null); + } +/* kdDebug(24000) << "Calling reparse from close " << endl; + parser->setSAParserEnabled(true); + quantaApp->reparse(true);*/ + } + return true; +} + +void QuantaView::addDocument(Document *document) +{ + if (!document) + return; + m_document = document; + connect(m_document, SIGNAL(editorGotFocus()), this, SLOT(slotSourceGetFocus())); + connect(m_document->view(), SIGNAL(cursorPositionChanged()), this, SIGNAL(cursorPositionChanged())); + + + m_kafkaDocument = KafkaDocument::ref(); + + connect(m_kafkaDocument->getKafkaWidget(), SIGNAL(hasFocus(bool)), + this, SLOT(slotVPLGetFocus(bool))); + connect(m_kafkaDocument, SIGNAL(newCursorPosition(int,int)), + this, SLOT(slotSetCursorPositionInSource(int, int))); + connect(m_kafkaDocument, SIGNAL(loadingError(Node *)), + this, SLOT(slotVPLLoadingError(Node *))); + + m_kafkaReloadingEnabled = true; + m_quantaReloadingEnabled = true; + m_curCol = m_curLine = m_curOffset = 0; + +//init the VPL part + m_currentViewsLayout = SourceOnly;//to correctly reload the timers. + + reloadUpdateTimers(); + + m_currentViewsLayout = -1; //force loading of this layout + slotSetSourceLayout(); +} + +void QuantaView::addPlugin(QuantaPlugin *plugin) +{ + ToolbarTabWidget *m_toolbarTab = ToolbarTabWidget::ref(); + m_toolbarTab->reparent(0, 0, QPoint(), false); + m_plugin = plugin; + m_splitter->hide(); + QWidget *w = m_plugin->widget(); + if (w) + { + w->reparent(m_documentArea, 0, QPoint(), true); + w->resize(m_documentArea->size()); + } + m_documentArea->reparent(this, 0, QPoint(), true); + m_viewLayout->addWidget(m_documentArea, 1, 0); + activated(); + updateTab(); +} + +void QuantaView::addCustomWidget(QWidget *widget, const QString &label) +{ + if (widget) + { + ToolbarTabWidget::ref()->reparent(0, 0, QPoint(), false); + m_customWidget = widget; + m_splitter->hide(); + widget->reparent(m_documentArea, 0, QPoint(), true); + widget->resize(m_documentArea->size()); + if (!label.isEmpty()) + { + widget->setCaption(label); + updateTab(); + } + m_viewLayout->addWidget(m_documentArea, 1, 0); + m_documentArea->show(); + } else + if (m_customWidget) + { + ToolbarTabWidget::ref()->reparent(this, 0, QPoint(), qConfig.enableDTDToolbar); + m_viewLayout->addWidget(ToolbarTabWidget::ref(), 0 , 0); + m_customWidget = 0L; //avoid infinite recursion + reloadLayout(); + } + if (m_documentArea->height() + ToolbarTabWidget::ref()->height() > height() && ToolbarTabWidget::ref()->isVisible()) + resize(m_documentArea->width(), m_documentArea->height() - ToolbarTabWidget::ref()->height()); + else if (ToolbarTabWidget::ref()->isHidden()) + resize(width(), height()); +} + +void QuantaView::reloadLayout() +{ + int currentViewsLayout = m_currentViewsLayout; + m_currentViewsLayout = -1; //force loading of this layout + switch (currentViewsLayout) + { + case SourceOnly: + slotSetSourceLayout(); + break; + case SourceAndVPL: + slotSetSourceAndVPLLayout(); + break; + case VPLOnly: + slotSetVPLOnlyLayout(); + break; + } +} + +void QuantaView::updateTab() +{ + if (qConfig.showCloseButtons == "ShowAlways") + { + setIcon(SmallIcon("fileclose")); + } + if (m_document) + { + // try to set the icon from mimetype + QIconSet mimeIcon (KMimeType::pixmapForURL(m_document->url(), 0, KIcon::Small)); + if (mimeIcon.isNull()) + mimeIcon = QIconSet(SmallIcon("document")); + QString urlStr = QExtFileInfo::shortName(m_document->url().path()); + if (m_document->isModified()) + { + if (qConfig.showCloseButtons == "ShowAlways") + { + setMDICaption(urlStr + " " + i18n("[modified]")); + } else + { + setIcon(SmallIcon("filesave")); + setMDICaption(urlStr); + } + m_szCaption = urlStr + " " + i18n("[modified]"); + } else + { + if (qConfig.showCloseButtons != "ShowAlways") + { + setIcon(mimeIcon.pixmap()); + } + setMDICaption(urlStr); + quantaApp->setTabToolTip(this, m_document->url().prettyURL(0, KURL::StripFileProtocol)); + } + } else + if (m_plugin) + { + if (qConfig.showCloseButtons != "ShowAlways") + { + setIcon(SmallIcon(m_plugin->icon())); + } + setMDICaption(m_plugin->name()); + } else + if (m_customWidget) + { + if (qConfig.showCloseButtons != "ShowAlways") + { + setIcon(*(m_customWidget->icon())); + } + setMDICaption(m_customWidget->caption()); + } +} + +QString QuantaView::tabName() +{ + if (m_document) + { + return m_document->url().fileName(); + } else + if (m_plugin) + { + return m_plugin->name(); + } else + if (m_customWidget) + { + return m_customWidget->caption(); + } else + return ""; +} + +void QuantaView::slotSetSourceLayout() +{ + emit hidePreview(); + if (m_currentViewsLayout == SourceOnly || !m_document) + return; + + if(m_currentViewsLayout == SourceAndVPL) + m_splitterSizes = m_splitter->sizes(); + + KToggleAction *ta = (KToggleAction *) quantaApp->actionCollection()->action( "show_quanta_editor" ); + if (ta) + ta->setChecked(true); + + //hide the VPL widget, reload the source if necessary + if ((m_currentViewsLayout == SourceAndVPL && m_kafkaDocument->getKafkaWidget()->view()->hasFocus()) || + m_currentViewsLayout == VPLOnly) + { + reloadSourceView(); + } + if (m_kafkaDocument->isLoaded()) + m_kafkaDocument->unloadDocument(); + +//show the document if full size + m_splitter->hide(); + m_kafkaDocument->getKafkaWidget()->view()->reparent(0, 0, QPoint(), false); + m_document->view()->reparent(m_documentArea, 0, QPoint(), true); + m_document->view()->resize(m_documentArea->size()); + m_viewLayout->addWidget(m_documentArea, 1, 0); + m_document->view()->setFocus(); + + m_currentViewsLayout = SourceOnly; + +//update timers are not needed in source only mode + m_sourceUpdateTimer.stop(); + m_VPLUpdateTimer.stop(); +} + + +void QuantaView::slotSetSourceAndVPLLayout() +{ + emit hidePreview(); + if (m_currentViewsLayout == SourceAndVPL || !m_document) + return; + + KToggleAction *ta = (KToggleAction *) quantaApp->actionCollection()->action( "show_kafka_and_quanta" ); + + if (m_document->defaultDTD()->name.contains("HTML", false) == 0) + { + KMessageBox::information(this, i18n("The VPL Mode does not support the current DTD, at the moment: %1").arg(m_document->defaultDTD()->nickName)); + KToggleAction *ta2 = (KToggleAction *) quantaApp->actionCollection()->action( "show_quanta_editor" ); + if (ta2) + ta2->setChecked(true); + return; + } + + if (ta) + ta->setChecked(true); + + + if (!m_kafkaDocument->isLoaded()) + m_kafkaDocument->loadDocument(m_document); + if (m_currentViewsLayout == VPLOnly) + { + reloadSourceView(); + } + m_kafkaDocument->getKafkaWidget()->view()->reparent(m_splitter, 0, QPoint(), true); + m_splitter->moveToFirst(m_kafkaDocument->getKafkaWidget()->view()); + m_document->view()->reparent(m_splitter, 0, QPoint(), true); + m_viewLayout->addWidget(m_splitter, 1, 0); + m_splitter->setSizes(m_splitterSizes); + m_splitter->show(); + + if ( m_currentViewsLayout == SourceOnly && + (!baseNode || (baseNode->tag->type == Tag::Empty && + !baseNode->next && !baseNode->child))) + { + quantaApp->documentProperties(true); + } + + m_currentViewsLayout = SourceAndVPL; + + reloadUpdateTimers(); +} + +void QuantaView::slotSetVPLOnlyLayout() +{ + emit hidePreview(); + if (m_currentViewsLayout == VPLOnly || !m_document) + return; + + if(m_currentViewsLayout == SourceAndVPL) + m_splitterSizes = m_splitter->sizes(); + + KToggleAction *ta = (KToggleAction *) quantaApp->actionCollection()->action( "show_kafka_view" ); + + if (m_document->defaultDTD()->name.contains("HTML", false) == 0) + { + KMessageBox::information(this, i18n("The VPL Mode does not support the current DTD, at the moment: %1").arg(m_document->defaultDTD()->nickName)); + KToggleAction *ta2 = (KToggleAction *) quantaApp->actionCollection()->action( "show_quanta_editor" ); + if (ta2) + ta2->setChecked(true); + return; + } + + if (ta) + ta->setChecked(true); + + m_splitter->hide(); + if (!m_kafkaDocument->isLoaded()) + m_kafkaDocument->loadDocument(m_document); + + m_kafkaDocument->getKafkaWidget()->view()->reparent(m_documentArea, 0, QPoint(), true); + m_kafkaDocument->getKafkaWidget()->view()->resize(m_documentArea->size()); + m_viewLayout->addWidget(m_documentArea, 1, 0); + m_kafkaDocument->getKafkaWidget()->view()->setFocus(); + + if ( m_currentViewsLayout == SourceOnly && + (!baseNode || (baseNode->tag->type == Tag::Empty && + !baseNode->next && !baseNode->child))) + { + quantaApp->documentProperties(true); + } + + m_currentViewsLayout = VPLOnly; + + +//update timers are not needed in VPL only mode + m_sourceUpdateTimer.stop(); + m_VPLUpdateTimer.stop(); +} + +void QuantaView::reloadUpdateTimers() +{ + QuantaView* view=ViewManager::ref()->activeView(); + + m_sourceUpdateTimer.stop(); + m_VPLUpdateTimer.stop(); + + if (m_kafkaDocument->isLoaded() && m_currentViewsLayout == SourceAndVPL && view && view == this) + { + if (m_currentFocus == VPLFocus && !qConfig.quantaRefreshOnFocus) + m_sourceUpdateTimer.start(qConfig.quantaRefreshDelay); + if (m_currentFocus == SourceFocus && !qConfig.kafkaRefreshOnFocus) + m_VPLUpdateTimer.start(qConfig.kafkaRefreshDelay); + } +} + +void QuantaView::slotVPLGetFocus(bool focus) +{ + // is Quanta exiting? + if (!quantaApp) return; +#ifdef LIGHT_DEBUG + kdDebug(25001)<< "slotVPLGetFocus(" << focus << ")" << endl; +#endif + int contentsX, contentsY; + KAction *action; + + if(focus) + { + //We reload the kafka part from the Node Tree + if (m_currentViewsLayout == SourceAndVPL && m_currentFocus == SourceFocus) + { + + contentsX = m_kafkaDocument->getKafkaWidget()->view()->contentsX(); + contentsY = m_kafkaDocument->getKafkaWidget()->view()->contentsY(); + + //Reload the kafka Editor only if Quanta was modified or if something has happened (e.g. a reparse) + //and NEED a kafka reload. + if (parser->parsingNeeded()) + baseNode = parser->rebuild(m_document); + reloadVPLView(); + //doesn't work! + m_kafkaDocument->getKafkaWidget()->view()->setContentsPos(contentsX, contentsY); + } + + //We disable some actions which doesn't work on kafka for the moment + action = quantaApp->actionCollection()->action("tag_edit_table"); + if(action) + action->setEnabled(false); + action = 0L; + action = quantaApp->actionCollection()->action("tag_quick_list"); + if(action) + action->setEnabled(false); + action = 0L; + action = quantaApp->actionCollection()->action("tag_color"); + if(action) + action->setEnabled(false); + action = 0L; + action = quantaApp->actionCollection()->action("tag_mail"); + if(action) + action->setEnabled(false); + action = 0L; + action = quantaApp->actionCollection()->action("tag_misc"); + if(action) + action->setEnabled(false); + action = 0L; + action = quantaApp->actionCollection()->action("tag_frame_wizard"); + if(action) + action->setEnabled(false); + action = 0L; + action = quantaApp->actionCollection()->action("insert_css"); + if(action) + action->setEnabled(false); + action = 0L; + action = quantaApp->actionCollection()->action("insert_char"); + if(action) + action->setEnabled(false); + + //TEMPORARY: Enable VPL undo/redo logging + m_document->docUndoRedo->turnOn(true); + + m_currentFocus = VPLFocus; + reloadUpdateTimers(); + } +} + +void QuantaView::slotSourceGetFocus() +{ + // is Quanta exiting? + if (!quantaApp) return; +#ifdef LIGHT_DEBUG + kdDebug(25001)<< "slotSourceGetFocus(true)" << endl; +#endif + KAction *action; + + quantaApp->partManager()->setActivePart(m_document->doc(), m_document->view()); + //We reload the quanta view from the Node Tree. + if (m_currentViewsLayout == SourceAndVPL && m_currentFocus == VPLFocus) + { + reloadSourceView(); + + //FIXME: the tree (and the output)is right, the pos aren't. + //This will reparse the whole Node tree and reload kafka. + baseNode = parser->parse(m_document); + } + + m_currentFocus = SourceFocus; + reloadUpdateTimers(); + + //We enable some actions which doesn't work on kafka for the moment + action = quantaApp->actionCollection()->action("tag_edit_table"); + if(action) + action->setEnabled(true); + action = 0L; + action = quantaApp->actionCollection()->action("tag_quick_list"); + if(action) + action->setEnabled(true); + action = 0L; + action = quantaApp->actionCollection()->action("tag_color"); + if(action) + action->setEnabled(true); + action = 0L; + action = quantaApp->actionCollection()->action("tag_mail"); + if(action) + action->setEnabled(true); + action = 0L; + action = quantaApp->actionCollection()->action("tag_misc"); + if(action) + action->setEnabled(true); + action = 0L; + action = quantaApp->actionCollection()->action("tag_frame_wizard"); + if(action) + action->setEnabled(true); + action = 0L; + action = quantaApp->actionCollection()->action("insert_css"); + if(action) + action->setEnabled(true); + action = 0L; + action = quantaApp->actionCollection()->action("insert_char"); + if(action) + action->setEnabled(true); + + //TEMPORARY: Disable VPL undo/redo logging + m_document->docUndoRedo->turnOn(false); + +} + +/** Reloads both views ONLY when changes have been made to the Node tree ONLY. */ +void QuantaView::reloadBothViews(bool force) +{ + reloadSourceView(force); + reloadVPLView(force); +} + +/** reload the Kafka view from the Node Tree. Set force to true if you want to reload even if not necessary. */ +void QuantaView::reloadVPLView(bool force) +{ + if (m_document && (m_kafkaReloadingEnabled || force)) + m_document->docUndoRedo->reloadKafkaEditor(force); +} + +/** reload the Quanta view from the Node Tree. Set force to true if you want to reload even if not necessary. */ +void QuantaView::reloadSourceView(bool force) +{ + if (m_quantaReloadingEnabled || force) + m_document->docUndoRedo->reloadQuantaEditor(force); +} + + +void QuantaView::VPLUpdateTimerTimeout() +{ + if(quantaApp && m_currentFocus == SourceFocus) + reloadVPLView(); +} + +void QuantaView::sourceUpdateTimerTimeout() +{ + if(quantaApp && m_currentFocus == VPLFocus) + reloadSourceView(); +} + +void QuantaView::slotVPLLoadingError(Node *) +{ + emit showProblemsView(); +} + + +void QuantaView::slotSetCursorPositionInSource(int col, int line) +{ + m_curCol = col; + m_curLine = line; + if (m_currentViewsLayout == SourceAndVPL || m_currentViewsLayout == SourceOnly) + m_document->viewCursorIf->setCursorPositionReal(line, col); +} + +void QuantaView::dragEnterEvent(QDragEnterEvent *e) +{ + e->accept(KURLDrag::canDecode(e)); +} + +void QuantaView::dropEvent(QDropEvent *e) +{ + emit dragInsert(e); +} + +void QuantaView::resizeEvent(QResizeEvent *e) +{ + QWidget::resizeEvent(e); + resize(m_documentArea->width(), m_documentArea->height()); +} + +void QuantaView::resize(int width, int height) +{ + if (m_plugin && m_plugin->widget()) + { + m_plugin->widget()->resize(width, height); + return; + } else + if (m_customWidget) + { + m_customWidget->resize(width, height); + return; + } else + if (!m_document) + return; + if (m_currentViewsLayout == SourceOnly) + m_document->view()->resize(width, height); + else + if (m_currentViewsLayout == VPLOnly) + m_kafkaDocument->getKafkaWidget()->view()->resize(width,height); + else + if (m_currentViewsLayout == SourceAndVPL) + { + m_splitter->resize(width, height); + m_splitterSizes = m_splitter->sizes(); + } +} + +void QuantaView::insertTag(const char *tag) +{ + if (!m_document ) + return; + QString tagStr = QuantaCommon::tagCase(tag); + const DTDStruct *dtd = m_document->currentDTD(true); + bool single = QuantaCommon::isSingleTag(dtd->name, tagStr); + bool optional = QuantaCommon::isOptionalTag(dtd->name, tagStr); + + QString startTag = tagStr; + startTag.prepend("<"); + if ( dtd->singleTagStyle == "xml" && + ( single || (optional && !qConfig.closeOptionalTags)) + ) + { + startTag.append(" /"); + } + startTag.append(">"); + + if ( (qConfig.closeTags && !single && !optional) || + (qConfig.closeOptionalTags && optional) ) + { + m_document->insertTag( startTag, QString("</")+tagStr+">"); + } + else + { + m_document->insertTag(startTag); + } +} + +//FIXME: Move out from here?? +/** Insert a new tag by bringing up the TagDialog. */ +void QuantaView::insertNewTag(const QString &tag, const QString &attr, bool insertInLine) +{ + if (m_document) + { + if (m_currentFocus == QuantaView::VPLFocus || + (m_currentFocus == QuantaView::SourceFocus && qConfig.smartTagInsertion)) + insertOutputInTheNodeTree("", "", quantaApp->showTagDialogAndReturnNode(tag, attr)); + else + { + QString selection; + if (m_document->selectionIf) + selection = m_document->selectionIf->selection(); + TagDialog *dlg = new TagDialog(QuantaCommon::tagFromDTD(m_document->getDTDIdentifier(), tag), selection, attr, baseURL()); + if (dlg->exec()) + { + dlg->insertTag(m_document, insertInLine); + } + + delete dlg; + } + } +} + +void QuantaView::insertOutputInTheNodeTree(const QString &str1, const QString &str2, Node *node) +{ + if (!m_document) + return; +#ifdef LIGHT_DEBUG + if (node) + kdDebug(25001)<< "QuantaView::insertOutputInTheNodeTree() - node : " << node->tag->name << + " - type : " << node->tag->type << endl; + else + kdDebug(25001)<< "QuantaView::insertOutputInTheNodeTree() - str1 : " << str1 << + " - str2 : " << str2 << endl; +#endif + KafkaWidget *kafkaPart = m_kafkaDocument->getKafkaWidget(); + NodeModifsSet *modifs; + DOM::Node domNode, domStartContainer, domEndContainer; + QString tagName; + QTag *nodeQTag, *qTag, *nodeParentQTag; + Node *nodeCursor, *startContainer, *endContainer, *nodeParent, *dummy; + QPtrList<QTag> qTagList; + int startCol, startLine, endCol, endLine; + bool specialTagInsertion = false; + long nodeOffset, startOffset, endOffset, domNodeOffset; + QValueList<int> loc; + uint line, col; + bool smartTagInsertion, hasSelection, nodeTreeModified; + + if (!node && str1.isEmpty() || node && !str1.isEmpty()) + return; + + //Three cases : + //- Tag insertion in VPL + //- Normal tag insertion in kate + //- Smart tag insertion in kate + smartTagInsertion = (m_currentFocus == QuantaView::SourceFocus && qConfig.smartTagInsertion); + + if (m_currentFocus == QuantaView::VPLFocus || smartTagInsertion) + { + modifs = new NodeModifsSet(); + if (!node && !str1.isEmpty()) + { + //We build the node from the str1 + node = kafkaCommon::createNode("", "", Tag::XmlTag, m_document); + node->tag->parse(str1, m_document); + node->tag->name = QuantaCommon::tagCase(node->tag->name); + node->tag->single = QuantaCommon::isSingleTag(m_document->defaultDTD()->name, + node->tag->name); + } + if (m_currentFocus == QuantaView::VPLFocus) + { + kafkaPart->getCurrentNode(domNode, domNodeOffset); + nodeCursor = m_kafkaDocument->getNode(domNode); + } + else + { + m_document->viewCursorIf->cursorPositionReal(&line, &col); + nodeCursor = parser->nodeAt(line, col, false); + } + + if (!nodeCursor) + return; + + nodeParent = nodeCursor; + 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 + nodeQTag = QuantaCommon::tagFromDTD(m_document->defaultDTD(), + node->tag->name); + + if (!nodeQTag) + return; + + qTagList = nodeQTag->parents(); +#ifdef HEAVY_DEBUG + kdDebug(25001)<< "nodeQTag name : " << nodeQTag->name() << endl; + /**kdDebug(25001)<< nodeQTag->isChild("#text", false) << endl; + kdDebug(25001)<< nodeQTag->isChild("#text", true) << endl;*/ +#endif + for (qTag = qTagList.first(); qTag; qTag = qTagList.next()) + { + if (qTag->isChild("#text", false)) + break; + if (qTag == qTagList.getLast()) + specialTagInsertion = true; + } + + if (m_currentFocus == QuantaView::VPLFocus) + { + m_kafkaDocument->translateKafkaIntoNodeCursorPosition(domNode, domNodeOffset, &dummy, nodeOffset); + kafkaPart->selection(domStartContainer, startOffset, domEndContainer, endOffset); + m_kafkaDocument->translateKafkaIntoNodeCursorPosition(domStartContainer, startOffset, + &startContainer, startOffset); + m_kafkaDocument->translateKafkaIntoNodeCursorPosition(domEndContainer, endOffset, + &endContainer,endOffset); + hasSelection = kafkaPart->hasSelection(); + } + else + if (m_document->selectionIfExt) + { + m_kafkaDocument->translateQuantaIntoNodeCursorPosition(line, col, &dummy, nodeOffset); + startCol = m_document->selectionIfExt->selStartCol(); + startLine = m_document->selectionIfExt->selStartLine(); + endCol = m_document->selectionIfExt->selEndCol(); + endLine = m_document->selectionIfExt->selEndLine(); + m_kafkaDocument->translateQuantaIntoNodeCursorPosition((unsigned)startLine, (unsigned)startCol, + &startContainer, startOffset); + m_kafkaDocument->translateQuantaIntoNodeCursorPosition((unsigned)endLine, (unsigned)endCol, + &endContainer, endOffset); + hasSelection = m_document->selectionIf->hasSelection(); + if (startContainer == endContainer && startContainer->tag->type == Tag::Empty) + { + hasSelection = false; + } + if (endContainer && endContainer->tag->type == Tag::XmlTag && endOffset < (signed)endContainer->tag->tagStr().length()) + { + endContainer = endContainer->previousSibling(); + endOffset = (endContainer)?endContainer->tag->tagStr().length():0; + } + /**else + { + if (startContainer && startContainer->tag->type == Tag::Empty) + startContainer = startContainer->nextNE(); + if (endContainer && endContainer->tag->type == Tag::Empty) + endContainer = endContainer->prevNE(); + }*/ + } + + nodeTreeModified = false; + if (specialTagInsertion) + { + //let's try to insert this node in the closest parent accepting it. + while (nodeParent) + { + nodeParentQTag = + QuantaCommon::tagFromDTD(m_document->defaultDTD(), + nodeParent->tag->name); + if (nodeParentQTag && nodeParentQTag->isChild(node)) + { + nodeCursor = kafkaCommon::createMandatoryNodeSubtree(node, + m_document); + nodeOffset = 0; + kafkaCommon::insertNodeSubtree(node, nodeParent, 0L, 0L, modifs); + nodeTreeModified = true; + break; + } + nodeParent = nodeParent->parent; + } + } + else if (hasSelection && !nodeQTag->isSingle()) + { + //If some text is selected in kafka, surround the selection with the new Node. + if(!startContainer || !endContainer) + return; + nodeTreeModified = kafkaCommon::DTDinsertRemoveNode(node, startContainer, (int)startOffset, + endContainer, (int)endOffset, m_document, &nodeCursor, + nodeOffset, modifs); + } + else + { + //Nothing is selected, simply inserting the Node if it is not an inline. +/* if(!kafkaCommon::isInline(node->tag->name) || nodeQTag->isSingle()) + {*/ + nodeTreeModified = kafkaCommon::DTDinsertRemoveNode(node, nodeCursor, (int)nodeOffset, nodeCursor, + (int)nodeOffset, m_document, &nodeCursor, nodeOffset, modifs); +// } + } + + m_document->docUndoRedo->addNewModifsSet(modifs, undoRedo::NodeTreeModif); + if (m_currentFocus == QuantaView::VPLFocus) + { + //view->reloadVPLView(); + //Now update the VPL cursor position + m_kafkaDocument->translateNodeIntoKafkaCursorPosition(nodeCursor, nodeOffset, domNode, + domNodeOffset); + if (!domNode.isNull() && domNode.nodeType() != DOM::Node::TEXT_NODE && + !domNode.firstChild().isNull() && domNode.firstChild().nodeType() == + DOM::Node::TEXT_NODE) + domNode = domNode.firstChild(); + if (!domNode.isNull()) + kafkaPart->setCurrentNode(domNode, domNodeOffset); + } + else + { + //view->reloadSourceView(); + //Now update the source cursor position + m_kafkaDocument->translateNodeIntoQuantaCursorPosition(nodeCursor, nodeOffset, line, col); + m_document->viewCursorIf->setCursorPositionReal(line, col); + } + if (!nodeTreeModified) + quantaApp->slotStatusMsg(i18n("Cannot insert the tag: invalid location.")); + + } + else + { + m_document->insertTag(str1, str2); + } +} + + +/** Returns the baseURL of the document. */ +KURL QuantaView::baseURL() +{ + KURL base; + if (m_document && !m_document->isUntitled() ) + { + base = QuantaCommon::convertToPath(m_document->url()); + } else + { + base = Project::ref()->projectBaseURL(); + } + return base; +} + +void QuantaView::refreshWindow() +{ + if (!m_document) + { + if (m_plugin) + quantaApp->partManager()->setActivePart(m_plugin->part(), m_plugin->widget()); + resize(width(), height()); + } else + { +/* + kdDebug(24000) << "m_documentArea->height(): " << m_documentArea->height() << endl; + kdDebug(24000) << "ToolbarTabWidget::ref()->height(): " << ToolbarTabWidget::ref()->height() << " hidden: " << ToolbarTabWidget::ref()->isHidden() << " visible: " << ToolbarTabWidget::ref()->isVisible() << endl; + kdDebug(24000) <<"sum: " << m_documentArea->height() + ToolbarTabWidget::ref()->height() << endl; + kdDebug(24000) << "height(): " << height() << endl; + */ + if (m_documentArea->height() + ToolbarTabWidget::ref()->height() - 1 > height() && !ToolbarTabWidget::ref()->isHidden()) //don't use isVisible alone instead of isHidden! + resize(m_documentArea->width(), m_documentArea->height() - ToolbarTabWidget::ref()->height()); + else if (ToolbarTabWidget::ref()->isHidden()) + resize(width(), height()); + } +} + +void QuantaView::activated() +{ + if (!m_document) + { + parser->setSAParserEnabled(false); + quantaApp->slotReloadStructTreeView(); + refreshWindow(); + return; + } + ToolbarTabWidget::ref()->reparent(this, 0, QPoint(), qConfig.enableDTDToolbar); + m_viewLayout->addWidget(ToolbarTabWidget::ref(), 0 , 0); + quantaApp->partManager()->setActivePart(m_document->doc(), m_document->view()); + m_document->checkDirtyStatus(); + StructTreeView::ref()->useOpenLevelSetting = true; + quantaApp->slotLoadToolbarForDTD(m_document->getDTDIdentifier()); + + //TEMP : If the activated document is not a (X)HTML document, disable smartTagInsertion + //Will be removed when VPL will support every DTD + KAction *action = quantaApp->actionCollection()->action("smart_tag_insertion"); + if(action && m_document->defaultDTD()->name.contains("HTML", false) == 0) + { + qConfig.smartTagInsertion = false; + (static_cast<KToggleAction* >(action))->setChecked(false); + } + + reloadLayout(); + refreshWindow(); + } + + +void QuantaView::deactivated() +{ + if (m_plugin) + { + quantaApp->statusBar()->changeItem("", IDS_STATUS); + } + m_sourceUpdateTimer.stop(); + m_VPLUpdateTimer.stop(); +} + +bool QuantaView::saveModified(bool ask) +{ + if (!m_document) + return true; + + bool completed=true; + QString fileName = m_document->url().fileName(); + + if (m_document->isModified() ) + { + if (m_currentFocus == VPLFocus) + reloadSourceView(); + int want_save; + if (ask) + want_save = KMessageBox::warningYesNoCancel(this, + i18n("The file \"%1\" has been modified.\nDo you want to save it?").arg(fileName), + i18n("Warning"), KStdGuiItem::save(), KStdGuiItem::discard()); + else + want_save = KMessageBox::Yes; + + switch (want_save) + { + case KMessageBox::Yes : + if (m_document->isUntitled()) + { + completed = quantaApp->slotFileSaveAs(this); + } + else + { + completed = saveDocument(m_document->url()); + }; + + break; + + case KMessageBox::No : + { + m_document->removeBackup(quantaApp->config()); + completed=true; + } + break; + + case KMessageBox::Cancel : + completed=false; + break; + + default: + completed=false; + break; + } + } else + m_document->removeBackup(quantaApp->config()); + return completed; +} + +bool QuantaView::saveDocument(const KURL& url) +{ + if (url.isEmpty()) + return false; + + emit eventHappened("before_save", url.url(), QString::null); + m_saveResult = true; + KURL oldURL = m_document->url(); + if (!m_document->isUntitled() && oldURL.isLocalFile()) + { + fileWatcher->removeFile(oldURL.path()); +// kdDebug(24000) << "removeFile[saveDocument]: " << oldURL.path() << endl; + } + if (url.isLocalFile()) + { + if (!m_document->saveAs(url)) + { + fileWatcher->addFile(oldURL.path()); +// kdDebug(24000) << "addFile[saveDocument]: " << oldURL.path() << endl; + return false; //saving to a local file failed + } else //successful saving to a local file + { + m_document->setDirtyStatus(false); + m_document->removeBackup(quantaApp->config()); + fileWatcher->addFile(m_document->url().path()); +// kdDebug(24000) << "addFile[saveDocument, 2]: " << m_document->url().path() << endl; + } + } else //saving to a remote file + { + KTextEditor::Document *doc = m_document->doc(); + m_eventLoopStarted = false; + connect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotSavingFailed(const QString &))); + connect(doc, SIGNAL(completed()), this, SLOT(slotSavingCompleted())); + m_saveResult = m_document->saveAs(url); + if (m_saveResult) + { + //start an event loop and wait until the saving finished + QExtFileInfo internalFileInfo; + m_eventLoopStarted = true; + internalFileInfo.enter_loop(); + } + disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotSavingFailed(const QString &))); + disconnect(doc, SIGNAL(completed()), this, SLOT(slotSavingCompleted())); + if (!m_saveResult) //there was an error while saving + { + if (oldURL.isLocalFile()) + { + fileWatcher->addFile(oldURL.path()); +// kdDebug(24000) << "addFile[saveDocument, 3]: " << oldURL.path() << endl; + } + return false; + } + } + // everything went fine + if (oldURL != m_document->url()) + { + setCaption(m_document->url().fileName()); + } + emit eventHappened("after_save", m_document->url().url(), QString::null); + return true; +} + +void QuantaView::slotSavingFailed(const QString &error) +{ + Q_UNUSED(error); + m_saveResult = false; + if (m_eventLoopStarted) + qApp->exit_loop(); +} + +void QuantaView::slotSavingCompleted() +{ + m_saveResult = true; + m_document->setDirtyStatus(false); + m_document->removeBackup(quantaApp->config()); + if (m_eventLoopStarted) + qApp->exit_loop(); +} + +#include "quantaview.moc" diff --git a/quanta/src/quantaview.h b/quanta/src/quantaview.h new file mode 100644 index 00000000..24ecd776 --- /dev/null +++ b/quanta/src/quantaview.h @@ -0,0 +1,233 @@ +/*************************************************************************** + quantaview.h - description + ------------------- + begin : ���� 9 13:29:57 EEST 2000 + copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev & Eric Laffoon <[email protected],[email protected],[email protected]> + (C) 2001-2005 Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 QUANTAVIEW_H +#define QUANTAVIEW_H + +// include files for Qt +#include <qguardedptr.h> +#include <qwidget.h> +#include <qptrlist.h> +#include <qvaluelist.h> +#include <qtimer.h> + +//kde includes +#include <kmdichildview.h> + +#include <dom/dom_node.h> + +class QuantaDoc; +class Document; +class ToolbarTabWidget; +class KafkaDocument; +class QuantaPlugin; + +class QSplitter; +class KURL; +class QGridLayout; + +namespace DOM +{ + class Node; +} + +namespace KTextEditor +{ + class Mark; +} +class QSplitter; +class Node; + +/** The QuantaView class provides the view widget for the QuantaApp + * instance. The View instance inherits QWidget as a base class and + * represents the view object of a KTMainWindow. As QuantaView is part + * of the docuement-view model, it needs a reference to the document + * object connected with it by the QuantaApp class to manipulate and + * display the document structure provided by the QuantaDoc class. + * + * @author Source Framework Automatically Generated by KDevelop, (c) The KDevelop Team. + * @version KDevelop version 0.4 code generation */ +class QuantaView : public KMdiChildView +{ + Q_OBJECT + +public: + + QuantaView(QWidget *parent = 0, const char *name=0, const QString &caption = QString::null); + ~QuantaView(); + + /** returns true if the view can be removed, false otherwise */ + bool mayRemove(); + + /**Adds a Document object to the view. Also creates the VPL widget for the document. */ + void addDocument(Document *document); + + /** Adds a custom widget to the view. */ + void addCustomWidget(QWidget *widget, const QString &label); + + /** returns the Document object associated with this view. Returns 0L if the view holds + a non-Document object */ + Document *document() {return m_document;}; + + /**Adds a QuantaPlugin object to the view.*/ + void addPlugin(QuantaPlugin *plugin); + + QWidget* documentArea() {return m_documentArea;} + + bool saveDocument(const KURL&); + /** Saves the document if it's modified. Ask the user if their + @param ask is true. */ + bool saveModified(bool ask = true); + /** Returns the baseURL of the document. */ + KURL baseURL(); + + /** Tells which widget had the focus the more recently */ + int hadLastFocus() {return m_currentFocus;} + /** Reloads both views ONLY when changes have been made to the Node tree ONLY. + * Set force to true if you want to reload even if not necessary.*/ + void reloadBothViews(bool force = false); + /** reload the Kafka view from the Node Tree. Set force to true if you want to reload even if not necessary. */ + void reloadVPLView(bool force = false); + /** reload the Quanta view from the Node Tree. Set force to true if you want to reload even if not necessary. */ + void reloadSourceView(bool force = false); + /** Return the curren views layout*/ + int currentViewsLayout() {return m_currentViewsLayout;} + + void reloadLayout(); + + /** Called when this view become the active one */ + void activated(); + /** Called when this view lost the active status */ + void deactivated(); + + void resizeEvent(QResizeEvent* e); + /** Resize the current view */ + void resize(int width, int height); + /** Redraws the view, resizes the components to their correct size */ + void refreshWindow(); + + /** Updates the icon at the name on the view tab */ + void updateTab(); + + /** Returns the tab name associated with this view */ + QString tabName(); + + void insertTag( const char *tag); + + /** Insert a new tag by bringing up the TagDialog. */ + void insertNewTag(const QString &tag, const QString &attr = QString::null, bool insertInLine = true); + /** + * This function take the output of the TagAction, parse it into Nodes and insert it + * in the Node tree. Then kafka will take care of updating itself from the Node Tree. + */ + void insertOutputInTheNodeTree(const QString &str1, const QString &str2 = QString::null, Node *node = 0L); + + enum ViewFocus { + SourceFocus = 0, + VPLFocus + }; + + enum ViewLayout { + SourceOnly = 0, + SourceAndVPL, + VPLOnly + }; + + +public slots: + void slotSetSourceLayout(); + void slotSetSourceAndVPLLayout(); + void slotSetVPLOnlyLayout(); + + /** + * Called whenever the KafkaWidget widget get/lost the focus. + */ + void slotVPLGetFocus(bool focus); + + /** + * Called whenever the KTextEditor::View widget get the focus. + */ + void slotSourceGetFocus(); + + /** + * Called when we want to set the Quanta cursor. + * Record the position until Quanta get the focus again. + * Useful when we want to set it when quanta doesn't have the focus. + */ + void slotSetCursorPositionInSource(int col, int line); + /** + * Called when an error occured when loading kafka. + * Pop up the Error reporter dialog. + */ + void slotVPLLoadingError(Node *node); + + /** Restarts the update timers according to the current settings */ + void reloadUpdateTimers(); + +private slots: + void slotSavingCompleted(); + void slotSavingFailed(const QString& error); + + /** + * Called to update VPL. + */ + void VPLUpdateTimerTimeout(); + + /** + * Called to update the source. + */ + void sourceUpdateTimerTimeout(); + +signals: + /** emitted when a file from the template view is dropped on the view */ + void dragInsert(QDropEvent *); + /** asks for hiding the preview widget */ + void hidePreview(); + void showProblemsView(); + void cursorPositionChanged(); + void title(const QString &); + /** emitted if this view contained an editor and it is closed */ + void documentClosed(const KURL&); + + void eventHappened(const QString&, const QString&, const QString& ); + +private: +/** Kafka stuff */ + QValueList<int> m_splitterSizes; + int m_curCol, m_curLine, m_curOffset; + DOM::Node curNode; + bool m_kafkaReloadingEnabled, m_quantaReloadingEnabled; + QTimer m_sourceUpdateTimer, m_VPLUpdateTimer; + + QWidget *m_documentArea;///< the area of the view which can be used to show the source/VPL + Document *m_document; + QuantaPlugin *m_plugin; + QWidget *m_customWidget; ///<view holds a custom widget, eg. a documentation + QGuardedPtr<KafkaDocument> m_kafkaDocument; + QSplitter *m_splitter; + QGridLayout *m_viewLayout; + int m_currentViewsLayout; ///< holds the current layout, which can be SourceOnly, VPLOnly or SourceAndVPL + int m_currentFocus; + bool m_saveResult; + bool m_eventLoopStarted; + +protected: + virtual void dropEvent(QDropEvent *e); + virtual void dragEnterEvent(QDragEnterEvent *e); +}; + +#endif // QUANTAVIEW_H diff --git a/quanta/src/viewmanager.cpp b/quanta/src/viewmanager.cpp new file mode 100644 index 00000000..f02108e9 --- /dev/null +++ b/quanta/src/viewmanager.cpp @@ -0,0 +1,756 @@ +/*************************************************************************** + viewmanager - implementation + begin : Fri Mar 26 2004 + copyright : (C) 2004 by Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * + * 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 <qdir.h> + +//kde includes +#include <kdirwatch.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmdimainfrm.h> +#include <kurl.h> +#include <kpopupmenu.h> +#include <ktexteditor/view.h> +#include <ktexteditor/encodinginterface.h> +#include <ktexteditor/highlightinginterface.h> +#include <ktexteditor/popupmenuinterface.h> +#include <ktexteditor/editorchooser.h> +#include <ktexteditor/markinterface.h> +#include <kio/netaccess.h> + + +//app includes +#include "undoredo.h" +#include "wkafkapart.h" +#include "tagactionmanager.h" +#include "tagactionset.h" + +#include "whtmlpart.h" +#include "document.h" +#include "quantaview.h" +#include "viewmanager.h" +#include "project.h" +#include "resource.h" +#include "quanta.h" +#include "quantabookmarks.h" +#include "toolbartabwidget.h" +#include "parser.h" +#include "qextfileinfo.h" +#include "qpevents.h" +#include "ksavealldialog.h" +#include "annotationoutput.h" + +#ifdef ENABLE_CVSSERVICE +#include "cvsservice.h" +#endif + +#define SEPARATOR_INDEX 3 +#define RELOAD_ID 11 +#define UPLOAD_ID 12 +#define DELETE_ID 13 + +ViewManager::ViewManager(QObject *parent, const char *name) : QObject(parent, name) +{ + m_lastActiveView = 0L; + m_lastActiveEditorView = 0L; + m_documentationView = 0L; + m_tabPopup = new KPopupMenu(quantaApp); + m_tabPopup->insertItem(SmallIcon("fileclose"), i18n("&Close"), this, SLOT(slotCloseView())); + m_tabPopup->insertItem(i18n("Close &Other Tabs"), this, SLOT(slotCloseOtherTabs())); + m_tabPopup->insertItem(i18n("Close &All"), this, SLOT(closeAll())); + m_tabPopup->insertItem(SmallIcon("revert"), i18n("&Reload"), this, SLOT(slotReloadFile()), 0, RELOAD_ID); + m_tabPopup->insertItem(SmallIcon("up"), i18n("&Upload File"), this, SLOT(slotUploadFile()), 0, UPLOAD_ID); + m_tabPopup->insertItem(SmallIcon("editdelete"), i18n("&Delete File"), this, SLOT(slotDeleteFile()), 0, DELETE_ID); + m_tabPopup->insertSeparator(); + m_fileListPopup = new KPopupMenu(quantaApp); + connect(m_fileListPopup, SIGNAL(aboutToShow()), this, SLOT(slotFileListPopupAboutToShow())); + connect(m_fileListPopup, SIGNAL(activated(int)), this, SLOT(slotFileListPopupItemActivated(int))); + m_bookmarks = new QuantaBookmarks(this, QuantaBookmarks::Position, true); + m_bookmarksMenu = new KPopupMenu(quantaApp); + m_bookmarks->setBookmarksMenu(m_bookmarksMenu); + connect(m_bookmarks, SIGNAL(gotoFileAndLine(const QString&, int, int)), quantaApp, SLOT(gotoFileAndLine(const QString&, int, int))); + m_bookmarksMenuId = m_tabPopup->insertItem(SmallIconSet("bookmark"), i18n("&Bookmarks"), m_bookmarksMenu); + m_tabPopup->insertItem(i18n("&Switch To"), m_fileListPopup); + m_contextView = 0L; + m_cvsMenuId = -1; + m_separatorVisible = false; +} + +QuantaView* ViewManager::createView(const QString &caption) +{ + QuantaView *view = new QuantaView(quantaApp, "", caption); + quantaApp->addWindow(view); + connect(view, SIGNAL(cursorPositionChanged()), quantaApp, SLOT(slotNewLineColumn())); + connect(view, SIGNAL(title(const QString &)), quantaApp, SLOT(slotNewLineColumn())); + connect(view, SIGNAL(dragInsert(QDropEvent*)), this, SIGNAL(dragInsert(QDropEvent *))); + connect(view, SIGNAL(hidePreview()), quantaApp, SLOT(slotChangePreviewStatus())); + disconnect(view, SIGNAL(childWindowCloseRequest( KMdiChildView *)), 0, 0 ); + connect(view, SIGNAL(childWindowCloseRequest( KMdiChildView*)), this, SLOT(slotCloseRequest(KMdiChildView*))); + connect(view, SIGNAL(documentClosed(const KURL&)), this, SLOT(slotDocumentClosed(const KURL&))); + connect(view, SIGNAL(eventHappened(const QString&, const QString&, const QString& )), QPEvents::ref(), SLOT(slotEventHappened(const QString&, const QString&, const QString& ))); + + return view; +} + +void ViewManager::createNewDocument() +{ + int i = 1; + while (isOpened(KURL("file:"+i18n("Untitled%1").arg(i)))) i++; + QString fname = i18n("Untitled%1").arg(i); + QuantaView *view = createView(fname); + +#ifdef ENABLE_EDITORS + KTextEditor::Document *doc = + KTextEditor::EditorChooser::createDocument( + view, + "KTextEditor::Document" + ); +#else + KTextEditor::Document *doc = KTextEditor::createDocument ("libkatepart", view, "KTextEditor::Document"); +#endif + Document *w = new Document(doc, 0L); + connect(w, SIGNAL(showAnnotation(uint, const QString&, const QPair<QString, QString>&)), quantaApp->annotationOutput(), SLOT(insertAnnotation(uint, const QString&, const QPair<QString, QString>&))); + QString encoding = quantaApp->defaultEncoding(); + KTextEditor::EncodingInterface* encodingIf = dynamic_cast<KTextEditor::EncodingInterface*>(doc); + if (encodingIf) + encodingIf->setEncoding(encoding); + + KTextEditor::View * v = w->view(); + + //[MB02] connect all kate views for drag and drop + connect(w->view(), SIGNAL(dropEventPass(QDropEvent *)), this, SIGNAL(dragInsert(QDropEvent *))); + + w->setUntitledUrl( fname ); + KTextEditor::PopupMenuInterface* popupIf = dynamic_cast<KTextEditor::PopupMenuInterface*>(w->view()); + if (popupIf) + popupIf->installPopup((QPopupMenu *)quantaApp->factory()->container("popup_editor", quantaApp)); + + quantaApp->setFocusProxy(w->view()); + w->view()->setFocusPolicy(QWidget::WheelFocus); + connect( v, SIGNAL(newStatus()), quantaApp, SLOT(slotNewStatus())); + + quantaApp->slotNewPart(doc, true); // register new part in partmanager and make active + view->addDocument(w); + view->activate(); //if we don't call this manually, the activeView() won't return the newly created view + view->activated(); //the previous activate does not call this, because it detects that the view was not changed (createView() also calls activate()) + m_lastActiveView = view; + m_lastActiveEditorView = view; + quantaApp->newCursorPosition("", 1 , 1); +} + +bool ViewManager::removeView(QuantaView *view, bool force, bool createNew) +{ + if (!view) return false; + int noOfViews = 0; + QValueList<Document*> list; + KMdiIterator<KMdiChildView*> *it = quantaApp->createIterator(); + for (it->first(); !it->isDone(); it->next()) + { + noOfViews++; + if (noOfViews > 1) + break; + } + delete it; + if (noOfViews == 1 && view->document() && view->document()->isUntitled() && !view->document()->isModified() && createNew) + { + quantaApp->slotShowPreviewWidget(false); + return true; + } + bool mayRemove = view->mayRemove(); + if (mayRemove) + { + if (force || mayRemove) + { + if (view == m_documentationView) + m_documentationView = 0L; + if (view == m_lastActiveView) + m_lastActiveView = 0L; + if (view == m_lastActiveEditorView) + m_lastActiveEditorView = 0L; + if (view == activeView()) + ToolbarTabWidget::ref()->reparent(0L, 0, QPoint(), false); + if (!createNew) + disconnect(quantaApp, SIGNAL(lastChildViewClosed()), this, SLOT(slotLastViewClosed())); + quantaApp->closeWindow(view); + if (createNew) + { + if (allEditorsClosed()) + { + quantaApp->slotFileNew(); + } + } else + connect(quantaApp, SIGNAL(lastChildViewClosed()), this, SLOT(slotLastViewClosed())); + return true; + } + } + return false; +} + +Document *ViewManager::activeDocument() +{ + QuantaView *view = activeView(); + return view ? view->document() : 0L; +} + +QuantaView* ViewManager::activeView() +{ + return static_cast<QuantaView *>(quantaApp->activeWindow()); +} + +void ViewManager::slotViewActivated(KMdiChildView *view) +{ + if (!quantaApp || m_lastActiveView == view) + return; + if (m_lastActiveView) + { + m_lastActiveView->deactivated(); + } + QuantaView *qView = static_cast<QuantaView*>(view); + qView->activated(); + +#ifdef DEBUG_PARSER +// kdDebug(24000) << "Calling clearGroups from ViewManager::slotViewActivated" << endl; +#endif + parser->clearGroups(); + parser->setSAParserEnabled(true); + quantaApp->reparse(true); //FIXME + quantaApp->slotNewStatus(); + quantaApp->slotNewLineColumn(); + typingInProgress = false; //need to reset, as it's set to true in the above slots + m_lastActiveView = qView; + if (m_lastActiveView->document()) + { + m_lastActiveEditorView = m_lastActiveView; + } + + Document *w = qView->document(); + if (w && !w->isUntitled()) + { + emit viewActivated(w->url()); + + bool flag = TagActionManager::canIndentDTD(w->defaultDTD()->name); + quantaApp->actionCollection()->action("apply_source_indentation")->setEnabled(flag); + } +} + +void ViewManager::slotCloseOtherTabs() +{ + KMdiChildView *currentView; + KMdiChildView *view; + QuantaView *qView; + if (m_contextView) + currentView = m_contextView; + else + currentView = quantaApp->activeWindow(); + if (dynamic_cast<QuantaView*>(currentView) && !static_cast<QuantaView*>(currentView)->document()) + ToolbarTabWidget::ref()->reparent(0, 0, QPoint(), false); + KMdiIterator<KMdiChildView*> *it = quantaApp->createIterator(); + //save the children first to a list, as removing invalidates our iterator + QValueList<KMdiChildView *> children; + for (it->first(); !it->isDone(); it->next()) + { + children.append(it->currentItem()); + } + delete it; + KURL::List modifiedList; + QValueListIterator<KMdiChildView *> childIt; + for (childIt = children.begin(); childIt != children.end(); ++childIt) + { + view = *childIt; + qView = dynamic_cast<QuantaView*>(view); + if (qView && view != currentView) + { + Document *w = qView->document(); + if (w && w->isModified()) + { + modifiedList += w->url(); + } + } + } + if (!modifiedList.isEmpty()) + { + KURL::List filesToSave; + KSaveSelectDialog dlg(modifiedList, filesToSave /*empty ignore list */, quantaApp); + if (dlg.exec() == QDialog::Accepted) + { + filesToSave = dlg.filesToSave(); + for (childIt = children.begin(); childIt != children.end(); ++childIt) + { + view = *childIt; + qView = dynamic_cast<QuantaView*>(view); + if (view && view != currentView) + { + Document *w = qView->document(); + if (w) + { + if (filesToSave.contains(w->url())) + if (!qView->saveModified(false)) + { + return; //save aborted + } + w->setModified(false); + qView->updateTab(); + } + } + } + } else + { + return; //save aborted + } + } + + for (childIt = children.begin(); childIt != children.end(); ++childIt) + { + view = *childIt; + if (view != currentView) + { + if (dynamic_cast<QuantaView*>(view) && !static_cast<QuantaView*>(view)->mayRemove() ) + continue; + quantaApp->closeWindow(view); + } + } +} + +QuantaView* ViewManager::isOpened(const KURL& url) +{ + KURL url2 = url; + if (url2.isLocalFile() && !url.path().startsWith(i18n("Untitled"))) + { + QDir dir(url2.path()); + url2.setPath(dir.canonicalPath()); + } + KMdiIterator<KMdiChildView*> *it = quantaApp->createIterator(); + QuantaView *view; + for (it->first(); !it->isDone(); it->next()) + { + view = dynamic_cast<QuantaView*>(it->currentItem()); + if (view && view->document() && view->document()->url() == url2) + { + delete it; + return view; + } + } + delete it; + return 0L; +} + +KURL::List ViewManager::openedFiles(bool noUntitled) +{ + KURL::List list; + + KMdiIterator<KMdiChildView*> *it = quantaApp->createIterator(); + QuantaView *view; + for (it->first(); !it->isDone(); it->next()) + { + view = dynamic_cast<QuantaView*>(it->currentItem()); + if (view) + { + Document *w = view->document(); + if ( w && (!w->isUntitled() || !noUntitled) ) + list.append( w->url() ); + } + } + delete it; + return list; +} + +QValueList<Document*> ViewManager::openedDocuments() +{ + QValueList<Document*> list; + KMdiIterator<KMdiChildView*> *it = quantaApp->createIterator(); + QuantaView *view; + for (it->first(); !it->isDone(); it->next()) + { + view = dynamic_cast<QuantaView*>(it->currentItem()); + if (view) + { + Document *w = view->document(); + if (w) + list.append(w); + } + } + delete it; + return list; +} + +bool ViewManager::saveAll() +{ + bool flagsave = true; + KMdiIterator<KMdiChildView*> *it = quantaApp->createIterator(); + QuantaView *view; + for (it->first(); !it->isDone(); it->next()) + { + view = dynamic_cast<QuantaView*>(it->currentItem()); + if (view) + { + Document *w = view->document(); + if ( w && w->isModified()) + { + if (!w->isUntitled()) + { + emit eventHappened("before_save", w->url().url(), QString::null); + w->docUndoRedo->fileSaved(); + w->save(); + w->removeBackup(quantaApp->config()); + if (w->isModified()) + flagsave = false; + emit eventHappened("after_save", w->url().url(), QString::null); + } else + { + if (!view->saveModified()) + flagsave = false; + } + } + view->updateTab(); + } + } + delete it; + + return flagsave; +} + +bool ViewManager::closeAll(bool createNew) +{ + quantaApp->slotShowPreviewWidget(false); + parser->setSAParserEnabled(false); + KMdiIterator<KMdiChildView*> *it = quantaApp->createIterator(); + QuantaView *view; + //save the children first to a list, as removing invalidates our iterator + QValueList<KMdiChildView *> children; + for (it->first(); !it->isDone(); it->next()) + { + children.append(it->currentItem()); + } + delete it; + KURL::List modifiedList; + QValueListIterator<KMdiChildView *> childIt; + for (childIt = children.begin(); childIt != children.end(); ++childIt) + { + view = dynamic_cast<QuantaView*>(*childIt); + if (view) + { + Document *w = view->document(); + if (w && w->isModified()) + { + modifiedList += w->url(); + } + } + } + if (!modifiedList.isEmpty()) + { + KURL::List filesToSave; + KSaveSelectDialog dlg(modifiedList, filesToSave /*empty ignore list */, quantaApp); + if (dlg.exec() == QDialog::Accepted) + { + filesToSave = dlg.filesToSave(); + for (childIt = children.begin(); childIt != children.end(); ++childIt) + { + view = dynamic_cast<QuantaView*>(*childIt); + if (view) + { + Document *w = view->document(); + if (w) + { + if (filesToSave.contains(w->url())) + if (!view->saveModified(false)) + { + emit filesClosed(false); + return false; //save aborted + } + w->setModified(false); + view->updateTab(); + } + } + } + } else + { + emit filesClosed(false); + return false; //save aborted + } + } + disconnect(quantaApp, SIGNAL(viewActivated (KMdiChildView *)), this, SLOT(slotViewActivated(KMdiChildView*))); + disconnect(quantaApp, SIGNAL(lastChildViewClosed()), this, SLOT(slotLastViewClosed())); + ToolbarTabWidget::ref()->reparent(0L, 0, QPoint(), false); + + for (childIt = children.begin(); childIt != children.end(); ++childIt) + { + view = dynamic_cast<QuantaView*>(*childIt); + if (view) + { + Document *w = view->document(); + if (w) + { + if (view->mayRemove()) + { + if (!w->isUntitled() && w->url().isLocalFile()) + { + fileWatcher->removeFile(w->url().path()); +// kdDebug(24000) << "removeFile[closeAll]: " << w->url().path() << endl; + } + quantaApp->guiFactory()->removeClient(w->view()); + if (view == m_lastActiveEditorView) + m_lastActiveEditorView = 0L; + if (view == m_lastActiveView) + m_lastActiveView = 0L; + quantaApp->closeWindow(view); + } else + { + //actually this code should be never executed + connect(quantaApp, SIGNAL(viewActivated (KMdiChildView *)), this, SLOT(slotViewActivated(KMdiChildView*))); + connect(quantaApp, SIGNAL(lastChildViewClosed()), this, SLOT(slotLastViewClosed())); + view->activated(); + emit filesClosed(false); + return false; + } + } else + { + if (view == m_documentationView) + m_documentationView = 0L; + view->mayRemove(); //unloads the plugin and reparents the custom widget + quantaApp->closeWindow(view); + } + } + } + connect(quantaApp, SIGNAL(viewActivated (KMdiChildView *)), this, SLOT(slotViewActivated(KMdiChildView*))); + connect(quantaApp, SIGNAL(lastChildViewClosed()), this, SLOT(slotLastViewClosed())); + if (createNew) + { + createNewDocument(); + quantaApp->slotNewStatus(); + } + emit filesClosed(true); + return true; +} + +bool ViewManager::isOneModified() +{ + KMdiIterator<KMdiChildView*> *it = quantaApp->createIterator(); + QuantaView *view; + for (it->first(); !it->isDone(); it->next()) + { + view = dynamic_cast<QuantaView*>(it->currentItem()); + if (view) + { + Document *w = view->document(); + if (w && w->isModified()) + { + delete it; + return true; + } + } + } + delete it; + + return false; +} + +QuantaView* ViewManager::documentationView(bool create) +{ + if (!m_documentationView && create) + { + m_documentationView = createView(); + m_documentationView->addCustomWidget((QWidget*)quantaApp->documentationPart()->view(), i18n("Documentation")); + } + return m_documentationView; +} + +void ViewManager::slotLastViewClosed() +{ + quantaApp->slotFileNew(); +} + +bool ViewManager::allEditorsClosed() +{ + KMdiIterator<KMdiChildView*> *it = quantaApp->createIterator(); + QuantaView *view; + for (it->first(); !it->isDone(); it->next()) + { + view = dynamic_cast<QuantaView*>(it->currentItem()); + if (view && view->document()) + { + delete it; + return false; + } + } + delete it; + return true; +} + +void ViewManager::slotTabContextMenu(QWidget *widget, const QPoint& point) +{ + if (m_separatorVisible) + m_tabPopup->removeItemAt(SEPARATOR_INDEX); + m_separatorVisible = false; + m_contextView = dynamic_cast<QuantaView*>(widget); + Document *w = 0L; + if (m_contextView) + w = m_contextView->document(); + if (w) + { + if (!w->isUntitled()) + { + m_separatorVisible = true; + m_tabPopup->insertSeparator(SEPARATOR_INDEX); + } + if (Project::ref()->hasProject() && Project::ref()->contains(w->url())) + m_tabPopup->setItemVisible(UPLOAD_ID, true); + else + m_tabPopup->setItemVisible(UPLOAD_ID, false); + if (w->isUntitled()) + { + m_tabPopup->setItemVisible(RELOAD_ID, false); + m_tabPopup->setItemVisible(DELETE_ID, false); + } else + { + m_tabPopup->setItemVisible(RELOAD_ID, true); + m_tabPopup->setItemVisible(DELETE_ID, true); + } + } else + { + m_tabPopup->setItemVisible(RELOAD_ID, false); + m_tabPopup->setItemVisible(UPLOAD_ID, false); + m_tabPopup->setItemVisible(DELETE_ID, false); + } + bool bookmarksFound = false; + if (w && w->markIf) + { + m_bookmarks->setDocument(w); + QPtrList<KTextEditor::Mark> m = w->markIf->marks(); + QPtrListIterator<KTextEditor::Mark> it(m); + for(; *it; ++it) + { + if ((*it)->type & KTextEditor::MarkInterface::markType01) + { + bookmarksFound = true; + break; + } + } + } + m_tabPopup->setItemVisible(m_bookmarksMenuId, bookmarksFound); +#ifdef ENABLE_CVSSERVICE + if (w && w->url().isLocalFile() && !w->isUntitled() && CVSService::ref()->exists()) + { + if (m_cvsMenuId == -1) + { + m_tabPopup->insertSeparator(); + m_cvsMenuId = m_tabPopup->insertItem(SmallIcon("cervisia"), i18n("CVS"), CVSService::ref()->menu()); + } + if (Project::ref()->contains(w->url())) + CVSService::ref()->setRepository(Project::ref()->projectBaseURL().path()); + else + CVSService::ref()->setRepository(w->url().directory()); + CVSService::ref()->setCurrentFile(w->url().path()); + } else + if (m_cvsMenuId != -1) + { + int idx = m_tabPopup->indexOf(m_cvsMenuId); + m_tabPopup->removeItemAt(idx-1); + m_tabPopup->removeItem(m_cvsMenuId); + m_cvsMenuId = -1; + } +#endif + m_tabPopup->exec(point); +} + +void ViewManager::slotFileListPopupAboutToShow() +{ + m_fileListPopup->clear(); + QStringList viewList; + KMdiIterator<KMdiChildView*> *it = quantaApp->createIterator(); + QuantaView *view; + int id = 0; + for (it->first(); !it->isDone(); it->next()) + { + view = dynamic_cast<QuantaView*>(it->currentItem()); + if (view) + { + m_fileListPopup->insertItem(view->tabName(), id); + id++; + } + } + delete it; +} + +void ViewManager::slotFileListPopupItemActivated(int id) +{ + KMdiIterator<KMdiChildView*> *it = quantaApp->createIterator(); + QuantaView *view; + int id2 = 0; + for (it->first(); !it->isDone(); it->next()) + { + view = dynamic_cast<QuantaView*>(it->currentItem()); + if (view && id == id2) + { + view->activate(); + view->activated(); + break; + } + id2++; + } + delete it; +} + +void ViewManager::slotReloadFile() +{ + quantaApp->slotFileReload(m_contextView); +} + +void ViewManager::slotUploadFile() +{ + quantaApp->slotUploadFile(m_contextView); +} + +void ViewManager::slotDeleteFile() +{ + quantaApp->slotDeleteFile(m_contextView); +} + +void ViewManager::slotCloseView() +{ + removeView(m_contextView); +} + +void ViewManager::slotCloseRequest(KMdiChildView *widget) +{ + QuantaView *view = dynamic_cast<QuantaView *>(widget); + if (view) + removeView(view); +} + + +void ViewManager::slotDocumentClosed(const KURL& url) +{ + emit documentClosed(url); +} + +/** Return the URL of the currently active document */ +QString ViewManager::currentURL() +{ + Document *w = activeDocument(); + if (w) + { + return w->url().url(); + } else + { + QuantaView * view = lastActiveEditorView(); + if (view) + return view->document()->url().url(); + else + return ""; + } +} + + +#include "viewmanager.moc" diff --git a/quanta/src/viewmanager.h b/quanta/src/viewmanager.h new file mode 100644 index 00000000..e7a4fb31 --- /dev/null +++ b/quanta/src/viewmanager.h @@ -0,0 +1,144 @@ +/*************************************************************************** + viewmanager - description + begin : Fri Mar 26 2004 + copyright : (C) 2004 by Andras Mantia <[email protected]> + ***************************************************************************/ + +/*************************************************************************** + * + * 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 VIEWMANAGER_H +#define VIEWMANAGER_H + +#include <kurl.h> + +//forward declarations +class Document; +class QuantaView; +class QuantaBookmarks; +class KafkaDocument; +class KPopupMenu; + +class KMdiChildView; + +/** This singleton class takes care of creating new views for documents, help, plugins, preview and so. +* As the name says, it also manages the views. */ + +class ViewManager : public QObject +{ + Q_OBJECT +public: + /** Returns a reference to the viewmanager object */ + static ViewManager* const ref(QObject *parent = 0L, const char *name = 0L) + { + static ViewManager *m_ref; + if (!m_ref) m_ref = new ViewManager(parent, name); + return m_ref; + } + /** The destructor. */ + virtual ~ViewManager(){}; + + /** Creates a QuantaView object */ + QuantaView *createView(const QString &caption = QString::null); + /** Removes a QuantaView object. Returns false on failure (eg. the view was not saved and it refused + the delete itself.) If force is true, the view is removed without asking for save. + */ + bool removeView(QuantaView *view, bool force = false, bool createNew = true); + /** Returns the active view */ + QuantaView *activeView(); + /** Returns the active document or 0L */ + Document *activeDocument(); + /** Returns the view holding the document loaded from url. */ + QuantaView *isOpened(const KURL &url); + + bool saveAll(); + + /** Returns true if at least one view has the modified flag set. */ + bool isOneModified(); + + void createNewDocument(); +/** Returns a list with the URLs of the opened documents */ + KURL::List openedFiles(bool noUntitled=true); + /** Returns a list with the Document* object of the opened documents */ + QValueList<Document*> openedDocuments(); + + /** Returns the view holding the documentation widget. If create is true and there is no such view yet, + it creates one. */ + QuantaView *documentationView(bool create = true); + + /** Returns the last active view which had an editor inside or 0 if there was no such view */ + QuantaView *lastActiveEditorView() {return m_lastActiveEditorView;} + + /** Return the URL of the currently active document */ + QString currentURL(); + +public slots: + /**called when a new view was activated */ + void slotViewActivated(KMdiChildView *view); + + /** Removes the active view Returns false on failure (eg. the view was not saved and it refused the delete itself.) */ + bool removeActiveView(bool createNew = true) { return removeView(activeView(), false, createNew); } + /** closes all the other but active tabs */ + void slotCloseOtherTabs(); + /** closes all views. If createNew is true, it creates a new view after closing the others. Return true if all documents were closed.*/ + bool closeAll(bool createNew = true); + /** called when the last view is closed */ + void slotLastViewClosed(); + + /** called when the context menu was invoked on a tab */ + void slotTabContextMenu(QWidget *widget, const QPoint & point); + /** called when the user requests to close a tab with the close button */ + void slotCloseRequest(KMdiChildView *widget); + /** called from the views and just emits the signal @ref documentClosed */ + void slotDocumentClosed(const KURL&); + +signals: + /** emitted when a file from the template view is dropped on a view */ + void dragInsert(QDropEvent *); + /** emitted when a view was activated */ + void viewActivated(const KURL &); + /** emitted when a view was closed */ + void documentClosed(const KURL&); + void eventHappened(const QString&, const QString&, const QString& ); + /** emitted when all files were closed. The argument is true if the closes + was successful, false if the unser canceled the closing */ + void filesClosed(bool); + +private slots: + /** called before the file list menu shows up, so it can be updated */ + void slotFileListPopupAboutToShow(); + /** called when an item is selected in the file list menu */ + void slotFileListPopupItemActivated(int id); + + /** Handle tab context menus for editor views */ + void slotReloadFile(); + void slotUploadFile(); + void slotDeleteFile(); + void slotCloseView(); + +private: + /** Private constructor for the singleton object. */ + ViewManager(QObject * parent = 0, const char * name = 0); + /** Returns true if there isn't any opened view holding an editor */ + bool allEditorsClosed(); + + QuantaView *m_lastActiveView; ///< Holds the last active view. Used to deactivate it when a new view is selected + QuantaView *m_lastActiveEditorView; ///< Contains the last active view which has an editor inside + QuantaView *m_documentationView; ///< Contains the view which holds the documentation browser + KPopupMenu *m_tabPopup; ///< the menu which pops up when the user clicks on a view tab + KPopupMenu *m_fileListPopup; ///< a menu containing the opened views as menu items + KPopupMenu *m_bookmarksMenu; + QuantaBookmarks *m_bookmarks; + + QuantaView *m_contextView; ///<the tab where the context menu was requested + bool m_separatorVisible; + int m_cvsMenuId; + int m_bookmarksMenuId; +}; + +#endif diff --git a/quanta/src/x-webprj.desktop b/quanta/src/x-webprj.desktop new file mode 100644 index 00000000..510b4778 --- /dev/null +++ b/quanta/src/x-webprj.desktop @@ -0,0 +1,51 @@ +[Desktop Entry] +Comment=Quanta Project +Comment[bg]=Проект на Quanta +Comment[br]=Raktres Quanta +Comment[ca]=Projecte Quanta +Comment[cs]=Quanta projekt +Comment[da]=Quanta-projekt +Comment[de]=Quanta Projekt +Comment[el]=Έργο Quanta +Comment[es]=Proyecto Quanta +Comment[et]=Quanta projekt +Comment[eu]=Quanta proiektua +Comment[fa]=پروژۀ Quanta +Comment[fi]=Quanta projekti +Comment[fr]=Projet Quanta +Comment[ga]=Tionscadal Quanta +Comment[hu]=Quanta-projekt +Comment[is]=Quanta verkefnið +Comment[it]=Progetto Quanta +Comment[ja]=Quanta プロジェクト +Comment[ka]=Quanta პროექტი +Comment[lt]=Quanta projektas +Comment[ms]=Projek Quanta +Comment[nds]=Quanta-Projekt +Comment[ne]=क्वान्टा परियोजना +Comment[nl]=Quanta project +Comment[pl]=Projekt Quanta +Comment[pt]=Projecto Quanta +Comment[pt_BR]=Projeto do Quanta +Comment[ro]=Proiect Quanta +Comment[ru]=Проект Quanta +Comment[sk]=Quanta projekt +Comment[sl]=Projekt Quante +Comment[sr]=Quanta пројекат +Comment[sr@Latn]=Quanta projekat +Comment[sv]=Quanta-projekt +Comment[ta]=குவாண்டா திட்டப்பணி +Comment[tg]=Нақшаи Quanta +Comment[tr]=Quanta Projesi +Comment[uk]=Проект Quanta +Comment[zh_CN]=Quanta 工程 +Comment[zh_HK]=Quanta 專案 +Comment[zh_TW]=Quanta 專案 +Icon=quanta +Type=MimeType +MimeType=application/x-webprj +Patterns=*.webprj + +[Property::X-KDE-text] +Type=bool +Value=true |