toplevel.cpp 15.1 KB
Newer Older
1
2
/* This file is part of the KDE project
   Copyright (C) 2000 David Faure <faure@kde.org>
3
   Copyright (C) 2002-2003 Alexander Kellett <lypanov@kde.org>
4
5
6

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
Tom Albers's avatar
Tom Albers committed
7
8
   License version 2 or at your option version 3 as published by
   the Free Software Foundation.
9
10
11
12

   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
13
   General Public License for more details.
14
15
16

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.  If not, write to
Dirk Mueller's avatar
Dirk Mueller committed
17
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Dirk Mueller's avatar
Dirk Mueller committed
18
   Boston, MA 02110-1301, USA.
19
20
*/

21
#include "toplevel.h"
22
23
#include <QVBoxLayout>
#include "globalbookmarkmanager.h"
24

25
#include "kbookmarkmodel/model.h"
26

David Faure's avatar
David Faure committed
27
#include "bookmarkinfowidget.h"
28
29
#include "actionsimpl.h"
#include "exporters.h"
30
#include "settings.h"
31
32
#include "kbookmarkmodel/commands.h"
#include "kbookmarkmodel/commandhistory.h"
33
#include "kebsearchline.h"
34

35
#include <stdlib.h>
36

37
38
39
#include <QClipboard>
#include <QSplitter>
#include <QApplication>
40

41
42
43
#include <KActionCollection>
#include <KToggleAction>
#include <KBookmarkManager>
Laurent Montel's avatar
Laurent Montel committed
44
#include "keditbookmarks_debug.h"
45
46
47
48
49
#include <KEditToolBar>
#include <KLineEdit>
#include <KLocalizedString>
#include <KMessageBox>
#include <KStandardAction>
50

Yuri Chornoivan's avatar
Yuri Chornoivan committed
51
#include <QDBusConnection>
Laurent Montel's avatar
Laurent Montel committed
52
KEBApp *KEBApp::s_topLevel = nullptr;
53

54
KEBApp::KEBApp(
55
    const QString &bookmarksFile, bool readonly,
56
57
    const QString &address, bool browser, const QString &caption,
    const QString &dbusObjectName
58
) : KXmlGuiWindow(), m_bookmarksFilename(bookmarksFile),
59
    m_caption(caption),
Laurent Montel's avatar
Laurent Montel committed
60
    m_dbusObjectName(dbusObjectName), m_readOnly(readonly),m_browser(browser)
