outputwidget.cpp 21.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* This file is part of KDevelop
 *
 * Copyright 2007 Andreas Pakulat <apaku@gmx.de>
 * Copyright 2007 Dukju Ahn <dukjuahn@gmail.com>
 *
 * 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 "outputwidget.h"

#include "standardoutputview.h"
25
26

#include <QAction>
27
28
#include <QApplication>
#include <QClipboard>
Kevin Funk's avatar
Kevin Funk committed
29
#include <QIcon>
Kevin Funk's avatar
Kevin Funk committed
30
#include <QLineEdit>
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <QRegExp>
#include <QSortFilterProxyModel>
#include <QStackedWidget>
#include <QTabWidget>
#include <QToolButton>
#include <QTreeView>
#include <QVBoxLayout>
#include <QWidgetAction>

#include <KActionCollection>
#include <KLocalizedString>
#include <KStandardAction>
#include <KToggleAction>
44

45
#include <outputview/ioutputviewmodel.h>
46
#include <util/focusedtreeview.h>
47
#include <util/expandablelineedit.h>
48

49
#include "outputmodel.h"
50
#include "toolviewdata.h"
51
#include <debug.h>
52

53
54
Q_DECLARE_METATYPE(QTreeView*)

55
OutputWidget::OutputWidget(QWidget* parent, const ToolViewData* tvdata)
Milian Wolff's avatar
Milian Wolff committed
56
    : QWidget( parent )
57
58
    , m_tabwidget(nullptr)
    , m_stackwidget(nullptr)
Milian Wolff's avatar
Milian Wolff committed
59
    , data(tvdata)
60
61
    , m_closeButton(nullptr)
    , m_closeOthersAction(nullptr)
62
63
64
65
66
67
    , m_nextAction(nullptr)
    , m_previousAction(nullptr)
    , m_activateOnSelect(nullptr)
    , m_focusOnSelect(nullptr)
    , m_filterInput(nullptr)
    , m_filterAction(nullptr)
