akregator/src

pageviewer.cpp

00001 /*
00002     This file is part of Akregator.
00003 
00004     Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net>
00005                   2005 Frank Osterfeld <frank.osterfeld at kdemail.net>
00006 
00007     This program is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020 
00021     As a special exception, permission is given to link this program
00022     with any edition of Qt, and distribute the resulting executable,
00023     without including the source code for Qt in the source distribution.
00024 */
00025 
00026 #include "akregatorconfig.h"
00027 #include "akregator_run.h" 
00028 #include "feediconmanager.h"
00029 #include "pageviewer.h"
00030 #include "viewer.h"
00031 
00032 #include <kaction.h>
00033 #include <kapplication.h>
00034 #include <kbookmark.h>
00035 #include <kbookmarkmanager.h>
00036 #include <kconfig.h>
00037 #include <kglobalsettings.h>
00038 #include <khtml_settings.h>
00039 #include <khtmlview.h>
00040 #include <kiconloader.h>
00041 #include <klocale.h>
00042 #include <kpopupmenu.h>
00043 #include <kstandarddirs.h>
00044 #include <kstdaccel.h>
00045 #include <kparts/browserinterface.h>
00046 
00047 #include <qclipboard.h>
00048 #include <qcstring.h>
00049 #include <qdatastream.h>
00050 #include <qdatetime.h>
00051 #include <qfile.h>
00052 #include <qmetaobject.h>
00053 #include <qscrollview.h>
00054 #include <qstring.h>
00055 #include <qvaluelist.h>
00056 #include <private/qucomextra_p.h>
00057 
00058 #include <cstdlib>
00059 using std::abs;
00060 
00061 namespace Akregator {
00062 
00063 
00064 // taken from KDevelop
00065 class PageViewer::HistoryEntry
00066 {
00067     public:
00068 
00069     KURL url;
00070     QString title;
00071     QByteArray state;
00072     int id;
00073 
00074     HistoryEntry() {}
00075     HistoryEntry(const KURL& u, const QString& t=QString::null): url(u), title(t)   
00076     {
00077         id = abs( QTime::currentTime().msecsTo( QTime() ) );    // nasty, but should provide a reasonably unique number
00078     }
00079     
00080 };
00081 
00082 class PageViewer::PageViewerPrivate
00083 {
00084     public:
00085 
00086     QValueList<HistoryEntry> history;
00087     QValueList<HistoryEntry>::Iterator current;
00088     
00089     KToolBarPopupAction* backAction;
00090     KToolBarPopupAction* forwardAction;
00091     KAction* reloadAction;
00092     KAction* stopAction;
00093     
00094     QString caption;
00095 };           
00096 
00097  
00098 PageViewer::PageViewer(QWidget *parent, const char *name)
00099     : Viewer(parent, name), d(new PageViewerPrivate)
00100 {
00101     // this hack is necessary since the part looks for []HTML Settings] in
00102     // KGlobal::config() by default, which is wrong when running in Kontact
00103     KHTMLSettings* s = const_cast<KHTMLSettings*> (settings());
00104     s->init(Settings::self()->config());
00105     
00106     setXMLFile(locate("data", "akregator/pageviewer.rc"), true);
00107 
00108     QPair< KGuiItem, KGuiItem > backForward = KStdGuiItem::backAndForward();
00109 
00110     d->backAction = new KToolBarPopupAction(backForward.first, 
00111                                 KStdAccel::shortcut(KStdAccel::Back), this, 
00112                                 SLOT(slotBack()), actionCollection(), 
00113                                 "pageviewer_back");
00114 
00115     connect(d->backAction->popupMenu(), SIGNAL(aboutToShow()),
00116             this, SLOT(slotBackAboutToShow()));
00117     connect(d->backAction->popupMenu(), SIGNAL(activated(int)),
00118             this, SLOT(slotPopupActivated(int)));
00119 
00120     
00121     d->forwardAction = new KToolBarPopupAction(backForward.second, 
00122                                 KStdAccel::shortcut(KStdAccel::Forward),this, 
00123                                 SLOT(slotForward()), actionCollection(), 
00124                                 "pageviewer_forward");
00125 
00126     connect(d->forwardAction->popupMenu(), SIGNAL(aboutToShow()),
00127             this, SLOT(slotForwardAboutToShow()));
00128     connect(d->forwardAction->popupMenu(), SIGNAL(activated(int)),
00129             this, SLOT(slotPopupActivated(int)));
00130 
00131     d->reloadAction = new KAction(i18n("Reload"), "reload", 0,
00132                             this, SLOT(slotReload()),
00133                             actionCollection(), "pageviewer_reload");
00134     d->stopAction = new KAction(KStdGuiItem::guiItem(KStdGuiItem::Stop), 0,
00135                                  this, SLOT(slotStop()),
00136                                  actionCollection(), "pageviewer_stop");
00137  
00138     //connect( this, SIGNAL(popupMenu(const QString &, const QPoint &)), this, SLOT(slotPopupMenu(const QString &, const QPoint &)));
00139 
00140     d->backAction->setEnabled(false);
00141     d->forwardAction->setEnabled(false);
00142     d->stopAction->setEnabled(false);
00143     
00144     connect( this, SIGNAL(setWindowCaption (const QString &)),
00145             this, SLOT(slotSetCaption (const QString &)) );
00146 
00147     connect(this, SIGNAL(started(KIO::Job *)), this, SLOT(slotStarted(KIO::Job* )));
00148     connect(this, SIGNAL(completed()), this, SLOT(slotCompleted()));
00149     connect(this, SIGNAL(canceled(const QString &)), this, SLOT(slotCancelled(const QString &)));
00150 
00151     d->current = d->history.end();
00152 
00153     // uncomment this to load konq plugins (doesn't work properly and clutters the GUI)
00154     //loadPlugins( partObject(), this, instance() );
00155     
00156 }
00157 
00158 PageViewer::~PageViewer()
00159 {
00160     delete d;
00161     d = 0;
00162 }
00163 
00164 // Taken from KDevelop (lib/widgets/kdevhtmlpart.cpp)
00165 void PageViewer::slotBack()
00166 {
00167     if ( d->current != d->history.begin() )
00168     {
00169         QValueList<HistoryEntry>::Iterator tmp = d->current;
00170         --tmp;
00171         restoreHistoryEntry(tmp);
00172     }
00173 }
00174 
00175 // Taken from KDevelop (lib/widgets/kdevhtmlpart.cpp)
00176 void PageViewer::slotForward()
00177 {
00178     if ( d->current != d->history.fromLast() && d->current != d->history.end() )
00179     {
00180         QValueList<HistoryEntry>::Iterator tmp = d->current;
00181         ++tmp;
00182         restoreHistoryEntry(tmp);
00183     }
00184 }
00185 
00186 void PageViewer::slotBackAboutToShow()
00187 {
00188     KPopupMenu *popup = d->backAction->popupMenu();
00189     popup->clear();
00190 
00191     if ( d->current == d->history.begin() )
00192         return;
00193 
00194     QValueList<HistoryEntry>::Iterator it = d->current;
00195     --it;
00196     
00197     int i = 0;
00198     while( i < 10 )
00199     {
00200         if ( it == d->history.begin() )
00201         {
00202             popup->insertItem( (*it).title, (*it).id );
00203             return;
00204         }
00205         
00206         popup->insertItem( (*it).title, (*it).id );
00207         ++i;
00208         --it;
00209     }
00210 }
00211 
00212 void PageViewer::slotForwardAboutToShow()
00213 {
00214     KPopupMenu *popup = d->forwardAction->popupMenu();
00215     popup->clear();
00216 
00217     if ( d->current == d->history.fromLast() )
00218         return;
00219 
00220     QValueList<HistoryEntry>::Iterator it = d->current;
00221     ++it;
00222     
00223     int i = 0;
00224     while( i < 10 )
00225     {
00226         if ( it == d->history.fromLast() )
00227         {
00228             popup->insertItem( (*it).title, (*it).id );
00229             return;
00230         }
00231         
00232         popup->insertItem( (*it).title, (*it).id );
00233         ++i;
00234         ++it;
00235     }
00236 }
00237 
00238 
00239 void PageViewer::slotReload()
00240 {
00241     openURL( url() );
00242 }
00243 
00244 void PageViewer::slotStop()
00245 {
00246     closeURL();
00247 }
00248 
00249 bool PageViewer::openURL(const KURL& url)
00250 {
00251     updateHistoryEntry(); // update old history entry before switching to the new one
00252     emit started(0);
00253 
00254     bool val = KHTMLPart::openURL(url);
00255     
00256     addHistoryEntry(url); // add new URL to history
00257     
00258     d->backAction->setEnabled( d->current != d->history.begin() );
00259     d->forwardAction->setEnabled( d->current != d->history.fromLast() );
00260   
00261     QString favicon = FeedIconManager::self()->iconLocation(url);
00262     if (!favicon.isEmpty()) 
00263         emit setTabIcon(QPixmap(KGlobal::dirs()->findResource("cache", favicon+".png")));
00264     else
00265         emit setTabIcon(SmallIcon("html"));
00266 
00267     return val;
00268 }
00269 
00270 
00271 void PageViewer::slotOpenURLRequest(const KURL& url, const KParts::URLArgs& args)
00272 {
00273     updateHistoryEntry();
00274     if (args.doPost())
00275     {
00276         browserExtension()->setURLArgs(args);
00277         openURL(url);
00278     }
00279 
00280 }
00281 
00282 void PageViewer::slotPopupActivated( int id )
00283 {
00284     QValueList<HistoryEntry>::Iterator it = d->history.begin();
00285     while( it != d->history.end() )
00286     {
00287         if ( (*it).id == id )
00288         {
00289             restoreHistoryEntry(it);
00290             return;
00291         }
00292         ++it;
00293     }
00294 }
00295 
00296 void PageViewer::updateHistoryEntry()
00297 {
00298     (*d->current).title = d->caption;
00299     (*d->current).state = QByteArray(); // Start with empty buffer.
00300     QDataStream stream( (*d->current).state, IO_WriteOnly);
00301     browserExtension()->saveState(stream);
00302 }
00303 
00304 void PageViewer::restoreHistoryEntry(const QValueList<HistoryEntry>::Iterator& entry)
00305 {
00306     updateHistoryEntry();
00307     
00308     QDataStream stream( (*entry).state, IO_ReadOnly );
00309     browserExtension()->restoreState( stream );
00310     d->current = entry;
00311     d->backAction->setEnabled( d->current != d->history.begin() );
00312     d->forwardAction->setEnabled( d->current != d->history.fromLast() );
00313     //openURL( entry.url ); // TODO read state
00314     
00315 
00316 }
00317 
00318 // Taken from KDevelop (lib/widgets/kdevhtmlpart.cpp)
00319 void PageViewer::addHistoryEntry(const KURL& url)
00320 {
00321     QValueList<HistoryEntry>::Iterator it = d->current;
00322     
00323     // if We're not already the last entry, we truncate the list here before adding an entry
00324     if ( it != d->history.end() && it != d->history.fromLast() )
00325     {
00326         d->history.erase( ++it, d->history.end() );
00327     }
00328     HistoryEntry newEntry( url, url.url() );
00329 
00330     // Only save the new entry if it is different from the last
00331     if ( newEntry.url != (*d->current).url )
00332     {
00333         d->history.append( newEntry );
00334         d->current = d->history.fromLast();
00335     }
00336     updateHistoryEntry();
00337 }
00338 
00339 // Taken from KDevelop (lib/widgets/kdevhtmlpart.cpp)
00340 void PageViewer::slotStarted( KIO::Job * )
00341 {
00342     d->stopAction->setEnabled(true);
00343 }
00344 
00345 // Taken from KDevelop (lib/widgets/kdevhtmlpart.cpp)
00346 void PageViewer::slotCompleted( )
00347 {
00348     d->stopAction->setEnabled(false);
00349 }
00350 
00351 // Taken from KDevelop (lib/widgets/kdevhtmlpart.cpp)
00352 void PageViewer::slotCancelled( const QString & /*errMsg*/ )
00353 {
00354     d->stopAction->setEnabled(false);
00355 }
00356 
00357 void PageViewer::urlSelected(const QString &url, int button, int state, const QString &_target, KParts::URLArgs args)
00358 {
00359     if (button == LeftButton)
00360     {
00361         m_url = completeURL(url);
00362         browserExtension()->setURLArgs(args); 
00363         slotOpenLinkInThisTab();
00364     }
00365     else
00366     {
00367         Viewer::urlSelected(url,button,state,_target,args);
00368     }
00369 }
00370 
00371 void PageViewer::slotSetCaption(const QString& cap) 
00372 {
00373     d->caption = cap;
00374     (*d->current).title = cap;
00375 }
00376 
00377 void PageViewer::slotPaletteOrFontChanged()
00378 {
00379     kdDebug() << "PageViewer::slotPaletteOrFontChanged()" << endl;
00380     // taken from KonqView (kdebase/konqueror/konq_view.cc)
00381     
00382     QObject *obj = KParts::BrowserExtension::childObject(this);
00383     if ( !obj ) // not all views have a browser extension !
00384         return;
00385     
00386     int id = obj->metaObject()->findSlot("reparseConfiguration()");
00387     if (id == -1)
00388         return;
00389     QUObject o[1];
00390 
00391     obj->qt_invoke(id, o);
00392     
00393     // this hack is necessary since the part looks for []HTML Settings] in
00394     // KGlobal::config() by default, which is wrong when running in Kontact
00395     // NOTE: when running in Kontact, immediate updating doesn't work
00396     KHTMLSettings* s = const_cast<KHTMLSettings*> (settings());
00397     s->init(Settings::self()->config());
00398 }
00399 
00400 void PageViewer::slotGlobalBookmarkArticle()
00401 {
00402     KBookmarkManager *mgr = KBookmarkManager::userBookmarksManager();
00403     KBookmarkGroup grp = mgr->root();
00404     grp.addBookmark(mgr, d->caption, toplevelURL());
00405     mgr->emitChanged(grp);
00406     mgr->save();
00407 }
00408 
00409 
00410 void PageViewer::slotPopupMenu(KXMLGUIClient*, const QPoint& p, const KURL& kurl, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags kpf, mode_t)
00411 {
00412     m_url = kurl;
00413     QString url = kurl.url(); // maximal url confusion
00414     
00415     const bool showReload = (kpf & KParts::BrowserExtension::ShowReload) != 0;
00416     const bool showNavigationItems = (kpf & KParts::BrowserExtension::ShowNavigationItems) != 0;
00417     const bool isLink = (kpf & (KParts::BrowserExtension::ShowNavigationItems | KParts::BrowserExtension::ShowTextSelectionItems)) == 0;
00418     const bool isSelection = (kpf & KParts::BrowserExtension::ShowTextSelectionItems) != 0;
00419         
00420     KPopupMenu popup(this->widget());
00421 
00422     int idNewWindow = -2;
00423     if (isLink)
00424     {
00425         idNewWindow = popup.insertItem(SmallIcon("tab_new"),i18n("Open Link in New &Tab"), this, SLOT(slotOpenLinkInForegroundTab()));
00426         popup.setWhatsThis(idNewWindow, i18n("<b>Open Link in New Tab</b><p>Opens current link in a new tab."));
00427         popup.insertItem(SmallIcon("window_new"), i18n("Open Link in External &Browser"), this, SLOT(slotOpenLinkInBrowser()));
00428                 
00429         popup.insertSeparator();
00430         action("savelinkas")->plug(&popup);    
00431         KAction* copylinkaddress = action("copylinkaddress");
00432         if (copylinkaddress)
00433         {
00434             copylinkaddress->plug( &popup);
00435             //popup.insertSeparator();
00436         }
00437     }
00438     else // we are not on a link
00439     {
00440         if (showNavigationItems)
00441         {
00442             d->backAction->plug( &popup );
00443             d->forwardAction->plug( &popup );
00444         }
00445 
00446         if (showReload)
00447             d->reloadAction->plug(&popup);
00448         
00449         d->stopAction->plug(&popup);
00450         
00451         popup.insertSeparator();
00452         
00453         if (isSelection)
00454         {
00455             action("viewer_copy")->plug(&popup);
00456             popup.insertSeparator();
00457         }
00458 
00459         KAction* incFontAction = this->action("incFontSizes");
00460         KAction* decFontAction = this->action("decFontSizes");
00461         if ( incFontAction && decFontAction )
00462         {
00463             incFontAction->plug( &popup );
00464             decFontAction->plug( &popup );
00465             popup.insertSeparator();
00466         }
00467     
00468         popup.insertItem(SmallIcon("window_new"), i18n("Open Page in External Browser"), this, SLOT(slotOpenLinkInBrowser()));
00469     
00470         action("viewer_print")->plug(&popup);
00471         popup.insertSeparator();
00472         
00473         KAction *ac = action("setEncoding");
00474         if (ac)
00475             ac->plug(&popup);
00476         popup.insertItem(SmallIcon("bookmark_add"),i18n("Add to Konqueror Bookmarks"), this, SLOT(slotGlobalBookmarkArticle()));
00477     }
00478     
00479     int r = popup.exec(p);
00480     
00481     if (r == idNewWindow)
00482     {
00483         KURL kurl;
00484         if (!KURL(url).path().startsWith("/"))
00485         {
00486             kdDebug() << "processing relative url: " << url << endl;
00487             if (url.startsWith("#"))
00488             {
00489                 kurl = KURL(PageViewer::url());
00490                 kurl.setRef(url.mid(1));
00491             }
00492             else
00493                 kurl = KURL(PageViewer::url().upURL().url(true)+url);
00494         }
00495         else
00496             kurl = KURL(url);
00497 //    kurl.addPath(url);
00498         if (kurl.isValid()) {
00499             //slotOpenInNewWindow(kurl);
00500         }
00501 //      ( kurl );
00502     }
00503 }
00504 
00505 } // namespace Akregator 
00506 
00507 #include "pageviewer.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys