<commit_before>/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** 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.
**
** GNU Lesser General Public License Usage
** 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
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "documentmodel.h"
#include "ieditor.h"
#include <coreplugin/idocument.h>

#include <utils/qtcassert.h>

#include <QDir>
#include <QIcon>

namespace Core {

struct DocumentModelPrivate
{
    DocumentModelPrivate();
    ~DocumentModelPrivate();

    const QIcon m_lockedIcon;
    const QIcon m_unlockedIcon;

    QList<DocumentModel::Entry *> m_documents;
    QMap<IDocument *, QList<IEditor *> > m_editors;
};

DocumentModelPrivate::DocumentModelPrivate() :
    m_lockedIcon(QLatin1String(":/core/images/locked.png")),
    m_unlockedIcon(QLatin1String(":/core/images/unlocked.png"))
{
}

DocumentModelPrivate::~DocumentModelPrivate()
{
    qDeleteAll(m_documents);
}

DocumentModel::Entry::Entry() :
    document(0)
{
}

DocumentModel::DocumentModel(QObject *parent) :
    QAbstractItemModel(parent), d(new DocumentModelPrivate)
{
}

DocumentModel::~DocumentModel()
{
    delete d;
}

QIcon DocumentModel::lockedIcon() const
{
    return d->m_lockedIcon;
}

QIcon DocumentModel::unlockedIcon() const
{
    return d->m_unlockedIcon;
}

QString DocumentModel::Entry::fileName() const {
    return document ? document->filePath() : m_fileName;
}

QString DocumentModel::Entry::displayName() const {
    return document ? document->displayName() : m_displayName;
}

Id DocumentModel::Entry::id() const
{
    return m_id;
}

int DocumentModel::columnCount(const QModelIndex &parent) const
{
    if (!parent.isValid())
        return 2;
    return 0;
}

int DocumentModel::rowCount(const QModelIndex &parent) const
{
    if (!parent.isValid())
        return d->m_documents.count() + 1/*<no document>*/;
    return 0;
}

// TODO remove
QList<IEditor *> DocumentModel::oneEditorForEachOpenedDocument() const
{
    QList<IEditor *> result;
    QMapIterator<IDocument *, QList<IEditor *> > it(d->m_editors);
    while (it.hasNext())
        result << it.next().value().first();
    return result;
}

void DocumentModel::addEditor(IEditor *editor, bool *isNewDocument)
{
    if (!editor)
        return;

    QList<IEditor *> &editorList = d->m_editors[editor->document()];
    bool isNew = editorList.isEmpty();
    if (isNewDocument)
        *isNewDocument = isNew;
    editorList << editor;
    if (isNew) {
        Entry *entry = new Entry;
        entry->document = editor->document();
        entry->m_id = editor->id();
        addEntry(entry);
    }
}

void DocumentModel::addRestoredDocument(const QString &fileName, const QString &displayName, const Id &id)
{
    Entry *entry = new Entry;
    entry->m_fileName = fileName;
    entry->m_displayName = displayName;
    entry->m_id = id;
    addEntry(entry);
}

DocumentModel::Entry *DocumentModel::firstRestoredDocument() const
{
    for (int i = 0; i < d->m_documents.count(); ++i)
        if (!d->m_documents.at(i)->document)
            return d->m_documents.at(i);
    return 0;
}

void DocumentModel::addEntry(Entry *entry)
{
    QString fileName = entry->fileName();

    // replace a non-loaded entry (aka 'restored') if possible
    int previousIndex = indexOfFilePath(fileName);
    if (previousIndex >= 0) {
        if (entry->document && d->m_documents.at(previousIndex)->document == 0) {
            Entry *previousEntry = d->m_documents.at(previousIndex);
            d->m_documents[previousIndex] = entry;
            delete previousEntry;
            connect(entry->document, SIGNAL(changed()), this, SLOT(itemChanged()));
        } else {
            delete entry;
        }
        return;
    }

    int index;
    QString displayName = entry->displayName();
    for (index = 0; index < d->m_documents.count(); ++index) {
        if (displayName < d->m_documents.at(index)->displayName())
            break;
    }
    int row = index + 1/*<no document>*/;
    beginInsertRows(QModelIndex(), row, row);
    d->m_documents.insert(index, entry);
    if (entry->document)
        connect(entry->document, SIGNAL(changed()), this, SLOT(itemChanged()));
    endInsertRows();
}

int DocumentModel::indexOfFilePath(const QString &filePath) const
{
    if (filePath.isEmpty())
        return -1;
    for (int i = 0; i < d->m_documents.count(); ++i) {
        if (d->m_documents.at(i)->fileName() == filePath)
            return i;
    }
    return -1;
}

void DocumentModel::removeEntry(DocumentModel::Entry *entry)
{
    QTC_ASSERT(!entry->document, return); // we wouldn't know what to do with the associated editors
    int index = d->m_documents.indexOf(entry);
    removeDocument(index);
}

void DocumentModel::removeEditor(IEditor *editor, bool *lastOneForDocument)
{
    if (lastOneForDocument)
        *lastOneForDocument = false;
    QTC_ASSERT(editor, return);
    IDocument *document = editor->document();
    QTC_ASSERT(d->m_editors.contains(document), return);<commit_msg>Resolve links and normalize file names when opening editors again.<commit_after>/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** 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.
**
** GNU Lesser General Public License Usage
** 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
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "documentmodel.h"
#include "ieditor.h"
#include <coreplugin/documentmanager.h>
#include <coreplugin/idocument.h>

#include <utils/qtcassert.h>

#include <QDir>
#include <QIcon>

namespace Core {

struct DocumentModelPrivate
{
    DocumentModelPrivate();
    ~DocumentModelPrivate();

    const QIcon m_lockedIcon;
    const QIcon m_unlockedIcon;

    QList<DocumentModel::Entry *> m_documents;
    QMap<IDocument *, QList<IEditor *> > m_editors;
};

DocumentModelPrivate::DocumentModelPrivate() :
    m_lockedIcon(QLatin1String(":/core/images/locked.png")),
    m_unlockedIcon(QLatin1String(":/core/images/unlocked.png"))
{
}

DocumentModelPrivate::~DocumentModelPrivate()
{
    qDeleteAll(m_documents);
}

DocumentModel::Entry::Entry() :
    document(0)
{
}

DocumentModel::DocumentModel(QObject *parent) :
    QAbstractItemModel(parent), d(new DocumentModelPrivate)
{
}

DocumentModel::~DocumentModel()
{
    delete d;
}

QIcon DocumentModel::lockedIcon() const
{
    return d->m_lockedIcon;
}

QIcon DocumentModel::unlockedIcon() const
{
    return d->m_unlockedIcon;
}

QString DocumentModel::Entry::fileName() const {
    return document ? document->filePath() : m_fileName;
}

QString DocumentModel::Entry::displayName() const {
    return document ? document->displayName() : m_displayName;
}

Id DocumentModel::Entry::id() const
{
    return m_id;
}

int DocumentModel::columnCount(const QModelIndex &parent) const
{
    if (!parent.isValid())
        return 2;
    return 0;
}

int DocumentModel::rowCount(const QModelIndex &parent) const
{
    if (!parent.isValid())
        return d->m_documents.count() + 1/*<no document>*/;
    return 0;
}

// TODO remove
QList<IEditor *> DocumentModel::oneEditorForEachOpenedDocument() const
{
    QList<IEditor *> result;
    QMapIterator<IDocument *, QList<IEditor *> > it(d->m_editors);
    while (it.hasNext())
        result << it.next().value().first();
    return result;
}

void DocumentModel::addEditor(IEditor *editor, bool *isNewDocument)
{
    if (!editor)
        return;

    QList<IEditor *> &editorList = d->m_editors[editor->document()];
    bool isNew = editorList.isEmpty();
    if (isNewDocument)
        *isNewDocument = isNew;
    editorList << editor;
    if (isNew) {
        Entry *entry = new Entry;
        entry->document = editor->document();
        entry->m_id = editor->id();
        addEntry(entry);
    }
}

void DocumentModel::addRestoredDocument(const QString &fileName, const QString &displayName, const Id &id)
{
    Entry *entry = new Entry;
    entry->m_fileName = fileName;
    entry->m_displayName = displayName;
    entry->m_id = id;
    addEntry(entry);
}

DocumentModel::Entry *DocumentModel::firstRestoredDocument() const
{
    for (int i = 0; i < d->m_documents.count(); ++i)
        if (!d->m_documents.at(i)->document)
            return d->m_documents.at(i);
    return 0;
}

void DocumentModel::addEntry(Entry *entry)
{
    QString fileName = entry->fileName();

    // replace a non-loaded entry (aka 'restored') if possible
    int previousIndex = indexOfFilePath(fileName);
    if (previousIndex >= 0) {
        if (entry->document && d->m_documents.at(previousIndex)->document == 0) {
            Entry *previousEntry = d->m_documents.at(previousIndex);
            d->m_documents[previousIndex] = entry;
            delete previousEntry;
            connect(entry->document, SIGNAL(changed()), this, SLOT(itemChanged()));
        } else {
            delete entry;
        }
        return;
    }

    int index;
    QString displayName = entry->displayName();
    for (index = 0; index < d->m_documents.count(); ++index) {
        if (displayName < d->m_documents.at(index)->displayName())
            break;
    }
    int row = index + 1/*<no document>*/;
    beginInsertRows(QModelIndex(), row, row);
    d->m_documents.insert(index, entry);
    if (entry->document)
        connect(entry->document, SIGNAL(changed()), this, SLOT(itemChanged()));
    endInsertRows();
}

int DocumentModel::indexOfFilePath(const QString &filePath) const
{
    if (filePath.isEmpty())
        return -1;
    const QString fixedPath = DocumentManager::fixFileName(filePath, DocumentManager::KeepLinks);
    for (int i = 0; i < d->m_documents.count(); ++i) {
        if (DocumentManager::fixFileName(d->m_documents.at(i)->fileName(), DocumentManager::KeepLinks) == fixedPath)
            return i;
    }
    return -1;
}

void DocumentModel::removeEntry(DocumentModel::Entry *entry)
{
    QTC_ASSERT(!entry->document, return); // we wouldn't know what to do with the associated editors
    int index = d->m_documents.indexOf(entry);
    removeDocument(index);
}

void DocumentModel::removeEditor(IEditor *editor, bool *lastOneForDocument)
{
    if (lastOneForDocument)
        *lastOneForDocument = false;
    QTC_ASSERT(editor, return);
    IDocument *document = editor->document();
    QTC_ASSERT(d->m_editors.contains(document), return);
{'fraction_non_alphanumeric': 0.14988179669030732, 'fraction_numerical': 0.004412923561859732, 'mean_word_length': 3.884911470361817, 'pattern_counts': {'":': 4, '<': 48, '<?xml version=': 0, '>': 114, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5559508', 'n_tokens_mistral': 3794, 'n_tokens_neox': 3704, 'n_words': 976}