diff options
Diffstat (limited to 'konq-plugins/rellinks')
-rw-r--r-- | konq-plugins/rellinks/AUTHORS | 3 | ||||
-rw-r--r-- | konq-plugins/rellinks/ChangeLog | 31 | ||||
-rw-r--r-- | konq-plugins/rellinks/Makefile.am | 12 | ||||
-rw-r--r-- | konq-plugins/rellinks/README | 13 | ||||
-rw-r--r-- | konq-plugins/rellinks/TODO | 20 | ||||
-rw-r--r-- | konq-plugins/rellinks/plugin_rellinks.cpp | 618 | ||||
-rw-r--r-- | konq-plugins/rellinks/plugin_rellinks.desktop | 120 | ||||
-rw-r--r-- | konq-plugins/rellinks/plugin_rellinks.h | 170 | ||||
-rw-r--r-- | konq-plugins/rellinks/plugin_rellinks.rc | 38 | ||||
-rw-r--r-- | konq-plugins/rellinks/rellinks.kdevelop | 157 | ||||
-rw-r--r-- | konq-plugins/rellinks/rellinks.lsm | 16 |
11 files changed, 1198 insertions, 0 deletions
diff --git a/konq-plugins/rellinks/AUTHORS b/konq-plugins/rellinks/AUTHORS new file mode 100644 index 0000000..0e65688 --- /dev/null +++ b/konq-plugins/rellinks/AUTHORS @@ -0,0 +1,3 @@ +Kevin Krammer <kevin.krammer@gmx.at> (author of sitelinks, merged project on 2004-01-28) +Franck Qulain <shift@free.fr> (konq-rellinks) +Anders Lund <anders@alweb.dk> (First version : rellinks) diff --git a/konq-plugins/rellinks/ChangeLog b/konq-plugins/rellinks/ChangeLog new file mode 100644 index 0000000..bc62fdf --- /dev/null +++ b/konq-plugins/rellinks/ChangeLog @@ -0,0 +1,31 @@ +Mon Jan 21 00:31:29 CET 2002 - Anders Lund <anders@alweb.dk> + o Initial Creation (of the rellinks plugin) +Sat May 17 14:46:33 CET 2003 - Franck Qulain <shift@free.fr> + o Rename it konqSiteNavBar for better understanding + o The plugin is now a full toolbar + o Add navigation buttons (home, up, prev, next,...) + o "Document" and "More" menus la Mozilla site navigation bar + o Toolip and contextuel help on most of the items + o Menu for Unclassified links +Mon Jun 9 23:10:00 CET 2003 - Franck Qulain <shift@free.fr> + o Configuration Menu + o "When necessary" mode : show / hide toolbar only when necessary + o Configuration stored in .kde/share/config/konq_rellinksrc +Wed Jun 18 22:05:00 CET 2003 - Franck Qulain <shift@free.fr> + o The name has changed to konq-rellinks after an interesting + discussion with Anders Lund. +Fri Jan 30 16:23 CET 2004 - Kevin Krammer <kevin.krammer@gmx.at> + o Bugfix: enabling "more" menu instead of "document" when links + are "help", "made" or "copyright" + o Made "search" a top level action + o Polling for links while the page is loading. This presents + clickable actions as soon as possible, nice on slow connections or + in large documents. +Sun Mar 21 16:23 CET 2004 - Franck Qulain <shift@free.fr> + o Support for "target" attribute + o Distinction between "rel" and "rev" relations +Tue Jun 22 23:05 CET 2004 - Kevin Krammer <kevin.krammer@gmx.at> + o Move toolbar creation from XML GUI to code as a workaround + because KHTML loads the plugins before it initializes its + d pointer and so there is no way to get to either the view or + the toolbar in the plugin's contructor. diff --git a/konq-plugins/rellinks/Makefile.am b/konq-plugins/rellinks/Makefile.am new file mode 100644 index 0000000..aeec3fc --- /dev/null +++ b/konq-plugins/rellinks/Makefile.am @@ -0,0 +1,12 @@ +INCLUDES = $(all_includes) +METASOURCES = AUTO +kde_module_LTLIBRARIES = librellinksplugin.la +librellinksplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +librellinksplugin_la_LIBADD = $(LIB_KPARTS) $(LIB_KHTML) +librellinksplugin_la_SOURCES = plugin_rellinks.cpp +plugins_DATA = plugin_rellinks.rc plugin_rellinks.desktop +pluginsdir = $(kde_datadir)/khtml/kpartplugins + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/rellinks.pot + diff --git a/konq-plugins/rellinks/README b/konq-plugins/rellinks/README new file mode 100644 index 0000000..e5f3324 --- /dev/null +++ b/konq-plugins/rellinks/README @@ -0,0 +1,13 @@ +What is Konq Rellinks ? +======================= + +Konq Rellinks is an add-on for Konqueror, the official KDE browser. + +It allows you to access the relations defined in the header of the document. These relations can be links to other versions of the document, RSS backend, search page, copyright informations,.... For more informations about this, see "The 'link'-Element in (X)HTML" at http://www.subotnik.net/html/link. + +The look of the Rellinks add-on is similar to the "Site Navigation Toolbar" of the Mozilla browser (not Mozilla Firebird). + +More Info +========= + +See http://shift.freezope.org/konq_rellinks
\ No newline at end of file diff --git a/konq-plugins/rellinks/TODO b/konq-plugins/rellinks/TODO new file mode 100644 index 0000000..28f206b --- /dev/null +++ b/konq-plugins/rellinks/TODO @@ -0,0 +1,20 @@ +Rellinks : +========= + +* Support multiple same relations for all relations in a beautiful way + (as FireFox Link Toolbar Extension do) : + by transfoming buttons (KAction) in menus (KActionMenu) if more than one item is found for a relation ? +* Possibility to configure keyboard shortcuts +* Manage "rel" and "rev" differently +* Manage "media" +* Respect the specifications : A LinkTypes value refers to a space-separated list of link types. White space characters are not permitted within link types. +* Send the referer when going to a link +* Support target attribut ? (Cf. Mozilla link toolbar bug : http://mozdev.org/bugs/show_bug.cgi?id=5259) + + +Rellinks website (http://shift.freezope.org/konq_rellinks) : +============================================================ + +* Add more testcasess +* Add more documentation +* Compare implementation with other browsers
\ No newline at end of file diff --git a/konq-plugins/rellinks/plugin_rellinks.cpp b/konq-plugins/rellinks/plugin_rellinks.cpp new file mode 100644 index 0000000..212c37f --- /dev/null +++ b/konq-plugins/rellinks/plugin_rellinks.cpp @@ -0,0 +1,618 @@ +/*************************************************************************** + * Copyright (C) 2002, Anders Lund <anders@alweb.dk> * + * Copyright (C) 2003, 2004, Franck Qulain <shift@free.fr> * + * Copyright (C) 2004, Kevin Krammer <kevin.krammer@gmx.at> * + * Copyright (C) 2004, 2006, Oliviet Goffart <ogoffart @ kde.org> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + + + + +// Qt includes +#include <qapplication.h> +#include <qtimer.h> + +// KDE include +#include <dom/dom_doc.h> +#include <dom/dom_element.h> +#include <dom/dom_string.h> +#include <dom/html_document.h> +#include <kaction.h> +#include <kdebug.h> +#include <kgenericfactory.h> +#include <khtml_part.h> +#include <khtmlview.h> +#include <kiconloader.h> +#include <kinstance.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kshortcut.h> +#include <ktoolbar.h> +#include <kurl.h> + +// local includes +#include "plugin_rellinks.h" + + +/** Rellinks factory */ +typedef KGenericFactory<RelLinksPlugin> RelLinksFactory; +#include <kdeversion.h> +#if KDE_IS_VERSION(3,2,90) +#include <kaboutdata.h> +static const KAboutData aboutdata("rellinks", I18N_NOOP("Rellinks") , "1.0" ); +K_EXPORT_COMPONENT_FACTORY( librellinksplugin, RelLinksFactory(&aboutdata) ) +#else +K_EXPORT_COMPONENT_FACTORY( librellinksplugin, RelLinksFactory("rellinks") ) +#endif + +/** Constructor of the plugin. */ +RelLinksPlugin::RelLinksPlugin(QObject *parent, const char *name, const QStringList &) + : KParts::Plugin( parent, name ), + m_part(0), + m_viewVisible(false) +{ + + setInstance(RelLinksFactory::instance()); + + // ------------- Navigation links -------------- + kaction_map["home"] = new KAction( i18n("&Top"), "2uparrow", KShortcut("Ctrl+Alt+T"), this, SLOT(goHome()), actionCollection(), "rellinks_top" ); + kaction_map["home"]->setWhatsThis( i18n("<p>This link references a home page or the top of some hierarchy.</p>") ); + + kaction_map["up"] = new KAction( i18n("&Up"), "1uparrow", KShortcut("Ctrl+Alt+U"), this, SLOT(goUp()), actionCollection(), "rellinks_up" ); + kaction_map["up"]->setWhatsThis( i18n("<p>This link references the immediate parent of the current document.</p>") ); + + bool isRTL = QApplication::reverseLayout(); + + kaction_map["begin"] = new KAction( i18n("&First"), isRTL ? "2rightarrow" : "2leftarrow", KShortcut("Ctrl+Alt+F"), this, SLOT(goFirst()), actionCollection(), "rellinks_first" ); + kaction_map["begin"]->setWhatsThis( i18n("<p>This link type tells search engines which document is considered by the author to be the starting point of the collection.</p>") ); + + kaction_map["prev"] = new KAction( i18n("&Previous"), isRTL ? "1rightarrow" : "1leftarrow", KShortcut("Ctrl+Alt+P"), this, SLOT(goPrevious()), actionCollection(), "rellinks_previous" ); + kaction_map["prev"]->setWhatsThis( i18n("<p>This link references the previous document in an ordered series of documents.</p>") ); + + kaction_map["next"] = new KAction( i18n("&Next"), isRTL ? "1leftarrow" : "1rightarrow", KShortcut("Ctrl+Alt+N"), this, SLOT(goNext()), actionCollection(), "rellinks_next" ); + kaction_map["next"]->setWhatsThis( i18n("<p>This link references the next document in an ordered series of documents.</p>") ); + + kaction_map["last"] = new KAction( i18n("&Last"), isRTL ? "2leftarrow" : "2rightarrow", KShortcut("Ctrl+Alt+L"), this, SLOT(goLast()), actionCollection(), "rellinks_last" ); + kaction_map["last"]->setWhatsThis( i18n("<p>This link references the end of a sequence of documents.</p>") ); + + // ------------ special items -------------------------- + kaction_map["search"] = new KAction( i18n("&Search"), "filefind", KShortcut("Ctrl+Alt+S"), this, SLOT(goSearch()), actionCollection(), "rellinks_search" ); + kaction_map["search"]->setWhatsThis( i18n("<p>This link references the search.</p>") ); + + // ------------ Document structure links --------------- + m_document = new KActionMenu( i18n("Document"), "contents", actionCollection(), "rellinks_document" ); + m_document->setWhatsThis( i18n("<p>This menu contains the links referring the document information.</p>") ); + m_document->setDelayed(false); + + kaction_map["contents"] = new KAction( i18n("Table of &Contents"), "contents", KShortcut("Ctrl+Alt+C"), this, SLOT(goContents()), actionCollection(), "rellinks_toc" ); + m_document->insert(kaction_map["contents"]); + kaction_map["contents"]->setWhatsThis( i18n("<p>This link references the table of contents.</p>") ); + + kactionmenu_map["chapter"] = new KActionMenu( i18n("Chapters"), "fileopen", actionCollection(), "rellinks_chapters" ); + m_document->insert(kactionmenu_map["chapter"]); + connect( kactionmenu_map["chapter"]->popupMenu(), SIGNAL( activated( int ) ), this, SLOT(goChapter(int))); + kactionmenu_map["chapter"]->setWhatsThis( i18n("<p>This menu references the chapters of the document.</p>") ); + kactionmenu_map["chapter"]->setDelayed(false); + + kactionmenu_map["section"] = new KActionMenu( i18n("Sections"), "fileopen", actionCollection(), "rellinks_sections" ); + m_document->insert(kactionmenu_map["section"]); + connect( kactionmenu_map["section"]->popupMenu(), SIGNAL( activated( int ) ), this, SLOT( goSection( int ) ) ); + kactionmenu_map["section"]->setWhatsThis( i18n("<p>This menu references the sections of the document.</p>") ); + kactionmenu_map["section"]->setDelayed(false); + + kactionmenu_map["subsection"] = new KActionMenu( i18n("Subsections"), "fileopen", actionCollection(), "rellinks_subsections" ); + m_document->insert(kactionmenu_map["subsection"]); + connect( kactionmenu_map["subsection"]->popupMenu(), SIGNAL( activated( int ) ), this, SLOT( goSubsection( int ) ) ); + kactionmenu_map["subsection"]->setWhatsThis( i18n("<p>This menu references the subsections of the document.</p>") ); + kactionmenu_map["subsection"]->setDelayed(false); + + kactionmenu_map["appendix"] = new KActionMenu( i18n("Appendix"), "edit", actionCollection(), "rellinks_appendix" ); + m_document->insert(kactionmenu_map["appendix"]); + connect( kactionmenu_map["appendix"]->popupMenu(), SIGNAL( activated( int ) ), this, SLOT( goAppendix( int ) ) ); + kactionmenu_map["appendix"]->setWhatsThis( i18n("<p>This link references the appendix.</p>") ); + kactionmenu_map["appendix"]->setDelayed(false); + + kaction_map["glossary"] = new KAction( i18n("&Glossary"), "flag", KShortcut("Ctrl+Alt+G"), this, SLOT(goGlossary()), actionCollection(), "rellinks_glossary" ); + m_document->insert(kaction_map["glossary"]); + kaction_map["glossary"]->setWhatsThis( i18n("<p>This link references the glossary.</p>") ); + + kaction_map["index"] = new KAction( i18n("&Index"), "info", KShortcut("Ctrl+Alt+I"), this, SLOT(goIndex()), actionCollection(), "rellinks_index" ); + m_document->insert(kaction_map["index"]); + kaction_map["index"]->setWhatsThis( i18n("<p>This link references the index.</p>") ); + + // Other links + m_more = new KActionMenu( i18n("More"), "misc", actionCollection(), "rellinks_more" ); + m_more->setWhatsThis( i18n("<p>This menu contains other important links.</p>") ); + m_more->setDelayed(false); + + kaction_map["help"] = new KAction( i18n("&Help"), "help", KShortcut("Ctrl+Alt+H"), this, SLOT(goHelp()), actionCollection(), "rellinks_help" ); + m_more->insert(kaction_map["help"]); + kaction_map["help"]->setWhatsThis( i18n("<p>This link references the help.</p>") ); + + kaction_map["author"] = new KAction( i18n("&Authors"), "mail_new", KShortcut("Ctrl+Alt+A"), this, SLOT(goAuthor()), actionCollection(), "rellinks_authors" ); + m_more->insert(kaction_map["author"]); + kaction_map["author"]->setWhatsThis( i18n("<p>This link references the author.</p>") ); + + kaction_map["copyright"] = new KAction( i18n("Copy&right"), "signature", KShortcut("Ctrl+Alt+R"), this, SLOT(goCopyright()), actionCollection(), "rellinks_copyright" ); + m_more->insert(kaction_map["copyright"]); + kaction_map["copyright"]->setWhatsThis( i18n("<p>This link references the copyright.</p>") ); + + kactionmenu_map["bookmark"] = new KActionMenu( i18n("Bookmarks"), "bookmark_folder", actionCollection(), "rellinks_bookmarks" ); + m_more->insert(kactionmenu_map["bookmark"]); + kactionmenu_map["bookmark"]->setWhatsThis( i18n("<p>This menu references the bookmarks.</p>") ); + connect( kactionmenu_map["bookmark"]->popupMenu(), SIGNAL( activated( int ) ), this, SLOT( goBookmark( int ) ) ); + kactionmenu_map["bookmark"]->setDelayed(false); + + kactionmenu_map["alternate"] = new KActionMenu( i18n("Other Versions"), "attach", actionCollection(), "rellinks_other_versions" ); + m_more->insert(kactionmenu_map["alternate"]); + kactionmenu_map["alternate"]->setWhatsThis( i18n("<p>This link references the alternate versions of this document.</p>") ); + connect( kactionmenu_map["alternate"]->popupMenu(), SIGNAL( activated( int ) ), this, SLOT( goAlternate( int ) ) ); + kactionmenu_map["alternate"]->setDelayed(false); + + // Unclassified menu + m_links = new KActionMenu( i18n("Miscellaneous"), "rellinks", actionCollection(), "rellinks_links" ); + kactionmenu_map["unclassified"] = m_links; + kactionmenu_map["unclassified"]->setWhatsThis( i18n("<p>Miscellaneous links.</p>") ); + connect( kactionmenu_map["unclassified"]->popupMenu(), SIGNAL( activated( int ) ), this, SLOT( goAllElements( int ) ) ); + kactionmenu_map["unclassified"]->setDelayed(false); + + // We unactivate all the possible actions + disableAll(); + + // When the rendering of the HTML is done, we update the site navigation bar + m_part = dynamic_cast<KHTMLPart *>(parent); + if (!m_part) + return; + + connect( m_part, SIGNAL( docCreated() ), this, SLOT( newDocument() ) ); + connect( m_part, SIGNAL( completed() ), this, SLOT( loadingFinished() ) ); + + // create polling timer and connect it + m_pollTimer = new QTimer(this, "polling timer"); + connect( m_pollTimer, SIGNAL( timeout() ), this, SLOT( updateToolbar() ) ); + + // delay access to our part's members until it has finished its initialisation + QTimer::singleShot(0, this, SLOT(delayedSetup())); +} + +/** Destructor */ +RelLinksPlugin::~RelLinksPlugin() { +} + +bool RelLinksPlugin::eventFilter(QObject *watched, QEvent* event) { + if (m_part == 0) return false; + + if (watched == 0 || event == 0) return false; + + if (watched == m_view) + { + switch (event->type()) + { + case QEvent::Show: + m_viewVisible = true; + updateToolbar(); + break; + + case QEvent::Hide: + m_viewVisible = false; + updateToolbar(); + break; + + case QEvent::Close: + m_pollTimer->stop(); + m_view->removeEventFilter(this); + break; + + default: + break; + } + } + + // we never filter an event, we just want to know about it + return false; +} + +void RelLinksPlugin::delayedSetup() +{ + if (m_part == 0) return; + + m_view = m_part->view(); + m_view->installEventFilter(this); + m_viewVisible = m_view->isVisible(); +} + +void RelLinksPlugin::newDocument() { + // start calling upateToolbar periodically to get the new links as soon as possible + + m_pollTimer->start(500); + //kdDebug(90210) << "newDocument()" << endl; + + updateToolbar(); +} + +void RelLinksPlugin::loadingFinished() { + m_pollTimer->stop(); + //kdDebug(90210) << "loadingFinished()" << endl; + updateToolbar(); + guessRelations(); +} + +/* Update the site navigation bar */ +void RelLinksPlugin::updateToolbar() { + + // If we have a part + if (!m_part) + return; + + // We disable all + disableAll(); + + // get a list of LINK nodes in document + DOM::NodeList linkNodes = m_part->document().getElementsByTagName( "link" ); + + //kdDebug(90210) << "Rellinks: Link nodes =" << linkNodes.length() << endl; + + bool showBar = false; + unsigned long nodeLength = linkNodes.length(); + + for ( unsigned int i=0; i < nodeLength; i++ ) { + // create a entry for each one + DOM::Element e( linkNodes.item( i ) ); + + + // --- Retrieve of the relation type -- + + QString rel = e.getAttribute( "rel" ).string(); + rel = rel.simplifyWhiteSpace(); + if (rel.isEmpty()) { + // If the "rel" attribut is null then use the "rev" attribute... + QString rev = e.getAttribute( "rev" ).string(); + rev = rev.simplifyWhiteSpace(); + if (rev.isEmpty()) { + // if "rev" attribut is also empty => ignore + continue; + } + // Determine the "rel" equivalent of "rev" type + rel = transformRevToRel(rev); + } + // Determin the name used internally + QString lrel = getLinkType(rel.lower()); + // relation to ignore + if (lrel.isEmpty()) continue; +// kdDebug() << "lrel=" << lrel << endl; + + // -- Retrieve of other usefull informations -- + + QString href = e.getAttribute( "href" ).string(); + // if nowhere to go, ignore the link + if (href.isEmpty()) continue; + QString title = e.getAttribute( "title" ).string(); + QString hreflang = e.getAttribute( "hreflang" ).string(); + + KURL ref( m_part->url(), href ); + if ( title.isEmpty() ) + title = ref.prettyURL(); + + // escape ampersand before settings as action title, otherwise the menu entry will interpret it as an + // accelerator + title.replace('&', "&&"); + + // -- Menus activation -- + + // Activation of "Document" menu ? + if (lrel == "contents" || lrel == "glossary" || lrel == "index" || lrel == "appendix") { + m_document->setEnabled(true); + } + // Activation of "More" menu ? + if (lrel == "help" || lrel == "author" || lrel == "copyright" ) { + m_more->setEnabled(true); + } + + // -- Buttons or menu items activation / creation -- + if (lrel == "bookmark" || lrel == "alternate") { + int id = kactionmenu_map[lrel]->popupMenu()->insertItem( title ); + m_more->setEnabled(true); + kactionmenu_map[lrel]->setEnabled(true); + element_map[lrel][id] = e; + + } else if (lrel == "appendix" || lrel == "chapter" || lrel == "section" || lrel == "subsection") { + int id = kactionmenu_map[lrel]->popupMenu()->insertItem( title ); + m_document->setEnabled(true); + kactionmenu_map[lrel]->setEnabled(true); + element_map[lrel][id] = e; + + } else { + // It is a unique action + element_map[lrel][0] = e; + if (kaction_map[lrel]) { + kaction_map[lrel]->setEnabled(true); + // Tooltip + if (hreflang.isEmpty()) { + kaction_map[lrel]->setToolTip( title ); + } else { + kaction_map[lrel]->setToolTip( title + " [" + hreflang + "]"); + } + } else { + // For the moment all the elements are reference in a separated menu + // TODO : reference the unknown ? + int id = kactionmenu_map["unclassified"]->popupMenu()->insertItem( lrel + " : " + title ); + kactionmenu_map["unclassified"]->setEnabled(true); + element_map["unclassified"][id] = e; + } + + } + + showBar = true; + } +} + + +void RelLinksPlugin::guessRelations() +{ + m_part = dynamic_cast<KHTMLPart *>(parent()); + if (!m_part || m_part->document().isNull() ) + return; + + //If the page already contains some link, that mean the webmaster is aware + //of the meaning of <link> so we can consider that if prev/next was possible + //they are already there. + if(!element_map.isEmpty()) + return; + + // - The number of didgit may not be more of 3, or this is certenly an id. + // - We make sure that the number is followed by a dot, a &, or the end, we + // don't want to match stuff like that: page.html?id=A14E12FD + // - We make also sure the number is not preceded dirrectly by others number + QRegExp rx("^(.*[=/?&][^=/?&.\\-0-9]*)([\\d]{1,3})([.&][^/0-9]{0,15})?$"); + + + const QString zeros("0000"); + QString url=m_part->url().url(); + if(rx.search(url)!=-1) + { + uint val=rx.cap(2).toUInt(); + uint lenval=rx.cap(2).length(); + QString nval_str=QString::number(val+1); + //prepend by zeros if the original also contains zeros. + if(nval_str.length() < lenval && rx.cap(2)[0]=='0') + nval_str.prepend(zeros.left(lenval-nval_str.length())); + + QString href=rx.cap(1)+ nval_str + rx.cap(3); + KURL ref( m_part->url(), href ); + QString title = i18n("[Autodetected] %1").arg(ref.prettyURL()); + DOM::Element e= m_part->document().createElement("link"); + e.setAttribute("href",href); + element_map["next"][0] = e; + kaction_map["next"]->setEnabled(true); + kaction_map["next"]->setToolTip( title ); + + if(val>1) + { + nval_str=QString::number(val-1); + if(nval_str.length() < lenval && rx.cap(2)[0]=='0') + nval_str.prepend(zeros.left(lenval-nval_str.length())); + QString href=rx.cap(1)+ nval_str + rx.cap(3); + KURL ref( m_part->url(), href ); + QString title = i18n("[Autodetected] %1").arg(ref.prettyURL()); + e= m_part->document().createElement("link"); + e.setAttribute("href",href); + element_map["prev"][0] = e; + kaction_map["prev"]->setEnabled(true); + kaction_map["prev"]->setToolTip( title ); + } + } +} + + +/** Menu links */ +void RelLinksPlugin::goToLink(const QString & rel, int id) { + // have the KHTML part open it + KHTMLPart *part = dynamic_cast<KHTMLPart *>(parent()); + if (!part) + return; + + DOM::Element e = element_map[rel][id]; + QString href = e.getAttribute("href").string(); + KURL url( part->url(), href ); + QString target = e.getAttribute("target").string(); + + // URL arguments + KParts::URLArgs args; + args.frameName = target; + + // Add base url if not valid + if (url.isValid()) { + part->browserExtension()->openURLRequest(url, args); + } else { + KURL baseURL = part->baseURL(); + QString endURL = url.prettyURL(); + KURL realURL = KURL(baseURL, endURL); + part->browserExtension()->openURLRequest(realURL, args); + } + +} + +void RelLinksPlugin::goHome() { + goToLink("home"); +} + +void RelLinksPlugin::goUp() { + goToLink("up"); +} + +void RelLinksPlugin::goFirst() { + goToLink("begin"); +} + +void RelLinksPlugin::goPrevious() { + goToLink("prev"); +} + +void RelLinksPlugin::goNext() { + goToLink("next"); +} + +void RelLinksPlugin::goLast() { + goToLink("last"); +} + +void RelLinksPlugin::goContents() { + goToLink("contents"); +} + +void RelLinksPlugin::goIndex() { + goToLink("index"); +} + +void RelLinksPlugin::goGlossary() { + goToLink("glossary"); +} + +void RelLinksPlugin::goHelp() { + goToLink("help"); +} + +void RelLinksPlugin::goSearch() { + goToLink("search"); +} + +void RelLinksPlugin::goAuthor() { + goToLink("author"); +} + + +void RelLinksPlugin::goCopyright() { + goToLink("copyright"); +} + +void RelLinksPlugin::goBookmark(int id) { + goToLink("bookmark", id); +} + +void RelLinksPlugin::goChapter(int id) { + goToLink("chapter", id); +} + +void RelLinksPlugin::goSection(int id) { + goToLink("section", id); +} + +void RelLinksPlugin::goSubsection(int id) { + goToLink("subsection", id); +} + +void RelLinksPlugin::goAppendix(int id) { + goToLink("appendix", id); +} + +void RelLinksPlugin::goAlternate(int id) { + goToLink("alternate", id); +} + +void RelLinksPlugin::goAllElements(int id) { + goToLink("unclassified", id); +} + +void RelLinksPlugin::disableAll() { + element_map.clear(); + + // Clear actions + KActionMap::Iterator it; + for ( it = kaction_map.begin(); it != kaction_map.end(); ++it ) { + // If I don't test it crash :( + if (it.data()) { + it.data()->setEnabled(false); + it.data()->setToolTip(it.data()->text().remove('&')); + } + } + + // Clear actions + KActionMenuMap::Iterator itmenu; + for ( itmenu = kactionmenu_map.begin(); itmenu != kactionmenu_map.end(); ++itmenu ) { + // If I don't test it crash :( + if (itmenu.data()) { + itmenu.data()->popupMenu()->clear(); + itmenu.data()->setEnabled(false); + itmenu.data()->setToolTip(itmenu.data()->text().remove('&')); + } + } + + // Unactivate menus + m_more->setEnabled(false); + m_document->setEnabled(false); + +} + + +QString RelLinksPlugin::getLinkType(const QString &lrel) { + // Relations to ignore... + if (lrel.contains("stylesheet") + || lrel == "script" + || lrel == "icon" + || lrel == "shortcut icon" + || lrel == "prefetch" ) + return QString::null; + + // ...known relations... + if (lrel == "top" || lrel == "origin" || lrel == "start") + return "home"; + if (lrel == "parent") + return "up"; + if (lrel == "first") + return "begin"; + if (lrel == "previous") + return "prev"; + if (lrel == "child") + return "next"; + if (lrel == "end") + return "last"; + if (lrel == "toc") + return "contents"; + if (lrel == "find") + return "search"; + if (lrel == "alternative stylesheet") + return "alternate stylesheet"; + if (lrel == "authors") + return "author"; + if (lrel == "toc") + return "contents"; + + //...unknown relations or name that don't need to change + return lrel; +} + +QString RelLinksPlugin::transformRevToRel(const QString &rev) { + QString altRev = getLinkType(rev); + + // Known relations + if (altRev == "prev") + return getLinkType("next"); + if (altRev == "next") + return getLinkType("prev"); + if (altRev == "made") + return getLinkType("author"); + if (altRev == "up") + return getLinkType("child"); + if (altRev == "sibling") + return getLinkType("sibling"); + + //...unknown inverse relation => ignore for the moment + return QString::null; +} + +#include "plugin_rellinks.moc" diff --git a/konq-plugins/rellinks/plugin_rellinks.desktop b/konq-plugins/rellinks/plugin_rellinks.desktop new file mode 100644 index 0000000..fba32e5 --- /dev/null +++ b/konq-plugins/rellinks/plugin_rellinks.desktop @@ -0,0 +1,120 @@ +[Desktop Entry] +X-KDE-Library=rellinks +X-KDE-PluginInfo-Author=Anders Lund, Franck Quélain, Kevin Krammer, Olivier Goffart +X-KDE-PluginInfo-Email=anders@alweb.dk, shift@free.fr, kevin.krammer@gmx.at, ogoffart@kde.org +X-KDE-PluginInfo-Name=rellinks +X-KDE-PluginInfo-Version=3.3 +X-KDE-PluginInfo-Website= +X-KDE-PluginInfo-Category=Tools +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=false +## TODO +##Icon=rellinks +Name=Document Relations Plugin +Name[bg]=Приставка за връзки между документите +Name[ca]=Connector de relacions entre documents +Name[cs]=Modul vztahů v dokumentu +Name[da]=Dokmentrelationer-plugin +Name[de]=Dokumentbeziehungs-Modul +Name[el]=Πρόσθετο συσχετίσεων εγγράφου +Name[eo]=Dokumenta rilata kromaĵo +Name[es]=Complemento de relaciones de documentos +Name[et]=Dokumendi seoste plugin +Name[eu]=Dokumentuen harreman plugina +Name[fa]=وصلۀ روابط سند +Name[fi]=Asiakirjojen suhteet sovelma +Name[fr]=Module de relations entre documents +Name[fy]=Dokumint Relaasjes plugin +Name[gl]=Plugin de Relacións de Documento +Name[he]=תוסף יחסי מסמך +Name[hi]=दस्तावेज़ सम्बन्ध प्लग-इन +Name[hr]=Dodatak za relacije dokumenata +Name[hu]=Dokumentumkapcsolatok modul +Name[is]=Skjalatengsla íforrit +Name[it]=Relazioni tra documenti +Name[ja]=ドキュメント関係プラグイン +Name[ka]=დოკუმენტის კავშირთა მოდული +Name[kk]=Құжаттың қатынастар плагин модулі +Name[km]=កម្មវិធីជំនួយទំនាក់ទំនងឯកសារ +Name[lt]=Dokumentų sąsajų priedas +Name[mk]=Приклучок за релации во документ +Name[ms]=Plugin Kehubungan Dokumen +Name[nb]=Programtillegg for dokumentrelasjoner +Name[nds]=Dokmentbetöög-Moduul +Name[ne]=कागजात सम्बन्धी प्लगइन +Name[nl]=Documentrelatiesplugin +Name[nn]=Programtillegg for dokumentrelasjonar +Name[pa]=ਦਸਤਾਵੇਜ਼ ਸਬੰਧ ਪਲੱਗਇਨ +Name[pl]=Wtyczka zależności w dokumencie +Name[pt]='Plugin' de Relações de Documento +Name[pt_BR]=Plug-in de Relações em Documentos +Name[ru]=Модуль связей документов +Name[sk]=Modul súvislosti dokumentov +Name[sl]=Vstavek povezav dokumenta +Name[sr]=Прикључак за односе међу документима +Name[sr@Latn]=Priključak za odnose među dokumentima +Name[sv]=Insticksprogram för dokumentförhållande +Name[ta]=ஆவண சம்பந்தப்பட்ட சொருகுப்பொருள் +Name[tg]=Модули санади робитаҳо +Name[tr]=Dosya İlişkileri Eklentisi +Name[uk]=Втулок зв'язку між документами +Name[uz]=Hujjatlar orasidagi bogʻliqlik plagini +Name[uz@cyrillic]=Ҳужжатлар орасидаги боғлиқлик плагини +Name[vi]=Bổ sung quan hệ tài liệu +Name[zh_CN]=文档关系插件 +Name[zh_TW]=文件關係外掛程式 +Comment=Document Relations plugin +Comment[bg]=Приставка за връзки между документите +Comment[ca]=Connector de relacions entre documents +Comment[cs]=Modul vztahů v dokumentu +Comment[da]=Dokumentrelationer-plugin +Comment[de]=Dokumentbeziehungs-Modul +Comment[el]=Πρόσθετο συσχετίσεων εγγράφου +Comment[eo]=Dokumenta rilata kromaĵo +Comment[es]=Complemento de relaciones de documentos +Comment[et]=Dokumendi seoste plugin +Comment[eu]=Dokumentuen harreman plugina +Comment[fa]=وصلۀ روابط سند +Comment[fi]=Asiakirjojen suhteet sovelma +Comment[fr]=Module de relations entre documents +Comment[fy]=Dokumint Relaasjes plugin +Comment[gl]=Plugin de relacións do documento +Comment[he]=תוסף יחסי מסמך +Comment[hi]=दस्तावेज़ सम्बन्ध प्लग-इन +Comment[hr]=Dodatak za relacije dokumenata +Comment[hu]=Dokumentumkapcsolatok bővítőmodul +Comment[is]=Skjalatengsla íforrit +Comment[it]=Plugin per gestire le relazioni tra documenti +Comment[ja]=ドキュメント関係プラグイン +Comment[ka]=დოკუმენტის კავშირთა მოდული +Comment[kk]=Құжаттың қатынастар плагин модулі +Comment[km]=កម្មវិធីជំនួយទំនាក់ទំនងឯកសារ +Comment[lt]=Dokumentų sąsajų priedas +Comment[mk]=Приклучок за релации во документ +Comment[ms]=Plugin Kehubungan Dokumen +Comment[nb]=Programtillegg for dokumentrelasjoner +Comment[nds]=Dokmentbetöög-Moduul +Comment[ne]=कागजात सम्बन्धी प्लगइन +Comment[nl]=Documentrelatiesplugin +Comment[nn]=Programtillegg for dokumentrelasjonar +Comment[pa]=ਦਸਤਾਵੇਜ਼ ਸਬੰਧ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka zależności w dokumencie +Comment[pt]='Plugin' de relações de documento +Comment[pt_BR]=Plug-in para Relacionamentos entre Documentos +Comment[ru]=Модуль связей документов +Comment[sk]=Modul súvislosti dokumentov +Comment[sl]=Vstavek povezav dokumenta +Comment[sr]=Прикључак за односе међу документима +Comment[sr@Latn]=Priključak za odnose među dokumentima +Comment[sv]=Insticksprogram för dokumentförhållande +Comment[ta]= =ஆவண சம்பந்தப்பட்ட சொருகுப்பொருள் +Comment[tg]=Модули санади робитаҳо +Comment[tr]=Dosya İlişkileri Eklentisi +Comment[uk]=Втулок зв'язку між документами +Comment[uz]=Hujjatlar orasidagi bogʻliqlik plagini +Comment[uz@cyrillic]=Ҳужжатлар орасидаги боғлиқлик плагини +Comment[vi]=Bổ sung quan hệ tài liệu +Comment[zh_CN]=文档关系插件 +Comment[zh_TW]=文件關係外掛程式 +X-KDE-ParentApp=konqueror diff --git a/konq-plugins/rellinks/plugin_rellinks.h b/konq-plugins/rellinks/plugin_rellinks.h new file mode 100644 index 0000000..63260ce --- /dev/null +++ b/konq-plugins/rellinks/plugin_rellinks.h @@ -0,0 +1,170 @@ +/*************************************************************************** + * Copyright (C) 2002, Anders Lund <anders@alweb.dk> * + * Copyright (C) 2003, 2004, Franck Qulain <shift@free.fr> * + * Copyright (C) 2004, Kevin Krammer <kevin.krammer@gmx.at> * + * Copyright (C) 2004, 2005, Oliviet Goffart <ogoffart @ kde.org> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + + +#ifndef _PLUGIN_RELLINKS_H_ +#define _PLUGIN_RELLINKS_H_ + +/* + This plugin queries the current HTML document for LINK elements. + + This plugin create a toolbar similar to the Site Navigation Bar of Mozilla +*/ + +// Qt includes +#include <qmap.h> + +// KDE includes +#include <kparts/plugin.h> +#include <dom/dom_string.h> + +// type definitions +typedef QMap<int,DOM::Element> DOMElementMap; +typedef QMap<QString, KAction*> KActionMap; +typedef QMap<QString, KActionMenu*> KActionMenuMap; + +// forward declarations +class KActionMenu; +class KHTMLPart; +class KHTMLView; +class KToolBar; +class KURL; +class QTimer; + +/** + * KPart plugin adding document relations toolbar to Konqueror + * @author Franck Qulain + * @author Anders Lund + */ +class RelLinksPlugin : public KParts::Plugin { + Q_OBJECT +public: + /** Constructor */ + RelLinksPlugin( QObject *parent, const char *name, const QStringList & ); + /** Destructor */ + virtual ~RelLinksPlugin(); + + bool eventFilter(QObject *watched, QEvent* event); + +private slots: + void delayedSetup(); + + /** + * KHTMLPart has created a new document, disable actions and + * start polling for links + */ + void newDocument(); + + /** + * KHTMLPart has finished loading, stop the poller and + * parse the document a last time. + */ + void loadingFinished(); + + /** + * Update the toolbar (Parse the document again) + */ + void updateToolbar(); + + + void goHome(); + void goUp(); + void goFirst(); + void goPrevious(); + void goNext(); + void goLast(); + void goContents(); + void goIndex(); + void goGlossary(); + void goHelp(); + void goSearch(); + void goCopyright(); + void goAuthor(); + + void goBookmark(int id); + void goChapter(int id); + void goSection(int id); + void goSubsection(int id); + void goAppendix(int id); + void goAlternate(int id); + void goAllElements(int id); + +private: + + /** + * Try to guess some relations from the url, if the document doesn't contains relations + * example: http://example.com/page4.html + * the "next" relation will be set to page5.html + */ + void guessRelations(); + + /** + * Function used to get link type of a relation. + * For example "prev" is of type "previous" and "toc" is of type "contents" + * If the relation must be ignored return NULL. + * If the relation is unknow return the input relation type. + * @param lrel Previous relation name + * @return New relation name + */ + QString getLinkType(const QString &lrel); + + /** + * Function used to return the "rel" equivalent of "rev" link type + * If the equivalent is not found return NULL + * @param rev Inverse relation name + * @return Equivalent relation name + */ + QString transformRevToRel(const QString &rev) ; + + /** + * Function used to disable all the item of the toolbar + */ + void disableAll(); + + /** + * Go to the link + * @param rel Relation name + * @param id Identifier of the menu item + */ + void goToLink(const QString & rel, int id=0); + +private: + KHTMLPart* m_part; + KHTMLView* m_view; + bool m_viewVisible; + + KActionMenu *m_document; + KActionMenu *m_more; + KActionMenu *m_links; + + /** Map of KAction */ + KActionMap kaction_map; + /** Map of KActionMenu */ + KActionMenuMap kactionmenu_map; + + /** Map of all the link element which can be managed by rellinks */ + QMap<QString,DOMElementMap> element_map; + + QTimer* m_pollTimer; +}; + +#endif // _PLUGIN_RELLINKS_H_ diff --git a/konq-plugins/rellinks/plugin_rellinks.rc b/konq-plugins/rellinks/plugin_rellinks.rc new file mode 100644 index 0000000..3c5dbcc --- /dev/null +++ b/konq-plugins/rellinks/plugin_rellinks.rc @@ -0,0 +1,38 @@ +<!DOCTYPE kpartgui> +<kpartplugin name="rellinks" library="librellinksplugin" version="3"> +<MenuBar> + <Menu name="tools"><Text>&Tools</Text> + <Menu name="rellinksMenu"><Text>Document Relations</Text> + <Action name="rellinks_top"/> + <Action name="rellinks_up"/> + <Action name="rellinks_first"/> + <Action name="rellinks_previous"/> + <Action name="rellinks_next"/> + <Action name="rellinks_last"/> + <Action name="rellinks_search"/> + <Action name="rellinks_document"/> + <Action name="rellinks_more"/> + <Action name="rellinks_links"/> + </Menu> + </Menu> +</MenuBar> + + +<ToolBar name="rellinksToolBar" hidden="true"><Text>Document Relations</Text> + <Action name="rellinks_top"/> + <Action name="rellinks_up"/> + <Action name="rellinks_first"/> + <Action name="rellinks_previous"/> + <Action name="rellinks_next"/> + <Action name="rellinks_last"/> + <Action name="rellinks_search"/> + <Separator /> + <Action name="rellinks_document"/> + <Separator /> + <Action name="rellinks_more"/> + <Separator /> + <Action name="rellinks_links"/> +</ToolBar> + + +</kpartplugin> diff --git a/konq-plugins/rellinks/rellinks.kdevelop b/konq-plugins/rellinks/rellinks.kdevelop new file mode 100644 index 0000000..5496bfc --- /dev/null +++ b/konq-plugins/rellinks/rellinks.kdevelop @@ -0,0 +1,157 @@ +<?xml version = '1.0'?> +<kdevelop> + <general> + <author>Franck Quélain</author> + <email>shift@free.fr</email> + <version>0.3</version> + <projectmanagement>KDevKDEAutoProject</projectmanagement> + <primarylanguage>C++</primarylanguage> + <keywords> + <keyword>C++</keyword> + <keyword>Code</keyword> + <keyword>Qt</keyword> + <keyword>KDE</keyword> + </keywords> + <projectdirectory>../..</projectdirectory> + <absoluteprojectpath>false</absoluteprojectpath> + <description/> + <ignoreparts/> + <secondaryLanguages/> + </general> + <kdevautoproject> + <general> + <activetarget>konq_plugins/rellinks/librellinksplugin.la</activetarget> + <useconfiguration>debug</useconfiguration> + </general> + <configurations> + <optimized> + <builddir>optimized</builddir> + <ccompiler>GccOptions</ccompiler> + <cxxcompiler>GppOptions</cxxcompiler> + <f77compiler>G77Options</f77compiler> + <cxxflags>-O2 -g0</cxxflags> + </optimized> + <debug> + <configargs>--enable-debug=full --prefix=/usr/local/kde</configargs> + <builddir>debug</builddir> + <ccompiler>kdevgccoptions</ccompiler> + <cxxcompiler>kdevgppoptions</cxxcompiler> + <f77compiler>kdevg77options</f77compiler> + <cxxflags>-O0 -g3</cxxflags> + <topsourcedir/> + <cppflags/> + <ldflags/> + <ccompilerbinary/> + <cxxcompilerbinary/> + <f77compilerbinary/> + <cflags/> + <f77flags/> + </debug> + </configurations> + <make> + <envvars> + <envvar value="1" name="WANT_AUTOCONF_2_5" /> + <envvar value="1" name="WANT_AUTOMAKE_1_6" /> + </envvars> + <abortonerror>false</abortonerror> + <numberofjobs>1</numberofjobs> + <dontact>false</dontact> + <makebin/> + </make> + <run> + <directoryradio>executable</directoryradio> + <customdirectory>/</customdirectory> + <mainprogram/> + <programargs/> + <terminal>false</terminal> + <autocompile>true</autocompile> + <envvars/> + </run> + </kdevautoproject> + <kdevfileview> + <groups> + <group pattern="*.cpp;*.cxx;*.h" name="Sources" /> + <group pattern="*.ui" name="User Interface" /> + <group pattern="*.png" name="Icons" /> + <group pattern="*.po;*.ts" name="Translations" /> + <group pattern="*" name="Others" /> + <hidenonprojectfiles>false</hidenonprojectfiles> + <hidenonlocation>false</hidenonlocation> + </groups> + <tree> + <hidepatterns>*.o,*.lo,CVS</hidepatterns> + <hidenonprojectfiles>false</hidenonprojectfiles> + <showvcsfields>false</showvcsfields> + </tree> + </kdevfileview> + <kdevdoctreeview> + <ignoretocs> + <toc>gtk</toc> + <toc>gnustep</toc> + <toc>python</toc> + <toc>php</toc> + <toc>perl</toc> + </ignoretocs> + <projectdoc> + <userdocDir>html/</userdocDir> + <apidocDir>html/</apidocDir> + </projectdoc> + <ignoreqt_xml/> + <ignoredoxygen/> + <ignorekdocs/> + <ignoredevhelp/> + </kdevdoctreeview> + <kdevdebugger> + <general> + <dbgshell>libtool</dbgshell> + <programargs/> + <gdbpath/> + <configGdbScript/> + <runShellScript/> + <runGdbScript/> + <breakonloadinglibs>true</breakonloadinglibs> + <separatetty>false</separatetty> + <floatingtoolbar>false</floatingtoolbar> + </general> + <display> + <staticmembers>false</staticmembers> + <demanglenames>true</demanglenames> + </display> + </kdevdebugger> + <kdevfilecreate> + <filetypes/> + <useglobaltypes> + <type ext="ui" /> + <type ext="cpp" /> + <type ext="h" /> + </useglobaltypes> + </kdevfilecreate> + <kdevcppsupport> + <references/> + <codecompletion> + <includeGlobalFunctions>true</includeGlobalFunctions> + <includeTypes>true</includeTypes> + <includeEnums>true</includeEnums> + <includeTypedefs>false</includeTypedefs> + <automaticCodeCompletion>true</automaticCodeCompletion> + <automaticArgumentsHint>true</automaticArgumentsHint> + <automaticHeaderCompletion>true</automaticHeaderCompletion> + <codeCompletionDelay>250</codeCompletionDelay> + <argumentsHintDelay>400</argumentsHintDelay> + <headerCompletionDelay>250</headerCompletionDelay> + </codecompletion> + </kdevcppsupport> + <cppsupportpart> + <filetemplates> + <interfacesuffix>.h</interfacesuffix> + <implementationsuffix>.cpp</implementationsuffix> + </filetemplates> + </cppsupportpart> + <kdevcvsservice> + <recursivewhenupdate>true</recursivewhenupdate> + <prunedirswhenupdate>true</prunedirswhenupdate> + <createdirswhenupdate>true</createdirswhenupdate> + <recursivewhencommitremove>true</recursivewhencommitremove> + <revertoptions>-C</revertoptions> + </kdevcvsservice> +</kdevelop> diff --git a/konq-plugins/rellinks/rellinks.lsm b/konq-plugins/rellinks/rellinks.lsm new file mode 100644 index 0000000..df11ae6 --- /dev/null +++ b/konq-plugins/rellinks/rellinks.lsm @@ -0,0 +1,16 @@ +Begin3 +Title: RelLinks -- Some description +Version: 0.3 +Entered-date: +Description: +Keywords: KDE Qt +Author: Franck Quélain <shift@free.fr> +Maintained-by: Franck Quélain <shift@free.fr> +Home-page: +Alternate-site: +Primary-site: ftp://ftp.kde.org/pub/kde/unstable/apps/utils + xxxxxx rellinks-0.3.tar.gz + xxx rellinks-0.3.lsm +Platform: Linux. Needs KDE +Copying-policy: GPL +End |