68
{
69
    setWindowTitle(i18n("Output View"));
70
    setWindowIcon(tvdata->icon);
71
    auto* layout = new QVBoxLayout(this);
72
    layout->setMargin(0);
73
74
    if( data->type & KDevelop::IOutputView::MultipleView )
    {
75
76
        m_tabwidget = new QTabWidget(this);
        layout->addWidget( m_tabwidget );
77
        m_closeButton = new QToolButton( this );
78
        connect( m_closeButton, &QToolButton::clicked, this, &OutputWidget::closeActiveView );
79
        m_closeButton->setIcon( QIcon::fromTheme( QStringLiteral( "tab-close") ) );
80
        m_closeButton->setToolTip( i18n( "Close the currently active output view") );
81
        m_closeButton->setAutoRaise(true);
82

83
        m_closeOthersAction = new QAction( this );
84
        connect(m_closeOthersAction, &QAction::triggered, this, &OutputWidget::closeOtherViews);
85
        m_closeOthersAction->setIcon(QIcon::fromTheme(QStringLiteral("tab-close-other")));
86
        m_closeOthersAction->setToolTip( i18n( "Close all other output views" ) );
87
        m_closeOthersAction->setText( m_closeOthersAction->toolTip() );
88
89
        addAction(m_closeOthersAction);

90
        m_tabwidget->setCornerWidget(m_closeButton, Qt::TopRightCorner);
91
        m_tabwidget->setDocumentMode(true);
92
93
    } else if ( data->type == KDevelop::IOutputView::HistoryView )
    {
94
95
96
97
98
99
100
101
102
        m_stackwidget = new QStackedWidget( this );
        layout->addWidget( m_stackwidget );

        m_previousAction = new QAction( QIcon::fromTheme( QStringLiteral( "arrow-left" ) ), i18n("Previous Output"), this );
        connect(m_previousAction, &QAction::triggered, this, &OutputWidget::previousOutput);
        addAction(m_previousAction);
        m_nextAction = new QAction( QIcon::fromTheme( QStringLiteral( "arrow-right" ) ), i18n("Next Output"), this );
        connect(m_nextAction, &QAction::triggered, this, &OutputWidget::nextOutput);
        addAction(m_nextAction);
103
    }
104

105
106
107
108
    m_activateOnSelect = new KToggleAction( QIcon(), i18n("Select activated Item"), this );
    m_activateOnSelect->setChecked( true );
    m_focusOnSelect = new KToggleAction( QIcon(), i18n("Focus when selecting Item"), this );
    m_focusOnSelect->setChecked( false );
109
    if( data->option & KDevelop::IOutputView::ShowItemsButton )
110
    {
111
112
        addAction(m_activateOnSelect);
        addAction(m_focusOnSelect);
113
    }
114

115
    auto *separator = new QAction(this);
116
    separator->setSeparator(true);
117
118
119
120
    addAction(separator);

    QAction* action;

121
122
123
124
    action = new QAction(QIcon::fromTheme(QStringLiteral("go-first")), i18n("First Item"), this);
    connect(action, &QAction::triggered, this, &OutputWidget::selectFirstItem);
    addAction(action);

125
    action = new QAction(QIcon::fromTheme(QStringLiteral("go-previous")), i18n("Previous Item"), this);
126
127
128
    connect(action, &QAction::triggered, this, &OutputWidget::selectPreviousItem);
    addAction(action);

129
    action = new QAction(QIcon::fromTheme(QStringLiteral("go-next")), i18n("Next Item"), this);
130
131
    connect(action, &QAction::triggered, this, &OutputWidget::selectNextItem);
    addAction(action);
132

133
134
135
136
    action = new QAction(QIcon::fromTheme(QStringLiteral("go-last")), i18n("Last Item"), this);
    connect(action, &QAction::triggered, this, &OutputWidget::selectLastItem);
    addAction(action);

Kevin Funk's avatar
Kevin Funk committed
137
    QAction* selectAllAction = KStandardAction::selectAll(this, SLOT(selectAll()), this);
Kevin Funk's avatar
Kevin Funk committed
138
    selectAllAction->setShortcut(QKeySequence()); //FIXME: why does CTRL-A conflict with Katepart (while CTRL-Cbelow doesn't) ?
139
140
141
    selectAllAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    addAction(selectAllAction);

Kevin Funk's avatar
Kevin Funk committed
142
    QAction* copyAction = KStandardAction::copy(this, SLOT(copySelection()), this);
143
    copyAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
144
    addAction(copyAction);
145

146
147
148
149
    QAction *clearAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-clear-list")), i18n("Clear"), this);
    connect(clearAction, &QAction::triggered, this, &OutputWidget::clearModel);
    addAction(clearAction);

150
    if( data->option & KDevelop::IOutputView::AddFilterAction )
151
    {
152
        auto *separator = new QAction(this);
153
        separator->setSeparator(true);
154
        addAction(separator);
155

156
        m_filterInput = new KExpandableLineEdit(this);
157
158
159
160
        m_filterInput->setPlaceholderText(i18n("Search..."));
        m_filterInput->setClearButtonEnabled(true);
        m_filterInput->setToolTip(i18n("Enter a wild card string to filter the output view"));
        m_filterAction = new QWidgetAction(this);
161
        m_filterAction->setText(m_filterInput->placeholderText());
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
162
        connect(m_filterAction, &QAction::triggered, this, [this]() {m_filterInput->setFocus();});
163
164
165
166
        m_filterAction->setDefaultWidget(m_filterInput);
        addAction(m_filterAction);

        connect(m_filterInput, &QLineEdit::textEdited,
167
                this, &OutputWidget::outputFilter );
168
169
        if( data->type & KDevelop::IOutputView::MultipleView )
        {
170
            connect(m_tabwidget, &QTabWidget::currentChanged,
171
                    this, &OutputWidget::updateFilter);
172
173
        } else if ( data->type == KDevelop::IOutputView::HistoryView )
        {
174
            connect(m_stackwidget, &QStackedWidget::currentChanged,
175
                    this, &OutputWidget::updateFilter);
176
        }
177
    }
178

179
    addActions(data->actionList);
180

181
182
    connect( data, &ToolViewData::outputAdded,
             this, &OutputWidget::addOutput );
183

184
185
    connect( this, &OutputWidget::outputRemoved,
             data->plugin, &StandardOutputView::outputRemoved );
186

187
    foreach( int id, data->outputdata.keys() )
188
    {
189
        changeModel( id );
190
        changeDelegate( id );
191
    }
192
    enableActions();
193
194
}

