<commit_before>/*
    This file is part of Android File Transfer For Linux.
    Copyright (C) 2015-2018  <NAME>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 2.1 of the License,
    or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library; if not, write to the Free Software Foundation,
    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "createdirectorydialog.h"
#include "progressdialog.h"
#include "renamedialog.h"
#include "mtpobjectsmodel.h"
#include "mtpstoragesmodel.h"
#include "fileuploader.h"
#include "utils.h"
#include <usb/Context.h>
#include <mtp/usb/TimeoutException.h>
#include <mtp/usb/DeviceBusyException.h>
#include <mtp/usb/DeviceNotFoundException.h>
#include <QClipboard>
#include <QDebug>
#include <QSortFilterProxyModel>
#include <QMessageBox>
#include <QKeyEvent>
#include <QFileDialog>
#include <QSettings>
#include <QDir>

MainWindow::MainWindow(QWidget *parent) :
	QMainWindow(parent),
	_ui(new Ui::MainWindow),
	_clipboard(QApplication::clipboard()),
	_proxyModel(new QSortFilterProxyModel),
	_storageModel(),
	_objectModel(new MtpObjectsModel()),
	_uploader(new FileUploader(_objectModel, this))
{
	_ui->setupUi(this);
	setWindowIcon(QIcon(":/android-file-transfer.png"));

	_ui->listView->setModel(_proxyModel);

	_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
	_proxyModel->sort(0);
	_proxyModel->setDynamicSortFilter(true);

	_objectModel->moveToThread(QApplication::instance()->thread());

	connect(_ui->listView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), SLOT(updateActionsState()));
	connect(_ui->listView, SIGNAL(doubleClicked(QModelIndex)), SLOT(onActivated(QModelIndex)));
	connect(_ui->listView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(showContextMenu(QPoint)));
	connect(_ui->actionBack, SIGNAL(triggered()), SLOT(back()));
	connect(_ui->actionGo_Down, SIGNAL(triggered()), SLOT(down()));
	connect(_ui->actionCreateDirectory, SIGNAL(triggered()), SLOT(createDirectory()));
	connect(_ui->actionUploadDirectory, SIGNAL(triggered()), SLOT(uploadDirectories()));
	connect(_ui->actionUpload_Album, SIGNAL(triggered()), SLOT(uploadAlbum()));
	connect(_ui->actionUpload, SIGNAL(triggered()), SLOT(uploadFiles()));
	connect(_ui->actionRename, SIGNAL(triggered()), SLOT(renameFile()));
	connect(_ui->actionDownload, SIGNAL(triggered()), SLOT(downloadFiles()));
	connect(_ui->actionDelete, SIGNAL(triggered()), SLOT(deleteFiles()));
	connect(_ui->storageList, SIGNAL(activated(int)), SLOT(onStorageChanged(int)));
	connect(_ui->actionRefresh, SIGNAL(triggered()), SLOT(refresh()));
	connect(_ui->actionPaste, SIGNAL(triggered()), SLOT(pasteFromClipboard()));

	connect(_objectModel, SIGNAL(onFilesDropped(QStringList)), SLOT(uploadFiles(QStringList)));
	connect(_objectModel, SIGNAL(existingFileOverwrite(QString)), SLOT(confirmOverwrite(QString)), Qt::BlockingQueuedConnection);

	connect(_clipboard, SIGNAL(dataChanged()), SLOT(validateClipboard()));
	validateClipboard();

	//fixme: find out how to specify alternative in designer
	_ui->actionBack->setShortcuts(_ui->actionBack->shortcuts() << QKeySequence("Alt+Up") << QKeySequence("Esc"));
	_ui->actionGo_Down->setShortcuts(_ui->actionGo_Down->shortcuts() << QKeySequence("Alt+Down") << QKeySequence("Enter"));
	_ui->actionCreateDirectory->setShortcuts(_ui->actionCreateDirectory->shortcuts() << QKeySequence("F7"));
	_ui->actionRefresh->setShortcuts(_ui->actionRefresh->shortcuts() << QKeySequence("Ctrl+R"));
	_ui->listView->setFocus();
}

MainWindow::~MainWindow()
{
	_proxyModel->setSourceModel(NULL);
	delete _objectModel;
	delete _ui;
}

void MainWindow::saveGeometry(const QString &name, const QWidget &widget)
{
	QSettings settings;
	settings.setValue("geometry/" + name, widget.saveGeometry());
}

void MainWindow::restoreGeometry(const QString &name, QWidget &widget)
{
	QSettings settings;
	QVariant geometry = settings.value("geometry/" + name);
	if (geometry.isValid())
		widget.restoreGeometry(geometry.toByteArray());
}

void MainWindow::closeEvent(QCloseEvent *event)
{
	QSettings settings;
	saveGeometry("main-window", *this);
	settings.setValue("state/main-window", saveState());
	QMainWindow::closeEvent(event);
}

bool MainWindow::reconnectToDevice()
{
	_session.reset();
	_device.reset();

	mtp::usb::ContextPtr ctx(new mtp::usb::Context);

	for (auto desc : ctx->GetDevices())
	try
	{
		auto device = mtp::Device::Open(ctx, desc);
		if (device)
			_device = device;
	}
	catch(const mtp::usb::DeviceBusyException &ex)
	{
		QMessageBox::critical(this, tr("Device is busy"), tr("Device is busy, maybe another process is using it. Close other MTP applications and restart Android File Transfer."));
	}
	catch(const std::exception &ex)
	{ qWarning("Device::Find failed: %s", ex.what()); }

	if (!_device)
	{
		QMessageBox::critical(this, tr("No MTP device found"), tr("No MTP device found"));
		return false;
	}

	qDebug() << "device found, opening session...";
	static const int MaxAttempts = 3;
	for(int attempt = 0; attempt < MaxAttempts; ++attempt)
	{
		try
		{
			_session = _device->OpenSession(1);
			mtp::msg::DeviceInfo di = _session->GetDeviceInfo();
			qDebug() << "device info" << fromUtf8(di.Manufacturer) << " " << fromUtf8(di.Model);
			break;
		}
		catch(const mtp::usb::TimeoutException &ex)
		{
			qDebug() << "timed out getting device info: " << fromUtf8(ex.what()) << ", retrying...";<commit_msg>implemented abort/ignore dialog + opening retry<commit_after>/*
    This file is part of Android File Transfer For Linux.
    Copyright (C) 2015-2018  <NAME>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 2.1 of the License,
    or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library; if not, write to the Free Software Foundation,
    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "createdirectorydialog.h"
#include "progressdialog.h"
#include "renamedialog.h"
#include "mtpobjectsmodel.h"
#include "mtpstoragesmodel.h"
#include "fileuploader.h"
#include "utils.h"
#include <usb/Context.h>
#include <mtp/usb/TimeoutException.h>
#include <mtp/usb/DeviceBusyException.h>
#include <mtp/usb/DeviceNotFoundException.h>
#include <QClipboard>
#include <QDebug>
#include <QSortFilterProxyModel>
#include <QMessageBox>
#include <QKeyEvent>
#include <QFileDialog>
#include <QSettings>
#include <QDir>

MainWindow::MainWindow(QWidget *parent) :
	QMainWindow(parent),
	_ui(new Ui::MainWindow),
	_clipboard(QApplication::clipboard()),
	_proxyModel(new QSortFilterProxyModel),
	_storageModel(),
	_objectModel(new MtpObjectsModel()),
	_uploader(new FileUploader(_objectModel, this))
{
	_ui->setupUi(this);
	setWindowIcon(QIcon(":/android-file-transfer.png"));

	_ui->listView->setModel(_proxyModel);

	_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
	_proxyModel->sort(0);
	_proxyModel->setDynamicSortFilter(true);

	_objectModel->moveToThread(QApplication::instance()->thread());

	connect(_ui->listView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), SLOT(updateActionsState()));
	connect(_ui->listView, SIGNAL(doubleClicked(QModelIndex)), SLOT(onActivated(QModelIndex)));
	connect(_ui->listView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(showContextMenu(QPoint)));
	connect(_ui->actionBack, SIGNAL(triggered()), SLOT(back()));
	connect(_ui->actionGo_Down, SIGNAL(triggered()), SLOT(down()));
	connect(_ui->actionCreateDirectory, SIGNAL(triggered()), SLOT(createDirectory()));
	connect(_ui->actionUploadDirectory, SIGNAL(triggered()), SLOT(uploadDirectories()));
	connect(_ui->actionUpload_Album, SIGNAL(triggered()), SLOT(uploadAlbum()));
	connect(_ui->actionUpload, SIGNAL(triggered()), SLOT(uploadFiles()));
	connect(_ui->actionRename, SIGNAL(triggered()), SLOT(renameFile()));
	connect(_ui->actionDownload, SIGNAL(triggered()), SLOT(downloadFiles()));
	connect(_ui->actionDelete, SIGNAL(triggered()), SLOT(deleteFiles()));
	connect(_ui->storageList, SIGNAL(activated(int)), SLOT(onStorageChanged(int)));
	connect(_ui->actionRefresh, SIGNAL(triggered()), SLOT(refresh()));
	connect(_ui->actionPaste, SIGNAL(triggered()), SLOT(pasteFromClipboard()));

	connect(_objectModel, SIGNAL(onFilesDropped(QStringList)), SLOT(uploadFiles(QStringList)));
	connect(_objectModel, SIGNAL(existingFileOverwrite(QString)), SLOT(confirmOverwrite(QString)), Qt::BlockingQueuedConnection);

	connect(_clipboard, SIGNAL(dataChanged()), SLOT(validateClipboard()));
	validateClipboard();

	//fixme: find out how to specify alternative in designer
	_ui->actionBack->setShortcuts(_ui->actionBack->shortcuts() << QKeySequence("Alt+Up") << QKeySequence("Esc"));
	_ui->actionGo_Down->setShortcuts(_ui->actionGo_Down->shortcuts() << QKeySequence("Alt+Down") << QKeySequence("Enter"));
	_ui->actionCreateDirectory->setShortcuts(_ui->actionCreateDirectory->shortcuts() << QKeySequence("F7"));
	_ui->actionRefresh->setShortcuts(_ui->actionRefresh->shortcuts() << QKeySequence("Ctrl+R"));
	_ui->listView->setFocus();
}

MainWindow::~MainWindow()
{
	_proxyModel->setSourceModel(NULL);
	delete _objectModel;
	delete _ui;
}

void MainWindow::saveGeometry(const QString &name, const QWidget &widget)
{
	QSettings settings;
	settings.setValue("geometry/" + name, widget.saveGeometry());
}

void MainWindow::restoreGeometry(const QString &name, QWidget &widget)
{
	QSettings settings;
	QVariant geometry = settings.value("geometry/" + name);
	if (geometry.isValid())
		widget.restoreGeometry(geometry.toByteArray());
}

void MainWindow::closeEvent(QCloseEvent *event)
{
	QSettings settings;
	saveGeometry("main-window", *this);
	settings.setValue("state/main-window", saveState());
	QMainWindow::closeEvent(event);
}

bool MainWindow::reconnectToDevice()
{
	_session.reset();
	_device.reset();

	mtp::usb::ContextPtr ctx(new mtp::usb::Context);

	auto devices = ctx->GetDevices();
	for (auto desc = devices.begin(); desc != devices.end();)
	{
		qDebug("probing device...");
		try
		{
			_device = mtp::Device::Open(ctx, *desc);
			++desc;
		}
		catch(const mtp::usb::DeviceBusyException &ex)
		{
			auto r = QMessageBox::warning(this,
				tr("Device is busy"),
				tr("Device is busy, maybe another process is using it. Close other MTP applications and restart Android File Transfer.\nPress Abort to kill them or Ignore to try next device."),
				QMessageBox::Abort | QMessageBox::Ignore
				);

			if ((r & QMessageBox::Abort) == QMessageBox::Abort)
			{
				qDebug("kill'em all");
				ex.Kill();
				qDebug("retrying..."); //do not increment desc, retry device
			}
			if ((r & QMessageBox::Ignore) == QMessageBox::Ignore)
				++desc;
		}
		catch(const std::exception &ex)
		{
			++desc;
			qWarning("Device::Find failed: %s", ex.what());
		}

		if (_device)
			break;
	}

	if (!_device)
	{
		QMessageBox::critical(this, tr("No MTP device found"), tr("No MTP device found"));
		return false;
	}

	qDebug() << "device found, opening session...";
	static const int MaxAttempts = 3;
	for(int attempt = 0; attempt < MaxAttempts; ++attempt)
	{
		try
		{
			_session = _device->OpenSession(1);
			mtp::msg::DeviceInfo di = _session->GetDeviceInfo();
			qDebug() << "device info" << fromUtf8(di.Manufacturer) << " " << fromUtf8(di.Model);
			break;
		}
		catch(const mtp::usb::TimeoutException &ex)
		{
			qDebug() << "timed out getting device info: " << fromUtf8(ex.what()) << ", retrying...";
{'fraction_non_alphanumeric': 0.15057822036620624, 'fraction_numerical': 0.00513973658849984, 'mean_word_length': 6.4125, 'pattern_counts': {'":': 2, '<': 87, '<?xml version=': 0, '>': 121, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1308629', 'n_tokens_mistral': 4106, 'n_tokens_neox': 3876, 'n_words': 883}