<commit_before>#include <QtGlobal>

#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
#include <QtWidgets>
#else
#include <QtGui>
#endif
#include <QHeaderView>
#include <QDesktopServices>
#include <QEvent>
#include <QShowEvent>
#include <QHideEvent>
#include <Qt>

#include "utils/utils.h"
#include "seafile-applet.h"
#include "account-mgr.h"
#include "rpc/rpc-client.h"
#include "rpc/local-repo.h"
#include "download-repo-dialog.h"
#include "clone-tasks-dialog.h"
#include "repo-item.h"
#include "repo-item-delegate.h"
#include "repo-tree-model.h"
#include "repo-detail-dialog.h"
#include "utils/paint-utils.h"
#include "repo-service.h"

#include "filebrowser/file-browser-manager.h"
#include "filebrowser/file-browser-dialog.h"
#include "utils/utils-mac.h"
#include "filebrowser/tasks.h"
#include "filebrowser/progress-dialog.h"
#include "ui/set-repo-password-dialog.h"

#include "repo-tree-view.h"

namespace {

const int kRepoTreeMenuIconSize = 16;
const int kRepoTreeToolbarIconSize = 24;

const char *kRepoTreeViewSettingsGroup = "RepoTreeView";
const char *kRepoTreeViewSettingsExpandedCategories = "expandedCategories";

QString buildMoreInfo(ServerRepo& repo, const QUrl& url_in)
{
    json_t *object = NULL;
    char *info = NULL;

    object = json_object();
    json_object_set_new(object, "is_readonly", json_integer(repo.readonly));

    QUrl url(url_in);
    url.setPath("/");
    json_object_set_new(
        object, "server_url", json_string(url.toString().toUtf8().data()));

    info = json_dumps(object, 0);
    QString ret = QString::fromUtf8(info);
    json_decref (object);
    free (info);
    return ret;
}

QString getRepoProperty(const QString& repo_id, const QString& name)
{
    QString value;
    seafApplet->rpcClient()->getRepoProperty(repo_id, name, &value);
    return value;
}

// A Helper Class to copy file
//
class FileCopyHelper : public QRunnable {
public:
    FileCopyHelper(const QString &source, const QString &target,
                   RepoTreeView *parent)
    : source_(source),
      target_(target),
      parent_(parent) {
    }
signals:
    void success(bool);
private:
    void run() {
        if (!QFile::copy(source_, target_)) {
            // cannot do GUI operations in this thread
            // callback to the main thread
            QMetaObject::invokeMethod(parent_, "copyFileFailed");
        }
    }
    bool autoDelete() {
        return true;
    }
    const QString source_;
    const QString target_;
    RepoTreeView *parent_;
};

FileCopyHelper* copyFile(const QString &source, const QString &target, RepoTreeView *parent) {
      FileCopyHelper* helper = new FileCopyHelper(source, target, parent);
      QThreadPool *pool = QThreadPool::globalInstance();
      pool->start(helper);
      return helper;
}

}

RepoTreeView::RepoTreeView(QWidget *parent)
    : QTreeView(parent)
{
    header()->hide();
    createActions();

    // We draw the indicator ourselves
    setIndentation(0);
    // We handle the click oursevles
    setExpandsOnDoubleClick(false);

    connect(this, SIGNAL(clicked(const QModelIndex&)),
            this, SLOT(onItemClicked(const QModelIndex&)));

    connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
            this, SLOT(onItemDoubleClicked(const QModelIndex&)));
#if defined(Q_OS_MAC)
    this->setAttribute(Qt::WA_MacShowFocusRect, 0);
#endif

    loadExpandedCategries();
    connect(qApp, SIGNAL(aboutToQuit()),
            this, SLOT(saveExpandedCategries()));
    connect(seafApplet->accountManager(), SIGNAL(beforeAccountChanged()),
            this, SLOT(saveExpandedCategries()));
    connect(seafApplet->accountManager(), SIGNAL(accountsChanged()),
            this, SLOT(loadExpandedCategries()));

    setAcceptDrops(true);
    setDefaultDropAction(Qt::CopyAction);
}

void RepoTreeView::loadExpandedCategries()
{
    Account account = seafApplet->accountManager()->currentAccount();
    if (!account.isValid()) {
        return;
    }
    expanded_categroies_.clear();
    QSettings settings;
    settings.beginGroup(kRepoTreeViewSettingsGroup);
    QString key = QString(kRepoTreeViewSettingsExpandedCategories) + "-" + account.getSignature();
    if (settings.contains(key)) {
        QString cats = settings.value(key, "").toString();
        expanded_categroies_ = QSet<QString>::fromList(cats.split("\t", QString::SkipEmptyParts));
    } else {
        // Expand "recent updated" on first use
        expanded_categroies_.insert(tr("Recently Updated"));
    }
    settings.endGroup();
}

void RepoTreeView::contextMenuEvent(QContextMenuEvent *event)
{
    QPoint pos = event->pos();
    QModelIndex index = indexAt(pos);
    if (!index.isValid()) {
        // Not clicked at a repo item
        return;
    }

    QStandardItem *item = getRepoItem(index);
    if (!item || item->type() != REPO_ITEM_TYPE) {
        return;
    }
    updateRepoActions();
    QMenu *menu = prepareContextMenu((RepoItem *)item);
    pos = viewport()->mapToGlobal(pos);
    menu->exec(pos);
}