Anton Anikin's avatar
Anton Anikin committed
195
196
197
198
199
200
201
202
203
204
205
OutputWidget::~OutputWidget()
{
    // Disconnect our widget to prevent updateFilter() slot calling from parent's destructor,
    // which leads to segfault since m_views hash will be destroyed before.
    if (m_tabwidget) {
        m_tabwidget->disconnect(this);
    } else if (m_stackwidget) {
        m_stackwidget->disconnect(this);
    }
}

206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
void OutputWidget::clearModel()
{
    auto view = qobject_cast<QAbstractItemView*>(currentWidget());
    if( !view || !view->isVisible())
        return;

    KDevelop::OutputModel *outputModel = nullptr;
    if (auto proxy = qobject_cast<QAbstractProxyModel*>(view->model())) {
        outputModel = qobject_cast<KDevelop::OutputModel*>(proxy->sourceModel());
    } else {
        outputModel = qobject_cast<KDevelop::OutputModel*>(view->model());
    }
    outputModel->clear();
}

221
222
void OutputWidget::addOutput( int id )
{
223
    QTreeView* listview = createListView(id);
224
    setCurrentWidget( listview );
225
226
    connect( data->outputdata.value(id), &OutputData::modelChanged, this, &OutputWidget::changeModel);
    connect( data->outputdata.value(id), &OutputData::delegateChanged, this, &OutputWidget::changeDelegate);
Dāvis Mosāns's avatar
Dāvis Mosāns committed
227

228
    enableActions();
229
}
230

231
void OutputWidget::setCurrentWidget( QTreeView* view )
232
233
234
{
    if( data->type & KDevelop::IOutputView::MultipleView )
    {
235
        m_tabwidget->setCurrentWidget( view );
236
237
    } else if( data->type & KDevelop::IOutputView::HistoryView )
    {
238
        m_stackwidget->setCurrentWidget( view );
239
240
241
    }
}

242
243
void OutputWidget::changeDelegate( int id )
{
244
245
246
247
    const auto viewIt = m_views.constFind(id);
    const auto dataIt = data->outputdata.constFind(id);
    if (dataIt != data->outputdata.constEnd() && viewIt != m_views.constEnd()) {
        (*viewIt).view->setItemDelegate((*dataIt)->delegate);
248
    } else {
249
        addOutput(id);
250
    }
251
252
}

253
void OutputWidget::changeModel( int id )
254
{
255
256
257
258
    const auto viewIt = m_views.constFind(id);
    const auto dataIt = data->outputdata.constFind(id);
    if (dataIt != data->outputdata.constEnd() && viewIt != m_views.constEnd()) {
        (*viewIt).view->setModel((*dataIt)->model);
259
    }
260
261
    else
    {
262
        addOutput( id );
263
264
265
    }
}

