/* * This file is part of the KDE Help Center * * Copyright (C) 2002 Frerich Raabe <raabe@kde.org> * * 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 <kaction.h> #include <kapplication.h> #include <kdebug.h> #include <kmainwindow.h> #include <kpopupmenu.h> #include <kstdguiitem.h> #include <kstringhandler.h> using namespace KHC; History *History::m_instance = 0; History &History::self() { if ( !m_instance ) m_instance = new History; return *m_instance; } History::History() : TQObject(), m_goBuffer( 0 ) { m_entries.setAutoDelete( true ); } History::~History() { } void History::setupActions( KActionCollection *coll ) { QPair<KGuiItem, KGuiItem> backForward = KStdGuiItem::backAndForward(); m_backAction = new KToolBarPopupAction( backForward.first, ALT+Key_Left, this, TQT_SLOT( back() ), coll, "back" ); connect( m_backAction->popupMenu(), TQT_SIGNAL( activated( int ) ), TQT_SLOT( backActivated( int ) ) ); connect( m_backAction->popupMenu(), TQT_SIGNAL( aboutToShow() ), TQT_SLOT( fillBackMenu() ) ); m_backAction->setEnabled( false ); m_forwardAction = new KToolBarPopupAction( backForward.second, ALT+Key_Right, this, TQT_SLOT( forward() ), coll, "forward" ); connect( m_forwardAction->popupMenu(), TQT_SIGNAL( activated( int ) ), TQT_SLOT( forwardActivated( int ) ) ); connect( m_forwardAction->popupMenu(), TQT_SIGNAL( aboutToShow() ), TQT_SLOT( fillForwardMenu() ) ); m_forwardAction->setEnabled( false ); } void History::installMenuBarHook( KMainWindow *mainWindow ) { TQPopupMenu *goMenu = dynamic_cast<TQPopupMenu *>( mainWindow->guiFactory()->container( "go_web", mainWindow ) ); if ( goMenu ) { connect( goMenu, TQT_SIGNAL( aboutToShow() ), TQT_SLOT( fillGoMenu() ) ); connect( goMenu, TQT_SIGNAL( activated( int ) ), TQT_SLOT( goMenuActivated( int ) ) ); m_goMenuIndex = goMenu->count(); } } void History::createEntry() { kdDebug() << "History::createEntry()" << endl; // First, remove any forward history Entry * current = m_entries.current(); if (current) { m_entries.at( m_entries.count() - 1 ); // go to last one for ( ; m_entries.current() != current ; ) { if ( !m_entries.removeLast() ) { // and remove from the end (faster and easier) Q_ASSERT(0); return; } else m_entries.at( m_entries.count() - 1 ); } // Now current is the current again. // If current entry is empty reuse it. if ( !current->view ) return; } // Append a new entry m_entries.append( new Entry ); // made current Q_ASSERT( m_entries.at() == (int) m_entries.count() - 1 ); } void History::updateCurrentEntry( View *view ) { if ( m_entries.isEmpty() ) return; KURL url = view->url(); Entry *current = m_entries.current(); TQDataStream stream( current->buffer, IO_WriteOnly ); view->browserExtension()->saveState( stream ); current->view = view; if ( url.isEmpty() ) { kdDebug() << "History::updateCurrentEntry(): internal url" << endl; url = view->internalUrl(); } kdDebug() << "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() { kdDebug( 1400 ) << "History::back()" << endl; goHistoryActivated( -1 ); } void History::backActivated( int id ) { kdDebug( 1400 ) << "History::backActivated(): id = " << id << endl; goHistoryActivated( -( m_backAction->popupMenu()->indexOf( id ) + 1 ) ); } void History::forward() { kdDebug( 1400 ) << "History::forward()" << endl; goHistoryActivated( 1 ); } void History::forwardActivated( int id ) { kdDebug( 1400 ) << "History::forwardActivated(): id = " << id << endl; goHistoryActivated( m_forwardAction->popupMenu()->indexOf( id ) + 1 ); } void History::goHistoryActivated( int steps ) { kdDebug( 1400 ) << "History::goHistoryActivated(): m_goBuffer = " << m_goBuffer << endl; if ( m_goBuffer ) return; m_goBuffer = steps; TQTimer::singleShot( 0, this, TQT_SLOT( goHistoryDelayed() ) ); } void History::goHistoryDelayed() { kdDebug( 1400 ) << "History::goHistoryDelayed(): m_goBuffer = " << m_goBuffer << endl; if ( !m_goBuffer ) return; int steps = m_goBuffer; m_goBuffer = 0; goHistory( steps ); } void History::goHistory( int steps ) { kdDebug() << "History::goHistory(): " << steps << endl; // If current entry is empty remove it. Entry *current = m_entries.current(); if ( current && !current->view ) m_entries.remove(); int newPos = m_entries.at() + steps; current = m_entries.at( newPos ); if ( !current ) { kdError() << "No History entry at position " << newPos << endl; return; } if ( !current->view ) { kdWarning() << "Empty history entry." << endl; return; } if ( current->search ) { kdDebug() << "History::goHistory(): search" << endl; current->view->lastSearch(); return; } if ( current->url.protocol() == "khelpcenter" ) { kdDebug() << "History::goHistory(): internal" << endl; emit goInternalUrl( current->url ); return; } kdDebug() << "History::goHistory(): restore state" << endl; emit goUrl( current->url ); Entry h( *current ); h.buffer.detach(); TQDataStream stream( h.buffer, IO_ReadOnly ); h.view->closeURL(); updateCurrentEntry( h.view ); h.view->browserExtension()->restoreState( stream ); updateActions(); } void History::fillBackMenu() { TQPopupMenu *menu = m_backAction->popupMenu(); menu->clear(); fillHistoryPopup( menu, true, false, false ); } void History::fillForwardMenu() { TQPopupMenu *menu = m_forwardAction->popupMenu(); menu->clear(); fillHistoryPopup( menu, false, true, false ); } void History::fillGoMenu() { KMainWindow *mainWindow = static_cast<KMainWindow *>( kapp->mainWidget() ); TQPopupMenu *goMenu = dynamic_cast<TQPopupMenu *>( mainWindow->guiFactory()->container( TQString::fromLatin1( "go" ), mainWindow ) ); if ( !goMenu || m_goMenuIndex == -1 ) return; for ( int i = goMenu->count() - 1 ; i >= m_goMenuIndex; i-- ) goMenu->removeItemAt( 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.at() + 4; // Forward not big enough ? if ( m_entries.at() > (int)m_entries.count() - 4 ) m_goMenuHistoryStartPos = m_entries.count() - 1; } Q_ASSERT( m_goMenuHistoryStartPos >= 0 && (uint)m_goMenuHistoryStartPos < m_entries.count() ); m_goMenuHistoryCurrentPos = m_entries.at(); // for slotActivated fillHistoryPopup( goMenu, false, false, true, m_goMenuHistoryStartPos ); } void History::goMenuActivated( int id ) { KMainWindow *mainWindow = static_cast<KMainWindow *>( kapp->mainWidget() ); TQPopupMenu *goMenu = dynamic_cast<TQPopupMenu *>( mainWindow->guiFactory()->container( TQString::fromLatin1( "go" ), mainWindow ) ); if ( !goMenu ) return; // 1 for first item in the list, etc. int index = goMenu->indexOf(id) - m_goMenuIndex + 1; if ( index > 0 ) { kdDebug(1400) << "Item clicked has index " << index << endl; // -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 :-) kdDebug(1400) << "Emit activated with steps = " << steps << endl; goHistory( steps ); } } void History::fillHistoryPopup( TQPopupMenu *popup, bool onlyBack, bool onlyForward, bool checkCurrentItem, uint startPos ) { Q_ASSERT ( popup ); // kill me if this 0... :/ Entry * current = m_entries.current(); TQPtrListIterator<Entry> it( m_entries ); if (onlyBack || onlyForward) { it += m_entries.at(); // Jump to current item if ( !onlyForward ) --it; else ++it; // And move off it } else if ( startPos ) it += startPos; // Jump to specified start pos uint i = 0; while ( it.current() ) { TQString text = it.current()->title; text = KStringHandler::csqueeze(text, 50); //CT: squeeze text.replace( "&", "&&" ); if ( checkCurrentItem && it.current() == current ) { int id = popup->insertItem( text ); // no pixmap if checked popup->setItemChecked( id, true ); } else popup->insertItem( text ); if ( ++i > 10 ) break; if ( !onlyForward ) --it; else ++it; } } bool History::canGoBack() const { return m_entries.at() > 0; } bool History::canGoForward() const { return m_entries.at() != static_cast<int>( m_entries.count() ) - 1; } #include "history.moc" // vim:ts=2:sw=2:et