61
 {
Laurent Montel's avatar
Laurent Montel committed
62
    QDBusConnection::sessionBus().registerObject(QStringLiteral("/keditbookmarks"), this, QDBusConnection::ExportScriptableSlots);
Daniel Teske's avatar
Daniel Teske committed
63
64
    Q_UNUSED(address);//FIXME sets the current item

65
66
    m_cmdHistory = new CommandHistory(this);
    m_cmdHistory->createActions(actionCollection());
Laurent Montel's avatar
Laurent Montel committed
67
    connect(m_cmdHistory, &CommandHistory::notifyCommandExecuted, this, &KEBApp::notifyCommandExecuted);
Alexander Kellett's avatar
Alexander Kellett committed
68

69
70
    GlobalBookmarkManager::self()->createManager(m_bookmarksFilename, m_dbusObjectName, m_cmdHistory);

71
    s_topLevel = this;
72

73
74
75
76
    createActions();
    if (m_browser)
        createGUI();
    else
Laurent Montel's avatar
Laurent Montel committed
77
        createGUI(QStringLiteral("keditbookmarks-genui.rc"));
78

Laurent Montel's avatar
Laurent Montel committed
79
80
    connect(qApp->clipboard(), &QClipboard::dataChanged,
                               this, &KEBApp::slotClipboardDataChanged);
Alexander Kellett's avatar
Alexander Kellett committed
81

82
83
    m_canPaste = false;

Daniel Teske's avatar
Daniel Teske committed
84
    mBookmarkListView = new BookmarkListView();
85
    mBookmarkListView->setModel( GlobalBookmarkManager::self()->model() );
86
87
    mBookmarkListView->setSelectionMode(QAbstractItemView::ExtendedSelection);
    mBookmarkListView->loadColumnSetting();
88
    mBookmarkListView->loadFoldedState();
89

Laurent Montel's avatar
Laurent Montel committed
90
    KViewSearchLineWidget *searchline = new KViewSearchLineWidget(mBookmarkListView, this);
Daniel Teske's avatar
Daniel Teske committed
91

Laurent Montel's avatar
Laurent Montel committed
92
    mBookmarkFolderView = new BookmarkFolderView(mBookmarkListView, this);
93
    mBookmarkFolderView->expandAll();
Urs Wolfer's avatar
Urs Wolfer committed
94

95
    QWidget * rightSide = new QWidget;
96
    QVBoxLayout *listLayout = new QVBoxLayout(rightSide);
Laurent Montel's avatar
Laurent Montel committed
97
    listLayout->setContentsMargins(0, 0, 0, 0);
Urs Wolfer's avatar
Urs Wolfer committed
98
99
100
    listLayout->addWidget(searchline);
    listLayout->addWidget(mBookmarkListView);

101
    m_bkinfo = new BookmarkInfoWidget(mBookmarkListView, GlobalBookmarkManager::self()->model());
102

103
    listLayout->addWidget(m_bkinfo);
104

Daniel Teske's avatar
Daniel Teske committed
105
106
107
    QSplitter *hsplitter = new QSplitter(this);
    hsplitter->setOrientation(Qt::Horizontal);
    hsplitter->addWidget(mBookmarkFolderView);
108
    hsplitter->addWidget(rightSide);
Sebastian Sauer's avatar
Sebastian Sauer committed
109
    hsplitter->setStretchFactor(1,1);
110

Daniel Teske's avatar
Daniel Teske committed
111
112
    setCentralWidget(hsplitter);

113
114
115
    slotClipboardDataChanged();
    setAutoSaveSettings();

Laurent Montel's avatar
Laurent Montel committed
116
117
    connect(mBookmarkListView->selectionModel(), &QItemSelectionModel::selectionChanged,
            this, &KEBApp::selectionChanged);
Daniel Teske's avatar
Daniel Teske committed
118

Laurent Montel's avatar
Laurent Montel committed
119
120
    connect(mBookmarkFolderView->selectionModel(), &QItemSelectionModel::selectionChanged,
            this, &KEBApp::selectionChanged);
121

Daniel Teske's avatar
Daniel Teske committed
122
123
    setCancelFavIconUpdatesEnabled(false);
    setCancelTestsEnabled(false);
124
    updateActions();
125
126
}

Daniel Teske's avatar
Daniel Teske committed
127
128
void KEBApp::expandAll()
{
129
    mBookmarkListView->expandAll();
Daniel Teske's avatar
Daniel Teske committed
130
131
132
133
134
135
136
}

void KEBApp::collapseAll()
{
    mBookmarkListView->collapseAll();
}

Laurent Montel's avatar
Laurent Montel committed
137
138
139
140
141
QString KEBApp::bookmarkFilename()
{
   return m_bookmarksFilename;
}

142
void KEBApp::reset(const QString & caption, const QString & bookmarksFileName)
143
{
144
    //FIXME check this code, probably should be model()->setRoot instead of resetModel()
145
146
    m_caption = caption;
    m_bookmarksFilename = bookmarksFileName;
147
    GlobalBookmarkManager::self()->createManager(m_bookmarksFilename, m_dbusObjectName, m_cmdHistory); //FIXME this is still a memory leak (iff called by slotLoad)
148
    GlobalBookmarkManager::self()->model()->resetModel();
149
150
    updateActions();
}
151

