1 | /* |
2 | * This file is part of the KDE Help Center |
3 | * |
4 | * Copyright (C) 2002 Frerich Raabe (raabe@kde.org) |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | */ |
20 | |
21 | #include "toc.h" |
22 | |
23 | #include "docentry.h" |
24 | |
25 | #include <KIconLoader> |
26 | #include <KProcess> |
27 | #include <KStandardDirs> |
28 | #include <KDebug> |
29 | #include <KXmlGuiWindow> |
30 | #include <KApplication> |
31 | #include <KStatusBar> |
32 | #include <KLocale> |
33 | |
34 | #include <QDir> |
35 | #include <QFileInfo> |
36 | #include <QTextStream> |
37 | #include <QPixmap> |
38 | |
39 | #include <sys/stat.h> |
40 | |
41 | using namespace KHC; |
42 | |
43 | class TOCItem : public NavigatorItem |
44 | { |
45 | public: |
46 | TOCItem( TOC *parent, QTreeWidgetItem *parentItem, QTreeWidgetItem *after, const QString &text ); |
47 | |
48 | const TOC *toc() const { return m_toc; } |
49 | |
50 | private: |
51 | TOC *m_toc; |
52 | }; |
53 | |
54 | class TOCChapterItem : public TOCItem |
55 | { |
56 | public: |
57 | TOCChapterItem( TOC *toc, NavigatorItem *parent, QTreeWidgetItem *after, const QString &title, |
58 | const QString &name ); |
59 | |
60 | virtual QString url(); |
61 | |
62 | private: |
63 | QString m_name; |
64 | }; |
65 | |
66 | class TOCSectionItem : public TOCItem |
67 | { |
68 | public: |
69 | TOCSectionItem( TOC *toc, TOCChapterItem *parent, QTreeWidgetItem *after, const QString &title, |
70 | const QString &name ); |
71 | |
72 | virtual QString url(); |
73 | |
74 | private: |
75 | QString m_name; |
76 | }; |
77 | |
78 | bool TOC::m_alreadyWarned = false; |
79 | |
80 | TOC::TOC( NavigatorItem *parentItem ) |
81 | { |
82 | m_parentItem = parentItem; |
83 | } |
84 | |
85 | void TOC::build( const QString &file ) |
86 | { |
87 | QFileInfo fileInfo( file ); |
88 | QString fileName = fileInfo.absoluteFilePath(); |
89 | const QStringList resourceDirs = KGlobal::dirs()->resourceDirs( "html" ); |
90 | QStringList::ConstIterator it = resourceDirs.begin(); |
91 | QStringList::ConstIterator end = resourceDirs.end(); |
92 | for ( ; it != end; ++it ) { |
93 | if ( fileName.startsWith( *it ) ) { |
94 | fileName.remove( 0, ( *it ).length() ); |
95 | break; |
96 | } |
97 | } |
98 | |
99 | QString cacheFile = fileName.replace( '/', "__" ); |
100 | #ifdef Q_WS_WIN |
101 | cacheFile = cacheFile.replace( ':', "_" ); |
102 | #endif |
103 | m_cacheFile = KStandardDirs::locateLocal( "cache" , "help/" + cacheFile ); |
104 | m_sourceFile = file; |
105 | |
106 | if ( cacheStatus() == NeedRebuild ) |
107 | buildCache(); |
108 | else |
109 | fillTree(); |
110 | } |
111 | |
112 | TOC::CacheStatus TOC::cacheStatus() const |
113 | { |
114 | if ( !QFile::exists( m_cacheFile ) || |
115 | sourceFileCTime() != cachedCTime() ) |
116 | return NeedRebuild; |
117 | |
118 | return CacheOk; |
119 | } |
120 | |
121 | int TOC::sourceFileCTime() const |
122 | { |
123 | struct stat stat_buf; |
124 | stat( QFile::encodeName( m_sourceFile ).data(), &stat_buf ); |
125 | |
126 | return stat_buf.st_ctime; |
127 | } |
128 | |
129 | int TOC::cachedCTime() const |
130 | { |
131 | QFile f( m_cacheFile ); |
132 | if ( !f.open( QIODevice::ReadOnly ) ) |
133 | return 0; |
134 | |
135 | QDomDocument doc; |
136 | if ( !doc.setContent( &f ) ) |
137 | return 0; |
138 | |
139 | QDomComment timestamp = doc.documentElement().lastChild().toComment(); |
140 | |
141 | return timestamp.data().trimmed().toInt(); |
142 | } |
143 | |
144 | void TOC::buildCache() |
145 | { |
146 | KXmlGuiWindow *mainWindow = dynamic_cast<KXmlGuiWindow *>( kapp->activeWindow() ); |
147 | |
148 | KProcess *meinproc = new KProcess; |
149 | connect( meinproc, SIGNAL( finished( int, QProcess::ExitStatus) ), |
150 | this, SLOT( meinprocExited( int, QProcess::ExitStatus) ) ); |
151 | |
152 | *meinproc << KStandardDirs::locate("exe" , "meinproc4" ); |
153 | *meinproc << "--stylesheet" << KStandardDirs::locate( "data" , "khelpcenter/table-of-contents.xslt" ); |
154 | *meinproc << "--output" << m_cacheFile; |
155 | *meinproc << m_sourceFile; |
156 | |
157 | meinproc->setOutputChannelMode(KProcess::OnlyStderrChannel); |
158 | meinproc->start(); |
159 | if (!meinproc->waitForStarted()) { |
160 | kError() << "could not start process" << meinproc->program(); |
161 | if (mainWindow && !m_alreadyWarned) { |
162 | ; // add warning message box with don't display again option |
163 | // http://api.kde.org/4.0-api/kdelibs-apidocs/kdeui/html/classKDialog.html |
164 | m_alreadyWarned = true; |
165 | } |
166 | delete meinproc; |
167 | } |
168 | } |
169 | |
170 | void TOC::meinprocExited( int exitCode, QProcess::ExitStatus exitStatus) |
171 | { |
172 | KProcess *meinproc = static_cast<KProcess *>(sender()); |
173 | KXmlGuiWindow *mainWindow = dynamic_cast<KXmlGuiWindow *>( kapp->activeWindow() ); |
174 | |
175 | if ( exitStatus == QProcess::CrashExit || exitCode != 0 ) { |
176 | kError() << "running" << meinproc->program() << "failed with exitCode" << exitCode; |
177 | kError() << "stderr output:" << meinproc->readAllStandardError(); |
178 | if (mainWindow && !m_alreadyWarned) { |
179 | ; // add warning message box with don't display again option |
180 | // http://api.kde.org/4.0-api/kdelibs-apidocs/kdeui/html/classKDialog.html |
181 | m_alreadyWarned = true; |
182 | } |
183 | delete meinproc; |
184 | return; |
185 | } |
186 | |
187 | delete meinproc; |
188 | |
189 | // add a timestamp to the meinproc4 created xml file |
190 | QFile f( m_cacheFile ); |
191 | if ( !f.open( QIODevice::ReadWrite ) ) |
192 | return; |
193 | |
194 | QDomDocument doc; |
195 | if ( !doc.setContent( &f ) ) |
196 | return; |
197 | |
198 | QDomComment timestamp = doc.createComment( QString::number( sourceFileCTime() ) ); |
199 | doc.documentElement().appendChild( timestamp ); |
200 | |
201 | // write back updated xml content |
202 | f.seek( 0 ); |
203 | QTextStream stream( &f ); |
204 | stream.setCodec( "UTF-8" ); |
205 | #ifdef Q_WS_WIN |
206 | /* |
207 | the problem that on german systems umlauts are displayed as '?' for unknown (Qt'r related ?) reasons |
208 | is caused by wrong encoding type conversations and has been fixed in kdelibs/kdoctools |
209 | To have propper encoding tags in the xml file, QXmlDocument::save() is used. |
210 | */ |
211 | doc.save(stream, 1, QDomNode::EncodingFromTextStream); |
212 | |
213 | #else |
214 | stream << doc.toString(); |
215 | #endif |
216 | f.close(); |
217 | fillTree(); |
218 | } |
219 | |
220 | void TOC::fillTree() |
221 | { |
222 | QFile f( m_cacheFile ); |
223 | if ( !f.open( QIODevice::ReadOnly ) ) |
224 | return; |
225 | |
226 | QDomDocument doc; |
227 | if ( !doc.setContent( &f ) ) |
228 | return; |
229 | |
230 | TOCChapterItem *chapItem = 0; |
231 | QDomNodeList chapters = doc.documentElement().elementsByTagName( "chapter" ); |
232 | for ( int chapterCount = 0; chapterCount < chapters.count(); chapterCount++ ) { |
233 | QDomElement chapElem = chapters.item( chapterCount ).toElement(); |
234 | QDomElement chapTitleElem = childElement( chapElem, QLatin1String( "title" ) ); |
235 | QString chapTitle = chapTitleElem.text().simplified(); |
236 | QDomElement chapRefElem = childElement( chapElem, QLatin1String( "anchor" ) ); |
237 | QString chapRef = chapRefElem.text().trimmed(); |
238 | |
239 | chapItem = new TOCChapterItem( this, m_parentItem, chapItem, chapTitle, chapRef ); |
240 | |
241 | TOCSectionItem *sectItem = 0; |
242 | QDomNodeList sections = chapElem.elementsByTagName( "section" ); |
243 | for ( int sectCount = 0; sectCount < sections.count(); sectCount++ ) { |
244 | QDomElement sectElem = sections.item( sectCount ).toElement(); |
245 | QDomElement sectTitleElem = childElement( sectElem, QLatin1String( "title" ) ); |
246 | QString sectTitle = sectTitleElem.text().simplified(); |
247 | QDomElement sectRefElem = childElement( sectElem, QLatin1String( "anchor" ) ); |
248 | QString sectRef = sectRefElem.text().trimmed(); |
249 | |
250 | sectItem = new TOCSectionItem( this, chapItem, sectItem, sectTitle, sectRef ); |
251 | } |
252 | } |
253 | } |
254 | |
255 | QDomElement TOC::childElement( const QDomElement &element, const QString &name ) |
256 | { |
257 | QDomElement e; |
258 | for ( e = element.firstChild().toElement(); !e.isNull(); e = e.nextSibling().toElement() ) |
259 | if ( e.tagName() == name ) |
260 | break; |
261 | return e; |
262 | } |
263 | |
264 | void TOC::slotItemSelected( QTreeWidgetItem *item ) |
265 | { |
266 | TOCItem *tocItem; |
267 | if ( ( tocItem = dynamic_cast<TOCItem *>( item ) ) ) |
268 | emit itemSelected( tocItem->entry()->url() ); |
269 | |
270 | item->setExpanded( !item->isExpanded() ); |
271 | } |
272 | |
273 | TOCItem::TOCItem( TOC *toc, QTreeWidgetItem *parentItem, QTreeWidgetItem *after, const QString &text ) |
274 | : NavigatorItem( new DocEntry( text ), parentItem, after ) |
275 | { |
276 | setAutoDeleteDocEntry( true ); |
277 | m_toc = toc; |
278 | } |
279 | |
280 | TOCChapterItem::TOCChapterItem( TOC *toc, NavigatorItem *parent, QTreeWidgetItem *after, |
281 | const QString &title, const QString &name ) |
282 | : TOCItem( toc, parent, after, title ), |
283 | m_name( name ) |
284 | { |
285 | setExpanded( false ); |
286 | entry()->setUrl(url()); |
287 | } |
288 | |
289 | QString TOCChapterItem::url() |
290 | { |
291 | return QLatin1String("help:" ) + toc()->application() + QLatin1Char('/') + m_name |
292 | + QLatin1String(".html" ); |
293 | } |
294 | |
295 | TOCSectionItem::TOCSectionItem( TOC *toc, TOCChapterItem *parent, QTreeWidgetItem *after, |
296 | const QString &title, const QString &name ) |
297 | : TOCItem( toc, parent, after, title ), |
298 | m_name( name ) |
299 | { |
300 | setIcon( 0, SmallIcon( "text-plain" ) ); |
301 | entry()->setUrl(url()); |
302 | } |
303 | |
304 | QString TOCSectionItem::url() |
305 | { |
306 | if ( static_cast<TOCSectionItem *>( parent()->child(0) ) == this ) |
307 | return static_cast<TOCChapterItem *>( parent() )->url() + '#' + m_name; |
308 | |
309 | return "help:" + toc()->application() + '/' + m_name + ".html" ; |
310 | } |
311 | |
312 | #include "toc.moc" |
313 | // vim:ts=2:sw=2:et |
314 | |