266
void OutputWidget::removeOutput( int id )
267
{
268
269
270
    const auto viewIt = m_views.constFind(id);
    if (data->outputdata.contains(id) && (viewIt != m_views.constEnd())) {
        auto view = viewIt->view;
271
        if( data->type & KDevelop::IOutputView::MultipleView || data->type & KDevelop::IOutputView::HistoryView )
272
        {
273
274
            if( data->type & KDevelop::IOutputView::MultipleView )
            {
275
                int idx = m_tabwidget->indexOf( view );
276
                if (idx != -1)
277
                {
278
                    m_tabwidget->removeTab( idx );
279
280
281
                }
            } else
            {
282
                int idx = m_stackwidget->indexOf( view );
283
                if (idx != -1)
284
                {
285
                    m_stackwidget->removeWidget(view);
286
                }
287
            }
Anton Anikin's avatar
Anton Anikin committed
288
289
290
291
        } else {
            // KDevelop::IOutputView::OneView case
            // Do nothig here since our single view will be automatically removed from layout
            // during it's destroy
292
        }
Anton Anikin's avatar
Anton Anikin committed
293

294
        m_views.erase(viewIt);
Anton Anikin's avatar
Anton Anikin committed
295
        // remove our view with proxy model which is view's child (see outputFilter() method).
296
        delete view;
Anton Anikin's avatar
Anton Anikin committed
297

298
        emit outputRemoved( data->toolViewId, id );
299
    }
300
    enableActions();
301
}
Andreas Pakulat's avatar
Andreas Pakulat committed
302

303
304
void OutputWidget::closeActiveView()
{
305
    QWidget* widget = m_tabwidget->currentWidget();
306
307
    if( !widget )
        return;
308
    foreach( int id, m_views.keys() )
309
    {
310
        if (m_views.value(id).view == widget)
311
        {
312
            OutputData* od = data->outputdata.value(id);
313
            if( od->behaviour & KDevelop::IOutputView::AllowUserClose )
Andreas Pakulat's avatar
Andreas Pakulat committed
314
            {
315
                data->plugin->removeOutput( id );
Andreas Pakulat's avatar
Andreas Pakulat committed
316
317
318
            }
        }
    }
319
    enableActions();
Andreas Pakulat's avatar
Andreas Pakulat committed
320
}
321

322
323
void OutputWidget::closeOtherViews()
{
324
    QWidget* widget = m_tabwidget->currentWidget();
325
326
327
    if (!widget)
        return;

328
    foreach (int id, m_views.keys()) {
329
        if (m_views.value(id).view == widget) {
330
331
332
333
334
335
336
337
338
339
340
            continue; // leave the active view open
        }

        OutputData* od = data->outputdata.value(id);
        if (od->behaviour & KDevelop::IOutputView::AllowUserClose) {
            data->plugin->removeOutput( id );
        }
    }
    enableActions();
}

341
QWidget* OutputWidget::currentWidget() const
342
343
344
345
{
    QWidget* widget;
    if( data->type & KDevelop::IOutputView::MultipleView )
    {
346
        widget = m_tabwidget->currentWidget();
347
    } else if( data->type & KDevelop::IOutputView::HistoryView )
348
    {
349
        widget = m_stackwidget->currentWidget();
350
351
    } else
    {
Anton Anikin's avatar
Anton Anikin committed
352
        widget = m_views.begin()->view;
353
354
355
356
    }
    return widget;
}

357
KDevelop::IOutputViewModel *OutputWidget::outputViewModel() const
358
{
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
359
360
    auto view = qobject_cast<QAbstractItemView*>(currentWidget());
    if( !view || !view->isVisible())
361
        return nullptr;
362
363

    QAbstractItemModel *absmodel = view->model();
364
    auto* iface = qobject_cast<KDevelop::IOutputViewModel*>(absmodel);
365
    if ( ! iface )
366
    {
367
        // try if it's a proxy model?
368
        if ( auto* proxy = qobject_cast<QAbstractProxyModel*>(absmodel) )
369
        {
370
            iface = qobject_cast<KDevelop::IOutputViewModel*>(proxy->sourceModel());
371
372
        }
    }
373
    return iface;
374
375
}

376
void OutputWidget::eventuallyDoFocus()
377
378
{
    QWidget* widget = currentWidget();
379
    if( m_focusOnSelect->isChecked() && !widget->hasFocus() ) {
Andreas Pakulat's avatar
Andreas Pakulat committed
380
381
        widget->setFocus( Qt::OtherFocusReason );
    }
382
}
Andreas Pakulat's avatar
Andreas Pakulat committed
383

384
385
QAbstractItemView *OutputWidget::outputView() const
{
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
386
    return qobject_cast<QAbstractItemView*>(currentWidget());
387
388
389
390
391
392
}

