OSDN Git Service

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