OSDN Git Service

6bf804e5b845c6ad191ecb94a14874aac3413fe4
[kita/kita.git] / kita / src / domtree.cpp
1 /***************************************************************************
2  *   Copyright (C) 2004, 2007 by Kita Developers                           *
3  *   ikemo@users.sourceforge.jp                                            *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  ***************************************************************************/
10
11 /* This class manages the DOM elements  */
12
13 #include "domtree.h"
14
15 #include <kurl.h>
16 #include <dom/dom_text.h>
17
18 #include "libkita/datinfo.h"
19 #include "libkita/datmanager.h"
20 #include "libkita/kita_misc.h"
21 #include "libkita/kita-utf8.h"
22
23 using namespace Kita;
24
25 DomTree::DomTree(const DOM::HTMLDocument& hdoc, const KUrl& datUrl)
26 {
27     m_hdoc = hdoc;
28     m_bufSize = 0;
29     m_bottomNum = 0;
30
31     /* create the nodes of footer, header, etc. */
32     createKokoyon();
33     createFooter();
34     createHeader();
35
36     /* get pointer of DatInfo */
37     /* Note that m_datURL is already locked in the HTMLPart. */
38     m_datInfo = DatManager(datUrl).getDatInfoPointer();
39 }
40
41 DomTree::~DomTree() {}
42
43 /*
44  * This function creates DOM elements of both title and body.
45  * To show the res, call appendRes()
46  */
47 bool DomTree::createResElement(int num)
48 {
49     Q_ASSERT(num > 0);
50     Q_ASSERT(m_datInfo != 0);
51
52     if (num < m_bufSize && m_resStatus[ num ] != HTML_NOTPARSED) {
53         /* already parsed */
54         return true;
55     }
56
57     if (num >= m_bufSize) {
58         /* resize buffer size */
59         if (m_bufSize == 0) m_bufSize = 100;
60         while (num >= m_bufSize) m_bufSize += 500;
61
62         m_titleElm.resize(m_bufSize);
63         m_bodyElm.resize(m_bufSize);
64         int statusSize = m_resStatus.count();
65         m_resStatus.resize(m_bufSize);
66         for (int i = statusSize; i < m_bufSize; i++) {
67             m_resStatus[i] = false;
68         }
69         int numSize = m_coloredNum.count();
70         m_coloredNum.resize(m_bufSize);
71         for (int i = numSize; i < m_bufSize; i++) {
72             m_coloredNum[i] = false;
73         }
74     }
75
76     /* cleate elements */
77     QString titleHTML, bodyHTML;
78     m_resStatus[ num ] = m_datInfo->getHTML(num, true, titleHTML, bodyHTML);
79
80     if (m_resStatus[ num ] == HTML_NOTPARSED) {
81         return false;
82     }
83
84     m_titleElm[ num ] = m_hdoc.createElement("DIV");
85     m_titleElm[ num ].setAttribute("class", "res_title");
86     m_titleElm[ num ].setAttribute("id", QString::number(num));
87     m_titleElm[ num ].setInnerHTML(titleHTML);
88
89     m_bodyElm[ num ] = m_hdoc.createElement("DIV");
90     m_bodyElm[ num ].setAttribute("class", "res_body");
91     m_bodyElm[ num ].setAttribute("id", QString::number(num));
92     m_bodyElm[ num ].setInnerHTML(bodyHTML);
93
94     return true;
95 }
96
97 /*
98  * append the response
99  */
100 bool DomTree::appendRes(int num)
101 {
102     if (!createResElement(num)) return false;
103
104     m_hdoc.body().appendChild(m_titleElm[ num ]);
105     m_hdoc.body().appendChild(m_bodyElm[ num ]);
106
107     if (num > m_bottomNum) m_bottomNum = num;
108
109     return true;
110 }
111
112 /*
113  * redraw all
114  */
115 void DomTree::redraw(bool force)
116 {
117     Q_ASSERT(m_datInfo != 0);
118
119     int readNum = m_datInfo->getReadNum();
120
121     /* don't forget to reset abone here... */
122     m_datInfo->resetAbone();
123
124     for (int i = 1; i <= readNum; i++) {
125         QString titleHTML, bodyHTML;
126
127         int oldStatus = m_resStatus[ i ];
128         m_resStatus[ i ] = m_datInfo->getHTML(i , true, titleHTML, bodyHTML);
129
130         if (force || oldStatus != m_resStatus[ i ]) {
131             m_titleElm[ i ].setInnerHTML(titleHTML);
132             m_bodyElm[ i ].setInnerHTML(bodyHTML);
133         }
134     }
135 }
136
137
138 /*
139  * change color of number of the res which is responsed.
140  *
141  * See also DomTree::changeColorOfNumber() ,
142  * DatInfo::copyOneLineToResDat(),
143  * and DatInfo::collectResponsedRes().
144  */
145 void DomTree::changeColorOfAllResponsedNumber()
146 {
147     for (int i = 1; i <= m_bottomNum; ++i) {
148         if (m_datInfo->isResponsed(i)) {
149             changeColorOfNumber(i);
150         }
151     }
152 }
153
154 int DomTree::getBottomResNumber() const
155 {
156     return m_bottomNum;
157 }
158
159 /*
160  * append footer & header
161  */
162 void DomTree::appendFooterAndHeader()
163 {
164     Q_ASSERT(m_datInfo != 0);
165
166     int readNum = m_datInfo->getReadNum();
167     if (!readNum) return ;
168
169     updateHeader(m_header);
170     updateFooter(m_footer);
171
172     m_hdoc.body().insertBefore(m_header, m_hdoc.body().firstChild());
173     m_hdoc.body().appendChild(m_footer);
174 }
175
176 /*
177  * append kokomadeyonda
178  */
179 void DomTree::appendKokoyon()
180 {
181     Q_ASSERT(m_datInfo != 0);
182
183     int readNum = m_datInfo->getReadNum();
184     if (!readNum) return ;
185
186     int viewPos = m_datInfo->getViewPos();
187     if (viewPos == 0) return ;
188
189     int i = viewPos + 1;
190
191     if (i <= readNum) m_hdoc.body().insertBefore(m_kokoyon, m_titleElm[ i ]);
192     else m_hdoc.body().appendChild(m_kokoyon);
193 }
194
195 /*
196  * private functions
197  */
198
199 /*
200  * append "A" Node to rootnode
201  */
202 void DomTree::appendAnchorNode(const DOM::Element& root, const QString& href, const QString& linkstr)
203 {
204     DOM::Element rootnode = root;
205     DOM::Element element;
206
207     element = rootnode.appendChild(m_hdoc.createElement("A"));
208     {
209         element.setAttribute("href", href);
210         element.appendChild(m_hdoc.createTextNode(linkstr));
211     }
212 }
213
214
215 /*
216  * update header
217  *
218  * example
219  * before: #KokomadeYonda 1- 101- 201- #ToSaigo<br><br>
220  * after : #KokomadeYonda 1- 101- 201- 301- 401- #ToSaigo<br><br>
221  */
222 void DomTree::updateHeader(DOM::Element& headerElement)
223 {
224     if (! m_datInfo) return ;
225
226     DOM::Element backupElement1, backupElement2, backupElement3;
227     int readNum = m_datInfo->getReadNum();
228
229     /* remove <a href="#tosaigo"> and <BR> */
230     backupElement1 = headerElement.removeChild(headerElement.lastChild()); /* BR */
231     backupElement2 = headerElement.removeChild(headerElement.lastChild()); /* BR */
232     backupElement3 = headerElement.removeChild(headerElement.lastChild()); /* "#tosaigo" */
233
234     DOM::Node node = headerElement.firstChild(); /* node is now "#kokomade_yonda" */
235     node = node.nextSibling(); /* " " */
236     node = node.nextSibling();
237
238     /* '1-', '101-' などのリンクを作成 */
239     for (int num = 1; num < readNum ; num += 100) {
240         if (node == 0) {
241             QString href = QString("#%1").arg(num);
242             QString linkStr = QString("%1-").arg(num);
243
244             appendAnchorNode(headerElement, href, linkStr);
245             node = headerElement.appendChild(m_hdoc.createTextNode(" "));
246             node = node.nextSibling();
247         } else {
248             // 既にリンクが作られている場合は飛ばす
249             node = node.nextSibling();
250             if (node != 0) node = node.nextSibling();
251         }
252     }
253
254     /* restore <a href="#tosaigo"> and <BR> */
255     headerElement.appendChild(backupElement3); /* "#tosaigo" */
256     headerElement.appendChild(backupElement2); /* BR */
257     headerElement.appendChild(backupElement1); /* BR */
258 }
259
260 /* 
261  * update footer
262  *
263  * example
264  * before: #KokomadeYonda 1- 101- 201- #ToSaigo
265  * after : #KokomadeYonda 1- 101- 201- 301- 401- #ToSaigo
266  */
267 void DomTree::updateFooter(DOM::Element& footerElement)
268 {
269     Q_ASSERT(m_datInfo != 0);
270
271     DOM::Element backupElement;
272     int readNum = m_datInfo->getReadNum();
273
274     backupElement = footerElement.removeChild(footerElement.lastChild()); /* "#tosaigo" */
275
276     DOM::Node node = footerElement.firstChild(); /* node is now "#kokomade_yonda" */
277     node = node.nextSibling(); /* " " */
278     node = node.nextSibling();
279
280     /* '1-', '101-' などのリンクを作成 */
281     for (int num = 1; num < readNum ; num += 100) {
282         if (node == 0) {
283             QString href = QString("#%1").arg(num);
284             QString linkStr = QString("%1-").arg(num);
285
286             appendAnchorNode(footerElement, href, linkStr);
287             node = footerElement.appendChild(m_hdoc.createTextNode(" "));
288             node = node.nextSibling();
289         } else {
290             // 既にリンクが作られている場合は飛ばす
291             node = node.nextSibling();
292             if (node != 0) node = node.nextSibling();
293         }
294     }
295
296     footerElement.appendChild(backupElement); /* "#tosaigo" */
297 }
298
299 /*
300  * create header node
301  */
302 void DomTree::createHeader()
303 {
304     QString str;
305     DOM::Element rootnode;
306
307     rootnode = m_hdoc.createElement("DIV");
308     {
309         rootnode.setAttribute("kita_type", "header");
310         rootnode.setAttribute("id", "header");
311
312         str = QString::fromUtf8(KITAUTF8_KOKOYON);
313         appendAnchorNode(rootnode, "#kokomade_yonda", str);
314         rootnode.appendChild(m_hdoc.createTextNode(" "));
315
316         str = QString::fromUtf8(KITAUTF8_SAIGO);
317         appendAnchorNode(rootnode, "#tosaigo", str);
318
319         rootnode.appendChild(m_hdoc.createElement("BR"));
320         rootnode.appendChild(m_hdoc.createElement("BR"));
321     }
322
323     m_header = rootnode;
324 }
325
326
327 /*
328  * create footer node
329  */
330 void DomTree::createFooter()
331 {
332     QString str;
333     DOM::Element rootnode;
334
335     rootnode = m_hdoc.createElement("DIV");
336     {
337         rootnode.setAttribute("kita_type", "footer");
338         rootnode.setAttribute("id", "footer");
339
340         str = QString::fromUtf8(KITAUTF8_KOKOYON);
341         appendAnchorNode(rootnode, "#kokomade_yonda", str);
342         rootnode.appendChild(m_hdoc.createTextNode(" "));
343
344         str = QString::fromUtf8(KITAUTF8_SAIGO);
345         appendAnchorNode(rootnode, "#tosaigo", str);
346     }
347
348     m_footer = rootnode;
349 }
350
351 /*
352  * create kokomadeyonda node
353  */
354 void DomTree::createKokoyon()
355 {
356     QString str, style;
357     DOM::Element rootnode;
358
359     str = QString::fromUtf8(KITAUTF8_KOKOYON2);
360
361     rootnode = m_hdoc.createElement("DIV");
362     {
363         rootnode.setAttribute("class", "kokoyon");
364         rootnode.setAttribute("kita_type", "kokoyon");
365         rootnode.setAttribute("id", "kokomade_yonda");
366         rootnode.appendChild(m_hdoc.createTextNode(str));
367     }
368
369     m_kokoyon = rootnode;
370 }
371
372 /*
373  * change color of number
374  * specify color like this:  "a.coloredLink:link{ color: red; }"
375  */
376 void DomTree::changeColorOfNumber(int num)
377 {
378     if (m_coloredNum[ num ]) return ;
379
380     m_coloredNum[ num ] = true;
381
382     DOM::Node node = m_titleElm[ num ];
383     node = node.firstChild();
384
385     static_cast<DOM::HTMLElement>(node).setAttribute("class", "coloredLink");
386 }