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 ?).
186 begin(KUrl("file:/dummy.htm"));
191 /*---------------------------------------------------------------*/
192 /*---------------------------------------------------------------*/
193 /* direct rendering functions */
197 * @warning don't forget to call updateScreen() later.
199 void HTMLPart::showResponses(int startnum, int endnum)
201 if (!m_domtree) return ;
203 for (int i = startnum ; i <= endnum; i++) m_domtree->appendRes(i);
207 /* do parsing only. */
208 /* call showResponses() later */ /* public */
209 void HTMLPart::parseResponses(int startnum, int endnum)
211 if (!m_domtree) return ;
213 for (int i = startnum ; i <= endnum; i++) m_domtree->createResElement(i);
217 /*------------------------------------------------*/
218 /* Show all responses ,header, footer, and etc,
219 if these are not shown. */
221 /* note that updateScreen() is called internally. */
222 /* So, you need not call it later. */ /* public slot */
223 void HTMLPart::showAll()
225 if (!m_domtree) return ;
227 int bottom = m_domtree->getBottomResNumber();
228 int readNum = DatManager(m_datUrl).getReadNum();
229 if (bottom != readNum) {
231 QCursor qc; qc.setShape(Qt::WaitCursor);
232 QApplication::setOverrideCursor(qc);
234 showResponses(1, readNum);
235 updateScreen(true, false);
237 QApplication::restoreOverrideCursor();
244 void HTMLPart::updateScreen(bool showHeaderEtc, bool clock)
251 /* show clock cursor */
253 QCursor qc; qc.setShape(Qt::WaitCursor);
254 QApplication::setOverrideCursor(qc);
257 /* show header, footer, and kokomadeyonda, etc. */
259 m_domtree->appendKokoyon();
260 m_domtree->appendFooterAndHeader();
263 /* change color of number of the res which is responsed. */
264 if (m_mode == HTMLPART_MODE_MAINPART) {
266 if (GlobalConfig::checkResponsed()) {
267 m_domtree->changeColorOfAllResponsedNumber();
272 htmlDocument().applyChanges();
274 view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
279 QApplication::restoreOverrideCursor();
285 void HTMLPart::setInnerHTML(const QString& innerHTML)
287 createHTMLDocument();
288 htmlDocument().body().setInnerHTML(innerHTML);
294 void HTMLPart::redrawHTMLPart(const KUrl& datUrl, bool force)
296 if (m_domtree == 0) return ;
297 if (m_datUrl != datUrl) return ;
299 m_domtree->redraw(force);
303 void HTMLPart::slotSetFaceOfHTMLPart()
305 QFont font = GlobalConfig::threadFont();
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();
316 /*---------------------------------------------------------------*/
317 /*---------------------------------------------------------------*/
318 /* cache functions */
321 /*----------------------------------*/
322 /* load thread from cache & show it */ /* public */
324 /* top = centerNum - preShowNum
325 bottom = centerNum + afterShowNum
326 readNum = DatManager::getReadNum
328 No.1 <- show -> No.20 <- not show -> No.(top) <- show -> No.(bottom) <- not show -> No.(readNum) */
330 bool HTMLPart::load(int centerNum)
332 m_centerNum = centerNum;
333 m_jumpNumAfterLoading = 0;
335 if (m_mode != HTMLPART_MODE_MAINPART)
339 DatManager datManager(m_datUrl);
340 if (datManager.getReadNum() == 0)
343 int endNum = datManager.getReadNum();
344 showResponses(1, endNum);
345 updateScreen(true , false);
346 gotoAnchor(QString::number(m_centerNum), false);
354 /*----------------------------*/
355 /* start reloading */
357 /* see also slotReceiveData()
358 and slotFinishLoad(). */ /* public */
359 bool HTMLPart::reload(int jumpNum)
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);
368 m_firstReceive = true;
369 if (m_centerNum == 0) m_centerNum = m_domtree->getBottomResNumber();
370 m_jumpNumAfterLoading = jumpNum;
372 /* DatManager will call back slotReceiveData and slotFinishLoad. */
373 DatManager(m_datUrl).updateCache(this);
381 /*---------------------------------------------*/
382 /* This slot is called after DatManager
383 received new data, and emits receiveData() */ /* !!! "public" slot !!! */
384 void HTMLPart::slotReceiveData()
386 const int delta = 20;
388 if (m_mode != HTMLPART_MODE_MAINPART) return ;
389 if (!m_domtree) return ;
391 int readNum = DatManager(m_datUrl).getReadNum();
392 int bottom = m_domtree->getBottomResNumber();
395 parseResponses(bottom + 1, readNum);
398 if (m_firstReceive || bottom + delta < readNum) {
399 showResponses(bottom + 1, readNum);
400 updateScreen(true, false);
403 if (m_firstReceive && m_centerNum < readNum) {
404 gotoAnchor(QString::number(m_centerNum), false);
405 m_firstReceive = false;
412 /*--------------------------------------------*/
413 /* This slot is called after DatManager
414 finished updating new data,
415 and emits finishReload() */ /* !!! "public" slot !!! */
416 void HTMLPart::slotFinishLoad()
418 if (m_mode != HTMLPART_MODE_MAINPART) return ;
419 if (!m_domtree) return ;
421 int bottom = m_domtree->getBottomResNumber();
422 int shownNum = m_centerNum + 5000;
424 showResponses(bottom + 1, shownNum);
425 updateScreen(true, false);
426 // m_domtree->parseAllRes();
429 if (m_jumpNumAfterLoading)
430 gotoAnchor(QString::number(m_jumpNumAfterLoading), false);
431 m_jumpNumAfterLoading = 0;
439 /*---------------------------------------------------------------*/
440 /*---------------------------------------------------------------*/
445 bool HTMLPart::gotoAnchor(const QString& anc, bool pushPosition)
447 if (anc.isEmpty()) return false;
448 if (!m_domtree || m_mode == HTMLPART_MODE_POPUP)
449 return KHTMLPart::gotoAnchor(anc);
453 QString ancstr = anc;
454 int res = ancstr.toInt();
458 /* is target valid ? */
459 if (!DatManager(m_datUrl).isResValid(res))
462 ancstr = QString::number(res);
465 if (res == 1) ancstr = "header";
466 if (pushPosition) pushCurrentPosition();
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
479 /* jump to kokomade yonda */ /* public slot */
480 void HTMLPart::slotGotoKokoyon()
482 if (!m_domtree) return ;
483 if (m_mode != HTMLPART_MODE_MAINPART) return ;
485 int kokoyon = DatManager(m_datUrl).getViewPos();
486 gotoAnchor(QString::number(kokoyon), false);
492 void HTMLPart::slotGobackAnchor()
494 if (m_anchorStack.isEmpty()) return ;
496 QString anc = m_anchorStack.last();
497 m_anchorStack.pop_back();
498 gotoAnchor(anc , false);
505 void HTMLPart::pushCurrentPosition()
507 m_anchorStack += getCurrentIdOfNode();
511 /* find the id of current node */ /* private */
512 QString HTMLPart::getCurrentIdOfNode()
515 node = nodeUnderMouse();
516 while (node != 0 && node.nodeName().string() != "div") node = node.parentNode();
517 if (node == 0) return QString();
519 return static_cast<DOM::Element>(node).getAttribute("id").string();
523 void HTMLPart::slotClickGotoFooter()
525 if (!m_domtree || m_mode != HTMLPART_MODE_MAINPART) {
526 gotoAnchor("footer", false);
530 int bottom = m_domtree->getBottomResNumber();
531 int readNum = DatManager(m_datUrl).getReadNum();
533 if (readNum != bottom) {
534 showResponses(bottom + 1, readNum);
535 updateScreen(true, true);
538 gotoAnchor("footer", false);
542 /*---------------------------------------------------------------*/
543 /*---------------------------------------------------------------*/
548 void HTMLPart::findTextInit()
558 bool HTMLPart::findText(const QString &query, bool reverse)
560 if (m_mode != HTMLPART_MODE_MAINPART) return false;
562 QRegExp regexp(query);
563 regexp.setCaseSensitivity(Qt::CaseInsensitive);
566 if (m_findNode.isNull()) {
568 m_findNode = htmlDocument().body();
571 /* move to the last child node */
573 while (!m_findNode.lastChild().isNull()) m_findNode = m_findNode.lastChild();
574 m_find_y = view() ->contentsHeight();
580 if (m_findNode.nodeType() == DOM::Node::TEXT_NODE
581 || m_findNode.nodeType() == DOM::Node::CDATA_SECTION_NODE) {
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);
588 if (reverse) m_findPos = nodestr.lastIndexOf(regexp, m_findPos);
589 else m_findPos = nodestr.indexOf(regexp, m_findPos + 1);
591 /* scroll & select & return */
592 if (m_findPos != -1) {
593 int matchLen = regexp.matchedLength();
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);
603 } else if (m_findNode.nodeName().string() == "table") {
605 QRect qr = m_findNode.getRect();
607 m_find_y = qr.bottom();
609 } else if (m_findNode.nodeName().string() == "div") {
611 QRect qr = m_findNode.getRect();
614 m_find_y = qr.bottom();
619 } else if (m_findNode.nodeName().string() == "br") {
621 DOM::Node tmpnode = m_findNode.previousSibling();
625 QRect qr = tmpnode.getRect();
626 if (reverse) m_find_y -= qr.bottom() - qr.top();
627 else m_find_y += qr.bottom() - qr.top();
631 /*------------------------*/
636 /* move to the next node */
639 next = m_findNode.firstChild();
640 if (next.isNull()) next = m_findNode.nextSibling();
642 while (!m_findNode.isNull() && next.isNull()) {
643 m_findNode = m_findNode.parentNode();
644 if (!m_findNode.isNull()) {
645 next = m_findNode.nextSibling();
652 next = m_findNode.lastChild();
653 if (next.isNull()) next = m_findNode.previousSibling();
655 while (!m_findNode.isNull() && next.isNull()) {
656 m_findNode = m_findNode.parentNode();
657 if (!m_findNode.isNull()) {
658 next = m_findNode.previousSibling();
664 if (m_findNode.isNull()) {
678 /*---------------------------------------------------------------*/
679 /*---------------------------------------------------------------*/
684 void HTMLPart::showPopupMenu(const KUrl& kurl)
686 QString url = kurl.prettyUrl();
687 bool showppm = false;
691 /* If selected Text is composed of only digits, then show res popup. */
692 if (!m_pushctrl && showSelectedDigitPopup()) return ;
694 /*-----------------------------------*/
695 /* create menu items */
697 KMenu popupMenu(view());
698 KMenu* backSubMenu = 0;
699 KMenu* markSubMenu = 0;
703 KAction* homeLinkAct = 0;
704 KAction* kokoLinkAct = 0;
705 KAction* endLinkAct = 0;
707 (m_mode == HTMLPART_MODE_MAINPART)) {
711 DatManager datManager(m_datUrl);
713 if (!m_anchorStack.isEmpty()) {
714 backSubMenu = new KMenu(view());
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);
723 backSubMenu->setTitle(
724 i18nc("@title:menu Go back to the responses", "Back"));
725 popupMenu.addMenu(backSubMenu);
726 popupMenu.addSeparator();
730 int readNum = datManager.getReadNum();
731 for (int i = 1; i <= readNum ; i++) {
732 if (datManager.isMarked(i)) {
734 markSubMenu = new KMenu(view());
735 markSubMenu->setTitle(i18nc("@title:menu", "Mark"));
736 popupMenu.addMenu(markSubMenu);
737 popupMenu.addSeparator();
739 str = QString::number(i) + " "
740 + datManager.getPlainBody(i).left(10);
741 KAction *gotoMarkAct = new KAction(str, this);
742 markSubMenu->addAction(gotoMarkAct);
747 homeLinkAct = new KAction(
748 i18nc("@action:inmenu Go to the original post", "Start"), this);
749 popupMenu.addAction(homeLinkAct);
752 if (m_mode == HTMLPART_MODE_MAINPART) {
753 int kokoyon = datManager.getViewPos();
755 str = i18nc("@action:inmenu", "Kokomade Yonda (%1)", kokoyon);
756 kokoLinkAct = new KAction(str, this);
757 popupMenu.addAction(kokoLinkAct);
761 endLinkAct = new KAction(i18nc("@action:inmenu", "End"), this);
762 popupMenu.addAction(endLinkAct);
766 KAction* copyStrAct = 0;
767 KAction* aboneWordAct = 0;
768 if (hasSelection()) {
769 if (showppm) popupMenu.addSeparator();
772 copyStrAct = new KAction(i18nc("@action:inmenu", "Copy"), this);
773 popupMenu.addAction(copyStrAct);
776 QString selectedStr = selectedText();
777 if (selectedStr.length() > 20) {
778 selectedStr.truncate(20);
779 selectedStr.append("...");
782 aboneWordAct = new KAction(i18nc("@action:inmenu",
783 "Add '%1' to abone list", selectedStr), this);
784 popupMenu.addAction(aboneWordAct);
789 KAction* openBrowserAct = 0;
790 KAction* copyLinkAct = 0;
791 if (!url.isEmpty()) {
792 if (showppm) popupMenu.addSeparator();
795 openBrowserAct= new KAction(i18nc("@action:inmenu",
796 "Open with Web Browser"), this);
797 popupMenu.addAction(openBrowserAct);
798 popupMenu.addSeparator();
800 copyLinkAct = new KAction(i18nc("@action:inmenu",
801 "Copy Link Location"), this);
802 popupMenu.addAction(copyLinkAct);
809 QClipboard * clipboard = QApplication::clipboard();
811 QAction* action = popupMenu.exec(QCursor::pos());
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) {
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);
838 QMenu* menu = action->menu();
844 if (menu == markSubMenu) {
845 gotoAnchor(QString::number(menu->actions().indexOf(action)),
847 } else if (menu == backSubMenu) {
848 for (int i = 0; i < menu->actions().indexOf(action); i++)
849 m_anchorStack.pop_back();
858 /*---------------------------------------------------------------*/
859 /*---------------------------------------------------------------*/
863 /* protected */ /* virtual */
864 void HTMLPart::customEvent(QEvent * e)
866 if (e->type() == EVENT_GotoAnchor) {
867 KHTMLPart::gotoAnchor(static_cast< GotoAnchorEvent* >(e) ->getAnc());
871 KHTMLPart::customEvent(e);
875 /*---------------------------------------------------------------*/
876 /*---------------------------------------------------------------*/
881 void HTMLPart::khtmlMousePressEvent(khtml::MousePressEvent* e)
883 emit mousePressed(); /* to ThreadView to focus this view. */
886 if (!e->url().string().isEmpty()) {
887 BoardDatabase db(m_datUrl);
888 kurl = KUrl(db.boardUrl(), e->url().string());
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;
898 if (e->url().string().at(0) == '#') { /* anchor */
900 kurl.setRef(e->url().string().mid(1)) ;
904 m_pushctrl = m_pushmidbt = m_pushrightbt = false;
911 m_pushctrl = m_pushmidbt = m_pushrightbt = false;
915 KHTMLPart::khtmlMousePressEvent(e);
922 /*-------------------------------------------------------*/
923 /*-------------------------------------------------------*/
929 void HTMLPart::slotOpenUrlRequest(const KUrl& urlin, const KParts::OpenUrlArguments&)
935 /*------------------------------------------------------*/
936 /* This function is called when user clicked res anchor */ /* private */
937 void HTMLPart::clickAnchor(const KUrl& urlin)
940 KUrl datUrl = getDatUrl(urlin , refstr);
942 /*--------------------*/
943 /* Ctrl + right click */
944 if (m_pushctrl && m_pushrightbt) {
945 showPopupMenu(urlin);
949 /*--------------------------------*/
950 /* If this is not anchor, then */
951 /* emit openURLRequest and return */
953 if (datUrl.host() != m_datUrl.host() || datUrl.path() != m_datUrl.path()) {
958 /* start multi-popup mode or show popup menu */
959 if (!startMultiPopup()) showPopupMenu(urlin);
965 ViewMediator::getInstance()->openUrl(urlin);
969 if (refstr.isEmpty()) return ;
971 /*---------------------------*/
972 /* show popupmenu for #write */
974 if (refstr.left(5) == "write") {
975 showWritePopupMenu(refstr);
979 /*----------------------------*/
980 /* extract responses by ID */
982 if (refstr.left(5) == "idpop") {
987 /*---------------------------*/
988 /* show popupmenu for #bepop */
990 if (refstr.left(5) == "bepop") {
991 showBePopupMenu(refstr);
995 /*-------------------------*/
996 /* start multi-popup mdde */
997 if (m_pushrightbt && startMultiPopup()) return ;
1000 /*----------------------------*/
1001 /* next 100 ,before 100 ,etc. */
1002 if (m_mode == HTMLPART_MODE_MAINPART) {
1003 if (refstr.left(7) == "tosaigo") {
1004 slotClickGotoFooter();
1009 /*-------------------------------*/
1010 /* open Kita Navi or goto anchor */
1012 int refNum, refNum2;
1014 int i = refstr.indexOf("-");
1016 refNum = refstr.left(i).toInt();
1017 refNum2 = refstr.mid(i + 1).toInt();
1018 if (refNum2 < refNum) {
1022 refNum = refNum2 = refstr.toInt();
1025 if (!refNum) return ;
1027 if (m_mode == HTMLPART_MODE_POPUP) {
1028 ViewMediator::getInstance()->openUrl(urlin);
1030 gotoAnchor(QString::number(refNum), true);
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)
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);
1048 if (m_pushrightbt) {
1050 QString htmlstr = datManager.getTreeByRes(resNum, num);
1052 QString tmpstr = QString("<DIV>No.%1 : [%2]<BR>").arg(resNum).arg(num);
1053 tmpstr += htmlstr + "<BR><BR></DIV>";
1054 showPopup(m_datUrl, tmpstr);
1059 /*---------------------*/
1060 /* create popup menu */
1063 KMenu popupMenu(view());
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);
1071 quoteAct = new KAction(i18nc("@action:inmenu", "Quote This"), this);
1072 popupMenu.addAction(quoteAct);
1074 popupMenu.addSeparator();
1078 KAction* markAct = new KAction(i18nc("@action:inmenu", "Mark"), this);
1079 markAct->setCheckable(true);
1080 markAct->setChecked(datManager.isMarked(resNum));
1081 popupMenu.addAction(markAct);
1083 popupMenu.addSeparator();
1086 KAction* showBrowserAct
1087 = new KAction(i18nc("@action:inmenu", "Open with Web Browser"), this);
1088 popupMenu.addAction(showBrowserAct);
1090 popupMenu.addSeparator();
1094 = new KAction(i18nc("@action:inmenu", "Res Tree"), this);
1095 popupMenu.addAction(resTreeAct);
1097 KAction* reverseResTreeAct
1098 = new KAction(i18nc("@action:inmenu", "Reverse Res Tree"), this);
1099 popupMenu.addAction(reverseResTreeAct);
1101 KAction* extractNameAct
1102 = new KAction(i18nc("@action:inmenu", "Extract by Name"), this);
1103 popupMenu.addAction(extractNameAct);
1105 popupMenu.addSeparator();
1109 = new KAction(i18nc("@action:inmenu", "Copy URL"), this);
1110 popupMenu.addAction(copyUrlAct);
1112 KAction* copyThreadNameAct
1113 = new KAction(i18nc("@action:inmenu", "Copy Title and URL"), this);
1114 popupMenu.addAction(copyThreadNameAct);
1116 KAction* copyAct = new KAction(i18nc("@action:inmenu", "Copy"), this);
1117 popupMenu.addAction(copyAct);
1120 KAction* setKokoYonAct = 0;
1121 if (m_domtree && m_mode == HTMLPART_MODE_MAINPART) {
1122 popupMenu.addSeparator();
1125 = new KAction(i18nc("@action:inmenu", "Set Kokomade Yonda"), this);
1126 popupMenu.addAction(setKokoYonAct);
1130 popupMenu.addSeparator();
1132 KAction* aboneNameAct
1133 = new KAction(i18nc("@action:inmenu", "Add Name to Abone List"), this);
1134 popupMenu.addAction(aboneNameAct);
1138 QAction* action = popupMenu.exec(QCursor::pos());
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) {
1156 if (action == copyThreadNameAct || action == copyAct) {
1157 str = datManager.threadName();
1161 if (!str.isEmpty()) str += '\n';
1162 str += datManager.threadUrl() + '/' + QString::number(resNum)
1166 if (action == copyAct) {
1168 + datManager.getPlainTitle(resNum) + '\n'
1169 + datManager.getPlainBody(resNum) + '\n';
1173 clipboard->setText(str , QClipboard::Clipboard);
1174 clipboard->setText(str , QClipboard::Selection);
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);
1183 } else if (action == showBrowserAct) {
1184 str = datManager.threadUrl() + '/' + QString::number(resNum);
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);
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)
1204 QString strid = refstr.mid(5)
1205 .replace("%2B", "+")
1206 .replace("%2F", "/");
1209 if (m_pushrightbt) {
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);
1220 KMenu popupMenu(view());
1222 KAction* aboneAct = new KAction(i18nc("@action:inmenu",
1223 "Add Id to Abone List"), this);
1224 popupMenu.addAction(aboneAct);
1226 QAction* action = popupMenu.exec(QCursor::pos());
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);
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)
1249 QString strUrl = "http://be.2ch.net/test/p.php?i=" + refstr.mid(5)
1250 + "&u=d:" + DatManager(m_datUrl).threadUrl() + "/l50";
1252 if (m_pushrightbt) {
1253 // create popup menu
1254 KMenu popupMenu(view());
1256 QClipboard * clipboard = QApplication::clipboard();
1259 = new KAction(i18nc("@action:inmenu", "copy URL"), this);
1260 popupMenu.addAction(copyUrlAct);
1262 KAction* showBrowserAct = new KAction(i18nc("@action:inmenu",
1263 "Open with Web Browser"), this);
1266 QAction* action = popupMenu.exec(QCursor::pos());
1270 if (action == copyUrlAct) {
1272 clipboard->setText(strUrl, QClipboard::Clipboard);
1273 clipboard->setText(strUrl, QClipboard::Selection);
1274 } else if (action == showBrowserAct) {
1275 KRun::runUrl(strUrl, "text/html", view());
1278 KRun::runUrl(strUrl, "text/html", 0);
1282 /*-------------------------------------------------------*/
1283 /*-------------------------------------------------------*/
1288 bool HTMLPart::isPopupVisible()
1290 if (!m_popup) return false;
1291 return m_popup->isVisible();
1296 void HTMLPart::slotDeletePopup()
1300 m_multiPopup = false;
1304 /* for convenience */ /* public slot */
1305 void HTMLPart::slotShowResPopup(const QPoint& point, int refNum, int refNum2)
1307 QString innerHTML = DatManager(m_datUrl).getHtml(refNum, refNum2);
1308 if (innerHTML.isEmpty()) return ;
1310 showPopupCore(m_datUrl, innerHTML, point);
1314 /* for convenience */ /* private */
1315 void HTMLPart::showPopup(const KUrl& url, const QString& innerHTML)
1317 showPopupCore(url, innerHTML, QCursor::pos());
1321 /* show popup window */ /* private */
1322 void HTMLPart::showPopupCore(const KUrl& url, const QString& innerHTML, const QPoint& point)
1325 m_multiPopup = false;
1327 m_popup = new ResPopup(view() , url);
1329 connect(m_popup, SIGNAL(hideChildPopup()), SLOT(slotHideChildPopup()));
1331 m_popup->setText(innerHTML);
1332 m_popup->adjustSize();
1333 m_popup->adjustPos(point);
1338 /*------------------------*/
1339 /* start multi-popup mode */ /* private */
1340 bool HTMLPart::startMultiPopup()
1343 if (m_popup && m_popup->isVisible()) {
1344 m_multiPopup = true;
1345 m_popup->moveMouseAbove();
1347 m_multiPopup = false;
1350 return m_multiPopup;
1354 /* Is it multi-popup mode now ? */ /* private */
1355 bool HTMLPart::isMultiPopupMode()
1358 m_multiPopup = false;
1359 } else if (m_popup->isHidden()) {
1360 m_multiPopup = false;
1363 return m_multiPopup;
1367 void HTMLPart::hidePopup()
1372 m_multiPopup = false;
1375 /* return true if this view is under mouse. */ /* private */
1376 bool HTMLPart::isUnderMouse(int mrgwd, int mrght)
1378 QPoint pos = QCursor::pos();
1379 int cx = pos.x(), cy = pos.y();
1381 QPoint viewpos = view() ->mapToGlobal(QPoint(0, 0));
1382 int px = viewpos.x(), py = viewpos.y();
1383 int wd = view() ->visibleWidth(), ht = view() ->visibleHeight();
1385 if ((px < cx && cx < px + wd + mrgwd)
1386 && (py < cy && cy < py + ht + mrght)) {
1394 void HTMLPart::slotLeave()
1396 if (isMultiPopupMode()) return ;
1397 if (view()->horizontalScrollBar()->isSliderDown()) return;
1398 if (view()->verticalScrollBar()->isSliderDown()) return;
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();
1409 void HTMLPart::slotVSliderReleased()
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();
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();
1425 void HTMLPart::slotHSliderReleased()
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();
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();
1442 void HTMLPart::slotHideChildPopup()
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();
1453 /*---------------------------------------------------*/
1454 /* This slot is called when mouse moves onto the URL */ /* private slot */
1455 void HTMLPart::slotOnUrl(const QString& url)
1459 const int maxpopup = 10; /* max number of responses shown in the popup window */
1461 /*----------------------------*/
1463 if (isMultiPopupMode()) return ;
1467 if (url.isEmpty()) return ;
1468 if (url.left(7) == "mailto:") return ;
1470 /* Is Kita active now ? */
1471 if(ViewMediator::getInstance()->isActive() == false) return;
1475 KUrl datUrl = m_datUrl;
1476 if (url.at(0) == '#') {
1477 refstr = url.mid(1);
1479 datUrl = getDatUrl(KUrl(m_datUrl, url) , refstr);
1482 DatManager datManager(m_datUrl);
1483 /*------------------------*/
1486 if (url.left(6) == "#idpop") {
1487 int num = datManager.getNumById(url.mid(6));
1490 tmpstr = QString("<DIV>ID:%1:[%2]</DIV>").arg(url.mid(6)).arg(num);
1493 + i18nc("@label:textbox This is the first reply", "None")
1496 showPopup(m_datUrl, tmpstr);
1501 /*------------------------*/
1502 /* show reffered num */
1504 if (refstr.left(5) == "write") {
1505 int no = refstr.mid(5).toInt();
1507 datManager.getTreeByRes(no, num);
1510 tmpstr = QString("<DIV>No.%1 : [%2]</DIV>").arg(no).arg(num);
1513 + i18nc("@label:textbox There is no reply", "None") + "</DIV>";
1515 showPopup(m_datUrl, tmpstr);
1520 /*------------------------*/
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);
1530 /*-------------------------*/
1531 /* popup for anchor */
1537 int i = refstr.indexOf("-");
1538 if (i != -1) { /* >>refNum-refNum2 */
1540 refNum = refstr.left(i).toInt();
1541 refNum2 = refstr.mid(i + 1).toInt();
1544 if (refNum2 < refNum) refNum2 = refNum;
1545 if (refNum2 - refNum > maxpopup - 1) refNum2 = refNum + maxpopup - 1;
1548 } else { /* >>refNum */
1549 refNum = refstr.toInt();
1553 DatManager localDatManager(datUrl);
1554 /* another thread ? */
1555 if (datUrl.host() != m_datUrl.host() || datUrl.path() != m_datUrl.path()) {
1557 /* get board name */
1558 BoardDatabase db(datUrl);
1559 QString boardName = db.boardName();
1560 if (!boardName.isEmpty()) innerHTML += '[' + boardName + "] ";
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();
1566 /* get thread Name */
1567 QString subName = localDatManager.threadName();
1568 if (!subName.isEmpty()) innerHTML += subName + "<br><br>";
1570 if (!refNum) refNum = refNum2 = 1;
1573 /* get HTML and show it */
1574 if (!refNum) return ;
1575 innerHTML += localDatManager.getHtml(refNum, refNum2);
1577 if (!innerHTML.isEmpty())
1578 showPopup(datUrl, innerHTML);
1583 /* If selected Text is composed of only digits,
1584 then show res popup. */ /* private */
1585 bool HTMLPart::showSelectedDigitPopup()
1587 if (!hasSelection()) return false;
1591 QString selectText = selectedText();
1592 const QChar *chpt = selectText.unicode();
1593 unsigned int length = selectText.length();
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);