classviewmanager.cpp 15.2 KB
Newer Older
1 2
/**************************************************************************
**
3
** Copyright (c) 2013 Denis Mingulov
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29 30

#include "classviewmanager.h"
31
#include "classviewsymbollocation.h"
32 33 34 35 36 37 38
#include "classviewnavigationwidgetfactory.h"
#include "classviewparser.h"
#include "classviewutils.h"

#include <utils/qtcassert.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
39
#include <cpptools/cppmodelmanagerinterface.h>
40 41 42 43 44 45 46
#include <cpptools/cpptoolsconstants.h>
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <texteditor/itexteditor.h>

47 48 49
#include <QThread>
#include <QMutex>
#include <QMutexLocker>
50 51 52 53 54 55

namespace ClassView {
namespace Internal {

///////////////////////////////// ManagerPrivate //////////////////////////////////

56 57 58
// static variable initialization
static Manager *managerInstance = 0;

59
/*!
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
    \class ClassView::Internal::Manager

    \brief The Manager class implements a class view manager that interacts with
    other \QC plugins and acts as a proxy between them and the parser.

    The parser is moved to a separate thread and is connected to the manager by
    using signals and slots. Manager's signals starting with 'request' are for
    the parser.
*/

/*!
    \fn explicit ClassView::Internal::Manager(QObject *parent = 0)

    Creates a shared instance of a \a parent object.
*/

/*!
    \fn void ClassView::Internal::Manager::stateChanged(bool state)

    Changes the internal manager state. \a state returns true if manager is
    enabled, otherwise false.

    \sa setState, state
*/

/*!
    \fn void ClassView::Internal::Manager::treeDataUpdate(QSharedPointer<QStandardItem> result)

    Emits a signal about a tree data update (to tree view). \a result holds the
    item with the current tree.
*/

/*!
    \fn void ClassView::Internal::Manager::requestTreeDataUpdate()

    Emits a signal that a request for sending the tree view has to be handled by
    listeners (the parser).

    \sa onRequestTreeDataUpdate
*/

/*!
    \fn void ClassView::Internal::Manager::requestDocumentUpdated(CPlusPlus::Document::Ptr doc)

    Emits a signal that \a doc was updated and has to be reparsed.

    \sa onDocumentUpdated
*/

/*!
    \fn void ClassView::Internal::Manager::requestResetCurrentState()

    Emits a signal that the parser has to reset its internal state to the
    current state from the code manager.
*/

/*!
    \fn void ClassView::Internal::Manager::requestClearCache()

    Requests the parser to clear a cache.
*/

/*!
    \fn void ClassView::Internal::Manager::requestClearCacheAll()

    Requests the parser to clear a full cache.
*/

/*!
    \fn void ClassView::Internal::Manager::requestSetFlatMode(bool flat)

    Requests the manager to set the flat mode without subprojects. Set \a flat
    to \c true to enable flat mode and to false to disable it.
*/

/*!
   \class ManagerPrivate
hjk's avatar
hjk committed
137
   \internal
138 139
   \brief The ManagerPrivate class contains private class data for the Manager
    class.
140
   \sa Manager
141 142
*/

143
class ManagerPrivate
144
{
145
public:
hjk's avatar
hjk committed
146
    ManagerPrivate() : state(false), disableCodeParser(false) {}
147 148 149 150 151 152 153 154 155 156

    //! State mutex
    QMutex mutexState;

    //! code state/changes parser
    Parser parser;

    //! separate thread for the parser
    QThread parserThread;

hjk's avatar
hjk committed
157 158 159
    //! Internal manager state. \sa Manager::state
    bool state;

160 161 162 163 164 165 166 167
    //! there is some massive operation ongoing so temporary we should wait
    bool disableCodeParser;
};

///////////////////////////////// Manager //////////////////////////////////

Manager::Manager(QObject *parent)
    : QObject(parent),
168
    d(new ManagerPrivate())
