OSDN Git Service

move files.
[kita/kita.git] / kita / src / thread / threadview.cpp
1 /***************************************************************************
2 *   Copyright (C) 2003 by Hideki Ikemoto                                  *
3 *   ikemo@users.sourceforge.jp                                            *
4 *   linux konqueror plugin no hito                                        *
5 *                                                                         *
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 ***************************************************************************/
11
12 #include "threadview.h"
13
14 #include <kpopupmenu.h>
15 #include <krun.h>
16 #include <kurl.h>
17 #include <khtmlview.h>
18 #include <kiconloader.h>
19 #include <klocale.h>
20 #include <kaction.h>
21 #include <kmessagebox.h>
22 #include <kdebug.h>
23 #include <kapplication.h>
24 #include <ktextbrowser.h>
25 #include <kcombobox.h>
26
27 #include <dom/html_inline.h>
28 #include <dom/html_base.h>
29 #include <dom/html_list.h>
30
31 #include <qclipboard.h>
32 #include <qlayout.h>
33 #include <qtoolbutton.h>
34 #include <qlabel.h>
35 #include <qcombobox.h>
36 #include <qprogressdialog.h>
37 #include <qcursor.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>
44
45 #include "htmlpart.h"
46
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"
57
58 #define MAX_LABEL_LENGTH 60
59
60
61 KitaThreadView::KitaThreadView( QWidget* parent, const char* name )
62 {
63     /* copied from Base class */
64     if ( !name )
65         setName( "KitaThreadViewBase" );
66     setFocusPolicy( QWidget::ClickFocus );
67     KitaThreadViewBaseLayout = new QVBoxLayout( this, 0, 0, "KitaThreadViewBaseLayout"); 
68
69     layout2 = new QHBoxLayout( 0, 0, 6, "layout2"); 
70
71     writeButton = new QToolButton( this, "writeButton" );
72     writeButton->setEnabled( FALSE );
73     layout2->addWidget( writeButton );
74
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 );
83
84     HighLightButton = new QToolButton( this, "HighLightButton" );
85     HighLightButton->setToggleButton( TRUE );
86     layout2->addWidget( HighLightButton );
87
88     BookmarkButton = new QToolButton( this, "BookmarkButton" );
89     BookmarkButton->setEnabled( FALSE );
90     BookmarkButton->setToggleButton( TRUE );
91     layout2->addWidget( BookmarkButton );
92
93     ReloadButton = new QToolButton( this, "ReloadButton" );
94     ReloadButton->setEnabled( FALSE );
95     layout2->addWidget( ReloadButton );
96
97     gotoCombo = new QComboBox( FALSE, this, "gotoCombo" );
98     gotoCombo->setMinimumSize( QSize( 200, 0 ) );
99     layout2->addWidget( gotoCombo );
100
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 );
106
107     closeButton = new QToolButton( this, "closeButton" );
108     closeButton->setEnabled( FALSE );
109     layout2->addWidget( closeButton );
110     KitaThreadViewBaseLayout->addLayout( layout2 );
111
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 );
118     /* copy end */
119
120     m_threadPart = new KitaHTMLPart( threadFrame );
121     QHBoxLayout* aLayout = new QHBoxLayout( threadFrame );
122     aLayout->addWidget( m_threadPart->view() );
123
124     {
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" ) );
131     }
132
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.
136
137     m_threadPart->enableMetaRefresh( false ); //disable <meta refresh="...">
138
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() ) );
151
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() ) );
162
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& ) ) );
180
181     connect( this, SIGNAL( activateThreadView( const KURL& ) ),
182              signalCollection, SIGNAL( activateThreadView( const KURL& ) ) );
183
184     /* write dock */
185     connect( this, SIGNAL( showWriteView( const KURL& , const QString& ) ),
186              signalCollection, SIGNAL( showWriteView( const KURL& , const QString& ) ) );
187
188     m_viewmode = VIEWMODE_MAINVIEW;
189     m_rescode = 200;
190     m_serverTime = 0;
191     m_datURL = QString::null;
192
193 }
194
195 KitaThreadView::~KitaThreadView()
196 {
197
198     if ( m_threadPart ) {
199         delete m_threadPart;
200         m_threadPart = NULL;
201     }
202 }
203
204 const KURL KitaThreadView::threadURL() const
205 {
206     return Kita::getThreadURL( m_datURL );
207 }
208
209 const KURL KitaThreadView::datURL() const
210 {
211     return m_datURL;
212 }
213
214 void KitaThreadView::slotDOMNodeActivated( const DOM::Node& node )
215 {
216     { //process Anchor tags. Anchor tags not proccessed here cause 'emit KParts::BrowserExtention::openURLRequest()'
217         DOM::HTMLAnchorElement anchor = node;
218
219         if ( ! anchor.href().isEmpty() ) {
220             kdDebug() << "AnchorNodeActivated::" << endl;
221         } // end: anchor.href().isEmpty()
222     } // end of Anchor tags.
223 }
224
225 void KitaThreadView::setSubjectLabel( const QString& boardName, const QString& threadName, const QString boardURL )
226 {
227     QString disp;
228     if ( boardName.isEmpty() ) {
229         disp = threadName;
230     } else {
231         disp = QString( "<a href=\"%1\">[%2]</a> %3" ).arg( boardURL ).arg( boardName ).arg( threadName );
232     }
233
234     //disp.truncate( MAX_LABEL_LENGTH );
235 }
236
237 void KitaThreadView::updateButton()
238 {
239     writeButton->setEnabled( true );
240     BookmarkButton->setEnabled( true );
241     ReloadButton->setEnabled( true );
242     deleteButton->setEnabled( true );
243     closeButton->setEnabled( true );
244
245     if ( HighLightButton->isOn() ) {
246         HighLightButton->toggle();
247     }
248
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 );
253     } else {
254         BookmarkButton->setOn( false );
255     }
256     connect( BookmarkButton, SIGNAL( toggled( bool ) ), SLOT( slotBookmarkButtonClicked( bool ) ) );
257 }
258
259
260 /*--------------------*/
261 /* write response     */
262 /*--------------------*/  /* private slots */
263 void KitaThreadView::slotWriteButtonClicked( QString resstr )
264 {
265     emit showWriteView( m_datURL, resstr );
266 }
267
268
269 void KitaThreadView::insertSearchCombo()
270 {
271     for ( int count = 0; count < SearchCombo->count(); ++count ) {
272         if ( SearchCombo->text( count ) == SearchCombo->currentText() ) {
273             // found
274             return ;
275         }
276     }
277     SearchCombo->insertItem( SearchCombo->currentText() );
278 }
279
280 void KitaThreadView::slotPopupMenu( KXMLGUIClient* client, const QPoint& global, const KURL& url, const QString& mimeType, mode_t mode )
281 {
282     KActionCollection * collection = client->actionCollection();
283     KAction* action;
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 );
286 }
287
288 void KitaThreadView::setFont( const QFont& font )
289 {
290     //  m_threadPart->setStandardFont(font.family());
291     SearchCombo->setFont( font );
292
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() ), "" );
296 }
297
298
299 void KitaThreadView::slotBookmarkButtonClicked( bool on )
300 {
301     emit bookmarked( m_datURL.prettyURL(), on );
302 }
303
304
305 void KitaThreadView::focusSearchCombo()
306 {
307     if ( ! SearchCombo->hasFocus() ) {
308         SearchCombo->setFocus();
309     } else {
310         setFocus();
311     }
312 }
313
314
315 /* public slot */ /* virtual */
316 void KitaThreadView::setFocus()
317 {
318     emit activateThreadView( m_datURL );
319     showStatusBar( QString::null );
320     m_threadPart->view() ->setFocus();
321 }
322
323 /*-------------------------------------------------*/
324 /* shobon extension                                */
325 /*-------------------------------------------------*/
326
327
328 /*-------*/
329 /* setup */
330 /*-------*/
331 void KitaThreadView::setup( const KURL& datURL, int mode )
332 {
333     /* config. */
334
335     /*---------------------------------------*/
336     /* setup                                 */
337
338     m_datURL = Kita::getDatURL( datURL );
339
340     /* setup HTMLPart */
341     int partMode = HTMLPART_MODE_MAINPART;
342     m_threadPart->setup( partMode, m_datURL );
343
344     /* mode. Mode is defined in kitathreadview.h */
345     m_viewmode = mode;
346
347     /* reset search direction */
348     m_revsearch = FALSE;
349
350     /* load SETTING.TXT */
351     Kita::BoardManager::loadBBSSetting( m_datURL, FALSE );
352 }
353
354
355 /*--------------------------------------------------------*/
356 /* Show thread                                            */
357 /* This function is called from KitaThreadTabWidget class */
358 /*--------------------------------------------------------*/
359 void KitaThreadView::showThread( const KURL& datURL, int num )
360 {
361     /* If this widget is not parent, then do nothing. */
362     if ( m_viewmode != VIEWMODE_MAINVIEW ) return ;
363
364     if ( num == 0 ) num = Kita::DatManager::getViewPos( datURL );
365
366     if ( topLevelWidget() ->isMinimized() ) topLevelWidget() ->showNormal();
367     topLevelWidget() ->raise();
368     setActiveWindow();
369
370     /* setup */
371     setup( datURL, VIEWMODE_MAINVIEW );
372
373     /* get log from cahce */
374     m_rescode = 200;
375     if ( !m_threadPart->load( num ) ) showStatusBar( "" );
376
377     /* update data */
378     slotUpdateInfo();
379     slotReloadButton();
380 }
381
382
383
384 /*---------*/
385 /* reload  */
386 /*---------*/ /* public slot */
387 void KitaThreadView::slotReloadButton( int jumpNum )
388 {
389     topLevelWidget() ->raise();
390     setActiveWindow();
391
392     if ( m_threadPart->reload( jumpNum ) ) {
393         showStatusBar( Kita::utf8ToUnicode( KITAUTF8_NOWRENEW ) );
394     }
395 }
396
397
398 /*-----------------------------------*/
399 /* stop loading the thread           */ /* public slot */
400 void KitaThreadView::slotStopLoading()
401 {
402     /* hide popup */
403     if ( m_threadPart->isPopupVisible() ) {
404         m_threadPart->slotDeletePopup();
405         return ;
406     }
407
408     /* unforcus search combo */
409     if ( SearchCombo->hasFocus() ) {
410         setFocus();
411         return ;
412     }
413
414     Kita::DatManager::stopLoading( m_datURL );
415 }
416
417
418 /*-----------------------------------------*/
419 /* show the information at the statusbar.  */
420 /*-----------------------------------------*/
421 void KitaThreadView::showStatusBar( QString info )
422 {
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 );
431
432     switch ( m_viewmode ) {
433
434     case VIEWMODE_MAINVIEW:
435
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.";
440
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;
445
446         captionStr = Kita::DatManager::threadName( m_datURL ) + QString( " (%1)" ).arg( viewPos );
447
448         emit setMainCaption( captionStr );
449         emit setMainStatusbar( infostr );
450         emit setMainURLLine( Kita::getThreadURL( m_datURL ) );
451
452         return ;
453         break;
454
455     default:
456
457         break;
458     }
459 }
460
461
462
463
464 /*--------------------*/
465 /* update information */ /* public */
466 void KitaThreadView::slotUpdateInfo()
467 {
468     m_rescode = Kita::DatManager::getResponseCode( m_datURL );
469     m_serverTime = Kita::DatManager::getServerTime( m_datURL );
470
471     /* uptate informations */
472     setSubjectLabel( Kita::BoardManager::boardName( m_datURL ),
473                      Kita::DatManager::threadName( m_datURL )
474                      + QString( " (%1)" )
475                      .arg( Kita::DatManager::getReadNum( m_datURL ) ),
476                      Kita::BoardManager::boardURL( m_datURL ) );
477     updateButton();
478
479     gotoCombo->clear();
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 ) + "-" );
484     }
485     gotoCombo->insertItem( Kita::utf8ToUnicode( KITAUTF8_SAIGO ) );
486     gotoCombo->adjustSize();
487
488     emit updateSubjectTab( m_datURL );
489     emit updateThreadTab( m_datURL );
490
491     showStatusBar( "" );
492
493     emit showThreadCompleted(); /* to KitaThreadPart */
494 }
495
496
497
498
499 /*------------------------*/
500 /* search function        */
501 /*------------------------*/  /* private slots */
502 void KitaThreadView::slotSearchButton()
503 {
504     if ( m_datURL.isEmpty() ) return ; /* Nothing is shown on the screen.*/
505
506     QString str = SearchCombo->currentText();
507     if ( str.at( 0 ) == ':' ) {
508
509         /* show res popup */
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 );
515             return ;
516         }
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();
521             return ;
522         }
523
524         /* jump */
525         QString anc = str.mid( 1 );
526         m_threadPart->gotoAnchor( anc, FALSE );
527         SearchCombo->setFocus();
528         return ;
529     }
530
531     slotSearchPrivate( FALSE );
532 }
533
534 /* public slot */
535 void KitaThreadView::slotSearchNext() { slotSearchPrivate( FALSE ); }
536 void KitaThreadView::slotSearchPrev() { slotSearchPrivate( TRUE ); }
537
538 /* private */
539 void KitaThreadView::slotSearchPrivate( bool rev )
540 {
541     if ( m_datURL.isEmpty() ) return ; /* Nothing is shown on the screen.*/
542
543     /* jump */
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 ;
549
550     QStringList query;
551     query += SearchCombo->currentText();
552     int ResNum = Kita::DatManager::getResNum( m_datURL );
553     for ( int i = 1; i <= ResNum; i++ ) {
554
555         if ( Kita::DatManager::checkWord( m_datURL, query, i, FALSE ) ) {
556
557             /* if this is parent, then show all responses, and search */
558             if ( m_viewmode == VIEWMODE_MAINVIEW ) m_threadPart->showAll();
559
560             insertSearchCombo();
561             QStringList list = Kita::parseSearchQuery( SearchCombo->currentText() );
562             m_threadPart->findText( SearchCombo->currentText(), rev );
563             SearchCombo->setFocus();
564
565             return ;
566         }
567     }
568
569     QMessageBox::information( this, i18n( "kita" ), i18n( "Not Found" ) );
570 }
571
572 /* public slot */
573 void KitaThreadView::slotGobackAnchor() { m_threadPart->slotGobackAnchor(); }
574 void KitaThreadView::slotGotoHeader() { m_threadPart->gotoAnchor( "header", FALSE );}
575 void KitaThreadView::slotGotoFooter() { m_threadPart->slotClickGotoFooter(); }
576
577 // vim:sw=2:
578
579 void KitaThreadView::slotComboActivated( int index )
580 {
581     if ( index == gotoCombo->count() - 1 ) {
582         // last
583         m_threadPart->gotoAnchor( "footer", FALSE );
584     } else if ( index == 1 ) {
585         // kokomade yonda
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 );
591     }
592 }
593
594 void KitaThreadView::slotDeleteButtonClicked()
595 {
596     if ( m_datURL.isEmpty() ) return ;
597
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,
602                                    "Kita",
603                                    i18n( "Do you want to delete Log ?" ),
604                                    QMessageBox::Ok, QMessageBox::Cancel | QMessageBox::Default )
605                 != QMessageBox::Ok ) return ;
606     }
607
608     if ( Kita::DatManager::deleteCache( m_datURL ) ) {
609         emit closeThreadTab( m_datURL );
610         emit updateSubjectTab( m_datURL );
611     }
612 }
613
614 void KitaThreadView::slotCloseButton()
615 {
616     emit closeCurrentTab();
617 }
618
619 const QString KitaThreadView::selectedText() const
620 {
621     return m_threadPart->selectedText();
622 }