2 * This file is part of the KDE Help Center
4 * Copyright (C) 2002 Frerich Raabe <raabe@kde.org>
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.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include <kactioncollection.h>
25 #include <kapplication.h>
28 #include <kxmlguiwindow.h>
30 #include <kstandardguiitem.h>
31 #include <kstringhandler.h>
32 #include <ktoolbarpopupaction.h>
33 #include <kxmlguifactory.h>
38 History
*History::m_instance
= 0;
40 History
&History::self()
43 m_instance
= new History
;
47 History::History() : QObject(),
50 m_entries
.setAutoDelete( true );
57 void History::setupActions( KActionCollection
*coll
)
59 QPair
<KGuiItem
, KGuiItem
> backForward
= KStandardGuiItem::backAndForward();
61 m_backAction
= new KToolBarPopupAction( KIcon( backForward
.first
.iconName() ), backForward
.first
.text(), this );
62 coll
->addAction( "back", m_backAction
);
63 m_backAction
->setShortcut( Qt::ALT
+Qt::Key_Left
);
64 connect( m_backAction
, SIGNAL( triggered() ), this, SLOT( back() ) );
66 connect( m_backAction
->menu(), SIGNAL( activated( int ) ),
67 SLOT( backActivated( int ) ) );
68 connect( m_backAction
->menu(), SIGNAL( aboutToShow() ),
69 SLOT( fillBackMenu() ) );
70 m_backAction
->setEnabled( false );
72 m_forwardAction
= new KToolBarPopupAction( KIcon( backForward
.second
.iconName() ), backForward
.second
.text(), this );
73 coll
->addAction( QLatin1String("forward"), m_forwardAction
);
74 m_forwardAction
->setShortcut( Qt::ALT
+Qt::Key_Right
);
75 connect( m_forwardAction
, SIGNAL( triggered() ), this, SLOT( forward() ) );
77 connect( m_forwardAction
->menu(), SIGNAL( activated( int ) ),
78 SLOT( forwardActivated( int ) ) );
79 connect( m_forwardAction
->menu(), SIGNAL( aboutToShow() ),
80 SLOT( fillForwardMenu() ) );
81 m_forwardAction
->setEnabled( false );
83 void History::installMenuBarHook( KXmlGuiWindow
*mainWindow
)
85 QMenu
*goMenu
= dynamic_cast<QMenu
*>(
86 mainWindow
->guiFactory()->container( QLatin1String("go_web"), mainWindow
) );
88 connect( goMenu
, SIGNAL( aboutToShow() ), SLOT( fillGoMenu() ) );
89 connect( goMenu
, SIGNAL( activated( int ) ),
90 SLOT( goMenuActivated( int ) ) );
91 m_goMenuIndex
= goMenu
->actions().count();
95 void History::createEntry()
97 kDebug() << "History::createEntry()";
99 // First, remove any forward history
100 Entry
* current
= m_entries
.current();
103 m_entries
.at( m_entries
.count() - 1 ); // go to last one
104 for ( ; m_entries
.current() != current
; )
106 if ( !m_entries
.removeLast() ) { // and remove from the end (faster and easier)
111 m_entries
.at( m_entries
.count() - 1 );
113 // Now current is the current again.
115 // If current entry is empty reuse it.
116 if ( !current
->view
) return;
119 // Append a new entry
120 m_entries
.append( new Entry
); // made current
121 Q_ASSERT( m_entries
.at() == (int) m_entries
.count() - 1 );
124 void History::updateCurrentEntry( View
*view
)
126 if ( m_entries
.isEmpty() )
129 KUrl url
= view
->url();
131 Entry
*current
= m_entries
.current();
133 QDataStream
stream( ¤t
->buffer
, QIODevice::WriteOnly
);
134 view
->browserExtension()->saveState( stream
);
136 current
->view
= view
;
138 if ( url
.isEmpty() ) {
139 kDebug() << "History::updateCurrentEntry(): internal url";
140 url
= view
->internalUrl();
143 kDebug() << "History::updateCurrentEntry(): " << view
->title()
144 << " (URL: " << url
.url() << ")" << endl
;
147 current
->title
= view
->title();
149 current
->search
= view
->state() == View::Search
;
152 void History::updateActions()
154 m_backAction
->setEnabled( canGoBack() );
155 m_forwardAction
->setEnabled( canGoForward() );
160 kDebug( 1400 ) << "History::back()";
161 goHistoryActivated( -1 );
164 void History::backActivated( int id
)
166 kDebug( 1400 ) << "History::backActivated(): id = " << id
;
167 goHistoryActivated( -( m_backAction
->menu()->indexOf( id
) + 1 ) );
170 void History::forward()
172 kDebug( 1400 ) << "History::forward()";
173 goHistoryActivated( 1 );
176 void History::forwardActivated( int id
)
178 kDebug( 1400 ) << "History::forwardActivated(): id = " << id
;
179 goHistoryActivated( m_forwardAction
->menu()->indexOf( id
) + 1 );
182 void History::goHistoryActivated( int steps
)
184 kDebug( 1400 ) << "History::goHistoryActivated(): m_goBuffer = " << m_goBuffer
;
188 QTimer::singleShot( 0, this, SLOT( goHistoryDelayed() ) );
191 void History::goHistoryDelayed()
193 kDebug( 1400 ) << "History::goHistoryDelayed(): m_goBuffer = " << m_goBuffer
;
196 int steps
= m_goBuffer
;
201 void History::goHistory( int steps
)
203 kDebug() << "History::goHistory(): " << steps
;
205 // If current entry is empty remove it.
206 Entry
*current
= m_entries
.current();
207 if ( current
&& !current
->view
) m_entries
.remove();
209 int newPos
= m_entries
.at() + steps
;
211 current
= m_entries
.at( newPos
);
213 kError() << "No History entry at position " << newPos
<< endl
;
217 if ( !current
->view
) {
218 kWarning() << "Empty history entry." ;
222 if ( current
->search
) {
223 kDebug() << "History::goHistory(): search";
224 current
->view
->lastSearch();
228 if ( current
->url
.protocol() == QLatin1String("khelpcenter") ) {
229 kDebug() << "History::goHistory(): internal";
230 emit
goInternalUrl( current
->url
);
234 kDebug() << "History::goHistory(): restore state";
236 emit
goUrl( current
->url
);
241 QDataStream
stream( h
.buffer
);
244 updateCurrentEntry( h
.view
);
245 h
.view
->browserExtension()->restoreState( stream
);
250 void History::fillBackMenu()
252 QMenu
*menu
= m_backAction
->menu();
254 fillHistoryPopup( menu
, true, false, false );
257 void History::fillForwardMenu()
259 QMenu
*menu
= m_forwardAction
->menu();
261 fillHistoryPopup( menu
, false, true, false );
264 void History::fillGoMenu()
266 KXmlGuiWindow
*mainWindow
= static_cast<KXmlGuiWindow
*>( kapp
->activeWindow() );
267 QMenu
*goMenu
= dynamic_cast<QMenu
*>( mainWindow
->guiFactory()->container( QLatin1String( "go" ), mainWindow
) );
268 if ( !goMenu
|| m_goMenuIndex
== -1 )
271 for ( int i
= goMenu
->actions().count() - 1 ; i
>= m_goMenuIndex
; i
-- )
272 goMenu
->removeAction( goMenu
->actions()[i
] );
274 // TODO perhaps smarter algorithm (rename existing items, create new ones only if not enough) ?
276 // Ok, we want to show 10 items in all, among which the current url...
278 if ( m_entries
.count() <= 9 )
280 // First case: limited history in both directions -> show it all
281 m_goMenuHistoryStartPos
= m_entries
.count() - 1; // Start right from the end
283 // Second case: big history, in one or both directions
285 // Assume both directions first (in this case we place the current URL in the middle)
286 m_goMenuHistoryStartPos
= m_entries
.at() + 4;
288 // Forward not big enough ?
289 if ( m_entries
.at() > (int)m_entries
.count() - 4 )
290 m_goMenuHistoryStartPos
= m_entries
.count() - 1;
292 Q_ASSERT( m_goMenuHistoryStartPos
>= 0 && (uint
)m_goMenuHistoryStartPos
< m_entries
.count() );
293 m_goMenuHistoryCurrentPos
= m_entries
.at(); // for slotActivated
294 fillHistoryPopup( goMenu
, false, false, true, m_goMenuHistoryStartPos
);
297 void History::goMenuActivated( int id
)
299 KXmlGuiWindow
*mainWindow
= static_cast<KXmlGuiWindow
*>( kapp
->activeWindow() );
300 QMenu
*goMenu
= dynamic_cast<QMenu
*>( mainWindow
->guiFactory()->container( QLatin1String( "go" ), mainWindow
) );
304 // 1 for first item in the list, etc.
305 int index
= goMenu
->indexOf(id
) - m_goMenuIndex
+ 1;
308 kDebug(1400) << "Item clicked has index " << index
;
309 // -1 for one step back, 0 for don't move, +1 for one step forward, etc.
310 int steps
= ( m_goMenuHistoryStartPos
+1 ) - index
- m_goMenuHistoryCurrentPos
; // make a drawing to understand this :-)
311 kDebug(1400) << "Emit activated with steps = " << steps
;
316 void History::fillHistoryPopup( QMenu
*popup
, bool onlyBack
, bool onlyForward
, bool checkCurrentItem
, uint startPos
)
318 Q_ASSERT ( popup
); // kill me if this 0... :/
320 Entry
* current
= m_entries
.current();
321 Q3PtrListIterator
<Entry
> it( m_entries
);
322 if (onlyBack
|| onlyForward
)
324 it
+= m_entries
.at(); // Jump to current item
325 if ( !onlyForward
) --it
; else ++it
; // And move off it
326 } else if ( startPos
)
327 it
+= startPos
; // Jump to specified start pos
330 while ( it
.current() )
332 QString text
= it
.current()->title
;
333 text
= KStringHandler::csqueeze(text
, 50); //CT: squeeze
334 text
.replace( "&", "&&" );
335 if ( checkCurrentItem
&& it
.current() == current
)
337 popup
->addAction( text
)->setChecked( true ); // no pixmap if checked
339 popup
->addAction( text
);
342 if ( !onlyForward
) --it
; else ++it
;
346 bool History::canGoBack() const
348 return m_entries
.at() > 0;
351 bool History::canGoForward() const
353 return m_entries
.at() != static_cast<int>( m_entries
.count() ) - 1;
356 #include "history.moc"