152
153
154
void KEBApp::startEdit( Column c )
{
    const QModelIndexList & list = mBookmarkListView->selectionModel()->selectedIndexes();
Daniel Teske's avatar
Daniel Teske committed
155
156
157
    QModelIndexList::const_iterator it, end;
    end = list.constEnd();
    for(it = list.constBegin(); it != end; ++it)
Daniel Teske's avatar
Daniel Teske committed
158
        if( (*it).column() == int(c) && (mBookmarkListView->model()->flags(*it) & Qt::ItemIsEditable) )
Laurent Montel's avatar
Laurent Montel committed
159
            mBookmarkListView->edit( *it );
160
161
}

162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//FIXME clean up and remove unneeded things
SelcAbilities KEBApp::getSelectionAbilities() const
{
    SelcAbilities selctionAbilities;
    selctionAbilities.itemSelected   = false;
    selctionAbilities.group          = false;
    selctionAbilities.separator      = false;
    selctionAbilities.urlIsEmpty     = false;
    selctionAbilities.root           = false;
    selctionAbilities.multiSelect    = false;
    selctionAbilities.singleSelect   = false;
    selctionAbilities.notEmpty       = false;
    selctionAbilities.deleteEnabled  = false;

    KBookmark nbk;
    QModelIndexList sel = mBookmarkListView->selectionModel()->selectedIndexes();
    int columnCount;
    if(sel.count())
    {
        nbk = mBookmarkListView->bookmarkForIndex(sel.first());
        columnCount = mBookmarkListView->model()->columnCount();
    }
    else
    {
        sel = mBookmarkFolderView->selectionModel()->selectedIndexes();
187
188
        if(sel.count())
            nbk = mBookmarkFolderView->bookmarkForIndex(sel.first());
189
190
191
192
193
        columnCount = mBookmarkFolderView->model()->columnCount();
    }

    if ( sel.count() > 0)
    {
194
        selctionAbilities.deleteEnabled = true;
195
196
197
198
        selctionAbilities.itemSelected   = true;
        selctionAbilities.group          = nbk.isGroup();
        selctionAbilities.separator      = nbk.isSeparator();
        selctionAbilities.urlIsEmpty     = nbk.url().isEmpty();
199
        selctionAbilities.root           = nbk.address() == GlobalBookmarkManager::self()->root().address();
200
201
202
203
        selctionAbilities.multiSelect    = (sel.count() > columnCount);
        selctionAbilities.singleSelect   = (!selctionAbilities.multiSelect && selctionAbilities.itemSelected);
    }
    //FIXME check next line, if it actually works
204
    selctionAbilities.notEmpty = GlobalBookmarkManager::self()->root().first().hasParent();
205

Laurent Montel's avatar
Laurent Montel committed
206
/*    //qCDebug(KEDITBOOKMARKS_LOG)
207
208
209
210
211
212
213
        <<"\nsa.itemSelected "<<selctionAbilities.itemSelected
        <<"\nsa.group        "<<selctionAbilities.group
        <<"\nsa.separator    "<<selctionAbilities.separator
        <<"\nsa.urlIsEmpty   "<<selctionAbilities.urlIsEmpty
        <<"\nsa.root         "<<selctionAbilities.root
        <<"\nsa.multiSelect  "<<selctionAbilities.multiSelect
        <<"\nsa.singleSelect "<<selctionAbilities.singleSelect
214
        <<"\nsa.deleteEnabled"<<selctionAbilities.deleteEnabled;
215
216
217
218
219
220
221
222
223
224
*/
    return selctionAbilities;
}