void OutputWidget::activateIndex(const QModelIndex &index, QAbstractItemView *view, KDevelop::IOutputViewModel *iface)
{
    if( ! index.isValid() )
        return;
393
394
    QModelIndex sourceIndex = index;
    QModelIndex viewIndex = index;
395
396
397
    auto fvIt = findFilteredView(view);
    if (fvIt != m_views.end() && fvIt->proxyModel) {
        auto proxy = fvIt->proxyModel;
398
        if ( index.model() == proxy ) {
399
400
401
402
403
            // index is from the proxy, map it to the source
            sourceIndex = proxy->mapToSource(index);
        } else if (proxy == view->model()) {
            // index is from the source, map it to the proxy
            viewIndex = proxy->mapFromSource(index);
404
405
        }
    }
406
407
408
409

    view->setCurrentIndex( viewIndex );
    view->scrollTo( viewIndex );

410
    if( m_activateOnSelect->isChecked() ) {
411
        iface->activate( sourceIndex );
412
    }
413
414
}

415
416
417
418
419
void OutputWidget::selectFirstItem()
{
    selectItem(First);
}

420
void OutputWidget::selectNextItem()
421
{
Kevin Funk's avatar
Kevin Funk committed
422
    selectItem(Next);
423
424
}

425
void OutputWidget::selectPreviousItem()
426
427
428
429
{
    selectItem(Previous);
}

430
431
432
433
434
435
void OutputWidget::selectLastItem()
{
    selectItem(Last);
}

void OutputWidget::selectItem(SelectionMode selectionMode)
436
437
438
439
{
    auto view = outputView();
    auto iface = outputViewModel();
    if ( ! view || ! iface )
440
        return;
441
    eventuallyDoFocus();
442
443

    auto index = view->currentIndex();
444
445
446
    auto fvIt = findFilteredView(view);
    if (fvIt != m_views.end() && fvIt->proxyModel) {
        auto proxy = fvIt->proxyModel;
447
448
449
450
451
452
        if ( index.model() == proxy ) {
            // index is from the proxy, map it to the source
            index = proxy->mapToSource(index);
        }
    }

453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
    QModelIndex newIndex;
    switch (selectionMode) {
        case First:
            newIndex = iface->firstHighlightIndex();
            break;
        case Next:
            newIndex = iface->nextHighlightIndex( index );
            break;
        case Previous:
            newIndex = iface->previousHighlightIndex( index );
            break;
        case Last:
            newIndex = iface->lastHighlightIndex();
            break;
    }
468

Dāvis Mosāns's avatar
Dāvis Mosāns committed
469
    qCDebug(PLUGIN_STANDARDOUTPUTVIEW) << "old:" << index << "- new:" << newIndex;
470
    activateIndex(newIndex, view, iface);
471
}
472

473
474
475
476
477
478
479
void OutputWidget::activate(const QModelIndex& index)
{
    auto iface = outputViewModel();
    auto view = outputView();
    if( ! view || ! iface )
        return;
    activateIndex(index, view, iface);
480
}
481

482
QTreeView* OutputWidget::createListView(int id)
483
{
Anton Anikin's avatar
Anton Anikin committed
484
    auto createHelper = [&]() -> QTreeView* {
485
        auto* listview = new KDevelop::FocusedTreeView(this);
486
487
488
489
490
491
492
493
494
495
        listview->setEditTriggers( QAbstractItemView::NoEditTriggers );
        listview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); //Always enable the scrollbar, so it doesn't flash around
        listview->setHeaderHidden(true);
        listview->setUniformRowHeights(true);
        listview->setRootIsDecorated(false);
        listview->setSelectionMode( QAbstractItemView::ContiguousSelection );

        if (data->outputdata.value(id)->behaviour & KDevelop::IOutputView::AutoScroll) {
            listview->setAutoScrollAtEnd(true);
        }
496

497
498
499
        connect(listview, &QTreeView::activated, this, &OutputWidget::activate);
        connect(listview, &QTreeView::clicked, this, &OutputWidget::activate);

Anton Anikin's avatar
Anton Anikin committed
500
        return listview;
501
    };
