OSDN Git Service

9d9467cb5388fce66b6878e61cbc92479af4a20a
[kita/kita.git] / kita / src / 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 #include "viewmediator.h"
47
48 #include "libkita/kitaconfig.h"
49 #include "libkita/kita-utf8.h"
50 #include "libkita/qcp932codec.h"
51 #include "libkita/favoritethreads.h"
52 #include "libkita/favoriteboards.h"
53 #include "libkita/datmanager.h"
54 #include "libkita/boardmanager.h"
55 #include "libkita/signalcollection.h"
56 #include "libkita/kita_misc.h"
57 #include "libkita/config_xt.h"
58
59 #define MAX_LABEL_LENGTH 60
60
61
62 KitaThreadView::KitaThreadView( KitaThreadTabWidget* parent, const char* name )
63 {
64     m_parent = parent;
65
66     /* copied from Base class */
67     if ( !name )
68         setName( "KitaThreadViewBase" );
69     setFocusPolicy( QWidget::ClickFocus );
70     KitaThreadViewBaseLayout = new QVBoxLayout( this, 0, 0, "KitaThreadViewBaseLayout"); 
71
72     layout2 = new QHBoxLayout( 0, 0, 6, "layout2"); 
73
74     writeButton = new QToolButton( this, "writeButton" );
75     writeButton->setEnabled( FALSE );
76     layout2->addWidget( writeButton );
77
78     SearchCombo = new QComboBox( FALSE, this, "SearchCombo" );
79     SearchCombo->setMinimumSize( QSize( 200, 0 ) );
80     SearchCombo->setEditable( TRUE );
81     SearchCombo->setSizeLimit( 10 );
82     SearchCombo->setMaxCount( 15 );
83     SearchCombo->setInsertionPolicy( QComboBox::AtTop );
84     SearchCombo->setDuplicatesEnabled( FALSE );
85     layout2->addWidget( SearchCombo );
86
87     HighLightButton = new QToolButton( this, "HighLightButton" );
88     HighLightButton->setToggleButton( TRUE );
89     layout2->addWidget( HighLightButton );
90
91     BookmarkButton = new QToolButton( this, "BookmarkButton" );
92     BookmarkButton->setEnabled( FALSE );
93     BookmarkButton->setToggleButton( TRUE );
94     layout2->addWidget( BookmarkButton );
95
96     ReloadButton = new QToolButton( this, "ReloadButton" );
97     ReloadButton->setEnabled( FALSE );
98     layout2->addWidget( ReloadButton );
99
100     gotoCombo = new QComboBox( FALSE, this, "gotoCombo" );
101     gotoCombo->setMinimumSize( QSize( 200, 0 ) );
102     layout2->addWidget( gotoCombo );
103
104     deleteButton = new QToolButton( this, "deleteButton" );
105     deleteButton->setEnabled( FALSE );
106     layout2->addWidget( deleteButton );
107     spacer2 = new QSpacerItem( 30, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
108     layout2->addItem( spacer2 );
109
110     closeButton = new QToolButton( this, "closeButton" );
111     closeButton->setEnabled( FALSE );
112     layout2->addWidget( closeButton );
113     KitaThreadViewBaseLayout->addLayout( layout2 );
114
115     threadFrame = new QFrame( this, "threadFrame" );
116     threadFrame->setFrameShape( QFrame::StyledPanel );
117     threadFrame->setFrameShadow( QFrame::Raised );
118     KitaThreadViewBaseLayout->addWidget( threadFrame );
119     resize( QSize(870, 480).expandedTo(minimumSizeHint()) );
120     clearWState( WState_Polished );
121     /* copy end */
122
123     m_threadPart = new KitaHTMLPart( threadFrame );
124     QHBoxLayout* aLayout = new QHBoxLayout( threadFrame );
125     aLayout->addWidget( m_threadPart->view() );
126
127     {
128         HighLightButton->setPixmap( SmallIcon( "idea" ) );
129         ReloadButton->setPixmap( SmallIcon( "reload" ) );
130         BookmarkButton->setPixmap( SmallIcon( "bookmark_add" ) );
131         writeButton->setPixmap( SmallIcon( "pencil" ) );
132         deleteButton->setPixmap( SmallIcon( "edittrash" ) );
133         closeButton->setPixmap( SmallIcon( "fileclose" ) );
134     }
135
136     setAcceptDrops( true ); // DND Drop eneble: 2nd stage. - enable on "KitaThreadView" widget and disable on the others(child widgets of "KitaThreadView").
137     threadFrame->setAcceptDrops( false ); // don't treat Drop event on child.
138     m_threadPart->view() ->setAcceptDrops( false ); // don't treat Drop event on child.
139
140     m_threadPart->enableMetaRefresh( false ); //disable <meta refresh="...">
141
142     connect( deleteButton, SIGNAL( clicked() ),
143              SLOT( slotDeleteButtonClicked() ) );
144     connect( writeButton, SIGNAL( clicked() ),
145              SLOT( slotWriteButtonClicked() ) );
146     connect( m_threadPart, SIGNAL( finishReload() ),
147              SLOT( slotUpdateInfo() ) );
148     connect( m_threadPart, SIGNAL( nodeActivated( const DOM::Node& ) ),
149              SLOT( slotDOMNodeActivated( const DOM::Node& ) ) );
150     connect( m_threadPart, SIGNAL( mousePressed() ),
151              SLOT( setFocus() ) );
152
153     connect( BookmarkButton, SIGNAL( toggled( bool ) ),
154              SLOT( slotBookmarkButtonClicked( bool ) ) );
155     connect( SearchCombo, SIGNAL( activated( int ) ),
156              SLOT( slotSearchButton() ) );
157     connect( ReloadButton, SIGNAL( clicked() ),
158              SLOT( slotReloadButton() ) );
159     connect( gotoCombo, SIGNAL( activated( int ) ),
160              SLOT( slotComboActivated( int ) ) );
161     connect( closeButton, SIGNAL( clicked() ),
162              SLOT( slotCloseButton() ) );
163
164     Kita::SignalCollection* signalCollection = Kita::SignalCollection::getInstance();
165     connect( this, SIGNAL( openURLRequestExt( const KURL&, const QString ) ),
166              signalCollection, SIGNAL( openURLRequestExt( const KURL&, const QString ) ) );
167
168     m_viewmode = VIEWMODE_MAINVIEW;
169     m_rescode = 200;
170     m_serverTime = 0;
171     m_datURL = QString::null;
172
173 }
174
175 KitaThreadView::~KitaThreadView()
176 {
177
178     if ( m_threadPart ) {
179         delete m_threadPart;
180         m_threadPart = NULL;
181     }
182 }
183
184 const KURL KitaThreadView::threadURL() const
185 {
186     return Kita::getThreadURL( m_datURL );
187 }
188
189 const KURL KitaThreadView::datURL() const
190 {
191     return m_datURL;
192 }
193
194 void KitaThreadView::slotDOMNodeActivated( const DOM::Node& node )
195 {
196     { //process Anchor tags. Anchor tags not proccessed here cause 'emit KParts::BrowserExtention::openURLRequest()'
197         DOM::HTMLAnchorElement anchor = node;
198
199         if ( ! anchor.href().isEmpty() ) {
200             kdDebug() << "AnchorNodeActivated::" << endl;
201         } // end: anchor.href().isEmpty()
202     } // end of Anchor tags.
203 }
204
205 void KitaThreadView::setSubjectLabel( const QString& boardName, const QString& threadName, const QString boardURL )
206 {
207     QString disp;
208     if ( boardName.isEmpty() ) {
209         disp = threadName;
210     } else {
211         disp = QString( "<a href=\"%1\">[%2]</a> %3" ).arg( boardURL ).arg( boardName ).arg( threadName );
212     }
213
214     //disp.truncate( MAX_LABEL_LENGTH );
215 }
216
217 void KitaThreadView::updateButton()
218 {
219     writeButton->setEnabled( true );
220     BookmarkButton->setEnabled( true );
221     ReloadButton->setEnabled( true );
222     deleteButton->setEnabled( true );
223     closeButton->setEnabled( true );
224
225     if ( HighLightButton->isOn() ) {
226         HighLightButton->toggle();
227     }
228
229     // don't emit SIGNAL( toggled() )
230     disconnect( BookmarkButton, SIGNAL( toggled( bool ) ), this, SLOT( slotBookmarkButtonClicked( bool ) ) );
231     if ( FavoriteThreads::getInstance() ->contains( m_datURL.prettyURL() ) ) {
232         BookmarkButton->setOn( true );
233     } else {
234         BookmarkButton->setOn( false );
235     }
236     connect( BookmarkButton, SIGNAL( toggled( bool ) ), SLOT( slotBookmarkButtonClicked( bool ) ) );
237 }
238
239
240 /*--------------------*/
241 /* write response     */
242 /*--------------------*/  /* private slots */
243 void KitaThreadView::slotWriteButtonClicked( QString resStr )
244 {
245     ViewMediator::getInstance()->showWriteView( m_datURL, resStr );
246 }
247
248
249 void KitaThreadView::insertSearchCombo()
250 {
251     for ( int count = 0; count < SearchCombo->count(); ++count ) {
252         if ( SearchCombo->text( count ) == SearchCombo->currentText() ) {
253             // found
254             return ;
255         }
256     }
257     SearchCombo->insertItem( SearchCombo->currentText() );
258 }
259
260 void KitaThreadView::slotPopupMenu( KXMLGUIClient* client, const QPoint& global, const KURL& url, const QString& mimeType, mode_t mode )
261 {
262     KActionCollection * collection = client->actionCollection();
263     KAction* action;
264     action = new KAction( i18n( "goback anchor" ), SmallIcon( "idea" ), KShortcut(), m_threadPart, SLOT( gobackAnchor() ), collection, "goback_anchor" );
265     emit popupMenu( client, global, url, mimeType, mode );
266 }
267
268 void KitaThreadView::setFont( const QFont& font )
269 {
270     //  m_threadPart->setStandardFont(font.family());
271     SearchCombo->setFont( font );
272
273     DOM::CSSStyleDeclaration style = m_threadPart->htmlDocument().body().style();
274     style.setProperty( "font-family", font.family(), "" );
275     style.setProperty( "font-size", QString( "%1pt" ).arg( font.pointSize() ), "" );
276 }
277
278
279 void KitaThreadView::slotBookmarkButtonClicked( bool on )
280 {
281     ViewMediator::getInstance()->bookmark( m_datURL.prettyURL(), on );
282 }
283
284
285 void KitaThreadView::focusSearchCombo()
286 {
287     if ( ! SearchCombo->hasFocus() ) {
288         SearchCombo->setFocus();
289     } else {
290         setFocus();
291     }
292 }
293
294
295 /* public slot */ /* virtual */
296 void KitaThreadView::setFocus()
297 {
298     ViewMediator::getInstance()->changeWriteTab( m_datURL );
299     showStatusBar( QString::null );
300     m_threadPart->view() ->setFocus();
301 }
302
303 /*-------------------------------------------------*/
304 /* shobon extension                                */
305 /*-------------------------------------------------*/
306
307
308 /*-------*/
309 /* setup */
310 /*-------*/
311 void KitaThreadView::setup( const KURL& datURL, int mode )
312 {
313     /* config. */
314
315     /*---------------------------------------*/
316     /* setup                                 */
317
318     m_datURL = Kita::getDatURL( datURL );
319
320     /* setup HTMLPart */
321     int partMode = HTMLPART_MODE_MAINPART;
322     m_threadPart->setup( partMode, m_datURL );
323
324     /* mode. Mode is defined in kitathreadview.h */
325     m_viewmode = mode;
326
327     /* reset search direction */
328     m_revsearch = FALSE;
329
330     /* load SETTING.TXT */
331     Kita::BoardManager::loadBBSSetting( m_datURL, FALSE );
332 }
333
334
335 /*--------------------------------------------------------*/
336 /* Show thread                                            */
337 /* This function is called from KitaThreadTabWidget class */
338 /*--------------------------------------------------------*/
339 void KitaThreadView::showThread( const KURL& datURL, int num )
340 {
341     /* If this widget is not parent, then do nothing. */
342     if ( m_viewmode != VIEWMODE_MAINVIEW ) return ;
343
344     if ( num == 0 ) num = Kita::DatManager::getViewPos( datURL );
345
346     if ( topLevelWidget() ->isMinimized() ) topLevelWidget() ->showNormal();
347     topLevelWidget() ->raise();
348     setActiveWindow();
349
350     /* setup */
351     setup( datURL, VIEWMODE_MAINVIEW );
352
353     /* get log from cahce */
354     m_rescode = 200;
355     if ( !m_threadPart->load( num ) ) showStatusBar( "" );
356
357     /* update data */
358     slotUpdateInfo();
359     slotReloadButton();
360 }
361
362
363
364 /*---------*/
365 /* reload  */
366 /*---------*/ /* public slot */
367 void KitaThreadView::slotReloadButton( int jumpNum )
368 {
369     topLevelWidget() ->raise();
370     setActiveWindow();
371
372     if ( m_threadPart->reload( jumpNum ) ) {
373         showStatusBar( Kita::utf8ToUnicode( KITAUTF8_NOWRENEW ) );
374     }
375 }
376
377
378 /*-----------------------------------*/
379 /* stop loading the thread           */ /* public slot */
380 void KitaThreadView::slotStopLoading()
381 {
382     /* hide popup */
383     if ( m_threadPart->isPopupVisible() ) {
384         m_threadPart->slotDeletePopup();
385         return ;
386     }
387
388     /* unforcus search combo */
389     if ( SearchCombo->hasFocus() ) {
390         setFocus();
391         return ;
392     }
393
394     Kita::DatManager::stopLoading( m_datURL );
395 }
396
397
398 /*-----------------------------------------*/
399 /* show the information at the statusbar.  */
400 /*-----------------------------------------*/
401 void KitaThreadView::showStatusBar( QString info )
402 {
403     if ( m_datURL.isEmpty() ) return ;
404     QString captionStr = QString::null;
405     QString infostr = QString::null;
406     QString errstr = QString::null;
407     int viewPos = Kita::DatManager::getViewPos( m_datURL );
408     int resNum = Kita::DatManager::getResNum( m_datURL );
409     bool broken = Kita::DatManager::isBroken( m_datURL );
410     int datSize = Kita::DatManager::getDatSize( m_datURL );
411
412     switch ( m_viewmode ) {
413
414     case VIEWMODE_MAINVIEW:
415
416         errstr = QString::null;
417         if ( m_rescode != 200 && m_rescode != 206 && m_rescode != 0 )
418             errstr = QString( "Error %1" ).arg( m_rescode );
419         if ( broken ) info += " This thread is broken.";
420
421         /* show status bar,caption, url  */
422         infostr = Kita::DatManager::threadName( m_datURL ) +
423                   QString( " [Total: %1 New: %2] %3 k" ).arg( resNum ).arg( resNum - viewPos ).arg( datSize / 1024 )
424                   + info + " " + errstr;
425
426         captionStr = Kita::DatManager::threadName( m_datURL ) + QString( " (%1)" ).arg( viewPos );
427
428         ViewMediator::getInstance()->setMainCaption( captionStr );
429         ViewMediator::getInstance()->setMainStatusBar( infostr );
430         ViewMediator::getInstance()->setMainURLLine( Kita::getThreadURL( m_datURL ) );
431
432         return ;
433         break;
434
435     default:
436
437         break;
438     }
439 }
440
441
442
443
444 /*--------------------*/
445 /* update information */ /* public */
446 void KitaThreadView::slotUpdateInfo()
447 {
448     m_rescode = Kita::DatManager::getResponseCode( m_datURL );
449     m_serverTime = Kita::DatManager::getServerTime( m_datURL );
450
451     /* uptate informations */
452     setSubjectLabel( Kita::BoardManager::boardName( m_datURL ),
453                      Kita::DatManager::threadName( m_datURL )
454                      + QString( " (%1)" )
455                      .arg( Kita::DatManager::getReadNum( m_datURL ) ),
456                      Kita::BoardManager::boardURL( m_datURL ) );
457     updateButton();
458
459     gotoCombo->clear();
460     gotoCombo->insertItem( Kita::utf8ToUnicode( KITAUTF8_GOTO ) );
461     gotoCombo->insertItem( Kita::utf8ToUnicode( KITAUTF8_KOKOYON ) );
462     for ( int i = 1; i < Kita::DatManager::getReadNum( m_datURL ); i += 100 ) {
463         gotoCombo->insertItem( QString().setNum( i ) + "-" );
464     }
465     gotoCombo->insertItem( Kita::utf8ToUnicode( KITAUTF8_SAIGO ) );
466     gotoCombo->adjustSize();
467
468     ViewMediator::getInstance()->updateBoardView( m_datURL );
469     ViewMediator::getInstance()->updateThreadView( m_datURL );
470
471     showStatusBar( "" );
472
473     emit showThreadCompleted(); /* to KitaThreadPart */
474 }
475
476
477
478
479 /*------------------------*/
480 /* search function        */
481 /*------------------------*/  /* private slots */
482 void KitaThreadView::slotSearchButton()
483 {
484     if ( m_datURL.isEmpty() ) return ; /* Nothing is shown on the screen.*/
485
486     QString str = SearchCombo->currentText();
487     if ( str.at( 0 ) == ':' ) {
488
489         /* show res popup */
490         if ( str.at( 1 ) == 'p' ) {
491             int refNum = str.mid( 2 ).toInt();
492             QPoint pos = mapToGlobal( SearchCombo->pos() );
493             pos.setY( pos.y() + SearchCombo->height() / 2 );
494             m_threadPart->slotShowResPopup( pos , refNum, refNum );
495             return ;
496         }
497         /* find by find dialog */
498         else if ( str.at( 1 ) == 'f' ) {
499             KAction * act = static_cast< KAction* >( m_threadPart->action( "find" ) );
500             if ( act ) act->activate();
501             return ;
502         }
503
504         /* jump */
505         QString anc = str.mid( 1 );
506         m_threadPart->gotoAnchor( anc, FALSE );
507         SearchCombo->setFocus();
508         return ;
509     }
510
511     slotSearchPrivate( FALSE );
512 }
513
514 /* public slot */
515 void KitaThreadView::slotSearchNext() { slotSearchPrivate( FALSE ); }
516 void KitaThreadView::slotSearchPrev() { slotSearchPrivate( TRUE ); }
517
518 /* private */
519 void KitaThreadView::slotSearchPrivate( bool rev )
520 {
521     if ( m_datURL.isEmpty() ) return ; /* Nothing is shown on the screen.*/
522
523     /* jump */
524     QString str = SearchCombo->currentText();
525     if ( str == QString::null ) return ;
526     if ( str == "" ) return ;
527     if ( str.at( 0 ) == ':' ) return ;
528     if ( str.at( 0 ) == '?' ) return ;
529
530     QStringList query;
531     query += SearchCombo->currentText();
532     int ResNum = Kita::DatManager::getResNum( m_datURL );
533     for ( int i = 1; i <= ResNum; i++ ) {
534
535         if ( Kita::DatManager::checkWord( m_datURL, query, i, FALSE ) ) {
536
537             /* if this is parent, then show all responses, and search */
538             if ( m_viewmode == VIEWMODE_MAINVIEW ) m_threadPart->showAll();
539
540             insertSearchCombo();
541             QStringList list = Kita::parseSearchQuery( SearchCombo->currentText() );
542             m_threadPart->findText( SearchCombo->currentText(), rev );
543             SearchCombo->setFocus();
544
545             return ;
546         }
547     }
548
549     QMessageBox::information( this, i18n( "kita" ), i18n( "Not Found" ) );
550 }
551
552 /* public slot */
553 void KitaThreadView::slotGobackAnchor() { m_threadPart->slotGobackAnchor(); }
554 void KitaThreadView::slotGotoHeader() { m_threadPart->gotoAnchor( "header", FALSE );}
555 void KitaThreadView::slotGotoFooter() { m_threadPart->slotClickGotoFooter(); }
556
557 // vim:sw=2:
558
559 void KitaThreadView::slotComboActivated( int index )
560 {
561     if ( index == gotoCombo->count() - 1 ) {
562         // last
563         m_threadPart->gotoAnchor( "footer", FALSE );
564     } else if ( index == 1 ) {
565         // kokomade yonda
566         m_threadPart->gotoAnchor( "kokomade_yonda", FALSE );
567     } else if ( index != 0 ) {
568         QString numText = gotoCombo->text( index );
569         numText.truncate( numText.length() - 1 );
570         m_threadPart->gotoAnchor( numText, FALSE );
571     }
572 }
573
574 void KitaThreadView::slotDeleteButtonClicked()
575 {
576     if ( m_datURL.isEmpty() ) return ;
577
578     int rescode = Kita::DatManager::getResponseCode( m_datURL );
579     if ( ( rescode != 200 && rescode != 206 )
580             || FavoriteThreads::getInstance() ->contains( m_datURL.prettyURL() ) ) {
581         if ( QMessageBox::warning( this,
582                                    "Kita",
583                                    i18n( "Do you want to delete Log ?" ),
584                                    QMessageBox::Ok, QMessageBox::Cancel | QMessageBox::Default )
585                 != QMessageBox::Ok ) return ;
586     }
587
588     if ( Kita::DatManager::deleteCache( m_datURL ) ) {
589         m_parent->slotCloseThreadTab( m_datURL );
590         ViewMediator::getInstance()->updateBoardView( m_datURL );
591     }
592 }
593
594 void KitaThreadView::slotCloseButton()
595 {
596     m_parent->slotCloseCurrentTab();
597 }
598
599 const QString KitaThreadView::selectedText() const
600 {
601     return m_threadPart->selectedText();
602 }