1 /***************************************************************************
2 * Copyright (C) 2003 by Hideki Ikemoto *
3 * ikemo@users.sourceforge.jp *
4 * linuxÈÄ ¥³¥ó¥«¥é¤×¤é¤°¤¤¤ó¤Î¿Í *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 ***************************************************************************/
12 #include "kitathreadview.h"
15 #include <khtmlview.h>
16 #include <kiconloader.h>
19 #include <kmessagebox.h>
22 #include <dom/html_inline.h>
23 #include <dom/html_base.h>
24 #include <dom/html_list.h>
27 #include <qtoolbutton.h>
29 #include <qcombobox.h>
30 #include <qprogressdialog.h>
32 #include <qtextbrowser.h>
33 #include <qmessagebox.h>
34 #include <qdatetime.h>
36 #include "kitahtmlpart.h"
37 #include "kitawritedialog.h"
38 #include "../kitacacheinfo.h"
41 #include "libkita/comment.h"
42 #include "libkita/qcp932codec.h"
43 #include "libkita/favoritethreads.h"
45 #define MAX_LABEL_LENGTH 60
47 static const char* cookie_title = "½ñ¤¹þ¤ß³Îǧ";
48 static const char* cookie_message =
50 "¡¦Åê¹Æ¤µ¤ì¤¿ÆâÍƤϥ³¥Ô¡¼¡¢Êݸ¡¢°úÍÑ¡¢Å¾ºÜÅù¤µ¤ì¤ë¾ì¹ç¤¬¤¢¤ê¤Þ¤¹¡£\n"
51 "¡¦Åê¹Æ¤Ë´Ø¤·¤ÆȯÀ¸¤¹¤ëÀÕǤ¤ÏÁ´¤ÆÅê¹Æ¼Ô¤Ëµ¢¤·¤Þ¤¹¡£\n"
53 "Á´ÀÕǤ¤òÉ餦¤³¤È¤ò¾µÂú¤·¤Æ½ñ¤¹þ¤ß¤Þ¤¹¤«¡©\n";
55 static const char* kokomade_yonda = "<p style=\"background-color: #CCCCCC; text-align: center\">"
56 "¨¡¨¡¨¡¨¡¨¡¨¡¨¡¨¡¨¡¨¡¤³¤³¤Þ¤ÇÆɤó¤À¨¡¨¡¨¡¨¡¨¡¨¡¨¡¨¡¨¡¨¡"
59 KitaThreadView::KitaThreadView(QWidget *parent, const char *name)
60 : KitaThreadViewBase(parent, name)
65 m_threadPart = new KitaHTMLPart(threadFrame);
66 QHBoxLayout* aLayout = new QHBoxLayout(threadFrame);
67 aLayout->addWidget(m_threadPart->view());
69 m_threadPart->setZoomFactor(120); // XXX
71 SearchButton->setPixmap( SmallIcon("find") );
72 HighLightButton->setPixmap( SmallIcon("idea") );
73 ReloadButton->setPixmap( SmallIcon("reload") );
74 GobackAnchorButton->setPixmap( SmallIcon("2leftarrow") );
75 BookmarkButton->setPixmap( SmallIcon("bookmark_add") );
78 setAcceptDrops( true ); // DND Drop eneble: 2nd stage. - enable on "KitaThreadView" widget and disable on the others(child widgets of "KitaThreadView").
79 threadFrame->setAcceptDrops( false ); // don't treat Drop event on child.
80 m_threadPart->view()->setAcceptDrops( false ); // don't treat Drop event on child.
82 m_threadPart->enableMetaRefresh( false ); //disable <meta refresh="...">
85 connect( writeButton, SIGNAL(clicked()), SLOT(slotWriteButtonClicked()));
86 connect( m_threadPart, SIGNAL( nodeActivated(const DOM::Node &) ), SLOT( slotDOMNodeActivated(const DOM::Node &) ) );
87 connect( m_threadPart, SIGNAL( onURL(const QString&) ), SLOT( slotOnURL(const QString&) ) );
88 connect( m_threadPart, SIGNAL( setLocationBarURL(const QString &) ),
89 SIGNAL( setLocationBarURL(const QString &) ) );
90 connect( BookmarkButton, SIGNAL( toggled(bool) ), SLOT( slotBookmarkButtonClicked(bool) ) );
91 connect( SearchButton, SIGNAL( clicked() ), SLOT( slotSearchButton() ) );
92 connect( SearchCombo, SIGNAL( activated(int) ), SLOT( slotSearchButton() ) );
93 connect( HighLightButton, SIGNAL( toggled(bool) ), SLOT( slotHighLightenButton(bool) ) );
94 connect( GobackAnchorButton, SIGNAL( clicked() ), m_threadPart, SLOT( gobackAnchor() ) );
95 connect( ReloadButton, SIGNAL( clicked() ), SLOT( slotReloadButton() ) );
97 KParts::BrowserExtension * ext = m_threadPart->browserExtension();
98 connect( ext, SIGNAL( openURLRequest(const KURL&, const KParts::URLArgs&) ),
99 SLOT( slotOpenURLRequest(const KURL&, const KParts::URLArgs&) ) );
100 connect( ext, SIGNAL( createNewWindow (const KURL&, const KParts::URLArgs&) ),
101 SIGNAL( createNewWindow (const KURL&, const KParts::URLArgs&) ) );
102 connect( ext, SIGNAL( setLocationBarURL(const QString &) ),
103 SIGNAL( setLocationBarURL(const QString &) ) );
104 connect( ext, SIGNAL( enableAction(const char*, bool) ),
105 SIGNAL( enableAction(const char*, bool) ) );
107 connect( ext, SIGNAL( popupMenu(KXMLGUIClient *, const QPoint&, const KURL&, const QString&, mode_t) ),
108 this, SLOT( slotPopupMenu(KXMLGUIClient *, const QPoint&, const KURL&, const QString&, mode_t) ) );
112 KitaThreadView::~KitaThreadView() {}
114 const QString KitaThreadView::threadName() const
116 return m_thread.name();
119 const KURL KitaThreadView::threadURL() const
121 return m_thread.url();
124 void KitaThreadView::slotDOMNodeActivated(const DOM::Node &node)
126 { //process Anchor tags. Anchor tags not proccessed here cause 'emit KParts::BrowserExtention::openURLRequest()'
127 DOM::HTMLAnchorElement anchor = node;
129 if ( ! anchor.href().isEmpty() )
131 kdDebug() << "AnchorNodeActivated::" << endl;
132 } // end: anchor.href().isEmpty()
133 } // end of Anchor tags.
136 void KitaThreadView::showThread( const Kita::Thread& thread )
138 { //reset member variables associated with a thread.
139 m_threadPart->reset();
144 m_access = new Kita::ThreadAccess( thread );
145 connect( m_access, SIGNAL( redirection( const QString& ) ), SIGNAL( setLocationBarURL( const QString& ) ) );
146 QString result = m_access->get();
147 m_serverTime = getServerTimeFromHttpHeaders( m_access->getHeader() );
151 int KitaThreadView::getServerTimeFromHttpHeaders( const QString& headers )
153 // parse HTTP headers
154 QStringList headerList = QStringList::split("\n", headers);
155 QRegExp regexp("Date: (...), (..) (...) (....) (..:..:..) .*");
156 QString dateStr = headerList.grep(regexp)[0];
157 if(regexp.search(dateStr) == -1) {
158 // invalid date format
159 return QDateTime::currentDateTime().toTime_t();
161 // I hate this format ;p
162 QString usLocalDateStr = regexp.cap(1) + " " + regexp.cap(3) + " " +
163 regexp.cap(2) + " " + regexp.cap(5) + " " + regexp.cap(4);
165 // 1970/01/01 00:00:00 GMT
166 QDateTime zeroTime(QDate(1970, 1, 1), QTime(0, 0));
167 return zeroTime.secsTo(QDateTime::fromString(usLocalDateStr));
171 void KitaThreadView::setSubjectLabel(const QString& boardName, const QString& threadName)
174 if ( boardName.isEmpty() ) {
177 disp = QString("[%1] %2").arg(boardName).arg(threadName);
180 disp.truncate( MAX_LABEL_LENGTH );
181 subjectLabel->setText( disp );
184 void KitaThreadView::update( const QString& result )
186 QStringList lines = QStringList::split( "\n", result );
190 int total = result.length();
192 int divide = total / ( 100 / 5 );
194 int prevResNum = m_thread.resNum();
195 QProgressDialog * progress = new QProgressDialog( m_parent );
196 progress->setTotalSteps( static_cast<int>(total * 0.7) );
197 progress->setLabelText( "Parse DAT file.....");
200 text += "<html><head>";
201 text += "</head><body>";
203 for( QStringList::iterator it = lines.begin(); it != lines.end(); ++it ) {
204 QString line = (*it);
206 if( line.isEmpty() ) {
210 Kita::Comment comment(line);
214 m_thread.setName(comment.getSubject());
216 text += comment.toHtml(num);
218 if( prevResNum == num ) {
219 text += QString::fromLocal8Bit( kokomade_yonda );
222 if( (step+=line.length()) > next ) {
223 progress->setProgress( step );
229 m_thread.setResNum(num);
230 emit thread( m_thread );
232 setSubjectLabel( m_thread.boardName(), m_thread.name() );
234 text += footer( prevResNum );
235 text += "</body></html>";
237 m_threadPart->displayContentsAndGotoURL( text, m_thread );
240 emit showThreadCompleted( m_thread.url() );
243 void KitaThreadView::updateButton()
245 writeButton->setEnabled( true );
246 BookmarkButton->setEnabled( true );
247 ReloadButton->setEnabled( true );
249 if ( HighLightButton->isOn() ) {
250 HighLightButton->toggle();
252 if( FavoriteThreads::getInstance()->contains( m_thread.datURL().url() ) ) {
253 BookmarkButton->setOn( true );
255 BookmarkButton->setOn( false );
259 const QString KitaThreadView::footer( int prevResNum ) const
263 for ( target = 1; target < m_thread.resNum(); target += 100 ) {
264 text += QString(" <a href=\"#%1\">%3</a> ").arg(target).arg(target);
266 text += QString(" <a href=\"#%1\">New</a> ").arg( prevResNum + 1 );
272 void KitaThreadView::slotShowErrorDialog( const QString& input, const KURL& )
274 kdDebug() << "'" << input << "'" << endl;
275 Kita::WriteResult writeResult(input);
277 kdDebug() << "code = " << writeResult.code() << endl;
278 switch( writeResult.code() ) {
279 case Kita::K2ch_Unknown:
281 emit writeSucceeded();
284 case Kita::K2ch_True:
285 case Kita::K2ch_False:
286 case Kita::K2ch_Error:
287 case Kita::K2ch_Check:
288 KMessageBox::error(0, writeResult.message(), writeResult.title());
290 case Kita::K2ch_Cookie:
291 if( KMessageBox::questionYesNo(0, QString::fromLocal8Bit(cookie_message),
292 QString::fromLocal8Bit(cookie_title)) == KMessageBox::Yes) {
293 KitaWriteDialog* dialog = openDialog( m_postInfo );
294 dialog->postMessage();
296 KitaWriteDialog* dialog = openDialog( m_postInfo );
305 KitaWriteDialog* KitaThreadView::openDialog( const Kita::PostInfo& info )
307 KitaWriteDialog* new_dialog = KitaWriteDialog::open( info, m_thread.boardName(), m_thread.name() );
308 connect( new_dialog, SIGNAL( postStarted( KIO::Job *, const Kita::PostInfo& ) ),
309 this, SLOT( slotPostStarted( KIO::Job*, const Kita::PostInfo&) ) );
310 connect( new_dialog, SIGNAL( postResponse(const QString&, const KURL&) ),
311 this, SLOT( slotShowErrorDialog(const QString&, const KURL&) ) );
312 connect( new_dialog, SIGNAL( postResponse(const QString&, const KURL&) ),
313 this, SIGNAL( postResponse(const QString&, const KURL&) ) );
317 void KitaThreadView::slotWriteButtonClicked()
319 // QSjisCodec cp932Codec;
320 KURL bbscgiURL = KURL( m_thread.boardUrl(), "../test/bbs.cgi");
321 bbscgiURL.setProtocol( "http" );
324 info.host = bbscgiURL.host();
325 info.bbs = m_thread.boardId();
326 info.key = m_thread.datID();
327 info.time = QString("%1").arg(m_serverTime);
329 KitaWriteDialog* dialog = openDialog( info );
330 dialog->show(); // work asynchronus.
333 void KitaThreadView::slotSearchButton()
336 QStringList list = parseSearchQuery( SearchCombo->currentText() );
340 void KitaThreadView::slotHighLightenButton(bool yes)
343 QStringList list = parseSearchQuery( SearchCombo->currentText() );
344 m_threadPart->highLighten( yes, list );
347 void KitaThreadView::insertSearchCombo()
349 for( int count = 0; count < SearchCombo->count(); ++count ) {
350 if ( SearchCombo->text( count ) == SearchCombo->currentText() ) {
355 SearchCombo->insertItem( SearchCombo->currentText() );
358 QStringList KitaThreadView::parseSearchQuery(const QString &input) const
360 QStringList tmp = QStringList::split( ' ', input );
361 QStringList ret_list;
362 QRegExp truncSpace("\\s*$");
363 QStringList::iterator it = tmp.begin();
364 for( ; it != tmp.end(); ++it )
365 ret_list += (*it).replace( truncSpace, "" );
369 void KitaThreadView::searchNext(const QStringList &query)
371 if ( query.isEmpty() ) return;
372 if ( ! HighLightButton->isOn() ) {
373 HighLightButton->toggle();
374 m_threadPart->resetHit(); //A next jump-search target reset to '0'.
375 // Process works asynchronusly. So Firstly, we don't do jump-search as a simple solution.
378 if ( query != m_threadPart->prevQuery() ) {
379 m_threadPart->highLighten( true, query );
380 m_threadPart->resetHit(); //A next jump-search target reset to '0'.
384 m_threadPart->gotoAnchor( QString("highlighten%1").arg( m_threadPart->nextHit() ) );
387 void KitaThreadView::slotOnURL(const QString& url)
389 emit signalChangeStatusbar(url);
391 // TODO: enterEvent()¤ò¼ÂÁõ¤·¤Æ¡¢Æþ¤Ã¤Æ¤¤¿¤È¤¤Ëtip¤òɽ¼¨¤¹¤ë¤³¤È¡£
392 if ( ! url.isEmpty() ) {
393 KHTMLView* view = m_threadPart->view();
399 m_popup = new QFrame( view, "res_popup", WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WType_TopLevel );
400 m_browser = new QTextBrowser( m_popup );
401 m_browser->zoomOut( static_cast<int>( m_browser->pointSize() * 0.3 ) );
402 m_browser->setPaletteBackgroundColor( "yellow" );
403 m_browser->setWordWrap( QTextEdit::NoWrap );
404 m_browser->setResizePolicy( QScrollView::AutoOne );
405 connect( m_threadPart->view(), SIGNAL( leave() ), SLOT( hidePopup() ) );
408 if ( url.at(0) == '#' ) {
409 refNum = url.mid(1).toInt();
411 KURL filteredURL = filterReadCGI( KURL( m_thread.datURL(), url ) );
412 refNum = filteredURL.ref().toInt();
416 DOM::NodeList comments = m_threadPart->htmlDocument().body().getElementsByTagName( "dl" );
417 DOM::HTMLDListElement comment_dl = static_cast<DOM::HTMLDListElement>(comments.item( refNum - 1 ));
418 QString innerHTML = comment_dl.innerHTML().string();
420 m_browser->setText( innerHTML );
421 m_browser->resize( m_browser->contentsWidth() + 10, m_browser->contentsHeight() );
422 m_popup->adjustSize();
424 QPoint pos = QCursor::pos();
425 pos -= QPoint( 0, m_popup->height() ) + QPoint( -10, 10 );
426 m_popup->move( pos );
435 void KitaThreadView::hidePopup()
442 void KitaThreadView::slotPopupMenu( KXMLGUIClient *client, const QPoint &global, const KURL &url, const QString &mimeType, mode_t mode)
444 KActionCollection * collection = client->actionCollection();
446 action = new KAction( i18n("goback anchor"), SmallIcon("idea"), KShortcut(), m_threadPart, SLOT( gobackAnchor() ), collection, "goback_anchor" );
447 emit popupMenu(client, global, url, mimeType, mode);
450 void KitaThreadView::slotOpenURLRequest(const KURL& url, const KParts::URLArgs& args)
452 if ( url.url().at(0) == '#' ) {
453 m_threadPart->gotoAnchor( url.ref() );
456 KURL datURL = filterReadCGI( url );
457 if ( datURL.host() == m_thread.datURL().host()
458 && datURL.path() == m_thread.datURL().path() )
460 if ( datURL.hasRef() ) m_threadPart->gotoAnchor( datURL.ref() );
463 emit openURLRequest(datURL, args);
466 void KitaThreadView::slotReloadButton()
468 showThread( m_thread );
471 KURL KitaThreadView::filterReadCGI(const KURL& url)
474 if ( url.path().contains("/test/read.cgi") ) {
475 newURL.setProtocol( m_thread.datURL().protocol() );
476 QString tmp = url.path().section("/test/read.cgi", 1);
478 QString newPath = QString( "/%1/dat/%2.dat" )
479 .arg( tmp.section('/', 1, 1) )
480 .arg( tmp.section('/', 2, 2) );
481 newURL.setPath( newPath );
483 QString refBase = tmp.section('/', 3);
484 if ( ! refBase.isEmpty() ) {
485 QString newRef = refBase.section('-', 0, 0);
486 if( ! newRef.isEmpty() ) newURL.setRef( newRef );
487 else if ( refBase.at(0) == '-' ) newURL.setRef("1");
488 else newURL.setRef( refBase );
491 kdDebug() << "newURL: " << newURL.url() << endl;
495 void KitaThreadView::setFont( const QFont& font )
497 m_threadPart->setStandardFont(font.family());
498 subjectLabel->setFont(font);
501 void KitaThreadView::slotPostStarted( KIO::Job*, const Kita::PostInfo& info)
506 void KitaThreadView::slotBookmarkButtonClicked( bool on )
508 emit bookmarked( m_thread, on );
511 void KitaThreadView::killJob()