OSDN Git Service

Move the directories
[kita/kita.git] / src / htmlpart.cpp
1 /***************************************************************************
2 *   Copyright (C) 2004 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 #include "htmlpart.h"
12
13 #include <QtCore/QRegExp>
14 #include <QtGui/QApplication>
15 #include <QtGui/QClipboard>
16 #include <QtGui/QCursor>
17 #include <QtGui/QScrollBar>
18 #include <QtGui/QStyle>
19 #include <QtGui/QStyleOptionComplex>
20
21 #include <kaction.h>
22 #include <khtml_events.h>
23 #include <kmenu.h>
24 #include <kmessagebox.h>
25 #include <krun.h>
26
27 #include "const.h"
28 #include "domtree.h"
29 #include "respopup.h"
30 #include "viewmediator.h"
31 #include "kitaui/htmlview.h"
32 #include "libkita/abone.h"
33 #include "libkita/boarddatabase.h"
34 #include "libkita/colorconfig.h"
35 #include "libkita/datmanager.h"
36 #include "libkita/globalconfig.h"
37 #include "libkita/kita_misc.h"
38
39 using namespace Kita;
40 /*-------------------------------------------*/
41
42
43 /*-------------------------------------*/
44 /* Don't forget to call setup() later. */
45
46 HTMLPart::HTMLPart(QWidget* parent) : KHTMLPart(new HTMLView(this, parent))
47 {
48     m_mode = HTMLPART_MODE_MAINPART;
49     m_popup = 0;
50     m_domtree = 0;
51     m_datUrl.clear();
52     m_updatedKokoyon = false;
53
54     clearPart();
55     createHTMLDocument();
56     connectSignals();
57 }
58
59
60 HTMLPart::~HTMLPart()
61 {
62     clearPart();
63 }
64
65
66 /* private */
67 void HTMLPart::clearPart()
68 {
69     slotDeletePopup();
70
71     /* delete DomTree */
72     delete m_domtree;
73     m_domtree = 0;
74
75     DatManager datManager(m_datUrl);
76     /* update ViewPos */
77     if (m_mode == HTMLPART_MODE_MAINPART && !m_updatedKokoyon
78             && !m_datUrl.isEmpty()) {
79         int readNum = datManager.getReadNum();
80         if (readNum) {
81             datManager.setViewPos(readNum);
82         }
83     }
84     m_updatedKokoyon = false;
85
86     /* clear variables */
87     m_anchorStack.clear();
88     m_centerNum = 0;
89     m_jumpNumAfterLoading = 0;
90     findTextInit();
91
92     if (!m_datUrl.isEmpty()) {  /* This part is opened. */
93         /* This part is on the main thread view. */
94         if (m_mode == HTMLPART_MODE_MAINPART) {
95
96             /* tell Thread class that "thread is closed" */
97             datManager.setMainThreadOpened(false);
98
99             /* emit "deactivated all thread view" SIGNAL */
100             KUrl nullUrl("");
101             ViewMediator::getInstance()->changeWriteTab(nullUrl);
102
103             /*  update subject tab. */
104         }
105     }
106
107     m_datUrl.clear();
108     m_mode = HTMLPART_MODE_MAINPART;
109 }
110
111
112
113 /* public */
114 bool HTMLPart::setup(int mode, const KUrl& url)
115 {
116     Q_ASSERT(!url.isEmpty());
117
118     clearPart();
119
120     m_datUrl = getDatUrl(url);
121     m_mode = mode;
122
123     if (m_mode == HTMLPART_MODE_MAINPART) {
124         /* This part is on the main thread view. */
125         DatManager datManager(m_datUrl);
126         /* create DatInfo explicitly to open new thread.   */
127         /* Usually, DatInfo is NOT created if ReadNum == 0.*/
128         /* See also DatManager::createDatInfo() and        */
129         /*          DatManager::getDatInfo().              */
130         datManager.createDatInfo();
131
132         /* tell Thread class that "thread is opened" */
133         datManager.setMainThreadOpened(true);
134
135         /* reset abone */
136         datManager.resetAbone();
137     }
138
139     /* create HTML Document */
140     createHTMLDocument();
141
142     /* create DOM manager */
143     if (m_mode == HTMLPART_MODE_MAINPART) {
144         m_domtree = new DomTree(htmlDocument(), m_datUrl);
145     }
146
147     return true;
148 }
149
150
151 /* private */
152 void HTMLPart::connectSignals()
153 {
154     /* popup */
155     connect(this, SIGNAL(onURL(const QString&)), SLOT(slotOnUrl(const QString&)));
156
157     connect(view(), SIGNAL(leave()), SLOT(slotLeave()));
158     connect(view()->verticalScrollBar(), SIGNAL(sliderReleased()), SLOT(slotVSliderReleased()));
159     connect(view()->horizontalScrollBar(), SIGNAL(sliderReleased()), SLOT(slotHSliderReleased()));
160 }
161
162
163
164 /* private */
165 void HTMLPart::createHTMLDocument()
166 {
167     /* style */
168     QString style = QString("body { font-size: %1pt; font-family: \"%2\"; color: %3; background-color: %4; }")
169                     .arg(GlobalConfig::threadFont().pointSize())
170                     .arg(GlobalConfig::threadFont().family())
171                     .arg(ColorConfig::thread().name())
172                     .arg(ColorConfig::threadBackground().name());
173
174     QString text = "<html><head><style>";
175     text += DEFAULT_STYLESHEET;
176     text += style;
177     text += "</style></head><body></body></html>";
178
179     setJScriptEnabled(false);
180     setJavaEnabled(false);
181
182     /* Use dummy URL here, and protocol should be "file:".
183        If protocol is "http:", local image files are not shown
184        (for security reasons ?).
185      */
186     begin(KUrl("file:/dummy.htm"));
187     write(text);
188     end();
189 }
190
191 /*---------------------------------------------------------------*/
192 /*---------------------------------------------------------------*/
193 /* direct rendering functions */
194
195 /**
196  * show responses.
197  * @warning don't forget to call updateScreen() later.
198  */
199 void HTMLPart::showResponses(int startnum, int endnum)
200 {
201     if (!m_domtree) return ;
202
203     for (int i = startnum ; i <= endnum; i++) m_domtree->appendRes(i);
204 }
205
206
207 /* do parsing only.            */
208 /* call showResponses() later  */ /* public */
209 void HTMLPart::parseResponses(int startnum, int endnum)
210 {
211     if (!m_domtree) return ;
212
213     for (int i = startnum ; i <= endnum; i++) m_domtree->createResElement(i);
214 }
215
216
217 /*------------------------------------------------*/
218 /* Show all responses ,header, footer, and etc,
219    if these are not shown.                        */
220
221 /* note that updateScreen() is called internally. */
222 /* So, you need not call it later.                */  /* public slot */
223 void HTMLPart::showAll()
224 {
225     if (!m_domtree) return ;
226
227     int bottom = m_domtree->getBottomResNumber();
228     int readNum = DatManager(m_datUrl).getReadNum();
229     if (bottom != readNum) {
230
231         QCursor qc; qc.setShape(Qt::WaitCursor);
232         QApplication::setOverrideCursor(qc);
233
234         showResponses(1, readNum);
235         updateScreen(true, false);
236
237         QApplication::restoreOverrideCursor();
238     }
239 }
240
241 /*
242  * update screen
243  */
244 void HTMLPart::updateScreen(bool showHeaderEtc, bool clock)
245 {
246     if (!m_domtree) {
247         view()->setFocus();
248         return ;
249     }
250
251     /* show clock cursor */
252     if (clock) {
253         QCursor qc; qc.setShape(Qt::WaitCursor);
254         QApplication::setOverrideCursor(qc);
255     }
256
257     /* show header, footer, and kokomadeyonda, etc. */
258     if (showHeaderEtc) {
259         m_domtree->appendKokoyon();
260         m_domtree->appendFooterAndHeader();
261     }
262
263     /* change color of number of the res which is responsed. */
264     if (m_mode == HTMLPART_MODE_MAINPART) {
265
266         if (GlobalConfig::checkResponsed()) {
267             m_domtree->changeColorOfAllResponsedNumber();
268         }
269     }
270
271     /* update display */
272     htmlDocument().applyChanges();
273     view()->layout();
274     view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
275     view()->setFocus();
276
277     /* restore cursor */
278     if (clock) {
279         QApplication::restoreOverrideCursor();
280     }
281 }
282
283
284 /* public */
285 void HTMLPart::setInnerHTML(const QString& innerHTML)
286 {
287     createHTMLDocument();
288     htmlDocument().body().setInnerHTML(innerHTML);
289 }
290
291
292
293 /* redraw screen  */
294 void HTMLPart::redrawHTMLPart(const KUrl& datUrl, bool force)
295 {
296     if (m_domtree == 0) return ;
297     if (m_datUrl != datUrl) return ;
298
299     m_domtree->redraw(force);
300 }
301
302 /* public slot */
303 void HTMLPart::slotSetFaceOfHTMLPart()
304 {
305     QFont font = GlobalConfig::threadFont();
306
307     DOM::CSSStyleDeclaration style = htmlDocument().body().style();
308     style.setProperty("font-family", font.family(), "");
309     style.setProperty("font-size", QString("%1pt").arg(font.pointSize()), "");
310     style.setProperty("color", ColorConfig::thread().name(), "");
311     style.setProperty("background-color",
312             ColorConfig::threadBackground().name(), "");
313     htmlDocument().applyChanges();
314 }
315
316 /*---------------------------------------------------------------*/
317 /*---------------------------------------------------------------*/
318 /* cache functions */
319
320
321 /*----------------------------------*/
322 /* load thread from cache & show it */ /* public */
323
324 /*  top = centerNum - preShowNum
325     bottom = centerNum + afterShowNum
326     readNum = DatManager::getReadNum
327  
328     No.1 <- show -> No.20 <- not show -> No.(top) <- show -> No.(bottom) <- not show -> No.(readNum) */
329
330 bool HTMLPart::load(int centerNum)
331 {
332     m_centerNum = centerNum;
333     m_jumpNumAfterLoading = 0;
334
335     if (m_mode != HTMLPART_MODE_MAINPART)
336         return false;
337     if (!m_domtree)
338         return false;
339     DatManager datManager(m_datUrl);
340     if (datManager.getReadNum() == 0)
341         return false;
342
343     int endNum = datManager.getReadNum();
344     showResponses(1, endNum);
345     updateScreen(true , false);
346     gotoAnchor(QString::number(m_centerNum), false);
347     view() ->setFocus();
348
349     return true;
350 }
351
352
353
354 /*----------------------------*/
355 /* start reloading            */
356
357 /* see also slotReceiveData()
358    and slotFinishLoad().      */ /* public */
359 bool HTMLPart::reload(int jumpNum)
360 {
361     if (!m_domtree) return false;
362     if (m_mode != HTMLPART_MODE_MAINPART) {
363         /* If this is not MainPart, then open MainPart.  */
364         ViewMediator::getInstance()->openUrl(m_datUrl);
365         return false;
366     }
367
368     m_firstReceive = true;
369     if (m_centerNum == 0) m_centerNum = m_domtree->getBottomResNumber();
370     m_jumpNumAfterLoading = jumpNum;
371
372     /* DatManager will call back slotReceiveData and slotFinishLoad. */
373     DatManager(m_datUrl).updateCache(this);
374     view() ->setFocus();
375
376     return true;
377 }
378
379
380
381 /*---------------------------------------------*/
382 /* This slot is called after DatManager
383    received new data, and emits receiveData()  */ /* !!! "public" slot !!! */
384 void HTMLPart::slotReceiveData()
385 {
386     const int delta = 20;
387
388     if (m_mode != HTMLPART_MODE_MAINPART) return ;
389     if (!m_domtree) return ;
390
391     int readNum = DatManager(m_datUrl).getReadNum();
392     int bottom = m_domtree->getBottomResNumber();
393
394     /* parsing */
395     parseResponses(bottom + 1, readNum);
396
397     /* rendering */
398     if (m_firstReceive || bottom + delta < readNum) {
399         showResponses(bottom + 1, readNum);
400         updateScreen(true, false);
401     }
402
403     if (m_firstReceive && m_centerNum < readNum) {
404         gotoAnchor(QString::number(m_centerNum), false);
405         m_firstReceive = false;
406     }
407
408     emit receiveData();
409 }
410
411
412 /*--------------------------------------------*/
413 /* This slot is called after DatManager
414    finished updating new data,
415    and emits finishReload()                   */ /* !!! "public" slot !!! */
416 void HTMLPart::slotFinishLoad()
417 {
418     if (m_mode != HTMLPART_MODE_MAINPART) return ;
419     if (!m_domtree) return ;
420
421     int bottom = m_domtree->getBottomResNumber();
422     int shownNum = m_centerNum + 5000;
423
424     showResponses(bottom + 1, shownNum);
425     updateScreen(true, false);
426     //    m_domtree->parseAllRes();
427     m_centerNum = 0;
428
429     if (m_jumpNumAfterLoading)
430         gotoAnchor(QString::number(m_jumpNumAfterLoading), false);
431     m_jumpNumAfterLoading = 0;
432
433     emit finishReload();
434 }
435
436
437
438
439 /*---------------------------------------------------------------*/
440 /*---------------------------------------------------------------*/
441 /* goto anchor */
442
443
444 /* public */
445 bool HTMLPart::gotoAnchor(const QString& anc, bool pushPosition)
446 {
447     if (anc.isEmpty()) return false;
448     if (!m_domtree || m_mode == HTMLPART_MODE_POPUP)
449         return KHTMLPart::gotoAnchor(anc);
450
451     hidePopup();
452
453     QString ancstr = anc;
454     int res = ancstr.toInt();
455
456     if (res > 1) {
457
458         /* is target valid ? */
459         if (!DatManager(m_datUrl).isResValid(res))
460             return false;
461
462         ancstr = QString::number(res);
463     }
464
465     if (res == 1) ancstr = "header";
466     if (pushPosition) pushCurrentPosition();
467
468     /* KHTMLPart::gotoAnchor() will fail if the thread is not shown. */
469     /* So KHTMLPart::gotoAnchor() should be called via custom event. */
470     /* See also HTMLPart::customEvent() */
471     GotoAnchorEvent * e = new GotoAnchorEvent(ancstr);
472     QApplication::postEvent(this, e);  // Qt will delete it when done
473
474     return true;
475 }
476
477
478
479 /* jump to kokomade yonda */ /* public slot */
480 void HTMLPart::slotGotoKokoyon()
481 {
482     if (!m_domtree) return ;
483     if (m_mode != HTMLPART_MODE_MAINPART) return ;
484
485     int kokoyon = DatManager(m_datUrl).getViewPos();
486     gotoAnchor(QString::number(kokoyon), false);
487 }
488
489
490
491 /* public slot  */
492 void HTMLPart::slotGobackAnchor()
493 {
494     if (m_anchorStack.isEmpty()) return ;
495
496     QString anc = m_anchorStack.last();
497     m_anchorStack.pop_back();
498     gotoAnchor(anc , false);
499 }
500
501
502
503
504 /* private */
505 void HTMLPart::pushCurrentPosition()
506 {
507     m_anchorStack += getCurrentIdOfNode();
508 }
509
510
511 /* find the id of current node */ /* private */
512 QString HTMLPart::getCurrentIdOfNode()
513 {
514     DOM::Node node;
515     node = nodeUnderMouse();
516     while (node != 0 && node.nodeName().string() != "div") node = node.parentNode();
517     if (node == 0) return QString();
518
519     return static_cast<DOM::Element>(node).getAttribute("id").string();
520 }
521
522 /* public slot */
523 void HTMLPart::slotClickGotoFooter()
524 {
525     if (!m_domtree || m_mode != HTMLPART_MODE_MAINPART) {
526         gotoAnchor("footer", false);
527         return ;
528     }
529
530     int bottom = m_domtree->getBottomResNumber();
531     int readNum = DatManager(m_datUrl).getReadNum();
532
533     if (readNum != bottom) {
534         showResponses(bottom + 1, readNum);
535         updateScreen(true, true);
536     }
537
538     gotoAnchor("footer", false);
539 }
540
541
542 /*---------------------------------------------------------------*/
543 /*---------------------------------------------------------------*/
544 /* search */
545
546
547 /* private */
548 void HTMLPart::findTextInit()
549 {
550     m_findNode = 0;
551     m_findPos = -1;
552     m_find_y = 0;
553 }
554
555
556
557 /* public */
558 bool HTMLPart::findText(const QString &query, bool reverse)
559 {
560     if (m_mode != HTMLPART_MODE_MAINPART) return false;
561
562     QRegExp regexp(query);
563     regexp.setCaseSensitivity(Qt::CaseInsensitive);
564
565     /* init */
566     if (m_findNode.isNull()) {
567
568         m_findNode = htmlDocument().body();
569         m_find_y = 0;
570
571         /* move to the last child node */
572         if (reverse) {
573             while (!m_findNode.lastChild().isNull()) m_findNode = m_findNode.lastChild();
574             m_find_y = view() ->contentsHeight();
575         }
576     }
577
578     while (1) {
579
580         if (m_findNode.nodeType() == DOM::Node::TEXT_NODE
581                 || m_findNode.nodeType() == DOM::Node::CDATA_SECTION_NODE) {
582
583             /* find the word in the current node */
584             DOM::DOMString nodeText = m_findNode.nodeValue();
585             QString nodestr = nodeText.string();
586             if (reverse && m_findPos != -1) nodestr.resize(m_findPos);
587
588             if (reverse) m_findPos = nodestr.lastIndexOf(regexp, m_findPos);
589             else m_findPos = nodestr.indexOf(regexp, m_findPos + 1);
590
591             /* scroll & select & return */
592             if (m_findPos != -1) {
593                 int matchLen = regexp.matchedLength();
594
595                 QRect qr = m_findNode.getRect();
596                 view() ->setContentsPos(qr.left() - 50, m_find_y - 100);
597                 DOM::Range rg(m_findNode, m_findPos, m_findNode, m_findPos + matchLen);
598                 setSelection(rg);
599
600                 return true;
601             }
602
603         } else if (m_findNode.nodeName().string() == "table") {
604
605             QRect qr = m_findNode.getRect();
606
607             m_find_y = qr.bottom();
608
609         } else if (m_findNode.nodeName().string() == "div") {
610
611             QRect qr = m_findNode.getRect();
612
613             if (reverse) {
614                 m_find_y = qr.bottom();
615             } else {
616                 m_find_y = qr.top();
617             }
618
619         } else if (m_findNode.nodeName().string() == "br") {
620
621             DOM::Node tmpnode = m_findNode.previousSibling();
622
623             if (tmpnode != 0) {
624
625                 QRect qr = tmpnode.getRect();
626                 if (reverse) m_find_y -= qr.bottom() - qr.top();
627                 else m_find_y += qr.bottom() - qr.top();
628             }
629         }
630
631         /*------------------------*/
632
633         m_findPos = -1;
634         DOM::Node next;
635
636         /* move to the next node */
637         if (!reverse) {
638
639             next = m_findNode.firstChild();
640             if (next.isNull()) next = m_findNode.nextSibling();
641
642             while (!m_findNode.isNull() && next.isNull()) {
643                 m_findNode = m_findNode.parentNode();
644                 if (!m_findNode.isNull()) {
645                     next = m_findNode.nextSibling();
646                 }
647             }
648         }
649         /* revearse */
650         else {
651
652             next = m_findNode.lastChild();
653             if (next.isNull()) next = m_findNode.previousSibling();
654
655             while (!m_findNode.isNull() && next.isNull()) {
656                 m_findNode = m_findNode.parentNode();
657                 if (!m_findNode.isNull()) {
658                     next = m_findNode.previousSibling();
659                 }
660             }
661         }
662
663         m_findNode = next;
664         if (m_findNode.isNull()) {
665             m_findNode = 0;
666             return false;
667         }
668     }
669
670     return false;
671 }
672
673
674
675
676
677
678 /*---------------------------------------------------------------*/
679 /*---------------------------------------------------------------*/
680 /* popup menu */
681
682
683 /* private */
684 void HTMLPart::showPopupMenu(const KUrl& kurl)
685 {
686     QString url = kurl.prettyUrl();
687     bool showppm = false;
688
689     QString str;
690
691     /* If selected Text is composed of only digits, then show res popup. */
692     if (!m_pushctrl && showSelectedDigitPopup()) return ;
693
694     /*-----------------------------------*/
695     /* create menu items                 */
696
697     KMenu popupMenu(view());
698     KMenu* backSubMenu = 0;
699     KMenu* markSubMenu = 0;
700
701     /*------*/
702     /* jump */
703     KAction* homeLinkAct = 0;
704     KAction* kokoLinkAct = 0;
705     KAction* endLinkAct = 0;
706     if (m_domtree &&
707             (m_mode == HTMLPART_MODE_MAINPART)) {
708
709         showppm = true;
710
711         DatManager datManager(m_datUrl);
712         // back
713         if (!m_anchorStack.isEmpty()) {
714             backSubMenu = new KMenu(view());
715
716             int i = m_anchorStack.size();
717             QStringList::iterator it;
718             for (it = m_anchorStack.begin(); it != m_anchorStack.end(); it++, i--) {
719                 str = (*it) + "   " + datManager.getPlainBody((*it).toInt()).left(10);
720                 KAction* backLink = new KAction(str, this);
721                 backSubMenu->addAction(backLink);
722             }
723             backSubMenu->setTitle(
724                     i18nc("@title:menu Go back to the responses", "Back"));
725             popupMenu.addMenu(backSubMenu);
726             popupMenu.addSeparator();
727         }
728
729         // mark
730         int readNum = datManager.getReadNum();
731         for (int i = 1; i <= readNum ; i++) {
732             if (datManager.isMarked(i)) {
733                 if (!markSubMenu) {
734                     markSubMenu = new KMenu(view());
735                     markSubMenu->setTitle(i18nc("@title:menu", "Mark"));
736                     popupMenu.addMenu(markSubMenu);
737                     popupMenu.addSeparator();
738                 }
739                 str = QString::number(i) + "   "
740                     + datManager.getPlainBody(i).left(10);
741                 KAction *gotoMarkAct = new KAction(str, this);
742                 markSubMenu->addAction(gotoMarkAct);
743             }
744         }
745
746         // home
747         homeLinkAct = new KAction(
748                 i18nc("@action:inmenu Go to the original post", "Start"), this);
749         popupMenu.addAction(homeLinkAct);
750
751         // template
752         if (m_mode == HTMLPART_MODE_MAINPART) {
753             int kokoyon = datManager.getViewPos();
754             if (kokoyon) {
755                 str = i18nc("@action:inmenu", "Kokomade Yonda (%1)", kokoyon);
756                 kokoLinkAct = new KAction(str, this);
757                 popupMenu.addAction(kokoLinkAct);
758             }
759         }
760         // end 
761         endLinkAct = new KAction(i18nc("@action:inmenu", "End"), this);
762         popupMenu.addAction(endLinkAct);
763     }
764
765     // copy & abone
766     KAction* copyStrAct = 0;
767     KAction* aboneWordAct = 0;
768     if (hasSelection()) {
769         if (showppm) popupMenu.addSeparator();
770         showppm = true;
771
772         copyStrAct = new KAction(i18nc("@action:inmenu", "Copy"), this);
773         popupMenu.addAction(copyStrAct);
774
775         // truncated
776         QString selectedStr = selectedText();
777         if (selectedStr.length() > 20) {
778             selectedStr.truncate(20);
779             selectedStr.append("...");
780         }
781
782         aboneWordAct = new KAction(i18nc("@action:inmenu",
783                     "Add '%1' to abone list", selectedStr), this);
784         popupMenu.addAction(aboneWordAct);
785     }
786
787
788     // copy link
789     KAction* openBrowserAct = 0;
790     KAction* copyLinkAct = 0;
791     if (!url.isEmpty()) {
792         if (showppm) popupMenu.addSeparator();
793         showppm = true;
794
795         openBrowserAct= new KAction(i18nc("@action:inmenu",
796                     "Open with Web Browser"), this);
797         popupMenu.addAction(openBrowserAct);
798         popupMenu.addSeparator();
799
800         copyLinkAct = new KAction(i18nc("@action:inmenu",
801                     "Copy Link Location"), this);
802         popupMenu.addAction(copyLinkAct);
803     }
804
805
806     // show menu
807     if (showppm) {
808
809         QClipboard * clipboard = QApplication::clipboard();
810
811         QAction* action = popupMenu.exec(QCursor::pos());
812         if (!action) {
813             delete backSubMenu;
814             delete markSubMenu;
815             return;
816         }
817         if (action == copyLinkAct) {
818             clipboard->setText(url , QClipboard::Clipboard);
819             clipboard->setText(url , QClipboard::Selection);
820         } else if (action == openBrowserAct) {
821             KRun::runUrl(kurl, "text/html", view());
822         } else if (action == homeLinkAct) {
823             gotoAnchor("header", false);
824         } else if (action == kokoLinkAct) {
825             slotGotoKokoyon();
826         } else if (action == endLinkAct) {
827             slotClickGotoFooter();
828         } else if (action == copyStrAct) {
829             clipboard->setText(selectedText(), QClipboard::Clipboard);
830         } else if (action == aboneWordAct) {
831             if (KMessageBox::warningYesNo(view(),
832                     i18n("Do you want to add '%1' to abone list?",
833                                selectedText())) == KMessageBox::Yes) {
834                 AboneConfig::aboneWordList().append(selectedText());
835                 redrawHTMLPart(m_datUrl, false);
836             }
837         } else {
838             QMenu* menu = action->menu();
839             if (!menu) {
840                 delete backSubMenu;
841                 delete markSubMenu;
842                 return;
843             }
844             if (menu == markSubMenu) {
845                 gotoAnchor(QString::number(menu->actions().indexOf(action)),
846                         false);
847             } else if (menu == backSubMenu) {
848                 for (int i = 0; i < menu->actions().indexOf(action); i++)
849                     m_anchorStack.pop_back();
850                 slotGobackAnchor();
851             }
852         }
853     }
854     delete backSubMenu;
855     delete markSubMenu;
856 }
857
858 /*---------------------------------------------------------------*/
859 /*---------------------------------------------------------------*/
860 /* user event */
861
862
863 /* protected */ /* virtual */
864 void HTMLPart::customEvent(QEvent * e)
865 {
866     if (e->type() == EVENT_GotoAnchor) {
867         KHTMLPart::gotoAnchor(static_cast< GotoAnchorEvent* >(e) ->getAnc());
868         return ;
869     }
870
871     KHTMLPart::customEvent(e);
872 }
873
874
875 /*---------------------------------------------------------------*/
876 /*---------------------------------------------------------------*/
877 /* mouse event */
878
879
880 /* protected */
881 void HTMLPart::khtmlMousePressEvent(khtml::MousePressEvent* e)
882 {
883     emit mousePressed(); /* to ThreadView to focus this view. */
884
885     KUrl kurl;
886     if (!e->url().string().isEmpty()) {
887         BoardDatabase db(m_datUrl);
888         kurl = KUrl(db.boardUrl(), e->url().string());
889     }
890
891     m_pushctrl = m_pushmidbt = m_pushrightbt = false;
892     if (e->qmouseEvent() ->button() & Qt::RightButton) m_pushrightbt = true;
893     if (e->qmouseEvent() ->modifiers() & Qt::ControlModifier) m_pushctrl = true;
894     if (e->qmouseEvent() ->button() & Qt::MidButton) m_pushmidbt = true;
895
896     if (e->url() != 0) {
897
898         if (e->url().string().at(0) == '#') { /* anchor */
899             kurl = m_datUrl;
900             kurl.setRef(e->url().string().mid(1)) ;
901         }
902
903         clickAnchor(kurl);
904         m_pushctrl = m_pushmidbt = m_pushrightbt = false;
905         return ;
906     }
907
908     /* popup menu */
909     if (m_pushrightbt) {
910         showPopupMenu(kurl);
911         m_pushctrl = m_pushmidbt = m_pushrightbt = false;
912         return ;
913     }
914
915     KHTMLPart::khtmlMousePressEvent(e);
916 }
917
918
919
920
921
922 /*-------------------------------------------------------*/
923 /*-------------------------------------------------------*/
924 /* click */
925
926
927
928 /* private slot */
929 void HTMLPart::slotOpenUrlRequest(const KUrl& urlin, const KParts::OpenUrlArguments&)
930 {
931     clickAnchor(urlin);
932 }
933
934
935 /*------------------------------------------------------*/
936 /* This function is called when user clicked res anchor */ /* private */
937 void HTMLPart::clickAnchor(const KUrl& urlin)
938 {
939     QString refstr;
940     KUrl datUrl = getDatUrl(urlin , refstr);
941
942     /*--------------------*/
943     /* Ctrl + right click */
944     if (m_pushctrl && m_pushrightbt) {
945         showPopupMenu(urlin);
946         return ;
947     }
948
949     /*--------------------------------*/
950     /* If this is not anchor, then    */
951     /* emit openURLRequest and return */
952
953     if (datUrl.host() != m_datUrl.host() || datUrl.path() != m_datUrl.path()) {
954
955         /* right click */
956         if (m_pushrightbt) {
957
958             /* start multi-popup mode or show popup menu */
959             if (!startMultiPopup()) showPopupMenu(urlin);
960
961             return ;
962         }
963         /* right click */
964
965         ViewMediator::getInstance()->openUrl(urlin);
966         return ;
967     }
968
969     if (refstr.isEmpty()) return ;
970
971     /*---------------------------*/
972     /* show popupmenu for #write */
973
974     if (refstr.left(5) == "write") {
975         showWritePopupMenu(refstr);
976         return ;
977     }
978
979     /*----------------------------*/
980     /* extract responses by ID    */
981
982     if (refstr.left(5) == "idpop") {
983         showIdPopup(refstr);
984         return ;
985     }
986
987     /*---------------------------*/
988     /* show popupmenu for #bepop */
989
990     if (refstr.left(5) == "bepop") {
991         showBePopupMenu(refstr);
992         return ;
993     }
994
995     /*-------------------------*/
996     /* start multi-popup mdde  */
997     if (m_pushrightbt && startMultiPopup()) return ;
998
999
1000     /*----------------------------*/
1001     /* next 100 ,before 100 ,etc. */
1002     if (m_mode == HTMLPART_MODE_MAINPART) {
1003         if (refstr.left(7) == "tosaigo") {
1004             slotClickGotoFooter();
1005             return;
1006         }
1007     }
1008
1009     /*-------------------------------*/
1010     /* open Kita Navi or goto anchor */
1011
1012     int refNum, refNum2;
1013
1014     int i = refstr.indexOf("-");
1015     if (i != -1) {
1016         refNum = refstr.left(i).toInt();
1017         refNum2 = refstr.mid(i + 1).toInt();
1018         if (refNum2 < refNum) {
1019             refNum2 = refNum;
1020         }
1021     } else {
1022         refNum = refNum2 = refstr.toInt();
1023     }
1024
1025     if (!refNum) return ;
1026
1027     if (m_mode == HTMLPART_MODE_POPUP) {
1028         ViewMediator::getInstance()->openUrl(urlin);
1029     } else {
1030         gotoAnchor(QString::number(refNum), true);
1031     }
1032 }
1033
1034
1035
1036 /*---------------------------------------------------------*/
1037 /* popup menu that is opened when user clicked res number. */
1038 /* This funtcion is called in only clickAnchor().          */ /* private */
1039 void HTMLPart::showWritePopupMenu(const QString& refstr)
1040 {
1041     QClipboard * clipboard = QApplication::clipboard();
1042     QString str, resstr;
1043     int resNum = refstr.mid(5).toInt();
1044     DatManager datManager(m_datUrl);
1045     QString namestr = datManager.getPlainName(resNum);
1046
1047     /* show res tree */
1048     if (m_pushrightbt) {
1049         int num;
1050         QString htmlstr = datManager.getTreeByRes(resNum, num);
1051         if (!num) return ;
1052         QString tmpstr = QString("<DIV>No.%1 : [%2]<BR>").arg(resNum).arg(num);
1053         tmpstr += htmlstr + "<BR><BR></DIV>";
1054         showPopup(m_datUrl, tmpstr);
1055         startMultiPopup();
1056         return ;
1057     }
1058
1059     /*---------------------*/
1060     /* create popup menu */
1061     QString plainStr;
1062
1063     KMenu popupMenu(view());
1064
1065     KAction* resAct = 0;
1066     KAction* quoteAct = 0;
1067     if (m_mode == HTMLPART_MODE_MAINPART) {
1068         resAct = new KAction(i18nc("@action:inmenu", "Write Response"), this);
1069         popupMenu.addAction(resAct);
1070         
1071         quoteAct = new KAction(i18nc("@action:inmenu", "Quote This"), this);
1072         popupMenu.addAction(quoteAct);
1073
1074         popupMenu.addSeparator();
1075     }
1076
1077     // mark
1078     KAction* markAct = new KAction(i18nc("@action:inmenu", "Mark"), this);
1079     markAct->setCheckable(true);
1080     markAct->setChecked(datManager.isMarked(resNum));
1081     popupMenu.addAction(markAct);
1082
1083     popupMenu.addSeparator();
1084
1085     // open
1086     KAction* showBrowserAct
1087         = new KAction(i18nc("@action:inmenu", "Open with Web Browser"), this);
1088     popupMenu.addAction(showBrowserAct);
1089
1090     popupMenu.addSeparator();
1091
1092     // util
1093     KAction* resTreeAct
1094         = new KAction(i18nc("@action:inmenu", "Res Tree"), this);
1095     popupMenu.addAction(resTreeAct);
1096
1097     KAction* reverseResTreeAct
1098         = new KAction(i18nc("@action:inmenu", "Reverse Res Tree"), this);
1099     popupMenu.addAction(reverseResTreeAct);
1100
1101     KAction* extractNameAct
1102         = new KAction(i18nc("@action:inmenu", "Extract by Name"), this);
1103     popupMenu.addAction(extractNameAct);
1104
1105     popupMenu.addSeparator();
1106
1107     // copy
1108     KAction* copyUrlAct
1109         = new KAction(i18nc("@action:inmenu", "Copy URL"), this);
1110     popupMenu.addAction(copyUrlAct);
1111
1112     KAction* copyThreadNameAct
1113         = new KAction(i18nc("@action:inmenu", "Copy Title and URL"), this);
1114     popupMenu.addAction(copyThreadNameAct);
1115
1116     KAction* copyAct = new KAction(i18nc("@action:inmenu", "Copy"), this);
1117     popupMenu.addAction(copyAct);
1118
1119     // kokkoma de yonda
1120     KAction* setKokoYonAct = 0;
1121     if (m_domtree && m_mode == HTMLPART_MODE_MAINPART) {
1122         popupMenu.addSeparator();
1123
1124         setKokoYonAct
1125             = new KAction(i18nc("@action:inmenu", "Set Kokomade Yonda"), this);
1126         popupMenu.addAction(setKokoYonAct);
1127     }
1128
1129     // abone
1130     popupMenu.addSeparator();
1131
1132     KAction* aboneNameAct
1133         = new KAction(i18nc("@action:inmenu", "Add Name to Abone List"), this);
1134     popupMenu.addAction(aboneNameAct);
1135
1136
1137     // show popup menu
1138     QAction* action = popupMenu.exec(QCursor::pos());
1139     if (!action) {
1140         return;
1141     }
1142     if (action == resAct) {
1143         resstr = ">>" + QString::number(resNum) + '\n';
1144         ViewMediator::getInstance()->showWriteView(m_datUrl, resstr);
1145     } else if (action == quoteAct) {
1146         resstr = ">>" + QString::number(resNum) + '\n'
1147                  + "> " + datManager.getPlainTitle(resNum) + '\n'
1148                  + "> " + datManager.getPlainBody(resNum).replace('\n', "\n> ") + '\n';
1149         ViewMediator::getInstance()->showWriteView(m_datUrl, resstr);
1150     } else if (action == markAct) {
1151         datManager.setMark(resNum, !datManager.isMarked(resNum));
1152     } else if (action == copyAct || action == copyUrlAct || action == copyThreadNameAct) {
1153         str.clear();
1154
1155         // title 
1156         if (action == copyThreadNameAct || action == copyAct) {
1157             str = datManager.threadName();
1158         }
1159
1160         // url
1161         if (!str.isEmpty()) str += '\n';
1162         str += datManager.threadUrl() + '/' + QString::number(resNum)
1163             + '\n';
1164
1165         // body
1166         if (action == copyAct) {
1167             str += '\n'
1168                    + datManager.getPlainTitle(resNum) + '\n'
1169                    + datManager.getPlainBody(resNum) + '\n';
1170         }
1171
1172         // copy
1173         clipboard->setText(str , QClipboard::Clipboard);
1174         clipboard->setText(str , QClipboard::Selection);
1175
1176     } else if (action == setKokoYonAct) {
1177         datManager.setViewPos(resNum);
1178         ViewMediator::getInstance()->updateBoardView(m_datUrl);
1179         m_updatedKokoyon = true;
1180         updateScreen(true, true);
1181         gotoAnchor(QString::number(resNum), false);
1182
1183     } else if (action == showBrowserAct) {
1184         str = datManager.threadUrl() + '/' + QString::number(resNum);
1185
1186         KRun::runUrl(str, "text/html", view());
1187     } else if (action == aboneNameAct) {
1188         if (KMessageBox::warningYesNo(view(),
1189                 i18n("Do you want to add '%1' to abone list?", namestr),
1190                 "Kita") == KMessageBox::Yes) {
1191             AboneConfig::aboneNameList().append(namestr);
1192             redrawHTMLPart(m_datUrl, false);
1193         }
1194     }
1195 }
1196
1197
1198
1199 /*--------------------------------------------------*/
1200 /* popup that is opened when user clicked ID        */
1201 /* This funtcion is called in only clickAnchor().   */ /* private */
1202 void HTMLPart::showIdPopup(const QString& refstr)
1203 {
1204     QString strid = refstr.mid(5)
1205                     .replace("%2B", "+")
1206                     .replace("%2F", "/");
1207
1208     /* popup */
1209     if (m_pushrightbt) {
1210         int num;
1211         QString htmlstr = DatManager(m_datUrl).getHtmlById(strid, num);
1212         if (num <= 1) return ;
1213         QString tmpstr = QString("<DIV>ID:%1:[%2]<BR>").arg(strid).arg(num);
1214         tmpstr += htmlstr + "<BR><BR></DIV>";
1215         showPopup(m_datUrl, tmpstr);
1216         startMultiPopup();
1217     }
1218
1219     else {
1220         KMenu popupMenu(view());
1221
1222         KAction* aboneAct = new KAction(i18nc("@action:inmenu",
1223                     "Add Id to Abone List"), this);
1224         popupMenu.addAction(aboneAct);
1225
1226         QAction* action = popupMenu.exec(QCursor::pos());
1227         if (!action) {
1228             return;
1229         }
1230         if (action == aboneAct) {
1231             // add ID to abone list
1232             if (KMessageBox::warningYesNo(view(),
1233                     i18n("Do you want to add '%1' to abone list?", strid),
1234                     "Kita") == KMessageBox::Yes) {
1235                 AboneConfig::aboneIDList().append(strid);
1236                 redrawHTMLPart(m_datUrl, false);
1237             }
1238         }
1239     }
1240 }
1241
1242
1243
1244 /*---------------------------------------------------------*/
1245 /* popup menu that is opened when user clicked be anchor.  */
1246 /* This funtcion is called in only clickAnchor().          */ /* private */
1247 void HTMLPart::showBePopupMenu(const QString& refstr)
1248 {
1249     QString strUrl = "http://be.2ch.net/test/p.php?i=" + refstr.mid(5)
1250                     + "&u=d:" + DatManager(m_datUrl).threadUrl() + "/l50";
1251
1252     if (m_pushrightbt) {
1253         // create popup menu
1254         KMenu popupMenu(view());
1255         
1256         QClipboard * clipboard = QApplication::clipboard();
1257
1258         KAction* copyUrlAct
1259             = new KAction(i18nc("@action:inmenu", "copy URL"), this);
1260         popupMenu.addAction(copyUrlAct);
1261
1262         KAction* showBrowserAct = new KAction(i18nc("@action:inmenu",
1263                     "Open with Web Browser"), this);
1264
1265         // show popup menu
1266         QAction* action = popupMenu.exec(QCursor::pos());
1267         if (!action) {
1268             return;
1269         }
1270         if (action == copyUrlAct) {
1271             // copy
1272             clipboard->setText(strUrl, QClipboard::Clipboard);
1273             clipboard->setText(strUrl, QClipboard::Selection);
1274         } else if (action == showBrowserAct) {
1275             KRun::runUrl(strUrl, "text/html", view());
1276         }
1277     } else {
1278         KRun::runUrl(strUrl, "text/html", 0);
1279     }
1280 }
1281
1282 /*-------------------------------------------------------*/
1283 /*-------------------------------------------------------*/
1284 /* popup */
1285
1286
1287 /* public */
1288 bool HTMLPart::isPopupVisible()
1289 {
1290     if (!m_popup) return false;
1291     return m_popup->isVisible();
1292 }
1293
1294
1295 /* public slot */
1296 void HTMLPart::slotDeletePopup()
1297 {
1298     delete m_popup;
1299     m_popup = 0;
1300     m_multiPopup = false;
1301 }
1302
1303
1304 /* for convenience */ /* public slot */
1305 void HTMLPart::slotShowResPopup(const QPoint& point, int refNum, int refNum2)
1306 {
1307     QString innerHTML = DatManager(m_datUrl).getHtml(refNum, refNum2);
1308     if (innerHTML.isEmpty()) return ;
1309
1310     showPopupCore(m_datUrl, innerHTML, point);
1311 }
1312
1313
1314 /* for convenience */ /* private */
1315 void HTMLPart::showPopup(const KUrl& url, const QString& innerHTML)
1316 {
1317     showPopupCore(url, innerHTML, QCursor::pos());
1318 }
1319
1320
1321 /* show popup window   */  /* private */
1322 void HTMLPart::showPopupCore(const KUrl& url, const QString& innerHTML, const QPoint& point)
1323 {
1324     slotDeletePopup();
1325     m_multiPopup = false;
1326
1327     m_popup = new ResPopup(view() , url);
1328
1329     connect(m_popup, SIGNAL(hideChildPopup()), SLOT(slotHideChildPopup()));
1330
1331     m_popup->setText(innerHTML);
1332     m_popup->adjustSize();
1333     m_popup->adjustPos(point);
1334     m_popup->show();
1335 }
1336
1337
1338 /*------------------------*/
1339 /* start multi-popup mode */ /* private */
1340 bool HTMLPart::startMultiPopup()
1341 {
1342
1343     if (m_popup && m_popup->isVisible()) {
1344         m_multiPopup = true;
1345         m_popup->moveMouseAbove();
1346     } else {
1347         m_multiPopup = false;
1348     }
1349
1350     return m_multiPopup;
1351 }
1352
1353
1354 /* Is it multi-popup mode now ? */ /* private */
1355 bool HTMLPart::isMultiPopupMode()
1356 {
1357     if (!m_popup) {
1358         m_multiPopup = false;
1359     } else if (m_popup->isHidden()) {
1360         m_multiPopup = false;
1361     }
1362
1363     return m_multiPopup;
1364 }
1365
1366 /* private */
1367 void HTMLPart::hidePopup()
1368 {
1369     if (m_popup) {
1370         m_popup->hide();
1371     }
1372     m_multiPopup = false;
1373 }
1374
1375 /* return true if this view is under mouse. */ /* private */
1376 bool HTMLPart::isUnderMouse(int mrgwd, int mrght)
1377 {
1378     QPoint pos = QCursor::pos();
1379     int cx = pos.x(), cy = pos.y();
1380
1381     QPoint viewpos = view() ->mapToGlobal(QPoint(0, 0));
1382     int px = viewpos.x(), py = viewpos.y();
1383     int wd = view() ->visibleWidth(), ht = view() ->visibleHeight();
1384
1385     if ((px < cx && cx < px + wd + mrgwd)
1386             && (py < cy && cy < py + ht + mrght)) {
1387         return true;
1388     }
1389
1390     return false;
1391 }
1392
1393 /* private slot */
1394 void HTMLPart::slotLeave()
1395 {
1396     if (isMultiPopupMode()) return ;
1397     if (view()->horizontalScrollBar()->isSliderDown()) return;
1398     if (view()->verticalScrollBar()->isSliderDown()) return;
1399
1400     hidePopup();
1401
1402     /* emit signal to have parent hide this if this is popup . */
1403     if (m_mode == HTMLPART_MODE_POPUP && !isUnderMouse(0, 0)) {
1404         emit hideChildPopup();
1405     }
1406 }
1407
1408 /* private slot */
1409 void HTMLPart::slotVSliderReleased()
1410 {
1411     QScrollBar * bar = view() ->verticalScrollBar();
1412     QRect rt = bar->style()->subControlRect(QStyle::CC_Slider, new QStyleOptionComplex(), QStyle::SC_SliderHandle);
1413     int mrg = rt.right() - rt.left();
1414
1415     hidePopup();
1416
1417     /* emit signal to have parent hide this if this is popup . */
1418     if (m_mode == HTMLPART_MODE_POPUP && !isUnderMouse(mrg, 0)) {
1419         emit hideChildPopup();
1420     }
1421 }
1422
1423
1424 /* private slot */
1425 void HTMLPart::slotHSliderReleased()
1426 {
1427     QScrollBar * bar = view() ->horizontalScrollBar();
1428     QRect rt = bar->style()->subControlRect(QStyle::CC_Slider, new QStyleOptionComplex(), QStyle::SC_SliderHandle);
1429     int mrg = rt.bottom() - rt.top();
1430
1431     hidePopup();
1432
1433     /* emit signal to have parent hide this if this is popup . */
1434     if (m_mode == HTMLPART_MODE_POPUP && !isUnderMouse(0, mrg)) {
1435         emit hideChildPopup();
1436     }
1437 }
1438
1439
1440
1441 /* private slot */
1442 void HTMLPart::slotHideChildPopup()
1443 {
1444     hidePopup();
1445
1446     /* emit signal to have parent hide this if this is popup . */
1447     if (m_mode == HTMLPART_MODE_POPUP && !isUnderMouse(0, 0)) {
1448         emit hideChildPopup();
1449     }
1450 }
1451
1452
1453 /*---------------------------------------------------*/
1454 /* This slot is called when mouse moves onto the URL */ /* private slot */
1455 void HTMLPart::slotOnUrl(const QString& url)
1456 {
1457     /* config */
1458
1459     const int maxpopup = 10;  /* max number of responses shown in the popup window */
1460
1461     /*----------------------------*/
1462
1463     if (isMultiPopupMode()) return ;
1464
1465     slotDeletePopup();
1466
1467     if (url.isEmpty()) return ;
1468     if (url.left(7) == "mailto:") return ;
1469
1470     /* Is Kita active now ? */
1471     if(ViewMediator::getInstance()->isActive() == false) return;
1472
1473     /* get reference */
1474     QString refstr;
1475     KUrl datUrl = m_datUrl;
1476     if (url.at(0) == '#') {
1477         refstr = url.mid(1);
1478     } else {
1479         datUrl = getDatUrl(KUrl(m_datUrl, url) , refstr);
1480     }
1481
1482     DatManager datManager(m_datUrl);
1483     /*------------------------*/
1484     /* id popup               */
1485
1486     if (url.left(6) == "#idpop") {
1487         int num = datManager.getNumById(url.mid(6));
1488         QString tmpstr;
1489         if (num >= 2) {
1490             tmpstr = QString("<DIV>ID:%1:[%2]</DIV>").arg(url.mid(6)).arg(num);
1491         } else {
1492             tmpstr = "<DIV>"
1493                 + i18nc("@label:textbox This is the first reply", "None")
1494                 + "</DIV>";
1495         }
1496         showPopup(m_datUrl, tmpstr);
1497         return ;
1498     }
1499
1500
1501     /*------------------------*/
1502     /* show reffered num      */
1503
1504     if (refstr.left(5) == "write") {
1505         int no = refstr.mid(5).toInt();
1506         int num = 0;
1507         datManager.getTreeByRes(no, num);
1508         QString tmpstr;
1509         if (num) {
1510             tmpstr = QString("<DIV>No.%1 : [%2]</DIV>").arg(no).arg(num);
1511         } else {
1512             tmpstr = "<DIV>"
1513                 + i18nc("@label:textbox There is no reply", "None") + "</DIV>";
1514         }
1515         showPopup(m_datUrl, tmpstr);
1516         return ;
1517     }
1518
1519
1520     /*------------------------*/
1521     /* abone                  */
1522
1523     if (url.left(6) == "#abone") {
1524         int no = url.mid(6).toInt();
1525         QString tmpstr = datManager.getHtml(no, no, false);
1526         showPopup(m_datUrl, tmpstr);
1527         return ;
1528     }
1529
1530     /*-------------------------*/
1531     /* popup for anchor        */
1532
1533     QString innerHTML;
1534     int refNum;
1535     int refNum2;
1536
1537     int i = refstr.indexOf("-");
1538     if (i != -1) { /* >>refNum-refNum2 */
1539
1540         refNum = refstr.left(i).toInt();
1541         refNum2 = refstr.mid(i + 1).toInt();
1542
1543         if (refNum) {
1544             if (refNum2 < refNum) refNum2 = refNum;
1545             if (refNum2 - refNum > maxpopup - 1) refNum2 = refNum + maxpopup - 1;
1546         }
1547
1548     } else { /* >>refNum */
1549         refNum = refstr.toInt();
1550         refNum2 = refNum;
1551     }
1552
1553     DatManager localDatManager(datUrl);
1554     /* another thread ? */
1555     if (datUrl.host() != m_datUrl.host() || datUrl.path() != m_datUrl.path()) {
1556
1557         /* get board name */
1558         BoardDatabase db(datUrl);
1559         QString boardName = db.boardName();
1560         if (!boardName.isEmpty()) innerHTML += '[' + boardName + "] ";
1561
1562         /* If idx file of datURL is not read, thread name cannot be obtained.
1563            so, create DatInfo if cache exists, and read idx file in DatInfo::DatInfo(). */
1564         localDatManager.getDatInfoPointer();
1565
1566         /* get thread Name */
1567         QString subName = localDatManager.threadName();
1568         if (!subName.isEmpty()) innerHTML += subName + "<br><br>";
1569
1570         if (!refNum) refNum = refNum2 = 1;
1571     }
1572
1573     /* get HTML and show it */
1574     if (!refNum) return ;
1575     innerHTML += localDatManager.getHtml(refNum, refNum2);
1576
1577     if (!innerHTML.isEmpty())
1578         showPopup(datUrl, innerHTML);
1579 }
1580
1581
1582
1583 /* If selected Text is composed of only digits,
1584    then show res popup.                          */ /* private */
1585 bool HTMLPart::showSelectedDigitPopup()
1586 {
1587     if (!hasSelection()) return false;
1588
1589     QString linkstr;
1590     int refNum;
1591     QString selectText = selectedText();
1592     const QChar *chpt = selectText.unicode();
1593     unsigned int length = selectText.length();
1594
1595     if ((refNum = stringToPositiveNum(chpt, length)) != -1) {
1596         QString innerHTML = DatManager(m_datUrl).getHtml(refNum, refNum);
1597         if (!innerHTML.isEmpty()) {
1598             showPopup(m_datUrl, innerHTML);
1599             startMultiPopup();
1600             return true;
1601         }
1602     }
1603
1604     return false;
1605 }