OSDN Git Service

c446e704fa87d4c47c814520f30e0aef9c2af1f6
[kita/kita.git] / kita / src / write / previewpart.cpp
1 /***************************************************************************
2 *   Copyright (C) 2004 by Kita Developers                                 *
3 *   ikemo@users.sourceforge.jp                                            *
4 *                                                                         *
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 ***************************************************************************/
10
11 #include <kpopupmenu.h>
12 #include <klocale.h>
13 #include <khtml_events.h>
14 #include <kdebug.h>
15
16 #include <dom/html_element.h>
17 #include <dom/html_misc.h>
18
19 #include <qcursor.h>
20 #include <qapplication.h>
21 #include <qclipboard.h>
22 #include <qmessagebox.h>
23 #include <qregexp.h>
24
25 #include "previewpart.h"
26
27 #include "thread/domtree.h"
28 #include "thread/respopup.h"
29
30 #include "kitaui/htmlview.h"
31
32 #include "libkita/kitaconfig.h"
33 #include "libkita/datmanager.h"
34 #include "libkita/boardmanager.h"
35 #include "libkita/datinfo.h"
36 #include "libkita/kita_misc.h"
37 #include "libkita/signalcollection.h"
38 #include "libkita/config_xt.h"
39 #include "libkita/abone.h"
40
41 /*-------------------------------------------*/
42
43
44 /*-------------------------------------*/
45 /* Don't forget to call setup() later. */
46
47 KitaPreviewPart::KitaPreviewPart( QWidget* parent, const char* name )
48         : KHTMLPart( new KitaHTMLView( this, parent, name ) )
49 {
50     m_popup = NULL;
51     m_datURL = QString::null;
52
53     clearPart();
54     createHTMLDocument();
55     connectSignals();
56 }
57
58 KitaPreviewPart::~KitaPreviewPart()
59 {
60     clearPart();
61 }
62
63 /* private */
64 void KitaPreviewPart::clearPart()
65 {
66     slotDeletePopup();
67
68     if ( !m_datURL.isEmpty() ) {  /* This part is opened. */
69
70         /* don't forget to unlock previous datURL here. */
71         Kita::DatManager::unlock( m_datURL );
72     }
73     m_datURL = QString::null;
74 }
75
76 /* public */
77 bool KitaPreviewPart::setup( const KURL& url )
78 {
79     if ( url.isEmpty() ) return FALSE;
80
81     clearPart();
82
83     m_datURL = Kita::getDatURL( url );
84
85     /* Lock datURL. Don't forget to unlock it later ! */
86     Kita::DatManager::lock ( m_datURL );
87
88     /* create HTML Document */
89     createHTMLDocument();
90
91     return TRUE;
92 }
93
94 /* private */
95 void KitaPreviewPart::connectSignals()
96 {
97     Kita::SignalCollection * signalCollection = Kita::SignalCollection::getInstance();
98
99     /* rendering */
100     connect( signalCollection, SIGNAL( threadFaceChanged() ), SLOT( slotSetFaceOfHTMLPart() ) );
101     connect( signalCollection, SIGNAL( setStyleSheetOfHTMLPart() ), SLOT( slotSetStyleSheetOfHTMLPart() ) );
102
103     /* popup */
104     connect( this, SIGNAL( onURL( const QString& ) ), SLOT( slotOnURL( const QString& ) ) );
105     connect( this, SIGNAL( isKitaActive() ), signalCollection, SIGNAL( isKitaActive() ) );
106
107     connect( view(), SIGNAL( leave() ), SLOT( slotLeave() ) );
108     connect( view(), SIGNAL( verticalSliderReleased() ), SLOT( slotVSliderReleased() ) );
109     connect( view(), SIGNAL( horizontalSliderReleased() ), SLOT( slotHSliderReleased() ) );
110
111     connect( signalCollection, SIGNAL( kitaIsActive() ), SLOT( slotKitaIsActive() ) );
112     connect( signalCollection, SIGNAL( windowDeactivated() ), SLOT( slotHideChildPopup() ) );
113
114     /* click */
115     connect( this, SIGNAL( openURLRequestExt(
116                                const KURL&, const KParts::URLArgs&, QString, int,
117                                const KURL& ) ),
118              signalCollection, SIGNAL( openURLRequestExt(
119                                            const KURL& , const KParts::URLArgs&, QString, int,
120                                            const KURL& ) ) );
121 }
122
123 /* private */
124 void KitaPreviewPart::createHTMLDocument()
125 {
126     /* style */
127     QString style = QString( "body { font-size: %1pt; font-family: \"%2\"; color: %3; background-color: %4; }" )
128                     .arg( Kita::Config::threadFont().pointSize() )
129                     .arg( Kita::Config::threadFont().family() )
130                     .arg( Kita::Config::threadColor().name() )
131                     .arg( Kita::Config::threadBackground().name() );
132
133     QString text = "<html><head><style>";
134     text += KitaConfig::defaultStyleSheetText();
135     text += style;
136     if ( Kita::Config::useStyleSheet() ) {
137         text += KitaConfig::styleSheetText();
138     }
139     text += "</style></head><body></body></html>";
140
141     setJScriptEnabled( false );
142     setJavaEnabled( false );
143
144     /* Use dummy URL here, and protocol should be "file:".
145        If protocol is "http:", local image files are not shown
146        (for security reasons ?).
147      */
148     begin( "file:/dummy.htm" );
149     write( text );
150     end();
151 }
152
153 /*---------------------------------------------------------------*/
154 /*---------------------------------------------------------------*/
155 /* direct rendering functions */
156
157 /* public */
158 void KitaPreviewPart::setInnerHTML( const QString& innerHTML )
159 {
160     createHTMLDocument();
161     htmlDocument().body().setInnerHTML( innerHTML );
162 }
163
164 /* public slot */
165 void KitaPreviewPart::slotSetFaceOfHTMLPart()
166 {
167     QFont font = Kita::Config::threadFont();
168
169     DOM::CSSStyleDeclaration style = htmlDocument().body().style();
170     style.setProperty( "font-family", font.family(), "" );
171     style.setProperty( "font-size", QString( "%1pt" ).arg( font.pointSize() ), "" );
172     style.setProperty( "color", Kita::Config::threadColor().name(), "" );
173     style.setProperty( "background-color", Kita::Config::threadBackground().name(), "" );
174     htmlDocument().applyChanges();
175 }
176
177 /* public slot */
178 void KitaPreviewPart::slotSetStyleSheetOfHTMLPart()
179 {
180     /* [0]<html> -> [1]<head> -> [2]<style> */
181     DOM::HTMLCollection collection = htmlDocument().all();
182     DOM::HTMLElement element;
183     unsigned int i;
184     for ( i = 0 ; i < collection.length() ; i++ ) {
185         element = collection.item( i );
186         if ( element.tagName().upper() == "STYLE" ) {
187             QString style = QString( "body { font-size: %1pt; font-family: %2; color: %3; background-color: %4; }" )
188                             .arg( Kita::Config::threadFont().pointSize() )
189                             .arg( Kita::Config::threadFont().family() )
190                             .arg( Kita::Config::threadColor().name() )
191                             .arg( Kita::Config::threadBackground().name() );
192
193             QString style0 = KitaConfig::defaultStyleSheetText();
194             style0 += style;
195             if ( Kita::Config::useStyleSheet() ) {
196                 style0 += KitaConfig::styleSheetText();
197             }
198
199             element.setInnerText( style0 );
200             htmlDocument().applyChanges();
201             break;
202         }
203     }
204 }
205
206 /*---------------------------------------------------------------*/
207 /*---------------------------------------------------------------*/
208 /* user event */
209
210 /* protected */ /* virtual */
211 void KitaPreviewPart::customEvent( QCustomEvent * e )
212 {
213     if ( e->type() == EVENT_GotoAnchor ) {
214         KHTMLPart::gotoAnchor( static_cast< GotoAnchorEvent* >( e ) ->getAnc() );
215         return ;
216     }
217
218     KHTMLPart::customEvent( e );
219 }
220
221 /*---------------------------------------------------------------*/
222 /*---------------------------------------------------------------*/
223 /* mouse event */
224
225 /* protected */
226 void KitaPreviewPart::khtmlMousePressEvent( khtml::MousePressEvent* e )
227 {
228     emit mousePressed(); /* to KitaThreadView to focus this view. */
229
230     KURL kurl;
231     if ( e->url().string() != QString::null ) {
232         kurl = KURL( Kita::BoardManager::boardURL( m_datURL ), e->url().string() );
233     }
234
235     if ( e->qmouseEvent()->button() & Qt::RightButton ) {
236         m_isPushedRightButton = TRUE;
237     } else {
238         m_isPushedRightButton = FALSE;
239     }
240
241     if ( e->url() != NULL ) {
242
243         if ( e->url().string().at( 0 ) == '#' ) { /* anchor */
244             kurl = m_datURL;
245             kurl.setRef( e->url().string().mid( 1 ) ) ;
246         }
247
248         clickAnchor( kurl );
249         m_isPushedRightButton = FALSE;
250         return;
251     }
252
253     KHTMLPart::khtmlMousePressEvent( e );
254 }
255
256 /*-------------------------------------------------------*/
257 /*-------------------------------------------------------*/
258 /* click */
259
260 /*------------------------------------------------------*/
261 /* This function is called when user clicked res anchor */ /* private */
262 void KitaPreviewPart::clickAnchor( const KURL& urlin )
263 {
264     QString refstr;
265     KURL datURL = Kita::getDatURL( urlin , refstr );
266
267     /*--------------------------------*/
268     /* If this is not anchor, then    */
269     /* emit openURLRequest and return */
270
271     if ( datURL.host() != m_datURL.host() || datURL.path() != m_datURL.path() ) {
272         emit openURLRequestExt( urlin );
273         return ;
274     }
275
276     if ( refstr == QString::null ) return ;
277
278     /*-------------------------*/
279     /* start multi-popup mdde  */
280     if ( m_isPushedRightButton && startMultiPopup() ) return ;
281
282     /*-------------------------------*/
283     /* open Kita Navi or goto anchor */
284
285     int refNum, refNum2;
286
287     int i = refstr.find( "-" );
288     if ( i != -1 ) {
289         refNum = refstr.left( i ).toInt();
290         refNum2 = refstr.mid( i + 1 ).toInt();
291         if ( refNum2 < refNum ) refNum2 = refNum;
292     } else refNum = refNum2 = refstr.toInt();
293
294     if ( !refNum ) return ;
295
296     emit openURLRequestExt( urlin );
297 }
298
299 /*-------------------------------------------------------*/
300 /*-------------------------------------------------------*/
301 /* popup */
302
303 void KitaPreviewPart::slotDeletePopup()
304 {
305     if ( m_popup ) delete m_popup;
306     m_popup = NULL;
307     m_multiPopup = FALSE;
308 }
309
310 /* for convenience */ /* private */
311 void KitaPreviewPart::showPopup( const KURL& url, const QString& innerHTML )
312 {
313     showPopupCore( url, innerHTML, QCursor::pos() );
314 }
315
316 /* show popup window   */  /* private */
317 void KitaPreviewPart::showPopupCore( const KURL& url, const QString& innerHTML, QPoint point )
318 {
319     slotDeletePopup();
320     m_multiPopup = FALSE;
321
322     m_popup = new Kita::ResPopup( view() , url );
323
324     connect( m_popup, SIGNAL( hideChildPopup() ), SLOT( slotHideChildPopup() ) );
325
326     m_popup->setText( innerHTML );
327     m_popup->adjustSize();
328     m_popup->adjustPos( point );
329     m_popup->show();
330 }
331
332 /*------------------------*/
333 /* start multi-popup mode */ /* private */
334 bool KitaPreviewPart::startMultiPopup()
335 {
336     if ( m_popup && m_popup->isVisible() ) {
337         m_multiPopup = TRUE;
338         m_popup->moveMouseAbove();
339     } else {
340         m_multiPopup = FALSE;
341     }
342
343     return m_multiPopup;
344 }
345
346 /* Is it multi-popup mode now ? */ /* private */
347 bool KitaPreviewPart::isMultiPopupMode()
348 {
349     if ( !m_popup ) m_multiPopup = FALSE;
350     else if ( m_popup->isHidden() ) m_multiPopup = FALSE;
351
352     return m_multiPopup;
353 }
354
355 /* private */
356 void KitaPreviewPart::hidePopup()
357 {
358     if ( m_popup ) m_popup->hide();
359     m_multiPopup = FALSE;
360 }
361
362 /* private slot */
363 void KitaPreviewPart::slotLeave()
364 {
365     if ( isMultiPopupMode() ) return ;
366     if ( view() ->isHorizontalSliderPressed() ) return ;
367     if ( view() ->isVerticalSliderPressed () ) return ;
368
369     hidePopup();
370 }
371
372 /* private slot */
373 void KitaPreviewPart::slotVSliderReleased()
374 {
375     QScrollBar * bar = view() ->verticalScrollBar();
376     QRect rt = bar->sliderRect();
377
378     hidePopup();
379 }
380
381 /* private slot */
382 void KitaPreviewPart::slotHSliderReleased()
383 {
384     QScrollBar * bar = view() ->horizontalScrollBar();
385     QRect rt = bar->sliderRect();
386
387     hidePopup();
388 }
389
390 /* private slot */
391 void KitaPreviewPart::slotHideChildPopup()
392 {
393     hidePopup();
394 }
395
396 /*------------------------------------------*/
397 /* called back when kita is active .
398    see also an explanation in slotOnURL.    */ /* private slot */
399 void KitaPreviewPart::slotKitaIsActive()
400 {
401     m_kitaIsActive = TRUE;
402 }
403
404 /*---------------------------------------------------*/
405 /* This slot is called when mouse moves onto the URL */ /* private slot */
406 void KitaPreviewPart::slotOnURL( const QString& url )
407 {
408     /* config */
409
410     const int maxpopup = 10;  /* max number of responses shown in the popup window */
411
412     /*----------------------------*/
413
414     if ( isMultiPopupMode() ) return ;
415
416     slotDeletePopup();
417
418     if ( url.isEmpty() ) return ;
419     if ( url.left( 7 ) == "mailto:" ) return ;
420
421     /* Is Kita active now ?
422
423        emit SIGNAL( isKitaActive() ) to KitaMainWindow, KitaNavi, etc. ,
424        and if one of them is active, then slotKitaIsActive() is called
425        back, and m_kitaIsActive is set to TRUE.   */
426     m_kitaIsActive = FALSE;
427     emit isKitaActive();
428     if ( !m_kitaIsActive ) return ;
429
430     /* get reference */
431     QString refstr;
432     KURL datURL = m_datURL;
433     if ( url.at( 0 ) == '#' ) refstr = url.mid( 1 );
434     else datURL = Kita::getDatURL( KURL( m_datURL, url ) , refstr );
435
436     /*-------------------------*/
437     /* popup for anchor        */
438
439     QString innerHTML = QString::null;
440     int refNum;
441     int refNum2;
442
443     int i = refstr.find( "-" );
444     if ( i != -1 ) { /* >>refNum-refNum2 */
445
446         refNum = refstr.left( i ).toInt();
447         refNum2 = refstr.mid( i + 1 ).toInt();
448
449         if ( refNum ) {
450             if ( refNum2 < refNum ) refNum2 = refNum;
451             if ( refNum2 - refNum > maxpopup - 1 ) refNum2 = refNum + maxpopup - 1;
452         }
453
454     } else { /* >>refNum */
455         refNum = refstr.toInt();
456         refNum2 = refNum;
457     }
458
459     /* another thread ? */
460     if ( datURL.host() != m_datURL.host() || datURL.path() != m_datURL.path() ) {
461
462         /* get board name */
463         QString boardName = Kita::BoardManager::boardName( datURL );
464         if ( boardName != QString::null ) innerHTML += "[" + boardName + "] ";
465
466         /* If idx file of datURL is not read, thread name cannot be obtained.
467            so, create DatInfo if cache exists, and read idx file in DatInfo::DatInfo(). */
468         Kita::DatManager::getDatInfoPointer( datURL );
469
470         /* get thread Name */
471         QString subName = Kita::DatManager::threadName( datURL );
472         if ( subName != QString::null ) innerHTML += subName + "<br><br>";
473
474         if ( !refNum ) refNum = refNum2 = 1;
475     }
476
477     /* get HTML and show it */
478     if ( !refNum ) return ;
479     innerHTML += Kita::DatManager::getHtml( datURL, refNum, refNum2 );
480
481     if ( innerHTML != QString::null ) showPopup( datURL, innerHTML );
482 }