void KEBApp::setActionsEnabled(SelcAbilities sa) {
    KActionCollection * coll = actionCollection();

    QStringList toEnable;

    if (sa.multiSelect || (sa.singleSelect && !sa.root))
Laurent Montel's avatar
Laurent Montel committed
225
        toEnable << QStringLiteral("edit_copy");
226
227

    if (sa.multiSelect || (sa.singleSelect && !sa.root && !sa.urlIsEmpty && !sa.group && !sa.separator))
Laurent Montel's avatar
Laurent Montel committed
228
            toEnable << QStringLiteral("openlink");
229
230
231

    if (!m_readOnly) {
        if (sa.notEmpty)
Laurent Montel's avatar
Laurent Montel committed
232
            toEnable << QStringLiteral("testall") << QStringLiteral("updateallfavicons");
233
234

        if ( sa.deleteEnabled && (sa.multiSelect || (sa.singleSelect && !sa.root)) )
Laurent Montel's avatar
Laurent Montel committed
235
                toEnable << QStringLiteral("delete") << QStringLiteral("edit_cut");
236
237
238

        if( sa.singleSelect)
            if (m_canPaste)
Laurent Montel's avatar
Laurent Montel committed
239
                toEnable << QStringLiteral("edit_paste");
240
241

        if( sa.multiSelect || (sa.singleSelect && !sa.root && !sa.urlIsEmpty && !sa.group && !sa.separator))
Laurent Montel's avatar
Laurent Montel committed
242
            toEnable << QStringLiteral("testlink") << QStringLiteral("updatefavicon");
243
244

        if (sa.singleSelect && !sa.root && !sa.separator) {
Laurent Montel's avatar
Laurent Montel committed
245
            toEnable << QStringLiteral("rename") << QStringLiteral("changeicon") << QStringLiteral("changecomment");
246
            if (!sa.group)
Laurent Montel's avatar
Laurent Montel committed
247
                toEnable << QStringLiteral("changeurl");
248
249
250
        }

        if (sa.singleSelect) {
Laurent Montel's avatar
Laurent Montel committed
251
            toEnable << QStringLiteral("newfolder") << QStringLiteral("newbookmark") << QStringLiteral("insertseparator");
252
            if (sa.group)
Laurent Montel's avatar
Laurent Montel committed
253
                toEnable << QStringLiteral("sort") << QStringLiteral("recursivesort") << QStringLiteral("setastoolbar");
254
255
256
        }
    }

Laurent Montel's avatar
Laurent Montel committed
257
258
    for ( QStringList::const_iterator it = toEnable.constBegin();
            it != toEnable.constEnd(); ++it )
259
    {
Laurent Montel's avatar
Laurent Montel committed
260
        ////qCDebug(KEDITBOOKMARKS_LOG) <<" enabling action "<<(*it);
261
        coll->action(*it)->setEnabled(true);
262
263
264
    }
}

265
266
KBookmark KEBApp::firstSelected() const
{
267
    QModelIndex index;
268
    const QModelIndexList & list = mBookmarkListView->selectionModel()->selectedIndexes();
269
270
271
    if(list.count()) // selection in main listview, return bookmark for firstSelected
        return mBookmarkListView->bookmarkForIndex(*list.constBegin());

272
    // no selection in main listview, fall back to selection in left tree
273
274
    const QModelIndexList & list2 = mBookmarkFolderView->selectionModel()->selectedIndexes();
    return mBookmarkFolderView->bookmarkForIndex(*list2.constBegin());
275
276
277
278
279
}

QString KEBApp::insertAddress() const
{
    KBookmark current = firstSelected();
280
    return (current.isGroup())
281
        ? (current.address() + "/0") //FIXME internal representation used
282
283
284
        : KBookmark::nextAddress(current.address());
}

