1 /***************************************************************************
2 * Copyright (C) 2004 by Kita Developers *
3 * ikemo@users.sourceforge.jp *
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 ***************************************************************************/
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>
22 #include <khtml_events.h>
24 #include <kmessagebox.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"
40 /*-------------------------------------------*/
43 /*-------------------------------------*/
44 /* Don't forget to call setup() later. */
46 HTMLPart::HTMLPart(QWidget* parent) : KHTMLPart(new HTMLView(this, parent))
48 m_mode = HTMLPART_MODE_MAINPART;
52 m_updatedKokoyon = false;
67 void HTMLPart::clearPart()
75 DatManager datManager(m_datUrl);
77 if (m_mode == HTMLPART_MODE_MAINPART && !m_updatedKokoyon
78 && !m_datUrl.isEmpty()) {
79 int readNum = datManager.getReadNum();
81 datManager.setViewPos(readNum);
84 m_updatedKokoyon = false;
87 m_anchorStack.clear();
89 m_jumpNumAfterLoading = 0;
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) {
96 // tell Thread class that "thread is closed"
97 datManager.setMainThreadOpened(false);
99 // emit "deactivated all thread view" SIGNAL
101 ViewMediator::getInstance()->changeWriteTab(nullUrl);
103 // update subject tab.
108 m_mode = HTMLPART_MODE_MAINPART;
114 bool HTMLPart::setup(int mode, const KUrl& url)
116 Q_ASSERT(!url.isEmpty());
120 m_datUrl = getDatUrl(url);
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();
132 // tell Thread class that "thread is opened"
133 datManager.setMainThreadOpened(true);
136 datManager.resetAbone();
139 // create HTML Document
140 createHTMLDocument();
142 // create DOM manager
143 if (m_mode == HTMLPART_MODE_MAINPART) {
144 m_domtree = new DomTree(htmlDocument(), m_datUrl);
152 void HTMLPart::connectSignals()
155 connect(this, SIGNAL(onURL(const QString&)), SLOT(slotOnUrl(const QString&)));
157 connect(view(), SIGNAL(leave()), SLOT(slotLeave()));
158 connect(view()->verticalScrollBar(), SIGNAL(sliderReleased()), SLOT(slotVSliderReleased()));
159 connect(view()->horizontalScrollBar(), SIGNAL(sliderReleased()), SLOT(slotHSliderReleased()));
165 void HTMLPart::createHTMLDocument()
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());
174 QString text = "<html><head><style>";
175 text += DEFAULT_STYLESHEET;
177 text += "</style></head><body></body></html>";
179 setJScriptEnabled(false);
180 setJavaEnabled(false);
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 begin(KUrl("file:/dummy.htm"));
190 /*---------------------------------------------------------------*/
191 /*---------------------------------------------------------------*/
192 /* direct rendering functions */
196 * @warning don't forget to call updateScreen() later.
198 void HTMLPart::showResponses(int startnum, int endnum)
200 if (!m_domtree) return ;
202 for (int i = startnum ; i <= endnum; i++) m_domtree->appendRes(i);
206 /* do parsing only. */
207 /* call showResponses() later */ /* public */
208 void HTMLPart::parseResponses(int startnum, int endnum)
210 if (!m_domtree) return ;
212 for (int i = startnum ; i <= endnum; i++) m_domtree->createResElement(i);
216 /*------------------------------------------------*/
217 /* Show all responses ,header, footer, and etc,
218 if these are not shown. */
220 /* note that updateScreen() is called internally. */
221 /* So, you need not call it later. */ /* public slot */
222 void HTMLPart::showAll()
224 if (!m_domtree) return ;
226 int bottom = m_domtree->getBottomResNumber();
227 int readNum = DatManager(m_datUrl).getReadNum();
228 if (bottom != readNum) {
230 QCursor qc; qc.setShape(Qt::WaitCursor);
231 QApplication::setOverrideCursor(qc);
233 showResponses(1, readNum);
234 updateScreen(true, false);
236 QApplication::restoreOverrideCursor();
243 void HTMLPart::updateScreen(bool showHeaderEtc, bool clock)
252 QCursor qc; qc.setShape(Qt::WaitCursor);
253 QApplication::setOverrideCursor(qc);
256 // show header, footer, and kokomadeyonda, etc.
258 m_domtree->appendKokoyon();
259 m_domtree->appendFooterAndHeader();
262 // change color of number of the res which is responsed.
263 if (m_mode == HTMLPART_MODE_MAINPART) {
265 if (GlobalConfig::checkResponsed()) {
266 m_domtree->changeColorOfAllResponsedNumber();
271 htmlDocument().applyChanges();
273 view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
278 QApplication::restoreOverrideCursor();
284 void HTMLPart::setInnerHTML(const QString& innerHTML)
286 createHTMLDocument();
287 htmlDocument().body().setInnerHTML(innerHTML);
293 void HTMLPart::redrawHTMLPart(const KUrl& datUrl, bool force)
295 if (m_domtree == 0) return ;
296 if (m_datUrl != datUrl) return ;
298 m_domtree->redraw(force);
302 void HTMLPart::slotSetFaceOfHTMLPart()
304 QFont font = GlobalConfig::threadFont();
306 DOM::CSSStyleDeclaration style = htmlDocument().body().style();
307 style.setProperty("font-family", font.family(), "");
308 style.setProperty("font-size", QString("%1pt").arg(font.pointSize()), "");
309 style.setProperty("color", ColorConfig::thread().name(), "");
310 style.setProperty("background-color",
311 ColorConfig::threadBackground().name(), "");
312 htmlDocument().applyChanges();
315 /*---------------------------------------------------------------*/
316 /*---------------------------------------------------------------*/
317 /* cache functions */
320 /*----------------------------------*/
321 /* load thread from cache & show it */ /* public */
323 /* top = centerNum - preShowNum
324 bottom = centerNum + afterShowNum
325 readNum = DatManager::getReadNum
327 No.1 <- show -> No.20 <- not show -> No.(top) <- show -> No.(bottom) <- not show -> No.(readNum) */
329 bool HTMLPart::load(int centerNum)
331 m_centerNum = centerNum;
332 m_jumpNumAfterLoading = 0;
334 if (m_mode != HTMLPART_MODE_MAINPART)
338 DatManager datManager(m_datUrl);
339 if (datManager.getReadNum() == 0)
342 int endNum = datManager.getReadNum();
343 showResponses(1, endNum);
344 updateScreen(true , false);
345 gotoAnchor(QString::number(m_centerNum), false);
353 /*----------------------------*/
354 /* start reloading */
356 /* see also slotReceiveData()
357 and slotFinishLoad(). */ /* public */
358 bool HTMLPart::reload(int jumpNum)
360 if (!m_domtree) return false;
361 if (m_mode != HTMLPART_MODE_MAINPART) {
362 // If this is not MainPart, then open MainPart.
363 ViewMediator::getInstance()->openUrl(m_datUrl);
367 m_firstReceive = true;
368 if (m_centerNum == 0) m_centerNum = m_domtree->getBottomResNumber();
369 m_jumpNumAfterLoading = jumpNum;
371 // DatManager will call back slotReceiveData and slotFinishLoad.
372 DatManager(m_datUrl).updateCache(this);
380 /*---------------------------------------------*/
381 /* This slot is called after DatManager
382 received new data, and emits receiveData() */ /* !!! "public" slot !!! */
383 void HTMLPart::slotReceiveData()
385 const int delta = 20;
387 if (m_mode != HTMLPART_MODE_MAINPART) return ;
388 if (!m_domtree) return ;
390 int readNum = DatManager(m_datUrl).getReadNum();
391 int bottom = m_domtree->getBottomResNumber();
394 parseResponses(bottom + 1, readNum);
397 if (m_firstReceive || bottom + delta < readNum) {
398 showResponses(bottom + 1, readNum);
399 updateScreen(true, false);
402 if (m_firstReceive && m_centerNum < readNum) {
403 gotoAnchor(QString::number(m_centerNum), false);
404 m_firstReceive = false;
411 /*--------------------------------------------*/
412 /* This slot is called after DatManager
413 finished updating new data,
414 and emits finishReload() */ /* !!! "public" slot !!! */
415 void HTMLPart::slotFinishLoad()
417 if (m_mode != HTMLPART_MODE_MAINPART) return ;
418 if (!m_domtree) return ;
420 int bottom = m_domtree->getBottomResNumber();
421 int shownNum = m_centerNum + 5000;
423 showResponses(bottom + 1, shownNum);
424 updateScreen(true, false);
425 // m_domtree->parseAllRes();
428 if (m_jumpNumAfterLoading)
429 gotoAnchor(QString::number(m_jumpNumAfterLoading), false);
430 m_jumpNumAfterLoading = 0;
438 /*---------------------------------------------------------------*/
439 /*---------------------------------------------------------------*/
444 bool HTMLPart::gotoAnchor(const QString& anc, bool pushPosition)
446 if (anc.isEmpty()) return false;
447 if (!m_domtree || m_mode == HTMLPART_MODE_POPUP)
448 return KHTMLPart::gotoAnchor(anc);
452 QString ancstr = anc;
453 int res = ancstr.toInt();
458 if (!DatManager(m_datUrl).isResValid(res))
461 ancstr = QString::number(res);
464 if (res == 1) ancstr = "header";
465 if (pushPosition) pushCurrentPosition();
467 // KHTMLPart::gotoAnchor() will fail if the thread is not shown.
468 // So KHTMLPart::gotoAnchor() should be called via custom event.
469 // See also HTMLPart::customEvent()
470 GotoAnchorEvent * e = new GotoAnchorEvent(ancstr);
471 QApplication::postEvent(this, e); // Qt will delete it when done
478 /* jump to kokomade yonda */ /* public slot */
479 void HTMLPart::slotGotoKokoyon()
481 if (!m_domtree) return ;
482 if (m_mode != HTMLPART_MODE_MAINPART) return ;
484 int kokoyon = DatManager(m_datUrl).getViewPos();
485 gotoAnchor(QString::number(kokoyon), false);
491 void HTMLPart::slotGobackAnchor()
493 if (m_anchorStack.isEmpty()) return ;
495 QString anc = m_anchorStack.last();
496 m_anchorStack.pop_back();
497 gotoAnchor(anc , false);
504 void HTMLPart::pushCurrentPosition()
506 m_anchorStack += getCurrentIdOfNode();
510 /* find the id of current node */ /* private */
511 QString HTMLPart::getCurrentIdOfNode()
514 node = nodeUnderMouse();
515 while (node != 0 && node.nodeName().string() != "div") node = node.parentNode();
516 if (node == 0) return QString();
518 return static_cast<DOM::Element>(node).getAttribute("id").string();
522 void HTMLPart::slotClickGotoFooter()
524 if (!m_domtree || m_mode != HTMLPART_MODE_MAINPART) {
525 gotoAnchor("footer", false);
529 int bottom = m_domtree->getBottomResNumber();
530 int readNum = DatManager(m_datUrl).getReadNum();
532 if (readNum != bottom) {
533 showResponses(bottom + 1, readNum);
534 updateScreen(true, true);
537 gotoAnchor("footer", false);
541 /*---------------------------------------------------------------*/
542 /*---------------------------------------------------------------*/
547 void HTMLPart::findTextInit()
557 bool HTMLPart::findText(const QString &query, bool reverse)
559 if (m_mode != HTMLPART_MODE_MAINPART) return false;
561 QRegExp regexp(query);
562 regexp.setCaseSensitivity(Qt::CaseInsensitive);
565 if (m_findNode.isNull()) {
567 m_findNode = htmlDocument().body();
570 // move to the last child node
572 while (!m_findNode.lastChild().isNull()) m_findNode = m_findNode.lastChild();
573 m_find_y = view() ->contentsHeight();
579 if (m_findNode.nodeType() == DOM::Node::TEXT_NODE
580 || m_findNode.nodeType() == DOM::Node::CDATA_SECTION_NODE) {
582 // find the word in the current node
583 DOM::DOMString nodeText = m_findNode.nodeValue();
584 QString nodestr = nodeText.string();
585 if (reverse && m_findPos != -1) nodestr.resize(m_findPos);
587 if (reverse) m_findPos = nodestr.lastIndexOf(regexp, m_findPos);
588 else m_findPos = nodestr.indexOf(regexp, m_findPos + 1);
590 // scroll & select & return
591 if (m_findPos != -1) {
592 int matchLen = regexp.matchedLength();
594 QRect qr = m_findNode.getRect();
595 view() ->setContentsPos(qr.left() - 50, m_find_y - 100);
596 DOM::Range rg(m_findNode, m_findPos, m_findNode, m_findPos + matchLen);
602 } else if (m_findNode.nodeName().string() == "table") {
604 QRect qr = m_findNode.getRect();
606 m_find_y = qr.bottom();
608 } else if (m_findNode.nodeName().string() == "div") {
610 QRect qr = m_findNode.getRect();
613 m_find_y = qr.bottom();
618 } else if (m_findNode.nodeName().string() == "br") {
620 DOM::Node tmpnode = m_findNode.previousSibling();
624 QRect qr = tmpnode.getRect();
625 if (reverse) m_find_y -= qr.bottom() - qr.top();
626 else m_find_y += qr.bottom() - qr.top();
630 //------------------------
635 // move to the next node
638 next = m_findNode.firstChild();
639 if (next.isNull()) next = m_findNode.nextSibling();
641 while (!m_findNode.isNull() && next.isNull()) {
642 m_findNode = m_findNode.parentNode();
643 if (!m_findNode.isNull()) {
644 next = m_findNode.nextSibling();
651 next = m_findNode.lastChild();
652 if (next.isNull()) next = m_findNode.previousSibling();
654 while (!m_findNode.isNull() && next.isNull()) {
655 m_findNode = m_findNode.parentNode();
656 if (!m_findNode.isNull()) {
657 next = m_findNode.previousSibling();
663 if (m_findNode.isNull()) {
677 /*---------------------------------------------------------------*/
678 /*---------------------------------------------------------------*/
683 void HTMLPart::showPopupMenu(const KUrl& kurl)
685 QString url = kurl.prettyUrl();
686 bool showppm = false;
690 // If selected Text is composed of only digits, then show res popup.
691 if (!m_pushctrl && showSelectedDigitPopup()) return ;
693 //-----------------------------------
696 KMenu popupMenu(view());
697 KMenu* backSubMenu = 0;
698 KMenu* markSubMenu = 0;
702 KAction* homeLinkAct = 0;
703 KAction* kokoLinkAct = 0;
704 KAction* endLinkAct = 0;
706 (m_mode == HTMLPART_MODE_MAINPART)) {
710 DatManager datManager(m_datUrl);
712 if (!m_anchorStack.isEmpty()) {
713 backSubMenu = new KMenu(view());
715 int i = m_anchorStack.size();
716 QStringList::iterator it;
717 for (it = m_anchorStack.begin(); it != m_anchorStack.end(); it++, i--) {
718 str = (*it) + " " + datManager.getPlainBody((*it).toInt()).left(10);
719 KAction* backLink = new KAction(str, this);
720 backSubMenu->addAction(backLink);
722 backSubMenu->setTitle(
723 i18nc("@title:menu Go back to the responses", "Back"));
724 popupMenu.addMenu(backSubMenu);
725 popupMenu.addSeparator();
729 int readNum = datManager.getReadNum();
730 for (int i = 1; i <= readNum ; i++) {
731 if (datManager.isMarked(i)) {
733 markSubMenu = new KMenu(view());
734 markSubMenu->setTitle(i18nc("@title:menu", "Mark"));
735 popupMenu.addMenu(markSubMenu);
736 popupMenu.addSeparator();
738 str = QString::number(i) + " "
739 + datManager.getPlainBody(i).left(10);
740 KAction *gotoMarkAct = new KAction(str, this);
741 markSubMenu->addAction(gotoMarkAct);
746 homeLinkAct = new KAction(
747 i18nc("@action:inmenu Go to the original post", "Start"), this);
748 popupMenu.addAction(homeLinkAct);
751 if (m_mode == HTMLPART_MODE_MAINPART) {
752 int kokoyon = datManager.getViewPos();
754 str = i18nc("@action:inmenu", "Kokomade Yonda (%1)", kokoyon);
755 kokoLinkAct = new KAction(str, this);
756 popupMenu.addAction(kokoLinkAct);
760 endLinkAct = new KAction(i18nc("@action:inmenu", "End"), this);
761 popupMenu.addAction(endLinkAct);
765 KAction* copyStrAct = 0;
766 KAction* aboneWordAct = 0;
767 if (hasSelection()) {
768 if (showppm) popupMenu.addSeparator();
771 copyStrAct = new KAction(i18nc("@action:inmenu", "Copy"), this);
772 popupMenu.addAction(copyStrAct);
775 QString selectedStr = selectedText();
776 if (selectedStr.length() > 20) {
777 selectedStr.truncate(20);
778 selectedStr.append("...");
781 aboneWordAct = new KAction(i18nc("@action:inmenu",
782 "Add '%1' to abone list", selectedStr), this);
783 popupMenu.addAction(aboneWordAct);
788 KAction* openBrowserAct = 0;
789 KAction* copyLinkAct = 0;
790 if (!url.isEmpty()) {
791 if (showppm) popupMenu.addSeparator();
794 openBrowserAct= new KAction(i18nc("@action:inmenu",
795 "Open with Web Browser"), this);
796 popupMenu.addAction(openBrowserAct);
797 popupMenu.addSeparator();
799 copyLinkAct = new KAction(i18nc("@action:inmenu",
800 "Copy Link Location"), this);
801 popupMenu.addAction(copyLinkAct);
808 QClipboard * clipboard = QApplication::clipboard();
810 QAction* action = popupMenu.exec(QCursor::pos());
816 if (action == copyLinkAct) {
817 clipboard->setText(url , QClipboard::Clipboard);
818 clipboard->setText(url , QClipboard::Selection);
819 } else if (action == openBrowserAct) {
820 KRun::runUrl(kurl, "text/html", view());
821 } else if (action == homeLinkAct) {
822 gotoAnchor("header", false);
823 } else if (action == kokoLinkAct) {
825 } else if (action == endLinkAct) {
826 slotClickGotoFooter();
827 } else if (action == copyStrAct) {
828 clipboard->setText(selectedText(), QClipboard::Clipboard);
829 } else if (action == aboneWordAct) {
830 if (KMessageBox::warningYesNo(view(),
831 i18n("Do you want to add '%1' to abone list?",
832 selectedText())) == KMessageBox::Yes) {
833 AboneConfig::aboneWordList().append(selectedText());
834 redrawHTMLPart(m_datUrl, false);
837 QMenu* menu = action->menu();
843 if (menu == markSubMenu) {
844 gotoAnchor(QString::number(menu->actions().indexOf(action)),
846 } else if (menu == backSubMenu) {
847 for (int i = 0; i < menu->actions().indexOf(action); i++)
848 m_anchorStack.pop_back();
857 /*---------------------------------------------------------------*/
858 /*---------------------------------------------------------------*/
862 /* protected */ /* virtual */
863 void HTMLPart::customEvent(QEvent * e)
865 if (e->type() == EVENT_GotoAnchor) {
866 KHTMLPart::gotoAnchor(static_cast< GotoAnchorEvent* >(e) ->getAnc());
870 KHTMLPart::customEvent(e);
874 /*---------------------------------------------------------------*/
875 /*---------------------------------------------------------------*/
880 void HTMLPart::khtmlMousePressEvent(khtml::MousePressEvent* e)
882 emit mousePressed(); // to ThreadView to focus this view.
885 if (!e->url().string().isEmpty()) {
886 BoardDatabase db(m_datUrl);
887 kurl = KUrl(db.boardUrl(), e->url().string());
890 m_pushctrl = m_pushmidbt = m_pushrightbt = false;
891 if (e->qmouseEvent() ->button() & Qt::RightButton) m_pushrightbt = true;
892 if (e->qmouseEvent() ->modifiers() & Qt::ControlModifier) m_pushctrl = true;
893 if (e->qmouseEvent() ->button() & Qt::MidButton) m_pushmidbt = true;
897 if (e->url().string().at(0) == '#') { // anchor
899 kurl.setRef(e->url().string().mid(1)) ;
903 m_pushctrl = m_pushmidbt = m_pushrightbt = false;
910 m_pushctrl = m_pushmidbt = m_pushrightbt = false;
914 KHTMLPart::khtmlMousePressEvent(e);
921 /*-------------------------------------------------------*/
922 /*-------------------------------------------------------*/
928 void HTMLPart::slotOpenUrlRequest(const KUrl& urlin, const KParts::OpenUrlArguments&)
934 /*------------------------------------------------------*/
935 /* This function is called when user clicked res anchor */ /* private */
936 void HTMLPart::clickAnchor(const KUrl& urlin)
939 KUrl datUrl = getDatUrl(urlin , refstr);
941 //--------------------
942 // Ctrl + right click
943 if (m_pushctrl && m_pushrightbt) {
944 showPopupMenu(urlin);
948 //--------------------------------
949 // If this is not anchor, then
950 // emit openURLRequest and return
952 if (datUrl.host() != m_datUrl.host() || datUrl.path() != m_datUrl.path()) {
957 // start multi-popup mode or show popup menu
958 if (!startMultiPopup()) showPopupMenu(urlin);
964 ViewMediator::getInstance()->openUrl(urlin);
968 if (refstr.isEmpty()) return ;
970 //---------------------------
971 // show popupmenu for #write
973 if (refstr.left(5) == "write") {
974 showWritePopupMenu(refstr);
978 //----------------------------
979 // extract responses by ID
981 if (refstr.left(5) == "idpop") {
986 //---------------------------
987 // show popupmenu for #bepop
989 if (refstr.left(5) == "bepop") {
990 showBePopupMenu(refstr);
994 //-------------------------
995 // start multi-popup mdde
996 if (m_pushrightbt && startMultiPopup()) return ;
999 //----------------------------
1000 // next 100 ,before 100 ,etc.
1001 if (m_mode == HTMLPART_MODE_MAINPART) {
1002 if (refstr.left(7) == "tosaigo") {
1003 slotClickGotoFooter();
1008 //-------------------------------
1009 // open Kita Navi or goto anchor
1011 int refNum, refNum2;
1013 int i = refstr.indexOf("-");
1015 refNum = refstr.left(i).toInt();
1016 refNum2 = refstr.mid(i + 1).toInt();
1017 if (refNum2 < refNum) {
1021 refNum = refNum2 = refstr.toInt();
1024 if (!refNum) return ;
1026 if (m_mode == HTMLPART_MODE_POPUP) {
1027 ViewMediator::getInstance()->openUrl(urlin);
1029 gotoAnchor(QString::number(refNum), true);
1035 /*---------------------------------------------------------*/
1036 /* popup menu that is opened when user clicked res number. */
1037 /* This funtcion is called in only clickAnchor(). */ /* private */
1038 void HTMLPart::showWritePopupMenu(const QString& refstr)
1040 QClipboard * clipboard = QApplication::clipboard();
1041 QString str, resstr;
1042 int resNum = refstr.mid(5).toInt();
1043 DatManager datManager(m_datUrl);
1044 QString namestr = datManager.getPlainName(resNum);
1047 if (m_pushrightbt) {
1049 QString htmlstr = datManager.getTreeByRes(resNum, num);
1051 QString tmpstr = QString("<DIV>No.%1 : [%2]<BR>").arg(resNum).arg(num);
1052 tmpstr += htmlstr + "<BR><BR></DIV>";
1053 showPopup(m_datUrl, tmpstr);
1058 //---------------------
1059 // create popup menu
1062 KMenu popupMenu(view());
1064 KAction* resAct = 0;
1065 KAction* quoteAct = 0;
1066 if (m_mode == HTMLPART_MODE_MAINPART) {
1067 resAct = new KAction(i18nc("@action:inmenu", "Write Response"), this);
1068 popupMenu.addAction(resAct);
1070 quoteAct = new KAction(i18nc("@action:inmenu", "Quote This"), this);
1071 popupMenu.addAction(quoteAct);
1073 popupMenu.addSeparator();
1077 KAction* markAct = new KAction(i18nc("@action:inmenu", "Mark"), this);
1078 markAct->setCheckable(true);
1079 markAct->setChecked(datManager.isMarked(resNum));
1080 popupMenu.addAction(markAct);
1082 popupMenu.addSeparator();
1085 KAction* showBrowserAct
1086 = new KAction(i18nc("@action:inmenu", "Open with Web Browser"), this);
1087 popupMenu.addAction(showBrowserAct);
1089 popupMenu.addSeparator();
1093 = new KAction(i18nc("@action:inmenu", "Res Tree"), this);
1094 popupMenu.addAction(resTreeAct);
1096 KAction* reverseResTreeAct
1097 = new KAction(i18nc("@action:inmenu", "Reverse Res Tree"), this);
1098 popupMenu.addAction(reverseResTreeAct);
1100 KAction* extractNameAct
1101 = new KAction(i18nc("@action:inmenu", "Extract by Name"), this);
1102 popupMenu.addAction(extractNameAct);
1104 popupMenu.addSeparator();
1108 = new KAction(i18nc("@action:inmenu", "Copy URL"), this);
1109 popupMenu.addAction(copyUrlAct);
1111 KAction* copyThreadNameAct
1112 = new KAction(i18nc("@action:inmenu", "Copy Title and URL"), this);
1113 popupMenu.addAction(copyThreadNameAct);
1115 KAction* copyAct = new KAction(i18nc("@action:inmenu", "Copy"), this);
1116 popupMenu.addAction(copyAct);
1119 KAction* setKokoYonAct = 0;
1120 if (m_domtree && m_mode == HTMLPART_MODE_MAINPART) {
1121 popupMenu.addSeparator();
1124 = new KAction(i18nc("@action:inmenu", "Set Kokomade Yonda"), this);
1125 popupMenu.addAction(setKokoYonAct);
1129 popupMenu.addSeparator();
1131 KAction* aboneNameAct
1132 = new KAction(i18nc("@action:inmenu", "Add Name to Abone List"), this);
1133 popupMenu.addAction(aboneNameAct);
1137 QAction* action = popupMenu.exec(QCursor::pos());
1141 if (action == resAct) {
1142 resstr = ">>" + QString::number(resNum) + '\n';
1143 ViewMediator::getInstance()->showWriteView(m_datUrl, resstr);
1144 } else if (action == quoteAct) {
1145 resstr = ">>" + QString::number(resNum) + '\n'
1146 + "> " + datManager.getPlainTitle(resNum) + '\n'
1147 + "> " + datManager.getPlainBody(resNum).replace('\n', "\n> ") + '\n';
1148 ViewMediator::getInstance()->showWriteView(m_datUrl, resstr);
1149 } else if (action == markAct) {
1150 datManager.setMark(resNum, !datManager.isMarked(resNum));
1151 } else if (action == copyAct || action == copyUrlAct || action == copyThreadNameAct) {
1155 if (action == copyThreadNameAct || action == copyAct) {
1156 str = datManager.threadName();
1160 if (!str.isEmpty()) str += '\n';
1161 str += datManager.threadUrl() + '/' + QString::number(resNum)
1165 if (action == copyAct) {
1167 + datManager.getPlainTitle(resNum) + '\n'
1168 + datManager.getPlainBody(resNum) + '\n';
1172 clipboard->setText(str , QClipboard::Clipboard);
1173 clipboard->setText(str , QClipboard::Selection);
1175 } else if (action == setKokoYonAct) {
1176 datManager.setViewPos(resNum);
1177 ViewMediator::getInstance()->updateBoardView(m_datUrl);
1178 m_updatedKokoyon = true;
1179 updateScreen(true, true);
1180 gotoAnchor(QString::number(resNum), false);
1182 } else if (action == showBrowserAct) {
1183 str = datManager.threadUrl() + '/' + QString::number(resNum);
1185 KRun::runUrl(str, "text/html", view());
1186 } else if (action == aboneNameAct) {
1187 if (KMessageBox::warningYesNo(view(),
1188 i18n("Do you want to add '%1' to abone list?", namestr),
1189 "Kita") == KMessageBox::Yes) {
1190 AboneConfig::aboneNameList().append(namestr);
1191 redrawHTMLPart(m_datUrl, false);
1198 /*--------------------------------------------------*/
1199 /* popup that is opened when user clicked ID */
1200 /* This funtcion is called in only clickAnchor(). */ /* private */
1201 void HTMLPart::showIdPopup(const QString& refstr)
1203 QString strid = refstr.mid(5)
1204 .replace("%2B", "+")
1205 .replace("%2F", "/");
1208 if (m_pushrightbt) {
1210 QString htmlstr = DatManager(m_datUrl).getHtmlById(strid, num);
1211 if (num <= 1) return ;
1212 QString tmpstr = QString("<DIV>ID:%1:[%2]<BR>").arg(strid).arg(num);
1213 tmpstr += htmlstr + "<BR><BR></DIV>";
1214 showPopup(m_datUrl, tmpstr);
1219 KMenu popupMenu(view());
1221 KAction* aboneAct = new KAction(i18nc("@action:inmenu",
1222 "Add Id to Abone List"), this);
1223 popupMenu.addAction(aboneAct);
1225 QAction* action = popupMenu.exec(QCursor::pos());
1229 if (action == aboneAct) {
1230 // add ID to abone list
1231 if (KMessageBox::warningYesNo(view(),
1232 i18n("Do you want to add '%1' to abone list?", strid),
1233 "Kita") == KMessageBox::Yes) {
1234 AboneConfig::aboneIDList().append(strid);
1235 redrawHTMLPart(m_datUrl, false);
1243 /*---------------------------------------------------------*/
1244 /* popup menu that is opened when user clicked be anchor. */
1245 /* This funtcion is called in only clickAnchor(). */ /* private */
1246 void HTMLPart::showBePopupMenu(const QString& refstr)
1248 QString strUrl = "http://be.2ch.net/test/p.php?i=" + refstr.mid(5)
1249 + "&u=d:" + DatManager(m_datUrl).threadUrl() + "/l50";
1251 if (m_pushrightbt) {
1252 // create popup menu
1253 KMenu popupMenu(view());
1255 QClipboard * clipboard = QApplication::clipboard();
1258 = new KAction(i18nc("@action:inmenu", "copy URL"), this);
1259 popupMenu.addAction(copyUrlAct);
1261 KAction* showBrowserAct = new KAction(i18nc("@action:inmenu",
1262 "Open with Web Browser"), this);
1265 QAction* action = popupMenu.exec(QCursor::pos());
1269 if (action == copyUrlAct) {
1271 clipboard->setText(strUrl, QClipboard::Clipboard);
1272 clipboard->setText(strUrl, QClipboard::Selection);
1273 } else if (action == showBrowserAct) {
1274 KRun::runUrl(strUrl, "text/html", view());
1277 KRun::runUrl(strUrl, "text/html", 0);
1281 /*-------------------------------------------------------*/
1282 /*-------------------------------------------------------*/
1287 bool HTMLPart::isPopupVisible()
1289 if (!m_popup) return false;
1290 return m_popup->isVisible();
1295 void HTMLPart::slotDeletePopup()
1299 m_multiPopup = false;
1303 /* for convenience */ /* public slot */
1304 void HTMLPart::slotShowResPopup(const QPoint& point, int refNum, int refNum2)
1306 QString innerHTML = DatManager(m_datUrl).getHtml(refNum, refNum2);
1307 if (innerHTML.isEmpty()) return ;
1309 showPopupCore(m_datUrl, innerHTML, point);
1313 /* for convenience */ /* private */
1314 void HTMLPart::showPopup(const KUrl& url, const QString& innerHTML)
1316 showPopupCore(url, innerHTML, QCursor::pos());
1320 /* show popup window */ /* private */
1321 void HTMLPart::showPopupCore(const KUrl& url, const QString& innerHTML, const QPoint& point)
1324 m_multiPopup = false;
1326 m_popup = new ResPopup(view() , url);
1328 connect(m_popup, SIGNAL(hideChildPopup()), SLOT(slotHideChildPopup()));
1330 m_popup->setText(innerHTML);
1331 m_popup->adjustSize();
1332 m_popup->adjustPos(point);
1337 /*------------------------*/
1338 /* start multi-popup mode */ /* private */
1339 bool HTMLPart::startMultiPopup()
1342 if (m_popup && m_popup->isVisible()) {
1343 m_multiPopup = true;
1344 m_popup->moveMouseAbove();
1346 m_multiPopup = false;
1349 return m_multiPopup;
1353 /* Is it multi-popup mode now ? */ /* private */
1354 bool HTMLPart::isMultiPopupMode()
1357 m_multiPopup = false;
1358 } else if (m_popup->isHidden()) {
1359 m_multiPopup = false;
1362 return m_multiPopup;
1366 void HTMLPart::hidePopup()
1371 m_multiPopup = false;
1374 /* return true if this view is under mouse. */ /* private */
1375 bool HTMLPart::isUnderMouse(int mrgwd, int mrght)
1377 QPoint pos = QCursor::pos();
1378 int cx = pos.x(), cy = pos.y();
1380 QPoint viewpos = view() ->mapToGlobal(QPoint(0, 0));
1381 int px = viewpos.x(), py = viewpos.y();
1382 int wd = view() ->visibleWidth(), ht = view() ->visibleHeight();
1384 if ((px < cx && cx < px + wd + mrgwd)
1385 && (py < cy && cy < py + ht + mrght)) {
1393 void HTMLPart::slotLeave()
1395 if (isMultiPopupMode()) return ;
1396 if (view()->horizontalScrollBar()->isSliderDown()) return;
1397 if (view()->verticalScrollBar()->isSliderDown()) return;
1401 // emit signal to have parent hide this if this is popup.
1402 if (m_mode == HTMLPART_MODE_POPUP && !isUnderMouse(0, 0)) {
1403 emit hideChildPopup();
1408 void HTMLPart::slotVSliderReleased()
1410 QScrollBar * bar = view() ->verticalScrollBar();
1411 QRect rt = bar->style()->subControlRect(QStyle::CC_Slider, new QStyleOptionComplex(), QStyle::SC_SliderHandle);
1412 int mrg = rt.right() - rt.left();
1416 // emit signal to have parent hide this if this is popup.
1417 if (m_mode == HTMLPART_MODE_POPUP && !isUnderMouse(mrg, 0)) {
1418 emit hideChildPopup();
1424 void HTMLPart::slotHSliderReleased()
1426 QScrollBar * bar = view() ->horizontalScrollBar();
1427 QRect rt = bar->style()->subControlRect(QStyle::CC_Slider, new QStyleOptionComplex(), QStyle::SC_SliderHandle);
1428 int mrg = rt.bottom() - rt.top();
1432 // emit signal to have parent hide this if this is popup.
1433 if (m_mode == HTMLPART_MODE_POPUP && !isUnderMouse(0, mrg)) {
1434 emit hideChildPopup();
1441 void HTMLPart::slotHideChildPopup()
1445 // emit signal to have parent hide this if this is popup.
1446 if (m_mode == HTMLPART_MODE_POPUP && !isUnderMouse(0, 0)) {
1447 emit hideChildPopup();
1452 /*---------------------------------------------------*/
1453 /* This slot is called when mouse moves onto the URL */ /* private slot */
1454 void HTMLPart::slotOnUrl(const QString& url)
1458 const int maxpopup = 10; // max number of responses shown in the popup window
1460 //----------------------------
1462 if (isMultiPopupMode()) return ;
1466 if (url.isEmpty()) return ;
1467 if (url.left(7) == "mailto:") return ;
1469 // Is Kita active now?
1470 if(ViewMediator::getInstance()->isActive() == false) return;
1474 KUrl datUrl = m_datUrl;
1475 if (url.at(0) == '#') {
1476 refstr = url.mid(1);
1478 datUrl = getDatUrl(KUrl(m_datUrl, url) , refstr);
1481 DatManager datManager(m_datUrl);
1482 //------------------------
1485 if (url.left(6) == "#idpop") {
1486 int num = datManager.getNumById(url.mid(6));
1489 tmpstr = QString("<DIV>ID:%1:[%2]</DIV>").arg(url.mid(6)).arg(num);
1492 + i18nc("@label:textbox This is the first reply", "None")
1495 showPopup(m_datUrl, tmpstr);
1500 //------------------------
1501 // show reffered num
1503 if (refstr.left(5) == "write") {
1504 int no = refstr.mid(5).toInt();
1506 datManager.getTreeByRes(no, num);
1509 tmpstr = QString("<DIV>No.%1 : [%2]</DIV>").arg(no).arg(num);
1512 + i18nc("@label:textbox There is no reply", "None") + "</DIV>";
1514 showPopup(m_datUrl, tmpstr);
1519 //------------------------
1522 if (url.left(6) == "#abone") {
1523 int no = url.mid(6).toInt();
1524 QString tmpstr = datManager.getHtml(no, no, false);
1525 showPopup(m_datUrl, tmpstr);
1529 //-------------------------
1536 int i = refstr.indexOf("-");
1537 if (i != -1) { // >>refNum-refNum2
1539 refNum = refstr.left(i).toInt();
1540 refNum2 = refstr.mid(i + 1).toInt();
1543 if (refNum2 < refNum) refNum2 = refNum;
1544 if (refNum2 - refNum > maxpopup - 1) refNum2 = refNum + maxpopup - 1;
1547 } else { // >>refNum
1548 refNum = refstr.toInt();
1552 DatManager localDatManager(datUrl);
1554 if (datUrl.host() != m_datUrl.host() || datUrl.path() != m_datUrl.path()) {
1557 BoardDatabase db(datUrl);
1558 QString boardName = db.boardName();
1559 if (!boardName.isEmpty()) innerHTML += '[' + boardName + "] ";
1561 // If idx file of datURL is not read, thread name cannot be obtained.
1562 // so, create DatInfo if cache exists, and read idx file in DatInfo::DatInfo().
1563 localDatManager.getDatInfoPointer();
1566 QString subName = localDatManager.threadName();
1567 if (!subName.isEmpty()) innerHTML += subName + "<br><br>";
1569 if (!refNum) refNum = refNum2 = 1;
1572 // get HTML and show it
1573 if (!refNum) return ;
1574 innerHTML += localDatManager.getHtml(refNum, refNum2);
1576 if (!innerHTML.isEmpty())
1577 showPopup(datUrl, innerHTML);
1582 /* If selected Text is composed of only digits,
1583 then show res popup. */ /* private */
1584 bool HTMLPart::showSelectedDigitPopup()
1586 if (!hasSelection()) return false;
1590 QString selectText = selectedText();
1591 const QChar *chpt = selectText.unicode();
1592 unsigned int length = selectText.length();
1594 if ((refNum = stringToPositiveNum(chpt, length)) != -1) {
1595 QString innerHTML = DatManager(m_datUrl).getHtml(refNum, refNum);
1596 if (!innerHTML.isEmpty()) {
1597 showPopup(m_datUrl, innerHTML);