summaryrefslogtreecommitdiffstats
path: root/klinkstatus/src
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commite9ae80694875f869892f13f4fcaf1170a00dea41 (patch)
treeaa2f8d8a217e2d376224c8d46b7397b68d35de2d /klinkstatus/src
downloadtdewebdev-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 'klinkstatus/src')
-rw-r--r--klinkstatus/src/Makefile.am62
-rw-r--r--klinkstatus/src/actionmanager.cpp273
-rw-r--r--klinkstatus/src/actionmanager.h69
-rw-r--r--klinkstatus/src/cfg/Makefile.am6
-rw-r--r--klinkstatus/src/cfg/dummy.cpp0
-rw-r--r--klinkstatus/src/cfg/klinkstatus.kcfg91
-rw-r--r--klinkstatus/src/cfg/klsconfig.kcfgc7
-rw-r--r--klinkstatus/src/engine/Makefile.am9
-rw-r--r--klinkstatus/src/engine/linkchecker.cpp703
-rw-r--r--klinkstatus/src/engine/linkchecker.h128
-rw-r--r--klinkstatus/src/engine/linkfilter.cpp46
-rw-r--r--klinkstatus/src/engine/linkfilter.h49
-rw-r--r--klinkstatus/src/engine/linkstatus.cpp214
-rw-r--r--klinkstatus/src/engine/linkstatus.h187
-rw-r--r--klinkstatus/src/engine/linkstatus_impl.h417
-rw-r--r--klinkstatus/src/engine/searchmanager.cpp916
-rw-r--r--klinkstatus/src/engine/searchmanager.h193
-rw-r--r--klinkstatus/src/engine/searchmanager_impl.h158
-rw-r--r--klinkstatus/src/global.cpp205
-rw-r--r--klinkstatus/src/global.h60
-rw-r--r--klinkstatus/src/klinkstatus.cpp213
-rw-r--r--klinkstatus/src/klinkstatus.desktop54
-rw-r--r--klinkstatus/src/klinkstatus.h86
-rw-r--r--klinkstatus/src/klinkstatus.lsm16
-rw-r--r--klinkstatus/src/klinkstatus_part.cpp207
-rw-r--r--klinkstatus/src/klinkstatus_part.desktop17
-rw-r--r--klinkstatus/src/klinkstatus_part.h87
-rw-r--r--klinkstatus/src/klinkstatus_part.rc58
-rw-r--r--klinkstatus/src/klinkstatus_shell.rc28
-rw-r--r--klinkstatus/src/main.cpp98
-rw-r--r--klinkstatus/src/parser/Makefile.am6
-rw-r--r--klinkstatus/src/parser/htmlparser.cpp455
-rw-r--r--klinkstatus/src/parser/htmlparser.h124
-rw-r--r--klinkstatus/src/parser/http.cpp87
-rw-r--r--klinkstatus/src/parser/http.h79
-rw-r--r--klinkstatus/src/parser/mstring.cpp278
-rw-r--r--klinkstatus/src/parser/mstring.h174
-rw-r--r--klinkstatus/src/parser/node.cpp255
-rw-r--r--klinkstatus/src/parser/node.h279
-rw-r--r--klinkstatus/src/parser/node_impl.h412
-rw-r--r--klinkstatus/src/parser/url.cpp350
-rw-r--r--klinkstatus/src/parser/url.h57
-rw-r--r--klinkstatus/src/tests/data/entities/bull&bladder.jpgbin0 -> 9769 bytes
-rw-r--r--klinkstatus/src/tests/data/entities/bull_bladder.jpgbin0 -> 9769 bytes
-rw-r--r--klinkstatus/src/tests/data/entities/link_with_html_entities.html7
-rw-r--r--klinkstatus/src/ui/Makefile.am11
-rw-r--r--klinkstatus/src/ui/celltooltip.cpp54
-rw-r--r--klinkstatus/src/ui/celltooltip.h45
-rw-r--r--klinkstatus/src/ui/documentrootdialog.cpp89
-rw-r--r--klinkstatus/src/ui/documentrootdialog.h57
-rw-r--r--klinkstatus/src/ui/klshistorycombo.cpp198
-rw-r--r--klinkstatus/src/ui/klshistorycombo.h42
-rw-r--r--klinkstatus/src/ui/resultssearchbar.cpp236
-rw-r--r--klinkstatus/src/ui/resultssearchbar.h73
-rw-r--r--klinkstatus/src/ui/resultview.cpp184
-rw-r--r--klinkstatus/src/ui/resultview.h134
-rw-r--r--klinkstatus/src/ui/sessionwidget.cpp723
-rw-r--r--klinkstatus/src/ui/sessionwidget.h149
-rw-r--r--klinkstatus/src/ui/sessionwidgetbase.ui602
-rw-r--r--klinkstatus/src/ui/settings/Makefile.am7
-rw-r--r--klinkstatus/src/ui/settings/configidentificationdialog.cpp55
-rw-r--r--klinkstatus/src/ui/settings/configidentificationdialog.h40
-rw-r--r--klinkstatus/src/ui/settings/configidentificationdialogui.ui134
-rw-r--r--klinkstatus/src/ui/settings/configresultsdialog.ui72
-rw-r--r--klinkstatus/src/ui/settings/configsearchdialog.ui353
-rw-r--r--klinkstatus/src/ui/settings/dummy.cpp0
-rw-r--r--klinkstatus/src/ui/tablelinkstatus.cpp750
-rw-r--r--klinkstatus/src/ui/tablelinkstatus.h202
-rw-r--r--klinkstatus/src/ui/tabwidgetsession.cpp274
-rw-r--r--klinkstatus/src/ui/tabwidgetsession.h85
-rw-r--r--klinkstatus/src/ui/treeview.cpp609
-rw-r--r--klinkstatus/src/ui/treeview.h142
-rw-r--r--klinkstatus/src/utils/Makefile.am7
-rw-r--r--klinkstatus/src/utils/mvector.h45
-rw-r--r--klinkstatus/src/utils/utils.cpp204
-rw-r--r--klinkstatus/src/utils/utils.h64
-rw-r--r--klinkstatus/src/utils/xsl.cpp437
-rw-r--r--klinkstatus/src/utils/xsl.h111
78 files changed, 13408 insertions, 0 deletions
diff --git a/klinkstatus/src/Makefile.am b/klinkstatus/src/Makefile.am
new file mode 100644
index 00000000..590957a2
--- /dev/null
+++ b/klinkstatus/src/Makefile.am
@@ -0,0 +1,62 @@
+# set the include path for X, qt and KDE
+INCLUDES = -I$(top_srcdir)/src/cfg -I./ui -I./ui/settings $(all_includes)
+
+# these are the headers for your project
+
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+# this Makefile creates both a KPart application and a KPart
+#########################################################################
+# APPLICATION SECTION
+#########################################################################
+# this is the program that gets installed. it's name is used for all
+# of the other Makefile.am variables
+bin_PROGRAMS = klinkstatus
+
+# the application source, library search path, and link libraries
+
+klinkstatus_LDFLAGS = $(KDE_RPATH) $(all_libraries)
+klinkstatus_LDADD = $(LIB_KPARTS)
+
+xdg_apps_DATA = klinkstatus.desktop
+
+# this is where the shell's XML-GUI resource file goes
+shellrcdir = $(kde_datadir)/klinkstatus
+shellrc_DATA = klinkstatus_shell.rc
+
+
+#########################################################################
+# KPART SECTION
+#########################################################################
+kde_module_LTLIBRARIES = libklinkstatuspart.la
+
+# the Part's source, library search path, and link libraries
+
+libklinkstatuspart_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+libklinkstatuspart_la_LIBADD = ui/settings/libsettings.la \
+ ui/libui.la engine/libengine.la \
+ parser/libparser.la utils/libutils.la $(LIB_KPARTS) $(LIB_KFILE)
+
+# this is where the desktop file will go
+partdesktopdir = $(kde_servicesdir)
+partdesktop_DATA = klinkstatus_part.desktop
+
+# this is where the part's XML-GUI resource file goes
+partrcdir = $(kde_datadir)/klinkstatuspart
+partrc_DATA = klinkstatus_part.rc
+noinst_HEADERS = klinkstatus.h klinkstatus_part.h global.h actionmanager.h
+klinkstatus_SOURCES = klinkstatus.cpp main.cpp
+libklinkstatuspart_la_SOURCES = klinkstatus_part.cpp global.cpp \
+ actionmanager.cpp
+SUBDIRS = cfg utils parser engine ui
+
+messages: rc.cpp
+ LIST=`find . -name \*.ui -o -name \*.kcfg -o -name \*.rc` ;\
+ if test -n "$$LIST"; then $(EXTRACTRC) $$LIST >> rc.cpp; fi
+ LIST=`find . -name \*.h -o -name \*.hh -o -name \*.H -o -name \*.hxx -o -name \*.hpp -o -name \*.cpp -o -name \*.cc -o -name \*.cxx -o -name \*.ecpp -o -name \*.C`; \
+ if test -n "$$LIST"; then \
+ $(XGETTEXT) $$LIST -o $(podir)/klinkstatus.pot; \
+ fi
+
diff --git a/klinkstatus/src/actionmanager.cpp b/klinkstatus/src/actionmanager.cpp
new file mode 100644
index 00000000..9f60e7bf
--- /dev/null
+++ b/klinkstatus/src/actionmanager.cpp
@@ -0,0 +1,273 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+#include "actionmanager.h"
+
+#include <kactioncollection.h>
+#include <kxmlguifactory.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kguiitem.h>
+
+#include <qbuttongroup.h>
+
+#include "klinkstatus_part.h"
+#include "ui/sessionwidget.h"
+#include "ui/tabwidgetsession.h"
+#include "cfg/klsconfig.h"
+
+
+ActionManager* ActionManager::m_self = 0;
+
+ActionManager* ActionManager::getInstance()
+{
+ Q_ASSERT(m_self);
+
+ return m_self;
+}
+
+void ActionManager::setInstance(ActionManager* manager)
+{
+ Q_ASSERT(manager);
+
+ m_self = manager;
+}
+
+class ActionManager::ActionManagerPrivate
+{
+public:
+ ActionManagerPrivate()
+ : part(0), tabWidgetSession(0), sessionWidget(0)
+ {}
+
+ KActionCollection* actionCollection;
+
+ KLinkStatusPart* part;
+ TabWidgetSession* tabWidgetSession;
+ SessionWidget* sessionWidget;
+};
+
+ActionManager::ActionManager(QObject *parent, const char *name)
+ : QObject(parent, name), d(new ActionManagerPrivate)
+{}
+
+ActionManager::~ActionManager()
+{
+ delete d;
+ d = 0;
+}
+
+void ActionManager::initPart(KLinkStatusPart* part)
+{
+ Q_ASSERT(part);
+
+ if(d->part)
+ return;
+
+ d->part = part;
+ d->actionCollection = part->actionCollection();
+
+ KAction* action = 0;
+
+ // *************** File menu *********************
+
+ new KAction(i18n("New Link Check"), "filenew",
+ 0,
+ d->part, SLOT(slotNewLinkCheck()),
+ d->actionCollection, "new_link_check");
+
+ new KAction(i18n("Open URL..."), "fileopen",
+ 0,
+ d->part, SLOT(slotOpenLink()),
+ d->actionCollection, "open_link");
+
+ action = new KAction(i18n("Close Tab"), "fileclose",
+ 0,
+ d->part, SLOT(slotClose()),
+ d->actionCollection, "close_tab");
+ action->setEnabled(false);
+
+ // *************** Settings menu *********************
+
+ (void) new KAction(i18n("Configure KLinkStatus..."), "configure",
+ 0, d->part, SLOT(slotConfigureKLinkStatus()),
+ d->actionCollection, "configure_klinkstatus");
+
+ // *************** Help menu *********************
+
+ (void) new KAction(i18n("About KLinkStatus"), "klinkstatus",
+ 0, d->part, SLOT(slotAbout()),
+ d->actionCollection, "about_klinkstatus");
+
+ (void) new KAction(i18n("&Report Bug..."), 0, 0, d->part,
+ SLOT(slotReportBug()), d->actionCollection, "report_bug");
+
+ // *************** View menu *********************
+}
+
+void ActionManager::initTabWidget(TabWidgetSession* tabWidgetSession)
+{
+ Q_ASSERT(tabWidgetSession);
+
+ if (d->tabWidgetSession)
+ return;
+
+ d->tabWidgetSession = tabWidgetSession;
+
+ // *************** File menu *********************
+
+ KAction* action = new KAction(i18n("E&xport Results as HTML..."), "filesave", 0,
+ d->tabWidgetSession, SLOT(slotExportAsHTML()),
+ d->actionCollection, "file_export_html");
+ action->setEnabled(false);
+
+ // *************** View menu *********************
+
+ // this action must be in the tabwidget because the slot can't be connected to a particular sessionWidget
+ KToggleAction* toggle_action = new KToggleAction(i18n("&Follow last Link checked"),
+ "make_kdevelop", "Ctrl+f",
+ d->tabWidgetSession, SLOT(slotFollowLastLinkChecked()),
+ d->actionCollection, "follow_last_link_checked");
+ toggle_action->setChecked(KLSConfig::followLastLinkChecked());
+
+ // this action must be in the tabwidget because the slot can't be connected to a particular sessionWidget
+ toggle_action = new KToggleAction(i18n("&Hide Search Panel"), "bottom", "Ctrl+h",
+ d->tabWidgetSession, SLOT(slotHideSearchPanel()),
+ d->actionCollection, "hide_search_bar");
+ KGuiItem item(i18n("&Show Search Panel"), "top", "Show Search Panel");
+ toggle_action->setCheckedState(item);
+
+ new KAction(i18n("&Reset Search Options"), "reload", "F5",
+ d->tabWidgetSession, SLOT(slotResetSearchOptions()),
+ d->actionCollection, "reset_search_bar");
+
+ // *************** Search menu *********************
+
+ toggle_action = new KToggleAction(i18n("&Start Search"),
+ "player_play", "Ctrl+s",
+ d->tabWidgetSession, SLOT(slotStartSearch()),
+ d->actionCollection, "start_search");
+ toggle_action->setEnabled(false);
+
+ toggle_action = new KToggleAction(i18n("&Pause Search"),
+ "player_pause", "Ctrl+p",
+ d->tabWidgetSession, SLOT(slotPauseSearch()),
+ d->actionCollection, "pause_search");
+ toggle_action->setEnabled(false);
+
+ action = new KAction(i18n("St&op Search"),
+ "player_stop", "Ctrl+c",
+ d->tabWidgetSession, SLOT(slotStopSearch()),
+ d->actionCollection, "stop_search");
+ action->setEnabled(false);
+}
+
+void ActionManager::initSessionWidget(SessionWidget* sessionWidget)
+{
+ Q_ASSERT(sessionWidget);
+
+ if (d->sessionWidget)
+ return;
+
+ d->sessionWidget = sessionWidget;
+
+}
+
+QWidget* ActionManager::container(const char* name)
+{
+ return d->part->factory()->container(name, d->part);
+}
+
+KActionCollection* ActionManager::actionCollection()
+{
+ return d->actionCollection;
+}
+
+KAction* ActionManager::action(const char* name, const char* classname)
+{
+ return d->actionCollection != 0 ? d->actionCollection->action(name, classname) : 0;
+}
+
+void ActionManager::slotUpdateSessionWidgetActions(SessionWidget* page)
+{
+ KToggleAction* start_search_action_ = static_cast<KToggleAction*> (action("start_search"));
+ KToggleAction* pause_search_action_ = static_cast<KToggleAction*> (action("pause_search"));
+ KAction* stop_search_action_ = action("stop_search");
+
+ if(page->inProgress())
+ {
+ Q_ASSERT(!page->stopped());
+
+ start_search_action_->setEnabled(true);
+ start_search_action_->setChecked(true);
+
+ pause_search_action_->setEnabled(true);
+
+ stop_search_action_->setEnabled(true);
+ }
+ if(page->paused())
+ {
+ Q_ASSERT(page->inProgress());
+ Q_ASSERT(!page->stopped());
+
+ start_search_action_->setEnabled(true);
+ start_search_action_->setChecked(true);
+
+ pause_search_action_->setEnabled(true);
+ pause_search_action_->setChecked(true);
+
+ stop_search_action_->setEnabled(true);
+ }
+ if(page->stopped())
+ {
+ Q_ASSERT(!page->inProgress());
+ Q_ASSERT(!page->paused());
+
+ start_search_action_->setEnabled(true);
+ start_search_action_->setChecked(false);
+
+ pause_search_action_->setEnabled(false);
+ pause_search_action_->setChecked(false);
+
+ stop_search_action_->setEnabled(false);
+ }
+
+// ____________________________________________________________________
+
+ KToggleAction* toggleAction = static_cast<KToggleAction*> (action("follow_last_link_checked"));
+
+ if(!toggleAction) // the first sessionWidget is created before initSessionWidget is called
+ {
+ initSessionWidget(page);
+ toggleAction = static_cast<KToggleAction*> (action("follow_last_link_checked"));
+ }
+ Q_ASSERT(toggleAction);
+ toggleAction->setChecked(page->followLastLinkChecked());
+
+ toggleAction = static_cast<KToggleAction*> (action("hide_search_bar"));
+ Q_ASSERT(toggleAction);
+ toggleAction->setChecked(page->buttongroup_search->isHidden());
+
+ // ____________________________________________________________________
+
+ action("file_export_html")->setEnabled(!page->isEmpty());
+}
+
+
+#include "actionmanager.moc"
diff --git a/klinkstatus/src/actionmanager.h b/klinkstatus/src/actionmanager.h
new file mode 100644
index 00000000..a33c37f4
--- /dev/null
+++ b/klinkstatus/src/actionmanager.h
@@ -0,0 +1,69 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 ACTIONMANAGER_H
+#define ACTIONMANAGER_H
+
+#include <qobject.h>
+
+class KAction;
+class KActionCollection;
+
+class SessionWidget;
+class KLinkStatusPart;
+class TabWidgetSession;
+
+/**
+ @author Paulo Moura Guedes <moura@kdewebdev.org>
+
+ interface for accessing actions, popup menus etc. from widgets.
+*/
+class ActionManager : public QObject
+{
+ Q_OBJECT
+public:
+ ActionManager(QObject* parent=0, const char* name=0);
+ virtual ~ActionManager();
+
+ static ActionManager* getInstance();
+ static void setInstance(ActionManager* manager);
+
+ virtual KAction* action(const char* name, const char* classname=0);
+ virtual QWidget* container(const char* name);
+
+ void initPart(KLinkStatusPart* part);
+ void initSessionWidget(SessionWidget* sessionWidget);
+ void initTabWidget(TabWidgetSession* tabWidgetSession);
+
+public slots:
+ void slotUpdateSessionWidgetActions(SessionWidget*);
+
+protected:
+
+ KActionCollection* actionCollection();
+
+private:
+
+ static ActionManager* m_self;
+
+ class ActionManagerPrivate;
+ ActionManagerPrivate* d;
+};
+
+#endif
diff --git a/klinkstatus/src/cfg/Makefile.am b/klinkstatus/src/cfg/Makefile.am
new file mode 100644
index 00000000..2e74b2ad
--- /dev/null
+++ b/klinkstatus/src/cfg/Makefile.am
@@ -0,0 +1,6 @@
+kde_kcfg_DATA = klinkstatus.kcfg
+METASOURCES = AUTO
+libcfg_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libcfg.la
+libcfg_la_SOURCES = dummy.cpp klsconfig.kcfgc
+AM_CPPFLAGS=$(all_includes) \ No newline at end of file
diff --git a/klinkstatus/src/cfg/dummy.cpp b/klinkstatus/src/cfg/dummy.cpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/klinkstatus/src/cfg/dummy.cpp
diff --git a/klinkstatus/src/cfg/klinkstatus.kcfg b/klinkstatus/src/cfg/klinkstatus.kcfg
new file mode 100644
index 00000000..ae43c6b1
--- /dev/null
+++ b/klinkstatus/src/cfg/klinkstatus.kcfg
@@ -0,0 +1,91 @@
+<!DOCTYPE kcfg SYSTEM "http://www.kde.org/standards/kcfg/1.0/kcfg.dtd">
+<kcfg>
+ <kcfgfile name="klinkstatus_shell.rc" />
+
+ <group name="klinkstatus" >
+
+ <entry key="MaxCountComboUrl" type="Int" >
+ <label>Maximum number of entries in the combo url.</label>
+ <default>50</default>
+ </entry>
+
+ <entry key="MaxConnectionsNumber" type="Int" >
+ <label>Maximum number of simultaneous connections.</label>
+ <default>5</default>
+ </entry>
+
+ <entry key="TimeOut" type="Int" >
+ <label>Timeout on getting an URL.</label>
+ <default>35</default>
+ </entry>
+
+ <entry key="ComboUrlHistory" type="StringList" >
+ <label>History of combo url.</label>
+ </entry>
+
+ <entry key="RecursiveCheck" type="Bool" >
+ <label>Whether to do a recursive check.</label>
+ <default>true</default>
+ </entry>
+
+ <entry key="Depth" type="Int" >
+ <label>Maximum depth to check.</label>
+ <default>0</default>
+ </entry>
+
+ <entry key="CheckParentFolders" type="Bool" >
+ <label>Whether to check parent folders.</label>
+ <default>true</default>
+ </entry>
+
+ <entry key="CheckExternalLinks" type="Bool" >
+ <label>Whether to check external links.</label>
+ <default>true</default>
+ </entry>
+
+ <entry key="RememberCheckSettings" type="Bool" >
+ <label>Whether to remeber the check settings like depth, and so on, on exit.</label>
+ <default>false</default>
+ </entry>
+
+ <entry key="UseQuantaUrlPreviewPrefix" type="Bool" >
+ <label>Whether preview prefix in Quanta project is used to set the URL to check.</label>
+ <default>true</default>
+ </entry>
+
+ <entry key="DisplayTreeView" type="Bool" >
+ <label>Whether to display a tree view or a flat view in the results view.</label>
+ <default>true</default>
+ </entry>
+
+ <entry key="DisplayFlatView" type="Bool" >
+ <label>Whether to display a tree view or a flat view in the results view.</label>
+ <default>false</default>
+ </entry>
+
+ <entry key="AutoAdjustColumnWidth" type="Bool" >
+ <label>Whether to automatically adjust the width of the result columns (Not used).</label>
+ <default>false</default>
+ </entry>
+
+ <entry key="FollowLastLinkChecked" type="Bool" >
+ <label>Whether the viewport of the result view should follow the last link checked.</label>
+ <default>true</default>
+ </entry>
+
+ <entry key="ShowMarkupStatus" type="Bool" >
+ <label>Whether the user can see if the markup is valid by showing a column with an icon indicator.</label>
+ <default>false</default>
+ </entry>
+
+ <entry key="SendIdentification" type="Bool" >
+ <label>Whether to send an User-Agent in HTTP requests.</label>
+ <default>true</default>
+ </entry>
+
+ <entry name="UserAgent" type="String">
+ <label>Defines the HTTP User-Agent to send.</label>
+ </entry>
+
+ </group>
+</kcfg>
diff --git a/klinkstatus/src/cfg/klsconfig.kcfgc b/klinkstatus/src/cfg/klsconfig.kcfgc
new file mode 100644
index 00000000..089abab7
--- /dev/null
+++ b/klinkstatus/src/cfg/klsconfig.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=klinkstatus.kcfg
+#IncludeFiles=defines.h
+ClassName=KLSConfig
+Singleton=true
+#CustomAdditions=true
+Mutators=true
diff --git a/klinkstatus/src/engine/Makefile.am b/klinkstatus/src/engine/Makefile.am
new file mode 100644
index 00000000..1bd3ba88
--- /dev/null
+++ b/klinkstatus/src/engine/Makefile.am
@@ -0,0 +1,9 @@
+INCLUDES = -I$(top_srcdir)/src/ui $(all_includes)
+METASOURCES = AUTO
+noinst_HEADERS = linkchecker.h linkstatus.h linkstatus_impl.h searchmanager.h \
+ searchmanager_impl.h linkfilter.h
+libengine_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libengine.la
+libengine_la_SOURCES = linkchecker.cpp linkstatus.cpp searchmanager.cpp \
+ linkfilter.cpp
+libengine_la_LIBADD = $(LIB_KHTML)
diff --git a/klinkstatus/src/engine/linkchecker.cpp b/klinkstatus/src/engine/linkchecker.cpp
new file mode 100644
index 00000000..bcc503ad
--- /dev/null
+++ b/klinkstatus/src/engine/linkchecker.cpp
@@ -0,0 +1,703 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Puto Moura *
+ * mojo@localhost.localdomain *
+ * *
+ * 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. *
+ ***************************************************************************/
+#include "linkchecker.h"
+#include "searchmanager.h"
+#include "../utils/utils.h"
+#include "../parser/htmlparser.h"
+
+#include <qstring.h>
+#include <qtimer.h>
+#include <qtextcodec.h>
+#include <qcstring.h>
+
+#include <kio/netaccess.h>
+#include <kio/global.h>
+#include <kio/job.h>
+#include <kio/scheduler.h>
+#include <kio/slave.h>
+#include <kmimetype.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <khtml_part.h>
+#include <dom/html_misc.h>
+#include <dom/dom_node.h>
+#include <dom/dom_string.h>
+
+
+int LinkChecker::count_ = 0;
+
+LinkChecker::LinkChecker(LinkStatus* linkstatus, int time_out,
+ QObject *parent, const char *name)
+ : QObject(parent, name), search_manager_(0),
+ linkstatus_(linkstatus), t_job_(0), time_out_(time_out), checker_(0), document_charset_(),
+ redirection_(false), header_checked_(false), finnished_(false),
+ parsing_(false), is_charset_checked_(false), has_defined_charset_(false)
+{
+ Q_ASSERT(linkstatus_);
+ Q_ASSERT(!linkstatus_->checked());
+
+ kdDebug(23100) << endl << ++count_ << ": " << "Checking " << linkstatus_->absoluteUrl().url() << endl;
+}
+
+LinkChecker::~LinkChecker()
+{}
+
+void LinkChecker::setSearchManager(SearchManager* search_manager)
+{
+ Q_ASSERT(search_manager);
+ search_manager_ = search_manager;
+}
+
+void LinkChecker::check()
+{
+ Q_ASSERT(!finnished_);
+
+ KURL url(linkStatus()->absoluteUrl());
+ Q_ASSERT(url.isValid());
+
+ if(url.hasRef()) {
+ KMimeType::Ptr mimeType = KMimeType::findByURL(url);
+ if(mimeType->is("text/html") || mimeType->is("application/xml")) {
+ checkRef();
+ return;
+ }
+ }
+
+ t_job_ = KIO::get(url, false, false);
+
+ t_job_->addMetaData("PropagateHttpHeader", "true"); // to have the http header
+
+ if (linkstatus_->parent()) {
+ t_job_->addMetaData("referrer", linkstatus_->parent()->absoluteUrl().prettyURL());
+ }
+
+ if(search_manager_->sendIdentification())
+ {
+ t_job_->addMetaData("SendUserAgent", "true");
+ t_job_->addMetaData("UserAgent", search_manager_->userAgent());
+ }
+ else
+ t_job_->addMetaData("SendUserAgent", "false");
+
+
+ QObject::connect(t_job_, SIGNAL(data(KIO::Job *, const QByteArray &)),
+ this, SLOT(slotData(KIO::Job *, const QByteArray &)));
+ QObject::connect(t_job_, SIGNAL(mimetype(KIO::Job *, const QString &)),
+ this, SLOT(slotMimetype(KIO::Job *, const QString &)));
+ QObject::connect(t_job_, SIGNAL(result(KIO::Job *)),
+ this, SLOT(slotResult(KIO::Job *)));
+ QObject::connect(t_job_, SIGNAL(redirection(KIO::Job *, const KURL &)),
+ this, SLOT(slotRedirection(KIO::Job *, const KURL &)));
+
+ QTimer::singleShot( time_out_ * 1000, this, SLOT(slotTimeOut()) );
+
+ t_job_->setInteractive(false);
+}
+
+void LinkChecker::slotTimeOut()
+{
+ if(!finnished_ && !parsing_)
+ {
+ kdDebug(23100) << "timeout: " << linkstatus_->absoluteUrl().url() << endl;
+ if(t_job_ && t_job_->slave())
+ kdDebug(23100) << " - " << t_job_->slave() << "/" << t_job_->slave()->slave_pid() << endl;
+ else
+ kdDebug(23100) << endl;
+
+
+// Q_ASSERT(t_job_); // can happen: e.g. bad result signal
+ if(t_job_->error() != KIO::ERR_USER_CANCELED)
+ {
+ linkstatus_->setErrorOccurred(true);
+ linkstatus_->setChecked(true);
+ linkstatus_->setError(i18n("Timeout"));
+ linkstatus_->setStatus(LinkStatus::TIMEOUT);
+
+ killJob();
+ finnish();
+ }
+ }
+}
+
+void LinkChecker::slotMimetype (KIO::Job* /*job*/, const QString &type)
+{
+ if(finnished_)
+ return;
+
+// kdDebug(23100) << "LinkChecker::slotMimetype:" << type << "-> " << linkstatus_->absoluteUrl().url()
+// << " - " << t_job_->slave() << "/" << t_job_->slave()->slave_pid() << endl;
+
+ Q_ASSERT(t_job_);
+
+ LinkStatus* ls = 0;
+/* if(redirection_)
+ ls = linkStatus()->redirection();
+ else*/
+ ls = linkstatus_;
+ Q_ASSERT(ls);
+
+ ls->setMimeType(type);
+ KURL url = ls->absoluteUrl();
+
+ // we doesn't do nothing if file is http or https because we need the header
+ // which is only available in the data response
+ if(!t_job_->error()) // if a error happened let result() handle that
+ {
+ if(ls->onlyCheckHeader())
+ {
+ //kdDebug(23100) << "only check header: " << ls->absoluteUrl().prettyURL() << endl;
+
+ // file is OK (http can have an error page though job->error() is false)
+ if(!url.protocol().startsWith("http"))
+ {
+ ls->setStatusText("OK");
+ ls->setStatus(LinkStatus::SUCCESSFULL);
+
+ killJob();
+ finnish();
+ }
+ }
+ else // !ls->onlyCheckHeader()
+ {
+ //kdDebug(23100) << "NOT only check header: " << ls->absoluteUrl().prettyURL() << endl;
+
+ // file is OK (http can have an error page though job->error() is false)
+ if(!url.protocol().startsWith("http")) // if not, it have to go trough slotData to get the http header
+ {
+ // it's not an html page, so we don't want the file content
+ if(type != "text/html"/* && type != "text/plain"*/)
+ {
+ //kdDebug(23100) << "mimetype: " << type << endl;
+ ls->setStatusText("OK");
+ ls->setStatus(LinkStatus::SUCCESSFULL);
+
+ killJob();
+ finnish();
+ }
+ }
+ }
+ }
+}
+
+void LinkChecker::slotData(KIO::Job* /*job*/, const QByteArray& data)
+{
+ if(finnished_)
+ return;
+
+ kdDebug(23100) << "LinkChecker::slotData -> " << linkstatus_->absoluteUrl().url()
+ << " - " << t_job_->slave() << "/" << t_job_->slave()->slave_pid() << endl;
+
+ Q_ASSERT(t_job_);
+
+ LinkStatus* ls = 0;
+/* if(redirection_)
+ ls = linkStatus()->redirection();
+ else*/
+ ls = linkstatus_;
+ Q_ASSERT(ls);
+
+ KURL url = ls->absoluteUrl();
+
+ if(!t_job_->error())
+ {
+ if(ls->onlyCheckHeader())
+ {
+ Q_ASSERT(header_checked_ == false);
+ // the job should have been killed in slotMimetype
+ Q_ASSERT(url.protocol() == "http" || url.protocol() == "https");
+
+ // get the header and quit
+ if(url.protocol().startsWith("http"))
+ {
+ // get the header
+ ls->setHttpHeader(getHttpHeader(t_job_));
+
+ if(t_job_->isErrorPage())
+ ls->setIsErrorPage(true);
+
+ if(header_checked_)
+ {
+ killJob();
+ linkstatus_->setStatus(getHttpStatus());
+ linkstatus_->setChecked(true);
+ finnish();
+ return;
+ }
+ }
+ }
+ else
+ {
+ if(url.protocol().startsWith("http"))
+ {
+ if(!header_checked_)
+ {
+ ls->setHttpHeader(getHttpHeader(t_job_));
+ }
+ if(ls->mimeType() != "text/html" && header_checked_)
+ {
+ //kdDebug(23100) << "mimetype of " << ls->absoluteUrl().prettyURL() << ": " << ls->mimeType() << endl;
+ ls->setStatus(getHttpStatus());
+ killJob();
+ finnish(); // if finnish is called before kill what you get is a segfault, don't know why
+ return;
+ }
+ else if(t_job_->isErrorPage() && header_checked_)
+ {
+ //kdDebug(23100) << "ERROR PAGE" << endl;
+ ls->setIsErrorPage(true);
+ ls->setStatus(getHttpStatus());
+ killJob();
+ finnish();
+ return;
+ }
+ }
+ else
+ {
+ Q_ASSERT(ls->mimeType() == "text/html");
+ }
+ if(!is_charset_checked_)
+ findDocumentCharset(data);
+
+ QTextCodec* codec = 0;
+ if(has_defined_charset_)
+ codec = QTextCodec::codecForName(document_charset_);
+ if(!codec)
+ codec = QTextCodec::codecForName("iso8859-1"); // default
+
+ doc_html_ += codec->toUnicode(data);
+ }
+ }
+}
+
+void LinkChecker::findDocumentCharset(QString const& doc)
+{
+ Q_ASSERT(!is_charset_checked_);
+
+ is_charset_checked_ = true; // only check the first stream of data
+
+ if(header_checked_)
+ document_charset_ = linkstatus_->httpHeader().charset();
+
+ // try to look in the meta elements
+ if(document_charset_.isNull() || document_charset_.isEmpty())
+ document_charset_ = HtmlParser::findCharsetInMetaElement(doc);
+
+ if(!document_charset_.isNull() && !document_charset_.isEmpty())
+ has_defined_charset_ = true;
+}
+
+// only comes here if an error happened or in case of a clean html page
+// if onlyCheckHeader is false
+void LinkChecker::slotResult(KIO::Job* /*job*/)
+{
+ if(finnished_)
+ return;
+
+ kdDebug(23100) << "LinkChecker::slotResult -> " << linkstatus_->absoluteUrl().url() << endl;
+
+ Q_ASSERT(t_job_);
+ if(!t_job_)
+ return;
+
+ if(redirection_) {
+ if(!processRedirection(redirection_url_)) {
+ t_job_ = 0;
+ linkstatus_->setChecked(true);
+ finnish();
+ return;
+ }
+ }
+
+ KIO::TransferJob* job = t_job_;
+ t_job_ = 0;
+
+ emit jobFinnished(this);
+
+ if(job->error() == KIO::ERR_USER_CANCELED)
+ {
+ // FIXME This can happen! If the job is non interactive...
+ kdWarning(23100) << endl << "Job killed quietly, yet signal result was emited..." << endl;
+ kdDebug(23100) << linkstatus_->toString() << endl;
+ finnish();
+ return;
+ }
+
+ LinkStatus* ls = 0;
+ if(redirection_)
+ ls = linkStatus()->redirection();
+ else
+ ls = linkstatus_;
+ Q_ASSERT(ls);
+
+ if(!(!ls->onlyCheckHeader() ||
+ job->error() ||
+ !header_checked_))
+ kdWarning(23100) << ls->toString() << endl;
+
+ Q_ASSERT(!ls->onlyCheckHeader() || job->error() || !header_checked_);
+
+ if(ls->isErrorPage())
+ kdWarning(23100) << "\n\n" << ls->toString() << endl << endl;
+
+ Q_ASSERT(!job->isErrorPage());
+
+ if(job->error())
+ {
+ kdDebug(23100) << "Job error: " << job->errorString() << endl;
+ kdDebug(23100) << "Job error code: " << job->error() << endl;
+
+ if(job->error() == KIO::ERR_IS_DIRECTORY)
+ {
+ ls->setStatusText("OK");
+ ls->setStatus(LinkStatus::SUCCESSFULL);
+ }
+ else
+ {
+ ls->setErrorOccurred(true);
+ if(job->error() == KIO::ERR_SERVER_TIMEOUT)
+ ls->setStatus(LinkStatus::TIMEOUT);
+ else
+ ls->setStatus(LinkStatus::BROKEN);
+
+ if(job->errorString().isEmpty())
+ kdWarning(23100) << "\n\nError string is empty, error = " << job->error() << "\n\n\n";
+ if(job->error() != KIO::ERR_NO_CONTENT)
+ ls->setError(job->errorString());
+ else
+ ls->setError(i18n("No Content"));
+ }
+ }
+
+ else
+ {
+ if(!ls->absoluteUrl().protocol().startsWith("http")) {
+ ls->setStatusText("OK");
+ ls->setStatus(LinkStatus::SUCCESSFULL);
+ }
+ else
+ {
+ if(!header_checked_)
+ {
+ kdDebug(23100) << "\n\nheader not received... checking again...\n\n\n";
+ //check again
+ check();
+ return;
+ }
+ Q_ASSERT(header_checked_);
+
+ ls->setStatus(getHttpStatus());
+ }
+
+ if(!doc_html_.isNull() && !doc_html_.isEmpty())
+ {
+ ls->setDocHtml(doc_html_);
+
+ parsing_ = true;
+ HtmlParser parser(doc_html_);
+
+ if(parser.hasBaseUrl())
+ ls->setBaseURI(KURL(parser.baseUrl().url()));
+ if(parser.hasTitle())
+ ls->setHtmlDocTitle(parser.title().attributeTITLE());
+
+ ls->setChildrenNodes(parser.nodes());
+ parsing_ = false;
+ }
+ }
+ finnish();
+}
+
+
+void LinkChecker::slotRedirection (KIO::Job* /*job*/, const KURL &url)
+{
+ kdDebug(23100) << "LinkChecker::slotRedirection -> " <<
+ linkstatus_->absoluteUrl().url() << " -> " << url.url() << endl;
+// << " - " << t_job_->slave() << "/" << t_job_->slave()->slave_pid() << endl;
+
+ redirection_ = true;
+ redirection_url_ = url;
+}
+
+bool LinkChecker::processRedirection(KURL const& toUrl)
+{
+ if(finnished_)
+ return true;
+
+ kdDebug(23100) << "LinkChecker::processRedirection -> " << linkstatus_->absoluteUrl().url() << " -> " << toUrl.url() << endl;
+
+ Q_ASSERT(t_job_);
+ Q_ASSERT(linkstatus_->absoluteUrl().protocol().startsWith("http"));
+ Q_ASSERT(redirection_);
+
+ linkstatus_->setHttpHeader(getHttpHeader(t_job_, false));
+ linkstatus_->setIsRedirection(true);
+ linkstatus_->setStatusText("redirection");
+ linkstatus_->setStatus(LinkStatus::HTTP_REDIRECTION);
+ linkstatus_->setChecked(true);
+
+ LinkStatus* ls_red = new LinkStatus(*linkstatus_);
+ ls_red->setAbsoluteUrl(toUrl);
+ ls_red->setRootUrl(linkstatus_->rootUrl());
+
+ if(!linkstatus_->onlyCheckHeader())
+ ls_red->setOnlyCheckHeader(false);
+
+ linkstatus_->setRedirection(ls_red);
+ ls_red->setParent(linkstatus_);
+ ls_red->setOriginalUrl(toUrl.url());
+
+ Q_ASSERT(search_manager_);
+
+ if(search_manager_->localDomain(ls_red->absoluteUrl()))
+ ls_red->setExternalDomainDepth(-1);
+ else
+ {
+ if(search_manager_->localDomain(linkstatus_->absoluteUrl()))
+ ls_red->setExternalDomainDepth(linkstatus_->externalDomainDepth() + 1);
+ else
+ ls_red->setExternalDomainDepth(linkstatus_->externalDomainDepth());
+ }
+
+ if(!toUrl.isValid() || search_manager_->existUrl(toUrl, linkstatus_->absoluteUrl()))
+ {
+ ls_red->setChecked(false);
+ return false;
+ }
+ else
+ {
+ ls_red->setChecked(true);
+ return true;
+ }
+}
+
+void LinkChecker::finnish()
+{
+ Q_ASSERT(!t_job_);
+
+ if(!finnished_)
+ {
+ kdDebug(23100) << "LinkChecker::finnish -> " << linkstatus_->absoluteUrl().url() << endl;
+
+ finnished_ = true;
+
+ if(redirection_)
+ Q_ASSERT(linkstatus_->checked());
+ else
+ linkstatus_->setChecked(true);
+
+ emit transactionFinished(linkstatus_, this);
+ }
+}
+
+HttpResponseHeader LinkChecker::getHttpHeader(KIO::Job* /*job*/, bool remember_check)
+{
+ //kdDebug(23100) << "LinkChecker::getHttpHeader -> " << linkstatus_->absoluteUrl().url() << endl;
+
+ Q_ASSERT(!finnished_);
+ Q_ASSERT(t_job_);
+
+ QString header_string = t_job_->queryMetaData("HTTP-Headers");
+ // Q_ASSERT(!header_string.isNull() && !header_string.isEmpty());
+// kdDebug(23100) << "HTTP header: " << endl << header_string << endl;
+// kdDebug(23100) << "Keys: " << HttpResponseHeader(header_string).keys() << endl;
+// kdDebug(23100) << "Content-type: " << HttpResponseHeader(header_string).contentType() << endl;
+// kdDebug(23100) << "Content-type: " << HttpResponseHeader(header_string).value("content-type") << endl;
+
+ if(header_string.isNull() || header_string.isEmpty())
+ {
+ header_checked_ = false;
+ kdWarning(23100) << "header_string.isNull() || header_string.isEmpty(): "
+ << linkstatus_->toString() << endl;
+ }
+ else if(remember_check)
+ header_checked_ = true;
+
+ return HttpResponseHeader(header_string);
+}
+
+void LinkChecker::checkRef()
+{
+ KURL url(linkStatus()->absoluteUrl());
+ Q_ASSERT(url.hasRef());
+
+ QString ref = url.ref();
+ if(ref == "" || ref == "top") {
+ linkstatus_->setStatusText("OK");
+ linkstatus_->setStatus(LinkStatus::SUCCESSFULL);
+ finnish();
+ return;
+ }
+
+ QString url_base;
+ LinkStatus const* ls_parent = 0;
+ int i_ref = -1;
+
+ if(linkStatus()->originalUrl().startsWith("#"))
+ ls_parent = linkStatus()->parent();
+
+ else
+ {
+ i_ref = url.url().find("#");
+ url_base = url.url().left(i_ref);
+ //kdDebug(23100) << "url_base: " << url_base << endl;
+
+ Q_ASSERT(search_manager_);
+
+ ls_parent = search_manager_->linkStatus(url_base);
+ }
+
+ if(ls_parent)
+ checkRef(ls_parent);
+ else
+ {
+ url = KURL::fromPathOrURL(url.url().left(i_ref));
+ checkRef(url);
+ }
+}
+
+void LinkChecker::checkRef(KURL const& url)
+{
+ Q_ASSERT(search_manager_);
+
+ QString url_string = url.url();
+ KHTMLPart* html_part = search_manager_->htmlPart(url_string);
+ if(!html_part)
+ {
+ kdDebug() << "new KHTMLPart: " + url_string << endl;
+
+ html_part = new KHTMLPart();
+ html_part->setOnlyLocalReferences(true);
+
+ QString tmpFile;
+ if(KIO::NetAccess::download(url, tmpFile, 0))
+ {
+ QString doc_html = FileManager::read(tmpFile);
+ html_part->begin();
+ html_part->write(doc_html);
+ html_part->end();
+
+ KIO::NetAccess::removeTempFile(tmpFile);
+ }
+ else
+ {
+ kdDebug(23100) << KIO::NetAccess::lastErrorString() << endl;
+ }
+
+ search_manager_->addHtmlPart(url_string, html_part);
+ }
+
+ if(hasAnchor(html_part, linkStatus()->absoluteUrl().ref()))
+ {
+ linkstatus_->setStatusText("OK");
+ linkstatus_->setStatus(LinkStatus::SUCCESSFULL);
+ }
+ else
+ {
+ linkstatus_->setErrorOccurred(true);
+ linkstatus_->setError(i18n( "Link destination not found." ));
+ linkstatus_->setStatus(LinkStatus::BROKEN);
+ }
+
+ finnish();
+}
+
+void LinkChecker::checkRef(LinkStatus const* linkstatus_parent)
+{
+ Q_ASSERT(search_manager_);
+
+ QString url_string = linkstatus_parent->absoluteUrl().url();
+ KHTMLPart* html_part = search_manager_->htmlPart(url_string);
+ if(!html_part)
+ {
+ kdDebug() << "new KHTMLPart: " + url_string << endl;
+
+ html_part = new KHTMLPart();
+ html_part->setOnlyLocalReferences(true);
+
+ html_part->begin();
+ html_part->write(linkstatus_parent->docHtml());
+ html_part->end();
+
+ search_manager_->addHtmlPart(url_string, html_part);
+ }
+
+ if(hasAnchor(html_part, linkStatus()->absoluteUrl().ref()))
+ {
+ linkstatus_->setStatusText("OK");
+ linkstatus_->setStatus(LinkStatus::SUCCESSFULL);
+ }
+ else
+ {
+ linkstatus_->setErrorOccurred(true);
+ linkstatus_->setError(i18n( "Link destination not found." ));
+ linkstatus_->setStatus(LinkStatus::BROKEN);
+ }
+
+ finnish();
+}
+
+bool LinkChecker::hasAnchor(KHTMLPart* html_part, QString const& anchor)
+{
+ DOM::HTMLDocument htmlDocument = html_part->htmlDocument();
+ DOM::HTMLCollection anchors = htmlDocument.anchors();
+
+ DOM::DOMString name_ref(anchor);
+ Q_ASSERT(!name_ref.isNull());
+
+ DOM::Node node = anchors.namedItem(name_ref);
+ if(node.isNull())
+ {
+ node = htmlDocument.getElementById(name_ref);
+ }
+
+ if(!node.isNull())
+ return true;
+ else
+ return false;
+}
+
+void LinkChecker::killJob()
+{
+ if(!t_job_)
+ return;
+
+ KIO::TransferJob* aux = t_job_;
+ t_job_ = 0;
+ aux->disconnect(this);
+ aux->kill(true); // quietly
+}
+
+LinkStatus::Status LinkChecker::getHttpStatus() const
+{
+ QString status_code = QString::number(linkstatus_->httpHeader().statusCode());
+
+ if(status_code[0] == '2')
+ return LinkStatus::SUCCESSFULL;
+ else if(status_code[0] == '3')
+ return LinkStatus::HTTP_REDIRECTION;
+ else if(status_code[0] == '4')
+ return LinkStatus::HTTP_CLIENT_ERROR;
+ else if(status_code[0] == '5')
+ return LinkStatus::HTTP_SERVER_ERROR;
+ else
+ return LinkStatus::UNDETERMINED;
+}
+
+#include "linkchecker.moc"
diff --git a/klinkstatus/src/engine/linkchecker.h b/klinkstatus/src/engine/linkchecker.h
new file mode 100644
index 00000000..a992e5fd
--- /dev/null
+++ b/klinkstatus/src/engine/linkchecker.h
@@ -0,0 +1,128 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 LINKCHECKER_H
+#define LINKCHECKER_H
+
+#include <qobject.h>
+#include <qthread.h>
+#include <qstring.h>
+
+#include <kio/jobclasses.h>
+class KHTMLPart;
+
+#include "../parser/http.h"
+#include "linkstatus.h"
+class SearchManager;
+
+#include <iostream>
+using namespace std;
+
+/**
+@author Paulo Moura Guedes
+*/
+class LinkChecker : public QObject
+{
+ Q_OBJECT
+public:
+ LinkChecker(LinkStatus* linkstatus, int time_out = 50,
+ QObject *parent = 0, const char *name = 0);
+ ~LinkChecker();
+
+ //virtual void run();
+ void check();
+ void setSearchManager(SearchManager* search_manager);
+
+ LinkStatus const* linkStatus() const;
+
+ static bool hasAnchor(KHTMLPart* html_part, QString const& anchor);
+
+signals:
+
+ void transactionFinished(const LinkStatus * linkstatus,
+ LinkChecker * checker);
+ void jobFinnished(LinkChecker * checker);
+
+protected slots:
+
+ void slotData(KIO::Job *, const QByteArray &data);
+ void slotRedirection (KIO::Job *, const KURL &url);
+ void slotMimetype(KIO::Job *, const QString &type);
+ void slotResult(KIO::Job* job);
+ void slotTimeOut();
+
+protected:
+
+ void finnish();
+ HttpResponseHeader getHttpHeader(KIO::Job* job, bool remember_check = true);
+ void checkRef(); // #...
+
+private:
+
+ LinkStatus::Status getHttpStatus() const;
+ void checkRef(LinkStatus const* linkstatus_parent);
+ void checkRef(KURL const& url);
+ void killJob();
+ /**
+ * @param url
+ * @return false if the redirection was already checked by the search manager
+ */
+ bool processRedirection(KURL const& url);
+
+ void findDocumentCharset(QString const& data);
+
+private:
+
+ SearchManager* search_manager_;
+ LinkStatus* const linkstatus_;
+ KIO::TransferJob* t_job_;
+ int time_out_;
+ LinkChecker* checker_;
+ QString document_charset_;
+/* A redirection has appened, with the current URL. Several redirections
+ can happen until the final URL is reached.*/
+ bool redirection_;
+ KURL redirection_url_;
+ QString doc_html_;
+ bool header_checked_;
+ bool finnished_;
+ bool parsing_;
+
+ /**
+ * Whether the charset of the document is already checked.
+ * (e.g. <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>)
+ */
+ bool is_charset_checked_;
+ /**
+ * Wheter the page define the enconding (latin1, utf8, etc).
+ * According to the spec (http://www.w3.org/TR/html4/charset.html),
+ * it first check the server response and then the info in the html meta element.
+ */
+ bool has_defined_charset_;
+
+ static int count_; // debug attribute that counts how many links were checked
+};
+
+inline LinkStatus const* LinkChecker::linkStatus() const
+{
+ return linkstatus_;
+}
+
+
+#endif
diff --git a/klinkstatus/src/engine/linkfilter.cpp b/klinkstatus/src/engine/linkfilter.cpp
new file mode 100644
index 00000000..4d15f2e6
--- /dev/null
+++ b/klinkstatus/src/engine/linkfilter.cpp
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+#include "linkfilter.h"
+
+#include "linkstatus.h"
+
+
+LinkMatcher::LinkMatcher(QString const& text, ResultView::Status status)
+ : m_text(text), m_status(status)
+{
+}
+
+LinkMatcher::~LinkMatcher()
+{
+}
+
+bool LinkMatcher::matches(LinkStatus const& link ) const
+{
+/* kdDebug() << link.absoluteUrl().url() << endl;
+ kdDebug() << link.label() << endl;
+ kdDebug() << link.absoluteUrl().url().contains(m_text) << endl;
+ kdDebug() << link.label().contains(m_text) << endl;
+ */
+ return (link.absoluteUrl().url().contains(m_text, false) || link.label().contains(m_text, false)) &&
+ ResultView::displayableWithStatus(&link, m_status);
+}
+
+
+
diff --git a/klinkstatus/src/engine/linkfilter.h b/klinkstatus/src/engine/linkfilter.h
new file mode 100644
index 00000000..84da16cb
--- /dev/null
+++ b/klinkstatus/src/engine/linkfilter.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 LINKFILTER_H
+#define LINKFILTER_H
+
+#include "../ui/resultview.h"
+
+/**
+ @author Paulo Moura Guedes <moura@kdewebdev.org>
+*/
+class LinkMatcher
+{
+public:
+ LinkMatcher(QString const& text, ResultView::Status status);
+ ~LinkMatcher();
+
+ bool matches(LinkStatus const& link) const;
+
+ void setText(const QString& text) { m_text = text; }
+ QString text() const { return m_text; }
+
+ void setStatus(ResultView::Status status) { m_status = status; }
+ ResultView::Status status() const { return m_status; }
+
+ bool nullFilter() const { return m_text.isEmpty() && m_status == ResultView::none; }
+
+private:
+ QString m_text;
+ ResultView::Status m_status;
+};
+
+#endif
diff --git a/klinkstatus/src/engine/linkstatus.cpp b/klinkstatus/src/engine/linkstatus.cpp
new file mode 100644
index 00000000..c8b359ed
--- /dev/null
+++ b/klinkstatus/src/engine/linkstatus.cpp
@@ -0,0 +1,214 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include "linkstatus.h"
+#include "../parser/node.h"
+#include "../ui/treeview.h"
+
+#include <klocale.h>
+#include <kcharsets.h>
+
+#include <qdom.h>
+
+
+LinkStatus::~LinkStatus()
+{
+ //kdDebug(23100) << "|";
+
+ for(uint i = 0; i != children_nodes_.size(); ++i)
+ {
+ if(children_nodes_[i])
+ {
+ delete children_nodes_[i];
+ children_nodes_[i] = 0;
+ }
+ }
+
+ children_nodes_.clear();
+
+ if(isRedirection())
+ {
+ if(redirection_)
+ {
+ delete redirection_;
+ redirection_ = 0;
+ }
+ }
+}
+
+void LinkStatus::reset()
+{
+ depth_ = -1;
+ external_domain_depth_ = -1;
+ is_root_ = false;
+ error_occurred_ = false;
+ is_redirection_ = false;
+ checked_ = false;
+ only_check_header_ = true;
+ malformed_ = false;
+ Q_ASSERT(!node_);
+ has_base_URI_ = false;
+ label_ = "";
+ absolute_url_ = "";
+ doc_html_ = "";
+ http_header_ = HttpResponseHeader();
+ error_ = "";
+
+ for(uint i = 0; i != children_nodes_.size(); ++i)
+ {
+ if(children_nodes_[i])
+ {
+ delete children_nodes_[i];
+ children_nodes_[i] = 0;
+ }
+ }
+
+ children_nodes_.clear();
+
+ if(isRedirection())
+ {
+ if(redirection_)
+ {
+ delete redirection_;
+ redirection_ = 0;
+ }
+ }
+ Q_ASSERT(!parent_);
+ base_URI_ = "";
+}
+
+QString const LinkStatus::toString() const
+{
+ QString aux;
+
+ if(!is_root_)
+ {
+ Q_ASSERT(parent_);
+ aux += i18n( "Parent: %1" ).arg( parent()->absoluteUrl().prettyURL() ) + "\n";
+ }
+ Q_ASSERT(!original_url_.isNull());
+
+ aux += i18n( "URL: %1" ).arg( absoluteUrl().prettyURL() ) + "\n";
+ aux += i18n( "Original URL: %1" ).arg( originalUrl() ) + "\n";
+ if(node())
+ aux += i18n( "Node: %1" ).arg( node()->content() ) + "\n";
+
+ return aux;
+}
+
+
+LinkStatus* LinkStatus::lastRedirection(LinkStatus* ls)
+{
+ if(ls->isRedirection())
+ if(ls->redirection())
+ return lastRedirection(ls->redirection());
+ else
+ return ls;
+ else
+ return ls;
+}
+
+void LinkStatus::loadNode()
+{
+ Q_ASSERT(node_);
+
+ setOriginalUrl(node_->url());
+ setLabel(node_->linkLabel());
+
+ if(malformed())
+ {
+ setErrorOccurred(true);
+ setError(i18n( "Malformed" ));
+ setStatus(LinkStatus::MALFORMED);
+ kdDebug(23100) << "Malformed:" << endl;
+ kdDebug(23100) << "Node: " << node()->content() << endl;
+ //kdDebug(23100) << toString() << endl; // probable segfault
+ }
+}
+
+bool LinkStatus::malformed() const // don't inline please (#include "node.h")
+{
+ return (malformed_ || node_->malformed());
+}
+
+void LinkStatus::setChildrenNodes(vector<Node*> const& nodes) // don't inline please (#include "node.h")
+{
+ children_nodes_.reserve(nodes.size());
+ children_nodes_ = nodes;
+}
+
+void LinkStatus::setMalformed(bool flag)
+{
+ malformed_ = flag;
+ if(flag)
+ {
+ setErrorOccurred(true);
+ setError(i18n( "Malformed" ));
+ setStatus(LinkStatus::MALFORMED);
+ kdDebug(23100) << "Malformed!" << endl;
+ kdDebug(23100) << node()->content() << endl;
+ //kdDebug(23100) << toString() << endl; // probable segfault
+ }
+ else if(error() == i18n( "Malformed" ))
+ {
+ setErrorOccurred(false);
+ setError("");
+ setStatus(LinkStatus::UNDETERMINED);
+ }
+}
+
+void LinkStatus::save(QDomElement& element) const
+{
+ QDomElement child_element = element.ownerDocument().createElement("link");
+
+ // <url>
+ QDomElement tmp_1 = element.ownerDocument().createElement("url");
+ tmp_1.appendChild(element.ownerDocument().createTextNode(absoluteUrl().prettyURL()));
+ child_element.appendChild(tmp_1);
+
+ // <status>
+ tmp_1 = element.ownerDocument().createElement("status");
+ tmp_1.setAttribute("broken",
+ ResultView::displayableWithStatus(this, ResultView::bad) ?
+ "true" : "false");
+ tmp_1.appendChild(element.ownerDocument().createTextNode(statusText()));
+ child_element.appendChild(tmp_1);
+
+ // <label>
+ tmp_1 = element.ownerDocument().createElement("label");
+ tmp_1.appendChild(element.ownerDocument().createTextNode(KCharsets::resolveEntities(label())));
+ child_element.appendChild(tmp_1);
+
+ // <referers>
+ tmp_1 = element.ownerDocument().createElement("referrers");
+
+ for(QValueVector<KURL>::const_iterator it = referrers_.begin(); it != referrers_.end(); ++it)
+ {
+ QDomElement tmp_2 = element.ownerDocument().createElement("url");
+ tmp_2.appendChild(element.ownerDocument().createTextNode(it->prettyURL()));
+
+ tmp_1.appendChild(tmp_2);
+ }
+ Q_ASSERT(!referrers_.isEmpty());
+ child_element.appendChild(tmp_1);
+
+ element.appendChild(child_element);
+}
+
diff --git a/klinkstatus/src/engine/linkstatus.h b/klinkstatus/src/engine/linkstatus.h
new file mode 100644
index 00000000..e7567460
--- /dev/null
+++ b/klinkstatus/src/engine/linkstatus.h
@@ -0,0 +1,187 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 LINKSTATUS_H
+#define LINKSTATUS_H
+
+#include "../parser/http.h"
+#include "../utils/mvector.h"
+
+#include <kurl.h>
+#include <klocale.h>
+#include <kdebug.h>
+class TreeView;
+class TreeViewItem;
+
+#include <qstring.h>
+#include <qobject.h>
+#include <qvaluevector.h>
+class QDomElement;
+
+#include <vector>
+#include <iostream>
+
+using namespace std;
+
+
+class Node;
+
+class LinkStatus
+{
+public:
+
+ enum Status {
+ UNDETERMINED,
+ SUCCESSFULL,
+ BROKEN,
+ HTTP_REDIRECTION,
+ HTTP_CLIENT_ERROR,
+ HTTP_SERVER_ERROR,
+ TIMEOUT,
+ NOT_SUPPORTED,
+ MALFORMED
+ };
+
+ LinkStatus();
+ LinkStatus(KURL const& absolute_url);
+ LinkStatus(Node* node, LinkStatus* parent);
+ ~LinkStatus();
+
+ void save(QDomElement& element) const;
+
+ void reset();
+ void setRootUrl(KURL const& url);
+ void setStatus(Status status);
+ void setDepth(uint depth);
+ void setParent(LinkStatus* parent);
+ void setOriginalUrl(QString const& url_original);
+ void setLabel(QString const& label);
+ void setAbsoluteUrl(KURL const& url_absoluto);
+ void setDocHtml(QString const& doc_html);
+ void setHttpHeader(HttpResponseHeader const& cabecalho_http);
+ void setStatusText(QString const& statusText); // FIXME Legacy. This should be eliminated in favor of LinkStatus::Status
+ void setError(QString const& error);
+ void setIsRoot(bool flag);
+ void setErrorOccurred(bool houve_error);
+ void setIsRedirection(bool e_redirection);
+ void setRedirection(LinkStatus* redirection);
+ void setNode(Node* node);
+ void setChildrenNodes(vector<Node*> const& nodes);
+ void addChildNode(Node* node);
+ void reserveMemoryForChildrenNodes(int n);
+ void setChecked(bool flag);
+ void setExternalDomainDepth(int p);
+ void setOnlyCheckHeader(bool flag);
+ void setMalformed(bool flag = true);
+ void setHasBaseURI(bool flag = true);
+ void setHasHtmlDocTitle(bool flag = true);
+ void setBaseURI(KURL const& base_url);
+ void setHtmlDocTitle(QString const& title);
+ void setIgnored(bool flag = true);
+ void setMimeType(QString const& mimetype);
+ void setIsErrorPage(bool flag);
+ void setIsLocalRestrict(bool flag);
+ void setTreeViewItem(TreeViewItem* tree_view_item);
+ void addReferrer(KURL const& url);
+
+ KURL const& rootUrl() const;
+ Status const& status() const;
+ uint depth() const;
+ bool local() const; // linkstatus.paradigma.co.pt == paradigma.co.pt
+ bool isLocalRestrict() const; // linkstatus.paradigma.co.pt != paradigma.co.pt
+ LinkStatus const* parent() const;
+ QString const& originalUrl() const;
+ QString const& label() const;
+ KURL const& absoluteUrl() const;
+ QString const& docHtml() const;
+ HttpResponseHeader const& httpHeader() const;
+ HttpResponseHeader& httpHeader();
+ QString statusText() const; // FIXME Legacy. This should be eliminated in favor of LinkStatus::Status
+ QString const& error() const;
+ bool isRoot() const;
+ bool errorOccurred() const;
+ bool isRedirection() const;
+ LinkStatus* redirection() const;
+ Node* node() const;
+ vector<Node*> const& childrenNodes() const;
+ QString const toString() const;
+ bool checked() const;
+ int externalDomainDepth() const;
+ bool onlyCheckHeader() const;
+ bool malformed() const;
+ bool hasBaseURI() const;
+ bool hasHtmlDocTitle() const;
+ KURL const& baseURI() const;
+ QString const& htmlDocTitle() const;
+ bool ignored() const;
+ bool redirectionExists(KURL const& url) const; // to avoid cyclic links
+ QString mimeType() const;
+ bool isErrorPage() const;
+ TreeViewItem* treeViewItem() const;
+ QValueVector<KURL> const& referrers() const;
+
+ static LinkStatus* lastRedirection(LinkStatus* ls);
+
+private:
+
+ /**
+ Load some atributes in function of his parent node.
+ */
+ void loadNode();
+
+private:
+
+ KURL root_url_; // The URL which made the search start
+ Status status_;
+ int depth_;
+ int external_domain_depth_; // Para se poder escolher explorar domains diferentes ate n depth
+ QString original_url_;
+ QString label_;
+ KURL absolute_url_;
+ QString doc_html_;
+ HttpResponseHeader http_header_;
+ QString status_text_; // FIXME Legacy. This should be eliminated in favor of LinkStatus::Status
+ QString error_;
+ bool is_root_;
+ bool error_occurred_;
+ bool is_redirection_;
+ vector<Node*> children_nodes_;
+ LinkStatus* parent_;
+ LinkStatus* redirection_;
+ bool checked_;
+ bool only_check_header_;
+ bool malformed_;
+ Node* node_;
+ bool has_base_URI_;
+ bool has_html_doc_title_;
+ KURL base_URI_;
+ QString html_doc_title_;
+ bool ignored_;
+ QString mimetype_;
+ bool is_error_page_;
+ bool is_local_restrict_;
+ TreeViewItem* tree_view_item_;
+ QValueVector<KURL> referrers_;
+};
+
+#include "../parser/url.h"
+#include "linkstatus_impl.h"
+
+#endif
diff --git a/klinkstatus/src/engine/linkstatus_impl.h b/klinkstatus/src/engine/linkstatus_impl.h
new file mode 100644
index 00000000..3359664c
--- /dev/null
+++ b/klinkstatus/src/engine/linkstatus_impl.h
@@ -0,0 +1,417 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+inline LinkStatus::LinkStatus()
+ : status_(LinkStatus::UNDETERMINED), depth_(-1), external_domain_depth_(-1), is_root_(false),
+ error_occurred_(false), is_redirection_(false), parent_(0), redirection_(0), checked_(false),
+ only_check_header_(true), malformed_(false),
+ node_(0), has_base_URI_(false), has_html_doc_title_(false), ignored_(false),
+ mimetype_(""), is_error_page_(false), tree_view_item_(0)
+{}
+
+inline LinkStatus::LinkStatus(KURL const& absolute_url)
+ : status_(LinkStatus::UNDETERMINED), depth_(-1), external_domain_depth_(-1), is_root_(false),
+ error_occurred_(false), is_redirection_(false), parent_(0), redirection_(0), checked_(false),
+ only_check_header_(true), malformed_(false),
+ node_(0), has_base_URI_(false), has_html_doc_title_(false), ignored_(false),
+ mimetype_(""), is_error_page_(false), tree_view_item_(0)
+{
+ setAbsoluteUrl(absolute_url);
+}
+
+inline LinkStatus::LinkStatus(Node* node, LinkStatus* parent)
+ : status_(LinkStatus::UNDETERMINED), depth_(-1), external_domain_depth_(-1), is_root_(false),
+ error_occurred_(false), is_redirection_(false), parent_(0), redirection_(0), checked_(false),
+ only_check_header_(true), malformed_(false),
+ node_(node), has_base_URI_(false), has_html_doc_title_(false), ignored_(false),
+ mimetype_(""), is_error_page_(false), tree_view_item_(0)
+{
+ loadNode();
+
+ setDepth(parent->depth() + 1);
+ setParent(parent);
+ setRootUrl(parent->rootUrl());
+}
+
+inline void LinkStatus::setRootUrl(KURL const& url)
+{
+ root_url_ = url;
+}
+
+inline void LinkStatus::setStatus(Status status)
+{
+ status_ = status;
+}
+
+inline void LinkStatus::setDepth(uint depth)
+{
+ depth_ = depth;
+}
+
+inline void LinkStatus::setParent(LinkStatus* parent)
+{
+ Q_ASSERT(parent);
+
+ parent_ = parent;
+ addReferrer(parent->absoluteUrl());
+}
+
+inline void LinkStatus::setAbsoluteUrl(KURL const& url_absoluto)
+{
+ absolute_url_ = url_absoluto;
+}
+
+inline void LinkStatus::setOriginalUrl(QString const& url_original)
+{
+ original_url_ = url_original;
+}
+
+inline void LinkStatus::setLabel(QString const& label)
+{
+ label_ = label;
+}
+
+inline void LinkStatus::setDocHtml(QString const& doc_html)
+{
+ Q_ASSERT(!doc_html.isEmpty());
+ doc_html_ = doc_html;
+}
+
+inline void LinkStatus::setHttpHeader(HttpResponseHeader const& cabecalho_http)
+{
+ http_header_ = cabecalho_http;
+}
+
+inline void LinkStatus::setStatusText(QString const& status)
+{
+ Q_ASSERT(!status.isEmpty());
+ status_text_ = status;
+}
+
+inline void LinkStatus::setError(QString const& error)
+{
+ Q_ASSERT(!error.isEmpty());
+ error_ = error;
+}
+
+inline void LinkStatus::setErrorOccurred(bool houve_error)
+{
+ error_occurred_ = houve_error;
+}
+
+inline void LinkStatus::setIsRoot(bool flag)
+{
+ is_root_ = flag;
+ label_ = i18n("ROOT");
+}
+
+inline void LinkStatus::setRedirection(LinkStatus* redirection)
+{
+ Q_ASSERT(redirection != NULL);
+ Q_ASSERT(isRedirection());
+ redirection_ = redirection;
+}
+
+inline void LinkStatus::setIsRedirection(bool e_redirection)
+{
+ is_redirection_ = e_redirection;
+}
+
+inline void LinkStatus::addChildNode(Node* node)
+{
+ children_nodes_.push_back(node);
+}
+
+inline void LinkStatus::reserveMemoryForChildrenNodes(int n)
+{
+ Q_ASSERT(n > 0);
+ children_nodes_.reserve(n);
+}
+
+inline void LinkStatus::setChecked(bool flag)
+{
+ checked_ = flag;
+}
+
+inline void LinkStatus::setExternalDomainDepth(int p)
+{
+ Q_ASSERT(p >= -1);
+ external_domain_depth_ = p;
+}
+
+inline void LinkStatus::setOnlyCheckHeader(bool flag)
+{
+ only_check_header_= flag;
+}
+
+inline void LinkStatus::setHasBaseURI(bool flag)
+{
+ has_base_URI_ = flag;
+}
+
+inline void LinkStatus::setHasHtmlDocTitle(bool flag)
+{
+ has_html_doc_title_ = flag;
+}
+
+inline void LinkStatus::setBaseURI(KURL const& base_url)
+{
+ if(!base_url.isValid())
+ {
+ kdWarning(23100) << "base url not valid: " << endl
+ << "parent: " << parent()->absoluteUrl().prettyURL() << endl
+ << "url: " << absoluteUrl().prettyURL() << endl
+ << "base url resolved: " << base_url.prettyURL() << endl;
+ }
+
+ Q_ASSERT(base_url.isValid());
+ has_base_URI_ = true;
+ base_URI_ = base_url;
+}
+
+inline void LinkStatus::setHtmlDocTitle(QString const& title)
+{
+ if(title.isNull() || title.isEmpty())
+ {
+ kdError(23100) << "HTML doc title is null or empty!" << endl
+ << toString() << endl;
+ }
+ Q_ASSERT(!title.isNull() && !title.isEmpty());
+
+ has_html_doc_title_ = true;
+ html_doc_title_ = title;
+}
+
+inline void LinkStatus::setIgnored(bool flag)
+{
+ ignored_ = flag;
+}
+
+inline void LinkStatus::setMimeType(QString const& mimetype)
+{
+ Q_ASSERT(!mimetype.isNull() && !mimetype.isEmpty());
+ mimetype_ = mimetype;
+}
+
+inline void LinkStatus::setIsErrorPage(bool flag)
+{
+ is_error_page_ = flag;
+}
+
+inline void LinkStatus::setIsLocalRestrict(bool flag)
+{
+ is_local_restrict_ = flag;
+}
+
+inline void LinkStatus::setTreeViewItem(TreeViewItem* tree_view_item)
+{
+ Q_ASSERT(tree_view_item);
+ tree_view_item_ = tree_view_item;
+}
+
+inline void LinkStatus::addReferrer(KURL const& url)
+{
+ Q_ASSERT(url.isValid());
+
+ referrers_.push_back(url);
+}
+
+
+
+
+inline KURL const& LinkStatus::rootUrl() const
+{
+ return root_url_;
+}
+
+inline LinkStatus::Status const& LinkStatus::status() const
+{
+ return status_;
+}
+
+inline uint LinkStatus::depth() const
+{
+ return depth_;
+}
+
+inline bool LinkStatus::local() const
+{
+ return external_domain_depth_ == -1;
+}
+
+inline bool LinkStatus::isLocalRestrict() const
+{
+ return is_local_restrict_;
+}
+
+inline LinkStatus const* LinkStatus::parent() const
+{
+ return parent_;
+}
+
+inline QString const& LinkStatus::originalUrl() const
+{
+ return original_url_;
+}
+
+inline QString const& LinkStatus::label() const
+{
+ return label_;
+}
+
+inline KURL const& LinkStatus::absoluteUrl() const
+{
+ return absolute_url_;
+}
+
+inline QString const& LinkStatus::docHtml() const
+{
+ return doc_html_;
+}
+
+inline HttpResponseHeader const& LinkStatus::httpHeader() const
+{
+ return http_header_;
+}
+
+inline HttpResponseHeader& LinkStatus::httpHeader()
+{
+ return http_header_;
+}
+
+inline QString LinkStatus::statusText() const
+{
+ if(errorOccurred())
+ return error();
+ else if(!absoluteUrl().protocol().startsWith("http"))
+ return status_text_;
+ else
+ {
+ QString string_code = QString::number(httpHeader().statusCode());
+ if(absoluteUrl().hasRef()) // ref URL
+ return status_text_;
+ else if(string_code == "200"/* or string_code == "304"*/)
+ return "OK";
+ else
+ return string_code;
+ }
+}
+
+inline QString const& LinkStatus::error() const
+{
+ return error_;
+}
+
+inline bool LinkStatus::isRoot() const
+{
+ return is_root_;
+}
+
+inline bool LinkStatus::errorOccurred() const
+{
+ return error_occurred_;
+}
+
+inline bool LinkStatus::isRedirection() const
+{
+ return is_redirection_;
+}
+
+inline LinkStatus* LinkStatus::redirection() const
+{
+ Q_ASSERT(isRedirection());
+
+ return redirection_;
+}
+
+inline Node* LinkStatus::node() const
+{
+ //Q_ASSERT(node_);
+ return node_;
+}
+
+inline vector<Node*> const& LinkStatus::childrenNodes() const
+{
+ return children_nodes_;
+}
+
+inline bool LinkStatus::checked() const
+{
+ return checked_;
+}
+
+inline int LinkStatus::externalDomainDepth() const
+{
+ return external_domain_depth_;
+}
+
+inline bool LinkStatus::onlyCheckHeader() const
+{
+ return only_check_header_;
+}
+
+inline bool LinkStatus::hasBaseURI() const
+{
+ return has_base_URI_;
+}
+
+inline bool LinkStatus::hasHtmlDocTitle() const
+{
+ return has_html_doc_title_;
+}
+
+inline KURL const& LinkStatus::baseURI() const
+{
+ Q_ASSERT(hasBaseURI());
+ return base_URI_;
+}
+
+inline QString const& LinkStatus::htmlDocTitle() const
+{
+ Q_ASSERT(has_html_doc_title_);
+ return html_doc_title_;
+}
+
+inline bool LinkStatus::ignored() const
+{
+ return ignored_;
+}
+
+inline QString LinkStatus::mimeType() const
+{
+ Q_ASSERT(!mimetype_.isNull());
+ return mimetype_;
+}
+
+inline bool LinkStatus::isErrorPage() const
+{
+ return is_error_page_;
+}
+
+inline TreeViewItem* LinkStatus::treeViewItem() const
+{
+ return tree_view_item_;
+}
+
+inline QValueVector<KURL> const& LinkStatus::referrers() const
+{
+ return referrers_;
+}
+
diff --git a/klinkstatus/src/engine/searchmanager.cpp b/klinkstatus/src/engine/searchmanager.cpp
new file mode 100644
index 00000000..81562a7a
--- /dev/null
+++ b/klinkstatus/src/engine/searchmanager.cpp
@@ -0,0 +1,916 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <khtml_part.h>
+#include <kprotocolmanager.h>
+
+#include <qstring.h>
+#include <qvaluevector.h>
+#include <qdom.h>
+
+#include <iostream>
+#include <unistd.h>
+
+#include "searchmanager.h"
+#include "../parser/mstring.h"
+#include "../cfg/klsconfig.h"
+
+
+SearchManager::SearchManager(int max_simultaneous_connections, int time_out,
+ QObject *parent, const char *name)
+ : QObject(parent, name),
+ max_simultaneous_connections_(max_simultaneous_connections), has_document_root_(false),
+ depth_(-1), current_depth_(0), external_domain_depth_(0),
+ current_node_(0), current_index_(0), links_being_checked_(0),
+ finished_connections_(max_simultaneous_connections_),
+ maximum_current_connections_(-1), general_domain_(false),
+ checked_general_domain_(false), time_out_(time_out), current_connections_(0),
+ send_identification_(true), canceled_(false), searching_(false), checked_links_(0), ignored_links_(0),
+ check_parent_dirs_(true), check_external_links_(true), check_regular_expressions_(false),
+ number_of_level_links_(0), number_of_links_to_check_(0)
+{
+ root_.setIsRoot(true);
+
+ if (KLSConfig::userAgent().isEmpty()) {
+ KLSConfig::setUserAgent(KProtocolManager::defaultUserAgent());
+ }
+ user_agent_ = KLSConfig::userAgent();
+}
+
+void SearchManager::reset()
+{
+ kdDebug(23100) << "SearchManager::reset()" << endl;
+
+ //Q_ASSERT(not links_being_checked_);
+
+ root_.reset();
+ cleanItems();
+ depth_ = -1;
+ current_depth_ = 0;
+ current_node_ = 0;
+ current_index_ = 0;
+ finished_connections_ = max_simultaneous_connections_;
+ domain_ = "";
+ maximum_current_connections_ = -1;
+ general_domain_ = false;
+ checked_general_domain_ = false;
+ check_regular_expressions_ = false;
+ current_connections_ = 0;
+ canceled_ = false;
+ searching_ = false;
+ checked_links_ = 0;
+ if(KLSConfig::userAgent().isEmpty()) {
+ KLSConfig::setUserAgent(KProtocolManager::defaultUserAgent());
+ }
+ user_agent_ = KLSConfig::userAgent();
+
+ removeHtmlParts();
+}
+
+SearchManager::~SearchManager()
+{
+ reset();
+}
+
+void SearchManager::cleanItems()
+{
+ for(uint i = 0; i != search_results_.size(); ++i)
+ {
+ for(uint j = 0; j != search_results_[i].size() ; ++j)
+ {
+ for(uint l = 0; l != (search_results_[i])[j].size(); ++l)
+ {
+ if(((search_results_[i])[j])[l] != 0)
+ {
+ delete ((search_results_[i])[j])[l];
+ ((search_results_[i])[j])[l] = 0;
+ }
+ else
+ kdDebug(23100) << "LinkStatus NULL!!" << endl;
+ }
+ search_results_[i][j].clear();
+ }
+ search_results_[i].clear();
+ }
+ search_results_.clear();
+ kdDebug(23100) << endl;
+}
+
+void SearchManager::startSearch(KURL const& root, SearchMode const& modo)
+{
+ canceled_ = false;
+
+ //time_.restart();
+ time_.start();
+
+ Q_ASSERT(root.isValid());
+ //Q_ASSERT(root.protocol() == "http" || root.protocol() == "https");
+
+ if(root.hasHost() && (domain_.isNull() || domain_.isEmpty()))
+ {
+ setDomain(root.host() + root.directory());
+ kdDebug(23100) << "Domain: " << domain_ << endl;
+ }
+ root_.setIsRoot(true);
+ root_.setDepth(0);
+ root_.setOriginalUrl(root.prettyURL());
+ root_.setAbsoluteUrl(root);
+ root_.setOnlyCheckHeader(false);
+ root_.setRootUrl(root);
+
+ search_mode_ = modo;
+ if(modo == depth)
+ Q_ASSERT(depth_ != -1);
+ else if(modo == domain)
+ Q_ASSERT(depth_ == -1);
+ else
+ Q_ASSERT(depth_ != -1);
+
+ searching_ = true;
+
+ //Q_ASSERT(domain_ != QString::null);
+ checkRoot();
+}
+
+void SearchManager::resume()
+{
+ searching_ = true;
+ canceled_ = false;
+ continueSearch();
+}
+
+void SearchManager::finnish()
+{
+ searching_ = false;
+ while(links_being_checked_)
+ {
+ kdDebug(23100) << "links_being_checked_: " << links_being_checked_ << endl;
+ sleep(1);
+ }
+ emit signalSearchFinished();
+}
+
+void SearchManager::pause()
+{
+ searching_ = false;
+ while(links_being_checked_)
+ {
+ kdDebug(23100) << "links_being_checked_: " << links_being_checked_ << endl;
+ sleep(1);
+ }
+ emit signalSearchPaused();
+}
+
+void SearchManager::cancelSearch()
+{
+ canceled_ = true;
+}
+
+void SearchManager::checkRoot()
+{
+ LinkChecker* checker = new LinkChecker(&root_, time_out_, this, "link_checker");
+ checker->setSearchManager(this);
+
+ connect(checker, SIGNAL(transactionFinished(const LinkStatus *, LinkChecker *)),
+ this, SLOT(slotRootChecked(const LinkStatus *, LinkChecker *)));
+ /*
+ connect(checker, SIGNAL(jobFinnished(LinkChecker *)),
+ this, SLOT(slotLinkCheckerFinnished(LinkChecker *)));
+ */
+ checker->check();
+}
+
+void SearchManager::slotRootChecked(const LinkStatus * link, LinkChecker * checker)
+{
+ kdDebug(23100) << "SearchManager::slotRootChecked:" << endl;
+ kdDebug(23100) << link->absoluteUrl().url() << " -> " <<
+ LinkStatus::lastRedirection(&root_)->absoluteUrl().url() << endl;
+
+ Q_ASSERT(checked_links_ == 0);
+ Q_ASSERT(search_results_.size() == 0);
+
+ ++checked_links_;
+ //kdDebug(23100) << "++checked_links_: SearchManager::slotRootChecked" << endl;
+ emit signalRootChecked(link, checker);
+
+ if(search_mode_ != depth || depth_ > 0)
+ {
+ current_depth_ = 1;
+
+ vector<LinkStatus*> no = children(LinkStatus::lastRedirection(&root_));
+
+ emit signalLinksToCheckTotalSteps(no.size());
+
+ vector< vector<LinkStatus*> > nivel;
+ nivel.push_back(no);
+
+ search_results_.push_back(nivel);
+
+ if(search_results_.size() != 1)
+ {
+ kdDebug(23100) << "search_results_.size() != 1:" << endl;
+ kdDebug(23100) << "size: " << search_results_.size() << endl;
+ }
+ Q_ASSERT(search_results_.size() == 1);
+
+ if(no.size() > 0)
+ {
+ startSearch();
+ }
+ else
+ {
+ kdDebug(23100) << "SearchManager::slotRootChecked#1" << endl;
+ finnish();
+ }
+ }
+
+ else
+ {
+ Q_ASSERT(search_results_.size() == 0);
+ kdDebug(23100) << "SearchManager::slotRootChecked#2" << endl;
+ finnish();
+ }
+
+ delete checker;
+ checker = 0;
+}
+
+vector<LinkStatus*> SearchManager::children(LinkStatus* link)
+{
+ vector<LinkStatus*> children;
+
+ if(!link || link->absoluteUrl().hasRef())
+ return children;
+
+ vector<Node*> const& nodes = link->childrenNodes();
+
+ int count = 0;
+ for(uint i = 0; i != nodes.size(); ++i)
+ {
+ ++count;
+
+ Node* node = nodes[i];
+ KURL url;
+ if(node->url().isEmpty())
+ url = "";
+ else
+ url = Url::normalizeUrl(node->url(), *link, documentRoot().path());
+
+ if( (node->isLink() &&
+ checkable(url, *link) &&
+ !Url::existUrl(url, children) &&
+ !node->url().isEmpty())
+ ||
+ node->malformed() )
+ {
+ LinkStatus* ls = new LinkStatus(node, link);
+ ls->setAbsoluteUrl(url);
+
+ if(localDomain(ls->absoluteUrl()))
+ ls->setExternalDomainDepth(-1);
+ else
+ ls->setExternalDomainDepth(link->externalDomainDepth() + 1);
+
+ //ls->setIsLocalRestrict(localDomain(url));
+ ls->setIsLocalRestrict(ls->local()); // @todo clean this nonsense
+
+ if(!validUrl(url)) {
+ ls->setMalformed(true);
+ ls->setErrorOccurred(true);
+ }
+
+ ls->setOnlyCheckHeader(onlyCheckHeader(ls));
+
+ if(link->externalDomainDepth() > external_domain_depth_)
+ {
+ kdDebug(23100) << "link->externalDomainDepth() > external_domain_depth_: "
+ << link->externalDomainDepth() << endl;
+ kdDebug(23100) << "link: " << endl << link->toString() << endl;
+ kdDebug(23100) << "child: " << endl << ls->toString() << endl;
+ }
+ Q_ASSERT(link->externalDomainDepth() <= external_domain_depth_);
+
+ children.push_back(ls);
+ }
+ if(count == 50)
+ {
+ kapp->processEvents();
+ count = 0;
+ }
+ }
+
+ return children;
+}
+
+bool SearchManager::existUrl(KURL const& url, KURL const& url_parent) const
+{
+ if(url.prettyURL().isEmpty() || root_.originalUrl() == url.prettyURL())
+ return true;
+
+ for(uint i = 0; i != search_results_.size(); ++i)
+ for(uint j = 0; j != search_results_[i].size(); ++j)
+ for(uint l = 0; l != (search_results_[i])[j].size(); ++l)
+ {
+ LinkStatus* tmp = search_results_[i][j][l];
+ Q_ASSERT(tmp);
+ if(tmp->absoluteUrl() == url)
+ { // URL exists
+ QValueVector<KURL> referrers(tmp->referrers());
+
+ // Add new referrer
+ for(uint i = 0; i != referrers.size(); ++i)
+ {
+ if(referrers[i] == url_parent)
+ return true;
+ }
+ tmp->addReferrer(url_parent);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+LinkStatus const* SearchManager::linkStatus(QString const& s_url) const
+{
+ Q_ASSERT(!s_url.isEmpty());
+
+ if(root_.absoluteUrl().url() == s_url)
+ return &root_;
+
+ int count = 0;
+ for(uint i = 0; i != search_results_.size(); ++i)
+ for(uint j = 0; j != search_results_[i].size(); ++j)
+ for(uint l = 0; l != (search_results_[i])[j].size(); ++l)
+ {
+ ++count;
+
+ LinkStatus* ls = search_results_[i][j][l];
+ Q_ASSERT(ls);
+ if(ls->absoluteUrl().url() == s_url && ls->checked())
+ return ls;
+
+ if(count == 50)
+ {
+ count = 0;
+ kapp->processEvents();
+ }
+
+ }
+
+ return 0;
+}
+
+
+void SearchManager::startSearch()
+{
+ Q_ASSERT(current_depth_ == 1);
+ Q_ASSERT(search_results_[current_depth_ - 1].size() == 1);
+ Q_ASSERT(current_node_ == 0);
+
+ if( (int)current_depth_ <= depth_ || search_mode_ != depth )
+ checkVectorLinks(nodeToAnalize());
+ else
+ {
+ kdDebug(23100) << "Search Finished! (SearchManager::comecaPesquisa)" << endl;
+ finnish();
+ }
+}
+
+void SearchManager::continueSearch()
+{
+ Q_ASSERT(!links_being_checked_);
+
+ vector<LinkStatus*> const& no = nodeToAnalize();
+
+ if((uint)current_index_ < no.size())
+ checkVectorLinks(no);
+
+ else
+ {
+ current_index_ = 0;
+ kdDebug(23100) << "Next node_____________________\n\n";
+ ++current_node_;
+ if( (uint)current_node_ < (search_results_[current_depth_ - 1]).size() )
+ checkVectorLinks(nodeToAnalize());
+ else
+ {
+ kdDebug(23100) << "Next Level_____________________________________________________________________________________\n\n\n";
+ if(search_mode_ == SearchManager::domain ||
+ current_depth_ < depth_)
+ {
+ current_node_ = 0;
+ ++current_depth_;
+
+ addLevel();
+
+ if( (uint)current_depth_ == search_results_.size() )
+ checkVectorLinks(nodeToAnalize());
+ else
+ {
+ kdDebug(23100) << "Search Finished! (SearchManager::continueSearch#1)" << endl;
+ finnish();
+ }
+ }
+ else
+ {
+ kdDebug(23100) << "Search Finished! (SearchManager::continueSearch#2)" << endl;
+ finnish();
+ }
+ }
+ }
+}
+
+vector<LinkStatus*> const& SearchManager::nodeToAnalize() const
+{
+ Q_ASSERT( (uint)current_depth_ == search_results_.size() );
+ Q_ASSERT( (uint)current_node_ < (search_results_[current_depth_ - 1]).size() );
+
+ return (search_results_[current_depth_ - 1])[current_node_];
+}
+
+void SearchManager::checkVectorLinks(vector<LinkStatus*> const& links)
+{
+ checkLinksSimultaneously(chooseLinks(links));
+}
+
+vector<LinkStatus*> SearchManager::chooseLinks(vector<LinkStatus*> const& links)
+{
+ vector<LinkStatus*> escolha;
+ for(int i = 0; i != max_simultaneous_connections_; ++i)
+ {
+ if((uint)current_index_ < links.size())
+ escolha.push_back(links[current_index_++]);
+ }
+ return escolha;
+}
+
+void SearchManager::checkLinksSimultaneously(vector<LinkStatus*> const& links)
+{
+ Q_ASSERT(finished_connections_ <= max_simultaneous_connections_);
+ finished_connections_ = 0;
+ links_being_checked_ = 0;
+ maximum_current_connections_ = -1;
+
+ if(links.size() < (uint)max_simultaneous_connections_)
+ maximum_current_connections_ = links.size();
+ else
+ maximum_current_connections_ = max_simultaneous_connections_;
+
+ for(uint i = 0; i != links.size(); ++i)
+ {
+ LinkStatus* ls(links[i]);
+ Q_ASSERT(ls);
+
+ QString protocol = ls->absoluteUrl().protocol();
+
+ ++links_being_checked_;
+ Q_ASSERT(links_being_checked_ <= max_simultaneous_connections_);
+
+ if(ls->malformed())
+ {
+ Q_ASSERT(ls->errorOccurred());
+ Q_ASSERT(ls->status() == LinkStatus::MALFORMED);
+
+ ls->setChecked(true);
+ slotLinkChecked(ls, 0);
+ }
+
+ else if(ls->absoluteUrl().prettyURL().contains("javascript:", false))
+ {
+ ++ignored_links_;
+ ls->setIgnored(true);
+ ls->setErrorOccurred(true);
+ ls->setError(i18n( "Javascript not supported" ));
+ ls->setStatus(LinkStatus::NOT_SUPPORTED);
+ ls->setChecked(true);
+ slotLinkChecked(ls, 0);
+ }
+ /*
+ else if(!(protocol == "http" || protocol == "https"))
+ {
+ ++ignored_links_;
+ ls->setIgnored(true);
+ ls->setErrorOccurred(true);
+ ls->setError(i18n("Protocol %1 not supported").arg(protocol));
+ ls->setStatus(LinkStatus::MALFORMED);
+ ls->setChecked(true);
+ slotLinkChecked(ls, 0);
+ }
+ */
+ else
+ {
+ LinkChecker* checker = new LinkChecker(ls, time_out_, this, "link_checker");
+ checker->setSearchManager(this);
+
+ connect(checker, SIGNAL(transactionFinished(const LinkStatus *, LinkChecker *)),
+ this, SLOT(slotLinkChecked(const LinkStatus *, LinkChecker *)));
+ /*
+ connect(checker, SIGNAL(jobFinnished(LinkChecker *)),
+ this, SLOT(slotLinkCheckerFinnished(LinkChecker *)));
+ */
+ checker->check();
+ }
+ }
+}
+
+void SearchManager::slotLinkChecked(const LinkStatus * link, LinkChecker * checker)
+{
+ kdDebug(23100) << "SearchManager::slotLinkChecked:" << endl;
+// kdDebug(23100) << link->absoluteUrl().url() << " -> " <<
+// LinkStatus::lastRedirection((const_cast<LinkStatus*> (link)))->absoluteUrl().url() << endl;
+
+ Q_ASSERT(link);
+ emit signalLinkChecked(link, checker);
+ ++checked_links_;
+ ++finished_connections_;
+ --links_being_checked_;
+
+ if(links_being_checked_ < 0)
+ kdDebug(23100) << link->toString() << endl;
+ Q_ASSERT(links_being_checked_ >= 0);
+
+ if(canceled_ && searching_ && !links_being_checked_)
+ {
+ pause();
+ }
+
+ else if(!canceled_ && finished_connections_ == maximumCurrentConnections() )
+ {
+ continueSearch();
+ return;
+ }
+ /*
+ delete checker;
+ checker = 0;
+ */
+}
+
+void SearchManager::addLevel()
+{
+ search_results_.push_back(vector< vector <LinkStatus*> >());
+ vector< vector <LinkStatus*> >& ultimo_nivel(search_results_[search_results_.size() - 2]);
+
+ number_of_level_links_ = 0;
+ number_of_links_to_check_ = 0;
+ uint end = ultimo_nivel.size();
+
+ for(uint i = 0; i != end; ++i) // nodes
+ {
+ uint end_sub1 = ultimo_nivel[i].size();
+ for(uint j = 0; j != end_sub1; ++j) // links
+ ++number_of_level_links_;
+ }
+
+ if(number_of_level_links_)
+ emit signalAddingLevelTotalSteps(number_of_level_links_);
+
+ for(uint i = 0; i != end; ++i) // nodes
+ {
+ uint end_sub1 = ultimo_nivel[i].size();
+ for(uint j = 0; j != end_sub1; ++j) // links
+ {
+ vector <LinkStatus*> f(children( LinkStatus::lastRedirection(((ultimo_nivel[i])[j])) ));
+ if(f.size() != 0)
+ {
+ search_results_[search_results_.size() - 1].push_back(f);
+ number_of_links_to_check_ += f.size();
+ }
+
+ emit signalAddingLevelProgress();
+// kapp->processEvents();
+ }
+ }
+ if( (search_results_[search_results_.size() - 1]).size() == 0 )
+ search_results_.pop_back();
+ else
+ emit signalLinksToCheckTotalSteps(number_of_links_to_check_);
+}
+
+bool SearchManager::checkable(KURL const& url, LinkStatus const& link_parent) const
+{
+ if(existUrl(url, link_parent.absoluteUrl()))
+ return false;
+
+ if(!checkableByDomain(url, link_parent))
+ return false;
+
+ if(!check_parent_dirs_)
+ {
+ if(Url::parentDir(root_.absoluteUrl(), url))
+ return false;
+ }
+ if(!check_external_links_)
+ {
+ if(Url::externalLink(root_.absoluteUrl(), url))
+ return false;
+ }
+ if(check_regular_expressions_)
+ {
+ Q_ASSERT(!reg_exp_.isEmpty());
+
+ if(reg_exp_.search(url.url()) != -1)
+ return false;
+ }
+
+ //kdDebug(23100) << "url " << url.url() << " is checkable!" << endl;
+ return true;
+}
+
+bool SearchManager::checkableByDomain(KURL const& url, LinkStatus const& link_parent) const
+{
+ bool result = false;
+ if(localDomain(url))
+ result = true;
+ else if( (link_parent.externalDomainDepth() + 1) < external_domain_depth_ )
+ result = true;
+ else
+ result = false;
+ /*
+ if(!result)
+ kdDebug(23100) << "\n\nURL " << url.url() << " is not checkable by domain\n\n" << endl;
+ */
+ return result;
+}
+/*
+bool SearchManager::localDomain(KURL const& url) const
+ {
+ KURL url_root = root_.absoluteUrl();
+
+ if(url_root.protocol() != url.protocol())
+ return false;
+
+ if(url_root.hasHost())
+ {
+ if(generalDomain())
+ {
+ return equalHost(domain_, url.host());
+ }
+ else
+ {
+ vector<QString> referencia = tokenizeWordsSeparatedBy(domain_, QChar('/'));
+ vector<QString> a_comparar = tokenizeWordsSeparatedBy(url.host() + url.directory(), QChar('/'));
+
+ if(a_comparar.size() < referencia.size())
+ return false;
+ else
+ {
+ for(uint i = 0; i != referencia.size(); ++i)
+ {
+ if(i == 0)
+ { // host, deal with specific function
+ if(!equalHost(referencia[i], a_comparar[i], !check_parent_dirs_))
+ return false;
+ }
+ else if(referencia[i] != a_comparar[i])
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ else if(checkParentDirs())
+ return true;
+ else
+ return url_root.isParentOf(url);
+ }
+*/
+
+/**
+ The same as SearchManager::localDomain(), but only for http or https.
+ http://linkstatus.paradigma.co.pt != http://paradigma.co.pt
+*/
+/*
+bool SearchManager::isLocalRestrict(KURL const& url) const
+ {
+ Q_ASSERT(url.protocol() == "http" || url.protocol() == "https");
+
+ KURL url_root = root_.absoluteUrl();
+
+ if(url_root.protocol() != url.protocol())
+ return false;
+
+ if(url_root.hasHost())
+ {
+ vector<QString> referencia = tokenizeWordsSeparatedBy(domain_, QChar('/'));
+ vector<QString> a_comparar = tokenizeWordsSeparatedBy(url.host() + url.directory(), QChar('/'));
+
+ if(a_comparar.size() < referencia.size())
+ return false;
+ else
+ {
+ for(uint i = 0; i != referencia.size(); ++i)
+ {
+ if(i == 0)
+ { // host, deal with specific function
+ if(!equalHost(referencia[i], a_comparar[i], true))
+ return false;
+ }
+ else if(referencia[i] != a_comparar[i])
+ return false;
+ }
+ }
+ return true;
+ }
+ else
+ return false;
+ }
+*/
+bool SearchManager::generalDomain() const
+{
+ if(checked_general_domain_)
+ return general_domain_;
+
+ else
+ {
+ Q_ASSERT(!domain_.isEmpty());
+
+ if(!check_parent_dirs_)
+ return false;
+
+ int barra = domain_.find('/');
+ if(barra != -1 && (uint)barra != domain_.length() - 1)
+ {
+ kdDebug(23100) << "Domain nao vago" << endl;
+ return false;
+ }
+ else
+ {
+ vector<QString> palavras = tokenizeWordsSeparatedByDots(domain_);
+ Q_ASSERT(palavras.size() >= 1); // host might be localhost
+
+ QString primeira_palavra = palavras[0];
+ if(primeira_palavra == "www")
+ {
+ Q_ASSERT(palavras.size() >= 3);
+ kdDebug(23100) << "Domain vago" << endl;
+ return true;
+ }
+ else if(palavras.size() == 2)
+ {
+ kdDebug(23100) << "Domain vago" << endl;
+ return true;
+ }
+ else
+ {
+ kdDebug(23100) << "Domain nao vago" << endl;
+ return false;
+ }
+ }
+ }
+}
+
+bool SearchManager::onlyCheckHeader(LinkStatus* ls) const
+{
+ if(search_mode_ == depth)
+ return current_depth_ == depth_;
+
+ else if(search_mode_ == domain)
+ return !ls->local() &&
+ ls->externalDomainDepth() == external_domain_depth_ - 1;
+
+ else
+ return
+ current_depth_ == depth_ ||
+ (!ls->local() &&
+ ls->externalDomainDepth() == external_domain_depth_ - 1);
+}
+
+void SearchManager::slotSearchFinished()
+{}
+
+void SearchManager::slotLinkCheckerFinnished(LinkChecker * checker)
+{
+ kdDebug(23100) << "deleting linkchecker" << endl;
+
+ Q_ASSERT(checker);
+ //Q_ASSERT(checker->linkStatus()->checked());
+
+ delete checker;
+ checker = 0;
+}
+
+KHTMLPart* SearchManager::htmlPart(QString const& key_url) const
+{
+ if(!html_parts_.contains(key_url))
+ return 0;
+
+ return html_parts_[key_url];
+}
+
+void SearchManager::addHtmlPart(QString const& key_url, KHTMLPart* html_part)
+{
+ Q_ASSERT(!key_url.isEmpty());
+ Q_ASSERT(html_part);
+
+ // FIXME configurable
+ if(html_parts_.count() > 150)
+ removeHtmlParts();
+
+ html_parts_.insert(key_url, html_part);
+}
+
+void SearchManager::removeHtmlParts()
+{
+ KHTMLPartMap::Iterator it;
+ for(it = html_parts_.begin(); it != html_parts_.end(); ++it)
+ {
+ delete it.data();
+ it.data() = 0;
+ }
+
+ html_parts_.clear();
+}
+
+void SearchManager::save(QDomElement& element) const
+{
+ // <url>
+ QDomElement child_element = element.ownerDocument().createElement("url");
+ child_element.appendChild(element.ownerDocument().createTextNode(root_.absoluteUrl().prettyURL()));
+ element.appendChild(child_element);
+
+ // <recursively>
+ bool recursively = searchMode() == domain || depth_ > 0;
+ child_element = element.ownerDocument().createElement("recursively");
+ child_element.appendChild(element.ownerDocument().createTextNode(recursively ? "true" : "false"));
+ element.appendChild(child_element);
+
+ // <depth>
+ child_element = element.ownerDocument().createElement("depth");
+ child_element.appendChild(element.ownerDocument().
+ createTextNode(searchMode() == domain ? QString("Unlimited") : QString::number(depth_)));
+ element.appendChild(child_element);
+
+ // <check_parent_folders>
+ child_element = element.ownerDocument().createElement("check_parent_folders");
+ child_element.appendChild(element.ownerDocument().
+ createTextNode(checkParentDirs() ? "true" : "false"));
+ element.appendChild(child_element);
+
+ // <check_external_links>
+ child_element = element.ownerDocument().createElement("check_external_links");
+ child_element.appendChild(element.ownerDocument().
+ createTextNode(checkExternalLinks() ? "true" : "false"));
+ element.appendChild(child_element);
+
+ // <check_regular_expression>
+ child_element = element.ownerDocument().createElement("check_regular_expression");
+ child_element.setAttribute("check", checkRegularExpressions() ? "true" : "false");
+ if(checkRegularExpressions())
+ child_element.appendChild(element.ownerDocument().
+ createTextNode(reg_exp_.pattern()));
+ element.appendChild(child_element);
+
+ child_element = element.ownerDocument().createElement("link_list");
+ element.appendChild(child_element);
+
+ for(uint i = 0; i != search_results_.size(); ++i)
+ {
+ for(uint j = 0; j != search_results_[i].size() ; ++j)
+ {
+ for(uint l = 0; l != (search_results_[i])[j].size(); ++l)
+ {
+ LinkStatus* ls = ((search_results_[i])[j])[l];
+ if(ls->checked())
+ ls->save(child_element);
+ }
+ }
+ }
+}
+
+QString SearchManager::toXML() const
+{
+ QDomDocument doc;
+ doc.appendChild(doc.createProcessingInstruction( "xml",
+ "version=\"1.0\" encoding=\"UTF-8\""));
+
+ QDomElement root = doc.createElement("klinkstatus");
+ doc.appendChild(root);
+
+ save(root);
+
+ return doc.toString(4);
+}
+
+#include "searchmanager.moc"
diff --git a/klinkstatus/src/engine/searchmanager.h b/klinkstatus/src/engine/searchmanager.h
new file mode 100644
index 00000000..135d267a
--- /dev/null
+++ b/klinkstatus/src/engine/searchmanager.h
@@ -0,0 +1,193 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 GESTOR_PESQUISA_H
+#define GESTOR_PESQUISA_H
+
+#include <kurl.h>
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qregexp.h>
+#include <qmap.h>
+class QDomElement;
+
+#include <vector>
+
+#include "linkstatus.h"
+#include "linkchecker.h"
+#include "../parser/node.h"
+#include "../parser/url.h"
+
+using namespace std;
+
+typedef QMap<QString, KHTMLPart*> KHTMLPartMap;
+
+class SearchManager: public QObject
+{
+ Q_OBJECT
+
+public:
+
+ enum SearchMode {
+ depth,
+ domain,
+ depth_and_domain
+ };
+
+ SearchManager(int max_simultaneous_connections = 3, int time_out = 50,
+ QObject *parent = 0, const char *name = 0);
+ ~SearchManager();
+
+ QString toXML() const;
+ void save(QDomElement& element) const;
+
+ KHTMLPartMap const& htmlParts() const { return html_parts_; }
+
+ KHTMLPart* htmlPart(QString const& key_url) const;
+ void addHtmlPart(QString const& key_url, KHTMLPart* html_part);
+ void removeHtmlParts();
+
+ void startSearch(KURL const& root);
+ void startSearch(KURL const& root, SearchMode const& modo);
+ void resume();
+ void cancelSearch();
+
+ bool hasDocumentRoot() const;
+ KURL const& documentRoot() const;
+ void setDocumentRoot(KURL const& url);
+
+ void setSearchMode(SearchMode modo);
+ void setDepth(int depth);
+ void setExternalDomainDepth(int depth);
+ void setDomain(QString const& domain);
+ void setCheckParentDirs(bool flag);
+ void setCheckExternalLinks(bool flag);
+ void setCheckRegularExpressions(bool flag);
+ void setRegularExpression(QString const& reg_exp, bool case_sensitive);
+ void setTimeOut(int time_out);
+
+ void cleanItems();
+ void reset();
+
+ bool searching() const;
+ bool localDomain(KURL const& url, bool restrict = true) const;
+ //bool isLocalRestrict(KURL const& url) const;
+ SearchMode const& searchMode() const;
+ bool checkRegularExpressions() const { return check_regular_expressions_; }
+ bool existUrl(KURL const& url, KURL const& url_parent) const;
+ LinkStatus const* linkStatus(QString const& s_url) const;
+ int checkedLinks() const;
+ QTime timeElapsed() const;
+ bool checkParentDirs() const;
+ bool checkExternalLinks() const;
+ LinkStatus const* linkStatusRoot() const;
+ int maxSimultaneousConnections() const;
+ int timeOut() const;
+
+ bool sendIdentification() const { return send_identification_; }
+ QString const& userAgent() const { return user_agent_; }
+
+private:
+
+ void checkRoot();
+ void checkVectorLinks(vector<LinkStatus*> const& links); // corresponde a um no de um nivel de depth
+ vector<LinkStatus*> children(LinkStatus* link);
+ void startSearch();
+ void continueSearch();
+ void finnish();
+ void pause();
+ vector<LinkStatus*> const& nodeToAnalize() const;
+ vector<LinkStatus*> chooseLinks(vector<LinkStatus*> const& links);
+ void checkLinksSimultaneously(vector<LinkStatus*> const& links);
+ void addLevel();
+ bool checkableByDomain(KURL const& url, LinkStatus const& link_parent) const;
+ bool checkable(KURL const& url, LinkStatus const& link_parent) const;
+ int maximumCurrentConnections() const;
+ bool onlyCheckHeader(LinkStatus* ls) const;
+
+ /*
+ Entende-se por domain vago um domain do tipo www.google.pt ou google.pt, pelo que,
+ por exemplo, imagens.google.pt, e considerado estar no mesmo domain.
+ pwp.netcabo.pt ou www.google.pt/imagens nao sao considerados domains vagos.
+ */
+ bool generalDomain() const;
+ bool generalDomainChecked() const; // Para garantir que o procedimento generalDomain() so e chamado uma vez
+
+private slots:
+
+ void slotRootChecked(const LinkStatus * link, LinkChecker * checker);
+ void slotLinkChecked(const LinkStatus * link, LinkChecker * checker);
+ void slotSearchFinished();
+ void slotLinkCheckerFinnished(LinkChecker * checker);
+
+signals:
+
+ void signalRootChecked(const LinkStatus * link, LinkChecker * checker);
+ void signalLinkChecked(const LinkStatus * link, LinkChecker * checker);
+ void signalSearchFinished();
+ void signalSearchPaused();
+ void signalAddingLevelTotalSteps(uint number_of_links);
+ void signalAddingLevelProgress();
+ void signalLinksToCheckTotalSteps(uint links_to_check);
+ //void signalLinksToCheckProgress();
+
+private:
+
+ int max_simultaneous_connections_;
+ SearchMode search_mode_;
+ LinkStatus root_;
+ bool has_document_root_;
+ KURL document_root_url_; // in case of non http protocols the document root must be explicitly given
+ int depth_;
+ int current_depth_;
+ int external_domain_depth_;
+ int current_node_;
+ int current_index_;
+ int links_being_checked_;
+ int finished_connections_;
+ int maximum_current_connections_;
+ QRegExp reg_exp_;
+ QString domain_;
+ bool general_domain_;
+ bool checked_general_domain_;
+ int time_out_;
+ int current_connections_;
+ bool send_identification_; // user-agent
+ QString user_agent_;
+
+ bool canceled_;
+ bool searching_;
+ int checked_links_;
+ QTime time_;
+ int ignored_links_;
+ bool check_parent_dirs_;
+ bool check_external_links_;
+ bool check_regular_expressions_;
+ uint number_of_level_links_;
+ uint number_of_links_to_check_;
+ vector< vector< vector <LinkStatus*> > > search_results_;
+ KHTMLPartMap html_parts_;
+};
+
+#include "searchmanager_impl.h"
+
+#endif
diff --git a/klinkstatus/src/engine/searchmanager_impl.h b/klinkstatus/src/engine/searchmanager_impl.h
new file mode 100644
index 00000000..eaa5e572
--- /dev/null
+++ b/klinkstatus/src/engine/searchmanager_impl.h
@@ -0,0 +1,158 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+
+
+
+inline int SearchManager::maximumCurrentConnections() const
+{
+ Q_ASSERT(maximum_current_connections_ != -1);
+ return maximum_current_connections_;
+}
+
+inline SearchManager::SearchMode const& SearchManager::searchMode() const
+{
+ return search_mode_;
+}
+
+inline int SearchManager::checkedLinks() const
+{
+ Q_ASSERT(checked_links_ > 0);
+ return checked_links_;
+}
+
+inline QTime SearchManager::timeElapsed() const
+{
+ int ms = time_.elapsed();
+ //kdDebug(23100) << "Time elapsed (ms): " << ms << endl;
+ return QTime(0, 0).addMSecs(ms);
+}
+
+inline void SearchManager::startSearch(KURL const& root)
+{
+ startSearch(root, search_mode_);
+}
+
+inline void SearchManager::setSearchMode(SearchMode modo)
+{
+ search_mode_ = modo;
+}
+
+inline void SearchManager::setDepth(int depth)
+{
+ depth_ = depth;
+}
+
+inline void SearchManager::setExternalDomainDepth(int depth)
+{
+ external_domain_depth_ = depth;
+}
+
+inline void SearchManager::setDomain(QString const& domain)
+{
+ Q_ASSERT(domain.find("http://") == -1);
+ domain_ = domain;
+ general_domain_ = generalDomain();
+ checked_general_domain_ = true;
+}
+
+inline void SearchManager::setCheckParentDirs(bool flag)
+{
+ check_parent_dirs_ = flag;
+}
+
+inline void SearchManager::setCheckExternalLinks(bool flag)
+{
+ check_external_links_ = flag;
+}
+
+inline void SearchManager::setCheckRegularExpressions(bool flag)
+{
+ check_regular_expressions_ = flag;
+}
+
+inline void SearchManager::setRegularExpression(QString const& reg_exp, bool case_sensitive)
+{
+ reg_exp_ = QRegExp(reg_exp, case_sensitive);
+}
+
+inline void SearchManager::setTimeOut(int time_out)
+{
+ Q_ASSERT(time_out > 0);
+ time_out_ = time_out;
+}
+
+
+
+inline bool SearchManager::checkParentDirs() const
+{
+ return check_parent_dirs_;
+}
+
+inline bool SearchManager::checkExternalLinks() const
+{
+ return check_external_links_;
+}
+
+inline LinkStatus const* SearchManager::linkStatusRoot() const
+{
+ return &root_;
+}
+
+inline bool SearchManager::searching() const
+{
+ return searching_;
+}
+
+inline bool SearchManager::localDomain(KURL const& url, bool restrict) const
+{
+ return Url::localDomain(root_.absoluteUrl(), url, restrict);
+}
+
+inline int SearchManager::maxSimultaneousConnections() const
+{
+ return max_simultaneous_connections_;
+}
+
+inline int SearchManager::timeOut() const
+{
+ return time_out_;
+}
+
+inline bool SearchManager::hasDocumentRoot() const
+{
+ return has_document_root_;
+}
+
+inline KURL const& SearchManager::documentRoot() const
+{
+ return document_root_url_;
+}
+
+inline void SearchManager::setDocumentRoot(KURL const& url)
+{
+ Q_ASSERT(url.isValid()); // includes empty URLs
+ Q_ASSERT(!url.protocol().startsWith("http"));
+
+ document_root_url_ = url;
+ has_document_root_ = true;
+}
+
+
diff --git a/klinkstatus/src/global.cpp b/klinkstatus/src/global.cpp
new file mode 100644
index 00000000..10395f7b
--- /dev/null
+++ b/klinkstatus/src/global.cpp
@@ -0,0 +1,205 @@
+//
+// C++ Implementation: global
+//
+// Description:
+//
+//
+// Author: Paulo Moura Guedes <moura@kdewebdev.org>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "global.h"
+
+#include <qstring.h>
+#include <qtimer.h>
+
+#include <dcopclient.h>
+#include <dcopref.h>
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kstaticdeleter.h>
+#include <kurl.h>
+#include <kprocess.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+
+Global* Global::m_self_ = 0;
+static KStaticDeleter<Global> staticDeleter;
+
+
+Global* Global::self()
+{
+ if (!m_self_)
+ {
+ staticDeleter.setObject(m_self_, new Global());
+ }
+
+ return m_self_;
+}
+
+Global::Global(QObject *parent, const char *name)
+ : QObject(parent, name), loop_started_(false)
+{
+ m_self_ = this;
+ dcop_client_ = kapp->dcopClient();
+}
+
+Global::~Global()
+{
+ if(m_self_ == this)
+ staticDeleter.setObject(m_self_, 0, false);
+}
+
+bool Global::isKLinkStatusEmbeddedInQuanta()
+{
+ QCString app_id = "quanta-" + QCString().setNum(getpid());
+ return self()->dcop_client_->isApplicationRegistered(app_id);
+}
+
+bool Global::isQuantaRunningAsUnique()
+{
+ return self()->dcop_client_->isApplicationRegistered("quanta");
+}
+
+bool Global::isQuantaAvailableViaDCOP()
+{
+ if(isQuantaRunningAsUnique() || isKLinkStatusEmbeddedInQuanta())
+ return true;
+
+ else
+ {
+ self()->execCommand("ps h -o pid -C quanta -C quanta_be");
+ QStringList ps_list = QStringList::split("\n", self()->script_output_);
+
+ for(uint i = 0; i != ps_list.size(); ++i)
+ {
+ ps_list[i] = ps_list[i].stripWhiteSpace ();
+ if(self()->dcop_client_->isApplicationRegistered("quanta-" + ps_list[i].local8Bit()))
+ {
+ //kdDebug(23100) << "Application registered!" << endl;
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+QCString Global::quantaDCOPAppId()
+{
+ DCOPClient* client = kapp->dcopClient();
+ QCString app_id;
+
+ if(client->isApplicationRegistered("quanta")) // quanta is unnique application
+ app_id = "quanta";
+
+ else if(self()->isKLinkStatusEmbeddedInQuanta()) // klinkstatus is running as a part inside quanta
+ {
+ QCString app = "quanta-";
+ QCString pid = QCString().setNum(getpid());
+ app_id = app + pid;
+ }
+
+ else
+ {
+ self()->execCommand("ps h -o pid -C quanta -C quanta_be");
+ QStringList ps_list = QStringList::split("\n", self()->script_output_);
+
+ for(uint i = 0; i != ps_list.size(); ++i)
+ {
+ ps_list[i] = ps_list[i].stripWhiteSpace ();
+ if(self()->dcop_client_->isApplicationRegistered("quanta-" + ps_list[i].local8Bit()))
+ app_id = "quanta-" + ps_list[i];
+ }
+ }
+
+ if(self()->dcop_client_->isApplicationRegistered(app_id))
+ return app_id;
+ else
+ {
+ kdError(23100) << "You didn't check if Global::isQuantaAvailableViaDCOP!" << endl;
+ return "";
+ }
+}
+
+KURL Global::urlWithQuantaPreviewPrefix(KURL const& url)
+{
+ Q_ASSERT(isKLinkStatusEmbeddedInQuanta());
+
+ DCOPRef quanta(Global::quantaDCOPAppId(),"WindowManagerIf");
+ QString string_url_with_prefix = quanta.call("urlWithPreviewPrefix", url.url());
+ //kdDebug(23100) << "string_url_with_prefix: " << string_url_with_prefix << endl;
+
+ return KURL(string_url_with_prefix);
+}
+
+void Global::openQuanta(QStringList const& args)
+{
+ QString command(args.join(" "));
+ Global::execCommand("quanta " + command);
+}
+
+void Global::execCommand(QString const& command)
+{
+
+ //We create a KProcess that executes the "ps" *nix command to get the PIDs of the
+ //other instances of quanta actually running
+ self()->process_PS_ = new KProcess();
+ *(self()->process_PS_) << QStringList::split(" ",command);
+
+ connect( self()->process_PS_, SIGNAL(receivedStdout(KProcess*,char*,int)),
+ self(), SLOT(slotGetScriptOutput(KProcess*,char*,int)));
+ connect( self()->process_PS_, SIGNAL(receivedStderr(KProcess*,char*,int)),
+ self(), SLOT(slotGetScriptError(KProcess*,char*,int)));
+ connect( self()->process_PS_, SIGNAL(processExited(KProcess*)),
+ self(), SLOT(slotProcessExited(KProcess*)));
+
+ //if KProcess fails I think a message box is needed... I will fix it
+ if (!self()->process_PS_->start(KProcess::NotifyOnExit,KProcess::All))
+ kdError() << "Failed to query for running KLinkStatus instances!" << endl;
+ //TODO: Replace the above error with a real messagebox after the message freeze is over
+ else
+ {
+ //To avoid lock-ups, start a timer.
+ QTimer* timer = new QTimer(self());
+ connect(timer, SIGNAL(timeout()),
+ self(), SLOT(slotProcessTimeout()));
+ timer->start(120*1000, true);
+ self()->loop_started_ = true;
+ kapp->enter_loop();
+ delete timer;
+ }
+}
+
+void Global::slotGetScriptOutput(KProcess* /*process*/, char* buf, int buflen)
+{
+ QCString tmp( buf, buflen + 1 );
+ script_output_ = QString::null;
+ script_output_ = QString::fromLocal8Bit(tmp).remove(" ");
+}
+
+void Global::slotGetScriptError(KProcess*, char* buf, int buflen)
+{
+ //TODO: Implement some error handling?
+ Q_UNUSED(buf);
+ Q_UNUSED(buflen);
+}
+
+void Global::slotProcessExited(KProcess*)
+{
+ slotProcessTimeout();
+}
+
+void Global::slotProcessTimeout()
+{
+ if (loop_started_)
+ {
+ kapp->exit_loop();
+ loop_started_ = false;
+ }
+}
+
+
+#include "global.moc"
diff --git a/klinkstatus/src/global.h b/klinkstatus/src/global.h
new file mode 100644
index 00000000..2ee2f0c0
--- /dev/null
+++ b/klinkstatus/src/global.h
@@ -0,0 +1,60 @@
+//
+// C++ Interface: global
+//
+// Description:
+//
+//
+// Author: Paulo Moura Guedes <moura@kdewebdev.org>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef GLOBAL_H
+#define GLOBAL_H
+
+#include <qobject.h>
+class QCString;
+
+class DCOPClient;
+class KURL;
+class KProcess;
+
+/**
+@author Paulo Moura Guedes
+*/
+class Global : public QObject
+{
+ Q_OBJECT
+public:
+ static Global* self();
+ ~Global();
+
+ static bool isKLinkStatusEmbeddedInQuanta();
+ static bool isQuantaRunningAsUnique();
+ static bool isQuantaAvailableViaDCOP();
+ static QCString quantaDCOPAppId();
+ static KURL urlWithQuantaPreviewPrefix(KURL const& url);
+
+ //static void setLoopStarted(bool flag);
+ static void openQuanta(QStringList const& args);
+
+private:
+ Global(QObject *parent = 0, const char *name = 0);
+ static void execCommand(QString const& command);
+
+private slots:
+ void slotGetScriptOutput(KProcess* process, char* buffer, int buflen);
+ void slotGetScriptError(KProcess* process, char* buffer, int buflen);
+ void slotProcessExited(KProcess* process);
+ void slotProcessTimeout();
+
+private:
+ static Global* m_self_;
+
+ DCOPClient* dcop_client_;
+ bool loop_started_;
+ QString script_output_;
+ KProcess* process_PS_;
+};
+
+#endif
diff --git a/klinkstatus/src/klinkstatus.cpp b/klinkstatus/src/klinkstatus.cpp
new file mode 100644
index 00000000..7d984f0f
--- /dev/null
+++ b/klinkstatus/src/klinkstatus.cpp
@@ -0,0 +1,213 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include "klinkstatus.h"
+
+#include <kkeydialog.h>
+#include <kfiledialog.h>
+#include <kconfig.h>
+#include <kurl.h>
+#include <kedittoolbar.h>
+#include <kaction.h>
+#include <kstdaction.h>
+#include <klibloader.h>
+#include <kmessagebox.h>
+#include <kstatusbar.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kaccel.h>
+
+
+KLinkStatus::KLinkStatus()
+ : KParts::MainWindow( 0L, "KLinkStatus" )
+{
+ // set the shell's ui resource file
+ setXMLFile("klinkstatus_shell.rc");
+
+ setupActions();
+
+ // and a status bar
+ //statusBar()->show();
+
+ // this routine will find and load our Part. it finds the Part by
+ // name which is a bad idea usually.. but it's alright in this
+ // case since our Part is made for this Shell
+ KLibFactory *factory = KLibLoader::self()->factory("libklinkstatuspart");
+ if (factory)
+ {
+ // now that the Part is loaded, we cast it to a Part to get
+ // our hands on it
+ m_part = static_cast<KParts::ReadOnlyPart *>(factory->create(this,
+ "klinkstatus_part", "KParts::ReadOnlyPart" ));
+
+ if (m_part)
+ {
+ // tell the KParts::MainWindow that this is indeed the main widget
+ setCentralWidget(m_part->widget());
+ setStandardToolBarMenuEnabled(true);
+
+ // and integrate the part's GUI with the shell's
+ createGUI(m_part);
+ removeDuplicatedActions();
+ }
+ }
+ else
+ {
+ // if we couldn't find our Part, we exit since the Shell by
+ // itself can't do anything useful
+ KMessageBox::error(this, i18n("Could not find the KLinkStatus part; did you configure with '--prefix=/$KDEDIR' and perform 'make install'?"));
+ kapp->quit();
+ // we return here, cause kapp->quit() only means "exit the
+ // next time we enter the event loop...
+ return;
+ }
+
+ // apply the saved mainwindow settings, if any, and ask the mainwindow
+ // to automatically save settings if changed: window size, toolbar
+ // position, icon size, etc.
+ setAutoSaveSettings();
+
+ setupPartActions();
+}
+
+KLinkStatus::~KLinkStatus()
+{}
+
+void KLinkStatus::load(const KURL& url)
+{
+ m_part->openURL( url );
+}
+
+void KLinkStatus::setupActions()
+{
+ // KStdAction::quit(kapp, SLOT(quit()), actionCollection());
+ // The above causes a segfault when using File->Quit.
+ // Here's Waldo's explanation:
+/* I had a look. The problem is due to the SessionWidget destructor calling
+ KLSConfig. If you use the window button, the window and the SessionWidget is
+ destructed first and then later the application is destructed.
+ If you use File->Quit it calls kapp->quit which destructs the application
+ without destructing the window first. The application first destructs all
+ static deleters and its administration, and then goes on to kill the
+ remaining windows that it owns. Therein lies the problem because by then the
+ static deleters aren't usable any longer, and calling KLSConfig from the
+ SessionWidget destructor crashes when it tries to recreate KLSConfig and
+ register it with staticKLSConfigDeleter due to the lack of static deleter
+ administration.
+ The easiest solution is to call close() on the mainwindow instead of
+ KApplication::quit()*/
+ KStdAction::quit(this, SLOT(close()), actionCollection());
+
+ //m_toolbarAction = KStdAction::showToolbar(this, SLOT(optionsShowToolbar()), actionCollection());
+ //m_statusbarAction = KStdAction::showStatusbar(this, SLOT(optionsShowStatusbar()), actionCollection());
+
+ KStdAction::keyBindings(this, SLOT(optionsConfigureKeys()), actionCollection());
+ KStdAction::configureToolbars(this, SLOT(optionsConfigureToolbars()), actionCollection());
+}
+
+void KLinkStatus::setupPartActions()
+{
+ Q_ASSERT(m_part);
+ KActionCollection* part_action_collection = m_part->actionCollection();
+ part_action_collection->action("new_link_check")->setShortcut(KStdAccel::shortcut(KStdAccel::New));
+ part_action_collection->action("open_link")->setShortcut(KStdAccel::shortcut(KStdAccel::Open));
+ part_action_collection->action("close_tab")->setShortcut(KStdAccel::shortcut(KStdAccel::Close));
+}
+
+void KLinkStatus::removeDuplicatedActions()
+{
+ KActionCollection* part_action_collection = m_part->actionCollection();
+ KAction* part_about_action = part_action_collection->action("about_klinkstatus");
+ KAction* part_report_action = part_action_collection->action("report_bug");
+
+ QWidget* container = part_about_action->container(0); // call this only once or segfault
+ part_about_action->unplug(container);
+ part_report_action->unplug(container);
+ part_action_collection->remove(part_about_action);
+ part_action_collection->remove(part_report_action);
+}
+
+void KLinkStatus::saveProperties(KConfig* /*config*/)
+{
+ // the 'config' object points to the session managed
+ // config file. anything you write here will be available
+ // later when this app is restored
+}
+
+void KLinkStatus::readProperties(KConfig* /*config*/)
+{
+ // the 'config' object points to the session managed
+ // config file. this function is automatically called whenever
+ // the app is being restored. read in here whatever you wrote
+ // in 'saveProperties'
+}
+
+void KLinkStatus::optionsShowToolbar()
+{
+ // this is all very cut and paste code for showing/hiding the
+ // toolbar
+ if (m_toolbarAction->isChecked())
+ toolBar()->show();
+ else
+ toolBar()->hide();
+}
+
+void KLinkStatus::optionsShowStatusbar()
+{
+ // this is all very cut and paste code for showing/hiding the
+ // statusbar
+ if (m_statusbarAction->isChecked())
+ statusBar()->show();
+ else
+ statusBar()->hide();
+}
+
+void KLinkStatus::optionsConfigureKeys()
+{
+ //KKeyDialog::configure(actionCollection());
+
+ KKeyDialog dlg( false, this );
+ QPtrList<KXMLGUIClient> clients = guiFactory()->clients();
+ for( QPtrListIterator<KXMLGUIClient> it( clients );
+ it.current(); ++it )
+ {
+ dlg.insert( (*it)->actionCollection() );
+ }
+ dlg.configure();
+}
+
+void KLinkStatus::optionsConfigureToolbars()
+{
+ saveMainWindowSettings(KGlobal::config(), autoSaveGroup());
+
+ // use the standard toolbar editor
+ KEditToolbar dlg(factory());
+ connect(&dlg, SIGNAL(newToolbarConfig()),
+ this, SLOT(applyNewToolbarConfig()));
+ dlg.exec();
+}
+
+void KLinkStatus::applyNewToolbarConfig()
+{
+ applyMainWindowSettings(KGlobal::config(), autoSaveGroup());
+}
+
+
+#include "klinkstatus.moc"
diff --git a/klinkstatus/src/klinkstatus.desktop b/klinkstatus/src/klinkstatus.desktop
new file mode 100644
index 00000000..bb1cdbd0
--- /dev/null
+++ b/klinkstatus/src/klinkstatus.desktop
@@ -0,0 +1,54 @@
+[Desktop Entry]
+Name=KLinkStatus
+Name[ne]=केडीई लिङ्क स्थिति
+Name[sk]=KLink status
+Name[sv]=Klinkstatus
+Name[ta]=Kதொகுதி நிலைமை
+Name[tr]=K Köprü Durumu
+Exec=klinkstatus %i %m -caption "%c"
+Icon=klinkstatus
+Type=Application
+DocPath=klinkstatus/index.html
+Terminal=false
+GenericName=Link Checker
+GenericName[bg]=Проверка на препратки
+GenericName[ca]=Comprovador d'enllaços
+GenericName[cs]=Kontrola odkazů
+GenericName[da]=Link-tjekker
+GenericName[de]=Überprüfungswerkzeug für Verknüpfungen
+GenericName[el]=Ελεγκτής σύνδεσης
+GenericName[es]=Comprobador de enlaces
+GenericName[et]=Viidakontrollija
+GenericName[eu]=Esteka egiaztatzailea
+GenericName[fa]=بررسی‌کنندۀ پیوند
+GenericName[fi]=Linkkien tarkistaja
+GenericName[fr]=Vérificateur de liens
+GenericName[gl]=Verificador de ligazóns
+GenericName[hu]=Linkellenőrző
+GenericName[is]=Yfirfer tengla
+GenericName[it]=Controllo dei collegamenti
+GenericName[ja]=リンクチェッカー
+GenericName[ka]=ბმულების შემმოწმებელი
+GenericName[lt]=Nuorody tikrintuvė
+GenericName[ms]=Pemeriksa Pautan
+GenericName[nds]=Linkprööv
+GenericName[ne]=लिङ्क परीक्षक
+GenericName[nl]=Linkchecker
+GenericName[pl]=Program sprawdzający odnośniki
+GenericName[pt]=Verificação de Ligações
+GenericName[pt_BR]=Verificador de Links
+GenericName[ru]=Проверка ссылок
+GenericName[sk]=Kontrole spojenia
+GenericName[sl]=Preverjalnik povezav
+GenericName[sr]=Провера везе
+GenericName[sr@Latn]=Provera veze
+GenericName[sv]=Länkkontroll
+GenericName[ta]=இணைப்பு சரிபார்ப்பான்
+GenericName[tg]=Тафтиши истинод
+GenericName[tr]=Bağlantı Kontrolcüsü
+GenericName[uk]=Перевірка посилань
+GenericName[zh_CN]=链接检查器
+GenericName[zh_HK]=連結檢查程式
+GenericName[zh_TW]=連結檢查程式
+Categories=Qt;KDE;Development;WebDevelopment;
+
diff --git a/klinkstatus/src/klinkstatus.h b/klinkstatus/src/klinkstatus.h
new file mode 100644
index 00000000..14ba2934
--- /dev/null
+++ b/klinkstatus/src/klinkstatus.h
@@ -0,0 +1,86 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 _KLINKSTATUS_H_
+#define _KLINKSTATUS_H_
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <kapplication.h>
+#include <kparts/mainwindow.h>
+
+class KToggleAction;
+
+/**
+ * This is the application "Shell". It has a menubar, toolbar, and
+ * statusbar but relies on the "Part" to do all the real work.
+ *
+ * @short Application Shell
+ * @author Paulo Moura Guedes <moura@kdewebdev.org>
+ * @version 0.1.3
+ */
+class KLinkStatus : public KParts::MainWindow
+{
+ Q_OBJECT
+public:
+ KLinkStatus();
+ virtual ~KLinkStatus();
+
+ /** Use this method to load whatever file/URL you have */
+ void load(const KURL& url);
+
+protected:
+ /**
+ * This method is called when it is time for the app to save its
+ * properties for session management purposes.
+ */
+ void saveProperties(KConfig *);
+
+ /**
+ * This method is called when this app is restored. The KConfig
+ * object points to the session management config file that was saved
+ * with @ref saveProperties
+ */
+ void readProperties(KConfig *);
+
+private slots:
+ void optionsShowToolbar();
+ void optionsShowStatusbar();
+ void optionsConfigureKeys();
+ void optionsConfigureToolbars();
+
+ void applyNewToolbarConfig();
+
+private:
+ void setupAccel();
+ void setupActions();
+ void setupPartActions();
+ void removeDuplicatedActions();
+
+private:
+ KParts::ReadOnlyPart *m_part;
+
+ KToggleAction *m_toolbarAction;
+ KToggleAction *m_statusbarAction;
+};
+
+#endif // _KLINKSTATUS_H_
diff --git a/klinkstatus/src/klinkstatus.lsm b/klinkstatus/src/klinkstatus.lsm
new file mode 100644
index 00000000..94221faf
--- /dev/null
+++ b/klinkstatus/src/klinkstatus.lsm
@@ -0,0 +1,16 @@
+Begin3
+Title: KLinkStatus -- Some description
+Version: 0.2.1
+Entered-date:
+Description:
+Keywords: KDE Qt
+Author: Paulo Moura Guedes <pmg@netcabo.pt>
+Maintained-by: Paulo Moura Guedes <pmg@netcabo.pt>
+Home-page:
+Alternate-site:
+Primary-site: ftp://ftp.kde.org/pub/kde/unstable/apps/utils
+ xxxxxx klinkstatus-0.1.0.tar.gz
+ xxx klinkstatus-0.1.0.lsm
+Platform: Linux. Needs KDE
+Copying-policy: GPL
+End
diff --git a/klinkstatus/src/klinkstatus_part.cpp b/klinkstatus/src/klinkstatus_part.cpp
new file mode 100644
index 00000000..cf04df7a
--- /dev/null
+++ b/klinkstatus/src/klinkstatus_part.cpp
@@ -0,0 +1,207 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include <kaboutdata.h>
+#include <klocale.h>
+#include <kinstance.h>
+#include <kaction.h>
+#include <kstdaction.h>
+#include <kfiledialog.h>
+#include <kparts/genericfactory.h>
+#include <kparts/factory.h>
+#include <kstandarddirs.h>
+#include <kaboutapplication.h>
+#include <kbugreport.h>
+#include <kconfigdialog.h>
+#include <kglobalsettings.h>
+#include <kshortcut.h>
+#include <kaccel.h>
+#include <kkeydialog.h>
+
+#include <qbuttongroup.h>
+
+#include "global.h"
+#include "cfg/klsconfig.h"
+#include "klinkstatus_part.h"
+#include "ui/tabwidgetsession.h"
+#include "ui/sessionwidget.h"
+#include "ui/settings/configsearchdialog.h"
+#include "ui/settings/configresultsdialog.h"
+#include "ui/settings/configidentificationdialog.h"
+#include "actionmanager.h"
+
+
+const char KLinkStatusPart::description_[] = I18N_NOOP( "A Link Checker" );
+const char KLinkStatusPart::version_[] = "0.3.2";
+
+// Factory code for KDE 3
+typedef KParts::GenericFactory<KLinkStatusPart> KLinkStatusFactory;
+K_EXPORT_COMPONENT_FACTORY( libklinkstatuspart, KLinkStatusFactory )
+
+KLinkStatusPart::KLinkStatusPart(QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name,
+ const QStringList & /*string_list*/)
+ : KParts::ReadOnlyPart(parent, name), m_dlgAbout(0)
+{
+ setInstance(KLinkStatusFactory::instance());
+
+ action_manager_ = new ActionManager(this);
+ ActionManager::setInstance(action_manager_);
+ initGUI();
+
+ tabwidget_ = new TabWidgetSession(parentWidget, widgetName);
+ setWidget(tabwidget_);
+ action_manager_->initTabWidget(tabwidget_);
+
+ // we are not modified since we haven't done anything yet
+ setModified(false);
+
+ openURL("");
+}
+
+KLinkStatusPart::~KLinkStatusPart()
+{}
+
+void KLinkStatusPart::initGUI()
+{
+ setXMLFile("klinkstatus_part.rc", true);
+
+ // initialize the part actions
+ action_manager_->initPart(this);
+}
+
+void KLinkStatusPart::setModified(bool modified)
+{
+ // get a handle on our Save action and make sure it is valid
+ KAction *save = actionCollection()->action(KStdAction::stdName(KStdAction::Save));
+ if (!save)
+ return;
+
+ // if so, we either enable or disable it based on the current
+ // state
+ if (modified)
+ save->setEnabled(true);
+ else
+ save->setEnabled(false);
+}
+
+bool KLinkStatusPart::openURL(KURL const& url)
+{
+ KURL url_aux = url;
+
+ if(KLSConfig::useQuantaUrlPreviewPrefix() && Global::isKLinkStatusEmbeddedInQuanta())
+ {
+ url_aux = Global::urlWithQuantaPreviewPrefix(url);
+ if(!url_aux.isValid() || url_aux.isEmpty())
+ url_aux = url;
+ }
+ else
+ url_aux = url;
+
+ tabwidget_->slotNewSession(url_aux);
+
+ return true;
+}
+
+bool KLinkStatusPart::openFile()
+{
+ return false;
+}
+
+void KLinkStatusPart::slotNewLinkCheck()
+{
+ openURL("");
+}
+
+void KLinkStatusPart::slotOpenLink()
+{
+ QString file_name = KFileDialog::getOpenURL().url();
+
+ if (file_name.isEmpty() == false)
+ {
+ openURL(file_name);
+ }
+}
+
+void KLinkStatusPart::slotClose()
+{
+ tabwidget_->closeSession();
+}
+
+void KLinkStatusPart::slotConfigureKLinkStatus()
+{
+ KConfigDialog *dialog = new KConfigDialog(tabwidget_, "klsconfig", KLSConfig::self());
+ dialog->addPage(new ConfigSearchDialog(0, "config_search_dialog"), i18n("Check"), "viewmag");
+ dialog->addPage(new ConfigResultsDialog(0, "config_results_dialog"), i18n("Results"), "player_playlist");
+ dialog->addPage(new ConfigIdentificationDialog(0), i18n("Identification"),
+ "agent", i18n("Configure the way KLinkstatus reports itself"));
+ dialog->show();
+ connect(dialog, SIGNAL(settingsChanged()), tabwidget_, SLOT(slotLoadSettings()));
+}
+
+void KLinkStatusPart::slotAbout()
+{
+ if(m_dlgAbout == 0)
+ {
+ m_dlgAbout = new KAboutApplication(createAboutData(), tabwidget_, "about_app");
+ if(m_dlgAbout == 0)
+ return;
+ }
+
+ if(!m_dlgAbout->isVisible())
+ {
+ m_dlgAbout->show();
+ }
+ else
+ {
+ m_dlgAbout->raise();
+ }
+}
+
+void KLinkStatusPart::slotReportBug()
+{
+ KAboutData aboutData("klinkstatus", I18N_NOOP("KLinkStatus"), version_);
+ KBugReport bugReportDlg(0, true, &aboutData);
+ bugReportDlg.exec();
+}
+
+KAboutData* KLinkStatusPart::createAboutData()
+{
+ KAboutData * about = new KAboutData("klinkstatuspart", I18N_NOOP("KLinkStatus Part"), version_,
+ description_, KAboutData::License_GPL_V2,
+ "(C) 2004 Paulo Moura Guedes", 0, 0, "moura@kdewebdev.org");
+
+ about->addAuthor("Paulo Moura Guedes", 0, "moura@kdewebdev.org");
+
+ about->addCredit("Manuel Menezes de Sequeira", 0, 0, "http://home.iscte.pt/~mms/");
+ about->addCredit("Gonçalo Silva", 0, "gngs@paradigma.co.pt");
+ about->addCredit("Nuno Monteiro", 0, 0, "http://www.itsari.org");
+ about->addCredit("Eric Laffoon", 0, "sequitur@kde.org");
+ about->addCredit("Andras Mantia", 0, "amantia@kde.org");
+ about->addCredit("Michal Rudolf", 0, "mrudolf@kdewebdev.org");
+ about->addCredit("Mathieu Kooiman", 0, " quanta@map-is.nl");
+ about->addCredit("Jens Herden", 0, "jens@kdewebdev.org");
+
+ KGlobal::dirs()->addResourceType("appicon", KStandardDirs::kde_default("data") + "klinkstatuspart/pics/");
+
+ return about;
+}
+
+#include "klinkstatus_part.moc"
diff --git a/klinkstatus/src/klinkstatus_part.desktop b/klinkstatus/src/klinkstatus_part.desktop
new file mode 100644
index 00000000..9515bbfa
--- /dev/null
+++ b/klinkstatus/src/klinkstatus_part.desktop
@@ -0,0 +1,17 @@
+[Desktop Entry]
+Name=KLinkStatusPart
+Name[de]=KLinkStatus-Komponente
+Name[fr]=Composant de KLinkStatus
+Name[nds]=KLinkStatus-Komponent
+Name[ne]=केडीई लिङ्क वस्तुस्थिति भाग
+Name[pt_BR]=Componente do KLinkStatus
+Name[ru]=Компонент KLinkStatus
+Name[sk]=KLink status Part
+Name[sv]=Klinkstatus-delprogram
+Name[ta]=Kதொகுதி நிலைமை பகுதி
+Name[tg]=Қисми KLinkStatus
+Icon=klinkstatus
+MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
+ServiceTypes=KParts/ReadOnlyPart,KParts/ReadWritePart
+X-KDE-Library=libklinkstatuspart
+Type=Service
diff --git a/klinkstatus/src/klinkstatus_part.h b/klinkstatus/src/klinkstatus_part.h
new file mode 100644
index 00000000..b32c8675
--- /dev/null
+++ b/klinkstatus/src/klinkstatus_part.h
@@ -0,0 +1,87 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 _KLINKSTATUSPART_H_
+#define _KLINKSTATUSPART_H_
+
+#include <kparts/part.h>
+
+class TabWidgetSession;
+class ActionManager;
+
+class QWidget;
+class QPainter;
+
+class KURL;
+class KAboutData;
+class KAboutApplication;
+class KAction;
+
+class KLinkStatusPart: public KParts::ReadOnlyPart
+{
+ Q_OBJECT
+public:
+ KLinkStatusPart(QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name,
+ const QStringList& args);
+ virtual ~KLinkStatusPart();
+
+ /** Reimplemented to disable and enable Save action */
+ virtual void setModified(bool modified);
+
+ static KAboutData* createAboutData();
+
+protected:
+ /** This must be implemented by each part */
+ virtual bool openFile();
+ virtual bool openURL (const KURL &url);
+ // virtual bool saveFile(){};
+
+protected slots:
+ void slotNewLinkCheck();
+ void slotOpenLink();
+ void slotClose();
+ void slotConfigureKLinkStatus();
+/* void slotDisplayAllLinks();
+ void slotDisplayGoodLinks();
+ void slotDisplayBadLinks();
+ void slotDisplayMalformedLinks();
+ void slotDisplayUndeterminedLinks();*/
+ void slotAbout();
+ void slotReportBug();
+
+// private slots:
+// void slotEnableDisplayLinksActions();
+// void slotDisableDisplayLinksActions();
+
+private:
+ void initGUI();
+
+private:
+ static const char description_[];
+ static const char version_[];
+
+ ActionManager* action_manager_;
+
+ TabWidgetSession* tabwidget_;
+ KAboutApplication* m_dlgAbout;
+};
+
+#endif // _KLINKSTATUSPART_H_
diff --git a/klinkstatus/src/klinkstatus_part.rc b/klinkstatus/src/klinkstatus_part.rc
new file mode 100644
index 00000000..f2d580de
--- /dev/null
+++ b/klinkstatus/src/klinkstatus_part.rc
@@ -0,0 +1,58 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="klinkstatus_part" version="2">
+ <MenuBar>
+
+ <Menu name="file"><text>File</text>
+ <Action name="new_link_check"/>
+ <Action name="open_link"/>
+ <Action name="close_tab"/>
+ <Separator/>
+ <Action name="file_export_html"/>
+ <Separator/>
+ </Menu>
+
+ <Menu name="settings"><text>&amp;Settings</text>
+ <Separator/>
+ <Action name="configure_klinkstatus"/>
+ </Menu>
+
+ <Menu name="view"><text>&amp;View</text>
+ <Action name="hide_search_bar"/>
+ <Action name="reset_search_bar"/>
+ <Separator/>
+ <Action name="follow_last_link_checked"/>
+<!-- <Separator/>
+ <Action name="display_all_links"/>
+ <Action name="display_good_links"/>
+ <Action name="display_bad_links"/>
+ <Action name="display_malformed_links"/>
+ <Action name="display_undetermined_links"/>-->
+ </Menu>
+
+ <Menu name="search"><text>S&amp;earch</text>
+ <Action name="start_search"/>
+ <Action name="pause_search"/>
+ <Action name="stop_search"/>
+ <Separator/>
+ </Menu>
+
+ <Menu name="help"><text>&amp;Help</text>
+ <Separator/>
+ <Action name="about_klinkstatus"/>
+ <Action name="report_bug"/>
+ </Menu>
+ </MenuBar>
+
+ <ToolBar name="linksToolBar">
+ <Action name="start_search"/>
+ <Action name="pause_search"/>
+ <Action name="stop_search"/>
+ <Separator/>
+ <Action name="file_export_html"/>
+ <Separator/>
+ <Action name="hide_search_bar"/>
+ <Action name="reset_search_bar"/>
+ <Separator/>
+ <Action name="follow_last_link_checked"/>
+ </ToolBar>
+</kpartgui>
diff --git a/klinkstatus/src/klinkstatus_shell.rc b/klinkstatus/src/klinkstatus_shell.rc
new file mode 100644
index 00000000..04cbc329
--- /dev/null
+++ b/klinkstatus/src/klinkstatus_shell.rc
@@ -0,0 +1,28 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="klinkstatus_shell" version="1">
+<MenuBar>
+
+ <Menu noMerge="1" name="file"><text>&amp;File</text>
+ <Merge/>
+ <Separator/>
+ <Action name="file_quit"/>
+ </Menu>
+
+ <Menu noMerge="1" name="view"><text>&amp;View</text>
+ <Separator/>
+ </Menu>
+
+ <Menu noMerge="1" name="search"><text>S&amp;earch</text>
+ <Separator/>
+ </Menu>
+
+ <Menu noMerge="1" name="settings"><text>&amp;Settings</text>
+ <Action name="options_configure_keybinding"/>
+ <Action name="options_configure_toolbars"/>
+ <Merge name="configure_merge"/>
+ <Separator/>
+ <Merge/>
+ </Menu>
+</MenuBar>
+
+</kpartgui>
diff --git a/klinkstatus/src/main.cpp b/klinkstatus/src/main.cpp
new file mode 100644
index 00000000..8f5f1f95
--- /dev/null
+++ b/klinkstatus/src/main.cpp
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include "klinkstatus.h"
+
+#include <kapplication.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <iostream>
+using namespace std;
+
+
+static const char description[] =
+ I18N_NOOP("A Link Checker.\n\nKLinkStatus belongs to the kdewebdev module from KDE.");
+
+static const char version[] = "0.3.2";
+
+static KCmdLineOptions options[] =
+ {
+ { "+[URL]", I18N_NOOP( "Document to open" ), 0 },
+ KCmdLineLastOption
+ };
+
+int main(int argc, char *argv[])
+{
+ //____________________________________________________
+
+ KAboutData about("klinkstatus", I18N_NOOP("KLinkStatus"), version, description,
+ KAboutData::License_GPL_V2, "(C) 2004 Paulo Moura Guedes", 0,
+ "http://klinkstatus.kdewebdev.org");
+
+ about.addAuthor("Paulo Moura Guedes", 0, "moura@kdewebdev.org");
+
+ about.addCredit("Manuel Menezes de Sequeira", 0, 0, "http://home.iscte.pt/~mms/");
+ about.addCredit("Gonçalo Silva", 0, "gngs@paradigma.co.pt");
+ about.addCredit("Nuno Monteiro", 0, 0, "http://www.itsari.org");
+ about.addCredit("Eric Laffoon", 0, "sequitur@kde.org");
+ about.addCredit("Andras Mantia", 0, "amantia@kde.org");
+ about.addCredit("Michal Rudolf", 0, "mrudolf@kdewebdev.org");
+ about.addCredit("Mathieu Kooiman", 0, " quanta@map-is.nl");
+ about.addCredit("Jens Herden", 0, "jens@kdewebdev.org");
+ about.addCredit("Helge Hielscher", 0, "hhielscher@unternehmen.com");
+
+ KCmdLineArgs::init(argc, argv, &about);
+ KCmdLineArgs::addCmdLineOptions( options );
+
+ KApplication app;
+
+ // see if we are starting with session management
+ if (app.isRestored())
+ {
+ RESTORE(KLinkStatus);
+ }
+ else
+ {
+ // no session.. just start up normally
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ KLinkStatus *widget = new KLinkStatus;
+ widget->show();
+
+ if ( args->count() == 0 )
+ {
+ widget->load(KURL());
+ }
+ else
+ {
+ int i = 0;
+ for (; i < args->count(); i++ )
+ {
+ widget->load( args->url( i ) );
+ }
+ }
+ args->clear();
+ }
+
+ return app.exec();
+}
diff --git a/klinkstatus/src/parser/Makefile.am b/klinkstatus/src/parser/Makefile.am
new file mode 100644
index 00000000..b99146c1
--- /dev/null
+++ b/klinkstatus/src/parser/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = $(all_includes)
+METASOURCES = AUTO
+noinst_HEADERS = htmlparser.h http.h mstring.h node.h node_impl.h url.h
+libparser_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libparser.la
+libparser_la_SOURCES = htmlparser.cpp http.cpp mstring.cpp node.cpp url.cpp
diff --git a/klinkstatus/src/parser/htmlparser.cpp b/klinkstatus/src/parser/htmlparser.cpp
new file mode 100644
index 00000000..6bc93761
--- /dev/null
+++ b/klinkstatus/src/parser/htmlparser.cpp
@@ -0,0 +1,455 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include "htmlparser.h"
+
+#include <kapplication.h>
+#include <kdebug.h>
+
+
+HtmlParser::HtmlParser(QString const& documento)
+ : is_content_type_set_(false), document_(documento)
+{
+ Q_ASSERT(!documento.isEmpty());
+
+ stripScriptContent();
+ stripComments(); // after removing the script because comments in scripts have diferent sintaxe
+
+ nodes_.reserve(estimativaLinks(documento.length() * 2)); // à confiança ;)
+
+ parseNodesOfTypeA();
+ parseNodesOfTypeAREA();
+ parseNodesOfTypeLINK();
+ parseNodesOfTypeMETA();
+ parseNodesOfTypeIMG();
+ parseNodesOfTypeFRAME();
+ parseNodesOfTypeIFRAME();
+ parseNodesOfTypeBASE();
+ parseNodesOfTypeTITLE();
+}
+
+bool HtmlParser::hasBaseUrl() const
+{
+ return (node_BASE_.element() == Node::BASE &&
+ !node_BASE_.url().isEmpty());
+}
+
+NodeBASE const& HtmlParser::baseUrl() const
+{
+ Q_ASSERT(hasBaseUrl());
+ return node_BASE_;
+}
+
+NodeMETA const& HtmlParser::contentTypeMetaNode() const
+{
+ Q_ASSERT(hasContentType());
+ return node_META_content_type_;
+}
+
+bool HtmlParser::hasTitle() const
+{
+ return (node_TITLE_.element() == Node::TITLE &&
+ !node_TITLE_.attributeTITLE().isEmpty());
+}
+
+NodeTITLE const& HtmlParser::title() const
+{
+ Q_ASSERT(hasTitle());
+ return node_TITLE_;
+}
+
+vector<QString> const& HtmlParser::parseNodesOfType(QString const& element)
+{
+ HtmlParser::parseNodesOfType(element, document_, aux_);
+ return aux_;
+}
+
+void HtmlParser::parseNodesOfType(QString const& tipo, QString const& document, vector<QString>& nodes)
+{
+ QString node;
+ QString doc(document);
+ int inicio = 0, fim = 0;
+
+ nodes.clear();
+ if(upperCase(tipo) == "A")
+ nodes.reserve(estimativaLinks(doc.length() * 2));
+
+ while(true)
+ {
+ inicio = findSeparableWord(doc, "<" + tipo);
+ if(inicio == -1)
+ return;
+
+ //if( (doc[inicio] != ' ' && doc[inicio] != '\n' && doc[inicio] != '\r') )
+ if(!::isSpace(doc[inicio]))
+ {
+ doc.remove(0, QString("<" + tipo).length());
+ continue;
+ }
+
+ if(upperCase(tipo) == "A")
+ fim = findWord(doc, "</A>", inicio);
+ else
+ {
+ //fim = findChar(doc, '>', inicio + 1);
+ fim = endOfTag(doc, inicio, '>');
+ }
+
+ if(fim == -1)
+ {
+ doc.remove(0, 1);
+ continue;
+ }
+
+ int tag_begining_go_back = (tipo.length() + QString("<").length());
+ node = doc.mid(inicio - tag_begining_go_back,
+ fim - inicio + tag_begining_go_back);
+ nodes.push_back(node);
+ doc.remove(0, fim);
+ }
+}
+
+int HtmlParser::endOfTag(QString const& s, int index, QChar end_of_tag)
+{
+ if( (uint)index >= s.length() )
+ return -1;
+
+ int _end_of_tag = s.find(end_of_tag, index);
+ if(_end_of_tag == -1)
+ return _end_of_tag;
+
+ int open_aspas = s.find('"', index);
+ if(open_aspas == -1)
+ return _end_of_tag + 1;
+
+ else if(_end_of_tag < open_aspas)
+ return _end_of_tag + 1;
+
+ else if( ((uint)open_aspas + 1) >= s.length() - 1 )
+ return -1;
+
+ else
+ {
+ int close_aspas = s.find('"', open_aspas + 1);
+ if(close_aspas != -1)
+ return endOfTag(s, close_aspas + 1, end_of_tag);
+ else
+ {
+ kdDebug(23100) << "Mismatched quotes (\"): " << s.mid(index, _end_of_tag - index) << endl;
+ //return -1;
+ return _end_of_tag + 1;
+ }
+ }
+}
+
+vector<Node*> const& HtmlParser::nodes() const
+{
+ return nodes_;
+}
+
+
+void HtmlParser::parseNodesOfTypeA()
+{
+ vector<QString> const& aux = parseNodesOfType("A");
+
+ for(vector<QString>::size_type i = 0; i != aux.size(); ++i)
+ {
+ nodes_.push_back( new NodeA(aux[i]) );
+ }
+}
+
+void HtmlParser::parseNodesOfTypeAREA()
+{
+ vector<QString> const& aux = parseNodesOfType("AREA");
+
+ for(vector<QString>::size_type i = 0; i != aux.size(); ++i)
+ {
+ nodes_.push_back( new NodeAREA(aux[i]) );
+ }
+}
+
+void HtmlParser::parseNodesOfTypeLINK()
+{
+ vector<QString> const& aux = parseNodesOfType("LINK");
+
+ for(vector<QString>::size_type i = 0; i != aux.size(); ++i)
+ nodes_.push_back( new NodeLINK(aux[i]) );
+}
+
+void HtmlParser::parseNodesOfTypeMETA()
+{
+ vector<QString> const& aux = parseNodesOfType("META");
+
+ for(vector<QString>::size_type i = 0; i != aux.size(); ++i)
+ {
+ NodeMETA* node = new NodeMETA(aux[i]);
+ nodes_.push_back(node);
+
+ if(!is_content_type_set_ && node->atributoHTTP_EQUIV().lower() == QString("Content-Type").lower()) {
+ is_content_type_set_ = true;
+ node_META_content_type_.setNode(aux[i]);
+ }
+ }
+}
+
+QString HtmlParser::findCharsetInMetaElement(QString const& html)
+{
+ vector<QString> metaTags;
+ parseNodesOfType("META", html, metaTags);
+
+ for(vector<QString>::size_type i = 0; i != metaTags.size(); ++i)
+ {
+ NodeMETA node(metaTags[i]);
+
+ if(node.atributoHTTP_EQUIV().lower() == QString("Content-Type").lower()) {
+ return node.charset();
+ }
+ }
+ return QString();
+}
+
+void HtmlParser::parseNodesOfTypeIMG()
+{
+ vector<QString> const& aux = parseNodesOfType("IMG");
+
+ for(vector<QString>::size_type i = 0; i != aux.size(); ++i)
+ nodes_.push_back( new NodeIMG(aux[i]) );
+}
+
+void HtmlParser::parseNodesOfTypeFRAME()
+{
+ vector<QString> const& aux = parseNodesOfType("FRAME");
+
+ for(vector<QString>::size_type i = 0; i != aux.size(); ++i)
+ nodes_.push_back( new NodeFRAME(aux[i]) );
+}
+
+void HtmlParser::parseNodesOfTypeIFRAME()
+{
+ vector<QString> const& aux = parseNodesOfType("IFRAME");
+
+ for(vector<QString>::size_type i = 0; i != aux.size(); ++i)
+ nodes_.push_back( new NodeFRAME(aux[i]) );
+}
+
+void HtmlParser::parseNodesOfTypeBASE()
+{
+ QString node;
+ QString doc = document_;
+ int inicio = 0, fim = 0;
+
+ inicio = findSeparableWord(doc, "<BASE");
+ if(inicio == -1 || !doc[inicio].isSpace())
+ return;
+
+ fim = doc.find(">", inicio);
+ if(fim == -1)
+ return;
+
+ node = doc.mid(inicio, fim-inicio);
+ node_BASE_.setNode(node);
+}
+
+void HtmlParser::parseNodesOfTypeTITLE()
+{
+ QString node;
+ QString doc = document_;
+ int inicio = 0, fim = 0;
+
+ inicio = findSeparableWord(doc, "<TITLE>");
+ if(inicio == -1)
+ return;
+
+ fim = findSeparableWord(doc, "</TITLE>", inicio);
+ if(fim == -1)
+ return;
+
+ node = doc.mid(inicio, fim-inicio);
+
+ node_TITLE_.setNode(node);
+}
+
+
+void HtmlParser::stripComments()
+{
+ QString begin_comment = "<!--";
+ QString end_comment = "-->";
+ uint const begin_comment_length = begin_comment.length();
+
+ int inicio = -1;
+ do
+ {
+ inicio = findWord(document_, begin_comment);
+ if(inicio != -1)
+ {
+ int fim = findWord(document_, end_comment, inicio);
+ if(fim == -1)
+ {
+ kdDebug(23100) << "End of comment is missing!" << endl;
+ document_.remove(inicio - begin_comment_length, begin_comment_length);
+ }
+ else
+ {
+ comments_ += "\n" + document_.mid(inicio - begin_comment_length,
+ fim - inicio + begin_comment_length);
+ document_.remove(inicio - begin_comment_length, fim - inicio + begin_comment_length);
+ }
+ }
+ }
+ while(inicio != -1);
+}
+
+void HtmlParser::stripScriptContent()
+{
+ int inicio = -1;
+ QString const begin_script = "<script";
+ QString const end_script = "</script>";
+ uint const begin_script_length = begin_script.length();
+
+ do
+ {
+ inicio = findWord(document_, begin_script);
+ if(inicio != -1)
+ {
+ int fim = findWord(document_, end_script, inicio);
+
+ if(fim == -1)
+ {
+ kdDebug(23100) << "Malformed script tag!" << endl;
+ document_.remove(inicio - begin_script_length, begin_script_length);
+ }
+ else
+ {
+ script_ += "\n" + document_.mid(inicio - begin_script_length,
+ fim - inicio + begin_script_length);
+
+ document_.remove(inicio - begin_script_length,
+ fim - inicio + begin_script_length);
+ }
+ }
+ }
+ while(inicio != -1);
+}
+
+
+
+
+#include <iostream>
+void HtmlParser::mostra() const
+{
+ kdDebug(23100) << "\nA:\n\n";
+ for(unsigned int i = 0; i != nodes_.size(); ++i)
+ {
+ if(nodes_[i]->element() == Node::A)
+ kdDebug(23100) << nodes_[i]->url() << "\t" << nodes_[i]->linkLabel() << endl;
+ }
+ kdDebug(23100) << "____________________________________________________________________" << endl;
+
+ kdDebug(23100) << "\nLINK:\n\n";
+ for(unsigned int i = 0; i != nodes_.size(); ++i)
+ {
+ if(nodes_[i]->element() == Node::LINK)
+ kdDebug(23100) << nodes_[i]->url() << "\t" << nodes_[i]->linkLabel() << endl;
+ }
+ kdDebug(23100) << "____________________________________________________________________" << endl;
+
+ kdDebug(23100) << "\nMETA:\n";
+ for(unsigned int i = 0; i != nodes_.size(); ++i)
+ {
+ if(nodes_[i]->element() == Node::META)
+ {
+#if defined Q_WS_WIN
+ NodeMETA* nm = (NodeMETA*)nodes_[i];
+#else
+
+ NodeMETA* nm = dynamic_cast<NodeMETA*>(nodes_[i]);
+#endif
+
+ kdDebug(23100) << nm->url() << endl
+ << nm->atributoHTTP_EQUIV() << endl
+ << nm->atributoNAME() << endl
+ << nm->atributoCONTENT() << endl;
+ }
+ }
+ kdDebug(23100) << "____________________________________________________________________" << endl;
+
+ kdDebug(23100) << "\nIMG:\n\n";
+ for(unsigned int i = 0; i != nodes_.size(); ++i)
+ {
+ if(nodes_[i]->element() == Node::IMG)
+ kdDebug(23100) << nodes_[i]->url() << "\t"
+ << nodes_[i]->linkLabel() << endl;
+ }
+ kdDebug(23100) << "____________________________________________________________________" << endl;
+
+ kdDebug(23100) << "\nFRAME:\n\n";
+ for(unsigned int i = 0; i != nodes_.size(); ++i)
+ {
+ if(nodes_[i]->element() == Node::FRAME)
+ kdDebug(23100) << nodes_[i]->url() << endl;
+ }
+ kdDebug(23100) << "____________________________________________________________________" << endl;
+
+ kdDebug(23100) << "\nBASE:\n\n";
+ kdDebug(23100) << node_BASE_.url() << endl;
+
+ kdDebug(23100) << "____________________________________________________________________" << endl;
+
+}
+
+#ifdef HTMLPARSER
+
+#include <fstream>
+
+int main()
+{
+ //ifstream stream("aterraprometida.html");
+ //ifstream stream("/var/www/html/STL/standard_library.html");
+ //ifstream stream("/var/www/html/qt-doc/functions.html");
+ ifstream stream("/var/www/html/index.html");
+
+ QString content;
+ while(stream)
+ {
+ char c;
+ stream.get(c);
+ content += c;
+ }
+ // kdDebug(23100) << content << endl;
+ kdDebug(23100) << "__________________________________________________________" << endl;
+ HtmlParser parser(content);
+ parser.mostra();
+ kdDebug(23100) << "__________________________________________________________\n\n\n" << endl;
+ vector<Node*> nods = parser.nodes();
+ for(int i = 0; i != nods.size(); ++i)
+ {
+ if(nods[i]->element() == Node::META)
+ {
+ NodeMETA* nod_meta = (NodeMETA*)(nods[i]);
+ //Node* nod_meta = nods[i];
+
+ kdDebug(23100) << nod_meta->atributoCONTENT() << endl;
+ }
+
+ }
+}
+
+
+#endif
diff --git a/klinkstatus/src/parser/htmlparser.h b/klinkstatus/src/parser/htmlparser.h
new file mode 100644
index 00000000..cf487ebf
--- /dev/null
+++ b/klinkstatus/src/parser/htmlparser.h
@@ -0,0 +1,124 @@
+ /***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 HTML_PARSER_H
+#define HTML_PARSER_H
+
+#include <qstring.h>
+
+#include <vector>
+
+
+#include "mstring.h"
+#include "node.h"
+
+#include <iostream>
+
+using namespace std;
+
+typedef unsigned int uint;
+
+
+
+class HtmlParser
+{
+public:
+
+ HtmlParser();
+ HtmlParser(QString const& documento);
+ ~HtmlParser();
+
+ vector<Node*> const& nodes() const;
+ bool hasBaseUrl() const;
+ bool hasTitle() const;
+ bool hasContentType() const;
+ NodeBASE const& baseUrl() const;
+ NodeTITLE const& title() const;
+ NodeMETA const& contentTypeMetaNode() const;
+
+ static uint estimativaLinks(uint doc_size);
+ /**
+ * Convenience function for performance as it only parse in order
+ * to get the charset.
+ */
+ static QString findCharsetInMetaElement(QString const& html);
+
+ // test:
+ void mostra() const;
+
+private:
+
+ vector<QString> const& parseNodesOfType(QString const& element);
+ /**
+ * Vector nodes passed for performance.
+ */
+ static void parseNodesOfType(QString const& element, QString const& doc, vector<QString>& nodes);
+
+ void parseNodesOfTypeA();
+ void parseNodesOfTypeAREA();
+ void parseNodesOfTypeLINK();
+ void parseNodesOfTypeMETA();
+ void parseNodesOfTypeIMG();
+ void parseNodesOfTypeFRAME();
+ void parseNodesOfTypeIFRAME();
+ void parseNodesOfTypeBASE();
+ void parseNodesOfTypeTITLE();
+
+ void stripComments();
+ void stripScriptContent();
+
+ /**
+ Return the index of the next character of the end of tag.
+ e.g.
+ endOfTag("<img src=\"bad > luck\">") => 22 (not 15)
+ */
+ static int endOfTag(QString const& s, int index = 0, QChar end_of_tag = '>');
+
+private:
+
+ vector<QString> aux_; // for what the hell is this? looks ugly... maybe I was drunk, can't remember
+ vector<Node*> nodes_;
+ NodeBASE node_BASE_;
+ NodeTITLE node_TITLE_;
+ NodeMETA node_META_content_type_;
+ bool is_content_type_set_;
+
+ QString document_;
+ QString script_; // Fica aqui guardado (JavaScript, etc)
+ QString comments_;
+};
+
+
+inline HtmlParser::~HtmlParser()
+{
+ //kdDebug(23100) << "*";
+}
+
+inline uint HtmlParser::estimativaLinks(uint doc_size)
+{
+ return doc_size / 100; // valor estimado...
+}
+
+inline bool HtmlParser::hasContentType() const
+{
+ return is_content_type_set_;
+}
+
+#endif
diff --git a/klinkstatus/src/parser/http.cpp b/klinkstatus/src/parser/http.cpp
new file mode 100644
index 00000000..1133c937
--- /dev/null
+++ b/klinkstatus/src/parser/http.cpp
@@ -0,0 +1,87 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include "http.h"
+#include "mstring.h"
+
+#include <kdebug.h>
+
+#include <iostream>
+
+
+
+void HttpResponseHeader::parseLocation()
+{
+ QString cabecalho(toString());
+
+ int location = findWord(cabecalho, "Location: ");
+ Q_ASSERT(location != -1);
+
+ int fim_de_linha_1 = cabecalho.find('\n', location);
+ int fim_de_linha_2 = cabecalho.find('\r', location);
+
+ Q_ASSERT(fim_de_linha_1 != -1 || fim_de_linha_2 != -1);
+
+ int fim_de_linha;
+
+ if(fim_de_linha_1 == -1 && fim_de_linha_2 != -1)
+ fim_de_linha = fim_de_linha_2;
+
+ else if(fim_de_linha_2 == -1 && fim_de_linha_1 != -1)
+ fim_de_linha = fim_de_linha_1;
+
+ else if(fim_de_linha_1 < fim_de_linha_2)
+ fim_de_linha = fim_de_linha_1;
+
+ else fim_de_linha = fim_de_linha_2;
+
+ location_ = cabecalho.mid(location, fim_de_linha - location);
+}
+
+QString HttpResponseHeader::charset() const
+{
+ return HttpResponseHeader::charset(value("content-type"));
+}
+
+QString HttpResponseHeader::charset(QString const& contentTypeHttpHeaderLine)
+{
+ QString _charset;
+
+ if(contentTypeHttpHeaderLine.isEmpty())
+ return _charset;
+
+ int index = contentTypeHttpHeaderLine.find("charset=");
+ if(index != -1)
+ index += QString("charset=").length();
+ else {
+ index = contentTypeHttpHeaderLine.find("charset:");
+ if(index != -1)
+ index += QString("charset:").length();
+ }
+
+ if(index != -1) {
+ _charset = contentTypeHttpHeaderLine.mid(index);
+ _charset = _charset.stripWhiteSpace();
+ }
+
+// kdDebug(23100) << "Charset: |" << _charset << "|" << endl;
+ return _charset;
+
+}
diff --git a/klinkstatus/src/parser/http.h b/klinkstatus/src/parser/http.h
new file mode 100644
index 00000000..5878cfd1
--- /dev/null
+++ b/klinkstatus/src/parser/http.h
@@ -0,0 +1,79 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 HTTP_H
+#define HTTP_H
+
+#include <qhttp.h>
+#include <qstring.h>
+
+
+class HttpResponseHeader: public QHttpResponseHeader
+{
+public:
+
+ HttpResponseHeader();
+ HttpResponseHeader(const QHttpResponseHeader & header);
+ HttpResponseHeader(QString const& str);
+ virtual ~HttpResponseHeader();
+
+ void parseLocation();
+ QString const& location() const;
+ QString charset() const;
+
+ /**
+ * Parses the charset from this kind of server response:
+ * Content-Type: text/html; charset=EUC-JP
+ * Return an empty string in case it doesn't find nothing.
+ */
+ static QString charset(QString const& contentTypeHttpHeaderLine);
+
+private:
+
+ QString location_;
+};
+
+
+inline HttpResponseHeader::HttpResponseHeader()
+ : QHttpResponseHeader()
+{
+}
+
+inline HttpResponseHeader::HttpResponseHeader(const QHttpResponseHeader & /*header*/)
+ : QHttpResponseHeader()
+{
+}
+
+inline HttpResponseHeader::HttpResponseHeader(QString const& str)
+ : QHttpResponseHeader()
+{
+ parse(str);
+}
+
+inline HttpResponseHeader::~HttpResponseHeader()
+{
+}
+
+inline QString const& HttpResponseHeader::location() const
+{
+ return location_;
+}
+
+#endif
diff --git a/klinkstatus/src/parser/mstring.cpp b/klinkstatus/src/parser/mstring.cpp
new file mode 100644
index 00000000..114d6dc6
--- /dev/null
+++ b/klinkstatus/src/parser/mstring.cpp
@@ -0,0 +1,278 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include "mstring.h"
+
+#include <iostream>
+
+
+using namespace std;
+
+
+int findWord(QString const& s, QString const& palavra, uint a_partir_do_indice)
+{
+ int indice = s.find(palavra, a_partir_do_indice, false);
+
+ if(indice == -1)
+ return indice;
+ else
+ return (indice + palavra.length());
+}
+
+int findChar(QString const& s, QChar letra, uint a_partir_do_indice)
+{
+ int index = s.find(letra, a_partir_do_indice, false);
+ if(index == -1)
+ return index;
+ else
+ return index + 1;
+}
+
+/**
+ The string palavra, must not have any spaces.
+*/
+int findSeparableWord(QString const& s_, QString const& palavra, uint a_partir_do_indice)
+{
+ bool encontrou = true;
+ QString s(s_);
+ uint indice_palavra = 0;
+ int indice = a_partir_do_indice;
+
+ do
+ {
+ encontrou = true;
+ indice_palavra = 0;
+
+ indice = findChar(s, (palavra[indice_palavra++]), indice );
+
+ if(indice == -1)
+ {
+ return indice;
+ }
+ --indice;
+
+ while(encontrou && indice_palavra != palavra.length() && indice < (int)s.length())
+ {
+ indice = nextNonSpaceChar(s, indice);
+
+ if(indice == -1)
+ return indice;
+
+ // Nao se incrementa o indice porque isso j��feito com a fun�o nextNonSpaceChar
+ encontrou = encontrou && !(notEqual(s[indice], palavra[indice_palavra++]) );
+
+ }
+ }
+ while(!encontrou && indice < (int)s.length());
+
+ if(encontrou && indice < (int)s.length())
+ return ++indice;
+ else
+ return -1;
+}
+
+int nextNonSpaceChar(QString const& s, uint i)
+{
+ ++i;
+ // while( (s[i] == ' ' || s[i] == '\t' || s[i] == '\r' || s[i] == '\n')
+ while(isSpace(s[i])
+ && i < s.length() )
+ ++i;
+
+ if(i < s.length())
+ return i;
+ else
+ return -1;
+}
+
+
+/**
+ e.g.
+ nextSpaceChar("o biltre") => 1
+*/
+int nextSpaceChar(QString const& s, uint i)
+{
+ //while( (s[i] != ' ' && s[i] != '\r' && s[i] != '\n' && s[i] != '\t') &&
+ //i < s.size() )
+ while(!isSpace(s[i]) &&
+ i < s.length() )
+ ++i;
+
+ if(i < s.length())
+ return i;
+ else
+ return -1;
+}
+
+int nextCharDifferentThan(QChar c, QString const& s, uint i)
+{
+ while(i < s.length() && s[i] == c)
+ ++i;
+
+ if(i != s.length())
+ return i;
+ else
+ return -1;
+}
+
+vector<QString> tokenize(QString s)
+{
+ Q_ASSERT(!s.isEmpty());
+ vector<QString> v;
+
+ while(true)
+ {
+ int inicio = 0;
+ //if(s[0] == ' ' || s[0] == '\t' || s[0] == '\r' || s[0] == '\n')
+ if(isSpace(s[0]))
+ inicio = nextNonSpaceChar(s, 0);
+ if(inicio == -1)
+ return v;
+
+ int fim = nextSpaceChar(s, inicio);
+ if(fim == -1)
+ {
+ v.push_back(s.mid(inicio));
+ return v;
+ }
+ else
+ {
+ QString palavra = s.mid(inicio, fim - inicio);
+ v.push_back(palavra);
+ s.remove(0, fim);
+ }
+ }
+}
+
+vector<QString> tokenizeWordsSeparatedByDots(QString s)
+{
+ vector<QString> v;
+
+ while(true)
+ {
+ int inicio = 0;
+ if(s[0] == '.')
+ inicio = nextCharDifferentThan(QChar('.'), s, 0);
+ if(inicio == -1)
+ return v;
+
+ int fim = s.find('.', inicio);
+ if(fim == -1)
+ {
+ v.push_back(s.mid(inicio));
+ return v;
+ }
+ else
+ {
+ QString palavra = s.mid(inicio, fim - inicio);
+ v.push_back(palavra);
+ s.remove(0, fim);
+ }
+ }
+}
+
+vector<QString> tokenizeWordsSeparatedBy(QString s, QChar criteria)
+{
+ vector<QString> v;
+
+ while(true)
+ {
+ int inicio = 0;
+ if(s[0] == criteria)
+ inicio = nextCharDifferentThan(criteria, s, 0);
+ if(inicio == -1)
+ return v;
+
+ int fim = s.find(criteria, inicio);
+ if(fim == -1)
+ {
+ v.push_back(s.mid(inicio));
+ return v;
+ }
+ else
+ {
+ QString palavra = s.mid(inicio, fim - inicio);
+ v.push_back(palavra);
+ s.remove(0, fim);
+ }
+ }
+}
+
+
+
+#ifdef STRING
+//c++ -g -o teste_string mstring.cpp -DSTRING
+#include <fstream>
+
+int main(int argc, char* argv[])
+{
+ string s;
+ s = "S";
+ s = "Afazer";
+ s = "O MeU S sdadsadd ";
+ s = "www.trolltech.com/search/qt-interest/bla bla%20Bla";
+ s = "...http://w.ww..go.o.gle.p.t.......";
+
+ /*
+ ifstream stream("testeparser.html");
+ string content;
+ while(stream) {
+ char c;
+ stream.get(c);
+ content += c;
+ }
+ */
+ // kdDebug(23100) << simplifyWhiteSpace(content) << endl;
+ kdDebug(23100) << simplifyWhiteSpace(s) << endl;
+
+ /*
+ vector<string> v(tokenize(s));
+ for(int i = 0; i != v.size(); ++i)
+ kdDebug(23100) << v[i] << endl;
+ */
+
+ /*
+ int i = nextSpaceChar(s, 0);
+ i = nextNonSpaceChar(s, i);
+ kdDebug(23100) << s.substr(i) << endl;
+ */
+
+
+ vector<string> v(tokenizeWordsSeparatedByDots(s));
+ for(int i = 0; i != v.size(); ++i)
+ kdDebug(23100) << v[i] << endl;
+
+ removeLastCharIfExists(s, '/');
+ kdDebug(23100) << s << endl;
+
+ /*
+ kdDebug(23100) << findChar(s, 'T') << endl;
+ kdDebug(23100) << findWord(s, "trolltech") << endl;
+ kdDebug(23100) << findWord(s, "TROLLTECH") << endl;
+ kdDebug(23100) << findWord(s, "TROLLTECH", 2) << endl;
+ */
+ /*
+ stripWhiteSpace(s);
+ kdDebug(23100) << s << endl;
+ */
+}
+
+
+#endif
diff --git a/klinkstatus/src/parser/mstring.h b/klinkstatus/src/parser/mstring.h
new file mode 100644
index 00000000..cd359c7d
--- /dev/null
+++ b/klinkstatus/src/parser/mstring.h
@@ -0,0 +1,174 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 STRING_H
+#define STRING_H
+
+#include <qstring.h>
+
+#include <vector>
+#include <cctype>
+
+class QString;
+
+typedef unsigned int uint;
+
+
+/* Similar to std::string::find but return the next index of the last char
+ of the first word it finds.
+ Case insensitive.
+ e.g.
+ findWord("Biltre larvado", "biltre") => 6
+*/
+int findWord(QString const& s, QString const& palavra, uint a_partir_do_indice = 0);
+
+/**
+ Similar to std::string::find but return the next index of the first char
+ it finds.
+ Case insensitive.
+*/
+int findChar(QString const& s, QChar letra, uint a_partir_do_indice = 0);
+
+/**
+ Same as findWord but non space chars are eliminated.
+ e.g.
+ findWord("<a href=""></a>", "<a") => 2
+ findSeparableWord("<a href=""></a>", "<a") => 2
+
+ findWord("<\na href=""></a>", "<a") => -1
+ findSeparableWord("<\na href=""></a>", "<a") => 3
+*/
+int findSeparableWord(QString const& s, QString const& palavra, uint a_partir_do_indice = 0);
+
+/**
+ Space means Unicode characters with decimal values
+ 9 (TAB), 10 (LF), 11 (VT), 12 (FF), 13 (CR), and 32 (Space).
+*/
+bool isSpace(QChar c);
+
+/**
+ Return -1 if unsuccessful.
+*/
+int nextNonSpaceChar(QString const& s, uint i);
+int nextNonSpaceCharReverse(QString const& s, uint i);
+int nextSpaceChar(QString const& s, uint i);
+
+int nextCharDifferentThan(QChar c, QString const& s, uint i);
+
+/** Return a vector with the words */
+std::vector<QString> tokenize(QString s);
+std::vector<QString> tokenizeWordsSeparatedByDots(QString s);
+std::vector<QString> tokenizeWordsSeparatedBy(QString s, QChar criteria);
+
+/**
+ Returns a string that has whitespace removed from the start and the end,
+ and which has each sequence of internal whitespace replaced with a single space.
+*/
+QString simplifyWhiteSpace(QString const& s);
+
+/**
+ If char 'caractere' is the last in the string 's' it is removed
+*/
+void removeLastCharIfExists(QString& s, QChar caractere);
+
+QString upperCase(QString const& s);
+QString lowerCase(QString const& s);
+
+/**
+ Remove whitespaces from the end of the string
+*/
+void stripWhiteSpaceFromTheEnd(QString& s);
+
+/**
+ Returns a string that has whitespace removed from the start and the end.
+*/
+void stripWhiteSpace(QString& s);
+
+/**
+ Case insensitive comparisons
+*/
+bool equal(QString const& s1, QString const& s2);
+bool notEqual(QString const& s1, QString const& s2);
+
+bool equal(QChar c1, QChar c2);
+bool notEqual(QChar c1, QChar c2);
+
+
+//_________________________________________________________________________
+
+inline bool isSpace(QChar c)
+{
+ return c.isSpace();
+}
+
+inline bool equal(QString const& s1, QString const& s2)
+{
+ if(s1 == s2)
+ return true;
+ else
+ return s1.lower() == s2.lower();
+}
+
+inline bool notEqual(QString const& s1, QString const& s2)
+{
+ return !(equal(s1, s2));
+}
+
+inline bool equal(QChar c1, QChar c2)
+{
+ return c1.lower() == c2.lower();
+}
+
+inline bool notEqual(QChar c1, QChar c2)
+{
+ return !(equal(c1, c2));
+}
+
+inline QString upperCase(QString const& s)
+{
+ return s.upper();
+}
+
+inline QString lowerCase(QString const& s)
+{
+ return s.lower();
+}
+
+inline QString simplifyWhiteSpace(QString const& s)
+{
+ return s.simplifyWhiteSpace();
+}
+
+inline void removeLastCharIfExists(QString& s, QChar caractere)
+{
+ int index = s.length() - 1;
+ if(s[index] == caractere)
+ s.remove(index);
+}
+
+inline void stripWhiteSpace(QString& s)
+{
+ s = s.stripWhiteSpace();
+}
+
+
+
+
+#endif
diff --git a/klinkstatus/src/parser/node.cpp b/klinkstatus/src/parser/node.cpp
new file mode 100644
index 00000000..068184ae
--- /dev/null
+++ b/klinkstatus/src/parser/node.cpp
@@ -0,0 +1,255 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include "node.h"
+#include "mstring.h"
+#include "url.h"
+#include "../utils/utils.h"
+
+
+/*
+ Node________________________________________________________________________
+*/
+
+QString Node::getAttribute(QString const& atributo)
+{
+ QString attribute_;
+ int fim = - 1;
+ bool tem_aspas_ou_plicas = false;
+
+ int inicio = findWord(content_, atributo);
+ if(inicio != -1)
+ {
+ if(content_[inicio] == '"')
+ {
+ fim = content_.find("\"", inicio + 1);
+ tem_aspas_ou_plicas = true;
+ }
+ else if(content_[inicio] == '\'')
+ {
+ fim = content_.find("'", inicio + 1);
+ tem_aspas_ou_plicas = true;
+ }
+ else
+ {
+ int fim_bloco = nextSpaceChar(content_, inicio + 1);
+ int fim_tag = content_.find(">", inicio + 1);
+ int fim_aspas = content_.find("\"", inicio + 1);
+
+ if(fim_bloco == -1 && fim_tag == -1 && fim_aspas == -1)
+ {
+ attribute_ = content_;
+ malformed_ = true;
+ return attribute_;
+ }
+
+ if(smallerUnsigned(fim_bloco, fim_tag) == -1 &&
+ smallerUnsigned(fim_bloco, fim_aspas) == -1)
+ fim = fim_bloco;
+
+ else if(smallerUnsigned(fim_tag, fim_aspas) == -1)
+ fim = fim_tag;
+
+ else
+ fim = fim_aspas;
+ }
+
+ if(fim == -1)
+ {
+ attribute_ = content_;
+ malformed_ = true;
+ return attribute_;
+ }
+
+ attribute_ = content_.mid(inicio, fim-inicio);
+
+ if(tem_aspas_ou_plicas)
+ {
+ attribute_ = attribute_.mid(1, attribute_.length() - 1);
+ }
+ else
+ {
+ ::stripWhiteSpace(attribute_);
+ }
+ }
+
+ else
+ {
+ attribute_ = "";
+ }
+ ::decode(attribute_);
+
+ return attribute_;
+}
+
+
+/*
+ NodeLink________________________________________________________________________
+*/
+
+void NodeLink::parseAttributeHREF()
+{
+ if(findWord(content(), "HREF") == -1 &&
+ findWord(content(), "NAME") == -1 &&
+ findWord(content(), "TARGET") == -1)
+ {
+ kdDebug(23100) << "MALFORMED: " << endl
+ << "NodeLink::parseAttributeHREF: " << content() << endl;
+ setMalformed(true);
+ return;
+ }
+
+ else if(findWord(content(), "HREF") != -1)
+ {
+ attribute_href_ = getAttribute("HREF=");
+
+ if( !(malformed() || attribute_href_.isEmpty()) )
+ {
+ // Definnishr o tipo de link
+ linktype_ = Url::resolveLinkType(attribute_href_);
+
+ parseLinkLabel();
+ }
+ }
+}
+
+void NodeLink::parseLinkLabel()
+{
+ int fim_tag = 0;
+ char proximo_caractere = ' ';
+
+ do
+ {
+ fim_tag = content_.find(">", fim_tag);
+
+ if(fim_tag != -1)
+ proximo_caractere = QChar(content_[++fim_tag]);
+
+ }
+ while(fim_tag != -1 && proximo_caractere == '<'/*If the label starts by <*/);
+
+ if(fim_tag != -1)
+ {
+ int fim_label = content_.find("<", fim_tag);
+
+ if(fim_label != -1)
+ {
+ link_label_ =
+ ::simplifyWhiteSpace(content_.mid(fim_tag,
+ fim_label - fim_tag));
+ }
+ }
+}
+
+
+/*
+ NodeMETA________________________________________________________________________
+*/
+
+void NodeMETA::parseAttributeURL()
+{
+ if(attribute_http_equiv_.isEmpty())
+ parseAttributeHTTP_EQUIV();
+
+ if(upperCase(attribute_http_equiv_) == "REFRESH")
+ {
+ is_redirection_ = true;
+
+ if(findWord(content(), "URL") == -1)
+ {
+ //setMalformed(true);
+ return;
+ }
+
+ attribute_url_ = getAttribute("URL=");
+
+ int aspas = -1;
+ do
+ {
+ aspas = attribute_url_.find("\"");
+ if(aspas != -1)
+ attribute_url_.remove(aspas, 1);
+ }
+ while(aspas != -1);
+
+ if(attribute_url_.isEmpty())
+ kdDebug(23100) << "void NodeMeta::parseAttributeURL(): Assertion `!attribute_url_.isEmpty()' failed.\n"
+ << content_ << endl << attribute_http_equiv_ << endl << attribute_url_ << endl;
+ Q_ASSERT(!attribute_url_.isEmpty());
+
+ linktype_ = Url::resolveLinkType(attribute_url_);
+ }
+}
+
+QString NodeMETA::charset() const
+{
+ QString charset;
+ QString content(atributoCONTENT());
+
+ if(content.isEmpty())
+ return charset;
+
+ int index = content.find("charset=");
+ if(index != -1)
+ {
+ index += QString("charset=").length();
+ charset = content.mid(index, content.length() - index);
+ charset = charset.stripWhiteSpace();
+ }
+
+// kdDebug(23100) << "Charset: |" << charset << "|" << endl;
+ return charset;
+}
+
+/*
+ NodeIMG________________________________________________________________________
+*/
+
+void NodeIMG::parseAttributeSRC()
+{
+ if(findWord(content(), "SRC") == -1)
+ {
+ kdDebug(23100) << "MALFORMED_____________________________________________________________" << endl;
+ kdDebug(23100) << "Conteudo: " << content() << endl;
+ setMalformed(true);
+ return;
+ }
+
+ attribute_src_ = getAttribute("SRC=");
+ linktype_ = Url::resolveLinkType(attribute_src_);
+}
+
+
+/*
+ NodeFRAME________________________________________________________________________
+*/
+
+void NodeFRAME::parseAttributeSRC()
+{
+ if(findWord(content(), "SRC") == -1)
+ {
+ //setMalformed(true);
+ return;
+ }
+
+ attribute_src_ = getAttribute("SRC=");
+ linktype_ = Url::resolveLinkType(attribute_src_);
+}
+
diff --git a/klinkstatus/src/parser/node.h b/klinkstatus/src/parser/node.h
new file mode 100644
index 00000000..1d0b1fc3
--- /dev/null
+++ b/klinkstatus/src/parser/node.h
@@ -0,0 +1,279 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 NODULO_H
+#define NODULO_H
+
+#include "mstring.h"
+
+#include <qstring.h>
+
+#include <kdebug.h>
+#include <kcharsets.h>
+
+using namespace std;
+
+typedef unsigned int uint;
+
+
+class Node
+{
+public:
+
+ enum Element {
+ A,
+ AREA,
+ LINK,
+ META,
+ IMG,
+ FRAME,
+ BASE,
+ TITLE
+ };
+ enum LinkType {
+ href,
+ file_href,
+ mailto,
+ relative
+ };
+
+ Node();
+ Node(QString const& content);
+ virtual ~Node();
+
+ QString getAttribute(QString const& atributo);
+ virtual QString const& url() const = 0;
+ virtual QString const& linkLabel() const = 0; // URL label
+ virtual void setNode(QString const& content);
+ virtual void parse() = 0;
+ void setMalformed(bool flag = true);
+ virtual void setLinkType(LinkType const& lt);
+
+ QString const& content() const;
+ bool malformed() const;
+ LinkType linkType() const;
+ Element element() const;
+ virtual bool isLink() const = 0;
+
+ bool isRedirection() const;
+
+protected:
+
+ Element element_;
+ LinkType linktype_;
+ QString link_label_;
+ QString content_;
+ bool is_redirection_;
+ bool malformed_;
+};
+
+
+class NodeLink: public Node
+{
+public:
+ NodeLink();
+ NodeLink(QString const& content);
+ ~NodeLink()
+ {}
+ ;
+
+ virtual void parse();
+
+ virtual QString const& url() const;
+ virtual QString const& linkLabel() const; // URL label
+ virtual QString mailto() const;
+ virtual bool isLink() const;
+
+private:
+ virtual void parseAttributeHREF();
+ void parseLinkLabel();
+
+private:
+ QString attribute_href_;
+};
+
+class NodeA: public NodeLink
+{
+public:
+ NodeA(QString const& content);
+ ~NodeA()
+ {}
+ ;
+ QString const& attributeNAME() const;
+
+ virtual void parse();
+
+private:
+ void parseAttributeNAME();
+
+private:
+ QString attribute_name_;
+};
+
+class NodeAREA: public NodeLink
+{
+public:
+ NodeAREA(QString const& content);
+ ~NodeAREA() {};
+
+ QString const& attributeTITLE() const;
+
+ virtual void parse();
+
+private:
+ void parseAttributeTITLE();
+
+private:
+ QString attribute_title_;
+};
+
+
+class NodeLINK: public NodeLink
+{
+public:
+ NodeLINK(QString const& content);
+ ~NodeLINK()
+ {}
+ ;
+};
+
+class NodeMETA: public Node
+{
+public:
+ NodeMETA();
+ NodeMETA(QString const& content);
+ ~NodeMETA()
+ {}
+ ;
+
+ virtual QString const& url() const;
+ virtual const QString& linkLabel() const;
+ virtual bool isLink() const;
+ QString const& atributoHTTP_EQUIV() const;
+ QString const& atributoNAME() const;
+ QString const& atributoCONTENT() const;
+ QString charset() const;
+ bool isRedirection() const;
+
+ virtual void parse();
+
+private:
+ /**
+ Procura se existem os atributos HTTP-EQUIV=Refresh e URL=...
+ Se existir considera o content do atributo URL como um link.
+ ex: <META HTTP-EQUIV=Refresh CONTENT="10; URL=http://www.htmlhelp.com/">
+ */
+ void parseAttributeURL();
+
+ void parseAttributeHTTP_EQUIV();
+ void parseAttributeNAME();
+ void parseAttributeCONTENT();
+
+private:
+ QString attribute_http_equiv_;
+ QString attribute_url_;
+ QString attribute_name_;
+ QString attribute_content_;
+};
+
+class NodeIMG: public Node
+{
+public:
+ NodeIMG(QString const& content);
+ ~NodeIMG()
+ {}
+ ;
+
+ virtual void parse();
+
+ virtual QString const& url() const;
+ virtual QString const& linkLabel() const; // Image label
+ virtual bool isLink() const;
+
+private:
+ void parseAttributeSRC();
+ void parseAttributeTITLE();
+ void parseAttributeALT();
+
+private:
+ QString attribute_src_;
+ QString attribute_title_;
+ QString attribute_alt_;
+};
+
+class NodeFRAME: public Node
+{
+public:
+ NodeFRAME(QString const& content);
+ ~NodeFRAME()
+ {}
+ ;
+
+ virtual void parse();
+ virtual QString const& url() const;
+ virtual QString const& linkLabel() const;
+ virtual bool isLink() const;
+
+private:
+ void parseAttributeSRC();
+
+private:
+ QString attribute_src_;
+};
+
+class NodeBASE: public NodeLink
+{
+public:
+ NodeBASE();
+ NodeBASE(QString const& content);
+ ~NodeBASE()
+ {}
+ ;
+
+ virtual bool isLink() const;
+};
+
+class NodeTITLE: public Node
+{
+public:
+ NodeTITLE();
+ NodeTITLE(QString const& content);
+ ~NodeTITLE()
+ {}
+ ;
+
+ virtual QString const& url() const;
+ virtual QString const& linkLabel() const;
+ virtual void parse();
+ virtual bool isLink() const;
+
+ QString const& attributeTITLE() const;
+
+private:
+ void parseAttributeTITLE();
+
+private:
+ QString attribute_title_;
+};
+
+
+#include "node_impl.h"
+
+#endif
diff --git a/klinkstatus/src/parser/node_impl.h b/klinkstatus/src/parser/node_impl.h
new file mode 100644
index 00000000..51249075
--- /dev/null
+++ b/klinkstatus/src/parser/node_impl.h
@@ -0,0 +1,412 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+inline Node::Node()
+ : is_redirection_(false), malformed_(false)
+{}
+
+inline Node::~Node()
+{
+ //kdDebug(23100) << "/";
+}
+
+inline Node::Node(QString const& content)
+ : content_(content), is_redirection_(false), malformed_(false)
+{}
+
+inline void Node::setNode(QString const& content)
+{
+ content_ = content;
+ parse();
+}
+
+inline QString const& Node::content() const
+{
+ return content_;
+}
+
+inline bool Node::malformed() const
+{
+ return malformed_;
+}
+
+inline void Node::setMalformed(bool flag)
+{
+ malformed_ = flag;
+}
+
+inline Node::LinkType Node::linkType() const
+{
+ return linktype_;
+}
+
+inline Node::Element Node::element() const
+{
+ return element_;
+}
+
+inline void Node::setLinkType(Node::LinkType const& lt)
+{
+ linktype_ = lt;
+}
+
+inline bool Node::isRedirection() const
+{
+ return is_redirection_;
+}
+
+// class NodeLink_______________________________________________________
+
+inline NodeLink::NodeLink()
+ : Node()
+{}
+
+inline NodeLink::NodeLink(QString const& content)
+ : Node(content)
+{
+ parse();
+}
+
+inline void NodeLink::parse()
+{
+ parseAttributeHREF();
+}
+
+inline QString const& NodeLink::url() const
+{
+ return attribute_href_;
+}
+
+inline QString const& NodeLink::linkLabel() const
+{
+ return link_label_;
+}
+
+inline QString NodeLink::mailto() const
+{
+ Q_ASSERT(linktype_ == Node::mailto);
+
+ QString href = KCharsets::resolveEntities(attribute_href_);
+
+ int inicio = findWord(href, "MAILTO:");
+ Q_ASSERT(inicio != -1);
+
+ return href.mid(inicio);
+}
+
+inline bool NodeLink::isLink() const
+{
+ if(Node::linkType() != Node::mailto && !url().isEmpty())
+ return true;
+ else
+ return false;
+}
+
+// class NodeA_______________________________________________________
+
+inline NodeA::NodeA(QString const& content)
+ : NodeLink(content)
+{
+ element_ = A;
+ parse();
+}
+
+inline QString const& NodeA::attributeNAME() const
+{
+ return attribute_name_;
+}
+
+inline void NodeA::parse()
+{
+ parseAttributeNAME();
+}
+
+inline void NodeA::parseAttributeNAME()
+{
+ attribute_name_ = getAttribute("NAME=");
+ //kdDebug(23100) << "NodeA::parseAttributeNAME: " << attribute_name_ << endl;
+}
+
+// class NodeAREA_______________________________________________________
+
+inline NodeAREA::NodeAREA(QString const& content)
+ : NodeLink(content)
+{
+ element_ = AREA;
+ parse();
+}
+
+inline QString const& NodeAREA::attributeTITLE() const
+{
+ return attribute_title_;
+}
+
+inline void NodeAREA::parse()
+{
+ parseAttributeTITLE();
+}
+
+inline void NodeAREA::parseAttributeTITLE()
+{
+ attribute_title_ = getAttribute("TITLE=");
+//kdDebug(23100) << "NodeAREA::parseAttributeTITLE: " << attribute_title_ << endl;
+}
+
+// class NodeLINK________________________________________
+
+inline NodeLINK::NodeLINK(QString const& content)
+ : NodeLink(content)
+{
+ element_ = LINK;
+}
+
+// class NodeMeta________________________________________
+
+inline NodeMETA::NodeMETA()
+ : Node()
+{
+ element_ = META;
+}
+
+inline NodeMETA::NodeMETA(QString const& content)
+ : Node(content)
+{
+ element_ = META;
+ parse();
+}
+
+inline QString const& NodeMETA::url() const
+{
+ return attribute_url_;
+}
+
+inline const QString& NodeMETA::linkLabel() const
+{
+ return link_label_;
+}
+
+inline bool NodeMETA::isLink() const
+{
+ if(upperCase(attribute_http_equiv_) == "REFRESH" &&
+ findWord(content(), "URL") != -1)
+ {
+ // Q_ASSERT(findWord(content(), "URL") != -1); // not necessarily
+ return true;
+ }
+ else
+ return false;
+}
+
+inline QString const& NodeMETA::atributoHTTP_EQUIV() const
+{
+ return attribute_http_equiv_;
+}
+
+inline QString const& NodeMETA::atributoNAME() const
+{
+ return attribute_name_;
+}
+
+inline QString const& NodeMETA::atributoCONTENT() const
+{
+ return attribute_content_;
+}
+
+inline bool NodeMETA::isRedirection() const
+{
+ return
+ upperCase(attribute_http_equiv_) == "REFRESH";
+}
+
+inline void NodeMETA::parse()
+{
+ parseAttributeHTTP_EQUIV();
+ parseAttributeNAME();
+ parseAttributeCONTENT();
+
+ parseAttributeURL();
+}
+
+inline void NodeMETA::parseAttributeHTTP_EQUIV()
+{
+ attribute_http_equiv_ = getAttribute("HTTP-EQUIV=");
+}
+
+inline void NodeMETA::parseAttributeNAME()
+{
+ attribute_name_ = getAttribute("NAME=");
+}
+
+inline void NodeMETA::parseAttributeCONTENT()
+{
+ attribute_content_ = getAttribute("CONTENT=");
+// kdDebug(23100) << "CONTENT: " << attribute_content_ << endl;
+}
+
+
+// class NodeIMG________________________________________
+
+inline NodeIMG::NodeIMG(QString const& content)
+ : Node(content)
+{
+ element_ = IMG;
+ parse();
+}
+
+inline void NodeIMG::parse()
+{
+ parseAttributeSRC();
+ parseAttributeTITLE();
+ parseAttributeALT();
+}
+
+inline QString const& NodeIMG::url() const
+{
+ return attribute_src_;
+}
+
+inline QString const& NodeIMG::linkLabel() const
+{
+ if(!attribute_title_.isEmpty())
+ return attribute_title_;
+ else
+ return attribute_alt_;
+}
+
+inline bool NodeIMG::isLink() const
+{
+ if(!url().isEmpty())
+ return true;
+ else
+ return false;
+}
+
+inline void NodeIMG::parseAttributeTITLE()
+{
+ attribute_title_ = getAttribute("TITLE=");
+}
+
+inline void NodeIMG::parseAttributeALT()
+{
+ attribute_alt_ = getAttribute("ALT=");
+}
+
+
+// class NodeFRAME________________________________________
+
+inline NodeFRAME::NodeFRAME(QString const& content)
+ : Node(content)
+{
+ element_ = FRAME;
+ parse();
+}
+
+inline void NodeFRAME::parse()
+{
+ parseAttributeSRC();
+}
+
+inline QString const& NodeFRAME::url() const
+{
+ return attribute_src_;
+}
+
+inline QString const& NodeFRAME::linkLabel() const
+{
+ return link_label_;
+}
+
+inline bool NodeFRAME::isLink() const
+{
+ if(!url().isEmpty())
+ return true;
+ else
+ return false;
+}
+
+// class NodeBASE________________________________________
+
+inline NodeBASE::NodeBASE()
+ : NodeLink()
+{
+ element_ = BASE;
+}
+
+inline NodeBASE::NodeBASE(QString const& content)
+ : NodeLink(content)
+{
+ element_ = BASE;
+}
+
+inline bool NodeBASE::isLink() const
+{
+ return false;
+}
+
+// class NodeTITLE________________________________________
+
+inline NodeTITLE::NodeTITLE()
+ : Node()
+{
+ element_ = TITLE;
+ parse();
+}
+
+inline NodeTITLE::NodeTITLE(QString const& content)
+ : Node(content)
+{
+ element_ = TITLE;
+ parse();
+}
+
+inline QString const& NodeTITLE::url() const
+{
+ return QString::null;
+}
+
+inline QString const& NodeTITLE::linkLabel() const
+{
+ return QString::null;
+}
+
+inline void NodeTITLE::parse()
+{
+ parseAttributeTITLE();
+}
+
+inline bool NodeTITLE::isLink() const
+{
+ return false;
+}
+
+inline QString const& NodeTITLE::attributeTITLE() const
+{
+ return attribute_title_;
+}
+
+inline void NodeTITLE::parseAttributeTITLE()
+{
+ attribute_title_ = content_;
+ attribute_title_.replace("<TITLE>", "", false);
+ attribute_title_.replace("</TITLE>", "", false);
+ attribute_title_.stripWhiteSpace();
+
+ //kdDebug(23100) << "TITLE: " << attribute_title_ << endl;
+}
diff --git a/klinkstatus/src/parser/url.cpp b/klinkstatus/src/parser/url.cpp
new file mode 100644
index 00000000..f7f1f6f8
--- /dev/null
+++ b/klinkstatus/src/parser/url.cpp
@@ -0,0 +1,350 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include <kresolver.h>
+
+#include "url.h"
+#include "mstring.h"
+#include "../utils/utils.h"
+
+#include <kcharsets.h>
+
+
+Node::LinkType Url::resolveLinkType(QString const& url)
+{
+ QString aux(url);
+ aux = KURL::decode_string(aux);
+
+ if(aux.isNull())
+ return Node::relative;
+
+ if(findWord(url, "FILE:") != -1)
+ return Node::file_href;
+ else if(findWord(KCharsets::resolveEntities(url), "MAILTO:") != -1)
+ return Node::mailto;
+ else if( (int)url.find(":/") != -1)
+ return Node::href;
+ else
+ return Node::relative;
+}
+
+KURL Url::normalizeUrl(QString const& string_url, LinkStatus const& link_parent, QString const& document_root)
+{
+ QString _string_url = string_url.stripWhiteSpace();
+
+ QString s_url;
+ KURL base_url;
+
+ // resolve base url
+ if(link_parent.hasBaseURI())
+ base_url = link_parent.baseURI();
+ else
+ base_url = link_parent.absoluteUrl();
+
+ // resolve relative url
+ if(_string_url.isEmpty())
+ return base_url;
+ else if(Url::hasProtocol(_string_url))
+ return KURL(_string_url);
+ else
+ {
+ s_url.prepend(base_url.protocol() + "://" + base_url.host());
+
+ if(_string_url[0] == '/') {
+ if(!base_url.protocol().startsWith("http")) {
+ s_url.append(document_root);
+ }
+ }
+ else {
+ s_url.append(base_url.directory(true, false) + "/");
+ }
+
+ if( (_string_url[0] == ';' || // parameters
+ _string_url[0] == '?' || // query
+ _string_url[0] == '#') ) // fragment or reference
+ {
+ s_url.append(base_url.fileName(false));
+ }
+
+ s_url.append(_string_url);
+ KURL url(s_url);
+ if(base_url.hasUser())
+ url.setUser(base_url.user());
+ if(base_url.hasPass())
+ url.setPass(base_url.pass());
+
+ url.setPort(base_url.port());
+
+ url.cleanPath();
+
+// kdDebug(23100) << "Normalized URL: "
+// << KCharsets::resolveEntities(KURL::decode_string(url.url())) << endl;
+
+ return KURL(KCharsets::resolveEntities(KURL::decode_string(url.url())));
+ }
+}
+
+KURL Url::normalizeUrl(QString const& string_url)
+{
+ QString qs_url(KCharsets::resolveEntities(string_url.stripWhiteSpace()));
+
+ if(qs_url[0] == '/')
+ {
+ KURL url;
+ url.setPath(qs_url);
+ url.cleanPath();
+ return url;
+ }
+
+ else
+ {
+ if(!Url::hasProtocol(qs_url))
+ qs_url.prepend("http://");
+
+ KURL url(qs_url);
+ url.cleanPath();
+ return url;
+ }
+}
+
+bool Url::existUrl(KURL const& url, vector<LinkStatus*> const& v)
+{
+ if(url.prettyURL().isEmpty())
+ return true;
+
+ for(uint i = 0; i != v.size(); ++i)
+ if(v[i]->absoluteUrl() == url)
+ return true;
+
+ return false;
+}
+
+/**
+ www.iscte.pt, iscte.pt => true;
+ iscte.pt, www.iscte.pt => true;
+ www.iscte.pt, alunos.iscte.pt => true; (if restrict = false)
+ www.iscte.pt, alunos.iscte.pt => false; (if restrict = true)
+ alunos.iscte.pt, www.iscte.pt => false;
+ alunos.iscte.pt, iscte.pt => false.
+*/
+// FIXME - Rename this function to sameDomain
+bool Url::equalHost(QString const& host1, QString const& host2, bool restrict)
+{
+ //Q_ASSERT(!host1.isEmpty());
+ //Q_ASSERT(!host2.isEmpty()); // this fails if href="javascript:......."
+ //if(host2.isEmpty())
+ //return false;
+
+ if(host1 == host2)
+ return true;
+
+ QString host1_(KNetwork::KResolver::normalizeDomain(host1));
+ QString host2_(KNetwork::KResolver::normalizeDomain(host2));
+ removeLastCharIfExists(host1_, '/');
+ removeLastCharIfExists(host2_, '/');
+
+ vector<QString> v1 = tokenizeWordsSeparatedByDots(host1_);
+ vector<QString> v2 = tokenizeWordsSeparatedByDots(host2_);
+ uint const size1 = v1.size();
+ uint const size2 = v2.size();
+
+ if( !(size1 >= 1 && size2 >= 1) && // localhost would have size = 1
+ !(host1_[0].isNumber() || host2_[0].isNumber()) ) // not (host == IP)
+ {
+ kdDebug(23100) << "Invalid host: " << host2 << endl;
+ return false;
+ }
+
+ vector<QString>::size_type aux = 0;
+ vector<QString>::size_type aux2 = 0;
+ if(v1[0] == "www")
+ aux = 1;
+ if(v2[0] == "www")
+ aux2 = 1;
+
+ if((size2 - aux2 < size1 - aux) && restrict) // e.g. paradigma.co.pt < linkstatus.paradigma.co.pt
+ return false;
+
+ if(restrict && (size2 - aux2 > size1 - aux)) // e.g. linkstatus.paradigma.co.pt > paradigma.co.pt
+ return false;
+
+ int i = 1;
+ while( ((int)(size1 - i) >= (int)aux) && ((int)(size2 - i) >= (int)aux) )
+ {
+ if( !(v1[size1 - i] == v2[size2 - i]) )
+ return false;
+
+ ++i;
+ }
+
+ return true;
+}
+
+/* This should be done by parsing but I wan't to know when some new scheme comes along :) */
+bool Url::hasProtocol(QString const& url)
+{
+ QString s_url(url);
+ s_url.stripWhiteSpace();
+
+ if(s_url[0] == '/')
+ return false;
+
+ else
+ {
+ KURL url = KURL::fromPathOrURL(s_url);
+ if(!url.protocol().isEmpty())
+ return true;
+ /*
+ if(s_url.startsWith("http:") ||
+ s_url.startsWith("https:") ||
+ s_url.startsWith("ftp:") ||
+ s_url.startsWith("sftp:") ||
+ s_url.startsWith("webdav:") ||
+ s_url.startsWith("webdavs:") ||
+ s_url.startsWith("finger:") ||
+ s_url.startsWith("fish:") ||
+ s_url.startsWith("imap:") ||
+ s_url.startsWith("imaps:") ||
+ s_url.startsWith("lan:") ||
+ s_url.startsWith("ldap:") ||
+ s_url.startsWith("pop3:") ||
+ s_url.startsWith("pop3s:") ||
+ s_url.startsWith("smtp:") ||
+ s_url.startsWith("smtps:") ||
+ s_url.startsWith("file:") ||
+ s_url.startsWith("news:") ||
+ s_url.startsWith("gopher:") ||
+ s_url.startsWith("mailto:") ||
+ s_url.startsWith("telnet:") ||
+ s_url.startsWith("prospero:") ||
+ s_url.startsWith("wais:") ||
+ s_url.startsWith("nntp:") )
+ {
+ return true;
+ }
+ */
+ else
+ return false;
+ }
+}
+
+/**
+ http://linkstatus.paradigma.co.pt/en/index.html&bix=bix -> /en/index.html&bix=bix
+*/
+QString Url::convertToLocal(LinkStatus const* ls)
+{
+ KURL url = ls->absoluteUrl();
+ KURL base_url = ls->rootUrl();
+
+ if(base_url == url)
+ return "./" + url.fileName();
+ else
+ return KURL::relativeURL(base_url, url);
+}
+
+/**
+ If url2 has the same domain has url1 returns true.
+ If restrict, sourceforge.net != quanta.sourceforge.net.
+ Else is equal.
+*/
+bool Url::localDomain(KURL const& url1, KURL const& url2, bool restrict)
+{
+ if(url1.protocol() != url2.protocol())
+ {
+ //kdDebug(23100) << "NOT localDomain" << endl;
+ return false;
+ }
+ else if(!url1.hasHost())
+ {
+ //kdDebug(23100) << "localDomain" << endl;
+ return true;
+ }
+ else
+ {
+ //return ::equalHost(url1.host(), url2.host(), restrict);
+ if(Url::equalHost(url1.host(), url2.host(), restrict))
+ {
+ //kdDebug(23100) << "localDomain" << endl;
+ return true;
+ }
+ else
+ {
+ //kdDebug(23100) << "NOT localDomain" << endl;
+ return false;
+ }
+
+ }
+}
+
+/**
+ Returns true if url2 is a parent of url1.
+*/
+bool Url::parentDir(KURL const& url1, KURL const& url2)
+{
+ if(url1.protocol() != url2.protocol())
+ return false;
+
+ else if(!url1.hasHost())
+ return url2.isParentOf(url1);
+
+ else
+ {
+ if(!equalHost(url1.host(), url2.host()))
+ return false;
+
+ vector<QString> tokens_1 = tokenizeWordsSeparatedBy(url1.directory(true, false), QChar('/'));
+ vector<QString> tokens_2 = tokenizeWordsSeparatedBy(url2.directory(true, false), QChar('/'));
+
+ if(tokens_1.size() == 0)
+ return false;
+
+ //if(tokens_2.size() > tokens_1.size() or tokens_2.size() == 0)
+ //return true;
+ vector<QString>::size_type size = 0;
+ if(tokens_1.size() < tokens_2.size())
+ size = tokens_1.size();
+ else
+ size = tokens_2.size();
+
+ for(vector<QString>::size_type i = 0; i != size; ++i)
+ {
+ if(tokens_2[i] != tokens_1[i])
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Url::externalLink(KURL const& url1, KURL const& url2, bool restrict)
+{
+ if(url1.protocol() != url2.protocol())
+ {
+ kdDebug(23100) << "externalLink" << endl;
+ return true;
+ }
+ else if(!url1.hasHost() && !url2.hasHost())
+ {
+ kdDebug(23100) << "NOT externalLink" << endl;
+ return false;
+ }
+ else
+ return !Url::equalHost(url1.host(), url2.host(), restrict);
+}
diff --git a/klinkstatus/src/parser/url.h b/klinkstatus/src/parser/url.h
new file mode 100644
index 00000000..6f22743d
--- /dev/null
+++ b/klinkstatus/src/parser/url.h
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 URL_H
+#define URL_H
+
+#include "../engine/linkstatus.h"
+#include "node.h"
+
+#include <kurl.h>
+#include <qstring.h>
+
+#include <vector>
+
+using namespace std;
+
+
+class LinkStatus;
+
+namespace Url
+{
+Node::LinkType resolveLinkType(QString const& url);
+KURL normalizeUrl(QString const& string_url, LinkStatus const& link_parent, QString const& document_root);
+KURL normalizeUrl(QString const& string_url);
+bool validUrl(KURL const& url);
+bool existUrl(KURL const& url, vector<LinkStatus*> const& v);
+bool equalHost(QString const& host1, QString const& host2, bool restrict = false);
+bool hasProtocol(QString const& url);
+QString convertToLocal(LinkStatus const* ls);
+bool localDomain(KURL const& url1, KURL const& url2, bool restrict = true);
+bool parentDir(KURL const& url1, KURL const& url2);
+bool externalLink(KURL const& url1, KURL const& url2, bool restrict = true);
+}
+
+inline bool validUrl(KURL const& url)
+{
+ return (url.isValid() /*&& url.hasHost()*/);
+}
+
+#endif
diff --git a/klinkstatus/src/tests/data/entities/bull&bladder.jpg b/klinkstatus/src/tests/data/entities/bull&bladder.jpg
new file mode 100644
index 00000000..f83f6c72
--- /dev/null
+++ b/klinkstatus/src/tests/data/entities/bull&bladder.jpg
Binary files differ
diff --git a/klinkstatus/src/tests/data/entities/bull_bladder.jpg b/klinkstatus/src/tests/data/entities/bull_bladder.jpg
new file mode 100644
index 00000000..f83f6c72
--- /dev/null
+++ b/klinkstatus/src/tests/data/entities/bull_bladder.jpg
Binary files differ
diff --git a/klinkstatus/src/tests/data/entities/link_with_html_entities.html b/klinkstatus/src/tests/data/entities/link_with_html_entities.html
new file mode 100644
index 00000000..e4d919c2
--- /dev/null
+++ b/klinkstatus/src/tests/data/entities/link_with_html_entities.html
@@ -0,0 +1,7 @@
+<!--
+ - Both links should not be broken.
+ - The visible text should be bull&bladder.jpg.
+-->
+<img src="bull_bladder.jpg">
+<br/><br/>
+<img src="bull&amp;bladder.jpg"/>
diff --git a/klinkstatus/src/ui/Makefile.am b/klinkstatus/src/ui/Makefile.am
new file mode 100644
index 00000000..7f687d39
--- /dev/null
+++ b/klinkstatus/src/ui/Makefile.am
@@ -0,0 +1,11 @@
+INCLUDES = -I$(top_srcdir)/src -I$(top_builddir)/klinkstatus/src/cfg \
+ -I$(top_builddir)/klinkstatus/src $(all_includes)
+METASOURCES = AUTO
+noinst_HEADERS = sessionwidget.h tabwidgetsession.h klshistorycombo.h \
+ resultview.h resultssearchbar.h
+libui_la_LDFLAGS = $(all_libraries)
+libui_la_LIBADD = $(top_builddir)/klinkstatus/src/cfg/libcfg.la
+noinst_LTLIBRARIES = libui.la
+libui_la_SOURCES = sessionwidgetbase.ui sessionwidget.cpp tabwidgetsession.cpp \
+ klshistorycombo.cpp resultview.cpp treeview.cpp resultssearchbar.cpp documentrootdialog.cpp
+SUBDIRS = settings
diff --git a/klinkstatus/src/ui/celltooltip.cpp b/klinkstatus/src/ui/celltooltip.cpp
new file mode 100644
index 00000000..b449cbce
--- /dev/null
+++ b/klinkstatus/src/ui/celltooltip.cpp
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include "celltooltip.h"
+#include "tablelinkstatus.h"
+
+#include <qscrollview.h>
+
+#include <iostream>
+using namespace std;
+
+
+CellToolTip::CellToolTip ( TableLinkstatus * table, QToolTipGroup * group)
+ : QToolTip(table->viewport(), group), table_(table)
+{}
+
+void CellToolTip::maybeTip ( const QPoint & p )
+{
+ QPoint cp = table_->viewportToContents(p);
+
+ int row = table_->rowAt(cp.y());
+ int col = table_->columnAt(cp.x());
+
+ if( row != -1 && col != -1)
+ {
+ if(col == 0 || !table_->textFitsInCell(row, col))
+ {
+ TableItem* item = table_->myItem(row, col);
+ QString tip_string = item->toolTip();
+
+ QRect cr = table_->cellGeometry( row, col );
+ cr.moveTopLeft( table_->contentsToViewport( cr.topLeft() ) );
+
+ tip(cr, tip_string);
+ }
+ }
+}
diff --git a/klinkstatus/src/ui/celltooltip.h b/klinkstatus/src/ui/celltooltip.h
new file mode 100644
index 00000000..33136ceb
--- /dev/null
+++ b/klinkstatus/src/ui/celltooltip.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 CELLTOOLTIP_H
+#define CELLTOOLTIP_H
+
+#include <qtooltip.h>
+
+
+class TableLinkstatus;
+
+class CellToolTip: public QToolTip
+{
+public:
+
+ CellToolTip ( TableLinkstatus * table, QToolTipGroup * group = 0 );
+
+protected:
+
+ virtual void maybeTip ( const QPoint & p );
+
+private:
+
+ TableLinkstatus * table_;
+};
+
+
+#endif
diff --git a/klinkstatus/src/ui/documentrootdialog.cpp b/klinkstatus/src/ui/documentrootdialog.cpp
new file mode 100644
index 00000000..c2ed3e7d
--- /dev/null
+++ b/klinkstatus/src/ui/documentrootdialog.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+#include "documentrootdialog.h"
+
+#include <kurlrequester.h>
+#include <klocale.h>
+#include <kurl.h>
+
+#include <qstring.h>
+#include <qlayout.h>
+#include <qlabel.h>
+
+
+DocumentRootDialog::DocumentRootDialog(QWidget *parent, QString const& url)
+ : KDialogBase(parent, "DocumentRootDialog", true, "Choose a Document Root",
+ KDialogBase::Ok, KDialogBase::Ok, true),
+ m_url(url)
+{
+ QWidget* page = new QWidget(this);
+ setMainWidget(page);
+ QVBoxLayout* topLayout = new QVBoxLayout(page, 0, spacingHint());
+
+ QLabel* label = new QLabel(i18n("As you are using a protocol different than HTTP, \nthere is no way to guess where the document root is, \nin order to resolve relative URLs like the ones started with \"/\".\n\nPlease specify one:"), page);
+ topLayout->addWidget(label);
+
+ m_urlRequester = new KURLRequester(page);
+ m_urlRequester->setURL(url);
+ m_urlRequester->setMinimumWidth(fontMetrics().maxWidth()*20);
+ m_urlRequester->setFocus();
+ topLayout->addWidget(m_urlRequester);
+
+ topLayout->addStretch(10);
+
+ // setInitialSize(configDialogSize("klinkstatus"));
+
+ m_urlRequester->setMode(KFile::Directory);
+// enableButtonOK(false);
+
+ connect(m_urlRequester, SIGNAL(textChanged (const QString &)),
+ this, SLOT(slotTextChanged (const QString &)));
+ connect(m_urlRequester, SIGNAL(returnPressed (const QString &)),
+ this, SLOT(slotReturnPressed (const QString &)));
+ connect(m_urlRequester, SIGNAL(urlSelected (const QString &)),
+ this, SLOT(slotTextChanged (const QString &)));
+}
+
+DocumentRootDialog::~DocumentRootDialog()
+{
+ saveDialogSize("klinkstatus", true);
+}
+
+void DocumentRootDialog::slotReturnPressed( const QString & )
+{
+ slotOk();
+}
+
+void DocumentRootDialog::slotTextChanged( const QString & s)
+{
+ KURL url(s);
+ enableButtonOK(!s.isEmpty() && url.isValid());
+}
+
+void DocumentRootDialog::slotOk( )
+{
+ m_url = m_urlRequester->url();
+
+ KDialogBase::slotOk();
+}
+
+
+
+#include "documentrootdialog.moc"
diff --git a/klinkstatus/src/ui/documentrootdialog.h b/klinkstatus/src/ui/documentrootdialog.h
new file mode 100644
index 00000000..52696727
--- /dev/null
+++ b/klinkstatus/src/ui/documentrootdialog.h
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 DOCUMENTROOTDIALOG_H
+#define DOCUMENTROOTDIALOG_H
+
+#include <kdialogbase.h>
+
+class KURLRequester;
+
+/**
+ @author Paulo Moura Guedes <moura@kdewebdev.org>
+*/
+class DocumentRootDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ DocumentRootDialog(QWidget *parent, QString const& url);
+ ~DocumentRootDialog();
+
+ void setUrl(const QString& theValue) { m_url = theValue; }
+ QString url() const { return m_url; }
+
+
+protected:
+ virtual void closeEvent (QCloseEvent*) {}
+
+protected slots:
+ virtual void reject() {}
+ virtual void slotOk();
+
+private slots:
+ void slotTextChanged(const QString &);
+ void slotReturnPressed(const QString &);
+
+private:
+ KURLRequester* m_urlRequester;
+ QString m_url;
+};
+
+#endif
diff --git a/klinkstatus/src/ui/klshistorycombo.cpp b/klinkstatus/src/ui/klshistorycombo.cpp
new file mode 100644
index 00000000..36deb385
--- /dev/null
+++ b/klinkstatus/src/ui/klshistorycombo.cpp
@@ -0,0 +1,198 @@
+//
+// C++ Implementation: klshistorycombo
+//
+// Description:
+//
+//
+// Author: Paulo Moura Guedes <moura@kdewebdev.org>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "klshistorycombo.h"
+#include "klsconfig.h"
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kcompletionbox.h>
+#include <kdebug.h>
+#include <kstdaccel.h>
+#include <kurldrag.h>
+#include <kglobalsettings.h>
+
+
+bool KLSHistoryCombo::items_saved_ = false;
+
+
+KLSHistoryCombo::KLSHistoryCombo(QWidget *parent, const char *name)
+ : KHistoryCombo(parent, name)
+{
+ setMaxCount(KLSConfig::maxCountComboUrl());
+
+ setDuplicatesEnabled(false);
+ setAutoCompletion(false);
+
+ connect(this, SIGNAL(activated(const QString& )),
+ this, SLOT(addToHistory(const QString& )));
+}
+
+KLSHistoryCombo::~KLSHistoryCombo()
+{}
+
+void KLSHistoryCombo::init()
+{
+ loadItems();
+}
+
+void KLSHistoryCombo::saveItems()
+{
+ if(items_saved_)
+ return;
+
+ QStringList items = historyItems();
+
+ KLSConfig::setComboUrlHistory(items);
+ KLSConfig::writeConfig();
+
+ items_saved_ = true;
+}
+
+void KLSHistoryCombo::loadItems()
+{
+ clear();
+
+ QStringList items = KLSConfig::comboUrlHistory();
+
+ bool block = signalsBlocked();
+ blockSignals( true );
+
+ setHistoryItems(items);
+ blockSignals(block);
+
+ completionObject()->setItems(items);
+
+ setCompletionMode(KGlobalSettings::completionMode());
+}
+
+bool KLSHistoryCombo::eventFilter( QObject *o, QEvent *ev )
+{
+ // Handle Ctrl+Del/Backspace etc better than the Qt widget, which always
+ // jumps to the next whitespace.
+ QLineEdit *edit = lineEdit();
+ if ( o == edit )
+ {
+ int type = ev->type();
+ if ( type == QEvent::KeyPress )
+ {
+ QKeyEvent *e = static_cast<QKeyEvent *>( ev );
+
+ if ( e->key() == Key_Return || e->key() == Key_Enter )
+ {
+ //m_modifier = e->state();
+ return false;
+ }
+
+ int delete_word_back = KStdAccel::deleteWordBack().keyCodeQt();
+ int delete_word_forward = KStdAccel::deleteWordForward().keyCodeQt();
+
+ if ( KKey( e ) == KKey(delete_word_back) ||
+ KKey( e ) == KKey(delete_word_forward) ||
+ ((e->state() & ControlButton) &&
+ (e->key() == Key_Left || e->key() == Key_Right) ) )
+ {
+ selectWord(e);
+ e->accept();
+ return true;
+ }
+ }
+
+ else if ( type == QEvent::MouseButtonDblClick )
+ {
+ edit->selectAll();
+ return true;
+ }
+ }
+ return KComboBox::eventFilter( o, ev );
+}
+
+/*
+ Handle Ctrl+Cursor etc better than the Qt widget, which always
+ jumps to the next whitespace. This code additionally jumps to
+ the next [/#?:], which makes more sense for URLs. The list of
+ chars that will stop the cursor are '/', '.', '?', '#', ':'.
+*/
+void KLSHistoryCombo::selectWord(QKeyEvent *e)
+{
+ QLineEdit* edit = lineEdit();
+ QString text = edit->text();
+ int pos = edit->cursorPosition();
+ int pos_old = pos;
+ int count = 0;
+
+ // TODO: make these a parameter when in kdelibs/kdeui...
+ QValueList<QChar> chars;
+ chars << QChar('/') << QChar('.') << QChar('?') << QChar('#') << QChar(':');
+ bool allow_space_break = true;
+
+ if( e->key() == Key_Left || e->key() == Key_Backspace )
+ {
+ do
+ {
+ pos--;
+ count++;
+ if( allow_space_break && text[pos].isSpace() && count > 1 )
+ break;
+ }
+ while( pos >= 0 && (chars.findIndex(text[pos]) == -1 || count <= 1) );
+
+ if( e->state() & ShiftButton )
+ {
+ edit->cursorForward(true, 1-count);
+ }
+ else if( e->key() == Key_Backspace )
+ {
+ edit->cursorForward(false, 1-count);
+ QString text = edit->text();
+ int pos_to_right = edit->text().length() - pos_old;
+ QString cut = text.left(edit->cursorPosition()) + text.right(pos_to_right);
+ edit->setText(cut);
+ edit->setCursorPosition(pos_old-count+1);
+ }
+ else
+ {
+ edit->cursorForward(false, 1-count);
+ }
+ }
+ else if( e->key() == Key_Right || e->key() == Key_Delete )
+ {
+ do
+ {
+ pos++;
+ count++;
+ if( allow_space_break && text[pos].isSpace() )
+ break;
+ }
+ while( pos < (int) text.length() && chars.findIndex(text[pos]) == -1 );
+
+ if( e->state() & ShiftButton )
+ {
+ edit->cursorForward(true, count+1);
+ }
+ else if( e->key() == Key_Delete )
+ {
+ edit->cursorForward(false, -count-1);
+ QString text = edit->text();
+ int pos_to_right = text.length() - pos - 1;
+ QString cut = text.left(pos_old) +
+ (pos_to_right > 0 ? text.right(pos_to_right) : QString() );
+ edit->setText(cut);
+ edit->setCursorPosition(pos_old);
+ }
+ else
+ {
+ edit->cursorForward(false, count+1);
+ }
+ }
+}
+
+#include "klshistorycombo.moc"
diff --git a/klinkstatus/src/ui/klshistorycombo.h b/klinkstatus/src/ui/klshistorycombo.h
new file mode 100644
index 00000000..ab990a1e
--- /dev/null
+++ b/klinkstatus/src/ui/klshistorycombo.h
@@ -0,0 +1,42 @@
+//
+// C++ Interface: klshistorycombo
+//
+// Description:
+//
+//
+// Author: Paulo Moura Guedes <moura@kdewebdev.org>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef KLSHISTORYCOMBO_H
+#define KLSHISTORYCOMBO_H
+
+#include <kcombobox.h>
+class KConfig;
+
+/**
+@author Paulo Moura Guedes
+Based on KonqCombo
+*/
+class KLSHistoryCombo : public KHistoryCombo
+{
+ Q_OBJECT
+
+public:
+ KLSHistoryCombo(QWidget* parent, const char* name);
+ ~KLSHistoryCombo();
+
+ void init();
+ void loadItems();
+ void saveItems();
+
+protected:
+ virtual bool eventFilter(QObject* o, QEvent* ev);
+ void selectWord(QKeyEvent* e);
+
+private:
+ static bool items_saved_;
+};
+
+#endif
diff --git a/klinkstatus/src/ui/resultssearchbar.cpp b/klinkstatus/src/ui/resultssearchbar.cpp
new file mode 100644
index 00000000..7f772b54
--- /dev/null
+++ b/klinkstatus/src/ui/resultssearchbar.cpp
@@ -0,0 +1,236 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include "resultssearchbar.h"
+
+#include <kcombobox.h>
+#include <kiconloader.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+
+#include <qapplication.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qpixmap.h>
+#include <qstring.h>
+#include <qtimer.h>
+#include <qtoolbutton.h>
+#include <qtooltip.h>
+#include <qlayout.h>
+
+
+class ResultsSearchBar::ResultsSearchBarPrivate
+{
+public:
+ ResultsSearchBarPrivate()
+ : layout(0), searchLine(0), searchCombo(0), delay(400), m_lastComboIndex(0)
+ {}
+
+ QString searchText;
+ QTimer timer;
+ QHBoxLayout* layout;
+ KLineEdit* searchLine;
+ KComboBox* searchCombo;
+ int delay;
+ int m_lastComboIndex;
+};
+
+ResultsSearchBar::ResultsSearchBar(QWidget* parent, const char* name)
+ : QWidget(parent, name), d(new ResultsSearchBar::ResultsSearchBarPrivate)
+{
+ setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+
+ d->layout = new QHBoxLayout(this);
+ d->layout->setMargin(2);
+ d->layout->setSpacing(5);
+
+ QToolButton* clearButton = new QToolButton(this);
+ clearButton->setIconSet(SmallIconSet(QApplication::reverseLayout() ? "clear_left" : "locationbar_erase"));
+ clearButton->setAutoRaise(true);
+ d->layout->addWidget(clearButton);
+
+ QLabel* searchLabel = new QLabel(this);
+ searchLabel->setText(i18n("S&earch:"));
+ d->layout->addWidget(searchLabel);
+
+ d->searchLine = new KLineEdit(this, "searchline");
+ connect(d->searchLine, SIGNAL(textChanged(const QString &)),
+ this, SLOT(slotSearchStringChanged(const QString &)));
+
+ searchLabel->setBuddy(d->searchLine);
+ d->layout->addWidget(d->searchLine);
+
+ QLabel* statusLabel = new QLabel(this);
+ statusLabel->setText( i18n("Status:") );
+ d->layout->addWidget(statusLabel);
+
+ d->searchCombo = new KComboBox(this, "searchcombo");
+ QPixmap iconAll = KGlobal::iconLoader()->loadIcon("exec", KIcon::Small);
+ QPixmap iconGood = KGlobal::iconLoader()->loadIcon("ok", KIcon::Small);
+ QPixmap iconBroken = KGlobal::iconLoader()->loadIcon("no", KIcon::Small);
+ QPixmap iconMalformed = KGlobal::iconLoader()->loadIcon("bug", KIcon::Small);
+ QPixmap iconUndetermined = KGlobal::iconLoader()->loadIcon("help", KIcon::Small);
+
+ d->searchCombo->insertItem(iconAll, i18n("All Links"));
+ d->searchCombo->insertItem(iconGood, i18n("Good Links"));
+ d->searchCombo->insertItem(iconBroken, i18n("Broken Links"));
+ d->searchCombo->insertItem(iconMalformed, i18n("Malformed Links"));
+ d->searchCombo->insertItem(iconUndetermined, i18n("Undetermined Links"));
+ d->layout->addWidget(d->searchCombo);
+
+ QToolTip::add(clearButton, i18n("Clear filter"));
+ QToolTip::add( d->searchLine, i18n("Enter the terms to filter the result link list"));
+ QToolTip::add( d->searchCombo, i18n("Choose what kind of link status to show in result list"));
+
+ connect(clearButton, SIGNAL( clicked() ),
+ this, SLOT(slotClearSearch()) );
+
+ connect(d->searchCombo, SIGNAL(activated(int)),
+ this, SLOT(slotSearchComboChanged(int)));
+
+ connect(&(d->timer), SIGNAL(timeout()), this, SLOT(slotActivateSearch()));
+}
+
+ResultsSearchBar::~ResultsSearchBar()
+{
+ delete d;
+ d = 0;
+}
+
+QString const& ResultsSearchBar::text() const
+{
+ return d->searchText;
+}
+
+int ResultsSearchBar::status() const
+{
+ return d->searchCombo->currentItem();
+}
+
+void ResultsSearchBar::setDelay(int ms)
+{
+ d->delay = ms;
+}
+
+int ResultsSearchBar::delay() const
+{
+ return d->delay;
+}
+
+void ResultsSearchBar::slotClearSearch()
+{
+ if(status() != 0 || !d->searchLine->text().isEmpty())
+ {
+ d->searchLine->clear();
+ d->searchCombo->setCurrentItem(0);
+ d->timer.stop();
+ slotActivateSearch();
+ }
+}
+
+void ResultsSearchBar::slotSetStatus(int status)
+{
+ d->searchCombo->setCurrentItem(status);
+}
+
+void ResultsSearchBar::slotSetText(const QString& text)
+{
+ d->searchLine->setText(text);
+}
+
+void ResultsSearchBar::slotSearchComboChanged(int index)
+{
+ if(d->timer.isActive())
+ d->timer.stop();
+
+ if(d->m_lastComboIndex == index)
+ return;
+
+ d->m_lastComboIndex = index;
+
+ d->timer.start(200, true);
+}
+
+void ResultsSearchBar::slotSearchStringChanged(const QString& search)
+{
+ if(d->timer.isActive())
+ d->timer.stop();
+
+ if(d->searchText == search)
+ return;
+
+ d->searchText = search;
+
+ d->timer.start(200, true);
+}
+
+void ResultsSearchBar::slotActivateSearch()
+{
+ kdDebug(23100) << "ResultsSearchBar::slotActivateSearch" << endl;
+
+ ResultView::Status status = selectedStatus();
+
+ emit signalSearch(LinkMatcher(d->searchLine->text(), status));
+}
+
+LinkMatcher ResultsSearchBar::currentLinkMatcher() const
+{
+ return LinkMatcher(d->searchLine->text(), selectedStatus());
+}
+
+ResultView::Status ResultsSearchBar::selectedStatus() const
+{
+ ResultView::Status status = ResultView::none;
+
+ if(d->searchCombo->currentItem())
+ {
+ switch(d->searchCombo->currentItem())
+ {
+ case 1: // Good
+ {
+ status = ResultView::good;
+ break;
+ }
+ case 2: // Broken
+ {
+ status = ResultView::bad;
+ break;
+ }
+ case 3: // Malformed
+ {
+ status = ResultView::malformed;
+ break;
+ }
+ case 4: // Undetermined
+ {
+ status = ResultView::undetermined;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return status;
+}
+
+
+#include "resultssearchbar.moc"
diff --git a/klinkstatus/src/ui/resultssearchbar.h b/klinkstatus/src/ui/resultssearchbar.h
new file mode 100644
index 00000000..67d30a99
--- /dev/null
+++ b/klinkstatus/src/ui/resultssearchbar.h
@@ -0,0 +1,73 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 RESULTSSEARCHBAR_H
+#define RESULTSSEARCHBAR_H
+
+#include <qstring.h>
+
+#include "resultview.h"
+#include "../engine/linkfilter.h"
+
+/**
+ @author Paulo Moura Guedes <moura@kdewebdev.org>
+ Based on Akregator code. Kudos ;)
+*/
+class ResultsSearchBar : public QWidget
+{
+ Q_OBJECT
+public:
+ ResultsSearchBar(QWidget *parent = 0, const char *name = 0);
+ ~ResultsSearchBar();
+
+ QString const& text() const;
+ int status() const;
+
+ void setDelay(int ms);
+ int delay() const;
+
+ LinkMatcher currentLinkMatcher() const;
+
+signals:
+ /** emitted when the text and status filters were updated. Params are textfilter, statusfilter */
+ void signalSearch(LinkMatcher);
+
+public slots:
+ void slotClearSearch();
+ void slotSetStatus(int status);
+ void slotSetText(const QString& text);
+
+private slots:
+
+ void slotSearchStringChanged(const QString& search);
+ void slotSearchComboChanged(int index);
+ void slotActivateSearch();
+
+private:
+
+ ResultView::Status selectedStatus() const;
+
+private:
+
+ class ResultsSearchBarPrivate;
+ ResultsSearchBarPrivate* d;
+};
+
+#endif
diff --git a/klinkstatus/src/ui/resultview.cpp b/klinkstatus/src/ui/resultview.cpp
new file mode 100644
index 00000000..4078bfca
--- /dev/null
+++ b/klinkstatus/src/ui/resultview.cpp
@@ -0,0 +1,184 @@
+//
+// C++ Implementation: resultlinkview
+//
+// Description:
+//
+//
+// Author: Paulo Moura Guedes <moura@kdewebdev.org>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "resultview.h"
+#include "../engine/linkstatus.h"
+
+#include <qpainter.h>
+#include <qcolor.h>
+
+#include <klocale.h>
+#include <kurl.h>
+#include <kiconloader.h>
+
+const QString ResultView::URL_LABEL = "URL";
+const QString ResultView::STATUS_LABEL = "Status";
+const QString ResultView::MARKUP_LABEL = "Markup";
+const QString ResultView::LINK_LABEL_LABEL = "Label";
+
+
+// ******************************* ResultView ********************************
+
+ResultView::ResultView()
+ : col_status_(-1),
+ col_label_(-1),
+ col_url_(-1),
+ col_markup_(-1),
+ sub_menu_(0),
+ cell_tip_(0)
+{}
+
+
+ResultView::~ResultView()
+{}
+
+void ResultView::setColumns(QStringList const& columns)
+{
+ Q_ASSERT(columns.size() != 0);
+
+ columns_.clear();
+ for(uint i = 0; i != columns.size(); ++i)
+ {
+ if(columns[i] == ResultView::URL_LABEL)
+ {
+ col_url_ = i + 1;
+ }
+ else if(columns[i] == ResultView::STATUS_LABEL)
+ {
+ col_status_ = i + 1;
+ }
+ else if(columns[i] == ResultView::MARKUP_LABEL)
+ {
+ col_markup_ = i + 1;
+ }
+ else if(columns[i] == ResultView::LINK_LABEL_LABEL)
+ {
+ col_label_ = i + 1;
+ }
+
+ columns_.push_back(columns[i]);
+ }
+ number_of_columns_ = columns.size();
+}
+
+bool ResultView::displayableWithStatus(LinkStatus const* ls, Status const& status)
+{
+ if(status == ResultView::good)
+ {
+ return
+ ls->status() == LinkStatus::SUCCESSFULL ||
+ ls->status() == LinkStatus::HTTP_REDIRECTION;
+ }
+ else if(status == ResultView::bad)
+ {
+ return
+ ls->status() == LinkStatus::BROKEN ||
+ ls->status() == LinkStatus::HTTP_CLIENT_ERROR ||
+ ls->status() == LinkStatus::HTTP_SERVER_ERROR;
+ }
+ else if(status == ResultView::malformed)
+ {
+ return ls->status() == LinkStatus::MALFORMED;
+ }
+ else if(status == ResultView::undetermined)
+ {
+ return
+ ls->status() == LinkStatus::UNDETERMINED ||
+ ls->status() == LinkStatus::TIMEOUT ||
+ ls->status() == LinkStatus::NOT_SUPPORTED;
+ }
+ else
+ return true;
+}
+
+
+// ******************************* ResultViewItem *****************************
+
+ResultViewItem::ResultViewItem(LinkStatus const* linkstatus, int column_index)
+ : ls_((LinkStatus*)linkstatus), column_index_(column_index)
+{
+ Q_ASSERT(ls_);
+ Q_ASSERT(column_index_ > 0);
+}
+
+ResultViewItem::~ResultViewItem()
+{}
+
+void ResultViewItem::setColumnIndex(int i)
+{
+ Q_ASSERT(i > 0);
+ column_index_ = i;
+}
+
+int ResultViewItem::columnIndex() const
+{
+ return column_index_;
+}
+
+LinkStatus const* ResultViewItem::linkStatus() const
+{
+ Q_ASSERT(ls_);
+ return ls_;
+}
+
+QColor const& ResultViewItem::textStatusColor() const
+{
+ if(linkStatus()->errorOccurred())
+ {
+ //kdDebug(23100) << "ERROR: " << linkStatus()->error() << ": " << linkStatus()->absoluteUrl().prettyURL() << endl;
+ if(linkStatus()->error() == i18n( "Javascript not supported" ))
+ return Qt::lightGray;
+ else
+ return Qt::red;
+ }
+
+ else if(linkStatus()->absoluteUrl().hasRef())
+ return Qt::blue;
+
+ else if(!linkStatus()->absoluteUrl().protocol().startsWith("http"))
+ return Qt::darkGreen;
+
+ else
+ {
+ QString status_code(QString::number(linkStatus()->httpHeader().statusCode()));
+
+ if(status_code[0] == '0')
+ {
+ kdWarning(23100) << "status code == 0: " << endl;
+ kdWarning(23100) << linkStatus()->toString() << endl;
+ kdWarning(23100) << linkStatus()->httpHeader().toString() << endl;
+ }
+ //Q_ASSERT(status_code[0] != '0');
+
+ if(status_code[0] == '5')
+ return Qt::darkMagenta;
+
+ else if(status_code[0] == '4')
+ return Qt::red;
+
+ else if(status_code[0] == '3')
+ return Qt::blue;
+
+ else if(status_code[0] == '2')
+ return Qt::darkGreen;
+
+ else
+ return Qt::red;
+ }
+}
+
+
+
+
+
+
+
+
diff --git a/klinkstatus/src/ui/resultview.h b/klinkstatus/src/ui/resultview.h
new file mode 100644
index 00000000..e6d3e789
--- /dev/null
+++ b/klinkstatus/src/ui/resultview.h
@@ -0,0 +1,134 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 RESULTVIEW_H
+#define RESULTVIEW_H
+
+#include <qvaluevector.h>
+#include <qpopupmenu.h>
+#include <qstringlist.h>
+
+class KURL;
+
+class LinkStatus;
+class CellToolTip;
+
+
+/**
+@author Paulo Moura Guedes
+*/
+class ResultView
+{
+public:
+
+ static const QString URL_LABEL;
+ static const QString STATUS_LABEL;
+ static const QString MARKUP_LABEL;
+ static const QString LINK_LABEL_LABEL;
+
+ enum Status {
+ none = 0,
+ good,
+ bad,
+ malformed,
+ undetermined // timeouts and refs
+ };
+
+ ResultView();
+ virtual ~ResultView();
+
+ //virtual void insertResult(LinkStatus const* linkstatus) = 0;
+ virtual void clear() = 0;
+ virtual void show(Status const& status) = 0;
+ virtual void showAll() = 0;
+ //virtual void ensureCellVisible(int row, int col) = 0;
+
+ virtual void setColumns(QStringList const& columns);
+ static bool displayableWithStatus(LinkStatus const* ls, Status const& status);
+
+ int numberOfColumns() const { return number_of_columns_; }
+
+ int urlColumnIndex() const {return col_url_; }
+ int statusColumnIndex() const {return col_status_; }
+ int markupColumnIndex() const {return col_markup_; }
+ int labelColumnIndex() const {return col_label_; }
+
+protected:
+ //virtual bool textFitsInCell(int row, int col) const = 0;
+ virtual bool isEmpty() const = 0;
+ virtual void loadContextTableMenu(QValueVector<KURL> const& referrers, bool is_root = false) = 0;
+
+protected slots:
+
+ //virtual void slotPopupContextMenu(int row, int col, const QPoint& pos) = 0;
+ virtual void slotCopyUrlToClipboard() const = 0;
+ virtual void slotCopyParentUrlToClipboard() const = 0;
+ virtual void slotCopyCellTextToClipboard() const = 0;
+ virtual void slotEditReferrersWithQuanta() = 0;
+ virtual void slotEditReferrerWithQuanta(int id) = 0;
+ virtual void slotEditReferrerWithQuanta(KURL const& url) = 0;
+ virtual void slotViewUrlInBrowser() = 0;
+ virtual void slotViewParentUrlInBrowser() = 0;
+
+protected:
+ QStringList columns_;
+ int col_status_;
+ int col_label_;
+ int col_url_;
+ int col_markup_; // optional
+ QPopupMenu context_table_menu_;
+ QPopupMenu* sub_menu_;
+ CellToolTip* cell_tip_;
+
+private:
+ int number_of_columns_;
+};
+
+
+class ResultViewItem
+{
+public:
+ ResultViewItem(LinkStatus const* linkstatus,
+ int column_index);
+ virtual ~ResultViewItem();
+
+ virtual void setColumnIndex(int i);
+ virtual int columnIndex() const;
+
+ virtual QString toolTip() const = 0;
+ LinkStatus const* linkStatus() const;
+
+protected:
+
+ QColor const& textStatusColor() const;
+ virtual void paint( QPainter *p, const QColorGroup &cg,
+ const QRect &cr, bool selected ) = 0;
+ virtual void setText() = 0;
+ virtual void setPixmap() = 0;
+
+protected:
+
+ LinkStatus* ls_;
+ int column_index_;
+ int alignment_;
+};
+
+
+#endif
diff --git a/klinkstatus/src/ui/sessionwidget.cpp b/klinkstatus/src/ui/sessionwidget.cpp
new file mode 100644
index 00000000..da128070
--- /dev/null
+++ b/klinkstatus/src/ui/sessionwidget.cpp
@@ -0,0 +1,723 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include <kapplication.h>
+#include <kurl.h>
+#include <kcombobox.h>
+#include <ksqueezedtextlabel.h>
+#include <kprogress.h>
+#include <kmessagebox.h>
+#include <kconfig.h>
+#include <kiconloader.h>
+#include <kglobal.h>
+#include <kpushbutton.h>
+#include <kfiledialog.h>
+#include <kactionclasses.h>
+#include <ktempfile.h>
+#include <ksavefile.h>
+#include <kstandarddirs.h>
+#include <kio/netaccess.h>
+
+#include <qevent.h>
+#include <qlineedit.h>
+#include <qspinbox.h>
+#include <qcheckbox.h>
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qlistbox.h>
+#include <qstringlist.h>
+#include <qbuttongroup.h>
+#include <qtoolbutton.h>
+#include <qregexp.h>
+
+#include "sessionwidget.h"
+#include "tablelinkstatus.h"
+#include "treeview.h"
+#include "documentrootdialog.h"
+#include "klshistorycombo.h"
+#include "klsconfig.h"
+#include "resultview.h"
+#include "../global.h"
+#include "../engine/linkstatus.h"
+#include "../engine/linkchecker.h"
+#include "../engine/searchmanager.h"
+#include "resultssearchbar.h"
+#include "../actionmanager.h"
+#include "../utils/utils.h"
+#include "../utils/xsl.h"
+
+
+SessionWidget::SessionWidget(int max_simultaneous_connections, int time_out,
+ QWidget* parent, const char* name, WFlags f)
+ : SessionWidgetBase(parent, name, f), search_manager_(0),
+ action_manager_(ActionManager::getInstance()),
+ ready_(true), to_start_(false), to_pause_(false), to_stop_(false),
+ in_progress_(false), paused_(false), stopped_(true),
+ bottom_status_timer_(this, "bottom_status_timer"),
+ max_simultaneous_connections_(max_simultaneous_connections),
+ time_out_(time_out), tree_display_(false), follow_last_link_checked_(KLSConfig::followLastLinkChecked()),
+ start_search_action_(0)
+{
+ newSearchManager();
+
+ init();
+ slotLoadSettings();
+
+ connect(combobox_url, SIGNAL( textChanged ( const QString & ) ),
+ this, SLOT( slotEnableCheckButton( const QString & ) ) );
+
+ connect(tree_view, SIGNAL( clicked ( QListViewItem * ) ),
+ this, SLOT( showBottomStatusLabel( QListViewItem * ) ) );
+
+ connect(&bottom_status_timer_, SIGNAL(timeout()), this, SLOT(clearBottomStatusLabel()) );
+}
+
+SessionWidget::~SessionWidget()
+{
+ //combobox_url->saveItems(); This is done every time a URL is checked
+
+ if(KLSConfig::rememberCheckSettings())
+ saveCurrentCheckSettings();
+}
+
+void SessionWidget::init()
+{
+ combobox_url->init();
+
+ toolButton_clear_combo->setIconSet(SmallIconSet("locationbar_erase"));
+
+ pushbutton_url->setIconSet(KGlobal::iconLoader()->loadIconSet("fileopen", KIcon::Small));
+ QPixmap pixMap = KGlobal::iconLoader()->loadIcon("fileopen", KIcon::Small);
+ pushbutton_url->setFixedSize(pixMap.width() + 8, pixMap.height() + 8);
+ connect(pushbutton_url, SIGNAL(clicked()), this, SLOT(slotChooseUrlDialog()));
+
+ resultsSearchBar->hide();
+
+ start_search_action_ = static_cast<KToggleAction*> (action_manager_->action("start_search"));
+
+ connect(resultsSearchBar, SIGNAL(signalSearch(LinkMatcher)),
+ this, SLOT(slotApplyFilter(LinkMatcher)));
+}
+
+void SessionWidget::slotLoadSettings(bool modify_current_widget_settings)
+{
+ if(modify_current_widget_settings)
+ {
+ checkbox_recursively->setChecked(KLSConfig::recursiveCheck());
+ spinbox_depth->setValue(KLSConfig::depth());
+ checkbox_subdirs_only->setChecked(!KLSConfig::checkParentFolders());
+ checkbox_external_links->setChecked(KLSConfig::checkExternalLinks());
+ tree_display_ = KLSConfig::displayTreeView();
+ tree_view->setTreeDisplay(tree_display_);
+ }
+
+ search_manager_->setTimeOut(KLSConfig::timeOut());
+
+ //kdDebug(23100) << "tree_display_: " << tree_display_ << endl;
+}
+
+void SessionWidget::saveCurrentCheckSettings()
+{
+ KLSConfig::setRecursiveCheck(checkbox_recursively->isChecked());
+ KLSConfig::setDepth(spinbox_depth->value());
+ KLSConfig::setCheckParentFolders(!checkbox_subdirs_only->isChecked());
+ KLSConfig::setCheckExternalLinks(checkbox_external_links->isChecked());
+
+ KLSConfig::writeConfig();
+}
+
+void SessionWidget::newSearchManager()
+{
+ if(search_manager_)
+ delete search_manager_;
+
+ search_manager_ = new SearchManager(KLSConfig::maxConnectionsNumber(),
+ KLSConfig::timeOut(),
+ this, "search_manager");
+ Q_ASSERT(search_manager_);
+
+ connect(search_manager_, SIGNAL(signalRootChecked(const LinkStatus *, LinkChecker *)),
+ this, SLOT(slotRootChecked(const LinkStatus *, LinkChecker *)));
+ connect(search_manager_, SIGNAL(signalLinkChecked(const LinkStatus *, LinkChecker *)),
+ this, SLOT(slotLinkChecked(const LinkStatus *, LinkChecker *)));
+ connect(search_manager_, SIGNAL(signalSearchFinished()),
+ this, SLOT(slotSearchFinished()));
+ connect(search_manager_, SIGNAL(signalSearchPaused()),
+ this, SLOT(slotSearchPaused()));
+ connect(search_manager_, SIGNAL(signalAddingLevelTotalSteps(uint)),
+ this, SLOT(slotAddingLevelTotalSteps(uint)));
+ connect(search_manager_, SIGNAL(signalAddingLevelProgress()),
+ this, SLOT(slotAddingLevelProgress()));
+ connect(search_manager_, SIGNAL(signalLinksToCheckTotalSteps(uint)),
+ this, SLOT(slotLinksToCheckTotalSteps(uint)));
+}
+
+void SessionWidget::setColumns(QStringList const& colunas)
+{
+ tree_view->setColumns(colunas);
+}
+
+void SessionWidget::setUrl(KURL const& url)
+{
+ combobox_url->setCurrentText(url.prettyURL());
+ combobox_url->setFocus();
+}
+
+bool SessionWidget::isEmpty() const
+{
+ Q_ASSERT(tree_view);
+ return tree_view->isEmpty();
+}
+
+SearchManager const* SessionWidget::getSearchManager() const
+{
+ return search_manager_;
+}
+
+void SessionWidget::slotEnableCheckButton(const QString & s)
+{
+ if(!(stopped_ && !pendingActions()))
+ return;
+
+ if(!s.isEmpty() && !search_manager_->searching())
+ {
+ start_search_action_->setEnabled(true);
+ }
+ else
+ {
+ start_search_action_->setEnabled(false);
+ }
+}
+
+void SessionWidget::slotCheck()
+{
+ Q_ASSERT(to_start_);
+ Q_ASSERT(!in_progress_);
+ Q_ASSERT(!paused_);
+ Q_ASSERT(stopped_);
+
+ ready_ = false;
+ if(!validFields())
+ {
+ ready_ = true;
+ KApplication::beep();
+ return;
+ }
+
+ emit signalSearchStarted();
+
+ in_progress_ = true;
+ paused_ = false;
+ stopped_ = false;
+
+ slotLoadSettings(false); // it seems that KConfigDialogManager is not trigering this slot
+
+ newSearchManager();
+
+ insertUrlAtCombobox(combobox_url->currentText());
+ combobox_url->saveItems();
+ progressbar_checker->reset();
+ progressbar_checker->setPercentageVisible(true);
+ progressbar_checker->setTotalSteps(1); // check root page
+ progressbar_checker->setProgress(0);
+ textlabel_progressbar->setText(i18n( "Checking..." ));
+
+ textlabel_elapsed_time->setEnabled(true);
+ //textlabel_elapsed_time_value->setText("");
+ textlabel_elapsed_time_value->setEnabled(true);
+
+ //table_linkstatus->clear();
+ tree_view->clear();
+
+ KURL url = Url::normalizeUrl(combobox_url->currentText());
+
+ if(!url.protocol().startsWith("http"))
+ {
+ QString documentRootHint = url.directory().isEmpty() ? "/" : url.directory();
+ DocumentRootDialog dialog(this, documentRootHint);
+ dialog.exec();
+ search_manager_->setDocumentRoot(KURL::fromPathOrURL(dialog.url()));
+ }
+
+ if(KLSConfig::useQuantaUrlPreviewPrefix() && Global::isKLinkStatusEmbeddedInQuanta())
+ {
+ KURL url_aux = Global::urlWithQuantaPreviewPrefix(url);
+ if(url_aux.isValid() && !url_aux.isEmpty())
+ url = url_aux;
+ }
+
+ if(!checkbox_recursively->isChecked())
+ {
+ search_manager_->setSearchMode(SearchManager::depth);
+ search_manager_->setDepth(0);
+ }
+
+ else if(checkbox_recursively->isChecked())
+ {
+ if(spinbox_depth->value() == 0)
+ {
+ search_manager_->setSearchMode(SearchManager::domain);
+ }
+ else
+ {
+ search_manager_->setSearchMode(SearchManager::depth_and_domain);
+ search_manager_->setDepth(spinbox_depth->value());
+ }
+
+ if(checkbox_subdirs_only->isChecked())
+ {
+ search_manager_->setCheckParentDirs(false);
+
+ if(url.hasHost())
+ search_manager_->setDomain(url.host() + url.directory(true, false));
+ }
+ else
+ {
+ search_manager_->setCheckParentDirs(true);
+
+ if(url.hasHost())
+ search_manager_->setDomain(url.host());
+ }
+ if(checkbox_external_links->isChecked())
+ {
+ search_manager_->setCheckExternalLinks(true);
+ search_manager_->setExternalDomainDepth(1);
+ }
+ else
+ {
+ search_manager_->setCheckExternalLinks(false);
+ search_manager_->setExternalDomainDepth(0);
+ }
+ }
+ if(!lineedit_reg_exp->text().isEmpty())
+ {
+ search_manager_->setCheckRegularExpressions(true);
+ search_manager_->setRegularExpression(lineedit_reg_exp->text(), false);
+ }
+
+ kdDebug(23100) << "URI: " << url.prettyURL() << endl;
+ combobox_url->setCurrentText(url.prettyURL());
+ search_manager_->startSearch(url);
+ slotSetTimeElapsed();
+}
+
+void SessionWidget::keyPressEvent ( QKeyEvent* e )
+{
+ if( e->key() == Qt::Key_Return &&
+ ( combobox_url->hasFocus() ||
+ //lineedit_domain->hasFocus() ||
+ //checkbox_depth->hasFocus() ||
+ spinbox_depth->hasFocus() ||
+ //checkbox_domain->hasFocus() ||
+ //spinbox_external_domain->hasFocus()
+ checkbox_recursively->hasFocus() ||
+ checkbox_external_links->hasFocus() ||
+ checkbox_subdirs_only->hasFocus() ) )
+ {
+ if(validFields())
+ {
+ slotStartSearch();
+ }
+ }
+
+ else if(e->key() == Qt::Key_F6)
+ {
+ combobox_url->lineEdit()->selectAll();
+ }
+}
+
+bool SessionWidget::validFields()
+{
+ if(combobox_url->currentText().isEmpty())
+ {
+ KMessageBox::sorry(this, i18n("Cowardly refusing to check an empty URL."));
+ return false;
+ }
+
+ else
+ return true;
+}
+
+void SessionWidget::slotRootChecked(LinkStatus const* linkstatus, LinkChecker * anal)
+{
+ slotSetTimeElapsed();
+ emit signalUpdateTabLabel(search_manager_->linkStatusRoot(), this);
+
+ Q_ASSERT(textlabel_progressbar->text() == i18n("Checking...") ||
+ textlabel_progressbar->text() == i18n("Stopped"));
+
+ progressbar_checker->setProgress(1);
+
+ //table_linkstatus->insertResult(linkstatus);
+ TreeViewItem* tree_view_item = new TreeViewItem(tree_view, tree_view->lastItem(), linkstatus);
+ LinkStatus* ls = const_cast<LinkStatus*> (linkstatus);
+ ls->setTreeViewItem(tree_view_item);
+
+ if(linkstatus->isRedirection() && linkstatus->redirection())
+ slotLinkChecked(linkstatus->redirection(), anal);
+
+ resultsSearchBar->show();
+ ActionManager::getInstance()->action("file_export_html")->setEnabled(!isEmpty());
+}
+
+void SessionWidget::slotLinkChecked(LinkStatus const* linkstatus, LinkChecker * anal)
+{
+ slotSetTimeElapsed();
+
+ kdDebug(23100) << textlabel_progressbar->text() << endl;
+
+ Q_ASSERT(textlabel_progressbar->text() == i18n("Checking...") ||
+ textlabel_progressbar->text() == i18n("Stopped"));
+
+ progressbar_checker->setProgress(progressbar_checker->progress() + 1);
+
+ if(linkstatus->checked())
+ {
+ TreeViewItem* tree_view_item = 0;
+ TreeViewItem* parent_item = linkstatus->parent()->treeViewItem();
+ bool match = resultsSearchBar->currentLinkMatcher().matches(*linkstatus);
+
+ if(tree_display_)
+ {
+ //kdDebug(23100) << "TREE!!!!!" << endl;
+ tree_view_item = new TreeViewItem(tree_view, parent_item, parent_item->lastChild(), linkstatus);
+
+ parent_item->setLastChild(tree_view_item);
+ if(follow_last_link_checked_)
+ tree_view->ensureRowVisible(tree_view_item, tree_display_);
+
+ tree_view_item->setEnabled(match);
+ }
+ else
+ {
+ //kdDebug(23100) << "FLAT!!!!!" << endl;
+ tree_view_item = new TreeViewItem(tree_view, tree_view->lastItem(), linkstatus);
+ if(follow_last_link_checked_)
+ tree_view->ensureRowVisible(tree_view_item, tree_display_);
+
+ tree_view_item->setVisible(match);
+ }
+
+ LinkStatus* ls = const_cast<LinkStatus*> (linkstatus);
+ ls->setTreeViewItem(tree_view_item);
+
+ if(linkstatus->isRedirection() && linkstatus->redirection())
+ slotLinkChecked(linkstatus->redirection(), anal);
+ }
+}
+
+void SessionWidget::slotSearchFinished()
+{
+ Q_ASSERT(in_progress_);
+ Q_ASSERT(!paused_);
+ Q_ASSERT(!stopped_);
+
+ KApplication::beep ();
+
+ textlabel_progressbar->setText(i18n( "Ready" ));
+ progressbar_checker->reset();
+ progressbar_checker->setPercentageVisible(false);
+ progressbar_checker->setTotalSteps(1);
+ progressbar_checker->setProgress(0);
+
+ ready_ = true;
+
+ textlabel_elapsed_time->setEnabled(true);
+ textlabel_elapsed_time_value->setEnabled(true);
+ textlabel_elapsed_time_value->setText(search_manager_->timeElapsed().toString("hh:mm:ss"));
+
+ in_progress_ = false;
+ paused_ = false;
+ stopped_ = true;
+ resetPendingActions();
+ action_manager_->slotUpdateSessionWidgetActions(this);
+
+ emit signalSearchFinnished();
+}
+
+void SessionWidget::slotSearchPaused()
+{
+ Q_ASSERT(pendingActions());
+ Q_ASSERT(in_progress_);
+
+ KApplication::beep();
+
+ textlabel_progressbar->setText(i18n("Stopped"));
+
+ ready_ = true;
+
+ if(to_stop_)
+ {
+ in_progress_ = false;
+ paused_ = false;
+ stopped_ = true;
+ }
+ else
+ {
+ Q_ASSERT(to_pause_);
+ Q_ASSERT(!stopped_);
+
+ paused_ = true;
+ }
+
+ textlabel_elapsed_time->setEnabled(true);
+ textlabel_elapsed_time_value->setEnabled(true);
+ textlabel_elapsed_time_value->setText(search_manager_->timeElapsed().toString("hh:mm:ss"));
+
+ resetPendingActions();
+ action_manager_->slotUpdateSessionWidgetActions(this);
+
+ emit signalSearchPaused();
+}
+
+void SessionWidget::insertUrlAtCombobox(QString const& url)
+{
+ combobox_url->addToHistory(url);
+}
+
+void SessionWidget::showBottomStatusLabel(QListViewItem* item)
+{
+ kdDebug(23100) << "SessionWidget::showBottomStatusLabel" << endl;
+
+ if(!item)
+ return;
+
+ TreeViewItem* _item = tree_view->myItem(item);
+ if(_item)
+ {
+ QString status = _item->linkStatus()->statusText();
+ textlabel_status->setText(status);
+
+ if(textlabel_status->sizeHint().width() > textlabel_status->maximumWidth())
+ QToolTip::add(textlabel_status, status);
+ else
+ QToolTip::remove(textlabel_status);
+
+ bottom_status_timer_.stop();
+ bottom_status_timer_.start(5 * 1000, true);
+ }
+}
+
+void SessionWidget::clearBottomStatusLabel()
+{
+ textlabel_status->clear();
+}
+
+void SessionWidget::slotSetTimeElapsed()
+{
+ textlabel_elapsed_time_value->setText(search_manager_->timeElapsed().toString("hh:mm:ss"));
+}
+
+void SessionWidget::slotAddingLevelTotalSteps(uint steps)
+{
+ textlabel_progressbar->setText(i18n( "Adding level..." ));
+ progressbar_checker->reset();
+ progressbar_checker->setTotalSteps(steps);
+ progressbar_checker->setProgress(0);
+}
+
+void SessionWidget::slotAddingLevelProgress()
+{
+ Q_ASSERT(textlabel_progressbar->text() == i18n( "Adding level..." ));
+ progressbar_checker->setProgress(progressbar_checker->progress() + 1);
+}
+
+void SessionWidget::slotLinksToCheckTotalSteps(uint steps)
+{
+ textlabel_progressbar->setText(i18n( "Checking..." ));
+ progressbar_checker->reset();
+ progressbar_checker->setTotalSteps(steps);
+ progressbar_checker->setProgress(0);
+}
+
+void SessionWidget::slotClearComboUrl()
+{
+ combobox_url->setCurrentText("");
+}
+
+void SessionWidget::slotChooseUrlDialog()
+{
+ setUrl(KFileDialog::getOpenURL());
+}
+
+void SessionWidget::slotHideSearchPanel()
+{
+ if(buttongroup_search->isHidden())
+ buttongroup_search->show();
+ else
+ buttongroup_search->hide();
+}
+
+void SessionWidget::setFollowLastLinkChecked(bool follow)
+{
+ kdDebug(23100) << "setFollowLastLinkChecked: " << follow << endl;
+ follow_last_link_checked_ = follow;
+}
+
+void SessionWidget::slotFollowLastLinkChecked()
+{
+ follow_last_link_checked_ = !follow_last_link_checked_;
+}
+
+void SessionWidget::slotResetSearchOptions()
+{
+ slotLoadSettings(true);
+
+ combobox_url->clear();
+ lineedit_reg_exp->clear();
+}
+
+void SessionWidget::slotStartSearch()
+{
+ if(in_progress_)
+ {
+ start_search_action_->setChecked(true); // do not toggle
+ Q_ASSERT(!stopped_);
+ KApplication::beep();
+ return;
+ }
+
+ to_start_ = true;
+ slotLoadSettings(false);
+ slotCheck();
+ resetPendingActions();
+
+ action_manager_->slotUpdateSessionWidgetActions(this);
+}
+
+void SessionWidget::slotPauseSearch()
+{
+ Q_ASSERT(in_progress_);
+ Q_ASSERT(!stopped_);
+
+ if(pendingActions())
+ return;
+
+ to_pause_ = true;
+
+ if(!paused_)
+ {
+ Q_ASSERT(!ready_);
+ Q_ASSERT(search_manager_->searching());
+
+ search_manager_->cancelSearch();
+ }
+ else
+ {
+ Q_ASSERT(ready_);
+
+ paused_ = false;
+
+ textlabel_progressbar->setText(i18n("Checking..."));
+ ready_ = false;
+ search_manager_->resume();
+
+ emit signalSearchStarted();
+ slotLoadSettings(isEmpty()); // it seems that KConfigDialogManager is not trigering this slot
+
+ resetPendingActions();
+ }
+}
+
+void SessionWidget::slotStopSearch()
+{
+ Q_ASSERT(in_progress_);
+ Q_ASSERT(!stopped_);
+
+ if(pendingActions())
+ return;
+
+ to_stop_ = true;
+
+ if(!paused_)
+ {
+ Q_ASSERT(!ready_);
+ Q_ASSERT(search_manager_->searching());
+
+ search_manager_->cancelSearch();
+ }
+ else
+ {
+ in_progress_ = false;
+ paused_ = false;
+ stopped_ = true;
+
+ action_manager_->slotUpdateSessionWidgetActions(this);
+ }
+}
+
+bool SessionWidget::pendingActions() const
+{
+ return (to_start_ || to_pause_ || to_stop_);
+}
+
+void SessionWidget::resetPendingActions()
+{
+ to_start_ = false;
+ to_pause_ = false;
+ to_stop_ = false;
+}
+
+void SessionWidget::slotApplyFilter(LinkMatcher link_matcher)
+{
+ tree_view->show(link_matcher);
+}
+
+void SessionWidget::slotExportAsHTML( )
+{
+ KURL url = KFileDialog::getSaveURL(QString::null,"text/html", 0, i18n("Export Results as HTML"));
+
+ if(url.isEmpty())
+ return;
+
+ QString filename;
+ KTempFile tmp; // ### only used for network export
+
+ if(url.isLocalFile())
+ filename = url.path();
+ else
+ filename = tmp.name();
+
+ KSaveFile *savefile = new KSaveFile(filename);
+ if(savefile->status() == 0) // ok
+ {
+ QTextStream *outputStream = savefile->textStream();
+ outputStream->setEncoding(QTextStream::UnicodeUTF8);
+
+ QString xslt_doc = FileManager::read(locate("appdata", "styles/results_stylesheet.xsl"));
+ XSLT xslt(xslt_doc);
+// kdDebug(23100) << search_manager_->toXML() << endl;
+ QString html_ouptut = xslt.transform(search_manager_->toXML());
+ (*outputStream) << html_ouptut << endl;
+
+ savefile->close();
+ }
+
+ delete savefile;
+
+ if (url.isLocalFile())
+ return;
+
+ KIO::NetAccess::upload(filename, url, 0);
+}
+
+
+#include "sessionwidget.moc"
diff --git a/klinkstatus/src/ui/sessionwidget.h b/klinkstatus/src/ui/sessionwidget.h
new file mode 100644
index 00000000..af525a08
--- /dev/null
+++ b/klinkstatus/src/ui/sessionwidget.h
@@ -0,0 +1,149 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 SESSION_WIDGET_H
+#define SESSION_WIDGET_H
+
+#include "sessionwidgetbase.h"
+#include "../engine/linkchecker.h"
+#include "../engine/linkstatus.h"
+class SearchManager;
+class TableItem;
+class ActionManager;
+class LinkMatcher;
+
+#include <qtimer.h>
+#include <qstring.h>
+class QStringList;
+class QListViewItem;
+
+class KURL;
+class KConfig;
+class KToggleAction;
+
+#include <vector>
+
+using namespace std;
+
+class SessionWidget: public SessionWidgetBase
+{
+ Q_OBJECT
+
+public:
+
+ SessionWidget(int max_simultaneous_connections = 3, int time_out = 50,
+ QWidget* parent = 0, const char* name = 0, WFlags f = 0);
+
+ ~SessionWidget();
+
+ void setColumns(QStringList const& colunas);
+ void setUrl(KURL const& url);
+
+ bool treeDisplay() const { return tree_display_; }
+
+ bool followLastLinkChecked() const { return follow_last_link_checked_; }
+ void setFollowLastLinkChecked(bool follow);
+
+ bool isEmpty() const;
+ SearchManager const* getSearchManager() const;
+
+ bool inProgress() const { return in_progress_; }
+ bool paused() const { return paused_; }
+ bool stopped() const { return stopped_; }
+
+signals:
+ void signalUpdateTabLabel(const LinkStatus *, SessionWidget*);
+ void signalSearchStarted();
+ void signalSearchPaused();
+ void signalSearchFinnished();
+
+public slots:
+
+ virtual void slotClearComboUrl();
+ void slotLoadSettings(bool modify_current_widget_settings = true);
+
+ void slotStartSearch();
+ void slotPauseSearch();
+ void slotStopSearch();
+
+ void slotHideSearchPanel();
+ void slotResetSearchOptions();
+ void slotFollowLastLinkChecked();
+
+ void slotExportAsHTML();
+
+private slots:
+
+ virtual void slotCheck();
+ virtual void slotCancel() {} // FIXME hack
+ //virtual void slotSuggestDomain(bool toogle);
+
+ void slotEnableCheckButton(const QString &);
+ void slotRootChecked(LinkStatus const* linkstatus, LinkChecker * anal);
+ void slotLinkChecked(LinkStatus const* linkstatus, LinkChecker * anal);
+ void slotSearchFinished();
+ void slotSearchPaused();
+ /** Shows the status of the clicked URL (row) for 5 seconds */
+ void showBottomStatusLabel(QListViewItem* item);
+ void clearBottomStatusLabel();
+ void slotSetTimeElapsed();
+ void newSearchManager();
+
+ void slotAddingLevelTotalSteps(uint steps);
+ void slotAddingLevelProgress();
+ void slotLinksToCheckTotalSteps(uint steps);
+
+ void slotChooseUrlDialog();
+
+ void slotApplyFilter(LinkMatcher);
+
+private:
+
+ virtual void keyPressEvent ( QKeyEvent* e );
+ bool validFields();
+ void insertUrlAtCombobox(QString const& url);
+ void init();
+ void saveCurrentCheckSettings();
+ bool pendingActions() const;
+ void resetPendingActions();
+
+private:
+ SearchManager* search_manager_;
+ ActionManager* action_manager_;
+
+ bool ready_;
+ bool to_start_;
+ bool to_pause_;
+ bool to_stop_;
+ bool in_progress_;
+ bool paused_;
+ bool stopped_;
+
+ QTimer bottom_status_timer_;
+ int max_simultaneous_connections_;
+ int time_out_;
+ bool tree_display_; // tree/flat result display
+ bool follow_last_link_checked_;
+ KToggleAction* start_search_action_;
+};
+
+
+
+#endif
diff --git a/klinkstatus/src/ui/sessionwidgetbase.ui b/klinkstatus/src/ui/sessionwidgetbase.ui
new file mode 100644
index 00000000..0d194143
--- /dev/null
+++ b/klinkstatus/src/ui/sessionwidgetbase.ui
@@ -0,0 +1,602 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>SessionWidgetBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>SessionWidgetBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>689</width>
+ <height>683</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>300</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>1000</width>
+ <height>500</height>
+ </size>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttongroup_search</cstring>
+ </property>
+ <property name="title">
+ <string>Search</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout15</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout14</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QToolButton">
+ <property name="name">
+ <cstring>toolButton_clear_combo</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>25</width>
+ <height>25</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>25</width>
+ <height>25</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textlabel_url</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>URL: </string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignLeft</set>
+ </property>
+ </widget>
+ <widget class="KLSHistoryCombo">
+ <property name="name">
+ <cstring>combobox_url</cstring>
+ </property>
+ <property name="focusPolicy">
+ <enum>StrongFocus</enum>
+ </property>
+ <property name="acceptDrops">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>pushbutton_url</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Maximum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>checkbox_recursively</cstring>
+ </property>
+ <property name="text">
+ <string>Recursivel&amp;y:</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Check pages recursively</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>spinbox_depth</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="specialValueText">
+ <string>Unlimited</string>
+ </property>
+ <property name="maxValue">
+ <number>15</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ <property name="value">
+ <number>0</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>checkbox_subdirs_only</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Do &amp;not check parent folders</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>160</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>checkbox_external_links</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Chec&amp;k external links</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>MinimumExpanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>174</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Do not check regular expression:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>lineedit_reg_exp</cstring>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Maximum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="ResultsSearchBar">
+ <property name="name">
+ <cstring>resultsSearchBar</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="TreeView">
+ <property name="name">
+ <cstring>tree_view</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>300</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textlabel_progressbar</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>94</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Ready</string>
+ </property>
+ </widget>
+ <widget class="KProgress">
+ <property name="name">
+ <cstring>progressbar_checker</cstring>
+ </property>
+ <property name="totalSteps">
+ <number>1</number>
+ </property>
+ <property name="progress">
+ <number>0</number>
+ </property>
+ <property name="percentageVisible">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KSqueezedTextLabel">
+ <property name="name">
+ <cstring>textlabel_status</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>470</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5_2_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Preferred</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>30</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12_2</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textlabel_elapsed_time</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Elapsed time:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>hh:mm:ss.zzz</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>textlabel_elapsed_time_value</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>hh:mm:ss.zzz</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+ <customwidget>
+ <class>KLSHistoryCombo</class>
+ <header location="global">klshistorycombo.h</header>
+ <sizehint>
+ <width>-1</width>
+ <height>-1</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ </customwidget>
+ <customwidget>
+ <class>TreeView</class>
+ <header location="local">treeview.h</header>
+ <sizehint>
+ <width>-1</width>
+ <height>-1</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>7</hordata>
+ <verdata>7</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ </customwidget>
+ <customwidget>
+ <class>ResultsSearchBar</class>
+ <header location="local">resultssearchbar.h</header>
+ <sizehint>
+ <width>-1</width>
+ <height>-1</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image1</pixmap>
+ </customwidget>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="XBM.GZ" length="79">789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f</data>
+ </image>
+ <image name="image1">
+ <data format="PNG" length="824">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000002ff49444154388db59531681c4714863f992dde820cb370815b50600f54e8ca0ba43970712a8fb838438a3895634813d238a5ab80e314ae4d0a812060a4226017c27221c8a9da6b8c4fe0e00d28b0571cec82043b85611f78c12966efa4bb8bc085f29a6567df7cef9f7fdeccaec571cc2cbaddee47ae21e2385e5b9b815f1ebcfcd8de6a63ad25cb338af7c52741acb5a4a729a3d723a82ec6bd99d267bf3f23fc1c4cab2442d14a915986e792fdfa59569766573049417784f1b12e8267954dab24b78714450a28beaf941f847c2a14e70a0841035a2d45d641eb027213c210c69756320767794684d6508bef0befde1a860796e4c402333542b4256c0f0cdd1e50b97191458be6e0e27d81563a87c643d8fb2d7793d685d696413cc8a6cae46f65f7d79c7c62b87b4f2e15fd0fb0d302be0fefde4a0d557a5f35e90f84e0334014d590f855c9de4ecee17e4eb319d1ff3a00ec02f8c67299f283307c61e7d06fbf1782d082588a33e1cf1705fd81cf773f3601e1f9bec59e2f4b5c7ef5209f0ac95f16630cfd818067c103b586dd274a726229cee0fe8380d191cb4d1267d3d58aa1de7d258ceae5d7d0a78fdd269a86f0c52d414c49bbe3762c9b686de41560d7a72e41c4795a6486a78f95e4c4151481d686efbe7b3398ac58b1a23868b8c474aaa8068c8e714a8dd06c1af2a9e5d1c38c641c909dba6e08237f19b358a7ac5cf3479bc2e41f257e55d2ffc6a73833746f09e186cfa387904f2cbffc90a2aa9886d0e99464d3c5965b512cebd01f1800f67672e2a392fb0f023a3d883a053ffddcc2340dd65ab452b6074dc2cd15c1cbceb863daed413e353cdfcfd97d92333a12da6d0181ec3443cf753ef3cdd092de0e116ff1a02cdc157338ca9d7b8269461cfee1ba2139b9286e1a427f10110f2d561555b076d18a39383d4d99a4c0cd0b787f20747b214962c8266e3cdcf0e97c59126ec2f6edd089f40a92f115e0d1eb11ba238461dd6a15f32b53666de841965bb203575a3cc15a48c64a965fe57105e3635db8fa96dcffc431172b5d715d7103dc3fea7f015f373c8ee3b57f0135105a0fae7717960000000049454e44ae426082</data>
+ </image>
+</images>
+<connections>
+ <connection>
+ <sender>checkbox_recursively</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>spinbox_depth</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>checkbox_recursively</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>checkbox_external_links</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>checkbox_recursively</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>checkbox_subdirs_only</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>toolButton_clear_combo</sender>
+ <signal>clicked()</signal>
+ <receiver>SessionWidgetBase</receiver>
+ <slot>slotClearComboUrl()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>combobox_url</tabstop>
+ <tabstop>checkbox_recursively</tabstop>
+ <tabstop>spinbox_depth</tabstop>
+ <tabstop>checkbox_subdirs_only</tabstop>
+ <tabstop>checkbox_external_links</tabstop>
+</tabstops>
+<slots>
+ <slot specifier="pure virtual">slotCheck()</slot>
+ <slot specifier="pure virtual">slotCancel()</slot>
+ <slot specifier="pure virtual">slotClearComboUrl()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klshistorycombo.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>resultssearchbar.h</includehint>
+ <includehint>treeview.h</includehint>
+ <includehint>kprogress.h</includehint>
+ <includehint>ksqueezedtextlabel.h</includehint>
+</includehints>
+</UI>
diff --git a/klinkstatus/src/ui/settings/Makefile.am b/klinkstatus/src/ui/settings/Makefile.am
new file mode 100644
index 00000000..777e9e5c
--- /dev/null
+++ b/klinkstatus/src/ui/settings/Makefile.am
@@ -0,0 +1,7 @@
+INCLUDES = -I$(top_builddir)/klinkstatus/src/cfg -I$(top_builddir)/klinkstatus/src $(all_includes)
+METASOURCES = AUTO
+libsettings_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libsettings.la
+libsettings_la_SOURCES = configsearchdialog.ui dummy.cpp configresultsdialog.ui \
+ configidentificationdialog.cpp configidentificationdialogui.ui
+noinst_HEADERS = configidentificationdialog.h
diff --git a/klinkstatus/src/ui/settings/configidentificationdialog.cpp b/klinkstatus/src/ui/settings/configidentificationdialog.cpp
new file mode 100644
index 00000000..3dcd1239
--- /dev/null
+++ b/klinkstatus/src/ui/settings/configidentificationdialog.cpp
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include "configidentificationdialog.h"
+
+#include <kprotocolmanager.h>
+#include <kpushbutton.h>
+#include <klineedit.h>
+
+#include <qstring.h>
+
+#include "../cfg/klsconfig.h"
+
+
+ConfigIdentificationDialog::ConfigIdentificationDialog(QWidget *parent, const char *name)
+ : ConfigIdentificationDialogUi(parent, name)
+{
+ if(KLSConfig::userAgent().isEmpty())
+ {
+ slotDefaultUA();
+ }
+
+ connect(buttonDefault, SIGNAL(clicked()), this, SLOT(slotDefaultUA()));
+}
+
+
+ConfigIdentificationDialog::~ConfigIdentificationDialog()
+{
+}
+
+void ConfigIdentificationDialog::slotDefaultUA()
+{
+ KLSConfig::setUserAgent(KProtocolManager::defaultUserAgent());
+ kcfg_UserAgent->setText(KLSConfig::userAgent());
+}
+
+
+#include "configidentificationdialog.moc"
diff --git a/klinkstatus/src/ui/settings/configidentificationdialog.h b/klinkstatus/src/ui/settings/configidentificationdialog.h
new file mode 100644
index 00000000..e75a65a6
--- /dev/null
+++ b/klinkstatus/src/ui/settings/configidentificationdialog.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 CONFIGIDENTIFICATIONDIALOG_H
+#define CONFIGIDENTIFICATIONDIALOG_H
+
+#include "configidentificationdialogui.h"
+
+/**
+ @author Paulo Moura Guedes <moura@kdewebdev.org>
+*/
+class ConfigIdentificationDialog : public ConfigIdentificationDialogUi
+{
+Q_OBJECT
+public:
+ ConfigIdentificationDialog(QWidget *parent = 0, const char *name = 0);
+ ~ConfigIdentificationDialog();
+
+private slots:
+ void slotDefaultUA();
+};
+
+#endif
diff --git a/klinkstatus/src/ui/settings/configidentificationdialogui.ui b/klinkstatus/src/ui/settings/configidentificationdialogui.ui
new file mode 100644
index 00000000..29723358
--- /dev/null
+++ b/klinkstatus/src/ui/settings/configidentificationdialogui.ui
@@ -0,0 +1,134 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>ConfigIdentificationDialogUi</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ConfigIdentificationDialogUi</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>570</width>
+ <height>113</height>
+ </rect>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup4</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>0</bold>
+ </font>
+ </property>
+ <property name="title">
+ <string>Identification</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>User-Agent</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_UserAgent</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="2">
+ <property name="name">
+ <cstring>buttonDefault</cstring>
+ </property>
+ <property name="text">
+ <string>Default</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_SendIdentification</cstring>
+ </property>
+ <property name="text">
+ <string>Send Identification</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>kcfg_SendIdentification</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_SendIdentification</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_UserAgent</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_SendIdentification</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>buttonDefault</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_SendIdentification</tabstop>
+ <tabstop>kcfg_UserAgent</tabstop>
+ <tabstop>buttonDefault</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/klinkstatus/src/ui/settings/configresultsdialog.ui b/klinkstatus/src/ui/settings/configresultsdialog.ui
new file mode 100644
index 00000000..544f3273
--- /dev/null
+++ b/klinkstatus/src/ui/settings/configresultsdialog.ui
@@ -0,0 +1,72 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>ConfigResultsDialog</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ConfigResultsDialog</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>233</width>
+ <height>183</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup13</cstring>
+ </property>
+ <property name="title">
+ <string>View</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>kcfg_DisplayTreeView</cstring>
+ </property>
+ <property name="text">
+ <string>Tree</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>kcfg_DisplayFlatView</cstring>
+ </property>
+ <property name="text">
+ <string>Flat</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup13_2</cstring>
+ </property>
+ <property name="title">
+ <string>Misc</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>kcfg_FollowLastLinkChecked</cstring>
+ </property>
+ <property name="text">
+ <string>Follow Last Link Checked</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/klinkstatus/src/ui/settings/configsearchdialog.ui b/klinkstatus/src/ui/settings/configsearchdialog.ui
new file mode 100644
index 00000000..604a431c
--- /dev/null
+++ b/klinkstatus/src/ui/settings/configsearchdialog.ui
@@ -0,0 +1,353 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>ConfigSearchDialog</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ConfigSearchDialog</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>459</width>
+ <height>365</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup13</cstring>
+ </property>
+ <property name="title">
+ <string>Network</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KIntSpinBox" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_MaxConnectionsNumber</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>5</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1_2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Timeout in seconds:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_TimeOut</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>3600</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>40</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Number of simultaneous connections:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup8</cstring>
+ </property>
+ <property name="title">
+ <string>Input</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KIntSpinBox" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_MaxCountComboUrl</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ <property name="minValue">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>50</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>kcfg_CheckParentFolders</cstring>
+ </property>
+ <property name="text">
+ <string>Check parent folders</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Number of items in URL history:</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>kcfg_CheckExternalLinks</cstring>
+ </property>
+ <property name="text">
+ <string>Check external links</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout21</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_RecursiveCheck</cstring>
+ </property>
+ <property name="text">
+ <string>Recursive</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer25</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Maximum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout20</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Depth:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>kcfg_Depth</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="specialValueText">
+ <string>Unlimited</string>
+ </property>
+ <property name="maxValue">
+ <number>15</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ <property name="value">
+ <number>1</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup3</cstring>
+ </property>
+ <property name="title">
+ <string>Quanta</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_UseQuantaUrlPreviewPrefix</cstring>
+ </property>
+ <property name="text">
+ <string>Use preview prefix</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Check this one if you want to use Quanta's project preview prefix in the URL to check</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_RememberCheckSettings</cstring>
+ </property>
+ <property name="text">
+ <string>Remember settings when exit</string>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>kcfg_RecursiveCheck</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_Depth</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_RecursiveCheck</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1_2_2_2</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_RecursiveCheck</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_CheckParentFolders</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_RecursiveCheck</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_CheckExternalLinks</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_MaxConnectionsNumber</tabstop>
+ <tabstop>kcfg_TimeOut</tabstop>
+ <tabstop>kcfg_MaxCountComboUrl</tabstop>
+ <tabstop>kcfg_RecursiveCheck</tabstop>
+ <tabstop>kcfg_Depth</tabstop>
+ <tabstop>kcfg_CheckParentFolders</tabstop>
+ <tabstop>kcfg_CheckExternalLinks</tabstop>
+ <tabstop>kcfg_RememberCheckSettings</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/klinkstatus/src/ui/settings/dummy.cpp b/klinkstatus/src/ui/settings/dummy.cpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/klinkstatus/src/ui/settings/dummy.cpp
diff --git a/klinkstatus/src/ui/tablelinkstatus.cpp b/klinkstatus/src/ui/tablelinkstatus.cpp
new file mode 100644
index 00000000..695365a9
--- /dev/null
+++ b/klinkstatus/src/ui/tablelinkstatus.cpp
@@ -0,0 +1,750 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include "tablelinkstatus.h"
+#include "../utils/utils.h"
+#include "../parser/url.h"
+#include "../global.h"
+
+#include <qmemarray.h>
+#include <qtooltip.h>
+#include <qpixmap.h>
+#include <qclipboard.h>
+#include <qpainter.h>
+#include <qprocess.h>
+
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <krun.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <dcopclient.h>
+#include <dcopref.h>
+
+
+/*
+
+********************* TableLinkstatus ***************************
+
+*/
+
+TableLinkstatus::TableLinkstatus(QWidget * parent, const char * name,
+ int column_index_status,
+ int column_index_label,
+ int column_index_URL)
+ : QTable(parent, name),
+ ResultView(column_index_status, column_index_label, column_index_URL)
+ //context_table_menu_(this, "context_table_menu")
+{
+ setShowGrid(false);
+ setSorting(false);
+ setSelectionMode(QTable::NoSelection);
+ setFocusStyle(QTable::FollowStyle);
+ setReadOnly(true);
+
+ verticalHeader()->hide();
+ setLeftMargin(0);
+
+ cell_tip_ = new CellToolTip(this);
+
+ sub_menu_ = new QPopupMenu(this, "sub_menu_referrers");
+
+ connect(this, SIGNAL( contextMenuRequested ( int, int, const QPoint& )),
+ this, SLOT( slotPopupContextMenu( int, int, const QPoint&)) );
+}
+
+TableLinkstatus::~TableLinkstatus()
+{
+ delete cell_tip_;
+}
+
+void TableLinkstatus::setColumns(QStringList const& columns)
+{
+ ResultView::setColumns(columns);
+
+ removeColunas();
+ setNumCols(columns.size());
+
+ QHeader* horizontal_header = horizontalHeader();
+ for(uint i = 0; i != columns.size(); ++i)
+ {
+ if(i == 0)
+ {
+ Q_ASSERT(columns[i] == i18n("Status") && col_status_ == 1);
+ setColumnWidth(i, STATUS_COLUMN_WIDTH);
+ }
+ else if(i == 1)
+ {
+ Q_ASSERT(columns[i] == i18n("Label") && col_label_ == 2);
+ setColumnWidth(i, width() / 3);
+ }
+ else if(i == 2)
+ Q_ASSERT(columns[i] == i18n("URL") && col_url_ == 3);
+
+ horizontal_header->setLabel(i, i18n(columns[i]));
+ }
+
+ setColumnStretchable(col_url_ - 1, true);
+ horizontal_header->adjustHeaderSize();
+}
+
+void TableLinkstatus::insertResult(LinkStatus const* linkstatus)
+{
+ insereLinha(generateRowOfTableItems(linkstatus));
+}
+
+vector<TableItem*> TableLinkstatus::generateRowOfTableItems(LinkStatus const* linkstatus)
+{
+ vector<TableItem*> items;
+ int column = 1;
+
+ TableItem* item1 = new TableItemStatus(this, QTableItem::Never,
+ linkstatus, column++);
+ TableItem* item2 = new TableItemNome(this, QTableItem::Never,
+ linkstatus, column++);
+ TableItem* item3 = new TableItemURL(this, QTableItem::Never,
+ linkstatus, column++);
+ items.push_back(item1);
+ items.push_back(item2);
+ items.push_back(item3);
+
+ // If more columns are choosed in the settings, create and add the items here
+ // ...
+
+ return items;
+}
+
+void TableLinkstatus::insereLinha(vector<TableItem*> items)
+{
+ Q_ASSERT(items.size() == (uint)numCols());
+
+ setNumRows(numRows() + 1);
+ int row = numRows() - 1;
+
+ for(vector<TableItem*>::size_type i = 0; i != items.size(); ++i)
+ {
+ Q_ASSERT(items[i]);
+
+ int col = items[i]->columnIndex() - 1;
+ setItem(row, col, items[i]);
+ }
+
+ if(items[col_url_ - 1]->sizeHint().width() > columnWidth(col_url_ - 1))
+ {
+ setColumnStretchable(col_url_ - 1, false);
+ setColumnWidth(col_url_ - 1, items[col_url_ - 1]->sizeHint().width());
+ }
+
+ ensureCellVisible(row, 0);
+}
+
+void TableLinkstatus::clear()
+{
+ QMemArray<int> linhas(numRows());
+ for(uint i = 0; i != linhas.size(); ++i)
+ linhas[i] = i + 1;
+
+ removeRows(linhas);
+
+ Q_ASSERT(numRows() == 0);
+}
+
+void TableLinkstatus::removeColunas()
+{
+ QMemArray<int> columns(numCols());
+ for(uint i = 0; i != columns.size(); ++i)
+ columns[i] = i + 1;
+
+ removeColumns(columns);
+
+ Q_ASSERT(numCols() == 0);
+}
+
+void TableLinkstatus::show(ResultView::Status const& status)
+{
+ for(int i = 0; i != numRows(); ++i)
+ {
+ int row = i;
+ TableItem* _item = myItem(row, col_status_);
+
+ if(!ResultView::displayableWithStatus(_item->linkStatus(), status))
+ hideRow(row);
+ else
+ showRow(row);
+ }
+}
+
+void TableLinkstatus::showAll()
+{
+ for(int i = 0; i != numRows(); ++i)
+ showRow(i);
+}
+
+/*
+void TableLinkstatus::mostraPorStatusCode(int status_code)
+{
+ for(int i = 0; i != numRows(); ++i)
+ {
+ int row = i + 1;
+ QTableItem* _item = myItem(row, col_status_);
+
+ if(status_code != _item->text().toInt())
+ hideRow(row);
+ }
+}
+*/
+/**
+ Use this procedure when you insert a row at the bottom of the table,
+ and you only want the to scroll down if you were already at the bottom,
+ before inserting the row.
+ This allows you to see what's going on on other cells without having
+ the table always scrolling down when every row is inserted.
+*/
+void TableLinkstatus::ensureCellVisible(int row, int col)
+{
+ // table viewport is at the bottom
+ if(rowPos(row - 1) <= (contentsY() + visibleHeight()))
+ QTable::ensureCellVisible(row, col);
+}
+
+bool TableLinkstatus::textFitsInCell(int row, int col) const
+{
+ QTableItem* itm(myItem(row, col));
+ Q_ASSERT(itm);
+
+ QSize size_hint(itm->sizeHint());
+ int visible_width = 0;
+
+ if(col == numCols() - 1)
+ visible_width = contentsX() + visibleWidth();
+ else
+ visible_width = columnPos(col) + columnWidth(col);
+
+ if(columnPos(col) + size_hint.width() > visible_width)
+ return false;
+ else
+ return true;
+}
+
+bool TableLinkstatus::isEmpty() const
+{
+ return numRows() == 0;
+}
+
+TableItem* TableLinkstatus::myItem(int row, int col) const
+{
+ TableItem* _item = dynamic_cast<TableItem*> (QTable::item(row, col));
+ Q_ASSERT(_item);
+ return _item;
+}
+
+void TableLinkstatus::slotPopupContextMenu(int r, int w, const QPoint& pos)
+{
+ TableItem* table_item = myItem(r, w);
+ if(table_item)
+ {
+ QValueVector<KURL> referrers = table_item->linkStatus()->referrers();
+ loadContextTableMenu(referrers, table_item->linkStatus()->isRoot());
+ context_table_menu_.popup(pos);
+ }
+}
+
+void TableLinkstatus::loadContextTableMenu(QValueVector<KURL> const& referrers, bool is_root)
+{
+ context_table_menu_.clear();
+ sub_menu_->clear();
+
+ if(!is_root)
+ {
+ sub_menu_->insertItem(i18n("All"), this, SLOT(slotEditReferrersWithQuanta()));
+ sub_menu_->insertSeparator();
+
+ for(uint i = 0; i != referrers.size(); ++i)
+ {
+ sub_menu_->insertItem(referrers[i].prettyURL());
+ }
+ connect(sub_menu_, SIGNAL(activated(int)), this, SLOT(slotEditReferrerWithQuanta(int)));
+
+ context_table_menu_.insertItem(SmallIconSet("fileopen"), i18n("Edit Referrer with Quanta"),
+ sub_menu_);
+ }
+ else
+ {
+ int id = context_table_menu_.insertItem(SmallIconSet("fileopen"), i18n("Edit Referrer with Quanta"));
+ context_table_menu_.setItemEnabled(id, false);
+ }
+
+ context_table_menu_.insertItem(SmallIconSet("fileopen"), i18n("Open URL"),
+ this, SLOT(slotViewUrlInBrowser()));
+
+ context_table_menu_.insertItem(SmallIconSet("fileopen"), i18n("Open Referrer URL"),
+ this, SLOT(slotViewParentUrlInBrowser()));
+
+ context_table_menu_.insertSeparator();
+
+ context_table_menu_.insertItem(SmallIconSet("editcopy"), i18n("Copy URL"),
+ this, SLOT(slotCopyUrlToClipboard()));
+
+ context_table_menu_.insertItem(SmallIconSet("editcopy"), i18n("Copy Referrer URL"),
+ this, SLOT(slotCopyParentUrlToClipboard()));
+
+ context_table_menu_.insertItem(SmallIconSet("editcopy"), i18n("Copy Cell Text"),
+ this, SLOT(slotCopyCellTextToClipboard()));
+}
+
+void TableLinkstatus::slotCopyUrlToClipboard() const
+{
+ TableItem* _item = myItem(currentRow(), currentColumn());
+ QString content(_item->linkStatus()->absoluteUrl().prettyURL());
+ QClipboard* cb = kapp->clipboard();
+ cb->setText(content);
+}
+
+void TableLinkstatus::slotCopyParentUrlToClipboard() const
+{
+ TableItem* _item = myItem(currentRow(), currentColumn());
+ QString content(_item->linkStatus()->parent()->absoluteUrl().prettyURL());
+ QClipboard* cb = kapp->clipboard();
+ cb->setText(content);
+}
+
+void TableLinkstatus::slotCopyCellTextToClipboard() const
+{
+ QString cell_text(text(currentRow(), currentColumn()));
+ QClipboard* cb = kapp->clipboard();
+ cb->setText(cell_text);
+}
+
+void TableLinkstatus::slotEditReferrersWithQuanta()
+{
+ TableItem* _item = myItem(currentRow(), currentColumn());
+ QValueVector<KURL> referrers = _item->linkStatus()->referrers();
+
+ if(Global::isQuantaAvailableViaDCOP())
+ {
+ for(uint i = 0; i != referrers.size(); ++i)
+ slotEditReferrerWithQuanta(referrers[i]);
+ }
+ else
+ {
+ QStringList list_urls;
+
+ for(uint i = 0; i != referrers.size(); ++i)
+ list_urls.append(referrers[i].url());
+
+ Global::openQuanta(list_urls);
+ }
+}
+
+void TableLinkstatus::slotEditReferrerWithQuanta(int id)
+{
+ int index = sub_menu_->indexOf(id);
+
+ if(index == 0)
+ return;
+ Q_ASSERT(index != -1);
+ Q_ASSERT(index != 1); // separator
+
+ //kdDebug(23100) << "id: " << id << endl;
+ //kdDebug(23100) << "index: " << index << endl;
+
+ index -= 2; // The list of referrers starts on index = 2
+
+ TableItem* _item = myItem(currentRow(), currentColumn());
+ QValueVector<KURL> referrers = _item->linkStatus()->referrers();
+ Q_ASSERT(index >= 0 && (uint)index < referrers.size());
+
+ slotEditReferrerWithQuanta(referrers[index]);
+}
+
+void TableLinkstatus::slotEditReferrerWithQuanta(KURL const& url)
+{
+ QString filePath = url.url();
+
+ if(Global::isQuantaAvailableViaDCOP())
+ {
+ DCOPRef quanta(Global::quantaDCOPAppId(),"WindowManagerIf");
+ bool success = quanta.send("openFile", filePath, 0, 0);
+
+ if(!success)
+ {
+ QString message = i18n("<qt>File <b>%1</b> cannot be opened. Might be a DCOP problem.</qt>").arg(filePath);
+ KMessageBox::error(parentWidget(), message);
+ }
+ }
+ else
+ {
+ QStringList args(url.url());
+ Global::openQuanta(args);
+ }
+}
+
+void TableLinkstatus::slotViewUrlInBrowser()
+{
+ TableItem* _item = myItem(currentRow(), currentColumn());
+ KURL url = _item->linkStatus()->absoluteUrl();
+
+ if(url.isValid())
+ {
+ (void) new KRun (url, 0, url.isLocalFile(), true);
+ }
+ else
+ KMessageBox::sorry(this, i18n("Invalid URL."));
+}
+
+void TableLinkstatus::slotViewParentUrlInBrowser()
+{
+ TableItem* _item = myItem(currentRow(), currentColumn());
+
+ if(_item->linkStatus()->isRoot())
+ {
+ KMessageBox::sorry(this, i18n("ROOT URL."));
+ }
+ else
+ {
+ LinkStatus const* ls_parent = _item->linkStatus()->parent();
+ Q_ASSERT(ls_parent);
+
+ KURL url = ls_parent->absoluteUrl();
+
+ if(url.isValid())
+ (void) new KRun (url, 0, url.isLocalFile(), true);
+ else
+ KMessageBox::sorry(this, i18n("Invalid URL."));
+ }
+}
+
+/*
+
+********************* TableItem ***************************
+
+*/
+
+TableItem::TableItem(QTable* table, EditType et,
+ LinkStatus const* linkstatus,
+ int column_index, int alignment)
+ : QTableItem(table, et, ""), ResultViewItem(linkstatus, column_index),
+ /*ls_((LinkStatus*)linkstatus),
+ column_index_(column_index),*/ alignment_(alignment)
+{
+ //Q_ASSERT(ls_);
+ //Q_ASSERT(column_index_ > 0);
+}
+
+TableItem::~TableItem()
+{}
+
+void TableItem::setColumnIndex(int i)
+{
+ Q_ASSERT(i > 0 && i <= table()->numCols());
+
+ //column_index_ = i;
+ ResultViewItem::setColumnIndex(i);
+}
+
+int TableItem::columnIndex() const
+{
+ Q_ASSERT(column_index_ <= table()->numCols());
+
+ return ResultViewItem::columnIndex();
+}
+
+void TableItem::setAlignment(int aFlags)
+{
+ alignment_ = aFlags;
+}
+
+int TableItem::alignment() const
+{
+ return alignment_;
+}
+/*
+LinkStatus const* const TableItem::linkStatus() const
+{
+ Q_ASSERT(ls_);
+ return ls_;
+}
+
+QColor const& TableItem::textStatusColor() const
+{
+ if(linkStatus()->errorOccurred())
+ {
+ //kdDebug(23100) << "ERROR: " << linkStatus()->error() << ": " << linkStatus()->absoluteUrl().prettyURL() << endl;
+ if(linkStatus()->error() == i18n( "Javascript not supported" ))
+ return Qt::lightGray;
+ else
+ return Qt::red;
+ }
+
+ else if(linkStatus()->absoluteUrl().hasRef())
+ return Qt::blue;
+
+ else if(linkStatus()->absoluteUrl().protocol() != "http" &&
+ linkStatus()->absoluteUrl().protocol() != "https")
+ return Qt::darkGreen;
+
+ else
+ {
+ QString status_code(QString::number(linkStatus()->httpHeader().statusCode()));
+
+ if(status_code[0] == '0')
+ {
+ kdWarning(23100) << "status code == 0: " << endl;
+ kdWarning(23100) << linkStatus()->toString() << endl;
+ kdWarning(23100) << linkStatus()->httpHeader().toString() << endl;
+ }
+ //Q_ASSERT(status_code[0] != '0');
+
+ if(status_code[0] == '5')
+ return Qt::darkMagenta;
+
+ else if(status_code[0] == '4')
+ return Qt::red;
+
+ else if(status_code[0] == '3')
+ return Qt::blue;
+
+ else if(status_code[0] == '2')
+ return Qt::darkGreen;
+
+ else
+ return Qt::red;
+ }
+}
+*/
+
+/*
+
+********************* TableItemURL ***************************
+
+*/
+
+TableItemURL::TableItemURL(QTable* table, EditType et,
+ LinkStatus const* linkstatus, int column_index)
+ : TableItem(table, et, linkstatus, column_index)
+{
+ setText();
+}
+
+void TableItemURL::setText()
+{
+ if(linkStatus()->node() && linkStatus()->malformed())
+ {
+ if(linkStatus()->node()->url().isEmpty())
+ QTableItem::setText( linkStatus()->node()->content().simplifyWhiteSpace() );
+ else
+ QTableItem::setText( linkStatus()->node()->url() );
+ }
+ else
+ {
+ KURL url = linkStatus()->absoluteUrl();
+ QTableItem::setText(::convertToLocal(linkStatus()));
+ }
+}
+
+void TableItemURL::setPixmap()
+{}
+
+QString TableItemURL::toolTip() const
+{
+ return text(); // Pode parecer repeticao mas nao eh... Ver construtor
+}
+
+void TableItemURL::paint( QPainter *p, const QColorGroup &cg,
+ const QRect &cr, bool selected )
+{
+ // Get a color to draw the text
+ QColorGroup m_cg(cg);
+ QColor color(textStatusColor());
+ m_cg.setColor(QColorGroup::Text, color);
+
+ QTableItem::paint(p, m_cg, cr, selected);
+}
+
+QColor const& TableItemURL::textStatusColor() const
+{
+ // TODO clean this code
+
+ QString status_code(QString::number(linkStatus()->httpHeader().statusCode()));
+
+ if(linkStatus()->errorOccurred())
+ {
+ if(linkStatus()->error().contains(i18n( "Timeout" )))
+ return darkMagenta;
+ else if(linkStatus()->error().contains(i18n( "not supported" )))
+ return lightGray;
+ else
+ return red;
+ }
+ else if(linkStatus()->absoluteUrl().protocol() != "http" &&
+ linkStatus()->absoluteUrl().protocol() != "https")
+ return black;
+
+ else if(status_code[0] == '5')
+ return darkMagenta;
+
+ else if(status_code[0] == '4')
+ return red;
+
+ else
+ return black;
+}
+
+/*
+
+********************* TableItemStatus ***************************
+
+*/
+
+TableItemStatus::TableItemStatus(QTable* table, EditType et,
+ LinkStatus const* linkstatus, int column_index)
+ : TableItem(table, et, linkstatus, column_index)
+{
+ setAlignment(Qt::AlignHCenter /*| Qt :: AlignVCenter*/);
+ setText();
+ setPixmap();
+}
+
+void TableItemStatus::setText()
+{
+ if(linkStatus()->errorOccurred() ||
+ linkStatus()->status() == "OK" ||
+ linkStatus()->status() == "304")
+ {
+ QTableItem::setText("");
+ }
+ else
+ {
+ /*
+ if(linkStatus()->httpHeader().statusCode() == 0)
+ {
+ kdDebug(23100) << "TableItemStatus::setText : statusCode() == 0" << endl;
+ kdDebug(23100) << linkStatus()->toString() << endl;
+ kdDebug(23100) << linkStatus()->docHtml() << endl;
+ }
+ */
+ //Q_ASSERT(linkStatus()->httpHeader().statusCode() != 0); //<------------------------------------------------------------
+ //QTableItem::setText( QString::number(linkStatus()->httpHeader().statusCode()) );
+ QTableItem::setText( linkStatus()->status() );
+ }
+}
+
+void TableItemStatus::setPixmap()
+{
+ if(linkStatus()->errorOccurred())
+ {
+
+ if(linkStatus()->error().contains(i18n( "Timeout" )))
+ {
+ QTableItem::setPixmap(SmallIcon("kalarm"));
+ }
+ else if(linkStatus()->error() == i18n( "Malformed" ))
+ {
+ QTableItem::setPixmap(SmallIcon("bug"));
+ }
+ else
+ {
+ QTableItem::setPixmap(SmallIcon("no"));
+ }
+ }
+ else if(linkStatus()->status() == "304")
+ QTableItem::setPixmap(UserIcon("304"));
+
+ else if(linkStatus()->status() == "OK")
+ QTableItem::setPixmap(SmallIcon("ok"));
+}
+
+QString TableItemStatus::toolTip() const
+{
+ if(linkStatus()->errorOccurred() ||
+ linkStatus()->absoluteUrl().hasRef() ||
+ (linkStatus()->absoluteUrl().protocol() != "http" &&
+ linkStatus()->absoluteUrl().protocol() != "https"))
+ {
+ return i18n("%1").arg(linkStatus()->status());
+ }
+ else
+ return i18n("%1").arg(linkStatus()->httpHeader().reasonPhrase());
+}
+
+void TableItemStatus::paint( QPainter *p, const QColorGroup &cg,
+ const QRect &cr, bool selected )
+{
+ p->fillRect( 0, 0, cr.width(), cr.height(),
+ selected ? cg.brush( QColorGroup::Highlight )
+ : cg.brush( QColorGroup::Base ) );
+
+ int w = cr.width();
+ int h = cr.height();
+
+ int x = 0;
+ if ( !pixmap().isNull() )
+ {
+ p->drawPixmap( ( w - pixmap().width() ) / 2,
+ ( h - pixmap().height() ) / 2,
+ pixmap() );
+ x = pixmap().width() + 2;
+ }
+
+ // Get a color to draw the text
+ QColorGroup m_cg(cg);
+ QColor color(textStatusColor());
+ m_cg.setColor(QColorGroup::Text, color);
+
+ //QTableItem::paint(p, m_cg, cr, selected);
+
+ if ( selected )
+ p->setPen( m_cg.highlightedText() );
+ else
+ p->setPen( m_cg.text() );
+ p->drawText( x + 2, 0, w - x - 4, h,
+ wordWrap() ? (alignment() | WordBreak) : alignment(), text() );
+}
+
+/*
+
+********************* TableItemNome ***************************
+
+*/
+
+TableItemNome::TableItemNome(QTable* table, EditType et,
+ LinkStatus const* linkstatus, int column_index)
+ : TableItem(table, et, linkstatus, column_index)
+{
+ setText();
+}
+
+void TableItemNome::setText()
+{
+ QString label(linkStatus()->label());
+ if(!label.isNull())
+ QTableItem::setText(label.simplifyWhiteSpace());
+}
+
+void TableItemNome::setPixmap()
+{}
+
+QString TableItemNome::toolTip() const
+{
+ return text(); // Pode parecer repeticao mas nao eh... Ver construtor
+}
+
+#include "tablelinkstatus.moc"
diff --git a/klinkstatus/src/ui/tablelinkstatus.h b/klinkstatus/src/ui/tablelinkstatus.h
new file mode 100644
index 00000000..0b3f2cf2
--- /dev/null
+++ b/klinkstatus/src/ui/tablelinkstatus.h
@@ -0,0 +1,202 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 TABLE_LINKSTATUS_H
+#define TABLE_LINKSTATUS_H
+
+#include <qtable.h>
+#include <qstring.h>
+#include <qcolor.h>
+#include <qpopupmenu.h>
+#include <qvaluevector.h>
+class QStringList;
+
+class KURL;
+
+#include <vector>
+
+#include "../engine/linkstatus.h"
+#include "celltooltip.h"
+#include "resultview.h"
+
+using namespace std;
+
+
+int const STATUS_COLUMN_WIDTH = 50;
+
+class TableItem;
+
+class TableLinkstatus: public QTable, public ResultView
+{
+ Q_OBJECT
+public:
+
+ TableLinkstatus(QWidget * parent = 0, const char * name = 0,
+ int column_index_status = 1,
+ int column_index_label = 2,
+ int column_index_URL = 3);
+ ~TableLinkstatus();
+
+ virtual void setColumns(QStringList const& columns);
+
+ /* Insere uma nova entrada no fim da tabela */
+ virtual void insertResult(LinkStatus const* linkstatus);
+
+
+ virtual void clear();
+ void removeColunas();
+ virtual void show(ResultView::Status const& status);
+ virtual void showAll();
+
+
+ /* Specialization of QTable::ensureCellVisible */
+ virtual void ensureCellVisible(int row, int col);
+
+ virtual bool textFitsInCell(int row, int col) const;
+ virtual bool isEmpty() const;
+
+ TableItem* myItem(int row, int col) const;
+
+private slots:
+
+ virtual void slotPopupContextMenu(int row, int col, const QPoint& pos);
+ virtual void slotCopyUrlToClipboard() const;
+ virtual void slotCopyParentUrlToClipboard() const;
+ virtual void slotCopyCellTextToClipboard() const;
+ virtual void slotEditReferrersWithQuanta();
+ virtual void slotEditReferrerWithQuanta(int id);
+ virtual void slotEditReferrerWithQuanta(KURL const& url);
+ virtual void slotViewUrlInBrowser();
+ virtual void slotViewParentUrlInBrowser();
+ virtual void loadContextTableMenu(QValueVector<KURL> const& referrers, bool is_root = false);
+
+private:
+
+ vector<TableItem*> generateRowOfTableItems(LinkStatus const* linkstatus);
+ void insereLinha(vector<TableItem*> items);
+
+private:
+/*
+ int col_status_;
+ int col_label_;
+ int col_url_;
+ CellToolTip* cell_tip_;
+ QPopupMenu context_table_menu_;
+ QPopupMenu* sub_menu_;
+*/
+};
+
+
+class TableItem: public QTableItem, public ResultViewItem
+{
+public:
+
+ TableItem(QTable* table, EditType et,
+ LinkStatus const* linkstatus,
+ int column_index, int alignment = Qt::AlignLeft);
+ virtual ~TableItem();
+
+ virtual void setColumnIndex(int i);
+ virtual int columnIndex() const;
+
+ void setAlignment(int aFlags);
+ virtual int alignment() const;
+
+ virtual QString toolTip() const = 0;
+ //LinkStatus const* const linkStatus() const;
+
+protected:
+
+ //QColor const& textStatusColor() const;
+ virtual void paint( QPainter *p, const QColorGroup &cg,
+ const QRect &cr, bool selected );
+ virtual void setText() = 0;
+ virtual void setPixmap() = 0;
+
+private:
+
+ //LinkStatus* ls_;
+ //int column_index_;
+ int alignment_;
+};
+
+
+class TableItemURL: public TableItem
+{
+public:
+
+ TableItemURL(QTable* table, EditType et,
+ LinkStatus const* linkstatus, int column_index = 3);
+ //virtual ~TableItemURL(){};
+
+ virtual QString toolTip() const;
+
+protected:
+
+ virtual void setText();
+ virtual void setPixmap();
+ virtual void paint( QPainter *p, const QColorGroup &cg, const QRect &cr, bool selected );
+ QColor const& textStatusColor() const;
+};
+
+
+class TableItemStatus: public TableItem
+{
+public:
+
+ TableItemStatus(QTable* table, EditType et,
+ LinkStatus const* linkstatus, int column_index = 1);
+ //virtual ~TableItemStatus(){};
+
+ virtual QString toolTip() const;
+
+protected:
+
+ virtual void setText();
+ virtual void setPixmap();
+ virtual void paint( QPainter *p, const QColorGroup &cg, const QRect &cr, bool selected );
+};
+
+
+class TableItemNome: public TableItem
+{
+public:
+
+ TableItemNome(QTable* table, EditType et,
+ LinkStatus const* linkstatus, int column_index = 2);
+ //virtual ~TableItemNome(){};
+
+ virtual QString toolTip() const;
+
+protected:
+
+ virtual void setText();
+ virtual void setPixmap();
+ //virtual void paint( QPainter *p, const QColorGroup &cg, const QRect &cr, bool selected );
+};
+
+
+inline void TableItem::paint( QPainter *p, const QColorGroup &cg,
+ const QRect &cr, bool selected )
+{
+ QTableItem::paint(p, cg, cr, selected);
+}
+
+#endif
diff --git a/klinkstatus/src/ui/tabwidgetsession.cpp b/klinkstatus/src/ui/tabwidgetsession.cpp
new file mode 100644
index 00000000..9d9033a7
--- /dev/null
+++ b/klinkstatus/src/ui/tabwidgetsession.cpp
@@ -0,0 +1,274 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+#include "tabwidgetsession.h"
+#include "sessionwidget.h"
+#include "klsconfig.h"
+#include "treeview.h"
+#include "../engine/searchmanager.h"
+#include "../actionmanager.h"
+
+#include <qtoolbutton.h>
+#include <qcursor.h>
+#include <qtooltip.h>
+#include <qpushbutton.h>
+#include <qpixmap.h>
+#include <qiconset.h>
+#include <qstringlist.h>
+
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kstringhandler.h>
+#include <kcharsets.h>
+#include <kmimetype.h>
+#include <kaction.h>
+#include <kiconloader.h>
+
+
+TabWidgetSession::TabWidgetSession(QWidget* parent, const char* name, WFlags f)
+ : KTabWidget(parent, name, f) // tabs_ is initialized with size 17
+{
+ setFocusPolicy(QTabWidget::NoFocus);
+ setMargin(0);
+ setTabReorderingEnabled(true);
+ setHoverCloseButton(true);
+ setHoverCloseButtonDelayed(true);
+
+ tabs_.setAutoDelete(false);
+
+ QToolButton* tabs_new = new QToolButton(this);
+ tabs_new->setAccel(QKeySequence("Ctrl+N"));
+ connect(tabs_new, SIGNAL(clicked()), this, SLOT(slotNewSession()));
+ tabs_new->setIconSet(SmallIconSet("tab_new_raised"));
+ tabs_new->adjustSize();
+ QToolTip::add(tabs_new, i18n("Open new tab"));
+ setCornerWidget(tabs_new, TopLeft);
+
+ tabs_close_ = new QToolButton(this);
+ tabs_close_->setAccel(QKeySequence("Ctrl+W"));
+ connect(tabs_close_, SIGNAL(clicked()), this, SLOT(closeSession()));
+ tabs_close_->setIconSet(SmallIconSet("tab_remove"));
+ tabs_close_->adjustSize();
+ QToolTip::add(tabs_close_, i18n("Close the current tab"));
+ setCornerWidget(tabs_close_, TopRight);
+
+ connect(this, SIGNAL(currentChanged(QWidget*)), this, SLOT(slotCurrentChanged(QWidget*)));
+}
+
+TabWidgetSession::~TabWidgetSession()
+{}
+
+SessionWidget* TabWidgetSession::currentSession() const
+{
+ return tabs_[currentPageIndex()];
+}
+
+bool TabWidgetSession::emptySessionsExist() const
+{
+ if(count() == 0)
+ return true;
+
+ for(int i = 0; i != count(); ++i)
+ {
+ Q_ASSERT(tabs_[i]);
+ if(tabs_[i]->isEmpty() && !tabs_[i]->getSearchManager()->searching())
+ return true;
+ }
+ return false;
+}
+
+SessionWidget* TabWidgetSession::getEmptySession() const
+{
+ Q_ASSERT(emptySessionsExist());
+ Q_ASSERT(count() != 0);
+
+ for(uint i = 0; i != tabs_.count(); ++i)
+ {
+ if(tabs_[i]->isEmpty())
+ return tabs_[i];
+ }
+ return 0;
+}
+
+// Remember to use count() and not size()
+QIntDict<SessionWidget> const& TabWidgetSession::sessions() const
+{
+ return tabs_;
+}
+
+SessionWidget* TabWidgetSession::newSession()
+{
+ // TODO: settings: number of connections, timeout
+ SessionWidget* session_widget = newSessionWidget();
+ connect(session_widget, SIGNAL(signalUpdateTabLabel(const LinkStatus *, SessionWidget*)),
+ this, SLOT(updateTabLabel(const LinkStatus *, SessionWidget*)));
+
+ insertTab(session_widget, i18n("Session") + i18n(QString::number(count() + 1).ascii()));
+
+ tabs_.insert(count() - 1, session_widget);
+ Q_ASSERT(tabs_[count() - 1]);
+ setCurrentPage(count() - 1);
+
+ return session_widget;
+}
+
+SessionWidget* TabWidgetSession::newSession(KURL const& url)
+{
+ SessionWidget* sessionwidget = newSession();
+ currentSession()->setUrl(url);
+
+ return sessionwidget;
+}
+
+void TabWidgetSession::closeSession()
+{
+ if(count() > 1)
+ removePage(currentPage());
+
+ tabs_close_->setEnabled(count() > 1);
+ ActionManager::getInstance()->action("close_tab")->setEnabled(count() > 1);
+}
+
+SessionWidget* TabWidgetSession::newSessionWidget()
+{
+ SessionWidget* session_widget = new SessionWidget(KLSConfig::maxConnectionsNumber(),
+ KLSConfig::timeOut(), this, QString("session_widget-" + count()));
+
+ QStringList columns;
+
+ columns.push_back(TreeView::URL_LABEL);
+ columns.push_back(TreeView::STATUS_LABEL);
+ if(KLSConfig::showMarkupStatus())
+ columns.push_back(TreeView::MARKUP_LABEL);
+ columns.push_back(TreeView::LINK_LABEL_LABEL);
+
+ session_widget->setColumns(columns);
+
+ session_widget->tree_view->restoreLayout(KLSConfig::self()->config(), "klinkstatus");
+
+ return session_widget;
+}
+
+void TabWidgetSession::updateTabLabel(LinkStatus const* linkstatus, SessionWidget* page)
+{
+ QString label;
+ KURL url = linkstatus->absoluteUrl();
+
+ if(linkstatus->hasHtmlDocTitle())
+ {
+ label = linkstatus->htmlDocTitle();
+ label = KStringHandler::csqueeze(label, 30);
+ }
+ else
+ {
+ if(url.fileName(false).isEmpty())
+ label = url.prettyURL();
+ else
+ label = url.fileName(false);
+
+ label = KStringHandler::lsqueeze(label, 30);
+ }
+
+ changeTab(page, KCharsets::resolveEntities(label));
+ setTabIconSet(page, QIconSet(KMimeType::pixmapForURL(url)));
+}
+
+void TabWidgetSession::slotLoadSettings()
+{
+ for(uint i = 0; i != tabs_.count(); ++i)
+ {
+ if(tabs_[i]->isEmpty())
+ {
+ SessionWidget* session_widget = tabs_[i];
+ if(session_widget->isEmpty())
+ session_widget->slotLoadSettings(true);
+ else
+ session_widget->slotLoadSettings(false);
+ }
+ }
+}
+
+void TabWidgetSession::setUrl(KURL const& url)
+{
+ currentSession()->setUrl(url);
+}
+
+void TabWidgetSession::slotCurrentChanged(QWidget* /*page*/)
+{
+ tabs_close_->setEnabled(count() > 1);
+
+ SessionWidget* session_widget = currentSession();
+ ActionManager::getInstance()->slotUpdateSessionWidgetActions(session_widget);
+}
+
+void TabWidgetSession::slotHideSearchPanel()
+{
+ currentSession()->slotHideSearchPanel();
+}
+
+void TabWidgetSession::slotFollowLastLinkChecked()
+{
+ currentSession()->slotFollowLastLinkChecked();
+}
+
+void TabWidgetSession::slotResetSearchOptions()
+{
+ currentSession()->slotResetSearchOptions();
+}
+
+void TabWidgetSession::slotNewSession(KURL const& url)
+{
+ if(count() == 0 || !emptySessionsExist())
+ {
+ SessionWidget* sessionwidget = newSession(url);
+ ActionManager::getInstance()->initSessionWidget(sessionwidget);
+ }
+ else
+ {
+ SessionWidget* sessionwidget = getEmptySession();
+ sessionwidget->setUrl(url);
+ showPage(sessionwidget);
+ }
+
+ ActionManager::getInstance()->action("close_tab")->setEnabled(count() > 1);
+}
+
+void TabWidgetSession::slotStartSearch()
+{
+ currentSession()->slotStartSearch();
+}
+
+void TabWidgetSession::slotPauseSearch()
+{
+ currentSession()->slotPauseSearch();
+}
+
+void TabWidgetSession::slotStopSearch()
+{
+ currentSession()->slotStopSearch();
+}
+
+void TabWidgetSession::slotExportAsHTML()
+{
+ currentSession()->slotExportAsHTML();
+}
+
+
+#include "tabwidgetsession.moc"
diff --git a/klinkstatus/src/ui/tabwidgetsession.h b/klinkstatus/src/ui/tabwidgetsession.h
new file mode 100644
index 00000000..616c8173
--- /dev/null
+++ b/klinkstatus/src/ui/tabwidgetsession.h
@@ -0,0 +1,85 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 TABWIDGETSESSION_H
+#define TABWIDGETSESSION_H
+
+#include <ktabwidget.h>
+#include <kurl.h>
+
+#include <qintdict.h>
+class QToolButton;
+
+class SessionWidget;
+class LinkStatus;
+
+
+/**
+This class handles the creation and destruction of sessions, i.e, severals instances of searching tabs.
+
+@author Paulo Moura Guedes
+*/
+class TabWidgetSession : public KTabWidget
+{
+ Q_OBJECT
+
+public:
+ TabWidgetSession(QWidget * parent = 0, const char * name = 0, WFlags f = 0);
+ ~TabWidgetSession();
+
+ /** Set the URL in the current session widget */
+ void setUrl(KURL const& url);
+
+ SessionWidget* currentSession() const;
+ bool emptySessionsExist() const;
+ /** Returns the first empty session it finds */
+ SessionWidget* getEmptySession() const;
+ QIntDict<SessionWidget> const& sessions() const;
+
+
+public slots:
+ void slotNewSession(KURL const& url = KURL());
+ SessionWidget* newSession();
+ SessionWidget* newSession(KURL const& url);
+ void closeSession();
+ void updateTabLabel(LinkStatus const* linkstatus, SessionWidget*);
+ void slotLoadSettings();
+
+ void slotHideSearchPanel();
+ void slotResetSearchOptions();
+ void slotFollowLastLinkChecked();
+
+ void slotStartSearch();
+ void slotPauseSearch();
+ void slotStopSearch();
+
+ void slotExportAsHTML();
+
+private slots:
+ void slotCurrentChanged(QWidget* page);
+
+private:
+ SessionWidget* newSessionWidget();
+
+private:
+ QIntDict<SessionWidget> tabs_;
+ QToolButton* tabs_close_;
+};
+
+#endif
diff --git a/klinkstatus/src/ui/treeview.cpp b/klinkstatus/src/ui/treeview.cpp
new file mode 100644
index 00000000..7ad92d8e
--- /dev/null
+++ b/klinkstatus/src/ui/treeview.cpp
@@ -0,0 +1,609 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kapplication.h>
+#include <kurl.h>
+#include <krun.h>
+#include <dcopref.h>
+#include <kmessagebox.h>
+#include <dcopclient.h>
+#include <kcharsets.h>
+
+#include <qvaluevector.h>
+#include <qheader.h>
+#include <qclipboard.h>
+
+#include "treeview.h"
+#include "../global.h"
+#include "../engine/linkstatus.h"
+#include "../engine/linkfilter.h"
+#include "../cfg/klsconfig.h"
+
+
+TreeView::TreeView(QWidget *parent, const char *name)
+ : KListView(parent, name),
+ ResultView(),
+ current_column_(0)
+{
+ setShowToolTips(true);
+ //setAllColumnsShowFocus(true);
+ setSorting(1000); // don't start sorting any column
+ setShowSortIndicator(true);
+ //setFocusPolicy( WheelFocus );
+ setRootIsDecorated(KLSConfig::displayTreeView());
+// setResizeMode(QListView::LastColumn);
+
+ sub_menu_ = new QPopupMenu(this, "sub_menu_referrers");
+
+ connect(this, SIGNAL( rightButtonClicked ( QListViewItem *, const QPoint &, int )),
+ this, SLOT( slotPopupContextMenu( QListViewItem *, const QPoint &, int )) );
+}
+
+
+TreeView::~TreeView()
+{
+ saveLayout(KLSConfig::self()->config(), "klinkstatus");
+}
+
+void TreeView::setColumns(QStringList const& columns)
+{
+ ResultView::setColumns(columns);
+ removeColunas();
+
+// resetColumns is called automatically
+ for(uint i = 0; i != columns.size(); ++i)
+ {
+ addColumn(i18n(columns[i]));
+ setColumnWidthMode(i, QListView::Manual);
+ }
+
+ setColumnAlignment(col_status_ - 1, Qt::AlignCenter);
+ if(KLSConfig::showMarkupStatus())
+ setColumnAlignment(col_markup_ - 1, Qt::AlignCenter);
+}
+
+void TreeView::resetColumns()
+{
+ setColumnWidth(col_url_ - 1, (int)(0.45 * width()));
+
+ setResizeMode(QListView::LastColumn); // fit to the window
+ // resize again
+ setColumnWidthMode(col_label_ - 1, QListView::Manual);
+ setResizeMode(QListView::NoColumn);
+}
+
+double TreeView::columnsWidth() const
+{
+ kdDebug(23100) << "columns: " << columns() << endl;
+
+ double width = 0.0;
+ for(int i = 0; i != columns(); ++i)
+ {
+ kdDebug(23100) << "column width: " << columnWidth(i) << endl;
+ width += columnWidth(i);
+ }
+ return width;
+}
+
+void TreeView::clear()
+{
+ KListView::clear();
+}
+
+void TreeView::removeColunas()
+{
+ clear();
+}
+
+void TreeView::show(ResultView::Status const& status)
+{
+ QListViewItemIterator it(static_cast<KListView*> (this));
+ while(it.current())
+ {
+ TreeViewItem* item = myItem(it.current());
+ if(!ResultView::displayableWithStatus(item->linkStatus(), status))
+ {
+ item->setVisible(false);
+ //kdDebug(23100) << "Hide: " << item->linkStatus()->absoluteUrl().url() << endl;
+ }
+ else
+ {
+ item->setVisible(true);
+ //item->setEnabled(true);
+ /*
+ if(KLSConfig::displayTreeView() && status != ResultView::good && item->parent())
+ {
+ TreeViewItem* parent = myItem(item->parent());
+ while(parent)
+ {
+ kdDebug(23100) << "Show: " << parent->linkStatus()->absoluteUrl().url() << endl;
+
+ parent->setVisible(true);
+ //parent->setEnabled(false);
+
+ if(parent->parent())
+ parent = myItem(parent->parent());
+ else
+ parent = 0;
+ }
+ }
+ */
+ }
+//
+ ++it;
+ }
+}
+
+void TreeView::show(LinkMatcher link_matcher)
+{
+ QListViewItemIterator it(this);
+ while(it.current())
+ {
+ TreeViewItem* item = myItem(it.current());
+ bool match = link_matcher.matches(*(item->linkStatus()));
+
+ if(tree_display_)
+ item->setEnabled(match);
+ else
+ item->setVisible(match);
+
+ ++it;
+ }
+}
+
+void TreeView::showAll()
+{
+ QListViewItemIterator it(this);
+ while(it.current())
+ {
+ it.current()->setVisible(true);
+ //it.current()->setEnabled(true);
+ ++it;
+ }
+}
+
+void TreeView::ensureRowVisible(const QListViewItem * i, bool tree_display)
+{
+ QScrollBar* vertical_scroll_bar = verticalScrollBar();
+
+ if(tree_display ||
+ vertical_scroll_bar->value() > (vertical_scroll_bar->maxValue() - vertical_scroll_bar->lineStep()))
+ ensureItemVisible(i);
+}
+
+bool TreeView::isEmpty() const
+{
+ return !childCount();
+}
+
+void TreeView::resizeEvent(QResizeEvent *e)
+{
+ KListView::resizeEvent(e);
+ resetColumns();
+ clipper()->repaint();
+}
+
+void TreeView::slotPopupContextMenu(QListViewItem* item, const QPoint& pos, int col)
+{
+ current_column_ = col;
+
+ TreeViewItem* tree_item = myItem(item);
+ if(tree_item)
+ {
+ QValueVector<KURL> referrers = tree_item->linkStatus()->referrers();
+ loadContextTableMenu(referrers, tree_item->linkStatus()->isRoot());
+ context_table_menu_.popup(pos);
+ }
+}
+
+void TreeView::slotCopyUrlToClipboard() const
+{
+ TreeViewItem* _item = myItem(currentItem());
+ QString content(_item->linkStatus()->absoluteUrl().prettyURL());
+ QClipboard* cb = kapp->clipboard();
+ cb->setText(content);
+}
+
+void TreeView::slotCopyParentUrlToClipboard() const
+{
+ TreeViewItem* _item = myItem(currentItem());
+ QString content(_item->linkStatus()->parent()->absoluteUrl().prettyURL());
+ QClipboard* cb = kapp->clipboard();
+ cb->setText(content);
+}
+
+void TreeView::slotCopyCellTextToClipboard() const
+{
+ TreeViewItem* _item = myItem(currentItem());
+ QString cell_text(_item->text(current_column_));
+ QClipboard* cb = kapp->clipboard();
+ cb->setText(cell_text);
+}
+
+void TreeView::slotEditReferrersWithQuanta()
+{
+ TreeViewItem* _item = myItem(currentItem());
+ QValueVector<KURL> referrers = _item->linkStatus()->referrers();
+
+ if(Global::isQuantaAvailableViaDCOP())
+ {
+ for(uint i = 0; i != referrers.size(); ++i)
+ slotEditReferrerWithQuanta(referrers[i]);
+ }
+ else
+ {
+ QStringList list_urls;
+
+ for(uint i = 0; i != referrers.size(); ++i)
+ list_urls.append(referrers[i].url());
+
+ Global::openQuanta(list_urls);
+ }
+}
+
+void TreeView::slotEditReferrerWithQuanta(int id)
+{
+ int index = sub_menu_->indexOf(id);
+
+ if(index == 0)
+ return;
+ Q_ASSERT(index != -1);
+ Q_ASSERT(index != 1); // separator
+
+ //kdDebug(23100) << "id: " << id << endl;
+ //kdDebug(23100) << "index: " << index << endl;
+
+ index -= 2; // The list of referrers starts on index = 2
+
+ TreeViewItem* _item = myItem(currentItem());
+ QValueVector<KURL> referrers = _item->linkStatus()->referrers();
+ Q_ASSERT(index >= 0 && (uint)index < referrers.size());
+
+ slotEditReferrerWithQuanta(referrers[index]);
+}
+
+void TreeView::slotEditReferrerWithQuanta(KURL const& url)
+{
+ QString filePath = url.url();
+
+ if(Global::isQuantaAvailableViaDCOP())
+ {
+ DCOPRef quanta(Global::quantaDCOPAppId(),"WindowManagerIf");
+ bool success = quanta.send("openFile", filePath, 0, 0);
+
+ if(!success)
+ {
+ QString message = i18n("<qt>File <b>%1</b> cannot be opened. Might be a DCOP problem.</qt>").arg(filePath);
+ KMessageBox::error(parentWidget(), message);
+ }
+ }
+ else
+ {
+ QStringList args(url.url());
+ Global::openQuanta(args);
+ }
+}
+
+void TreeView::slotViewUrlInBrowser()
+{
+ TreeViewItem* _item = myItem(currentItem());
+ KURL url = _item->linkStatus()->absoluteUrl();
+
+ if(url.isValid())
+ {
+ (void) new KRun (url, 0, url.isLocalFile(), true);
+ }
+ else
+ KMessageBox::sorry(this, i18n("Invalid URL."));
+}
+
+void TreeView::slotViewParentUrlInBrowser()
+{
+ TreeViewItem* _item = myItem(currentItem());
+
+ if(_item->linkStatus()->isRoot())
+ {
+ KMessageBox::sorry(this, i18n("ROOT URL."));
+ }
+ else
+ {
+ LinkStatus const* ls_parent = _item->linkStatus()->parent();
+ Q_ASSERT(ls_parent);
+
+ KURL url = ls_parent->absoluteUrl();
+
+ if(url.isValid())
+ (void) new KRun (url, 0, url.isLocalFile(), true);
+ else
+ KMessageBox::sorry(this, i18n("Invalid URL."));
+ }
+}
+
+void TreeView::loadContextTableMenu(QValueVector<KURL> const& referrers, bool is_root)
+{
+ context_table_menu_.clear();
+ sub_menu_->clear();
+
+ if(!is_root)
+ {
+ sub_menu_->insertItem(i18n("All"), this, SLOT(slotEditReferrersWithQuanta()));
+ sub_menu_->insertSeparator();
+
+ for(uint i = 0; i != referrers.size(); ++i)
+ {
+ sub_menu_->insertItem(referrers[i].prettyURL());
+ }
+ connect(sub_menu_, SIGNAL(activated(int)), this, SLOT(slotEditReferrerWithQuanta(int)));
+
+ context_table_menu_.insertItem(SmallIconSet("edit"), i18n("Edit Referrer with Quanta"),
+ sub_menu_);
+ context_table_menu_.insertSeparator();
+ }
+ else
+ {
+ int id = context_table_menu_.insertItem(SmallIconSet("fileopen"), i18n("Edit Referrer with Quanta"));
+ context_table_menu_.setItemEnabled(id, false);
+ }
+
+ context_table_menu_.insertItem(SmallIconSet("fileopen"), i18n("Open URL"),
+ this, SLOT(slotViewUrlInBrowser()));
+
+ context_table_menu_.insertItem(/*SmallIconSet("fileopen"), */i18n("Open Referrer URL"),
+ this, SLOT(slotViewParentUrlInBrowser()));
+
+ context_table_menu_.insertSeparator();
+
+ context_table_menu_.insertItem(SmallIconSet("editcopy"), i18n("Copy URL"),
+ this, SLOT(slotCopyUrlToClipboard()));
+
+ context_table_menu_.insertItem(/*SmallIconSet("editcopy"), */i18n("Copy Referrer URL"),
+ this, SLOT(slotCopyParentUrlToClipboard()));
+
+ context_table_menu_.insertItem(/*SmallIconSet("editcopy"), */i18n("Copy Cell Text"),
+ this, SLOT(slotCopyCellTextToClipboard()));
+}
+
+TreeViewItem* TreeView::myItem(QListViewItem* item) const
+{
+ TreeViewItem* _item = dynamic_cast<TreeViewItem*> (item);
+ Q_ASSERT(_item);
+ return _item;
+}
+
+
+/* ******************************* TreeViewItem ******************************* */
+
+TreeViewItem::TreeViewItem(TreeView* parent, QListViewItem* after,
+ LinkStatus const* linkstatus)
+ : KListViewItem(parent, after),
+ last_child_(0), root_(parent)
+{
+ init(linkstatus);
+}
+
+TreeViewItem::TreeViewItem(TreeView* root, QListViewItem* listview_item, QListViewItem* after,
+ LinkStatus const* linkstatus)
+ : KListViewItem(listview_item, after),
+ last_child_(0), root_(root)
+
+{
+ init(linkstatus);
+}
+
+TreeViewItem::~TreeViewItem()
+{}
+
+void TreeViewItem::init(LinkStatus const* linkstatus)
+{
+ setOpen(true);
+
+ for(int i = 0; i != root_->numberOfColumns(); ++i)
+ {
+ TreeColumnViewItem item(root_, linkstatus, i + 1);
+ column_items_.push_back(item);
+
+ if(i + 1 == root_->urlColumnIndex()) {
+ setText(item.columnIndex() - 1, KURL::decode_string(
+ KCharsets::resolveEntities(item.text(i + 1))));
+ }
+ else {
+ setText(item.columnIndex() - 1, KCharsets::resolveEntities(item.text(i + 1)));
+ }
+
+ setPixmap(item.columnIndex() - 1, item.pixmap(i + 1));
+ }
+}
+
+void TreeViewItem::setLastChild(QListViewItem* last_child)
+{
+ Q_ASSERT(last_child);
+ last_child_ = last_child;
+}
+
+QListViewItem* TreeViewItem::lastChild() const
+{
+ return last_child_;
+}
+
+QString TreeViewItem::key(int column, bool) const
+{
+ // FIXME magic numbers
+ switch(column)
+ {
+ case 1: // status column
+ return linkStatus()->statusText();
+ }
+
+ return text(column);
+}
+
+LinkStatus const* TreeViewItem::linkStatus() const
+{
+ return column_items_[0].linkStatus();
+}
+
+void TreeViewItem::paintCell(QPainter * p, const QColorGroup & cg, int column, int width, int align)
+{
+ TreeColumnViewItem item = column_items_[column];
+
+ // Get a color to draw the text
+ QColorGroup m_cg(cg);
+ QColor color(item.textStatusColor());
+ m_cg.setColor(QColorGroup::Text, color);
+
+ KListViewItem::paintCell(p, m_cg, column, width, align);
+
+ setHeight(22);
+}
+
+
+/* ******************************* TreeColumnViewItem ******************************* */
+
+TreeColumnViewItem::TreeColumnViewItem(TreeView* root, LinkStatus const* linkstatus, int column_index)
+ : root_(root), ls_(linkstatus), column_index_(column_index)
+{
+ Q_ASSERT(ls_);
+// Q_ASSERT(column_index_ > 0);
+}
+
+TreeColumnViewItem::~TreeColumnViewItem()
+{}
+
+/*
+void TreeColumnViewItem::setColumnIndex(int i)
+{
+ Q_ASSERT(i > 0);
+ column_index_ = i;
+}
+*/
+
+int TreeColumnViewItem::columnIndex() const
+{
+ return column_index_;
+}
+
+LinkStatus const* TreeColumnViewItem::linkStatus() const
+{
+ Q_ASSERT(ls_);
+ return ls_;
+}
+
+QColor const& TreeColumnViewItem::textStatusColor() const
+{
+ if(columnIndex() == root_->urlColumnIndex() || columnIndex() == root_->statusColumnIndex())
+ {
+ if(linkStatus()->status() == LinkStatus::BROKEN)
+ return Qt::red;
+ else if(linkStatus()->status() == LinkStatus::HTTP_CLIENT_ERROR)
+ return Qt::red;
+ else if(linkStatus()->status() == LinkStatus::HTTP_REDIRECTION)
+ return Qt::black;
+ else if(linkStatus()->status() == LinkStatus::HTTP_SERVER_ERROR)
+ return Qt::darkMagenta;
+ else if(linkStatus()->status() == LinkStatus::MALFORMED)
+ return Qt::red;
+ else if(linkStatus()->status() == LinkStatus::NOT_SUPPORTED)
+ return Qt::lightGray;
+ else if(linkStatus()->status() == LinkStatus::SUCCESSFULL)
+ return Qt::black;
+ else if(linkStatus()->status() == LinkStatus::TIMEOUT)
+ return Qt::darkMagenta;
+ else if(linkStatus()->status() == LinkStatus::UNDETERMINED)
+ return Qt::blue;
+
+ return Qt::red;
+ }
+ else
+ return Qt::black;
+}
+
+
+QString TreeColumnViewItem::text(int column) const
+{
+ Q_ASSERT(column > 0);
+
+
+ if(column == root_->urlColumnIndex())
+ {
+ if(linkStatus()->node() && linkStatus()->malformed())
+ {
+ if(linkStatus()->node()->url().isEmpty())
+ return linkStatus()->node()->content().simplifyWhiteSpace();
+ else
+ return linkStatus()->node()->url();
+ }
+ else
+ {
+ KURL url = linkStatus()->absoluteUrl();
+ return Url::convertToLocal(linkStatus());
+ }
+ }
+ else if(column == root_->statusColumnIndex())
+ {
+ return QString();
+ }
+ else if(column == root_->labelColumnIndex())
+ {
+ QString label(linkStatus()->label());
+ if(!label.isNull())
+ return label.simplifyWhiteSpace();
+ }
+
+ return QString();
+}
+
+QPixmap TreeColumnViewItem::pixmap(int column) const
+{
+ Q_ASSERT(column > 0);
+
+ if(column == root_->statusColumnIndex())
+ {
+ if(linkStatus()->status() == LinkStatus::BROKEN)
+ return SmallIcon("no");
+ else if(linkStatus()->status() == LinkStatus::HTTP_CLIENT_ERROR)
+ return SmallIcon("no");
+ else if(linkStatus()->status() == LinkStatus::HTTP_REDIRECTION)
+ {
+ if(linkStatus()->statusText() == "304")
+ return UserIcon("304");
+ else
+ return SmallIcon("redo");
+ }
+ else if(linkStatus()->status() == LinkStatus::HTTP_SERVER_ERROR)
+ return SmallIcon("no");
+ else if(linkStatus()->status() == LinkStatus::MALFORMED)
+ return SmallIcon("editdelete");
+ else if(linkStatus()->status() == LinkStatus::NOT_SUPPORTED)
+ return SmallIcon("help");
+ else if(linkStatus()->status() == LinkStatus::SUCCESSFULL)
+ return SmallIcon("ok");
+ else if(linkStatus()->status() == LinkStatus::TIMEOUT)
+ return SmallIcon("history_clear");
+ else if(linkStatus()->status() == LinkStatus::UNDETERMINED)
+ return SmallIcon("help");
+ }
+
+ return QPixmap();
+}
+
+
+#include "treeview.moc"
diff --git a/klinkstatus/src/ui/treeview.h b/klinkstatus/src/ui/treeview.h
new file mode 100644
index 00000000..eef34ff8
--- /dev/null
+++ b/klinkstatus/src/ui/treeview.h
@@ -0,0 +1,142 @@
+//
+// C++ Interface: treeview
+//
+// Description:
+//
+//
+// Author: Paulo Moura Guedes <moura@kdewebdev.org>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef TREEVIEW_H
+#define TREEVIEW_H
+
+#include <klistview.h>
+
+#include "resultview.h"
+class TreeViewItem;
+class TreeColumnViewItem;
+class LinkMatcher;
+
+/**
+@author Paulo Moura Guedes
+TreeView and TreeViewItem and currently a little messes up in its API
+because of ResultView. ResultView class was to be the base interface to
+a QTable and a QListView, but the APIs are a little diferent... then I realize
+that a QTable view isn't needed at all so some day I will clean this up.
+*/
+class TreeView : public KListView, public ResultView
+{
+ Q_OBJECT
+public:
+
+ TreeView(QWidget *parent = 0, const char *name = 0);
+ ~TreeView();
+
+ virtual void setColumns(QStringList const& columns);
+ virtual void clear();
+ void removeColunas();
+ virtual void show(ResultView::Status const& status);
+ void show(LinkMatcher link_matcher);
+ virtual void showAll();
+
+ void setTreeDisplay(bool tree_display);
+
+ /**
+ If tree_display is false the view scrolls to follow the last link inserted,
+ except if the user scrolls the view up (like Konsole).
+ If tree_view, it follows always the last link inserted.
+ */
+ void ensureRowVisible(const QListViewItem * i, bool tree_display);
+ virtual bool isEmpty() const;
+
+ TreeViewItem* myItem(QListViewItem* item) const;
+
+protected:
+ virtual void resizeEvent(QResizeEvent *e);
+
+private slots:
+
+ void slotPopupContextMenu(QListViewItem *, const QPoint &, int);
+ virtual void slotCopyUrlToClipboard() const;
+ virtual void slotCopyParentUrlToClipboard() const;
+ virtual void slotCopyCellTextToClipboard() const;
+ virtual void slotEditReferrersWithQuanta();
+ virtual void slotEditReferrerWithQuanta(int id);
+ virtual void slotEditReferrerWithQuanta(KURL const& url);
+ virtual void slotViewUrlInBrowser();
+ virtual void slotViewParentUrlInBrowser();
+ virtual void loadContextTableMenu(QValueVector<KURL> const& referrers, bool is_root = false);
+
+private:
+ void resetColumns();
+ double columnsWidth() const;
+
+private:
+ int current_column_; // apparently it's impossible to know what is the current column
+ bool tree_display_;
+};
+
+inline void TreeView::setTreeDisplay(bool tree_display) {
+ tree_display_ = tree_display;
+ setRootIsDecorated(tree_display_);
+}
+
+
+/* ******************************* TreeViewItem ******************************* */
+
+class TreeViewItem: public KListViewItem
+{
+public:
+
+ TreeViewItem(TreeView* parent, QListViewItem* after,
+ LinkStatus const* linkstatus);
+ TreeViewItem(TreeView* root, QListViewItem* parent_item, QListViewItem* after,
+ LinkStatus const* linkstatus);
+ virtual ~TreeViewItem();
+
+ void setLastChild(QListViewItem* last_child);
+ QListViewItem* lastChild() const;
+
+ QString key(int column, bool) const;
+ LinkStatus const* linkStatus() const;
+
+protected:
+ virtual void paintCell(QPainter * p, const QColorGroup & cg, int column, int width, int align);
+
+private:
+ void init(LinkStatus const* linkstatus);
+
+private:
+ QValueVector<TreeColumnViewItem> column_items_;
+ QListViewItem* last_child_;
+ TreeView* root_;
+};
+
+
+/* ******************************* TreeColumnViewItem ******************************* */
+
+class TreeColumnViewItem
+{
+public:
+ TreeColumnViewItem()
+ {}
+ ;
+ TreeColumnViewItem(TreeView* root, LinkStatus const* linkstatus, int column_index);
+ ~TreeColumnViewItem();
+
+ //void setColumnIndex(int i);
+ int columnIndex() const;
+ LinkStatus const* linkStatus() const;
+ QColor const& textStatusColor() const;
+ QString text(int column) const;
+ QPixmap pixmap(int column) const;
+
+private:
+ TreeView* root_;
+ LinkStatus const* ls_;
+ int column_index_;
+};
+
+#endif
diff --git a/klinkstatus/src/utils/Makefile.am b/klinkstatus/src/utils/Makefile.am
new file mode 100644
index 00000000..0d2ba5ba
--- /dev/null
+++ b/klinkstatus/src/utils/Makefile.am
@@ -0,0 +1,7 @@
+INCLUDES = $(LIBXSLT_CFLAGS) $(all_includes)
+METASOURCES = AUTO
+libutils_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libutils.la
+noinst_HEADERS = mvector.h utils.h xsl.h
+libutils_la_SOURCES = utils.cpp xsl.cpp
+libutils_la_LIBADD = $(LIBXSLT_LIBS) $(LIBXML_LIBS)
diff --git a/klinkstatus/src/utils/mvector.h b/klinkstatus/src/utils/mvector.h
new file mode 100644
index 00000000..e48cfda3
--- /dev/null
+++ b/klinkstatus/src/utils/mvector.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 VECTOR_H
+#define VECTOR_H
+
+#include <vector>
+
+using namespace std;
+
+typedef unsigned int uint;
+
+
+template<class T>
+void append(vector<T>* const v1, vector<T> const* const v2);
+
+
+template<class T>
+void append(vector<T>* const v1, vector<T> const* const v2)
+{
+ v1->reserve(v1->size() + v2->size());
+
+ for(uint i = 0; i != v2->size(); ++i)
+ v1->push_back( (*v2)[i]);
+}
+
+
+#endif
diff --git a/klinkstatus/src/utils/utils.cpp b/klinkstatus/src/utils/utils.cpp
new file mode 100644
index 00000000..6259f8d0
--- /dev/null
+++ b/klinkstatus/src/utils/utils.cpp
@@ -0,0 +1,204 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include "utils.h"
+
+#include <qprocess.h>
+#include <qwidget.h>
+
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+
+
+QString htmlDocCharset[NUMBER_OF_HTML_CODES][2] = {
+
+ { "&euro;", "@" },
+ { "&#09;", "\t" },
+ { "&#10;", "\n" },
+ { "&#13;", "\r" },
+ { "&#32;", " " },
+ { "&#33;", "!" },
+ { "&#34;", "\"" },
+ { "&#35;", "#" },
+ { "&#36;", "$" },
+ { "&#37;", "%" },
+ { "&#38;", "&" },
+ { "&#39;", "'" },
+ { "&#40;", "(" },
+ { "&#41;", ")" },
+ { "&#42;", "*" },
+ { "&#43;", "+" },
+ { "&#44;", "," },
+ { "&#45;", "-" },
+ { "&#46;", "." },
+ { "&#47;", "/" },
+ // numbers....
+ { "&#58;", ":" },
+ { "&#59;", ";" },
+ { "&#60;", "<" },
+ { "&#61;", "=" },
+ { "&#62;", ">" },
+ { "&#63;", "?" },
+ { "&#64;", "@" },
+ // letters...
+ { "&#91;", "[" },
+ { "&#92;", "\\" },
+ { "&#93;", "]" },
+ { "&#94;", "^" },
+ { "&#95;", "_" },
+ { "&#96;", "`" },
+ //letters...
+ { "&#123;", "{" },
+ { "&#124;", "|" },
+ { "&#125;", "}" },
+ { "&#126;", "~" },
+ { "&#128;", "?" },
+ { "&#130;", "," },
+ { "&#131;", "?" },
+ { "&#132;", "\"" },
+ { "&#133;", "?" },
+ { "&#134;", "?" },
+ { "&#135;", "?" },
+ { "&#137;", "?" },
+ { "&#138;", "?" },
+ { "&#139;", "<" },
+ { "&#140;", "?" },
+ { "&#142;", "?" },
+ { "&#145;", "'" },
+ { "&#146;", "'" },
+ { "&#147;", "\"" },
+ { "&#148;", "\"" },
+ { "&#149;", "*" },
+ { "&#150;", "-" },
+ { "&#151;", "-" },
+ { "&#152;", "~" },
+ { "&#153;", "?" },
+ { "&#154;", "?" },
+ { "&#155;", ">" },
+ { "&#156;", "?" },
+ { "&#158;", "?" },
+ { "&#159;", "?" },
+ { "&#161;", "?" },
+ { "&#162;", "?" },
+ { "&#163;", "?" },
+ { "&#164;", "?" },
+ { "&#165;", "?" },
+ { "&#166;", "?" },
+ { "&#167;", "?" },
+ { "&#168;", "?" },
+ { "&#169;", "" },
+ { "&#170;", "?" },
+ { "&#171;", "?" },
+ { "&#172;", "?" },
+ { "&#174;", "?" },
+ { "&#175;", "?" },
+ { "&#176;", "" },
+ { "&#177;", "?" },
+ { "&#178;", "" },
+ { "&#179;", "?" },
+ { "&#180;", "?" },
+ { "&#181;", "?" },
+ { "&#182;", "?" },
+ { "&#183;", "" },
+ { "&#184;", "?" },
+ { "&#185;", "?" },
+ { "&#186;", "?" },
+ { "&#187;", "?" },
+ { "&#188;", "?" },
+ { "&#189;", "?" },
+ { "&#190;", "?" }
+
+};
+
+
+void decode(QString& url)
+{
+ if( (int)url.find('&') != -1)
+ {
+ for(int i = 0; i != NUMBER_OF_HTML_CODES; ++i)
+ {
+ int index = url.find(htmlDocCharset[i][0]);
+ if(index != - 1)
+ {
+ url.replace(htmlDocCharset[i][0], htmlDocCharset[i][1]);
+ }
+ }
+ }
+}
+/*
+void decode(string& url)
+{
+ if( (int)url.find('&') != -1)
+ {
+ for(int i = 0; i != NUMBER_OF_HTML_CODES; ++i)
+ {
+ int index = url.find(htmlDocCharset[i][0].latin1());
+ if(index != - 1)
+ {
+ int length = htmlDocCharset[i][0].length();
+ url.replace(index, length, htmlDocCharset[i][1].latin1());
+ }
+ }
+ }
+}
+*/
+int smallerUnsigned(int a, int b)
+{
+ if(a >= 0 && b >= 0)
+ {
+ if(a < b)
+ return -1;
+ else if(a > b)
+ return 1;
+ else
+ return 0;
+ }
+
+ else if(a < 0 && b < 0)
+ return 0;
+
+ else if(a < 0)
+ return 1;
+
+ else
+ return -1;
+}
+
+namespace FileManager
+{
+QString read(QString const& path)
+{
+ QFile file(path);
+
+ if(!file.open(IO_ReadOnly))
+ {
+ kdDebug() << "File " << path << " not found." << endl;
+ return QString();
+ }
+
+ QTextStream stream(&file);
+ QString fileString = stream.read();
+
+ file.close();
+
+ return fileString;
+}
+}
diff --git a/klinkstatus/src/utils/utils.h b/klinkstatus/src/utils/utils.h
new file mode 100644
index 00000000..97d81ea0
--- /dev/null
+++ b/klinkstatus/src/utils/utils.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 UTILS_H
+#define UTILS_H
+
+#include <kurl.h>
+#include <qstring.h>
+
+//#include <string>
+
+
+using namespace std;
+
+
+int const NUMBER_OF_HTML_CODES = 92;
+extern QString htmlDocCharset[NUMBER_OF_HTML_CODES][2];
+
+/**
+ Decode the html charset.
+ e.g.
+ decode("mail&#64;server&#46;org") => "mail@server.org"
+*/
+void decode(QString& url);
+//void decode(string& url);
+
+/**
+ Compares to integers and returns -1 if a is smaller than b,
+ 1 if b is smaller than a, and 0 if a and b are equal or both negative.
+ If one of the integers is negative and the other isn't, it is considered
+ that the positive is smaller.
+ e.g.:
+ a = 0, b = +1 => -1
+ a = +1, b = 0 => +1
+ a = -1, b = -1 => 0
+ a = +3, b = +3 => 0
+ a = +1, b = -1 => -1
+*/
+int smallerUnsigned(int a, int b);
+
+namespace FileManager
+{
+QString read(QString const& path);
+}
+
+
+#endif
diff --git a/klinkstatus/src/utils/xsl.cpp b/klinkstatus/src/utils/xsl.cpp
new file mode 100644
index 00000000..c3b13412
--- /dev/null
+++ b/klinkstatus/src/utils/xsl.cpp
@@ -0,0 +1,437 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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. *
+ ***************************************************************************/
+#include "xsl.h"
+
+#include <libxml/globals.h>
+#include <libxml/parser.h>
+
+// Don't try to sort the libxslt includes alphabetically!
+// transform.h _HAS_ to be after xsltInternals.h and xsltconfig.h _HAS_ to be
+// the first libxslt include or it will break the compilation on some
+// libxslt versions
+#include <libxslt/xsltconfig.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/transform.h>
+
+// stdlib.h is required to build on Solaris
+#include <stdlib.h>
+
+#include <qregexp.h>
+#include <qsignal.h>
+#include <qstylesheet.h>
+#include <qthread.h>
+#include <qevent.h>
+#include <qmutex.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+
+/**
+ * @author Jason Keirstead <jason@keirstead.org>
+ *
+ * The thread class that actually performs the XSL processing.
+ * Using a thread allows async operation.
+ */
+class KopeteXSLThread : public QObject, public QThread
+{
+public:
+ /**
+ * Thread constructor
+ *
+ * @param xmlString The XML to be transformed
+ * @param xslString The XSL stylesheet we will use to transform
+ * @param target Target object to connect to for async operation
+ * @param slotCompleted Slot to fire on completion in asnc operation
+ */
+ KopeteXSLThread( const QString &xmlString, xsltStylesheetPtr xslDoc, QObject *target = 0L, const char *slotCompleted = 0L );
+
+ /**
+ * Reimplemented from QThread. Does the processing.
+ */
+ virtual void run();
+
+ /**
+ * A user event is used to get back to the UI thread to emit the completed signal
+ */
+ bool event( QEvent *event );
+
+ static QString xsltTransform( const QString &xmlString, xsltStylesheetPtr xslDoc );
+
+ /**
+ * Returns the result string
+ */
+ const QString &result()
+ { return m_resultString; };
+
+private:
+ QString m_xml;
+ xsltStylesheetPtr m_xsl;
+ QString m_resultString;
+ QObject *m_target;
+ const char *m_slotCompleted;
+ QMutex dataMutex;
+};
+
+KopeteXSLThread::KopeteXSLThread( const QString &xmlString, xsltStylesheetPtr xslDoc, QObject *target, const char *slotCompleted )
+{
+ m_xml = xmlString;
+ m_xsl = xslDoc;
+
+ m_target = target;
+ m_slotCompleted = slotCompleted;
+}
+
+void KopeteXSLThread::run()
+{
+ dataMutex.lock();
+ m_resultString = xsltTransform( m_xml, m_xsl );
+ dataMutex.unlock();
+ // get back to the main thread
+ qApp->postEvent( this, new QEvent( QEvent::User ) );
+}
+
+bool KopeteXSLThread::event( QEvent *event )
+{
+ if ( event->type() == QEvent::User )
+ {
+ dataMutex.lock();
+ if( m_target && m_slotCompleted )
+ {
+ QSignal completeSignal( m_target );
+ completeSignal.connect( m_target, m_slotCompleted );
+ completeSignal.setValue( m_resultString );
+ completeSignal.activate();
+ }
+ dataMutex.unlock();
+ delete this;
+ return true;
+ }
+ return QObject::event( event );
+}
+
+QString KopeteXSLThread::xsltTransform( const QString &xmlString, xsltStylesheetPtr styleSheet )
+{
+ // Convert QString into a C string
+ QCString xmlCString = xmlString.utf8();
+
+ QString resultString;
+ QString errorMsg;
+
+ xmlDocPtr xmlDoc = xmlParseMemory( xmlCString, xmlCString.length() );
+ if ( xmlDoc )
+ {
+ if ( styleSheet )
+ {
+ static QCString appPath( QString::fromLatin1("\"%1\"").arg( KApplication::kApplication()->dirs()->findDirs("appdata", QString::fromLatin1("styles/data") ).front() ).utf8() );
+
+ static const char* params[3] = {
+ "appdata",
+ appPath,
+ NULL
+ };
+
+ xmlDocPtr resultDoc = xsltApplyStylesheet( styleSheet, xmlDoc, params );
+ if ( resultDoc )
+ {
+ // Save the result into the QString
+ xmlChar *mem;
+ int size;
+ xmlDocDumpMemory( resultDoc, &mem, &size );
+ resultString = QString::fromUtf8( QCString( ( char * )( mem ), size + 1 ) );
+ xmlFree( mem );
+ xmlFreeDoc( resultDoc );
+ }
+ else
+ {
+ errorMsg = i18n( "Message is null." );
+ }
+ }
+ else
+ {
+ errorMsg = i18n( "The selected stylesheet is invalid." );
+ }
+
+ xmlFreeDoc( xmlDoc );
+ }
+ else
+ {
+ errorMsg = i18n( "Message could not be parsed. This is likely due to an encoding problem." );
+ }
+
+ if ( resultString.isEmpty() )
+ {
+ resultString = i18n( "<div><b>KLinkStatus encountered the following error while parsing a message:</b><br />%1</div>" ).arg( errorMsg );
+ }
+
+ #ifdef RAWXSL
+ kdDebug(23100) << k_funcinfo << resultString << endl;
+ #endif
+ return resultString;
+}
+
+class XSLTPrivate
+{
+public:
+ xmlDocPtr xslDoc;
+ xsltStylesheetPtr styleSheet;
+ unsigned int flags;
+};
+
+XSLT::XSLT( const QString &document, QObject *parent )
+ : QObject( parent ), d(new XSLTPrivate)
+{
+ d->flags = 0;
+ d->xslDoc = 0;
+ d->styleSheet = 0;
+
+ // Init Stuff
+ xmlLoadExtDtdDefaultValue = 0;
+ xmlSubstituteEntitiesDefault( 1 );
+
+ setXSLT( document );
+}
+
+XSLT::~XSLT()
+{
+ xsltFreeStylesheet( d->styleSheet );
+
+ delete d;
+}
+
+void XSLT::setXSLT( const QString &_document )
+{
+ // Search for '<kopete-i18n>' elements and feed them through i18n().
+ // After that replace the %VAR% variables with their proper XSLT counterpart.
+ //
+ // FIXME: Preprocessing the document using the QString API is fast and simple,
+ // but also error-sensitive.
+ // In fact, there are a couple of known issues with this algorithm that
+ // depend on the strings in the current styles. If these strings change
+ // they may break the parsing here.
+ //
+ // The reason I'm doing it like this is because of issues with QDOM and
+ // namespaces in earlier Qt versions. When we drop Qt 3.1.x support we
+ // should probably convert this to more accurate DOM code. - Martijn
+ //
+ // Actually, since we need to parse into a libxml2 document anyway, this whole
+ // nonsense could be replaced with some simple XPath expressions - JK
+ //
+ QRegExp elementMatch( QString::fromLatin1( "<kopete-i18n>(.*)</kopete-i18n>" ) );
+ elementMatch.setMinimal( true );
+ QString document = _document;
+ int pos;
+ while ( ( pos = elementMatch.search( document ) ) != -1 )
+ {
+ QString orig = elementMatch.cap( 1 );
+ //kdDebug( 14010 ) << k_funcinfo << "Original text: " << orig << endl;
+
+ // Split on % and go over all parts
+ // WARNING: If you change the translator comment, also change it in the
+ // styles/extracti18n Perl script, because the strings have to be
+ // identical!
+ QStringList parts = QStringList::split( '%', i18n(
+ "Translators: The %FOO% placeholders are variables that are substituted "
+ "in the code, please leave them untranslated", orig.utf8() ), true );
+
+ // The first part is always text, as our variables are written like %FOO%
+ QStringList::Iterator it = parts.begin();
+ QString trans = *it;
+ bool prependPercent = true;
+ it = parts.remove( it );
+ for ( it = parts.begin(); it != parts.end(); ++it )
+ {
+ prependPercent = false;
+
+ if ( *it == QString::fromLatin1( "TIME" ) )
+ {
+ trans += QString::fromLatin1( "<xsl:value-of select=\"@time\"/>" );
+ }
+ else if ( *it == QString::fromLatin1( "TIMESTAMP" ) )
+ {
+ trans += QString::fromLatin1( "<xsl:value-of select=\"@timestamp\"/>" );
+ }
+ else if ( *it == QString::fromLatin1( "FORMATTEDTIMESTAMP" ) )
+ {
+ trans += QString::fromLatin1( "<xsl:value-of select=\"@formattedTimestamp\"/>" );
+ }
+ else if ( *it == QString::fromLatin1( "FROM_CONTACT_DISPLAYNAME" ) )
+ {
+ trans += QString::fromLatin1( "<span><xsl:attribute name=\"title\">"
+ "<xsl:choose>"
+ "<xsl:when test='from/contact/@contactId=from/contact/contactDisplayName/@text'>"
+ "<xsl:value-of disable-output-escaping=\"yes\" select=\"from/contact/metaContactDisplayName/@text\"/>"
+ "</xsl:when>"
+ "<xsl:otherwise>"
+ "<xsl:value-of disable-output-escaping=\"yes\" select=\"from/contact/metaContactDisplayName/@text\"/>&#160;"
+ "(<xsl:value-of disable-output-escaping=\"yes\" select=\"from/contact/@contactId\"/>)"
+ "</xsl:otherwise>"
+ "</xsl:choose></xsl:attribute>"
+ "<xsl:attribute name=\"dir\">"
+ "<xsl:value-of select=\"from/contact/contactDisplayName/@dir\"/>"
+ "</xsl:attribute>"
+ "<xsl:value-of disable-output-escaping=\"yes\" select=\"from/contact/contactDisplayName/@text\"/></span>" );
+ }
+ else if ( *it == QString::fromLatin1( "TO_CONTACT_DISPLAYNAME" ) )
+ {
+ trans += QString::fromLatin1( "<span><xsl:attribute name=\"title\">"
+ "<xsl:choose>"
+ "<xsl:when test='to/contact/@contactId=from/contact/contactDisplayName/@text'>"
+ "<xsl:value-of disable-output-escaping=\"yes\" select=\"to/contact/metaContactDisplayName/@text\"/>"
+ "</xsl:when>"
+ "<xsl:otherwise>"
+ "<xsl:value-of disable-output-escaping=\"yes\" select=\"to/contact/metaContactDisplayName/@text\"/>&#160;"
+ "(<xsl:value-of disable-output-escaping=\"yes\" select=\"to/contact/@contactId\"/>)"
+ "</xsl:otherwise>"
+ "</xsl:choose></xsl:attribute>"
+ "<xsl:attribute name=\"dir\">"
+ "<xsl:value-of select=\"to/contact/contactDisplayName/@dir\"/>"
+ "</xsl:attribute>"
+ "<xsl:value-of disable-output-escaping=\"yes\" select=\"to/contact/contactDisplayName/@text\"/></span>" );
+ }
+ else if ( *it == QString::fromLatin1( "FROM_METACONTACT_DISPLAYNAME" ) )
+ {
+ trans += QString::fromLatin1( "<span>"
+ "<xsl:attribute name=\"dir\">"
+ "<xsl:value-of select=\"from/contact/metaContactDisplayName/@dir\"/>"
+ "</xsl:attribute>"
+ "<xsl:value-of disable-output-escaping=\"yes\" select=\"from/contact/metaContactDisplayName/@text\"/></span>" );
+ }
+ else if ( *it == QString::fromLatin1( "TO_METACONTACT_DISPLAYNAME" ) )
+ {
+ trans += QString::fromLatin1( "<span>"
+ "<xsl:attribute name=\"dir\">"
+ "<xsl:value-of select=\"to/contact/metaContactDisplayName/@dir\"/>"
+ "</xsl:attribute>"
+ "<xsl:value-of disable-output-escaping=\"yes\" select=\"to/contact/metaContactDisplayName/@text\"/></span>" );
+ }
+ else if ( *it == QString::fromLatin1( "FROM_CONTACT_ID" ) )
+ {
+ trans += QString::fromLatin1( "<span><xsl:attribute name=\"title\">"
+ "<xsl:value-of disable-output-escaping=\"yes\" select=\"from/contact/contactDisplayName/@text\"/></xsl:attribute>"
+ "<xsl:value-of disable-output-escaping=\"yes\" select=\"from/contact/@contactId\"/></span>" );
+ }
+ else if ( *it == QString::fromLatin1( "TO_CONTACT_ID" ) )
+ {
+ trans += QString::fromLatin1( "<span><xsl:attribute name=\"title\">"
+ "<xsl:value-of disable-output-escaping=\"yes\" select=\"to/contact/contactDisplayName/@text\"/></xsl:attribute>"
+ "<xsl:value-of disable-output-escaping=\"yes\" select=\"to/contact/@contactId\"/></span>" );
+ }
+ else if ( *it == QString::fromLatin1( "BODY" ) )
+ {
+ trans += QString::fromLatin1( "<xsl:value-of disable-output-escaping=\"yes\" select=\"body\"/>" );
+ }
+ else
+ {
+ if ( prependPercent )
+ trans += '%';
+ trans += *it;
+ prependPercent = true;
+ }
+ }
+ //kdDebug( 14010 ) << k_funcinfo << "Translated text: " << trans << endl;
+ // Add "<kopete-i18n>" and "</kopete-i18n>" to length, hence the '+ 27'
+ document.replace( uint( pos ), orig.length() + 27, trans );
+ }
+
+ #ifdef RAWXSL
+ kdDebug(14000) << k_funcinfo << document.utf8() << endl;
+ #endif
+
+ //Freeing the stylesheet also frees the doc pointer;
+ xsltFreeStylesheet( d->styleSheet );
+ d->styleSheet = 0;
+ d->xslDoc = 0;
+ d->flags = 0;
+
+ QCString rawDocument = document.utf8();
+ d->xslDoc = xmlParseMemory( rawDocument, rawDocument.length() );
+
+ if( d->xslDoc )
+ {
+ d->styleSheet = xsltParseStylesheetDoc( d->xslDoc );
+ if( d->styleSheet )
+ {
+ // Check for flags
+ QStringList flags;
+ for( xmlNodePtr child = d->xslDoc->children; child != d->xslDoc->last; child = child->next )
+ {
+ if( child->type == XML_PI_NODE )
+ {
+ //We have a flag. Enable it;
+ QCString flagData( (const char*)child->content );
+
+ if( flagData.contains( "Flag:" ) )
+ {
+ flags += flagData.mid(5);
+ }
+ }
+ }
+
+ if( !flags.isEmpty() )
+ setProperty("flags", flags.join( QString::fromLatin1("|") ) );
+ }
+ else
+ {
+ kdWarning(14000) << "Invalid stylesheet provided" << endl;
+
+ //We don't have a stylesheet, so free the doc pointer
+ xmlFreeDoc( d->xslDoc );
+ d->styleSheet = 0;
+ d->xslDoc = 0;
+ }
+ }
+ else
+ {
+ kdWarning(14000) << "Invalid stylesheet provided" << endl;
+ d->xslDoc = 0;
+ }
+}
+
+QString XSLT::transform( const QString &xmlString )
+{
+ return KopeteXSLThread::xsltTransform( xmlString, d->styleSheet );
+}
+
+void XSLT::transformAsync( const QString &xmlString, QObject *target, const char *slotCompleted )
+{
+ ( new KopeteXSLThread( xmlString, d->styleSheet, target, slotCompleted ) )->start();
+}
+
+bool XSLT::isValid() const
+{
+ return d->styleSheet != NULL;
+}
+
+void XSLT::setFlags( unsigned int flags )
+{
+ d->flags = flags;
+}
+
+unsigned int XSLT::flags() const
+{
+ return d->flags;
+}
+
+#include "xsl.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/klinkstatus/src/utils/xsl.h b/klinkstatus/src/utils/xsl.h
new file mode 100644
index 00000000..9d12c1c1
--- /dev/null
+++ b/klinkstatus/src/utils/xsl.h
@@ -0,0 +1,111 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Paulo Moura Guedes *
+ * moura@kdewebdev.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * 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 XSL_H
+#define XSL_H
+
+#include <qobject.h>
+
+class XSLTPrivate;
+
+/**
+ @author Paulo Moura Guedes <moura@kdewebdev.org>
+
+ Taken from kopetexsl. Kudos to the Kopete team.
+ *
+ * This class provides an easy to use interface to basic
+ * libxslt transformations.
+*/
+class XSLT : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY( Flags flags READ flags WRITE setFlags )
+ Q_PROPERTY( bool isValid READ isValid )
+
+ Q_SETS( Flags )
+
+public:
+ /**
+ * Special flags to be used during the transformation process. Passed
+ * into the engine as processing instructions.
+ */
+ enum Flags
+ {
+ TransformAllMessages = 1
+ };
+
+ /**
+ * Constructor.
+ *
+ * Constructs a new XSLT parser using the provided XSLT document
+ */
+ XSLT( const QString &xsltDocument, QObject *parent = 0L );
+
+ virtual ~XSLT();
+
+ /**
+ * Set the XSLT document
+ *
+ * @return an ORed set of @ref Flags, or 0 if none
+ */
+ void setXSLT( const QString &document );
+
+ /**
+ * Transforms the XML string using the XSLT document, synchronously
+ *
+ * @param xmlString The source XML
+ * @return The result of the transformation
+ */
+ QString transform( const QString &xmlString );
+
+ /**
+ * Transforms the XML string using the XSLT document, asynchronously
+ *
+ * @param xmlString The source XML
+ * @param target The QObject that contains the slot to be executed when processing is complete
+ * @param slotCompleted A slot that accepts a QVariant & paramater, that is the result
+ * of the transformation
+ */
+ void transformAsync( const QString &xmlString, QObject *target, const char *slotCompleted );
+
+ /**
+ * Check whether the XSLT document is valid
+ *
+ * @return Whether the document represents a valid XSLT stylesheet
+ */
+ bool isValid() const;
+
+ /**
+ * @return An ORed list of Flags that the current stylesheet provides via processing instructions
+ */
+ unsigned int flags() const;
+
+ /**
+ * Sets flags to be used for the transformation.
+ *
+ * @param flags An ORed list of flags
+ */
+ void setFlags( unsigned int flags );
+
+private:
+ XSLTPrivate *d;
+};
+
+#endif