502

Anton Anikin's avatar
Anton Anikin committed
503
    QTreeView* listview = nullptr;
504
505
506
507
    const auto viewIt = m_views.constFind(id);
    if (viewIt != m_views.constEnd()) {
        listview = viewIt->view;
    } else {
508
509
        bool newView = true;

510
511
        if( data->type & KDevelop::IOutputView::MultipleView || data->type & KDevelop::IOutputView::HistoryView )
        {
Dāvis Mosāns's avatar
Dāvis Mosāns committed
512
            qCDebug(PLUGIN_STANDARDOUTPUTVIEW) << "creating listview";
513
            listview = createHelper();
514

515
516
            if( data->type & KDevelop::IOutputView::MultipleView )
            {
Anton Anikin's avatar
Anton Anikin committed
517
                m_tabwidget->addTab(listview, data->outputdata.value(id)->title);
518
519
            } else
            {
Anton Anikin's avatar
Anton Anikin committed
520
521
                const int index = m_stackwidget->addWidget(listview);
                m_stackwidget->setCurrentIndex(index);
522
523
524
            }
        } else
        {
525
            if( m_views.isEmpty() )
526
            {
527
                listview = createHelper();
Anton Anikin's avatar
Anton Anikin committed
528
                layout()->addWidget(listview);
529
530
            } else
            {
531
                listview = m_views.begin().value().view;
532
                newView = false;
533
            }
534
        }
535
        m_views[id].view = listview;
536

537
538
        changeModel( id );
        changeDelegate( id );
539
540
541

        if (newView)
            listview->scrollToBottom();
542
    }
543
    enableActions();
Anton Anikin's avatar
Anton Anikin committed
544
    return listview;
545
546
}

547
void OutputWidget::raiseOutput(int id)
548
{
549
550
551
    const auto viewIt = m_views.constFind(id);
    if (viewIt != m_views.constEnd()) {
        auto view = viewIt->view;
552
        if( data->type & KDevelop::IOutputView::MultipleView )
553
        {
554
            int idx = m_tabwidget->indexOf(view);
555
556
            if( idx >= 0 )
            {
557
                m_tabwidget->setCurrentIndex( idx );
558
559
560
            }
        } else if( data->type & KDevelop::IOutputView::HistoryView )
        {
561
            int idx = m_stackwidget->indexOf(view);
562
563
            if( idx >= 0 )
            {
564
                m_stackwidget->setCurrentIndex( idx );
565
            }
566
        }
567
    }
568
569
570
571
572
    enableActions();
}

void OutputWidget::nextOutput()
{
573
    if( m_stackwidget && m_stackwidget->currentIndex() < m_stackwidget->count()-1 )
574
    {
575
        m_stackwidget->setCurrentIndex( m_stackwidget->currentIndex()+1 );
576
577
578
579
580
581
    }
    enableActions();
}

void OutputWidget::previousOutput()
{
582
    if( m_stackwidget && m_stackwidget->currentIndex() > 0 )
583
    {
584
        m_stackwidget->setCurrentIndex( m_stackwidget->currentIndex()-1 );
585
586
587
588
589
590
591
592
    }
    enableActions();
}

void OutputWidget::enableActions()
{
    if( data->type == KDevelop::IOutputView::HistoryView )
    {
593
594
595
596
597
        Q_ASSERT(m_stackwidget);
        Q_ASSERT(m_nextAction);
        Q_ASSERT(m_previousAction);
        m_previousAction->setEnabled( ( m_stackwidget->currentIndex() > 0 ) );
        m_nextAction->setEnabled( ( m_stackwidget->currentIndex() < m_stackwidget->count() - 1 ) );
598
    }
599
}
600

601
602
603
604
605
void OutputWidget::scrollToIndex( const QModelIndex& idx )
{
    QWidget* w = currentWidget();
    if( !w )
        return;
606
    auto *view = static_cast<QAbstractItemView*>(w);
607
608
609
    view->scrollTo( idx );
}