169
{
170
    managerInstance = this;
171 172 173 174 175 176 177

    // register - to be able send between signal/slots
    qRegisterMetaType<QSharedPointer<QStandardItem> >("QSharedPointer<QStandardItem>");

    initialize();

    // start a separate thread for the parser
178 179
    d->parser.moveToThread(&d->parserThread);
    d->parserThread.start();
180 181 182 183 184 185 186

    // initial setup
    onProjectListChanged();
}

Manager::~Manager()
{
187 188 189 190
    d->parserThread.quit();
    d->parserThread.wait();
    delete d;
    managerInstance = 0;
191 192
}

193
Manager *Manager::instance()
194
{
195
    return managerInstance;
196 197
}

198 199 200 201
/*!
    Checks \a item for lazy data population of a QStandardItemModel.
*/

202 203
bool Manager::canFetchMore(QStandardItem *item) const
{
204
    return d->parser.canFetchMore(item);
205 206
}

207 208 209 210 211
/*!
    Checks \a item for lazy data population of a QStandardItemModel.
    \a skipRoot item is needed for the manual update, call not from model.
*/

212 213
void Manager::fetchMore(QStandardItem *item, bool skipRoot)
{
214
    d->parser.fetchMore(item, skipRoot);
215 216 217 218 219 220 221
}

void Manager::initialize()
{
    // use Qt::QueuedConnection everywhere

    // widget factory signals
222
    connect(NavigationWidgetFactory::instance(), SIGNAL(widgetIsCreated()),
223 224 225 226 227
            SLOT(onWidgetIsCreated()), Qt::QueuedConnection);

    // internal manager state is changed
    connect(this, SIGNAL(stateChanged(bool)), SLOT(onStateChanged(bool)), Qt::QueuedConnection);

Tobias Hunger's avatar
Tobias Hunger committed
228
    // connections to enable/disable navi widget factory
229 230 231 232 233 234 235 236
    ProjectExplorer::SessionManager *sessionManager =
        ProjectExplorer::ProjectExplorerPlugin::instance()->session();
    connect(sessionManager, SIGNAL(projectAdded(ProjectExplorer::Project*)),
            SLOT(onProjectListChanged()), Qt::QueuedConnection);
    connect(sessionManager, SIGNAL(projectRemoved(ProjectExplorer::Project*)),
            SLOT(onProjectListChanged()), Qt::QueuedConnection);

    // connect to the progress manager for signals about Parsing tasks
hjk's avatar
hjk committed
237
    connect(Core::ICore::progressManager(), SIGNAL(taskStarted(QString)),
238
            SLOT(onTaskStarted(QString)), Qt::QueuedConnection);
hjk's avatar
hjk committed
239
    connect(Core::ICore::progressManager(), SIGNAL(allTasksFinished(QString)),
240 241 242 243
            SLOT(onAllTasksFinished(QString)), Qt::QueuedConnection);

    // when we signals that really document is updated - sent it to the parser
    connect(this, SIGNAL(requestDocumentUpdated(CPlusPlus::Document::Ptr)),
244
            &d->parser, SLOT(parseDocument(CPlusPlus::Document::Ptr)), Qt::QueuedConnection);
245 246

    // translate data update from the parser to listeners
247
    connect(&d->parser, SIGNAL(treeDataUpdate(QSharedPointer<QStandardItem>)),
248 249 250 251
            this, SLOT(onTreeDataUpdate(QSharedPointer<QStandardItem>)), Qt::QueuedConnection);

    // requet current state - immediately after a notification
    connect(this, SIGNAL(requestTreeDataUpdate()),
252
            &d->parser, SLOT(requestCurrentState()), Qt::QueuedConnection);
253 254 255

    // full reset request to parser
    connect(this, SIGNAL(requestResetCurrentState()),
256
            &d->parser, SLOT(resetDataToCurrentState()), Qt::QueuedConnection);
257 258 259

    // clear cache request
    connect(this, SIGNAL(requestClearCache()),
260
            &d->parser, SLOT(clearCache()), Qt::QueuedConnection);
261 262 263

    // clear full cache request
    connect(this, SIGNAL(requestClearCacheAll()),
264
            &d->parser, SLOT(clearCacheAll()), Qt::QueuedConnection);
265 266 267

    // flat mode request
    connect(this, SIGNAL(requestSetFlatMode(bool)),
268
            &d->parser, SLOT(setFlatMode(bool)), Qt::QueuedConnection);
hjk's avatar
hjk committed
269 270

    // connect to the cpp model manager for signals about document updates
271 272
    CppTools::CppModelManagerInterface *codeModelManager
        = CppTools::CppModelManagerInterface::instance();
hjk's avatar
hjk committed
273 274 275 276 277 278

    // when code manager signals that document is updated - handle it by ourselves
    connect(codeModelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
            SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)), Qt::QueuedConnection);
    //
    connect(codeModelManager, SIGNAL(aboutToRemoveFiles(QStringList)),
279
            &d->parser, SLOT(removeFiles(QStringList)), Qt::QueuedConnection);
