<commit_before>#include "main_window.hpp"
#include "../data/data_library.hpp"
#include "data_library_view.hpp"
#include "plot_data_settings_view.hpp"
#include "plot_settings_view2.hpp"
#include "../plot/plot_view.hpp"
#include "../plot/line_plot.hpp"
#include "../plot/heat_map.hpp"
#include "../plot/scatter_plot_1d.hpp"
#include "../plot/scatter_plot_2d.hpp"
#include "../io/hdf5.hpp"
#include "../json/json.hpp"
#include "../json/utils.hpp"
#include "../utility/error.hpp"
#include <project/project.pb.h>
#include <sstream>

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QMenuBar>
#include <QAction>
#include <QFileDialog>
#include <QDebug>
#include <QMessageBox>
#include <QToolButton>
#include <QToolBar>
#include <QContextMenuEvent>
#include <QMenu>
#include <QDialog>
#include <QDialogButtonBox>
#include <QMimeData>

#include <fstream>

namespace datavis {

MainWindow::MainWindow(QWidget * parent):
    QMainWindow(parent)
{
    m_lib = new DataLibrary(this);

    m_lib_view = new DataLibraryView;
    m_lib_view->setLibrary(m_lib);

    auto lib_action_bar = new QToolBar;
    {
        auto action = lib_action_bar->addAction("Plot");
        connect(action, &QAction::triggered,
                this, &MainWindow::plotSelectedObject);
    }

    lib_action_bar->addSeparator();

    {
        auto action = lib_action_bar->addAction("Add View");
        connect(action, &QAction::triggered,
                this, &MainWindow::addPlotView);
    }

    auto tool_layout = new QVBoxLayout;
    tool_layout->addWidget(m_lib_view);
    tool_layout->addWidget(lib_action_bar);

    auto content_view = new QWidget;

    auto layout = new QHBoxLayout(content_view);
    layout->addLayout(tool_layout, 0);

    setCentralWidget(content_view);

    connect(m_lib, &DataLibrary::openFailed,
            this, &MainWindow::onOpenFailed,
            Qt::QueuedConnection);

    connect(m_lib_view, &DataLibraryView::selectionChanged,
            this, &MainWindow::onSelectedDataChanged);

    makeMenu();

    setAcceptDrops(true);
}

void MainWindow::makeMenu()
{
    //auto menuBar = new QMenuBar();
    auto menuBar = this->menuBar();

    auto fileMenu = menuBar->addMenu("File");

    {
        auto action = fileMenu->addAction("Open Project...");
        connect(action, &QAction::triggered,
                this, &MainWindow::openProject);
    }
    {
        auto action = fileMenu->addAction("Save Project");
        connect(action, &QAction::triggered,
                this, &MainWindow::saveProject);
    }
    {
        auto action = fileMenu->addAction("Save Project As...");
        connect(action, &QAction::triggered,
                this, &MainWindow::saveProjectAs);
    }
    {
        auto action = fileMenu->addAction("Close Project.");
        connect(action, &QAction::triggered,
                this, &MainWindow::closeProject);
    }
    {
        auto action = fileMenu->addAction("Open Data...");
        connect(action, &QAction::triggered,
                this, &MainWindow::openData);
    }
    fileMenu->addSeparator();
    {
        auto action = fileMenu->addAction("Quit");
        action->setMenuRole(QAction::QuitRole);
        action->setShortcut(QKeySequence::Quit);
        connect(action, &QAction::triggered,
                this, &MainWindow::close);
    }
}

void MainWindow::openFile(const QString & path)
{
    if (path.endsWith(".json"))
    {
        openProjectFile(path);
    }
    else
    {
        openDataFile(path);
    }
}

void MainWindow::openData()
{
    auto file = QFileDialog::getOpenFileName(this);
    if (file.isEmpty())
        return;

    openDataFile(file);
}

void MainWindow::openDataFile(const QString & file_path)
{
    m_lib->open(file_path);
}

void MainWindow::onOpenFailed(const QString & path, const QString & reason)
{
    auto message = QString("Failed to open file:\n") + path;
    if (!reason.isEmpty())
    {
        message += "\n\n";
        message += reason;
    }

    QMessageBox::warning(this, "Open Failed", message);
}

void MainWindow::onSelectedDataChanged()
{

}

bool MainWindow::hasSelectedObject()
{
    auto object_id = m_lib_view->selectedDatasetId();
    return !object_id.empty();
}

void MainWindow::plotSelectedObject()
{
    auto source = m_lib_view->selectedSource();
    if (!source)
        return;

    auto object_id = m_lib_view->selectedDatasetId();
    if (object_id.empty())
        return;

    plot(source, object_id);
}

PlotGridView * MainWindow::addPlotView()
{
    auto view = new PlotGridView;
    view->setAcceptDrops(true);
    view->installEventFilter(this);
    connect(view, &PlotGridView::datasetDropped,
            this, &MainWindow::onDatasetDroppedOnPlotView);
    m_plot_views.push_back(view);

    view->show();

    return view;
}

void MainWindow::removePlotView(PlotGridView * view)
{
    if (m_selected_plot_view == view)
        m_selected_plot_view = nullptr;

    m_plot_views.remove(view);
    view->removeEventFilter(this);
    view->deleteLater();
}

void MainWindow::onDatasetDroppedOnPlotView(const QVariant & data)
{
    auto dropped_data = data.value<PlotGridView::DroppedDataset>();

    auto source = m_lib->source(QString::fromStdString(dropped_data.source_id));
    if (!source)
        return;

    auto plot = makePlot(source, dropped_data.dataset_id);
    if (!plot)
        return;

    dropped_data.view->addPlot(plot, dropped_data.row, dropped_data.column);
}

Plot * MainWindow::makePlot(DataSource * source, const string & datasetId)
{<commit_msg>Improve plot window placement<commit_after>#include "main_window.hpp"
#include "../data/data_library.hpp"
#include "data_library_view.hpp"
#include "plot_data_settings_view.hpp"
#include "plot_settings_view2.hpp"
#include "../plot/plot_view.hpp"
#include "../plot/line_plot.hpp"
#include "../plot/heat_map.hpp"
#include "../plot/scatter_plot_1d.hpp"
#include "../plot/scatter_plot_2d.hpp"
#include "../io/hdf5.hpp"
#include "../json/json.hpp"
#include "../json/utils.hpp"
#include "../utility/error.hpp"
#include <project/project.pb.h>
#include <sstream>

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QMenuBar>
#include <QAction>
#include <QFileDialog>
#include <QDebug>
#include <QMessageBox>
#include <QToolButton>
#include <QToolBar>
#include <QContextMenuEvent>
#include <QMenu>
#include <QDialog>
#include <QDialogButtonBox>
#include <QMimeData>
#include <QGuiApplication>
#include <QScreen>

#include <fstream>
#include <algorithm>

namespace datavis {

MainWindow::MainWindow(QWidget * parent):
    QMainWindow(parent)
{
    m_lib = new DataLibrary(this);

    m_lib_view = new DataLibraryView;
    m_lib_view->setLibrary(m_lib);

    auto lib_action_bar = new QToolBar;
    {
        auto action = lib_action_bar->addAction("Plot");
        connect(action, &QAction::triggered,
                this, &MainWindow::plotSelectedObject);
    }

    lib_action_bar->addSeparator();

    {
        auto action = lib_action_bar->addAction("Add View");
        connect(action, &QAction::triggered,
                this, &MainWindow::addPlotView);
    }

    auto tool_layout = new QVBoxLayout;
    tool_layout->addWidget(m_lib_view);
    tool_layout->addWidget(lib_action_bar);

    auto content_view = new QWidget;

    auto layout = new QHBoxLayout(content_view);
    layout->addLayout(tool_layout, 0);

    setCentralWidget(content_view);

    connect(m_lib, &DataLibrary::openFailed,
            this, &MainWindow::onOpenFailed,
            Qt::QueuedConnection);

    connect(m_lib_view, &DataLibraryView::selectionChanged,
            this, &MainWindow::onSelectedDataChanged);

    makeMenu();

    setAcceptDrops(true);
}

void MainWindow::makeMenu()
{
    //auto menuBar = new QMenuBar();
    auto menuBar = this->menuBar();

    auto fileMenu = menuBar->addMenu("File");

    {
        auto action = fileMenu->addAction("Open Project...");
        connect(action, &QAction::triggered,
                this, &MainWindow::openProject);
    }
    {
        auto action = fileMenu->addAction("Save Project");
        connect(action, &QAction::triggered,
                this, &MainWindow::saveProject);
    }
    {
        auto action = fileMenu->addAction("Save Project As...");
        connect(action, &QAction::triggered,
                this, &MainWindow::saveProjectAs);
    }
    {
        auto action = fileMenu->addAction("Close Project.");
        connect(action, &QAction::triggered,
                this, &MainWindow::closeProject);
    }
    {
        auto action = fileMenu->addAction("Open Data...");
        connect(action, &QAction::triggered,
                this, &MainWindow::openData);
    }
    fileMenu->addSeparator();
    {
        auto action = fileMenu->addAction("Quit");
        action->setMenuRole(QAction::QuitRole);
        action->setShortcut(QKeySequence::Quit);
        connect(action, &QAction::triggered,
                this, &MainWindow::close);
    }
}

void MainWindow::openFile(const QString & path)
{
    if (path.endsWith(".json"))
    {
        openProjectFile(path);
    }
    else
    {
        openDataFile(path);
    }
}

void MainWindow::openData()
{
    auto file = QFileDialog::getOpenFileName(this);
    if (file.isEmpty())
        return;

    openDataFile(file);
}

void MainWindow::openDataFile(const QString & file_path)
{
    m_lib->open(file_path);
}

void MainWindow::onOpenFailed(const QString & path, const QString & reason)
{
    auto message = QString("Failed to open file:\n") + path;
    if (!reason.isEmpty())
    {
        message += "\n\n";
        message += reason;
    }

    QMessageBox::warning(this, "Open Failed", message);
}

void MainWindow::onSelectedDataChanged()
{

}

bool MainWindow::hasSelectedObject()
{
    auto object_id = m_lib_view->selectedDatasetId();
    return !object_id.empty();
}

void MainWindow::plotSelectedObject()
{
    auto source = m_lib_view->selectedSource();
    if (!source)
        return;

    auto object_id = m_lib_view->selectedDatasetId();
    if (object_id.empty())
        return;

    plot(source, object_id);
}

PlotGridView * MainWindow::addPlotView()
{
    auto view = new PlotGridView;
    view->setAcceptDrops(true);
    view->installEventFilter(this);
    connect(view, &PlotGridView::datasetDropped,
            this, &MainWindow::onDatasetDroppedOnPlotView);
    m_plot_views.push_back(view);

    auto * screen = qGuiApp->primaryScreen();

    if (screen)
    {
        auto screenRect = screen->availableGeometry();
        int margin = std::min(screenRect.width(), screenRect.height()) * 0.15;
        //int xMargin = screenRect.width() * 0.15;
        //int yMargin = screenRect.height() * 0.15;
        int xMargin = margin;
        int yMargin = margin;
        view->setGeometry(screenRect.adjusted(xMargin, yMargin, -xMargin, -yMargin));
    }

    view->showMaximized();

    return view;
}

void MainWindow::removePlotView(PlotGridView * view)
{
    if (m_selected_plot_view == view)
        m_selected_plot_view = nullptr;

    m_plot_views.remove(view);
    view->removeEventFilter(this);
    view->deleteLater();
}

void MainWindow::onDatasetDroppedOnPlotView(const QVariant & data)
{
    auto dropped_data = data.value<PlotGridView::DroppedDataset>();

    auto source = m_lib->source(QString::fromStdString(dropped_data.source_id));
    if (!source)
        return;

    auto plot = makePlot(source, dropped_data.dataset_id);
    if (!plot)
        return;

    dropped_data.view->addPlot(plot, dropped_data.row, dropped_data.column);
}

Plot * MainWindow::makePlot(DataSource * source, const string & datasetId)
{
{'fraction_non_alphanumeric': 0.1375679640976957, 'fraction_numerical': 0.0016397687063087944, 'mean_word_length': 3.341701011614837, 'pattern_counts': {'":': 0, '<': 42, '<?xml version=': 0, '>': 103, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '723761', 'n_tokens_mistral': 3761, 'n_tokens_neox': 3658, 'n_words': 666}