/* * This file is part of the KDE Help Center * * Copyright (C) 2002 Frerich Raabe * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "history.h" #include "view.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace KHC; // TODO: Needs complete redo! // TODO: oh yeah History *History::m_instance = 0; History &History::self() { if ( !m_instance ) m_instance = new History; return *m_instance; } History::History() : QObject(), m_goBuffer( 0 ) { m_entries_current = m_entries.end(); } History::~History() { qDeleteAll(m_entries); } void History::setupActions( KActionCollection *coll ) { QPair backForward = KStandardGuiItem::backAndForward(); m_backAction = new KToolBarPopupAction( KIcon( backForward.first.iconName() ), backForward.first.text(), this ); coll->addAction( "back", m_backAction ); m_backAction->setShortcut(KStandardShortcut::back()); connect( m_backAction, SIGNAL( triggered() ), this, SLOT( back() ) ); connect( m_backAction->menu(), SIGNAL( triggered( QAction* ) ), SLOT( backActivated( QAction* ) ) ); connect( m_backAction->menu(), SIGNAL( aboutToShow() ), SLOT( fillBackMenu() ) ); m_backAction->setEnabled( false ); m_forwardAction = new KToolBarPopupAction( KIcon( backForward.second.iconName() ), backForward.second.text(), this ); coll->addAction( QLatin1String("forward"), m_forwardAction ); m_forwardAction->setShortcut(KStandardShortcut::forward()); connect( m_forwardAction, SIGNAL( triggered() ), this, SLOT( forward() ) ); connect( m_forwardAction->menu(), SIGNAL( triggered( QAction* ) ), SLOT( forwardActivated( QAction* ) ) ); connect( m_forwardAction->menu(), SIGNAL( aboutToShow() ), SLOT( fillForwardMenu() ) ); m_forwardAction->setEnabled( false ); } void History::installMenuBarHook( KXmlGuiWindow *mainWindow ) { QMenu *goMenu = dynamic_cast( mainWindow->guiFactory()->container( QLatin1String("go_web"), mainWindow ) ); if ( goMenu ) { connect( goMenu, SIGNAL( aboutToShow() ), SLOT( fillGoMenu() ) ); connect( goMenu, SIGNAL( triggered( QAction* ) ), SLOT( goMenuActivated( QAction* ) ) ); m_goMenuIndex = goMenu->actions().count(); } } void History::createEntry() { kDebug() << "History::createEntry()"; // First, remove any forward history if (m_entries_current!=m_entries.end()) { m_entries.erase(m_entries.begin(),m_entries_current); // If current entry is empty reuse it. if ( !(*m_entries_current)->view ) { return; } } // Append a new entry m_entries_current = m_entries.insert(m_entries_current, new Entry ); // made current } void History::updateCurrentEntry( View *view ) { if ( m_entries.isEmpty() ) return; KUrl url = view->url(); Entry *current = *m_entries_current; QDataStream stream( ¤t->buffer, QIODevice::WriteOnly ); current->view = view; if ( url.isEmpty() ) { kDebug() << "History::updateCurrentEntry(): internal url"; url = view->internalUrl(); } kDebug() << "History::updateCurrentEntry(): " << view->title() << " (URL: " << url.url() << ")" << endl; current->url = url; current->title = view->title(); current->search = view->state() == View::Search; } void History::updateActions() { m_backAction->setEnabled( canGoBack() ); m_forwardAction->setEnabled( canGoForward() ); } void History::back() { kDebug( 1400 ) << "History::back()"; goHistoryActivated( -1 ); } void History::backActivated( QAction *action ) { int id = action->data().toInt(); kDebug( 1400 ) << "History::backActivated(): id = " << id; goHistoryActivated( -( id + 1 ) ); } void History::forward() { kDebug( 1400 ) << "History::forward()"; goHistoryActivated( 1 ); } void History::forwardActivated( QAction *action ) { int id = action->data().toInt(); kDebug( 1400 ) << "History::forwardActivated(): id = " << id; goHistoryActivated( id + 1 ); } void History::goHistoryActivated( int steps ) { kDebug( 1400 ) << "History::goHistoryActivated(): m_goBuffer = " << m_goBuffer; if ( m_goBuffer ) return; m_goBuffer = steps; QTimer::singleShot( 0, this, SLOT( goHistoryDelayed() ) ); } void History::goHistoryDelayed() { kDebug( 1400 ) << "History::goHistoryDelayed(): m_goBuffer = " << m_goBuffer; if ( !m_goBuffer ) return; int steps = m_goBuffer; m_goBuffer = 0; goHistory( steps ); } void History::goHistory( int steps ) { kDebug() << "History::goHistory(): " << steps; // If current entry is empty remove it. Entry *current = *m_entries_current; if ( current && !current->view ) m_entries_current = m_entries.erase(m_entries_current); EntryList::iterator newPos = m_entries_current - steps; current = *newPos; if ( !current ) { kError() << "No History entry at position " << newPos - m_entries.begin() << endl; return; } if ( !current->view ) { kWarning() << "Empty history entry." ; return; } m_entries_current = newPos; if ( current->search ) { kDebug() << "History::goHistory(): search"; current->view->lastSearch(); return; } if ( current->url.protocol() == QLatin1String("khelpcenter") ) { kDebug() << "History::goHistory(): internal"; emit goInternalUrl( current->url ); return; } kDebug() << "History::goHistory(): restore state"; emit goUrl( current->url ); Entry h( *current ); h.buffer.detach(); QDataStream stream( h.buffer ); updateCurrentEntry( h.view ); updateActions(); } void History::fillBackMenu() { QMenu *menu = m_backAction->menu(); menu->clear(); fillHistoryPopup( menu, true, false, false ); } void History::fillForwardMenu() { QMenu *menu = m_forwardAction->menu(); menu->clear(); fillHistoryPopup( menu, false, true, false ); } void History::fillGoMenu() { KXmlGuiWindow *mainWindow = static_cast( kapp->activeWindow() ); QMenu *goMenu = dynamic_cast( mainWindow->guiFactory()->container( QLatin1String( "go" ), mainWindow ) ); if ( !goMenu || m_goMenuIndex == -1 ) return; for ( int i = goMenu->actions().count() - 1 ; i >= m_goMenuIndex; i-- ) goMenu->removeAction( goMenu->actions()[i] ); // TODO perhaps smarter algorithm (rename existing items, create new ones only if not enough) ? // Ok, we want to show 10 items in all, among which the current url... if ( m_entries.count() <= 9 ) { // First case: limited history in both directions -> show it all m_goMenuHistoryStartPos = m_entries.count() - 1; // Start right from the end } else // Second case: big history, in one or both directions { // Assume both directions first (in this case we place the current URL in the middle) m_goMenuHistoryStartPos = (m_entries_current - m_entries.begin()) + 4; // Forward not big enough ? if ( m_goMenuHistoryStartPos > (int) m_entries.count() - 4 ) m_goMenuHistoryStartPos = m_entries.count() - 1; } Q_ASSERT( m_goMenuHistoryStartPos >= 0 && (int) m_goMenuHistoryStartPos < m_entries.count() ); m_goMenuHistoryCurrentPos = m_entries_current - m_entries.begin(); // for slotActivated fillHistoryPopup( goMenu, false, false, true, m_goMenuHistoryStartPos ); } void History::goMenuActivated( QAction* action ) { KXmlGuiWindow *mainWindow = static_cast( kapp->activeWindow() ); QMenu *goMenu = dynamic_cast( mainWindow->guiFactory()->container( QLatin1String( "go" ), mainWindow ) ); if ( !goMenu ) return; // 1 for first item in the list, etc. int index = goMenu->actions().indexOf(action) - m_goMenuIndex + 1; if ( index > 0 ) { kDebug(1400) << "Item clicked has index " << index; // -1 for one step back, 0 for don't move, +1 for one step forward, etc. int steps = ( m_goMenuHistoryStartPos+1 ) - index - m_goMenuHistoryCurrentPos; // make a drawing to understand this :-) kDebug(1400) << "Emit activated with steps = " << steps; goHistory( steps ); } } void History::fillHistoryPopup( QMenu *popup, bool onlyBack, bool onlyForward, bool checkCurrentItem, uint startPos ) { Q_ASSERT ( popup ); // kill me if this 0... :/ Entry * current = *m_entries_current; QList::iterator it = m_entries.begin(); if (onlyBack || onlyForward) { it = m_entries_current; // Jump to current item // And move off it if ( !onlyForward ) { if ( it != m_entries.end() ) ++it; } else { if ( it != m_entries.begin() ) --it; } } else if ( startPos ) it += startPos; // Jump to specified start pos uint i = 0; while ( it != m_entries.end() ) { QString text = (*it)->title; text = KStringHandler::csqueeze(text, 50); //CT: squeeze text.replace( '&', "&&" ); QAction *action = popup->addAction( text ); action->setData( i ); if ( checkCurrentItem && *it == current ) { action->setChecked( true ); // no pixmap if checked } if ( ++i > 10 ) break; if ( !onlyForward ) { ++it; } else { if ( it == m_entries.begin() ) { it = m_entries.end(); } else { --it; } } } } bool History::canGoBack() const { return m_entries.size()>1 && EntryList::const_iterator(m_entries_current) != (m_entries.begin()+(m_entries.size()-1)); } bool History::canGoForward() const { return EntryList::const_iterator(m_entries_current) != m_entries.constBegin() && m_entries.size() > 1; } void History::dumpHistory() const { for(EntryList::const_iterator it = m_entries.constBegin() ; it!=m_entries.constEnd() ; ++it) { kDebug() << (*it)->title << (*it)->url << (it==EntryList::const_iterator(m_entries_current) ? "current" : "" ) ; } } #include "moc_history.cpp" // vim:ts=2:sw=2:et