280 281
}

282 283 284 285 286 287 288 289 290
/*!
    Gets the internal manager state. If it is disabled, does not emit signals
    about parsing requests. If enabled, does parsing in the background even if
    the navigation pane is not visible. Returns true if the manager is enabled,
    false otherwise.

   \sa setState, stateChanged
*/

291 292
bool Manager::state() const
{
293
    return d->state;
294 295
}

296 297 298 299 300 301 302
/*!
    Sets the internal manager \a state to \c true if the manager has to be
    enabled, otherwise sets it to \c false.

   \sa state, stateChanged
*/

303 304
void Manager::setState(bool state)
{
305
    QMutexLocker locker(&d->mutexState);
306 307

    // boolean comparsion - should be done correctly by any compiler
308
    if (state == d->state)
309 310
        return;

311
    d->state = state;
312

313
    emit stateChanged(d->state);
314 315
}

316 317 318 319 320 321
/*!
    Reacts to the widget factory creating a widget.

    \sa setState, state
*/

322 323 324 325 326
void Manager::onWidgetIsCreated()
{
    // do nothing - continue to sleep
}

327 328 329 330 331 332 333
/*!
    Reacts to the \a visibility of one navigation pane widget being changed
    (there might be a lot of them).

   \sa setState, state
*/

334 335 336 337 338 339 340
void Manager::onWidgetVisibilityIsChanged(bool visibility)
{
    // activate data handling - when 1st time 'Class View' will be open
    if (visibility)
        setState(true);
}

341 342 343 344 345 346 347
/*!
    Reacts to the state changed signal for the current manager \a state.
    For example, requests the currect code snapshot if needed.

    \sa setState, state, stateChanged
*/

348 349 350 351 352 353 354 355 356 357 358
void Manager::onStateChanged(bool state)
{
    if (state) {
        // enabled - request a current snapshots etc?..
        emit requestResetCurrentState();
    } else {
        // disabled - clean parsers internal cache
        emit requestClearCacheAll();
    }
}

359 360 361 362 363
/*!
    Reacts to the project list being changed by updating the navigation pane
    visibility if necessary.
*/

364 365 366 367 368 369 370 371 372 373
void Manager::onProjectListChanged()
{
    // do nothing if Manager is disabled
    if (!state())
        return;

    // update to the latest state
    requestTreeDataUpdate();
}

374 375 376 377 378 379 380
/*!
    Handles parse tasks started by the progress manager. \a type holds the
    task index, which should be \c CppTools::Constants::TASK_INDEX.

   \sa CppTools::Constants::TASK_INDEX
*/

381 382
void Manager::onTaskStarted(const QString &type)
{
383
    if (type != QLatin1String(CppTools::Constants::TASK_INDEX))
384 385 386
        return;

    // disable tree updates to speed up
387
    d->disableCodeParser = true;
388 389
}

390 391 392 393 394 395 396
/*!
    Handles parse tasks finished by the progress manager.\a type holds the
    task index, which should be \c CppTools::Constants::TASK_INDEX.

   \sa CppTools::Constants::TASK_INDEX
*/