285
static bool lessAddress(const QString& first, const QString& second)
286
{
Robert Knight's avatar
Robert Knight committed
287
288
289
    QString a = first;
    QString b = second;

290
291
292
    if(a == b)
         return false;

Laurent Montel's avatar
Laurent Montel committed
293
    QString error(QStringLiteral("ERROR"));
294
295
296
297
298
    if(a == error)
        return false;
    if(b == error)
        return true;

Thorsten Roeder's avatar
Thorsten Roeder committed
299
300
    a += '/';
    b += '/';
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315

    uint aLast = 0;
    uint bLast = 0;
    uint aEnd = a.length();
    uint bEnd = b.length();
    // Each iteration checks one "/"-delimeted part of the address
    // "" is treated correctly
    while(true)
    {
        // Invariant: a[0 ... aLast] == b[0 ... bLast]
        if(aLast + 1 == aEnd) //The last position was the last slash
            return true; // That means a is shorter than b
        if(bLast +1 == bEnd)
            return false;

Laurent Montel's avatar
Laurent Montel committed
316
317
        uint aNext = a.indexOf(QLatin1String("/"), aLast + 1);
        uint bNext = b.indexOf(QLatin1String("/"), bLast + 1);
318
319

        bool okay;
Laurent Montel's avatar
Laurent Montel committed
320
        uint aNum = a.midRef(aLast + 1, aNext - aLast - 1).toUInt(&okay);
321
322
        if(!okay)
            return false;
Laurent Montel's avatar
Laurent Montel committed
323
        uint bNum = b.midRef(bLast + 1, bNext - bLast - 1).toUInt(&okay);
324
325
326
327
328
329
330
331
332
333
334
        if(!okay)
            return true;

        if(aNum != bNum)
            return aNum < bNum;

        aLast = aNext;
        bLast = bNext;
    }
}

335
static bool lessBookmark(const KBookmark & first, const KBookmark & second) //FIXME Using internal represantation
336
337
338
339
{
    return lessAddress(first.address(), second.address());
}

Daniel Teske's avatar
Daniel Teske committed
340
341
342
KBookmark::List KEBApp::allBookmarks() const
{
    KBookmark::List bookmarks;
343
    selectedBookmarksExpandedHelper(GlobalBookmarkManager::self()->root(), bookmarks);
Daniel Teske's avatar
Daniel Teske committed
344
345
346
    return bookmarks;
}

347
KBookmark::List KEBApp::selectedBookmarks() const
348
{
349
    KBookmark::List bookmarks;
350
    const QModelIndexList & list = mBookmarkListView->selectionModel()->selectedIndexes();
351
352
353
354
355
356
357
    if (!list.isEmpty()) {
      QModelIndexList::const_iterator it, end;
      end = list.constEnd();
      for( it = list.constBegin(); it != end; ++it) {
	  if((*it).column() != 0)
	      continue;
	  KBookmark bk = mBookmarkListView->bookmarkModel()->bookmarkForIndex(*it);;
358
	  if(bk.address() != GlobalBookmarkManager::self()->root().address())
359
360
              bookmarks.push_back( bk );
      }
Laurent Montel's avatar
Laurent Montel committed
361
      std::sort(bookmarks.begin(), bookmarks.end(), lessBookmark);
362
363
    } else {
        bookmarks.push_back(firstSelected());
364
    }
Daniel Teske's avatar
Daniel Teske committed
365

366
367
368
    return bookmarks;
}

369
KBookmark::List KEBApp::selectedBookmarksExpanded() const
370
{
371
372
373
    KBookmark::List bookmarks = selectedBookmarks();
    KBookmark::List result;
    KBookmark::List::const_iterator it, end;
374
375
376
377
378
379
380
381
    end = bookmarks.constEnd();
    for(it = bookmarks.constBegin(); it != end; ++it)
    {
        selectedBookmarksExpandedHelper( *it, result );
    }
    return result;
}

Robert Knight's avatar
Robert Knight committed
382
void KEBApp::selectedBookmarksExpandedHelper(const KBookmark& bk, KBookmark::List & bookmarks) const
383
{
Daniel Teske's avatar
Daniel Teske committed
384
385
    //FIXME in which order parents should ideally be: parent then child
    // or child and then parents
386
387
388
389
    if(bk.isGroup())
    {
        KBookmarkGroup parent = bk.toGroup();
        KBookmark child = parent.first();
390
        while(!child.isNull())
391
392
393
394
395
        {
            selectedBookmarksExpandedHelper(child, bookmarks);
            child = parent.next(child);
        }
    }
Daniel Teske's avatar
Daniel Teske committed
396
397
398
399
    else
    {
        bookmarks.push_back( bk );
    }
Daniel Teske's avatar
Daniel Teske committed
400
401
}

