1 // -*- mode: c++; c-basic-offset: 2 -*-
2 /* This file is part of the KDE project
3 Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21
22 #include <sys/types.h>
23 #include <sys/stat.h>
24
25 #include <assert.h>
26 #include <dirent.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stddef.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32
33 #include "kcelbookmarkmenu.h"
34 #include <kbookmarkmenu.h>
35 #include "kbookmarkimporter.h"
36 #include "kcelbookmarkowner.h"
37
38 #include <qfile.h>
39 #include <qregexp.h>
40
41 #include <kaction.h>
42 #include <kdebug.h>
43 #include <klocale.h>
44 #include <kmessagebox.h>
45 #include <kpopupmenu.h>
46 #include <kstdaccel.h>
47 #include <kstdaction.h>
48
49 template class QPtrList<KCelBookmarkMenu>;
50
51 /********************************************************************
52 *
53 * KCelBookmarkMenu
54 *
55 ********************************************************************/
56
KCelBookmarkMenu(KBookmarkManager * mgr,KCelBookmarkOwner * _owner,KPopupMenu * _parentMenu,KActionCollection * collec,bool _isRoot,bool _add,const QString & parentAddress)57 KCelBookmarkMenu::KCelBookmarkMenu( KBookmarkManager* mgr,
58 KCelBookmarkOwner * _owner, KPopupMenu * _parentMenu,
59 KActionCollection *collec, bool _isRoot, bool _add,
60 const QString & parentAddress )
61 : m_bIsRoot(_isRoot), m_bAddBookmark(_add),
62 m_pManager(mgr), m_pOwner(_owner),
63 m_parentMenu( _parentMenu ),
64 m_actionCollection( collec ),
65 m_parentAddress( parentAddress )
66 {
67 m_lstSubMenus.setAutoDelete( true );
68 m_actions.setAutoDelete( true );
69
70 m_bNSBookmark = m_parentAddress.isNull();
71 if ( !m_bNSBookmark ) // not for the netscape bookmark
72 {
73 //kdDebug(1203) << "KBookmarkMenu::KBookmarkMenu " << this << " address : " << m_parentAddress << endl;
74
75 connect( _parentMenu, SIGNAL( aboutToShow() ),
76 SLOT( slotAboutToShow() ) );
77
78 if ( m_bIsRoot )
79 {
80 connect( m_pManager, SIGNAL( changed(const QString &, const QString &) ),
81 SLOT( slotBookmarksChanged(const QString &) ) );
82 }
83 }
84
85 // add entries that possibly have a shortcut, so they are available _before_ first popup
86 if ( m_bIsRoot )
87 {
88 if ( m_bAddBookmark ) {
89 addAddBookmark();
90 }
91
92 addEditBookmarks();
93 }
94
95 m_bDirty = true;
96 }
97
~KCelBookmarkMenu()98 KCelBookmarkMenu::~KCelBookmarkMenu()
99 {
100 //kdDebug(1203) << "KBookmarkMenu::~KBookmarkMenu() " << this << endl;
101 QPtrListIterator<KAction> it( m_actions );
102 for (; it.current(); ++it )
103 it.current()->unplugAll();
104
105 m_lstSubMenus.clear();
106 m_actions.clear();
107 }
108
ensureUpToDate()109 void KCelBookmarkMenu::ensureUpToDate()
110 {
111 slotAboutToShow();
112 }
113
114
slotAboutToShow()115 void KCelBookmarkMenu::slotAboutToShow()
116 {
117 // Did the bookmarks change since the last time we showed them ?
118 if ( m_bDirty )
119 {
120 m_bDirty = false;
121 refill();
122 }
123 }
124
slotBookmarksChanged(const QString & groupAddress)125 void KCelBookmarkMenu::slotBookmarksChanged( const QString & groupAddress )
126 {
127 if (m_bNSBookmark)
128 return;
129
130 if ( groupAddress == m_parentAddress )
131 {
132 //kdDebug(1203) << "KBookmarkMenu::slotBookmarksChanged -> setting m_bDirty on " << groupAddress << endl;
133 m_bDirty = true;
134 }
135 else
136 {
137 // Iterate recursively into child menus
138 QPtrListIterator<KCelBookmarkMenu> it( m_lstSubMenus );
139 for (; it.current(); ++it )
140 {
141 it.current()->slotBookmarksChanged( groupAddress );
142 }
143 }
144 }
145
refill()146 void KCelBookmarkMenu::refill()
147 {
148 //kdDebug(1203) << "KBookmarkMenu::refill()" << endl;
149 m_lstSubMenus.clear();
150
151 QPtrListIterator<KAction> it( m_actions );
152 for (; it.current(); ++it )
153 it.current()->unplug( m_parentMenu );
154
155 m_parentMenu->clear();
156 m_actions.clear();
157
158 fillBookmarkMenu();
159 m_parentMenu->adjustSize();
160 }
161
addAddBookmark()162 void KCelBookmarkMenu::addAddBookmark()
163 {
164 KAction * paAddBookmarks = new KAction( i18n( "&Add Bookmark" ),
165 "bookmark_add",
166 m_bIsRoot ? ALT + Key_B : 0,
167 this,
168 SLOT( slotAddBookmark() ),
169 m_actionCollection, m_bIsRoot ? "add_bookmark" : 0 );
170
171 paAddBookmarks->setStatusText( i18n( "Add a bookmark for the current document" ) );
172
173 paAddBookmarks->plug( m_parentMenu );
174 m_actions.append( paAddBookmarks );
175 addAddRelativeBookmark();
176 addAddSettingsBookmark();
177 }
178
addAddRelativeBookmark()179 void KCelBookmarkMenu::addAddRelativeBookmark()
180 {
181 KAction * paAddBookmarks = new KAction( i18n( "Add &Relative Bookmark" ),
182 "bookmark_add",
183 m_bIsRoot ? ALT + Key_R : 0, //m_bIsRoot ? KStdAccel::addBookmark() : KShortcut(),
184 this,
185 SLOT( slotAddRelativeBookmark() ),
186 m_actionCollection, m_bIsRoot ? "add_relative_bookmark" : 0 );
187
188 paAddBookmarks->setStatusText( i18n( "Add a relative bookmark for the current document" ) );
189
190 paAddBookmarks->plug( m_parentMenu );
191 m_actions.append( paAddBookmarks );
192 }
193
addAddSettingsBookmark()194 void KCelBookmarkMenu::addAddSettingsBookmark()
195 {
196 KAction * paAddBookmarks = new KAction( i18n( "Add &Settings Bookmark" ),
197 "bookmark_add",
198 m_bIsRoot ? CTRL + ALT + Key_S : 0, //m_bIsRoot ? KStdAccel::addBookmark() : KShortcut(),
199 this,
200 SLOT( slotAddSettingsBookmark() ),
201 m_actionCollection, m_bIsRoot ? "add_settings_bookmark" : 0 );
202
203 paAddBookmarks->setStatusText( i18n( "Add a settings bookmark for the current document" ) );
204
205 paAddBookmarks->plug( m_parentMenu );
206 m_actions.append( paAddBookmarks );
207 }
208
addEditBookmarks()209 void KCelBookmarkMenu::addEditBookmarks()
210 {
211 KAction * m_paEditBookmarks = KStdAction::editBookmarks( m_pManager, SLOT( slotEditBookmarks() ),
212 m_actionCollection, "edit_bookmarks" );
213 m_paEditBookmarks->plug( m_parentMenu );
214 m_paEditBookmarks->setStatusText( i18n( "Edit your bookmark collection in a separate window" ) );
215 m_actions.append( m_paEditBookmarks );
216 }
217
addNewFolder()218 void KCelBookmarkMenu::addNewFolder()
219 {
220 KAction * paNewFolder = new KAction( i18n( "&New Folder..." ),
221 "folder_new", //"folder",
222 0,
223 this,
224 SLOT( slotNewFolder() ),
225 m_actionCollection );
226
227 paNewFolder->setStatusText( i18n( "Create a new bookmark folder in this menu" ) );
228
229 paNewFolder->plug( m_parentMenu );
230 m_actions.append( paNewFolder );
231 }
232
fillBookmarkMenu()233 void KCelBookmarkMenu::fillBookmarkMenu()
234 {
235 if ( m_bIsRoot )
236 {
237 if ( m_bAddBookmark )
238 addAddBookmark();
239
240 addEditBookmarks();
241
242 if ( m_bAddBookmark )
243 addNewFolder();
244
245 }
246
247 KBookmarkGroup parentBookmark = m_pManager->findByAddress( m_parentAddress ).toGroup();
248 Q_ASSERT(!parentBookmark.isNull());
249 bool separatorInserted = false;
250 for ( KBookmark bm = parentBookmark.first(); !bm.isNull(); bm = parentBookmark.next(bm) )
251 {
252 QString text = bm.text();
253 text.replace( QRegExp( "&" ), "&&" );
254 if ( !separatorInserted && m_bIsRoot) { // inserted before the first konq bookmark, to avoid the separator if no konq bookmark
255 m_parentMenu->insertSeparator();
256 separatorInserted = true;
257 }
258 if ( !bm.isGroup() )
259 {
260 if ( bm.isSeparator() )
261 {
262 m_parentMenu->insertSeparator();
263 }
264 else
265 {
266 // kdDebug(1203) << "Creating URL bookmark menu item for " << bm.text() << endl;
267 // create a normal URL item, with ID as a name
268 KAction * action = new KAction( text, bm.icon(), 0,
269 this, SLOT( slotBookmarkSelected() ),
270 m_actionCollection, bm.url().url().utf8() );
271
272 action->setStatusText( bm.url().prettyURL() );
273
274 action->plug( m_parentMenu );
275 m_actions.append( action );
276 }
277 }
278 else
279 {
280 // kdDebug(1203) << "Creating bookmark submenu named " << bm.text() << endl;
281 KActionMenu * actionMenu = new KActionMenu( text, bm.icon(),
282 m_actionCollection, 0L );
283 actionMenu->plug( m_parentMenu );
284 m_actions.append( actionMenu );
285 KCelBookmarkMenu *subMenu = new KCelBookmarkMenu( m_pManager, m_pOwner, actionMenu->popupMenu(),
286 m_actionCollection, false,
287 m_bAddBookmark,
288 bm.address() );
289 m_lstSubMenus.append( subMenu );
290 }
291 }
292
293 if ( !m_bIsRoot && m_bAddBookmark )
294 {
295 m_parentMenu->insertSeparator();
296 addAddBookmark();
297 addNewFolder();
298 }
299 }
300
slotAddRelativeBookmark()301 void KCelBookmarkMenu::slotAddRelativeBookmark()
302 {
303 Url Url = m_pOwner->currentUrl(Url::Relative);
304 QString url = QString(Url.getAsString().c_str());;
305 if (url.isEmpty())
306 {
307 KMessageBox::error( 0L, i18n("Can't add bookmark with empty URL"));
308 return;
309 }
310 QString title = QString::fromUtf8(Url::decodeString(Url.getName()).c_str());
311 if (title.isEmpty())
312 title = url;
313
314 KBookmarkGroup parentBookmark = m_pManager->findByAddress( m_parentAddress ).toGroup();
315 Q_ASSERT(!parentBookmark.isNull());
316 // If this title is already used, we'll try to find something unused.
317 KBookmark ch = parentBookmark.first();
318 int count = 1;
319 QString uniqueTitle = title;
320 do
321 {
322 while ( !ch.isNull() )
323 {
324 if ( uniqueTitle == ch.text() )
325 {
326 // Title already used !
327 if ( url != ch.url().url() )
328 {
329 uniqueTitle = title + QString(" (%1)").arg(++count);
330 // New title -> restart search from the beginning
331 ch = parentBookmark.first();
332 break;
333 }
334 else
335 {
336 // this exact URL already exists
337 return;
338 }
339 }
340 ch = parentBookmark.next( ch );
341 }
342 } while ( !ch.isNull() );
343
344 parentBookmark.addBookmark( m_pManager, uniqueTitle, url, m_pOwner->currentIcon() );
345 m_pManager->emitChanged( parentBookmark );
346 }
347
slotAddSettingsBookmark()348 void KCelBookmarkMenu::slotAddSettingsBookmark()
349 {
350 Url Url = m_pOwner->currentUrl(Url::Settings);
351 QString url = QString(Url.getAsString().c_str());;
352 if (url.isEmpty())
353 {
354 KMessageBox::error( 0L, i18n("Can't add bookmark with empty URL"));
355 return;
356 }
357 QString title = QString::fromUtf8(Url::decodeString(Url.getName()).c_str());
358 if (title.isEmpty())
359 title = url;
360
361 KBookmarkGroup parentBookmark = m_pManager->findByAddress( m_parentAddress ).toGroup();
362 Q_ASSERT(!parentBookmark.isNull());
363 // If this title is already used, we'll try to find something unused.
364 KBookmark ch = parentBookmark.first();
365 int count = 1;
366 QString uniqueTitle = title;
367 do
368 {
369 while ( !ch.isNull() )
370 {
371 if ( uniqueTitle == ch.text() )
372 {
373 // Title already used !
374 if ( url != ch.url().url() )
375 {
376 uniqueTitle = title + QString(" (%1)").arg(++count);
377 // New title -> restart search from the beginning
378 ch = parentBookmark.first();
379 break;
380 }
381 else
382 {
383 // this exact URL already exists
384 return;
385 }
386 }
387 ch = parentBookmark.next( ch );
388 }
389 } while ( !ch.isNull() );
390
391 parentBookmark.addBookmark( m_pManager, uniqueTitle, url, m_pOwner->currentIcon() );
392 m_pManager->emitChanged( parentBookmark );
393 }
394
slotAddBookmark()395 void KCelBookmarkMenu::slotAddBookmark()
396 {
397 Url Url = m_pOwner->currentUrl(Url::Absolute);
398 QString url = QString(Url.getAsString().c_str());;
399 if (url.isEmpty())
400 {
401 KMessageBox::error( 0L, i18n("Can't add bookmark with empty URL"));
402 return;
403 }
404 QString title = QString::fromUtf8(Url::decodeString(Url.getName()).c_str());
405 if (title.isEmpty())
406 title = url;
407
408 KBookmarkGroup parentBookmark = m_pManager->findByAddress( m_parentAddress ).toGroup();
409 Q_ASSERT(!parentBookmark.isNull());
410 // If this title is already used, we'll try to find something unused.
411 KBookmark ch = parentBookmark.first();
412 int count = 1;
413 QString uniqueTitle = title;
414 do
415 {
416 while ( !ch.isNull() )
417 {
418 if ( uniqueTitle == ch.text() )
419 {
420 // Title already used !
421 if ( url != ch.url().url() )
422 {
423 uniqueTitle = title + QString(" (%1)").arg(++count);
424 // New title -> restart search from the beginning
425 ch = parentBookmark.first();
426 break;
427 }
428 else
429 {
430 // this exact URL already exists
431 return;
432 }
433 }
434 ch = parentBookmark.next( ch );
435 }
436 } while ( !ch.isNull() );
437
438 parentBookmark.addBookmark( m_pManager, uniqueTitle, url, m_pOwner->currentIcon() );
439 m_pManager->emitChanged( parentBookmark );
440 }
441
slotNewFolder()442 void KCelBookmarkMenu::slotNewFolder()
443 {
444 if ( !m_pOwner ) return; // this view doesn't handle bookmarks...
445 KBookmarkGroup parentBookmark = m_pManager->findByAddress( m_parentAddress ).toGroup();
446 Q_ASSERT(!parentBookmark.isNull());
447 KBookmarkGroup group = parentBookmark.createNewFolder( m_pManager );
448 if ( !group.isNull() )
449 {
450 KBookmarkGroup parentGroup = group.parentGroup();
451 m_pManager->emitChanged( parentGroup );
452 }
453 }
454
slotBookmarkSelected()455 void KCelBookmarkMenu::slotBookmarkSelected()
456 {
457 //kdDebug(1203) << "KBookmarkMenu::slotBookmarkSelected()" << endl;
458 if ( !m_pOwner ) return; // this view doesn't handle bookmarks...
459 //kdDebug(1203) << sender()->name() << endl;
460
461 // The name of the action is the URL to open
462 m_pOwner->openBookmarkURL( QString::fromUtf8(sender()->name()) );
463 }
464
465 // -----------------------------------------------------------------------------
466
467
468