QMenu* RepoTreeView::prepareContextMenu(const RepoItem *item)
{<commit_msg>fix the memory leak every time the repo item menu is popped<commit_after>#include <QtGlobal>

#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
#include <QtWidgets>
#else
#include <QtGui>
#endif
#include <QHeaderView>
#include <QDesktopServices>
#include <QEvent>
#include <QShowEvent>
#include <QHideEvent>
#include <Qt>

#include "utils/utils.h"
#include "seafile-applet.h"
#include "account-mgr.h"
#include "rpc/rpc-client.h"
#include "rpc/local-repo.h"
#include "download-repo-dialog.h"
#include "clone-tasks-dialog.h"
#include "repo-item.h"
#include "repo-item-delegate.h"
#include "repo-tree-model.h"
#include "repo-detail-dialog.h"
#include "utils/paint-utils.h"
#include "repo-service.h"

#include "filebrowser/file-browser-manager.h"
#include "filebrowser/file-browser-dialog.h"
#include "utils/utils-mac.h"
#include "filebrowser/tasks.h"
#include "filebrowser/progress-dialog.h"
#include "ui/set-repo-password-dialog.h"

#include "repo-tree-view.h"

namespace {

const int kRepoTreeMenuIconSize = 16;
const int kRepoTreeToolbarIconSize = 24;

const char *kRepoTreeViewSettingsGroup = "RepoTreeView";
const char *kRepoTreeViewSettingsExpandedCategories = "expandedCategories";

QString buildMoreInfo(ServerRepo& repo, const QUrl& url_in)
{
    json_t *object = NULL;
    char *info = NULL;

    object = json_object();
    json_object_set_new(object, "is_readonly", json_integer(repo.readonly));

    QUrl url(url_in);
    url.setPath("/");
    json_object_set_new(
        object, "server_url", json_string(url.toString().toUtf8().data()));

    info = json_dumps(object, 0);
    QString ret = QString::fromUtf8(info);
    json_decref (object);
    free (info);
    return ret;
}

QString getRepoProperty(const QString& repo_id, const QString& name)
{
    QString value;
    seafApplet->rpcClient()->getRepoProperty(repo_id, name, &value);
    return value;
}

// A Helper Class to copy file
//
class FileCopyHelper : public QRunnable {
public:
    FileCopyHelper(const QString &source, const QString &target,
                   RepoTreeView *parent)
    : source_(source),
      target_(target),
      parent_(parent) {
    }
signals:
    void success(bool);
private:
    void run() {
        if (!QFile::copy(source_, target_)) {
            // cannot do GUI operations in this thread
            // callback to the main thread
            QMetaObject::invokeMethod(parent_, "copyFileFailed");
        }
    }
    bool autoDelete() {
        return true;
    }
    const QString source_;
    const QString target_;
    RepoTreeView *parent_;
};

FileCopyHelper* copyFile(const QString &source, const QString &target, RepoTreeView *parent) {
      FileCopyHelper* helper = new FileCopyHelper(source, target, parent);
      QThreadPool *pool = QThreadPool::globalInstance();
      pool->start(helper);
      return helper;
}

}

RepoTreeView::RepoTreeView(QWidget *parent)
    : QTreeView(parent)
{
    header()->hide();
    createActions();

    // We draw the indicator ourselves
    setIndentation(0);
    // We handle the click oursevles
    setExpandsOnDoubleClick(false);

    connect(this, SIGNAL(clicked(const QModelIndex&)),
            this, SLOT(onItemClicked(const QModelIndex&)));

    connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
            this, SLOT(onItemDoubleClicked(const QModelIndex&)));
#if defined(Q_OS_MAC)
    this->setAttribute(Qt::WA_MacShowFocusRect, 0);
#endif

    loadExpandedCategries();
    connect(qApp, SIGNAL(aboutToQuit()),
            this, SLOT(saveExpandedCategries()));
    connect(seafApplet->accountManager(), SIGNAL(beforeAccountChanged()),
            this, SLOT(saveExpandedCategries()));
    connect(seafApplet->accountManager(), SIGNAL(accountsChanged()),
            this, SLOT(loadExpandedCategries()));

    setAcceptDrops(true);
    setDefaultDropAction(Qt::CopyAction);
}

void RepoTreeView::loadExpandedCategries()
{
    Account account = seafApplet->accountManager()->currentAccount();
    if (!account.isValid()) {
        return;
    }
    expanded_categroies_.clear();
    QSettings settings;
    settings.beginGroup(kRepoTreeViewSettingsGroup);
    QString key = QString(kRepoTreeViewSettingsExpandedCategories) + "-" + account.getSignature();
    if (settings.contains(key)) {
        QString cats = settings.value(key, "").toString();
        expanded_categroies_ = QSet<QString>::fromList(cats.split("\t", QString::SkipEmptyParts));
    } else {
        // Expand "recent updated" on first use
        expanded_categroies_.insert(tr("Recently Updated"));
    }
    settings.endGroup();
}

void RepoTreeView::contextMenuEvent(QContextMenuEvent *event)
{
    QPoint pos = event->pos();
    QModelIndex index = indexAt(pos);
    if (!index.isValid()) {
        // Not clicked at a repo item
        return;
    }

    QStandardItem *item = getRepoItem(index);
    if (!item || item->type() != REPO_ITEM_TYPE) {
        return;
    }
    updateRepoActions();
    QMenu *menu = prepareContextMenu((RepoItem *)item);
    pos = viewport()->mapToGlobal(pos);
    menu->exec(pos);
    menu->deleteLater();
}

QMenu* RepoTreeView::prepareContextMenu(const RepoItem *item)
{
{'fraction_non_alphanumeric': 0.1332093569540961, 'fraction_numerical': 0.0023490261329157285, 'mean_word_length': 3.884321223709369, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 52, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6169252', 'n_tokens_mistral': 3294, 'n_tokens_neox': 3270, 'n_words': 678}