1 /***************************************************************************
2 * Copyright (C) 2003 by Hideki Ikemoto *
3 * ikemo@users.sourceforge.jp *
4 * linux konqueror plugin no hito *
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 "threadview.h"
14 #include <kpopupmenu.h>
17 #include <khtmlview.h>
18 #include <kiconloader.h>
21 #include <kmessagebox.h>
23 #include <kapplication.h>
24 #include <ktextbrowser.h>
25 #include <kcombobox.h>
27 #include <dom/html_inline.h>
28 #include <dom/html_base.h>
29 #include <dom/html_list.h>
31 #include <qclipboard.h>
33 #include <qtoolbutton.h>
35 #include <qcombobox.h>
36 #include <qprogressdialog.h>
38 #include <qtextbrowser.h>
39 #include <qmessagebox.h>
40 #include <qdatetime.h>
41 #include <qeucjpcodec.h>
42 #include <qsimplerichtext.h>
43 #include <qpopupmenu.h>
47 #include "libkita/kitaconfig.h"
48 #include "libkita/kita-utf8.h"
49 #include "libkita/qcp932codec.h"
50 #include "libkita/favoritethreads.h"
51 #include "libkita/favoriteboards.h"
52 #include "libkita/datmanager.h"
53 #include "libkita/boardmanager.h"
54 #include "libkita/signalcollection.h"
55 #include "libkita/kita_misc.h"
56 #include "libkita/config_xt.h"
58 #define MAX_LABEL_LENGTH 60
61 KitaThreadView::KitaThreadView( QWidget* parent, const char* name )
63 /* copied from Base class */
65 setName( "KitaThreadViewBase" );
66 setFocusPolicy( QWidget::ClickFocus );
67 KitaThreadViewBaseLayout = new QVBoxLayout( this, 0, 0, "KitaThreadViewBaseLayout");
69 layout2 = new QHBoxLayout( 0, 0, 6, "layout2");
71 writeButton = new QToolButton( this, "writeButton" );
72 writeButton->setEnabled( FALSE );
73 layout2->addWidget( writeButton );
75 SearchCombo = new QComboBox( FALSE, this, "SearchCombo" );
76 SearchCombo->setMinimumSize( QSize( 200, 0 ) );
77 SearchCombo->setEditable( TRUE );
78 SearchCombo->setSizeLimit( 10 );
79 SearchCombo->setMaxCount( 15 );
80 SearchCombo->setInsertionPolicy( QComboBox::AtTop );
81 SearchCombo->setDuplicatesEnabled( FALSE );
82 layout2->addWidget( SearchCombo );
84 HighLightButton = new QToolButton( this, "HighLightButton" );
85 HighLightButton->setToggleButton( TRUE );
86 layout2->addWidget( HighLightButton );
88 BookmarkButton = new QToolButton( this, "BookmarkButton" );
89 BookmarkButton->setEnabled( FALSE );
90 BookmarkButton->setToggleButton( TRUE );
91 layout2->addWidget( BookmarkButton );
93 ReloadButton = new QToolButton( this, "ReloadButton" );
94 ReloadButton->setEnabled( FALSE );
95 layout2->addWidget( ReloadButton );
97 gotoCombo = new QComboBox( FALSE, this, "gotoCombo" );
98 gotoCombo->setMinimumSize( QSize( 200, 0 ) );
99 layout2->addWidget( gotoCombo );
101 deleteButton = new QToolButton( this, "deleteButton" );
102 deleteButton->setEnabled( FALSE );
103 layout2->addWidget( deleteButton );
104 spacer2 = new QSpacerItem( 30, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
105 layout2->addItem( spacer2 );
107 closeButton = new QToolButton( this, "closeButton" );
108 closeButton->setEnabled( FALSE );
109 layout2->addWidget( closeButton );
110 KitaThreadViewBaseLayout->addLayout( layout2 );
112 threadFrame = new QFrame( this, "threadFrame" );
113 threadFrame->setFrameShape( QFrame::StyledPanel );
114 threadFrame->setFrameShadow( QFrame::Raised );
115 KitaThreadViewBaseLayout->addWidget( threadFrame );
116 resize( QSize(870, 480).expandedTo(minimumSizeHint()) );
117 clearWState( WState_Polished );
120 m_threadPart = new KitaHTMLPart( threadFrame );
121 QHBoxLayout* aLayout = new QHBoxLayout( threadFrame );
122 aLayout->addWidget( m_threadPart->view() );
125 HighLightButton->setPixmap( SmallIcon( "idea" ) );
126 ReloadButton->setPixmap( SmallIcon( "reload" ) );
127 BookmarkButton->setPixmap( SmallIcon( "bookmark_add" ) );
128 writeButton->setPixmap( SmallIcon( "pencil" ) );
129 deleteButton->setPixmap( SmallIcon( "edittrash" ) );
130 closeButton->setPixmap( SmallIcon( "fileclose" ) );
133 setAcceptDrops( true ); // DND Drop eneble: 2nd stage. - enable on "KitaThreadView" widget and disable on the others(child widgets of "KitaThreadView").
134 threadFrame->setAcceptDrops( false ); // don't treat Drop event on child.
135 m_threadPart->view() ->setAcceptDrops( false ); // don't treat Drop event on child.
137 m_threadPart->enableMetaRefresh( false ); //disable <meta refresh="...">
139 connect( deleteButton, SIGNAL( clicked() ),
140 SLOT( slotDeleteButtonClicked() ) );
141 connect( writeButton, SIGNAL( clicked() ),
142 SLOT( slotWriteButtonClicked() ) );
143 connect( m_threadPart, SIGNAL( openWriteDialog( QString ) ),
144 SLOT( slotWriteButtonClicked ( QString ) ) );
145 connect( m_threadPart, SIGNAL( finishReload() ),
146 SLOT( slotUpdateInfo() ) );
147 connect( m_threadPart, SIGNAL( nodeActivated( const DOM::Node& ) ),
148 SLOT( slotDOMNodeActivated( const DOM::Node& ) ) );
149 connect( m_threadPart, SIGNAL( mousePressed() ),
150 SLOT( setFocus() ) );
152 connect( BookmarkButton, SIGNAL( toggled( bool ) ),
153 SLOT( slotBookmarkButtonClicked( bool ) ) );
154 connect( SearchCombo, SIGNAL( activated( int ) ),
155 SLOT( slotSearchButton() ) );
156 connect( ReloadButton, SIGNAL( clicked() ),
157 SLOT( slotReloadButton() ) );
158 connect( gotoCombo, SIGNAL( activated( int ) ),
159 SLOT( slotComboActivated( int ) ) );
160 connect( closeButton, SIGNAL( clicked() ),
161 SLOT( slotCloseButton() ) );
163 Kita::SignalCollection* signalCollection = Kita::SignalCollection::getInstance();
164 connect( this, SIGNAL( setMainStatusbar( const QString& ) ),
165 signalCollection, SIGNAL( setMainStatusbar ( const QString& ) ) );
166 connect( this, SIGNAL( bookmarked( const QString&, bool ) ),
167 signalCollection, SIGNAL( bookmarked( const QString&, bool ) ) );
168 connect( this, SIGNAL( setMainURLLine( const KURL& ) ),
169 signalCollection, SIGNAL( setMainURLLine( const KURL& ) ) );
170 connect( this, SIGNAL( updateSubjectTab( const KURL& ) ),
171 signalCollection, SIGNAL( updateSubjectTab( const KURL& ) ) );
172 connect( this, SIGNAL( updateThreadTab( const KURL& ) ),
173 signalCollection, SIGNAL( updateThreadTab( const KURL& ) ) );
174 connect( this, SIGNAL( openURLRequestExt( const KURL&, const QString ) ),
175 signalCollection, SIGNAL( openURLRequestExt( const KURL&, const QString ) ) );
176 connect( this, SIGNAL( setMainCaption( const QString& ) ),
177 signalCollection, SIGNAL( setMainCaption( const QString& ) ) );
178 connect( this, SIGNAL( closeThreadTab( const KURL& ) ),
179 signalCollection, SIGNAL( closeThreadTab( const KURL& ) ) );
181 connect( this, SIGNAL( activateThreadView( const KURL& ) ),
182 signalCollection, SIGNAL( activateThreadView( const KURL& ) ) );
185 connect( this, SIGNAL( showWriteView( const KURL& , const QString& ) ),
186 signalCollection, SIGNAL( showWriteView( const KURL& , const QString& ) ) );
188 m_viewmode = VIEWMODE_MAINVIEW;
191 m_datURL = QString::null;
195 KitaThreadView::~KitaThreadView()
198 if ( m_threadPart ) {
204 const KURL KitaThreadView::threadURL() const
206 return Kita::getThreadURL( m_datURL );
209 const KURL KitaThreadView::datURL() const
214 void KitaThreadView::slotDOMNodeActivated( const DOM::Node& node )
216 { //process Anchor tags. Anchor tags not proccessed here cause 'emit KParts::BrowserExtention::openURLRequest()'
217 DOM::HTMLAnchorElement anchor = node;
219 if ( ! anchor.href().isEmpty() ) {
220 kdDebug() << "AnchorNodeActivated::" << endl;
221 } // end: anchor.href().isEmpty()
222 } // end of Anchor tags.
225 void KitaThreadView::setSubjectLabel( const QString& boardName, const QString& threadName, const QString boardURL )
228 if ( boardName.isEmpty() ) {
231 disp = QString( "<a href=\"%1\">[%2]</a> %3" ).arg( boardURL ).arg( boardName ).arg( threadName );
234 //disp.truncate( MAX_LABEL_LENGTH );
237 void KitaThreadView::updateButton()
239 writeButton->setEnabled( true );
240 BookmarkButton->setEnabled( true );
241 ReloadButton->setEnabled( true );
242 deleteButton->setEnabled( true );
243 closeButton->setEnabled( true );
245 if ( HighLightButton->isOn() ) {
246 HighLightButton->toggle();
249 // don't emit SIGNAL( toggled() )
250 disconnect( BookmarkButton, SIGNAL( toggled( bool ) ), this, SLOT( slotBookmarkButtonClicked( bool ) ) );
251 if ( FavoriteThreads::getInstance() ->contains( m_datURL.prettyURL() ) ) {
252 BookmarkButton->setOn( true );
254 BookmarkButton->setOn( false );
256 connect( BookmarkButton, SIGNAL( toggled( bool ) ), SLOT( slotBookmarkButtonClicked( bool ) ) );
260 /*--------------------*/
262 /*--------------------*/ /* private slots */
263 void KitaThreadView::slotWriteButtonClicked( QString resstr )
265 emit showWriteView( m_datURL, resstr );
269 void KitaThreadView::insertSearchCombo()
271 for ( int count = 0; count < SearchCombo->count(); ++count ) {
272 if ( SearchCombo->text( count ) == SearchCombo->currentText() ) {
277 SearchCombo->insertItem( SearchCombo->currentText() );
280 void KitaThreadView::slotPopupMenu( KXMLGUIClient* client, const QPoint& global, const KURL& url, const QString& mimeType, mode_t mode )
282 KActionCollection * collection = client->actionCollection();
284 action = new KAction( i18n( "goback anchor" ), SmallIcon( "idea" ), KShortcut(), m_threadPart, SLOT( gobackAnchor() ), collection, "goback_anchor" );
285 emit popupMenu( client, global, url, mimeType, mode );
288 void KitaThreadView::setFont( const QFont& font )
290 // m_threadPart->setStandardFont(font.family());
291 SearchCombo->setFont( font );
293 DOM::CSSStyleDeclaration style = m_threadPart->htmlDocument().body().style();
294 style.setProperty( "font-family", font.family(), "" );
295 style.setProperty( "font-size", QString( "%1pt" ).arg( font.pointSize() ), "" );
299 void KitaThreadView::slotBookmarkButtonClicked( bool on )
301 emit bookmarked( m_datURL.prettyURL(), on );
305 void KitaThreadView::focusSearchCombo()
307 if ( ! SearchCombo->hasFocus() ) {
308 SearchCombo->setFocus();
315 /* public slot */ /* virtual */
316 void KitaThreadView::setFocus()
318 emit activateThreadView( m_datURL );
319 showStatusBar( QString::null );
320 m_threadPart->view() ->setFocus();
323 /*-------------------------------------------------*/
324 /* shobon extension */
325 /*-------------------------------------------------*/
331 void KitaThreadView::setup( const KURL& datURL, int mode )
335 /*---------------------------------------*/
338 m_datURL = Kita::getDatURL( datURL );
341 int partMode = HTMLPART_MODE_MAINPART;
342 m_threadPart->setup( partMode, m_datURL );
344 /* mode. Mode is defined in kitathreadview.h */
347 /* reset search direction */
350 /* load SETTING.TXT */
351 Kita::BoardManager::loadBBSSetting( m_datURL, FALSE );
355 /*--------------------------------------------------------*/
357 /* This function is called from KitaThreadTabWidget class */
358 /*--------------------------------------------------------*/
359 void KitaThreadView::showThread( const KURL& datURL, int num )
361 /* If this widget is not parent, then do nothing. */
362 if ( m_viewmode != VIEWMODE_MAINVIEW ) return ;
364 if ( num == 0 ) num = Kita::DatManager::getViewPos( datURL );
366 if ( topLevelWidget() ->isMinimized() ) topLevelWidget() ->showNormal();
367 topLevelWidget() ->raise();
371 setup( datURL, VIEWMODE_MAINVIEW );
373 /* get log from cahce */
375 if ( !m_threadPart->load( num ) ) showStatusBar( "" );
386 /*---------*/ /* public slot */
387 void KitaThreadView::slotReloadButton( int jumpNum )
389 topLevelWidget() ->raise();
392 if ( m_threadPart->reload( jumpNum ) ) {
393 showStatusBar( Kita::utf8ToUnicode( KITAUTF8_NOWRENEW ) );
398 /*-----------------------------------*/
399 /* stop loading the thread */ /* public slot */
400 void KitaThreadView::slotStopLoading()
403 if ( m_threadPart->isPopupVisible() ) {
404 m_threadPart->slotDeletePopup();
408 /* unforcus search combo */
409 if ( SearchCombo->hasFocus() ) {
414 Kita::DatManager::stopLoading( m_datURL );
418 /*-----------------------------------------*/
419 /* show the information at the statusbar. */
420 /*-----------------------------------------*/
421 void KitaThreadView::showStatusBar( QString info )
423 if ( m_datURL.isEmpty() ) return ;
424 QString captionStr = QString::null;
425 QString infostr = QString::null;
426 QString errstr = QString::null;
427 int viewPos = Kita::DatManager::getViewPos( m_datURL );
428 int resNum = Kita::DatManager::getResNum( m_datURL );
429 bool broken = Kita::DatManager::isBroken( m_datURL );
430 int datSize = Kita::DatManager::getDatSize( m_datURL );
432 switch ( m_viewmode ) {
434 case VIEWMODE_MAINVIEW:
436 errstr = QString::null;
437 if ( m_rescode != 200 && m_rescode != 206 && m_rescode != 0 )
438 errstr = QString( "Error %1" ).arg( m_rescode );
439 if ( broken ) info += " This thread is broken.";
441 /* show status bar,caption, url */
442 infostr = Kita::DatManager::threadName( m_datURL ) +
443 QString( " [Total: %1 New: %2] %3 k" ).arg( resNum ).arg( resNum - viewPos ).arg( datSize / 1024 )
444 + info + " " + errstr;
446 captionStr = Kita::DatManager::threadName( m_datURL ) + QString( " (%1)" ).arg( viewPos );
448 emit setMainCaption( captionStr );
449 emit setMainStatusbar( infostr );
450 emit setMainURLLine( Kita::getThreadURL( m_datURL ) );
464 /*--------------------*/
465 /* update information */ /* public */
466 void KitaThreadView::slotUpdateInfo()
468 m_rescode = Kita::DatManager::getResponseCode( m_datURL );
469 m_serverTime = Kita::DatManager::getServerTime( m_datURL );
471 /* uptate informations */
472 setSubjectLabel( Kita::BoardManager::boardName( m_datURL ),
473 Kita::DatManager::threadName( m_datURL )
475 .arg( Kita::DatManager::getReadNum( m_datURL ) ),
476 Kita::BoardManager::boardURL( m_datURL ) );
480 gotoCombo->insertItem( Kita::utf8ToUnicode( KITAUTF8_GOTO ) );
481 gotoCombo->insertItem( Kita::utf8ToUnicode( KITAUTF8_KOKOYON ) );
482 for ( int i = 1; i < Kita::DatManager::getReadNum( m_datURL ); i += 100 ) {
483 gotoCombo->insertItem( QString().setNum( i ) + "-" );
485 gotoCombo->insertItem( Kita::utf8ToUnicode( KITAUTF8_SAIGO ) );
486 gotoCombo->adjustSize();
488 emit updateSubjectTab( m_datURL );
489 emit updateThreadTab( m_datURL );
493 emit showThreadCompleted(); /* to KitaThreadPart */
499 /*------------------------*/
500 /* search function */
501 /*------------------------*/ /* private slots */
502 void KitaThreadView::slotSearchButton()
504 if ( m_datURL.isEmpty() ) return ; /* Nothing is shown on the screen.*/
506 QString str = SearchCombo->currentText();
507 if ( str.at( 0 ) == ':' ) {
510 if ( str.at( 1 ) == 'p' ) {
511 int refNum = str.mid( 2 ).toInt();
512 QPoint pos = mapToGlobal( SearchCombo->pos() );
513 pos.setY( pos.y() + SearchCombo->height() / 2 );
514 m_threadPart->slotShowResPopup( pos , refNum, refNum );
517 /* find by find dialog */
518 else if ( str.at( 1 ) == 'f' ) {
519 KAction * act = static_cast< KAction* >( m_threadPart->action( "find" ) );
520 if ( act ) act->activate();
525 QString anc = str.mid( 1 );
526 m_threadPart->gotoAnchor( anc, FALSE );
527 SearchCombo->setFocus();
531 slotSearchPrivate( FALSE );
535 void KitaThreadView::slotSearchNext() { slotSearchPrivate( FALSE ); }
536 void KitaThreadView::slotSearchPrev() { slotSearchPrivate( TRUE ); }
539 void KitaThreadView::slotSearchPrivate( bool rev )
541 if ( m_datURL.isEmpty() ) return ; /* Nothing is shown on the screen.*/
544 QString str = SearchCombo->currentText();
545 if ( str == QString::null ) return ;
546 if ( str == "" ) return ;
547 if ( str.at( 0 ) == ':' ) return ;
548 if ( str.at( 0 ) == '?' ) return ;
551 query += SearchCombo->currentText();
552 int ResNum = Kita::DatManager::getResNum( m_datURL );
553 for ( int i = 1; i <= ResNum; i++ ) {
555 if ( Kita::DatManager::checkWord( m_datURL, query, i, FALSE ) ) {
557 /* if this is parent, then show all responses, and search */
558 if ( m_viewmode == VIEWMODE_MAINVIEW ) m_threadPart->showAll();
561 QStringList list = Kita::parseSearchQuery( SearchCombo->currentText() );
562 m_threadPart->findText( SearchCombo->currentText(), rev );
563 SearchCombo->setFocus();
569 QMessageBox::information( this, i18n( "kita" ), i18n( "Not Found" ) );
573 void KitaThreadView::slotGobackAnchor() { m_threadPart->slotGobackAnchor(); }
574 void KitaThreadView::slotGotoHeader() { m_threadPart->gotoAnchor( "header", FALSE );}
575 void KitaThreadView::slotGotoFooter() { m_threadPart->slotClickGotoFooter(); }
579 void KitaThreadView::slotComboActivated( int index )
581 if ( index == gotoCombo->count() - 1 ) {
583 m_threadPart->gotoAnchor( "footer", FALSE );
584 } else if ( index == 1 ) {
586 m_threadPart->gotoAnchor( "kokomade_yonda", FALSE );
587 } else if ( index != 0 ) {
588 QString numText = gotoCombo->text( index );
589 numText.truncate( numText.length() - 1 );
590 m_threadPart->gotoAnchor( numText, FALSE );
594 void KitaThreadView::slotDeleteButtonClicked()
596 if ( m_datURL.isEmpty() ) return ;
598 int rescode = Kita::DatManager::getResponseCode( m_datURL );
599 if ( ( rescode != 200 && rescode != 206 )
600 || FavoriteThreads::getInstance() ->contains( m_datURL.prettyURL() ) ) {
601 if ( QMessageBox::warning( this,
603 i18n( "Do you want to delete Log ?" ),
604 QMessageBox::Ok, QMessageBox::Cancel | QMessageBox::Default )
605 != QMessageBox::Ok ) return ;
608 if ( Kita::DatManager::deleteCache( m_datURL ) ) {
609 emit closeThreadTab( m_datURL );
610 emit updateSubjectTab( m_datURL );
614 void KitaThreadView::slotCloseButton()
616 emit closeCurrentTab();
619 const QString KitaThreadView::selectedText() const
621 return m_threadPart->selectedText();