610
611
612
613
614
void OutputWidget::copySelection()
{
    QWidget* widget = currentWidget();
    if( !widget )
        return;
615
    auto* view = qobject_cast<QAbstractItemView*>(widget);
616
617
618
619
    if( !view )
        return;

    QClipboard *cb = QApplication::clipboard();
620
    const QModelIndexList indexes = view->selectionModel()->selectedRows();
621
622
    QStringList content;
    content.reserve(indexes.size());
623
    for (const QModelIndex& index : indexes) {
624
        content += index.data().toString();
625
    }
626
    cb->setText(content.join(QLatin1Char('\n')));
627
628
}

629
630
void OutputWidget::selectAll()
{
631
    if (auto *view = qobject_cast<QAbstractItemView*>(currentWidget()))
632
        view->selectAll();
633
634
}

635
int OutputWidget::currentOutputIndex()
William Gil's avatar
William Gil committed
636
{
Loïc Favier's avatar
Loïc Favier committed
637
638
639
    int index = 0;
    if( data->type & KDevelop::IOutputView::MultipleView )
    {
640
        index = m_tabwidget->currentIndex();
Loïc Favier's avatar
Loïc Favier committed
641
642
    } else if( data->type & KDevelop::IOutputView::HistoryView )
    {
643
        index = m_stackwidget->currentIndex();
Loïc Favier's avatar
Loïc Favier committed
644
    }
645
646
647
    return index;
}

Milian Wolff's avatar
Milian Wolff committed
648
void OutputWidget::outputFilter(const QString& filter)
649
{
650
    auto *view = qobject_cast<QAbstractItemView*>(currentWidget());
651
652
    if( !view )
        return;
653

654
    auto fvIt = findFilteredView(view);
655
    auto proxyModel = qobject_cast<QSortFilterProxyModel*>(view->model());
656
    if( !proxyModel )
657
    {
Anton Anikin's avatar
Anton Anikin committed
658
659
660
        // create new proxy model and make view it's parent. This allows us destroy view and
        // it's model with "one shot" (see removeOutput() method).
        fvIt->proxyModel = proxyModel = new QSortFilterProxyModel(view);
661
662
663
        proxyModel->setDynamicSortFilter(true);
        proxyModel->setSourceModel(view->model());
        view->setModel(proxyModel);
William Gil's avatar
William Gil committed
664
    }
665
666
    QRegExp regExp(filter, Qt::CaseInsensitive);
    proxyModel->setFilterRegExp(regExp);
667
    fvIt->filter = filter;
668
669
670
671
}

void OutputWidget::updateFilter(int index)
{
672
673
    QWidget *view = (data->type & KDevelop::IOutputView::MultipleView)
        ? m_tabwidget->widget(index) : m_stackwidget->widget(index);
674
    auto fvIt = findFilteredView(qobject_cast<QAbstractItemView*>(view));
675

676
    if (fvIt != m_views.end() && !fvIt->filter.isEmpty())
677
    {
678
        m_filterInput->setText(fvIt->filter);
679
680
    } else
    {
681
        m_filterInput->clear();
682
    }
William Gil's avatar
William Gil committed
683
}
684

685
686
void OutputWidget::setTitle(int outputId, const QString& title)
{
687
688
    auto fview = m_views.value(outputId, FilteredView{});
    if (fview.view && (data->type & KDevelop::IOutputView::MultipleView)) {
Anton Anikin's avatar
Anton Anikin committed
689
        const int idx = m_tabwidget->indexOf(fview.view);
690
        if (idx >= 0) {
691
            m_tabwidget->setTabText(idx, title);
692
        }
693
694
    }
}
695
696
697
698
699
700
701
702
703
704

QHash<int, OutputWidget::FilteredView>::iterator OutputWidget::findFilteredView(QAbstractItemView *view)
{
    for (auto it = m_views.begin(); it != m_views.end(); ++it) {
        if (it->view == view) {
            return it;
        }
    }
    return m_views.end();
}