397 398
void Manager::onAllTasksFinished(const QString &type)
{
399
    if (type != QLatin1String(CppTools::Constants::TASK_INDEX))
400 401 402
        return;

    // parsing is finished, enable tree updates
403
    d->disableCodeParser = false;
404 405 406 407 408 409 410 411 412 413 414 415

    // do nothing if Manager is disabled
    if (!state())
        return;

    // any document might be changed, emit signal to clear cache
    emit requestClearCache();

    // request to update a tree to the current state
    emit requestResetCurrentState();
}

416 417 418 419 420 421 422
/*!
    Emits the signal \c documentUpdated when the code model manager state is
    changed for \a doc.

    \sa documentUpdated
*/

423 424 425 426 427 428 429
void Manager::onDocumentUpdated(CPlusPlus::Document::Ptr doc)
{
    // do nothing if Manager is disabled
    if (!state())
        return;

    // do nothing if updates are disabled
430
    if (d->disableCodeParser)
431 432 433 434 435
        return;

    emit requestDocumentUpdated(doc);
}

436 437 438 439 440
/*!
    Opens the text editor for the file \a fileName on \a line (1-based) and
    \a column (1-based).
*/

441 442
void Manager::gotoLocation(const QString &fileName, int line, int column)
{
443
    Core::EditorManager::openEditorAt(fileName, line, column);
444 445
}

446 447 448 449 450 451
/*!
    Opens the text editor for any of the symbol locations in the \a list.

   \sa Manager::gotoLocations
*/

452 453 454 455 456 457 458 459 460 461 462 463 464
void Manager::gotoLocations(const QList<QVariant> &list)
{
    QSet<SymbolLocation> locations = Utils::roleToLocations(list);

    if (locations.count() == 0)
        return;

    QString fileName;
    int line = 0;
    int column = 0;
    bool currentPositionAvailable = false;

    // what is open now?
hjk's avatar
hjk committed
465
    if (Core::IEditor *editor = Core::EditorManager::currentEditor()) {
466
        // get current file name
David Schulz's avatar
David Schulz committed
467
        if (Core::IDocument *document = editor->document())
468
            fileName = document->filePath();
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502

        // if text file - what is current position?
        TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
        if (textEditor) {
            // there is open currently text editor
            int position = textEditor->position();
            textEditor->convertPosition(position, &line, &column);
            currentPositionAvailable = true;
        }
    }

    // if there is something open - try to check, is it currently activated symbol?
    if (currentPositionAvailable) {
        SymbolLocation current(fileName, line, column);
        QSet<SymbolLocation>::const_iterator it = locations.find(current);
        QSet<SymbolLocation>::const_iterator end = locations.constEnd();
        // is it known location?
        if (it != end) {
            // found - do one additional step
            ++it;
            if (it == end)
                it = locations.begin();
            const SymbolLocation &found = *it;
            gotoLocation(found.fileName(), found.line(), found.column());
            return;
        }
    }

    // no success - open first item in the list
    const SymbolLocation loc = *locations.constBegin();

    gotoLocation(loc.fileName(), loc.line(), loc.column());
}

503 504 505 506 507 508 509
/*!
    Emits the signal \c requestTreeDataUpdate if the latest tree info is
    requested and if parsing is enabled.

   \sa requestTreeDataUpdate, NavigationWidget::requestDataUpdate
*/

510 511 512 513 514 515 516 517 518
void Manager::onRequestTreeDataUpdate()
{
    // do nothing if Manager is disabled
    if (!state())
        return;

    emit requestTreeDataUpdate();
}

519 520 521 522
/*!
    Switches to flat mode (without subprojects) if \a flat is set to \c true.
*/

523 524 525 526 527
void Manager::setFlatMode(bool flat)
{
    emit requestSetFlatMode(flat);
}

528 529 530 531 532
/*!
    Sends a new tree data update to a tree view. \a result holds the item with
    the current tree.
*/

533 534 535 536 537 538 539 540 541 542 543
void Manager::onTreeDataUpdate(QSharedPointer<QStandardItem> result)
{
    // do nothing if Manager is disabled
    if (!state())
        return;

    emit treeDataUpdate(result);
}

} // namespace Internal
} // namespace ClassView