Alexander Kellett's avatar
Alexander Kellett committed
402
KEBApp::~KEBApp() {
403
404

    // Save again, just in case the user expanded/collapsed folders (#131127)
405
    GlobalBookmarkManager::self()->notifyManagers();
406

Laurent Montel's avatar
Laurent Montel committed
407
    s_topLevel = nullptr;
Daniel Teske's avatar
Daniel Teske committed
408
    delete m_cmdHistory;
409
    delete m_actionsImpl;
410
    delete mBookmarkListView;
411
    delete GlobalBookmarkManager::self();
412
413
}

414
KToggleAction* KEBApp::getToggleAction(const char *action) const {
415
    return static_cast<KToggleAction*>(actionCollection()->action(action));
416
417
}

Alexander Kellett's avatar
Alexander Kellett committed
418
void KEBApp::resetActions() {
Laurent Montel's avatar
Laurent Montel committed
419
420
    stateChanged(QStringLiteral("disablestuff"));
    stateChanged(QStringLiteral("normal"));
Alexander Kellett's avatar
Alexander Kellett committed
421

422
    if (!m_readOnly)
Laurent Montel's avatar
Laurent Montel committed
423
        stateChanged(QStringLiteral("notreadonly"));
424
}
Alexander Kellett's avatar
Alexander Kellett committed
425

426
427
428
429
430
void  KEBApp::selectionChanged()
{
    updateActions();
}

Alexander Kellett's avatar
Alexander Kellett committed
431
void KEBApp::updateActions() {
432
    resetActions();
433
    setActionsEnabled(getSelectionAbilities());
434
435
}

Alexander Kellett's avatar
Alexander Kellett committed
436
void KEBApp::slotClipboardDataChanged() {
Laurent Montel's avatar
Laurent Montel committed
437
    // //qCDebug(KEDITBOOKMARKS_LOG) << "KEBApp::slotClipboardDataChanged";
438
    if (!m_readOnly) {
Daniel Teske's avatar
Daniel Teske committed
439
440
        m_canPaste = KBookmark::List::canDecode(
                        QApplication::clipboard()->mimeData());
Daniel Teske's avatar
Daniel Teske committed
441
        updateActions();
442
    }
443
444
}

Alexander Kellett's avatar
Alexander Kellett committed
445
/* -------------------------- */
446

447
void KEBApp::notifyCommandExecuted() {
Laurent Montel's avatar
Laurent Montel committed
448
    // //qCDebug(KEDITBOOKMARKS_LOG) << "KEBApp::notifyCommandExecuted()";
449
    updateActions();
450
451
}

452
/* -------------------------- */
453

Alexander Kellett's avatar
Alexander Kellett committed
454
void KEBApp::slotConfigureToolbars() {
Laurent Montel's avatar
Laurent Montel committed
455
    //PORT TO QT5 saveMainWindowSettings(KConfigGroup( KSharedConfig::openConfig(), "MainWindow") );
Craig Drummond's avatar
Craig Drummond committed
456
    KEditToolBar dlg(actionCollection(), this);
Laurent Montel's avatar
Laurent Montel committed
457
    connect(&dlg, &KEditToolBar::newToolBarConfig, this, &KEBApp::slotNewToolbarConfig);
458
    dlg.exec();
459
460
}

Alexander Kellett's avatar
Alexander Kellett committed
461
void KEBApp::slotNewToolbarConfig() {
462
463
    // called when OK or Apply is clicked
    createGUI();
Laurent Montel's avatar
Laurent Montel committed
464
    applyMainWindowSettings(KConfigGroup(KSharedConfig::openConfig(), "MainWindow") );
465
466
}

Alexander Kellett's avatar
Alexander Kellett committed
467
/* -------------------------- */
Alexander Kellett's avatar
Alexander Kellett committed
468

Laurent Montel's avatar
Laurent Montel committed
469

470