|
|
|
/*
|
|
|
|
* 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 )
|
|
|
|
{
|
|
|
|
TQPair<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.tqat( 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.tqat( 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.tqat() == (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.tqat() + steps;
|
|
|
|
|
|
|
|
current = m_entries.tqat( 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::tqfromLatin1( "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.tqat() + 4;
|
|
|
|
|
|
|
|
// Forward not big enough ?
|
|
|
|
if ( m_entries.tqat() > (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.tqat(); // 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::tqfromLatin1( "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.tqat(); // 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.tqreplace( "&", "&&" );
|
|
|
|
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.tqat() > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool History::canGoForward() const
|
|
|
|
{
|
|
|
|
return m_entries.tqat() != static_cast<int>( m_entries.count() ) - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "history.moc"
|
|
|
|
// vim:ts=2:sw=2:et
|