kbookmarkbar.cc
00001 // -*- c-basic-offset:4; indent-tabs-mode:nil -*- 00002 // vim: set ts=4 sts=4 sw=4 et: 00003 /* This file is part of the KDE project 00004 Copyright (C) 1999 Kurt Granroth <granroth@kde.org> 00005 Copyright (C) 1998, 1999 Torben Weis <weis@kde.org> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library 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 GNU 00015 Library General Public License for more details. 00016 00017 You should have received a copy of the GNU Library General Public License 00018 along with this library; see the file COPYING.LIB. If not, write to 00019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00020 Boston, MA 02110-1301, USA. 00021 */ 00022 #include <tqregexp.h> 00023 #include <tqfile.h> 00024 00025 #include <kbookmarkbar.h> 00026 #include <kbookmarkdrag.h> 00027 00028 #include <kbookmarkmenu.h> 00029 #include <kdebug.h> 00030 00031 #include <tdetoolbar.h> 00032 #include <tdetoolbarbutton.h> 00033 00034 #include <tdeconfig.h> 00035 #include <tdepopupmenu.h> 00036 00037 #include "kbookmarkdrag.h" 00038 #include "kbookmarkmenu_p.h" 00039 #include "kbookmarkdombuilder.h" 00040 00041 #include "dptrtemplate.h" 00042 00043 #include <tqapplication.h> 00044 00045 class KBookmarkBarPrivate : public dPtrTemplate<KBookmarkBar, KBookmarkBarPrivate> 00046 { 00047 public: 00048 TQPtrList<TDEAction> m_actions; 00049 bool m_readOnly; 00050 KBookmarkManager* m_filteredMgr; 00051 TDEToolBar* m_sepToolBar; 00052 int m_sepIndex; 00053 bool m_atFirst; 00054 TQString m_dropAddress; 00055 TQString m_highlightedAddress; 00056 public: 00057 KBookmarkBarPrivate() { 00058 m_readOnly = false; 00059 m_filteredMgr = 0; 00060 m_sepToolBar = 0; 00061 m_sepIndex = -1; 00062 m_atFirst = false; 00063 } 00064 }; 00065 template<> TQPtrDict<KBookmarkBarPrivate>* dPtrTemplate<KBookmarkBar, KBookmarkBarPrivate>::d_ptr = 0; 00066 00067 KBookmarkBarPrivate* KBookmarkBar::dptr() const 00068 { 00069 return KBookmarkBarPrivate::d( this ); 00070 } 00071 00072 // usage of KXBELBookmarkImporterImpl is just plain evil, but it reduces code dup. so... 00073 class ToolbarFilter : public KXBELBookmarkImporterImpl 00074 { 00075 public: 00076 ToolbarFilter() : m_visible(false) { ; } 00077 void filter( const KBookmarkGroup &grp ) { traverse(grp); } 00078 private: 00079 virtual void visit( const KBookmark & ); 00080 virtual void visitEnter( const KBookmarkGroup & ); 00081 virtual void visitLeave( const KBookmarkGroup & ); 00082 private: 00083 bool m_visible; 00084 KBookmarkGroup m_visibleStart; 00085 }; 00086 00087 KBookmarkBar::KBookmarkBar( KBookmarkManager* mgr, 00088 KBookmarkOwner *_owner, TDEToolBar *_toolBar, 00089 TDEActionCollection *coll, 00090 TQObject *parent, const char *name ) 00091 : TQObject( parent, name ), m_pOwner(_owner), m_toolBar(_toolBar), 00092 m_actionCollection( coll ), m_pManager(mgr) 00093 { 00094 m_lstSubMenus.setAutoDelete( true ); 00095 00096 m_toolBar->setAcceptDrops( true ); 00097 m_toolBar->installEventFilter( this ); // for drops 00098 00099 dptr()->m_actions.setAutoDelete( true ); 00100 00101 connect( mgr, TQT_SIGNAL( changed(const TQString &, const TQString &) ), 00102 TQT_SLOT( slotBookmarksChanged(const TQString &) ) ); 00103 00104 KBookmarkGroup toolbar = getToolbar(); 00105 fillBookmarkBar( toolbar ); 00106 } 00107 00108 TQString KBookmarkBar::parentAddress() 00109 { 00110 return dptr()->m_filteredMgr ? TQString::null : m_pManager->toolbar().address(); 00111 } 00112 00113 #define CURRENT_TOOLBAR() ( \ 00114 dptr()->m_filteredMgr ? dptr()->m_filteredMgr->root() \ 00115 : m_pManager->toolbar() ) 00116 00117 #define CURRENT_MANAGER() ( \ 00118 dptr()->m_filteredMgr ? dptr()->m_filteredMgr \ 00119 : m_pManager ) 00120 00121 KBookmarkGroup KBookmarkBar::getToolbar() 00122 { 00123 if ( KBookmarkSettings::self()->m_filteredtoolbar ) 00124 { 00125 if ( !dptr()->m_filteredMgr ) { 00126 dptr()->m_filteredMgr = KBookmarkManager::createTempManager(); 00127 } else { 00128 KBookmarkGroup bkRoot = dptr()->m_filteredMgr->root(); 00129 TQValueList<KBookmark> bks; 00130 for (KBookmark bm = bkRoot.first(); !bm.isNull(); bm = bkRoot.next(bm)) 00131 bks << bm; 00132 for ( TQValueListConstIterator<KBookmark> it = bks.begin(); it != bks.end(); ++it ) 00133 bkRoot.deleteBookmark( (*it) ); 00134 } 00135 ToolbarFilter filter; 00136 KBookmarkDomBuilder builder( dptr()->m_filteredMgr->root(), 00137 dptr()->m_filteredMgr ); 00138 builder.connectImporter( &filter ); 00139 filter.filter( m_pManager->root() ); 00140 } 00141 00142 return CURRENT_TOOLBAR(); 00143 } 00144 00145 KBookmarkBar::~KBookmarkBar() 00146 { 00147 //clear(); 00148 KBookmarkBarPrivate::delete_d(this); 00149 } 00150 00151 void KBookmarkBar::clear() 00152 { 00153 TQPtrListIterator<TDEAction> it( dptr()->m_actions ); 00154 m_toolBar->clear(); 00155 for (; it.current(); ++it ) { 00156 (*it)->unplugAll(); 00157 } 00158 dptr()->m_actions.clear(); 00159 m_lstSubMenus.clear(); 00160 } 00161 00162 void KBookmarkBar::slotBookmarksChanged( const TQString & group ) 00163 { 00164 KBookmarkGroup tb = getToolbar(); // heavy for non cached toolbar version 00165 kdDebug(7043) << "slotBookmarksChanged( " << group << " )" << endl; 00166 00167 if ( tb.isNull() ) 00168 return; 00169 00170 if ( KBookmark::commonParent(group, tb.address()) == group // Is group a parent of tb.address? 00171 || KBookmarkSettings::self()->m_filteredtoolbar ) 00172 { 00173 clear(); 00174 fillBookmarkBar( tb ); 00175 } 00176 else 00177 { 00178 // Iterate recursively into child menus 00179 TQPtrListIterator<KBookmarkMenu> it( m_lstSubMenus ); 00180 for (; it.current(); ++it ) 00181 { 00182 it.current()->slotBookmarksChanged( group ); 00183 } 00184 } 00185 } 00186 00187 void KBookmarkBar::fillBookmarkBar(KBookmarkGroup & parent) 00188 { 00189 if (parent.isNull()) 00190 return; 00191 00192 for (KBookmark bm = parent.first(); !bm.isNull(); bm = parent.next(bm)) 00193 { 00194 TQString text = bm.text(); 00195 text.replace( '&', "&&" ); 00196 if (!bm.isGroup()) 00197 { 00198 if ( bm.isSeparator() ) 00199 m_toolBar->insertLineSeparator(); 00200 else 00201 { 00202 TDEAction *action = new KBookmarkAction( text, bm.icon(), 0, m_actionCollection, 0 ); 00203 connect(action, TQT_SIGNAL( activated ( TDEAction::ActivationReason, TQt::ButtonState )), 00204 this, TQT_SLOT( slotBookmarkSelected( TDEAction::ActivationReason, TQt::ButtonState ) )); 00205 00206 action->setProperty( "url", bm.url().url() ); 00207 action->setProperty( "address", bm.address() ); 00208 00209 action->setToolTip( bm.url().pathOrURL() ); 00210 00211 action->plug(m_toolBar); 00212 00213 dptr()->m_actions.append( action ); 00214 } 00215 } 00216 else 00217 { 00218 TDEActionMenu *action = new KBookmarkActionMenu( text, bm.icon(), 00219 m_actionCollection, 00220 "bookmarkbar-actionmenu"); 00221 action->setProperty( "address", bm.address() ); 00222 action->setProperty( "readOnly", dptr()->m_readOnly ); 00223 action->setDelayed( false ); 00224 00225 // this flag doesn't have any UI yet 00226 TDEGlobal::config()->setGroup( "Settings" ); 00227 bool addEntriesBookmarkBar = TDEGlobal::config()->readBoolEntry("AddEntriesBookmarkBar",true); 00228 00229 KBookmarkMenu *menu = new KBookmarkMenu(CURRENT_MANAGER(), m_pOwner, action->popupMenu(), 00230 m_actionCollection, false, addEntriesBookmarkBar, 00231 bm.address()); 00232 connect(menu, TQT_SIGNAL( aboutToShowContextMenu(const KBookmark &, TQPopupMenu * ) ), 00233 this, TQT_SIGNAL( aboutToShowContextMenu(const KBookmark &, TQPopupMenu * ) )); 00234 connect(menu, TQT_SIGNAL( openBookmark( const TQString &, TQt::ButtonState) ), 00235 this, TQT_SIGNAL( openBookmark( const TQString &, TQt::ButtonState) )); 00236 menu->fillBookmarkMenu(); 00237 action->plug(m_toolBar); 00238 m_lstSubMenus.append( menu ); 00239 00240 dptr()->m_actions.append( action ); 00241 } 00242 } 00243 } 00244 00245 void KBookmarkBar::setReadOnly(bool readOnly) 00246 { 00247 dptr()->m_readOnly = readOnly; 00248 } 00249 00250 bool KBookmarkBar::isReadOnly() const 00251 { 00252 return dptr()->m_readOnly; 00253 } 00254 00255 void KBookmarkBar::slotBookmarkSelected( TDEAction::ActivationReason /*reason*/, TQt::ButtonState state ) 00256 { 00257 if (!m_pOwner) return; // this view doesn't handle bookmarks... 00258 00259 const TDEAction* action = dynamic_cast<const TDEAction *>(sender()); 00260 if(action) 00261 { 00262 const TQString & url = sender()->property("url").toString(); 00263 m_pOwner->openBookmarkURL(url); 00264 emit openBookmark( url, state ); 00265 } 00266 } 00267 00268 void KBookmarkBar::slotBookmarkSelected() 00269 { 00270 slotBookmarkSelected(TDEAction::ToolBarActivation, Qt::NoButton); 00271 } 00272 00273 static const int const_sepId = -9999; // FIXME this is ugly, 00274 // surely there is another 00275 // way of doing this... 00276 00277 static void removeTempSep(KBookmarkBarPrivate* p) 00278 { 00279 if (p->m_sepToolBar) { 00280 p->m_sepToolBar->removeItem(const_sepId); 00281 p->m_sepToolBar = 0; // needed? 00282 } 00283 } 00284 00285 static TDEAction* findPluggedAction(TQPtrList<TDEAction> actions, TDEToolBar *tb, int id) 00286 { 00287 TQPtrListIterator<TDEAction> it( actions ); 00288 for (; (*it); ++it ) 00289 if ((*it)->isPlugged(tb, id)) 00290 return (*it); 00291 return 0; 00292 } 00293 00304 static TQString handleToolbarDragMoveEvent( 00305 KBookmarkBarPrivate *p, TDEToolBar *tb, TQPoint pos, TQPtrList<TDEAction> actions, 00306 bool &atFirst, KBookmarkManager *mgr 00307 ) { 00308 Q_UNUSED( mgr ); 00309 Q_ASSERT( actions.isEmpty() || (tb == dynamic_cast<TDEToolBar*>(actions.first()->container(0))) ); 00310 p->m_sepToolBar = tb; 00311 p->m_sepToolBar->removeItemDelayed(const_sepId); 00312 00313 int index = 0; 00314 TDEToolBarButton* b; 00315 00316 b = dynamic_cast<TDEToolBarButton*>(tb->childAt(pos)); 00317 TDEAction *a = 0; 00318 TQString address; 00319 atFirst = false; 00320 00321 if (b) 00322 { 00323 index = tb->itemIndex(b->id()); 00324 TQRect r = b->geometry(); 00325 if (pos.x() < ((r.left() + r.right())/2)) 00326 { 00327 // if in first half of button then 00328 // we jump to previous index 00329 if ( index == 0 ) 00330 atFirst = true; 00331 else { 00332 index--; 00333 b = tb->getButton(tb->idAt(index)); 00334 } 00335 } 00336 } 00337 else if (actions.isEmpty()) 00338 { 00339 atFirst = true; 00340 index = 0; 00341 // we skip the action related stuff 00342 // and do what it should have... 00343 // FIXME - here we want to get the 00344 // parent address of the bookmark 00345 // bar itself and return that + "/0" 00346 p->m_sepIndex = 0; 00347 goto skipact; 00348 } 00349 else // (!b) 00350 { 00351 index = actions.count() - 1; 00352 b = tb->getButton(tb->idAt(index)); 00353 // if !b and not past last button, we didn't find button 00354 if (pos.x() <= b->geometry().left()) 00355 goto skipact; // TODO - rename 00356 } 00357 00358 if ( !b ) 00359 return TQString::null; // TODO Make it works for that case 00360 00361 a = findPluggedAction(actions, tb, b->id()); 00362 Q_ASSERT(a); 00363 address = a->property("address").toString(); 00364 p->m_sepIndex = index + (atFirst ? 0 : 1); 00365 00366 #if 0 00367 { // ugly workaround to fix the goto scoping problems... 00368 KBookmark bk = mgr->findByAddress( address ); 00369 if (bk.isGroup()) // TODO - fix this ****!!!, manhatten distance should be used!!! 00370 { 00371 kdDebug() << "kbookmarkbar:: popping up " << bk.text() << endl; 00372 KBookmarkActionMenu *menu = dynamic_cast<KBookmarkActionMenu*>(a); 00373 Q_ASSERT(menu); 00374 menu->popup(tb->mapToGlobal(b->geometry().center())); 00375 } 00376 } 00377 #endif 00378 00379 skipact: 00380 tb->insertLineSeparator(p->m_sepIndex, const_sepId); 00381 return address; 00382 } 00383 00384 // TODO - document!!!! 00385 static TDEAction* handleToolbarMouseButton(TQPoint pos, TQPtrList<TDEAction> actions, 00386 KBookmarkManager * /*mgr*/, TQPoint & pt) 00387 { 00388 TDEAction *act = actions.first(); 00389 if (!act) { 00390 return 0; 00391 } 00392 00393 TDEToolBar *tb = dynamic_cast<TDEToolBar*>(act->container(0)); 00394 Q_ASSERT(tb); 00395 00396 TDEToolBarButton *b; 00397 b = dynamic_cast<TDEToolBarButton*>(tb->childAt(pos)); 00398 if (!b) 00399 return 0; 00400 00401 TDEAction *a = 0; 00402 a = findPluggedAction(actions, tb, b->id()); 00403 Q_ASSERT(a); 00404 pt = tb->mapToGlobal(pos); 00405 00406 return a; 00407 } 00408 00409 // TODO *** drop improvements *** 00410 // open submenus on drop interactions 00411 00412 // TODO *** generic rmb improvements *** 00413 // don't *ever* show the rmb on press, always relase, possible??? 00414 00415 class KBookmarkBarRMBAssoc : public dPtrTemplate<KBookmarkBar, RMB> { }; 00416 template<> TQPtrDict<RMB>* dPtrTemplate<KBookmarkBar, RMB>::d_ptr = 0; 00417 00418 static RMB* rmbSelf(KBookmarkBar *m) { return KBookmarkBarRMBAssoc::d(m); } 00419 00420 void RMB::begin_rmb_action(KBookmarkBar *self) 00421 { 00422 RMB *s = rmbSelf(self); 00423 s->recv = self; 00424 s->m_parentAddress = self->parentAddress(); 00425 s->s_highlightedAddress = self->dptr()->m_highlightedAddress; // rename in RMB 00426 s->m_pManager = self->m_pManager; 00427 s->m_pOwner = self->m_pOwner; 00428 s->m_parentMenu = 0; 00429 } 00430 00431 void KBookmarkBar::slotRMBActionEditAt( int val ) 00432 { RMB::begin_rmb_action(this); rmbSelf(this)->slotRMBActionEditAt( val ); } 00433 00434 void KBookmarkBar::slotRMBActionProperties( int val ) 00435 { RMB::begin_rmb_action(this); rmbSelf(this)->slotRMBActionProperties( val ); } 00436 00437 void KBookmarkBar::slotRMBActionInsert( int val ) 00438 { RMB::begin_rmb_action(this); rmbSelf(this)->slotRMBActionInsert( val ); } 00439 00440 void KBookmarkBar::slotRMBActionRemove( int val ) 00441 { RMB::begin_rmb_action(this); rmbSelf(this)->slotRMBActionRemove( val ); } 00442 00443 void KBookmarkBar::slotRMBActionCopyLocation( int val ) 00444 { RMB::begin_rmb_action(this); rmbSelf(this)->slotRMBActionCopyLocation( val ); } 00445 00446 bool KBookmarkBar::eventFilter( TQObject *o, TQEvent *e ) 00447 { 00448 if (dptr()->m_readOnly || dptr()->m_filteredMgr) // note, we assume m_pManager in various places, 00449 // this shouldn't really be the case 00450 return false; // todo: make this limit the actions 00451 00452 if ( (e->type() == TQEvent::MouseButtonRelease) || (e->type() == TQEvent::MouseButtonPress) ) // FIXME, which one? 00453 { 00454 TQMouseEvent *mev = (TQMouseEvent*)e; 00455 00456 TQPoint pt; 00457 TDEAction *_a; 00458 00459 // FIXME, see how this holds up on an empty toolbar 00460 _a = handleToolbarMouseButton( mev->pos(), dptr()->m_actions, m_pManager, pt ); 00461 if (_a && mev->button() == Qt::RightButton) 00462 { 00463 dptr()->m_highlightedAddress = _a->property("address").toString(); 00464 KBookmark bookmark = m_pManager->findByAddress( dptr()->m_highlightedAddress ); 00465 RMB::begin_rmb_action(this); 00466 TDEPopupMenu *pm = new TDEPopupMenu; 00467 rmbSelf(this)->fillContextMenu( pm, dptr()->m_highlightedAddress, 0 ); 00468 emit aboutToShowContextMenu( rmbSelf(this)->atAddress( dptr()->m_highlightedAddress ), pm ); 00469 rmbSelf(this)->fillContextMenu2( pm, dptr()->m_highlightedAddress, 0 ); 00470 pm->popup( pt ); 00471 mev->accept(); 00472 } 00473 00474 return !!_a; // ignore the event if we didn't find the button 00475 } 00476 else if ( e->type() == TQEvent::DragLeave ) 00477 { 00478 removeTempSep(dptr()); 00479 dptr()->m_dropAddress = TQString::null; 00480 } 00481 else if ( e->type() == TQEvent::Drop ) 00482 { 00483 removeTempSep(dptr()); 00484 TQDropEvent *dev = (TQDropEvent*)e; 00485 if ( !KBookmarkDrag::canDecode( dev ) ) 00486 return false; 00487 TQValueList<KBookmark> list = KBookmarkDrag::decode( dev ); 00488 if (list.count() > 1) 00489 kdWarning(7043) << "Sorry, currently you can only drop one address " 00490 "onto the bookmark bar!" << endl; 00491 KBookmark toInsert = list.first(); 00492 KBookmark bookmark = m_pManager->findByAddress( dptr()->m_dropAddress ); 00493 Q_ASSERT(!bookmark.isNull()); 00494 kdDebug(7043) << "inserting " 00495 << TQString(dptr()->m_atFirst ? "before" : "after") 00496 << " dptr()->m_dropAddress == " << dptr()->m_dropAddress << endl; 00497 KBookmarkGroup parentBookmark = bookmark.parentGroup(); 00498 Q_ASSERT(!parentBookmark.isNull()); 00499 KBookmark newBookmark = parentBookmark.addBookmark( 00500 m_pManager, toInsert.fullText(), 00501 toInsert.url() ); 00502 parentBookmark.moveItem( newBookmark, dptr()->m_atFirst ? KBookmark() : bookmark ); 00503 m_pManager->emitChanged( parentBookmark ); 00504 return true; 00505 } 00506 else if ( e->type() == TQEvent::DragMove ) 00507 { 00508 TQDragMoveEvent *dme = (TQDragMoveEvent*)e; 00509 if (!KBookmarkDrag::canDecode( dme )) 00510 return false; 00511 bool _atFirst; 00512 TQString dropAddress; 00513 TDEToolBar *tb = (TDEToolBar*)o; 00514 dropAddress = handleToolbarDragMoveEvent(dptr(), tb, dme->pos(), dptr()->m_actions, _atFirst, m_pManager); 00515 if (!dropAddress.isNull()) 00516 { 00517 dptr()->m_dropAddress = dropAddress; 00518 dptr()->m_atFirst = _atFirst; 00519 dme->accept(); 00520 } 00521 } 00522 return false; 00523 } 00524 00525 static bool showInToolbar( const KBookmark &bk ) { 00526 return (bk.internalElement().attributes().namedItem("showintoolbar").toAttr().value() == "yes"); 00527 } 00528 00529 void ToolbarFilter::visit( const KBookmark &bk ) { 00530 //kdDebug() << "visit(" << bk.text() << ")" << endl; 00531 if ( m_visible || showInToolbar(bk) ) 00532 KXBELBookmarkImporterImpl::visit(bk); 00533 } 00534 00535 void ToolbarFilter::visitEnter( const KBookmarkGroup &grp ) { 00536 //kdDebug() << "visitEnter(" << grp.text() << ")" << endl; 00537 if ( !m_visible && showInToolbar(grp) ) 00538 { 00539 m_visibleStart = grp; 00540 m_visible = true; 00541 } 00542 if ( m_visible ) 00543 KXBELBookmarkImporterImpl::visitEnter(grp); 00544 } 00545 00546 void ToolbarFilter::visitLeave( const KBookmarkGroup &grp ) { 00547 //kdDebug() << "visitLeave()" << endl; 00548 if ( m_visible ) 00549 KXBELBookmarkImporterImpl::visitLeave(grp); 00550 if ( m_visible && grp.address() == m_visibleStart.address() ) 00551 m_visible = false; 00552 } 00553 00554 #include "kbookmarkbar.moc"