<commit_before>#ifndef VIENNADATA_FORWARDS_HPP
#define VIENNADATA_FORWARDS_HPP

/* =======================================================================
   Copyright (c) 2011-2013, Institute for Microelectronics,
                            Institute for Analysis and Scientific Computing,
                            TU Wien.

                            -----------------
                     ViennaData - The Vienna Data Library
                            -----------------

   Authors:      Florian Rudolf                    rudolf@iue.tuwien.ac.at
                 Karl Rupp                           rupp@iue.tuwien.ac.at

   (A list of additional contributors can be found in the PDF manual)

   License:      MIT (X11), see file LICENSE in the base directory
======================================================================= */

#include <string>
#include <typeinfo>

#include "viennadata/meta/typemap.hpp"
#include "viennadata/meta/utils.hpp"

namespace viennadata
{
  /** @brief viennadata::all for erase, move and copy */
  struct all;

  /** @brief A tag which indicates that object id is used to access the data */
  struct id_access_tag {};
  /** @brief A tag which indicates that object pointer is used to access the data */
  struct pointer_access_tag {};

  /** @brief Associative container key type for id, constructible from ElementType */
  template<typename ElementType>
  struct id_key
  {
      typedef typename ElementType::id_type id_type;

      id_key(ElementType const & element) : id(element.id()) {}

      id_type id;
  };

  /** @brief compare operator for id_key (less comparable) */
  template<typename ElementType>
  bool operator<(id_key<ElementType> const & lhs, id_key<ElementType> const & rhs)
  {
      return lhs.id < rhs.id;
  }

  /** @brief Associative container key type for pointer, constructable from element_type */
  template<typename ElementType>
  struct pointer_key
  {
      pointer_key(ElementType const & element) : ptr(&element) {}

      ElementType const * ptr;
  };

  /** @brief Compare operator for pointer_key (less comparable) */
  template<typename ElementType>
  bool operator<(pointer_key<ElementType> const & lhs, pointer_key<ElementType> const & rhs)
  {
      return lhs.ptr < rhs.ptr;
  }

  /** @brief Default tag for configuration */
  struct default_tag {};

  /** @brief Indicates the use of std::vector for data container (dense storage) */
  struct std_vector_tag {};

  /** @brief Indicates the use of std::deque for data container (dense storage) */
  struct std_deque_tag {};

  /** Indicates the use of std::map for data container (sparse storage) */
  struct std_map_tag {};

  /** @brief runtime information classes are used to map the compile-time type to a runt-time key_type */
  struct typeid_string_runtime_information
  {
    typedef std::string runtime_key_type;

    template<typename type>
    runtime_key_type key() const
    {
      return typeid(type).name();
    }
  };

  /** @brief Default container config; using std::map and pointer access */
  typedef viennameta::make_typemap<
      default_tag,
      viennameta::static_pair<std_map_tag, pointer_access_tag>
  >::type default_container_config;

  /** @brief Base class for dynamic type wrapper */
  struct base_dynamic_type_wrapper
  {
      virtual ~base_dynamic_type_wrapper() {}
  };

  /** @brief Dynamic type wrapper, used for dynamic type searching (e.g. which types are stored on an element), knows its element type */
  template<typename ElementType>
  struct dynamic_type_wrapper : public base_dynamic_type_wrapper
  {
      dynamic_type_wrapper(ElementType const & val) : value(val) {}
      ElementType const & value;
  };

  template<typename ContainerConfig = default_container_config, typename RuntimeTypeInformation = typeid_string_runtime_information>
  class storage;

  template<typename StorageType, typename KeyType>
  class storage_container_accessor_proxy
  {
  public:
    storage_container_accessor_proxy(StorageType & s, KeyType const & k) : storage_(s), key_(k) {}

    StorageType & storage() { return storage_; }
    KeyType const & key() const { return key_; }

  private:
    StorageType & storage_;
    KeyType const & key_;
  };

  template<typename ContainerType, typename ElementType, typename AccessTag>
  class container_accessor;
}

#endif // FORWARDS_HPP
<commit_msg>Fixed default configuration: viennadata::storage<> works again.<commit_after>#ifndef VIENNADATA_FORWARDS_HPP
#define VIENNADATA_FORWARDS_HPP

/* =======================================================================
   Copyright (c) 2011-2013, Institute for Microelectronics,
                            Institute for Analysis and Scientific Computing,
                            TU Wien.

                            -----------------
                     ViennaData - The Vienna Data Library
                            -----------------

   Authors:      Florian Rudolf                    rudolf@iue.tuwien.ac.at
                 Karl Rupp                           rupp@iue.tuwien.ac.at

   (A list of additional contributors can be found in the PDF manual)

   License:      MIT (X11), see file LICENSE in the base directory
======================================================================= */

#include <string>
#include <typeinfo>

#include "viennadata/meta/typemap.hpp"
#include "viennadata/meta/utils.hpp"

namespace viennadata
{
  /** @brief viennadata::all for erase, move and copy */
  struct all;

  /** @brief A tag which indicates that object id is used to access the data */
  struct id_access_tag {};
  /** @brief A tag which indicates that object pointer is used to access the data */
  struct pointer_access_tag {};

  /** @brief Associative container key type for id, constructible from ElementType */
  template<typename ElementType>
  struct id_key
  {
      typedef typename ElementType::id_type id_type;

      id_key(ElementType const & element) : id(element.id()) {}

      id_type id;
  };

  /** @brief compare operator for id_key (less comparable) */
  template<typename ElementType>
  bool operator<(id_key<ElementType> const & lhs, id_key<ElementType> const & rhs)
  {
      return lhs.id < rhs.id;
  }

  /** @brief Associative container key type for pointer, constructable from element_type */
  template<typename ElementType>
  struct pointer_key
  {
      pointer_key(ElementType const & element) : ptr(&element) {}

      ElementType const * ptr;
  };

  /** @brief Compare operator for pointer_key (less comparable) */
  template<typename ElementType>
  bool operator<(pointer_key<ElementType> const & lhs, pointer_key<ElementType> const & rhs)
  {
      return lhs.ptr < rhs.ptr;
  }

  /** @brief Default tag for configuration */
  struct default_tag {};

  /** @brief Indicates the use of std::vector for data container (dense storage) */
  struct std_vector_tag {};

  /** @brief Indicates the use of std::deque for data container (dense storage) */
  struct std_deque_tag {};

  /** Indicates the use of std::map for data container (sparse storage) */
  struct std_map_tag {};

  /** @brief runtime information classes are used to map the compile-time type to a runt-time key_type */
  struct typeid_string_runtime_information
  {
    typedef std::string runtime_key_type;

    template<typename type>
    runtime_key_type key() const
    {
      return typeid(type).name();
    }
  };

  /** @brief Default container config; using std::map and pointer access */
  typedef viennameta::make_typemap<
      default_tag,
      viennameta::static_pair<std_map_tag, pointer_access_tag>
  >::type default_container_config;

  /** @brief Default config wrapping the default configuration typemap default_container_config */
  struct wrapped_default_container_config
  {
    typedef default_container_config   type;
  };

  /** @brief Base class for dynamic type wrapper */
  struct base_dynamic_type_wrapper
  {
      virtual ~base_dynamic_type_wrapper() {}
  };

  /** @brief Dynamic type wrapper, used for dynamic type searching (e.g. which types are stored on an element), knows its element type */
  template<typename ElementType>
  struct dynamic_type_wrapper : public base_dynamic_type_wrapper
  {
      dynamic_type_wrapper(ElementType const & val) : value(val) {}
      ElementType const & value;
  };

  template<typename ContainerConfig = wrapped_default_container_config, typename RuntimeTypeInformation = typeid_string_runtime_information>
  class storage;

  template<typename StorageType, typename KeyType>
  class storage_container_accessor_proxy
  {
  public:
    storage_container_accessor_proxy(StorageType & s, KeyType const & k) : storage_(s), key_(k) {}

    StorageType & storage() { return storage_; }
    KeyType const & key() const { return key_; }

  private:
    StorageType & storage_;
    KeyType const & key_;
  };

  template<typename ContainerType, typename ElementType, typename AccessTag>
  class container_accessor;
}

#endif // FORWARDS_HPP
<|endoftext|>


<commit_before>#include "KVMainWindow.h"
#include <QMenuBar>
#include <QMenu>
#include <QWebFrame>
#include <QInputDialog>
#include <QFile>
#include <QUrl>
#include <QUrlQuery>
#include <QApplication>
#include <QNetworkProxy>
#include <QNetworkReply>
#include <QHostAddress>
#include <QSettings>
#include <QStandardPaths>
#include <QDebug>
#include <QTimer>

#include "KVDefaults.h"
#include "KVSettingsDialog.h"
#include "KVNetworkAccessManager.h"
#include "KVTranslator.h"

KVMainWindow::KVMainWindow(QWidget *parent, Qt::WindowFlags flags):
	QMainWindow(parent, flags)
{
	this->setWindowTitle("KanColleTool Viewer");

	// Set up the window and menus and stuff
	QMenuBar *menuBar = new QMenuBar(this);

	QMenu *viewerMenu = menuBar->addMenu("Viewer");
	viewerMenu->addAction("Change API Link", this, SLOT(askForAPILink()), Qt::CTRL + Qt::Key_L);
	viewerMenu->addAction("Settings", this, SLOT(openSettings()));
	viewerMenu->addAction("Clear Cache", this, SLOT(clearCache()));
	viewerMenu->addSeparator();
	viewerMenu->addAction("Refresh", this, SLOT(loadBundledIndex()), Qt::CTRL + Qt::Key_R);
	viewerMenu->addAction("Quit", qApp, SLOT(quit()), Qt::CTRL + Qt::Key_Q);

	QMenu *helpMenu = menuBar->addMenu("Help");
	helpMenu->addAction("About", this, SLOT(showAbout()));
	// Updates on Linux are handled by the package manager
#if !defined(Q_OS_LINUX)
	helpMenu->addAction("Check for Updates", this, SLOT(checkForUpdates()));
#endif

	this->setMenuBar(menuBar);

	// Set a custom network access manager to let us set up a cache and proxy.
	// Without a cache, the game takes ages to load.
	// Without a proxy, we can't do cool things like translating the game.
	wvManager = new KVNetworkAccessManager(this);

	// Set up a cache; a larger-than-normal disk cache is quite enough for our purposes
	cache = new QNetworkDiskCache(this);
	cache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
	cache->setMaximumCacheSize(1073741824);
	wvManager->setCache(cache);

	// Load settings from the settings file
	this->loadSettings();

	// Set up the web view, using our custom Network Access Manager
	webView = new QWebView(this);
	webView->page()->setNetworkAccessManager(wvManager);

	// The context menu only contains "Reload" anyways
	webView->setContextMenuPolicy(Qt::PreventContextMenu);
	// These are so large that they create a need for themselves >_>
	webView->page()->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
	webView->page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);

	connect(webView, SIGNAL(loadStarted()), this, SLOT(onLoadStarted()));
	connect(webView, SIGNAL(loadFinished(bool)), this, SLOT(onLoadFinished(bool)));

	// To set the window size right, we have to first set the central widget (which we set to
	// the web view)'s size to a fixed size, ask the window to auto-adjust to that, and then
	// fix its size to what it adjusted itself to. A bit clumsy, but it works, and won't stop
	// us from adding any additional elements to the window, as this will account for ANYTHING.
	this->setCentralWidget(webView);
	this->centralWidget()->setFixedSize(800, 480);
	this->adjustSize();
	this->setFixedSize(this->width(), this->height());

	// Check for updates
	// Updates on Linux are handled by the package manager
#if !defined(Q_OS_LINUX)
	this->checkForUpdates();
#endif

	this->loadBundledIndex();
}

void KVMainWindow::checkForUpdates()
{
	QNetworkReply *reply = manager.get(QNetworkRequest(QUrl("http://kancolletool.github.io/VERSION")));
	connect(reply, SIGNAL(finished()), this, SLOT(onVersionCheckFinished()));
}

void KVMainWindow::loadTranslation(QString language)
{
	KVTranslator *translator = KVTranslator::instance();
	if(translator->loaded()) return;

	connect(translator, SIGNAL(loadFailed(QString)), this, SLOT(onTranslationLoadFailed(QString)));
	translator->loadTranslation(language);
}

void KVMainWindow::loadBundledIndex()
{
	QFile file(":/index.html");
	if(file.open(QIODevice::ReadOnly))
	{
		webView->setHtml(file.readAll(), apiLink);
	}
	else
	{
		QMessageBox::critical(this, "Can't load resource", "Couldn't load the local resources needed to start the client.

I have no idea how you even managed to make this happen, since the resources are supposed to be inside the executable, but it probably involved a recompilation that went wrong.

index.html needs to be in the root of resources.qrc.");
		exit(1);
	}
}

void KVMainWindow::generateAPILinkURL()
{
	apiLink = QUrl(QString("http://%1/kcs/mainD2.swf?api_token=%2").arg(server, apiToken));
}

void KVMainWindow::askForAPILink(bool reload)
{
	// Get the link from the user
	QString link = QInputDialog::getText(this, "Enter API Link", "Please enter your API Link.

It should look something like:
http://125.6.XXX.XXX/kcs/mainD2.swf?api_token=xxxxxxxxxx...");

	// If the link is empty, the user pressed cancel, for whatever reason
	if(link.isEmpty())
		return;

	// Make an URL and a Query from it
	QUrl url(link);
	QUrlQuery query(url);

	// Extract the important bits, and generate a well-formed URL from that
	// (It's important that nothing we're doing is noticeable to the staff!)
	server = url.host();
	apiToken = query.queryItemValue("api_token");
	this->generateAPILinkURL();

	// Put it in the settings and force a sync
	QSettings settings;
	settings.setValue("server", server);
	settings.setValue("apiToken", apiToken);
	settings.sync();

	// If we're in-game and expected to reload the page, do so.
	// This is NOT true when called from loadAPILink(), and should
	// be true when called from the menu item.
	if(reload)
		this->loadBundledIndex();
}

void KVMainWindow::openSettings()
{
	KVSettingsDialog *settingsDialog = new KVSettingsDialog(this);
	connect(settingsDialog, SIGNAL(accepted()), SLOT(implementSettings()));
	connect(settingsDialog, SIGNAL(finished(int)), settingsDialog, SLOT(deleteLater()));
	settingsDialog->show();
}

void KVMainWindow::loadSettings()
{
	QSettings settings;

	server = settings.value("server").toString();
	apiToken = settings.value("apiToken").toString();

	if(server.isEmpty() || apiToken.isEmpty())
	{
		this->askForAPILink(false);
		if(server.isEmpty() || apiToken.isEmpty())
			exit(0);
	}
	else this->generateAPILinkURL();

	qDebug() << "Server:" << server;
	qDebug() << "API Token:" << apiToken;
	qDebug() << "API Link:" << apiLink.toString();

	this->implementSettings(true);
}

void KVMainWindow::implementSettings()
{
	QSettings settings;

	bool translation = settings.value("viewerTranslation", kDefaultTranslation).toBool();
	if(translation != wvManager->translation) {
		wvManager->translation = translation;
		if(translation) loadTranslation();
		loadBundledIndex();	// Reset the game
	}

	if(settings.value("proxy", kDefaultProxy).toBool()) {
		wvManager->setProxy(QNetworkProxy(
			static_cast<QNetworkProxy::ProxyType>(settings.value("proxyType", kDefaultProxyType).toInt()),
			settings.value("proxyServer", kDefaultProxyServer).toString(),
			settings.value("proxyPort", kDefaultProxyPort).toInt(),
			settings.value("proxyUser", kDefaultProxyUser).toString(),
			settings.value("proxyPass", kDefaultProxyPass).toString()));
	} else {
		wvManager->setProxy(QNetworkProxy());
	}
}

void KVMainWindow::clearCache()
{
	cache->clear();
	this->loadBundledIndex();
}

void KVMainWindow::showAbout()
{
	QMessageBox::about(this, "About KCTViewer",
		QString(
			"
KCTViewer %1
"
		).arg(
			QCoreApplication::applicationVersion()
		)
	);
}

void KVMainWindow::onVersionCheckFinished()
{
	QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());

	// If the check failed, do nothing
	if(reply->error() != QNetworkReply::NoError)
		return;

	// Parse the version numbers
	QString newVersion = QString::fromUtf8(reply->readAll()).trimmed();
	QList<int> newVersionComponents;
	foreach(QString part, newVersion.split("."))
		newVersionComponents.append(part.toInt());

	QString appVersion = qApp->applicationVersion();
	QList<int> appVersionComponents;
	foreach(QString part, appVersion.split("."))
		appVersionComponents.append(part.toInt());

	// Compare component-per-component to see if we're outdated
	bool outdated = false;
	for(int i = 0; i < qMin(newVersionComponents.length(), appVersionComponents.length()); i++)
	{
		if(newVersionComponents[i] != appVersionComponents[i])
		{
			outdated = (newVersionComponents[i] > appVersionComponents[i]);
			break;
		}
	}

	// Display a message if we are
	if(outdated)
		QMessageBox::information(this, "New Version Available", "Version " + newVersion + " has been released, and is available at:
http://kancolletool.github.io/downloads/");
}

void KVMainWindow::onLoadStarted()
{
	qDebug() << "Loading Started...";
}

void KVMainWindow::onLoadFinished(bool ok)
{
	qDebug() << "Finished Loading!" << ok;
	if(ok) this->setHTMLAPILink();
}

void KVMainWindow::onTranslationLoadFailed(QString error)
{
	qDebug() << "Translation failed to load:" << error;

	QMessageBox::StandardButton button;
	if(KVTranslator::instance()->loaded()) {
		button = QMessageBox::warning(this, "Couldn't load network translation", "This might mean that your connection is bad. However, a cached translation has been loaded. Would you like to retry loading the translation from the network?", QMessageBox::Retry|QMessageBox::Ok, QMessageBox::Ok);
	} else {
		button = QMessageBox::warning(this, "Couldn't load translation", "This might mean that your connection is bad. You can continue without translation, but the game will be in Japanese.", QMessageBox::Retry|QMessageBox::Ok, QMessageBox::Ok);

		// Disable translation if they want it
		if(button != QMessageBox::Retry)
			wvManager->translation = false;
	}

	// To retry, just send the request again.
	if(button == QMessageBox::Retry)
		this->loadTranslation();
}

void KVMainWindow::setHTMLAPILink()
{
	qDebug() << "Updating web view credentials to" << server << "-" << apiToken;
	webView->page()->mainFrame()->evaluateJavaScript(QString("setCredentials(\"%1\", \"%2\"); null").arg(server, apiToken));
}

/*void KVMainWindow::onAPIError(KVProxyServer::APIStatus error)
{
	qDebug() << error;

	QString readableError;

	switch(error)
	{
		case KVProxyServer::OK:
			readableError = "No Error, you shouldn't be seeing this.";
			break;
		case KVProxyServer::Unauthorized:
			readableError = "Either your API Link is invalid, or it has expired. That happens sometimes.";
			break;
		case KVProxyServer::InvalidAPIVersion:
		case KVProxyServer::MissingParameters:
			readableError = "The game was updated, but you have an old client.";
			break;
		default:
			readableError = "An unknown error occurred. Please tell the developers that this happened ALONG WITH THE ERROR CODE, and what you think caused it.";
	}

	QMessageBox::critical(this, QString("Errorcat (Code %1)").arg((int)error), readableError);
}*/
<commit_msg>Oops, forgot to remove this<commit_after>#include "KVMainWindow.h"
#include <QMenuBar>
#include <QMenu>
#include <QWebFrame>
#include <QInputDialog>
#include <QFile>
#include <QUrl>
#include <QUrlQuery>
#include <QApplication>
#include <QNetworkProxy>
#include <QNetworkReply>
#include <QHostAddress>
#include <QSettings>
#include <QStandardPaths>
#include <QDebug>
#include <QTimer>

#include "KVDefaults.h"
#include "KVSettingsDialog.h"
#include "KVNetworkAccessManager.h"
#include "KVTranslator.h"

KVMainWindow::KVMainWindow(QWidget *parent, Qt::WindowFlags flags):
	QMainWindow(parent, flags)
{
	this->setWindowTitle("KanColleTool Viewer");

	// Set up the window and menus and stuff
	QMenuBar *menuBar = new QMenuBar(this);

	QMenu *viewerMenu = menuBar->addMenu("Viewer");
	viewerMenu->addAction("Change API Link", this, SLOT(askForAPILink()), Qt::CTRL + Qt::Key_L);
	viewerMenu->addAction("Settings", this, SLOT(openSettings()));
	viewerMenu->addAction("Clear Cache", this, SLOT(clearCache()));
	viewerMenu->addSeparator();
	viewerMenu->addAction("Refresh", this, SLOT(loadBundledIndex()), Qt::CTRL + Qt::Key_R);
	viewerMenu->addAction("Quit", qApp, SLOT(quit()), Qt::CTRL + Qt::Key_Q);

	QMenu *helpMenu = menuBar->addMenu("Help");
	helpMenu->addAction("About", this, SLOT(showAbout()));
	// Updates on Linux are handled by the package manager
#if !defined(Q_OS_LINUX)
	helpMenu->addAction("Check for Updates", this, SLOT(checkForUpdates()));
#endif

	this->setMenuBar(menuBar);

	// Set a custom network access manager to let us set up a cache and proxy.
	// Without a cache, the game takes ages to load.
	// Without a proxy, we can't do cool things like translating the game.
	wvManager = new KVNetworkAccessManager(this);

	// Set up a cache; a larger-than-normal disk cache is quite enough for our purposes
	cache = new QNetworkDiskCache(this);
	cache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
	cache->setMaximumCacheSize(1073741824);
	wvManager->setCache(cache);

	// Load settings from the settings file
	this->loadSettings();

	// Set up the web view, using our custom Network Access Manager
	webView = new QWebView(this);
	webView->page()->setNetworkAccessManager(wvManager);

	// The context menu only contains "Reload" anyways
	webView->setContextMenuPolicy(Qt::PreventContextMenu);
	// These are so large that they create a need for themselves >_>
	webView->page()->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
	webView->page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);

	connect(webView, SIGNAL(loadStarted()), this, SLOT(onLoadStarted()));
	connect(webView, SIGNAL(loadFinished(bool)), this, SLOT(onLoadFinished(bool)));

	// To set the window size right, we have to first set the central widget (which we set to
	// the web view)'s size to a fixed size, ask the window to auto-adjust to that, and then
	// fix its size to what it adjusted itself to. A bit clumsy, but it works, and won't stop
	// us from adding any additional elements to the window, as this will account for ANYTHING.
	this->setCentralWidget(webView);
	this->centralWidget()->setFixedSize(800, 480);
	this->adjustSize();
	this->setFixedSize(this->width(), this->height());

	// Check for updates
	// Updates on Linux are handled by the package manager
#if !defined(Q_OS_LINUX)
	this->checkForUpdates();
#endif

	this->loadBundledIndex();
}

void KVMainWindow::checkForUpdates()
{
	QNetworkReply *reply = manager.get(QNetworkRequest(QUrl("http://kancolletool.github.io/VERSION")));
	connect(reply, SIGNAL(finished()), this, SLOT(onVersionCheckFinished()));
}

void KVMainWindow::loadTranslation(QString language)
{
	KVTranslator *translator = KVTranslator::instance();
	if(translator->loaded()) return;

	connect(translator, SIGNAL(loadFailed(QString)), this, SLOT(onTranslationLoadFailed(QString)));
	translator->loadTranslation(language);
}

void KVMainWindow::loadBundledIndex()
{
	QFile file(":/index.html");
	if(file.open(QIODevice::ReadOnly))
	{
		webView->setHtml(file.readAll(), apiLink);
	}
	else
	{
		QMessageBox::critical(this, "Can't load resource", "Couldn't load the local resources needed to start the client.

I have no idea how you even managed to make this happen, since the resources are supposed to be inside the executable, but it probably involved a recompilation that went wrong.

index.html needs to be in the root of resources.qrc.");
		exit(1);
	}
}

void KVMainWindow::generateAPILinkURL()
{
	apiLink = QUrl(QString("http://%1/kcs/mainD2.swf?api_token=%2").arg(server, apiToken));
}

void KVMainWindow::askForAPILink(bool reload)
{
	// Get the link from the user
	QString link = QInputDialog::getText(this, "Enter API Link", "Please enter your API Link.

It should look something like:
http://125.6.XXX.XXX/kcs/mainD2.swf?api_token=xxxxxxxxxx...");

	// If the link is empty, the user pressed cancel, for whatever reason
	if(link.isEmpty())
		return;

	// Make an URL and a Query from it
	QUrl url(link);
	QUrlQuery query(url);

	// Extract the important bits, and generate a well-formed URL from that
	// (It's important that nothing we're doing is noticeable to the staff!)
	server = url.host();
	apiToken = query.queryItemValue("api_token");
	this->generateAPILinkURL();

	// Put it in the settings and force a sync
	QSettings settings;
	settings.setValue("server", server);
	settings.setValue("apiToken", apiToken);
	settings.sync();

	// If we're in-game and expected to reload the page, do so.
	// This is NOT true when called from loadAPILink(), and should
	// be true when called from the menu item.
	if(reload)
		this->loadBundledIndex();
}

void KVMainWindow::openSettings()
{
	KVSettingsDialog *settingsDialog = new KVSettingsDialog(this);
	connect(settingsDialog, SIGNAL(accepted()), SLOT(implementSettings()));
	connect(settingsDialog, SIGNAL(finished(int)), settingsDialog, SLOT(deleteLater()));
	settingsDialog->show();
}

void KVMainWindow::loadSettings()
{
	QSettings settings;

	server = settings.value("server").toString();
	apiToken = settings.value("apiToken").toString();

	if(server.isEmpty() || apiToken.isEmpty())
	{
		this->askForAPILink(false);
		if(server.isEmpty() || apiToken.isEmpty())
			exit(0);
	}
	else this->generateAPILinkURL();

	qDebug() << "Server:" << server;
	qDebug() << "API Token:" << apiToken;
	qDebug() << "API Link:" << apiLink.toString();

	this->implementSettings();
}

void KVMainWindow::implementSettings()
{
	QSettings settings;

	bool translation = settings.value("viewerTranslation", kDefaultTranslation).toBool();
	if(translation != wvManager->translation) {
		wvManager->translation = translation;
		if(translation) loadTranslation();
		loadBundledIndex();	// Reset the game
	}

	if(settings.value("proxy", kDefaultProxy).toBool()) {
		wvManager->setProxy(QNetworkProxy(
			static_cast<QNetworkProxy::ProxyType>(settings.value("proxyType", kDefaultProxyType).toInt()),
			settings.value("proxyServer", kDefaultProxyServer).toString(),
			settings.value("proxyPort", kDefaultProxyPort).toInt(),
			settings.value("proxyUser", kDefaultProxyUser).toString(),
			settings.value("proxyPass", kDefaultProxyPass).toString()));
	} else {
		wvManager->setProxy(QNetworkProxy());
	}
}

void KVMainWindow::clearCache()
{
	cache->clear();
	this->loadBundledIndex();
}

void KVMainWindow::showAbout()
{
	QMessageBox::about(this, "About KCTViewer",
		QString(
			"
KCTViewer %1
"
		).arg(
			QCoreApplication::applicationVersion()
		)
	);
}

void KVMainWindow::onVersionCheckFinished()
{
	QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());

	// If the check failed, do nothing
	if(reply->error() != QNetworkReply::NoError)
		return;

	// Parse the version numbers
	QString newVersion = QString::fromUtf8(reply->readAll()).trimmed();
	QList<int> newVersionComponents;
	foreach(QString part, newVersion.split("."))
		newVersionComponents.append(part.toInt());

	QString appVersion = qApp->applicationVersion();
	QList<int> appVersionComponents;
	foreach(QString part, appVersion.split("."))
		appVersionComponents.append(part.toInt());

	// Compare component-per-component to see if we're outdated
	bool outdated = false;
	for(int i = 0; i < qMin(newVersionComponents.length(), appVersionComponents.length()); i++)
	{
		if(newVersionComponents[i] != appVersionComponents[i])
		{
			outdated = (newVersionComponents[i] > appVersionComponents[i]);
			break;
		}
	}

	// Display a message if we are
	if(outdated)
		QMessageBox::information(this, "New Version Available", "Version " + newVersion + " has been released, and is available at:
http://kancolletool.github.io/downloads/");
}

void KVMainWindow::onLoadStarted()
{
	qDebug() << "Loading Started...";
}

void KVMainWindow::onLoadFinished(bool ok)
{
	qDebug() << "Finished Loading!" << ok;
	if(ok) this->setHTMLAPILink();
}

void KVMainWindow::onTranslationLoadFailed(QString error)
{
	qDebug() << "Translation failed to load:" << error;

	QMessageBox::StandardButton button;
	if(KVTranslator::instance()->loaded()) {
		button = QMessageBox::warning(this, "Couldn't load network translation", "This might mean that your connection is bad. However, a cached translation has been loaded. Would you like to retry loading the translation from the network?", QMessageBox::Retry|QMessageBox::Ok, QMessageBox::Ok);
	} else {
		button = QMessageBox::warning(this, "Couldn't load translation", "This might mean that your connection is bad. You can continue without translation, but the game will be in Japanese.", QMessageBox::Retry|QMessageBox::Ok, QMessageBox::Ok);

		// Disable translation if they want it
		if(button != QMessageBox::Retry)
			wvManager->translation = false;
	}

	// To retry, just send the request again.
	if(button == QMessageBox::Retry)
		this->loadTranslation();
}

void KVMainWindow::setHTMLAPILink()
{
	qDebug() << "Updating web view credentials to" << server << "-" << apiToken;
	webView->page()->mainFrame()->evaluateJavaScript(QString("setCredentials(\"%1\", \"%2\"); null").arg(server, apiToken));
}

/*void KVMainWindow::onAPIError(KVProxyServer::APIStatus error)
{
	qDebug() << error;

	QString readableError;

	switch(error)
	{
		case KVProxyServer::OK:
			readableError = "No Error, you shouldn't be seeing this.";
			break;
		case KVProxyServer::Unauthorized:
			readableError = "Either your API Link is invalid, or it has expired. That happens sometimes.";
			break;
		case KVProxyServer::InvalidAPIVersion:
		case KVProxyServer::MissingParameters:
			readableError = "The game was updated, but you have an old client.";
			break;
		default:
			readableError = "An unknown error occurred. Please tell the developers that this happened ALONG WITH THE ERROR CODE, and what you think caused it.";
	}

	QMessageBox::critical(this, QString("Errorcat (Code %1)").arg((int)error), readableError);
}*/
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "views/controls/label.h"

#include <math.h>

#include "app/gfx/chrome_canvas.h"
#include "app/gfx/chrome_font.h"
#include "app/gfx/insets.h"
#include "app/gfx/text_elider.h"
#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "views/background.h"

namespace views {

const char Label::kViewClassName[] = "views/Label";

static const SkColor kEnabledColor = SK_ColorBLACK;
static const SkColor kDisabledColor = SkColorSetRGB(161, 161, 146);
static const int kFocusBorderPadding = 1;

Label::Label() {
  Init(L"", GetDefaultFont());
}

Label::Label(const std::wstring& text) {
  Init(text, GetDefaultFont());
}

Label::Label(const std::wstring& text, const ChromeFont& font) {
  Init(text, font);
}

void Label::Init(const std::wstring& text, const ChromeFont& font) {
  contains_mouse_ = false;
  font_ = font;
  text_size_valid_ = false;
  SetText(text);
  url_set_ = false;
  color_ = kEnabledColor;
  horiz_alignment_ = ALIGN_CENTER;
  is_multi_line_ = false;
  allow_character_break_ = false;
  collapse_when_hidden_ = false;
  rtl_alignment_mode_ = USE_UI_ALIGNMENT;
  paint_as_focused_ = false;
  has_focus_border_ = false;
}

Label::~Label() {
}

gfx::Size Label::GetPreferredSize() {
  gfx::Size prefsize;

  // Return a size of (0, 0) if the label is not visible and if the
  // collapse_when_hidden_ flag is set.
  // TODO(munjal): This logic probably belongs to the View class. But for now,
  // put it here since putting it in View class means all inheriting classes
  // need ot respect the collapse_when_hidden_ flag.
  if (!IsVisible() && collapse_when_hidden_)
    return prefsize;

  if (is_multi_line_) {
    int w = width(), h = 0;
    ChromeCanvas::SizeStringInt(text_, font_, &w, &h, ComputeMultiLineFlags());
    prefsize.SetSize(w, h);
  } else {
    prefsize = GetTextSize();
  }

  gfx::Insets insets = GetInsets();
  prefsize.Enlarge(insets.width(), insets.height());
  return prefsize;
}

int Label::ComputeMultiLineFlags() {
  int flags = ChromeCanvas::MULTI_LINE;
  if (allow_character_break_)
    flags |= ChromeCanvas::CHARACTER_BREAK;
  switch (horiz_alignment_) {
    case ALIGN_LEFT:
      flags |= ChromeCanvas::TEXT_ALIGN_LEFT;
      break;
    case ALIGN_CENTER:
      flags |= ChromeCanvas::TEXT_ALIGN_CENTER;
      break;
    case ALIGN_RIGHT:
      flags |= ChromeCanvas::TEXT_ALIGN_RIGHT;
      break;
  }
  return flags;
}

void Label::CalculateDrawStringParams(
    std::wstring* paint_text, gfx::Rect* text_bounds, int* flags) {
  DCHECK(paint_text && text_bounds && flags);

  if (url_set_) {
    // TODO(jungshik) : Figure out how to get 'intl.accept_languages'
    // preference and use it when calling ElideUrl.
    *paint_text = gfx::ElideUrl(url_, font_, width(), std::wstring());

    // An URLs is always treated as an LTR text and therefore we should
    // explicitly mark it as such if the locale is RTL so that URLs containing
    // Hebrew or Arabic characters are displayed correctly.
    //
    // Note that we don't check the View's UI layout setting in order to
    // determine whether or not to insert the special Unicode formatting
    // characters. We use the locale settings because an URL is always treated
    // as an LTR string, even if its containing view does not use an RTL UI
    // layout.
    if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT)
      l10n_util::WrapStringWithLTRFormatting(paint_text);
  } else {
    *paint_text = text_;
  }

  if (is_multi_line_) {
    gfx::Insets insets = GetInsets();
    text_bounds->SetRect(insets.left(),
                         insets.top(),
                         width() - insets.width(),
                         height() - insets.height());
    *flags = ComputeMultiLineFlags();
  } else {
    *text_bounds = GetTextBounds();
    *flags = 0;
  }
}

void Label::Paint(ChromeCanvas* canvas) {
  PaintBackground(canvas);
  std::wstring paint_text;
  gfx::Rect text_bounds;
  int flags = 0;
  CalculateDrawStringParams(&paint_text, &text_bounds, &flags);
  canvas->DrawStringInt(paint_text,
                        font_,
                        color_,
                        text_bounds.x(),
                        text_bounds.y(),
                        text_bounds.width(),
                        text_bounds.height(),
                        flags);

  // The focus border always hugs the text, regardless of the label's bounds.
  if (HasFocus() || paint_as_focused_) {
    int w = text_bounds.width();
    int h = 0;
    // We explicitly OR in MULTI_LINE here since SizeStringInt seems to return
    // an incorrect height for single line text when the MULTI_LINE flag isn't
    // specified. o_O...
    ChromeCanvas::SizeStringInt(paint_text, font_, &w, &h,
                                flags | ChromeCanvas::MULTI_LINE);
    gfx::Rect focus_rect = text_bounds;
    focus_rect.set_width(w);
    focus_rect.set_height(h);
    focus_rect.Inset(-kFocusBorderPadding, -kFocusBorderPadding);
    canvas->DrawFocusRect(MirroredLeftPointForRect(focus_rect), focus_rect.y(),
                          focus_rect.width(), focus_rect.height());
  }
}

void Label::PaintBackground(ChromeCanvas* canvas) {
  const Background* bg = contains_mouse_ ? GetMouseOverBackground() : NULL;
  if (!bg)
    bg = background();
  if (bg)
    bg->Paint(canvas, this);
}

void Label::SetFont(const ChromeFont& font) {
  font_ = font;
  text_size_valid_ = false;
  SchedulePaint();
}

ChromeFont Label::GetFont() const {
  return font_;
}

void Label::SetText(const std::wstring& text) {
  text_ = text;
  url_set_ = false;
  text_size_valid_ = false;
  SchedulePaint();
}

void Label::SetURL(const GURL& url) {
  url_ = url;
  text_ = UTF8ToWide(url_.spec());
  url_set_ = true;
  text_size_valid_ = false;
  SchedulePaint();
}

const std::wstring Label::GetText() const {
  if (url_set_)
    return UTF8ToWide(url_.spec());
  else
    return text_;
}

const GURL Label::GetURL() const {
  if (url_set_)
    return url_;
  else
    return GURL(WideToUTF8(text_));
}

gfx::Size Label::GetTextSize() {
  if (!text_size_valid_) {
    text_size_.SetSize(font_.GetStringWidth(text_), font_.height());
    text_size_valid_ = true;
  }

  if (text_size_valid_)
    return text_size_;
  return gfx::Size();
}

int Label::GetHeightForWidth(int w) {
  if (is_multi_line_) {
    gfx::Insets insets = GetInsets();
    w = std::max<int>(0, w - insets.width());
    int h = 0;
    ChromeCanvas cc(0, 0, true);
    cc.SizeStringInt(text_, font_, &w, &h, ComputeMultiLineFlags());
    return h + insets.height();
  }

  return View::GetHeightForWidth(w);
}

std::string Label::GetClassName() const {
  return kViewClassName;
}

void Label::SetColor(const SkColor& color) {
  color_ = color;
}

const SkColor Label::GetColor() const {
  return color_;
}

void Label::SetHorizontalAlignment(Alignment a) {
  // If the View's UI layout is right-to-left and rtl_alignment_mode_ is
  // USE_UI_ALIGNMENT, we need to flip the alignment so that the alignment
  // settings take into account the text directionality.
  if (UILayoutIsRightToLeft() && rtl_alignment_mode_ == USE_UI_ALIGNMENT) {
    if (a == ALIGN_LEFT)
      a = ALIGN_RIGHT;
    else if (a == ALIGN_RIGHT)
      a = ALIGN_LEFT;
  }
  if (horiz_alignment_ != a) {
    horiz_alignment_ = a;
    SchedulePaint();
  }
}

Label::Alignment Label::GetHorizontalAlignment() const {
  return horiz_alignment_;
}

void Label::SetRTLAlignmentMode(RTLAlignmentMode mode) {
  rtl_alignment_mode_ = mode;
}

Label::RTLAlignmentMode Label::GetRTLAlignmentMode() const {
  return rtl_alignment_mode_;
}

void Label::SetMultiLine(bool f) {
  if (f != is_multi_line_) {
    is_multi_line_ = f;
    SchedulePaint();
  }
}

void Label::SetAllowCharacterBreak(bool f) {
  if (f != allow_character_break_) {
    allow_character_break_ = f;
    SchedulePaint();
  }
}

bool Label::IsMultiLine() {
  return is_multi_line_;
}

void Label::SetTooltipText(const std::wstring& tooltip_text) {
  tooltip_text_ = tooltip_text;
}

bool Label::GetTooltipText(int x, int y, std::wstring* tooltip) {
  DCHECK(tooltip);

  // If a tooltip has been explicitly set, use it.
  if (!tooltip_text_.empty()) {
    tooltip->assign(tooltip_text_);
    return true;
  }

  // Show the full text if the text does not fit.
  if (!is_multi_line_ && font_.GetStringWidth(text_) > width()) {
    *tooltip = text_;
    return true;
  }
  return false;
}

void Label::OnMouseMoved(const MouseEvent& e) {
  UpdateContainsMouse(e);
}

void Label::OnMouseEntered(const MouseEvent& event) {
  UpdateContainsMouse(event);
}

void Label::OnMouseExited(const MouseEvent& event) {
  SetContainsMouse(false);
}

void Label::SetMouseOverBackground(Background* background) {
  mouse_over_background_.reset(background);
}

const Background* Label::GetMouseOverBackground() const {
  return mouse_over_background_.get();
}

void Label::SetEnabled(bool enabled) {
  if (enabled == enabled_)
    return;
  View::SetEnabled(enabled);
  SetColor(enabled ? kEnabledColor : kDisabledColor);
}

gfx::Insets Label::GetInsets() const {
  gfx::Insets insets = View::GetInsets();
  if (IsFocusable() || has_focus_border_)  {
    insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding,
                          kFocusBorderPadding, kFocusBorderPadding);
  }
  return insets;
}

// static
ChromeFont Label::GetDefaultFont() {
  return ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont);
}

void Label::UpdateContainsMouse(const MouseEvent& event) {
  SetContainsMouse(GetTextBounds().Contains(event.x(), event.y()));
}

void Label::SetContainsMouse(bool contains_mouse) {
  if (contains_mouse_ == contains_mouse)
    return;
  contains_mouse_ = contains_mouse;
  if (GetMouseOverBackground())
    SchedulePaint();
}

gfx::Rect Label::GetTextBounds() {
  gfx::Size text_size = GetTextSize();
  gfx::Insets insets = GetInsets();
  int avail_width = width() - insets.width();
  // Respect the size set by the owner view
  text_size.set_width(std::min(avail_width, text_size.width()));

  int text_y = insets.top() +
      (height() - text_size.height() - insets.height()) / 2;
  int text_x;
  switch (horiz_alignment_) {
    case ALIGN_LEFT:
      text_x = insets.left();
      break;
    case ALIGN_CENTER:
      // We put any extra margin pixel on the left rather than the right, since
      // GetTextExtentPoint32() can report a value one too large on the right.
      text_x = insets.left() + (avail_width + 1 - text_size.width()) / 2;
      break;
    case ALIGN_RIGHT:
      text_x = width() - insets.right() - text_size.width();
      break;
    default:
      NOTREACHED();
      text_x = 0;
      break;
  }
  return gfx::Rect(text_x, text_y, text_size.width(), text_size.height());
}

void Label::SizeToFit(int max_width) {
  DCHECK(is_multi_line_);

  std::vector<std::wstring> lines;
  SplitString(text_, L'\n', &lines);

  int label_width = 0;
  for (std::vector<std::wstring>::const_iterator iter = lines.begin();
       iter != lines.end(); ++iter) {
    label_width = std::max(label_width, font_.GetStringWidth(*iter));
  }

  gfx::Insets insets = GetInsets();
  label_width += insets.width();

  if (max_width > 0)
    label_width = std::min(label_width, max_width);

  SetBounds(x(), y(), label_width, 0);
  SizeToPreferredSize();
}

bool Label::GetAccessibleRole(AccessibilityTypes::Role* role) {
  DCHECK(role);

  *role = AccessibilityTypes::ROLE_TEXT;
  return true;
}

bool Label::GetAccessibleName(std::wstring* name) {
  *name = GetText();
  return true;
}

bool Label::GetAccessibleState(AccessibilityTypes::State* state) {
  DCHECK(state);

  *state = AccessibilityTypes::STATE_READONLY;
  return true;
}

}  // namespace views
<commit_msg>This CL fix issue 11907 -- RTL: Focus is not on the text after checking "Delete all profile data"<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "views/controls/label.h"

#include <math.h>

#include "app/gfx/chrome_canvas.h"
#include "app/gfx/chrome_font.h"
#include "app/gfx/insets.h"
#include "app/gfx/text_elider.h"
#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "views/background.h"

namespace views {

const char Label::kViewClassName[] = "views/Label";

static const SkColor kEnabledColor = SK_ColorBLACK;
static const SkColor kDisabledColor = SkColorSetRGB(161, 161, 146);
static const int kFocusBorderPadding = 1;

Label::Label() {
  Init(L"", GetDefaultFont());
}

Label::Label(const std::wstring& text) {
  Init(text, GetDefaultFont());
}

Label::Label(const std::wstring& text, const ChromeFont& font) {
  Init(text, font);
}

void Label::Init(const std::wstring& text, const ChromeFont& font) {
  contains_mouse_ = false;
  font_ = font;
  text_size_valid_ = false;
  SetText(text);
  url_set_ = false;
  color_ = kEnabledColor;
  horiz_alignment_ = ALIGN_CENTER;
  is_multi_line_ = false;
  allow_character_break_ = false;
  collapse_when_hidden_ = false;
  rtl_alignment_mode_ = USE_UI_ALIGNMENT;
  paint_as_focused_ = false;
  has_focus_border_ = false;
}

Label::~Label() {
}

gfx::Size Label::GetPreferredSize() {
  gfx::Size prefsize;

  // Return a size of (0, 0) if the label is not visible and if the
  // collapse_when_hidden_ flag is set.
  // TODO(munjal): This logic probably belongs to the View class. But for now,
  // put it here since putting it in View class means all inheriting classes
  // need ot respect the collapse_when_hidden_ flag.
  if (!IsVisible() && collapse_when_hidden_)
    return prefsize;

  if (is_multi_line_) {
    int w = width(), h = 0;
    ChromeCanvas::SizeStringInt(text_, font_, &w, &h, ComputeMultiLineFlags());
    prefsize.SetSize(w, h);
  } else {
    prefsize = GetTextSize();
  }

  gfx::Insets insets = GetInsets();
  prefsize.Enlarge(insets.width(), insets.height());
  return prefsize;
}

int Label::ComputeMultiLineFlags() {
  int flags = ChromeCanvas::MULTI_LINE;
  if (allow_character_break_)
    flags |= ChromeCanvas::CHARACTER_BREAK;
  switch (horiz_alignment_) {
    case ALIGN_LEFT:
      flags |= ChromeCanvas::TEXT_ALIGN_LEFT;
      break;
    case ALIGN_CENTER:
      flags |= ChromeCanvas::TEXT_ALIGN_CENTER;
      break;
    case ALIGN_RIGHT:
      flags |= ChromeCanvas::TEXT_ALIGN_RIGHT;
      break;
  }
  return flags;
}

void Label::CalculateDrawStringParams(
    std::wstring* paint_text, gfx::Rect* text_bounds, int* flags) {
  DCHECK(paint_text && text_bounds && flags);

  if (url_set_) {
    // TODO(jungshik) : Figure out how to get 'intl.accept_languages'
    // preference and use it when calling ElideUrl.
    *paint_text = gfx::ElideUrl(url_, font_, width(), std::wstring());

    // An URLs is always treated as an LTR text and therefore we should
    // explicitly mark it as such if the locale is RTL so that URLs containing
    // Hebrew or Arabic characters are displayed correctly.
    //
    // Note that we don't check the View's UI layout setting in order to
    // determine whether or not to insert the special Unicode formatting
    // characters. We use the locale settings because an URL is always treated
    // as an LTR string, even if its containing view does not use an RTL UI
    // layout.
    if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT)
      l10n_util::WrapStringWithLTRFormatting(paint_text);
  } else {
    *paint_text = text_;
  }

  if (is_multi_line_) {
    gfx::Insets insets = GetInsets();
    text_bounds->SetRect(insets.left(),
                         insets.top(),
                         width() - insets.width(),
                         height() - insets.height());
    *flags = ComputeMultiLineFlags();
  } else {
    *text_bounds = GetTextBounds();
    *flags = 0;
  }
}

void Label::Paint(ChromeCanvas* canvas) {
  PaintBackground(canvas);
  std::wstring paint_text;
  gfx::Rect text_bounds;
  int flags = 0;
  CalculateDrawStringParams(&paint_text, &text_bounds, &flags);
  canvas->DrawStringInt(paint_text,
                        font_,
                        color_,
                        text_bounds.x(),
                        text_bounds.y(),
                        text_bounds.width(),
                        text_bounds.height(),
                        flags);

  // The focus border always hugs the text, regardless of the label's bounds.
  if (HasFocus() || paint_as_focused_) {
    int w = text_bounds.width();
    int h = 0;
    // We explicitly OR in MULTI_LINE here since SizeStringInt seems to return
    // an incorrect height for single line text when the MULTI_LINE flag isn't
    // specified. o_O...
    ChromeCanvas::SizeStringInt(paint_text, font_, &w, &h,
                                flags | ChromeCanvas::MULTI_LINE);
    gfx::Rect focus_rect = text_bounds;
    focus_rect.set_width(w);
    focus_rect.set_height(h);
    focus_rect.Inset(-kFocusBorderPadding, -kFocusBorderPadding);
    // If the label is a single line of text, then the computed text bound
    // corresponds directly to the text being drawn and no mirroring is needed
    // for the RTL case. For multiline text, the text bound is an estimation
    // and is recomputed in ChromeCanvas::SizeStringInt(). For multiline text
    // in RTL, we need to take mirroring into account when computing the focus
    // rectangle.
    int x = focus_rect.x();
    if (flags & ChromeCanvas::MULTI_LINE)
      x = MirroredLeftPointForRect(focus_rect);
    canvas->DrawFocusRect(x, focus_rect.y(), focus_rect.width(),
                          focus_rect.height());
  }
}

void Label::PaintBackground(ChromeCanvas* canvas) {
  const Background* bg = contains_mouse_ ? GetMouseOverBackground() : NULL;
  if (!bg)
    bg = background();
  if (bg)
    bg->Paint(canvas, this);
}

void Label::SetFont(const ChromeFont& font) {
  font_ = font;
  text_size_valid_ = false;
  SchedulePaint();
}

ChromeFont Label::GetFont() const {
  return font_;
}

void Label::SetText(const std::wstring& text) {
  text_ = text;
  url_set_ = false;
  text_size_valid_ = false;
  SchedulePaint();
}

void Label::SetURL(const GURL& url) {
  url_ = url;
  text_ = UTF8ToWide(url_.spec());
  url_set_ = true;
  text_size_valid_ = false;
  SchedulePaint();
}

const std::wstring Label::GetText() const {
  if (url_set_)
    return UTF8ToWide(url_.spec());
  else
    return text_;
}

const GURL Label::GetURL() const {
  if (url_set_)
    return url_;
  else
    return GURL(WideToUTF8(text_));
}

gfx::Size Label::GetTextSize() {
  if (!text_size_valid_) {
    text_size_.SetSize(font_.GetStringWidth(text_), font_.height());
    text_size_valid_ = true;
  }

  if (text_size_valid_)
    return text_size_;
  return gfx::Size();
}

int Label::GetHeightForWidth(int w) {
  if (is_multi_line_) {
    gfx::Insets insets = GetInsets();
    w = std::max<int>(0, w - insets.width());
    int h = 0;
    ChromeCanvas cc(0, 0, true);
    cc.SizeStringInt(text_, font_, &w, &h, ComputeMultiLineFlags());
    return h + insets.height();
  }

  return View::GetHeightForWidth(w);
}

std::string Label::GetClassName() const {
  return kViewClassName;
}

void Label::SetColor(const SkColor& color) {
  color_ = color;
}

const SkColor Label::GetColor() const {
  return color_;
}

void Label::SetHorizontalAlignment(Alignment a) {
  // If the View's UI layout is right-to-left and rtl_alignment_mode_ is
  // USE_UI_ALIGNMENT, we need to flip the alignment so that the alignment
  // settings take into account the text directionality.
  if (UILayoutIsRightToLeft() && rtl_alignment_mode_ == USE_UI_ALIGNMENT) {
    if (a == ALIGN_LEFT)
      a = ALIGN_RIGHT;
    else if (a == ALIGN_RIGHT)
      a = ALIGN_LEFT;
  }
  if (horiz_alignment_ != a) {
    horiz_alignment_ = a;
    SchedulePaint();
  }
}

Label::Alignment Label::GetHorizontalAlignment() const {
  return horiz_alignment_;
}

void Label::SetRTLAlignmentMode(RTLAlignmentMode mode) {
  rtl_alignment_mode_ = mode;
}

Label::RTLAlignmentMode Label::GetRTLAlignmentMode() const {
  return rtl_alignment_mode_;
}

void Label::SetMultiLine(bool f) {
  if (f != is_multi_line_) {
    is_multi_line_ = f;
    SchedulePaint();
  }
}

void Label::SetAllowCharacterBreak(bool f) {
  if (f != allow_character_break_) {
    allow_character_break_ = f;
    SchedulePaint();
  }
}

bool Label::IsMultiLine() {
  return is_multi_line_;
}

void Label::SetTooltipText(const std::wstring& tooltip_text) {
  tooltip_text_ = tooltip_text;
}

bool Label::GetTooltipText(int x, int y, std::wstring* tooltip) {
  DCHECK(tooltip);

  // If a tooltip has been explicitly set, use it.
  if (!tooltip_text_.empty()) {
    tooltip->assign(tooltip_text_);
    return true;
  }

  // Show the full text if the text does not fit.
  if (!is_multi_line_ && font_.GetStringWidth(text_) > width()) {
    *tooltip = text_;
    return true;
  }
  return false;
}

void Label::OnMouseMoved(const MouseEvent& e) {
  UpdateContainsMouse(e);
}

void Label::OnMouseEntered(const MouseEvent& event) {
  UpdateContainsMouse(event);
}

void Label::OnMouseExited(const MouseEvent& event) {
  SetContainsMouse(false);
}

void Label::SetMouseOverBackground(Background* background) {
  mouse_over_background_.reset(background);
}

const Background* Label::GetMouseOverBackground() const {
  return mouse_over_background_.get();
}

void Label::SetEnabled(bool enabled) {
  if (enabled == enabled_)
    return;
  View::SetEnabled(enabled);
  SetColor(enabled ? kEnabledColor : kDisabledColor);
}

gfx::Insets Label::GetInsets() const {
  gfx::Insets insets = View::GetInsets();
  if (IsFocusable() || has_focus_border_)  {
    insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding,
                          kFocusBorderPadding, kFocusBorderPadding);
  }
  return insets;
}

// static
ChromeFont Label::GetDefaultFont() {
  return ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont);
}

void Label::UpdateContainsMouse(const MouseEvent& event) {
  SetContainsMouse(GetTextBounds().Contains(event.x(), event.y()));
}

void Label::SetContainsMouse(bool contains_mouse) {
  if (contains_mouse_ == contains_mouse)
    return;
  contains_mouse_ = contains_mouse;
  if (GetMouseOverBackground())
    SchedulePaint();
}

gfx::Rect Label::GetTextBounds() {
  gfx::Size text_size = GetTextSize();
  gfx::Insets insets = GetInsets();
  int avail_width = width() - insets.width();
  // Respect the size set by the owner view
  text_size.set_width(std::min(avail_width, text_size.width()));

  int text_y = insets.top() +
      (height() - text_size.height() - insets.height()) / 2;
  int text_x;
  switch (horiz_alignment_) {
    case ALIGN_LEFT:
      text_x = insets.left();
      break;
    case ALIGN_CENTER:
      // We put any extra margin pixel on the left rather than the right, since
      // GetTextExtentPoint32() can report a value one too large on the right.
      text_x = insets.left() + (avail_width + 1 - text_size.width()) / 2;
      break;
    case ALIGN_RIGHT:
      text_x = width() - insets.right() - text_size.width();
      break;
    default:
      NOTREACHED();
      text_x = 0;
      break;
  }
  return gfx::Rect(text_x, text_y, text_size.width(), text_size.height());
}

void Label::SizeToFit(int max_width) {
  DCHECK(is_multi_line_);

  std::vector<std::wstring> lines;
  SplitString(text_, L'\n', &lines);

  int label_width = 0;
  for (std::vector<std::wstring>::const_iterator iter = lines.begin();
       iter != lines.end(); ++iter) {
    label_width = std::max(label_width, font_.GetStringWidth(*iter));
  }

  gfx::Insets insets = GetInsets();
  label_width += insets.width();

  if (max_width > 0)
    label_width = std::min(label_width, max_width);

  SetBounds(x(), y(), label_width, 0);
  SizeToPreferredSize();
}

bool Label::GetAccessibleRole(AccessibilityTypes::Role* role) {
  DCHECK(role);

  *role = AccessibilityTypes::ROLE_TEXT;
  return true;
}

bool Label::GetAccessibleName(std::wstring* name) {
  *name = GetText();
  return true;
}

bool Label::GetAccessibleState(AccessibilityTypes::State* state) {
  DCHECK(state);

  *state = AccessibilityTypes::STATE_READONLY;
  return true;
}

}  // namespace views
<|endoftext|>


<commit_before>/*
* Copyright (c) 2003-2008 Rony Shapiro <ronys@users.sourceforge.net>.
* All rights reserved. Use of the code is allowed under the
* Artistic License 2.0 terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

/**
 * \file Windows-specific implementation of file.h
 */
#include <afx.h>
#include <Windows.h>
#include <LMCONS.H> // for UNLEN definition
#include <io.h>
#include "../file.h"
#include "../dir.h"
#include "../env.h"

bool pws_os::FileExists(const stringT &filename)
{
  struct _stat statbuf;
  int status;

  status = ::_tstat(filename.c_str(), &statbuf);
  return (status == 0);
}

bool pws_os::FileExists(const stringT &filename, bool &bReadOnly)
{
  struct _stat statbuf;
  int status;

  status = ::_tstat(filename.c_str(), &statbuf);

  // As "stat" gives "user permissions" not "file attributes"....
  if (status == 0) {
    DWORD dwAttr = GetFileAttributes(filename.c_str());
    bReadOnly = (FILE_ATTRIBUTE_READONLY & dwAttr) == FILE_ATTRIBUTE_READONLY;
    return true;
  } else {
    bReadOnly = false;
    return false;
  }
}

bool pws_os::RenameFile(const stringT &oldname, const stringT &newname)
{
  _tremove(newname.c_str()); // otherwise rename will fail if newname exists
  int status = _trename(oldname.c_str(), newname.c_str());

  return (status == 0);
}

/*
* The file lock/unlock functions were first implemented (in 2.08)
* with Posix semantics (using open(_O_CREATE|_O_EXCL) to detect
* an existing lock.
* This fails to check liveness of the locker process, specifically,
* if a user just turns of her PC, the lock file will remain.
* So, I'm keeping the Posix code under idef POSIX_FILE_LOCK,
* and re-implementing using the Win32 API, whose semantics
* supposedly protect against this scenario.
* Thanks to Frank (xformer) for discussion on the subject.
*/

static stringT GetLockFileName(const stringT &filename)
{
  ASSERT(!filename.empty());
  // derive lock filename from filename
  stringT retval(filename, 0, filename.find_last_of(TCHAR('.')));
  retval += _T(".plk");
  return retval;
}

static void GetLocker(const stringT &lock_filename, stringT &locker)
{
  locker = _T("Unable to determine locker");
  // read locker data ("user@machine:nnnnnnnn") from file
  TCHAR lockerStr[UNLEN + MAX_COMPUTERNAME_LENGTH + 11];
  // flags here counter (my) intuition, but see
  // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/base/creating_and_opening_files.asp
  HANDLE h2 = ::CreateFile(lock_filename.c_str(),
                           GENERIC_READ,
                           FILE_SHARE_WRITE,
                           NULL,
                           OPEN_EXISTING,
                           (FILE_ATTRIBUTE_NORMAL |
                            // (Lockheed Martin) Secure Coding  11-14-2007
                            SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION),
                           NULL);
  // Make sure it's a file and not a pipe.  (Lockheed Martin) Secure Coding  11-14-2007
  if (h2 != INVALID_HANDLE_VALUE) {
    if (::GetFileType( h2 ) != FILE_TYPE_DISK) {
      ::CloseHandle( h2 );
      h2 = INVALID_HANDLE_VALUE;
    }
  }
  // End of Change.  (Lockheed Martin) Secure Coding  11-14-2007

  if (h2 != INVALID_HANDLE_VALUE) {
    DWORD bytesRead;
    (void)::ReadFile(h2, lockerStr, sizeof(lockerStr)-1,
                     &bytesRead, NULL);
    CloseHandle(h2);
    if (bytesRead > 0) {
      lockerStr[bytesRead/sizeof(TCHAR)] = TCHAR('\0');
      locker = lockerStr;
    } // read info from lock file
  }
}

bool pws_os::LockFile(const stringT &filename, stringT &locker,
                      HANDLE &lockFileHandle, int &LockCount)
{
  const stringT lock_filename = GetLockFileName(filename);
  stringT s_locker;
#ifdef POSIX_FILE_LOCK
  int fh = _open(lock_filename, (_O_CREAT | _O_EXCL | _O_WRONLY),
                 (_S_IREAD | _S_IWRITE));

  if (fh == -1) { // failed to open exclusively. Already locked, or ???
    switch (errno) {
    case EACCES:
      // Tried to open read-only file for writing, or file's
      // sharing mode does not allow specified operations, or given path is directory
      locker.LoadString(IDSC_NOLOCKACCESS);
      break;
    case EEXIST: // filename already exists
      {
        // read locker data ("user@machine:nnnnnnnn") from file
        TCHAR lockerStr[UNLEN + MAX_COMPUTERNAME_LENGTH + sizeof(TCHAR) * 11];
        int fh2 = _open(lock_filename, _O_RDONLY);
        if (fh2 == -1) {
          locker.LoadString(IDSC_CANTGETLOCKER);
        } else {
          int bytesRead = _read(fh2, lockerStr, sizeof(lockerStr)-1);
          _close(fh2);
          if (bytesRead > 0) {
            lockerStr[bytesRead] = TCHAR('\0');
            locker = lockerStr;
          } else { // read failed for some reason
            locker = _T("Unable to read locker?");
          } // read info from lock file
        } // open lock file for read
        break;
      } // EEXIST block
    case EINVAL: // Invalid oflag or pmode argument
      locker.LoadString(IDSC_INTERNALLOCKERROR);
      break;
    case EMFILE: // No more file handles available (too many open files)
      locker.LoadString(IDSC_SYSTEMLOCKERROR);
      break;
    case ENOENT: //File or path not found
      locker.LoadString(IDSC_LOCKFILEPATHNF);
      break;
    default:
      locker.LoadString(IDSC_LOCKUNKNOWNERROR);
      break;
    } // switch (errno)
    return false;
  } else { // valid filehandle, write our info
    int numWrit;
    numWrit = _write(fh, m_user, m_user.GetLength() * sizeof(TCHAR));
    numWrit += _write(fh, _T("@"), sizeof(TCHAR));
    numWrit += _write(fh, m_sysname, m_sysname.GetLength() * sizeof(TCHAR));
    numWrit += _write(fh, _T(":"), sizeof(TCHAR));
    numWrit += _write(fh, m_ProcessID, m_ProcessID.GetLength() * sizeof(TCHAR));
    ASSERT(numWrit > 0);
    _close(fh);
    return true;
  }
#else
  const stringT user = pws_os::getusername();
  const stringT host = pws_os::gethostname();
  const stringT pid = pws_os::getprocessid();

  const stringT path(lock_filename);
  stringT drv, dir, fname, ext;

  pws_os::splitpath(path, drv, dir, fname, ext);

  // Use Win32 API for locking - supposedly better at
  // detecting dead locking processes
  if (lockFileHandle != INVALID_HANDLE_VALUE) {
    // here if we've open another (or same) dbase previously,
    // need to unlock it. A bit inelegant...
    // If app was minimized and ClearData() called, we've a small
    // potential for a TOCTTOU issue here. Worse case, lock
    // will fail.

    const stringT cs_me = user + _T("@") + host + _T(":") + pid;
    GetLocker(lock_filename, s_locker);

    if (cs_me == s_locker) {
      LockCount++;
      locker.clear();
      return true;
    } else {
      pws_os::UnlockFile(filename, lockFileHandle, LockCount);
    }
  }
  lockFileHandle = ::CreateFile(lock_filename.c_str(),
                                GENERIC_WRITE,
                                FILE_SHARE_READ,
                                NULL,
                                CREATE_ALWAYS, // rely on share to fail if exists!
                                FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH |
                                // (Lockheed Martin) Secure Coding  11-14-2007
                                SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION,
                                NULL);

  // Make sure it's a file and not a pipe.  (Lockheed Martin) Secure Coding  11-14-2007
  if (lockFileHandle != INVALID_HANDLE_VALUE) {
    if (::GetFileType( lockFileHandle ) != FILE_TYPE_DISK) {
      ::CloseHandle( lockFileHandle );
      lockFileHandle = INVALID_HANDLE_VALUE;
    }
  }
  // End of Change.  (Lockheed Martin) Secure Coding  11-14-2007

  if (lockFileHandle == INVALID_HANDLE_VALUE) {
    DWORD error = GetLastError();
    switch (error) {
    case ERROR_SHARING_VIOLATION: // already open by a live process
      GetLocker(lock_filename, s_locker);
      locker = s_locker.c_str();
      break;
    default:
      locker = _T("Cannot create lock file - no permission in directory?");
      break;
    } // switch (error)
    return false;
  } else { // valid filehandle, write our info
    DWORD numWrit, sumWrit;
    BOOL write_status;
    write_status = ::WriteFile(lockFileHandle,
                               user.c_str(), user.length() * sizeof(TCHAR),
                               &sumWrit, NULL);
    write_status &= ::WriteFile(lockFileHandle,
                                _T("@"), sizeof(TCHAR),
                                &numWrit, NULL);
    sumWrit += numWrit;
    write_status &= ::WriteFile(lockFileHandle,
                                host.c_str(), host.length() * sizeof(TCHAR),
                                &numWrit, NULL);
    sumWrit += numWrit;
    write_status &= ::WriteFile(lockFileHandle,
                                _T(":"), sizeof(TCHAR),
                                &numWrit, NULL);
    sumWrit += numWrit;
    write_status &= ::WriteFile(lockFileHandle,
                                pid.c_str(), pid.length() * sizeof(TCHAR),
                                &numWrit, NULL);
    sumWrit += numWrit;
    ASSERT(sumWrit > 0);
    LockCount++;
    return (write_status == TRUE);
  }
#endif // POSIX_FILE_LOCK
}

void pws_os::UnlockFile(const stringT &filename,
                        HANDLE &lockFileHandle, int &LockCount)
{
#ifdef POSIX_FILE_LOCK
  stringT lock_filename = GetLockFileName(filename);
  _unlink(lock_filename);
#else
  const stringT user = pws_os::getusername();
  const stringT host = pws_os::gethostname();
  const stringT pid = pws_os::getprocessid();

  // Use Win32 API for locking - supposedly better at
  // detecting dead locking processes
  if (lockFileHandle != INVALID_HANDLE_VALUE) {
    stringT locker;
    const stringT lock_filename = GetLockFileName(filename);
    const stringT cs_me = user + _T("@") + host + _T(":") + pid;
    GetLocker(lock_filename, locker);

    const stringT path(lock_filename);
    stringT drv, dir, fname, ext;

    pws_os::splitpath(path, drv, dir, fname, ext);

    if (cs_me == locker && LockCount > 1) {
      LockCount--;
    } else {
      LockCount = 0;
      CloseHandle(lockFileHandle);
      lockFileHandle = INVALID_HANDLE_VALUE;
      DeleteFile(lock_filename.c_str());
    }
  }
#endif // POSIX_FILE_LOCK
}

bool pws_os::IsLockedFile(const stringT &filename)
{
  const stringT lock_filename = GetLockFileName(filename);
#ifdef POSIX_FILE_LOCK
  return pws_os::FileExists(lock_filename);
#else
  // under this scheme, we need to actually try to open the file to determine
  // if it's locked.
  HANDLE h = CreateFile(lock_filename.c_str(),
                        GENERIC_WRITE,
                        FILE_SHARE_READ,
                        NULL,
                        OPEN_EXISTING, // don't create one!
                        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH |
                        // (Lockheed Martin) Secure Coding  11-14-2007
                        SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION,
                        NULL);

  // Make sure it's a file and not a pipe.  (Lockheed Martin) Secure Coding  11-14-2007
  if (h != INVALID_HANDLE_VALUE) {
    if (::GetFileType( h ) != FILE_TYPE_DISK) {
      ::CloseHandle( h );
      h = INVALID_HANDLE_VALUE;
    }
  }
  // End of Change.  (Lockheed Martin) Secure Coding  11-14-2007

  if (h == INVALID_HANDLE_VALUE) {
    DWORD error = GetLastError();
    if (error == ERROR_SHARING_VIOLATION)
      return true;
    else
      return false; // couldn't open it, probably doesn't exist.
  } else {
    CloseHandle(h); // here if exists but lockable.
    return false;
  }
#endif // POSIX_FILE_LOCK
}
<commit_msg>Fixed NU build.<commit_after>/*
* Copyright (c) 2003-2008 Rony Shapiro <ronys@users.sourceforge.net>.
* All rights reserved. Use of the code is allowed under the
* Artistic License 2.0 terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

/**
 * \file Windows-specific implementation of file.h
 */
#include <afx.h>
#include <Windows.h>
#include <LMCONS.H> // for UNLEN definition
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "../typedefs.h"
#include "../file.h"
#include "../dir.h"
#include "../env.h"

bool pws_os::FileExists(const stringT &filename)
{
  struct _stat statbuf;
  int status;

  status = _tstat(filename.c_str(), &statbuf);
  return (status == 0);
}

bool pws_os::FileExists(const stringT &filename, bool &bReadOnly)
{
  struct _stat statbuf;
  int status;

  status = _tstat(filename.c_str(), &statbuf);

  // As "stat" gives "user permissions" not "file attributes"....
  if (status == 0) {
    DWORD dwAttr = GetFileAttributes(filename.c_str());
    bReadOnly = (FILE_ATTRIBUTE_READONLY & dwAttr) == FILE_ATTRIBUTE_READONLY;
    return true;
  } else {
    bReadOnly = false;
    return false;
  }
}

bool pws_os::RenameFile(const stringT &oldname, const stringT &newname)
{
  _tremove(newname.c_str()); // otherwise rename will fail if newname exists
  int status = _trename(oldname.c_str(), newname.c_str());

  return (status == 0);
}

/*
* The file lock/unlock functions were first implemented (in 2.08)
* with Posix semantics (using open(_O_CREATE|_O_EXCL) to detect
* an existing lock.
* This fails to check liveness of the locker process, specifically,
* if a user just turns of her PC, the lock file will remain.
* So, I'm keeping the Posix code under idef POSIX_FILE_LOCK,
* and re-implementing using the Win32 API, whose semantics
* supposedly protect against this scenario.
* Thanks to Frank (xformer) for discussion on the subject.
*/

static stringT GetLockFileName(const stringT &filename)
{
  ASSERT(!filename.empty());
  // derive lock filename from filename
  stringT retval(filename, 0, filename.find_last_of(TCHAR('.')));
  retval += _T(".plk");
  return retval;
}

static void GetLocker(const stringT &lock_filename, stringT &locker)
{
  locker = _T("Unable to determine locker");
  // read locker data ("user@machine:nnnnnnnn") from file
  TCHAR lockerStr[UNLEN + MAX_COMPUTERNAME_LENGTH + 11];
  // flags here counter (my) intuition, but see
  // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/base/creating_and_opening_files.asp
  HANDLE h2 = ::CreateFile(lock_filename.c_str(),
                           GENERIC_READ,
                           FILE_SHARE_WRITE,
                           NULL,
                           OPEN_EXISTING,
                           (FILE_ATTRIBUTE_NORMAL |
                            // (Lockheed Martin) Secure Coding  11-14-2007
                            SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION),
                           NULL);
  // Make sure it's a file and not a pipe.  (Lockheed Martin) Secure Coding  11-14-2007
  if (h2 != INVALID_HANDLE_VALUE) {
    if (::GetFileType( h2 ) != FILE_TYPE_DISK) {
      ::CloseHandle( h2 );
      h2 = INVALID_HANDLE_VALUE;
    }
  }
  // End of Change.  (Lockheed Martin) Secure Coding  11-14-2007

  if (h2 != INVALID_HANDLE_VALUE) {
    DWORD bytesRead;
    (void)::ReadFile(h2, lockerStr, sizeof(lockerStr)-1,
                     &bytesRead, NULL);
    CloseHandle(h2);
    if (bytesRead > 0) {
      lockerStr[bytesRead/sizeof(TCHAR)] = TCHAR('\0');
      locker = lockerStr;
    } // read info from lock file
  }
}

bool pws_os::LockFile(const stringT &filename, stringT &locker,
                      HANDLE &lockFileHandle, int &LockCount)
{
  const stringT lock_filename = GetLockFileName(filename);
  stringT s_locker;
#ifdef POSIX_FILE_LOCK
  int fh = _open(lock_filename, (_O_CREAT | _O_EXCL | _O_WRONLY),
                 (_S_IREAD | _S_IWRITE));

  if (fh == -1) { // failed to open exclusively. Already locked, or ???
    switch (errno) {
    case EACCES:
      // Tried to open read-only file for writing, or file's
      // sharing mode does not allow specified operations, or given path is directory
      locker.LoadString(IDSC_NOLOCKACCESS);
      break;
    case EEXIST: // filename already exists
      {
        // read locker data ("user@machine:nnnnnnnn") from file
        TCHAR lockerStr[UNLEN + MAX_COMPUTERNAME_LENGTH + sizeof(TCHAR) * 11];
        int fh2 = _open(lock_filename, _O_RDONLY);
        if (fh2 == -1) {
          locker.LoadString(IDSC_CANTGETLOCKER);
        } else {
          int bytesRead = _read(fh2, lockerStr, sizeof(lockerStr)-1);
          _close(fh2);
          if (bytesRead > 0) {
            lockerStr[bytesRead] = TCHAR('\0');
            locker = lockerStr;
          } else { // read failed for some reason
            locker = _T("Unable to read locker?");
          } // read info from lock file
        } // open lock file for read
        break;
      } // EEXIST block
    case EINVAL: // Invalid oflag or pmode argument
      locker.LoadString(IDSC_INTERNALLOCKERROR);
      break;
    case EMFILE: // No more file handles available (too many open files)
      locker.LoadString(IDSC_SYSTEMLOCKERROR);
      break;
    case ENOENT: //File or path not found
      locker.LoadString(IDSC_LOCKFILEPATHNF);
      break;
    default:
      locker.LoadString(IDSC_LOCKUNKNOWNERROR);
      break;
    } // switch (errno)
    return false;
  } else { // valid filehandle, write our info
    int numWrit;
    numWrit = _write(fh, m_user, m_user.GetLength() * sizeof(TCHAR));
    numWrit += _write(fh, _T("@"), sizeof(TCHAR));
    numWrit += _write(fh, m_sysname, m_sysname.GetLength() * sizeof(TCHAR));
    numWrit += _write(fh, _T(":"), sizeof(TCHAR));
    numWrit += _write(fh, m_ProcessID, m_ProcessID.GetLength() * sizeof(TCHAR));
    ASSERT(numWrit > 0);
    _close(fh);
    return true;
  }
#else
  const stringT user = pws_os::getusername();
  const stringT host = pws_os::gethostname();
  const stringT pid = pws_os::getprocessid();

  const stringT path(lock_filename);
  stringT drv, dir, fname, ext;

  pws_os::splitpath(path, drv, dir, fname, ext);

  // Use Win32 API for locking - supposedly better at
  // detecting dead locking processes
  if (lockFileHandle != INVALID_HANDLE_VALUE) {
    // here if we've open another (or same) dbase previously,
    // need to unlock it. A bit inelegant...
    // If app was minimized and ClearData() called, we've a small
    // potential for a TOCTTOU issue here. Worse case, lock
    // will fail.

    const stringT cs_me = user + _T("@") + host + _T(":") + pid;
    GetLocker(lock_filename, s_locker);

    if (cs_me == s_locker) {
      LockCount++;
      locker.clear();
      return true;
    } else {
      pws_os::UnlockFile(filename, lockFileHandle, LockCount);
    }
  }
  lockFileHandle = ::CreateFile(lock_filename.c_str(),
                                GENERIC_WRITE,
                                FILE_SHARE_READ,
                                NULL,
                                CREATE_ALWAYS, // rely on share to fail if exists!
                                FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH |
                                // (Lockheed Martin) Secure Coding  11-14-2007
                                SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION,
                                NULL);

  // Make sure it's a file and not a pipe.  (Lockheed Martin) Secure Coding  11-14-2007
  if (lockFileHandle != INVALID_HANDLE_VALUE) {
    if (::GetFileType( lockFileHandle ) != FILE_TYPE_DISK) {
      ::CloseHandle( lockFileHandle );
      lockFileHandle = INVALID_HANDLE_VALUE;
    }
  }
  // End of Change.  (Lockheed Martin) Secure Coding  11-14-2007

  if (lockFileHandle == INVALID_HANDLE_VALUE) {
    DWORD error = GetLastError();
    switch (error) {
    case ERROR_SHARING_VIOLATION: // already open by a live process
      GetLocker(lock_filename, s_locker);
      locker = s_locker.c_str();
      break;
    default:
      locker = _T("Cannot create lock file - no permission in directory?");
      break;
    } // switch (error)
    return false;
  } else { // valid filehandle, write our info
    DWORD numWrit, sumWrit;
    BOOL write_status;
    write_status = ::WriteFile(lockFileHandle,
                               user.c_str(), user.length() * sizeof(TCHAR),
                               &sumWrit, NULL);
    write_status &= ::WriteFile(lockFileHandle,
                                _T("@"), sizeof(TCHAR),
                                &numWrit, NULL);
    sumWrit += numWrit;
    write_status &= ::WriteFile(lockFileHandle,
                                host.c_str(), host.length() * sizeof(TCHAR),
                                &numWrit, NULL);
    sumWrit += numWrit;
    write_status &= ::WriteFile(lockFileHandle,
                                _T(":"), sizeof(TCHAR),
                                &numWrit, NULL);
    sumWrit += numWrit;
    write_status &= ::WriteFile(lockFileHandle,
                                pid.c_str(), pid.length() * sizeof(TCHAR),
                                &numWrit, NULL);
    sumWrit += numWrit;
    ASSERT(sumWrit > 0);
    LockCount++;
    return (write_status == TRUE);
  }
#endif // POSIX_FILE_LOCK
}

void pws_os::UnlockFile(const stringT &filename,
                        HANDLE &lockFileHandle, int &LockCount)
{
#ifdef POSIX_FILE_LOCK
  stringT lock_filename = GetLockFileName(filename);
  _unlink(lock_filename);
#else
  const stringT user = pws_os::getusername();
  const stringT host = pws_os::gethostname();
  const stringT pid = pws_os::getprocessid();

  // Use Win32 API for locking - supposedly better at
  // detecting dead locking processes
  if (lockFileHandle != INVALID_HANDLE_VALUE) {
    stringT locker;
    const stringT lock_filename = GetLockFileName(filename);
    const stringT cs_me = user + _T("@") + host + _T(":") + pid;
    GetLocker(lock_filename, locker);

    const stringT path(lock_filename);
    stringT drv, dir, fname, ext;

    pws_os::splitpath(path, drv, dir, fname, ext);

    if (cs_me == locker && LockCount > 1) {
      LockCount--;
    } else {
      LockCount = 0;
      CloseHandle(lockFileHandle);
      lockFileHandle = INVALID_HANDLE_VALUE;
      DeleteFile(lock_filename.c_str());
    }
  }
#endif // POSIX_FILE_LOCK
}

bool pws_os::IsLockedFile(const stringT &filename)
{
  const stringT lock_filename = GetLockFileName(filename);
#ifdef POSIX_FILE_LOCK
  return pws_os::FileExists(lock_filename);
#else
  // under this scheme, we need to actually try to open the file to determine
  // if it's locked.
  HANDLE h = CreateFile(lock_filename.c_str(),
                        GENERIC_WRITE,
                        FILE_SHARE_READ,
                        NULL,
                        OPEN_EXISTING, // don't create one!
                        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH |
                        // (Lockheed Martin) Secure Coding  11-14-2007
                        SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION,
                        NULL);

  // Make sure it's a file and not a pipe.  (Lockheed Martin) Secure Coding  11-14-2007
  if (h != INVALID_HANDLE_VALUE) {
    if (::GetFileType( h ) != FILE_TYPE_DISK) {
      ::CloseHandle( h );
      h = INVALID_HANDLE_VALUE;
    }
  }
  // End of Change.  (Lockheed Martin) Secure Coding  11-14-2007

  if (h == INVALID_HANDLE_VALUE) {
    DWORD error = GetLastError();
    if (error == ERROR_SHARING_VIOLATION)
      return true;
    else
      return false; // couldn't open it, probably doesn't exist.
  } else {
    CloseHandle(h); // here if exists but lockable.
    return false;
  }
#endif // POSIX_FILE_LOCK
}
<|endoftext|>


<commit_before>/*
Copyright(c) 2016-2020 Panos Karabelas

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions :

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

//= IMPLEMENTATION ===============
#include "../RHI_Implementation.h"
#ifdef API_GRAPHICS_D3D11
//================================

//= INCLUDES ========================
#include "../RHI_SwapChain.h"
#include "../RHI_Device.h"
#include "../../Logging/Log.h"
#include "../RHI_CommandList.h"
#include "../../Rendering/Renderer.h"
#include "../../Profiling/Profiler.h"
//===================================

//= NAMESPACES ================
using namespace std;
using namespace Spartan::Math;
//=============================

namespace Spartan
{
	RHI_SwapChain::RHI_SwapChain(
		void* window_handle,
        const shared_ptr<RHI_Device>& rhi_device,
		const uint32_t width,
		const uint32_t height,
		const RHI_Format format	    /*= Format_R8G8B8A8_UNORM*/,
		const uint32_t buffer_count	/*= 1 */,
        const uint32_t flags	    /*= Present_Immediate */
	)
	{
        // Validate device
        if (!rhi_device || !rhi_device->GetContextRhi()->device)
        {
            LOG_ERROR("Invalid device.");
            return;
        }

        // Validate window handle
		const auto hwnd	= static_cast<HWND>(window_handle);
		if (!hwnd|| !IsWindow(hwnd))
		{
			LOG_ERROR_INVALID_PARAMETER();
			return;
		}

        // Validate resolution
        if (!rhi_device->ValidateResolution(width, height))
        {
            LOG_WARNING("%dx%d is an invalid resolution", width, height);
            return;
        }

		// Get factory
		IDXGIFactory* dxgi_factory = nullptr;
		if (const auto& adapter = rhi_device->GetPrimaryPhysicalDevice())
		{
			auto dxgi_adapter = static_cast<IDXGIAdapter*>(adapter->GetData());
			dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory));

			if (!dxgi_factory)
			{
				LOG_ERROR("Failed to get adapter's factory");
				return;
			}
		}
		else
		{
			LOG_ERROR("Invalid primary adapter");
			return;
		}

		// Save parameters
		m_format		= format;
        m_rhi_device    = rhi_device.get();
		m_buffer_count	= buffer_count;
		m_windowed		= true;
		m_width			= width;
		m_height		= height;
		m_flags			= d3d11_utility::swap_chain::validate_flags(flags);

		// Create swap chain
		{
			DXGI_SWAP_CHAIN_DESC desc;
			ZeroMemory(&desc, sizeof(desc));
			desc.BufferCount					= static_cast<UINT>(buffer_count);
			desc.BufferDesc.Width				= static_cast<UINT>(width);
			desc.BufferDesc.Height				= static_cast<UINT>(height);
			desc.BufferDesc.Format				= d3d11_format[format];
			desc.BufferUsage					= DXGI_USAGE_RENDER_TARGET_OUTPUT;
			desc.OutputWindow					= hwnd;
			desc.SampleDesc.Count				= 1;
			desc.SampleDesc.Quality				= 0;
			desc.Windowed						= m_windowed ? TRUE : FALSE;
			desc.BufferDesc.ScanlineOrdering	= DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
			desc.BufferDesc.Scaling				= DXGI_MODE_SCALING_UNSPECIFIED;
			desc.SwapEffect						= d3d11_utility::swap_chain::get_swap_effect(m_flags);
			desc.Flags							= d3d11_utility::swap_chain::get_flags(m_flags);

			auto swap_chain		= static_cast<IDXGISwapChain*>(m_swap_chain_view);
			const auto result	= dxgi_factory->CreateSwapChain(m_rhi_device->GetContextRhi()->device, &desc, &swap_chain);
			if (FAILED(result))
			{
				LOG_ERROR("%s", d3d11_utility::dxgi_error_to_string(result));
				return;
			}
			m_swap_chain_view = static_cast<void*>(swap_chain);
		}

		// Create the render target
		if (auto swap_chain = static_cast<IDXGISwapChain*>(m_swap_chain_view))
		{
			ID3D11Texture2D* backbuffer = nullptr;
			auto result = swap_chain->GetBuffer(0, IID_PPV_ARGS(&backbuffer));
			if (FAILED(result))
			{
				LOG_ERROR("%s", d3d11_utility::dxgi_error_to_string(result));
				return;
			}

			auto render_target_view = static_cast<ID3D11RenderTargetView*>(m_resource_view_renderTarget);
			result = rhi_device->GetContextRhi()->device->CreateRenderTargetView(backbuffer, nullptr, &render_target_view);
			backbuffer->Release();
			if (FAILED(result))
			{
				LOG_ERROR("%s", d3d11_utility::dxgi_error_to_string(result));
				return;
			}
			m_resource_view_renderTarget = static_cast<void*>(render_target_view);
		}

        // Create command lists
        for (uint32_t i = 0; i < m_buffer_count; i++)
        {
            m_cmd_lists.emplace_back(make_shared<RHI_CommandList>(i, this, rhi_device->GetContext()));
        }

		m_initialized = true;
	}

	RHI_SwapChain::~RHI_SwapChain()
	{
		auto swap_chain = static_cast<IDXGISwapChain*>(m_swap_chain_view);

		// Before shutting down set to windowed mode to avoid swap chain exception
		if (swap_chain && !m_windowed)
		{
			swap_chain->SetFullscreenState(false, nullptr);
		}

        m_cmd_lists.clear();

		safe_release(swap_chain);
		safe_release(*reinterpret_cast<ID3D11RenderTargetView**>(&m_resource_view_renderTarget));
	}

	bool RHI_SwapChain::Resize(const uint32_t width, const uint32_t height)
	{
		if (!m_swap_chain_view)
		{
			LOG_ERROR_INVALID_INTERNALS();
			return false;
		}

        // Validate resolution
        m_present = m_rhi_device->ValidateResolution(width, height);
        if (!m_present)
        {
            // Return true as when minimizing, a resolution
            // of 0,0 can be passed in, and this is fine.
            return true;
        }

		auto swap_chain			= static_cast<IDXGISwapChain*>(m_swap_chain_view);
		auto render_target_view	= static_cast<ID3D11RenderTargetView*>(m_resource_view_renderTarget);

		// Release previous stuff
		safe_release(render_target_view);

		// Set this flag to enable an application to switch modes by calling IDXGISwapChain::ResizeTarget.
		// When switching from windowed to full-screen mode, the display mode (or monitor resolution)
		// will be changed to match the dimensions of the application window.
		if (m_flags & RHI_SwapChain_Allow_Mode_Switch)
		{
            const DisplayMode& display_mode = m_rhi_device->GetActiveDisplayMode();

            // Resize swapchain target
            DXGI_MODE_DESC dxgi_mode_desc   = {};
            dxgi_mode_desc.Width			= static_cast<UINT>(width);
            dxgi_mode_desc.Height			= static_cast<UINT>(height);
            dxgi_mode_desc.Format			= d3d11_format[m_format];
            dxgi_mode_desc.RefreshRate		= DXGI_RATIONAL{ display_mode.numerator, display_mode.denominator };
            dxgi_mode_desc.Scaling			= DXGI_MODE_SCALING_UNSPECIFIED;
            dxgi_mode_desc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;

            // Resize swapchain target
            const auto result = swap_chain->ResizeTarget(&dxgi_mode_desc);
            if (FAILED(result))
            {
            	LOG_ERROR("Failed to resize swapchain target, %s.", d3d11_utility::dxgi_error_to_string(result));
            	return false;
            }
		}

		// Resize swapchain buffers
		const UINT d3d11_flags = d3d11_utility::swap_chain::get_flags(d3d11_utility::swap_chain::validate_flags(m_flags));
		auto result = swap_chain->ResizeBuffers(m_buffer_count, static_cast<UINT>(width), static_cast<UINT>(height), d3d11_format[m_format], d3d11_flags);
		if (FAILED(result))
		{
			LOG_ERROR("Failed to resize swapchain buffers, %s.", d3d11_utility::dxgi_error_to_string(result));
			return false;
		}

		// Get swapchain back-buffer
		ID3D11Texture2D* backbuffer = nullptr;
		result = swap_chain->GetBuffer(0, IID_PPV_ARGS(&backbuffer));
		if (FAILED(result))
		{
			LOG_ERROR("Failed to get swapchain buffer, %s.", d3d11_utility::dxgi_error_to_string(result));
			return false;
		}

		// Create render target view
		result = m_rhi_device->GetContextRhi()->device->CreateRenderTargetView(backbuffer, nullptr, &render_target_view);
		safe_release(backbuffer);
		if (FAILED(result))
		{
			LOG_ERROR("Failed to create render target view, %s.", d3d11_utility::dxgi_error_to_string(result));
			return false;
		}
		m_resource_view_renderTarget = static_cast<void*>(render_target_view);

		return true;
	}

    bool RHI_SwapChain::AcquireNextImage()
    {
        return true;
    }

	bool RHI_SwapChain::Present()
    {
        if (!m_present)
            return true;

		if (!m_swap_chain_view)
		{
			LOG_ERROR_INVALID_INTERNALS();
			return false;
		}

        // Build flags
		const bool tearing_allowed	= m_flags & RHI_Present_Immediate;
        const UINT sync_interval    = tearing_allowed ? 0 : 1; // sync interval can go up to 4, so this could be improved
		const UINT flags			= (tearing_allowed && m_windowed) ? DXGI_PRESENT_ALLOW_TEARING : 0;

        // Present
        auto ptr_swap_chain = static_cast<IDXGISwapChain*>(m_swap_chain_view);
        const auto result = ptr_swap_chain->Present(sync_interval, flags);
        if (FAILED(result))
        {
            LOG_ERROR("Failed to present, %s.", d3d11_utility::dxgi_error_to_string(result));
            return false;
        }

		return true;
	}
}
#endif
<commit_msg>[D3D11] Build fix<commit_after>/*
Copyright(c) 2016-2020 Panos Karabelas

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions :

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

//= IMPLEMENTATION ===============
#include "../RHI_Implementation.h"
#ifdef API_GRAPHICS_D3D11
//================================

//= INCLUDES ========================
#include "../RHI_SwapChain.h"
#include "../RHI_Device.h"
#include "../../Logging/Log.h"
#include "../RHI_CommandList.h"
#include "../../Rendering/Renderer.h"
#include "../../Profiling/Profiler.h"
//===================================

//= NAMESPACES ================
using namespace std;
using namespace Spartan::Math;
//=============================

namespace Spartan
{
	RHI_SwapChain::RHI_SwapChain(
		void* window_handle,
        const shared_ptr<RHI_Device>& rhi_device,
		const uint32_t width,
		const uint32_t height,
		const RHI_Format format	    /*= Format_R8G8B8A8_UNORM*/,
		const uint32_t buffer_count	/*= 1 */,
        const uint32_t flags	    /*= Present_Immediate */
	)
	{
        // Validate device
        if (!rhi_device || !rhi_device->GetContextRhi()->device)
        {
            LOG_ERROR("Invalid device.");
            return;
        }

        // Validate window handle
		const auto hwnd	= static_cast<HWND>(window_handle);
		if (!hwnd|| !IsWindow(hwnd))
		{
			LOG_ERROR_INVALID_PARAMETER();
			return;
		}

        // Validate resolution
        if (!rhi_device->ValidateResolution(width, height))
        {
            LOG_WARNING("%dx%d is an invalid resolution", width, height);
            return;
        }

		// Get factory
		IDXGIFactory* dxgi_factory = nullptr;
		if (const auto& adapter = rhi_device->GetPrimaryPhysicalDevice())
		{
			auto dxgi_adapter = static_cast<IDXGIAdapter*>(adapter->GetData());
			dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory));

			if (!dxgi_factory)
			{
				LOG_ERROR("Failed to get adapter's factory");
				return;
			}
		}
		else
		{
			LOG_ERROR("Invalid primary adapter");
			return;
		}

		// Save parameters
		m_format		= format;
        m_rhi_device    = rhi_device.get();
		m_buffer_count	= buffer_count;
		m_windowed		= true;
		m_width			= width;
		m_height		= height;
		m_flags			= d3d11_utility::swap_chain::validate_flags(flags);

		// Create swap chain
		{
			DXGI_SWAP_CHAIN_DESC desc;
			ZeroMemory(&desc, sizeof(desc));
			desc.BufferCount					= static_cast<UINT>(buffer_count);
			desc.BufferDesc.Width				= static_cast<UINT>(width);
			desc.BufferDesc.Height				= static_cast<UINT>(height);
			desc.BufferDesc.Format				= d3d11_format[format];
			desc.BufferUsage					= DXGI_USAGE_RENDER_TARGET_OUTPUT;
			desc.OutputWindow					= hwnd;
			desc.SampleDesc.Count				= 1;
			desc.SampleDesc.Quality				= 0;
			desc.Windowed						= m_windowed ? TRUE : FALSE;
			desc.BufferDesc.ScanlineOrdering	= DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
			desc.BufferDesc.Scaling				= DXGI_MODE_SCALING_UNSPECIFIED;
			desc.SwapEffect						= d3d11_utility::swap_chain::get_swap_effect(m_flags);
			desc.Flags							= d3d11_utility::swap_chain::get_flags(m_flags);

			auto swap_chain		= static_cast<IDXGISwapChain*>(m_swap_chain_view);
			const auto result	= dxgi_factory->CreateSwapChain(m_rhi_device->GetContextRhi()->device, &desc, &swap_chain);
			if (FAILED(result))
			{
				LOG_ERROR("%s", d3d11_utility::dxgi_error_to_string(result));
				return;
			}
			m_swap_chain_view = static_cast<void*>(swap_chain);
		}

		// Create the render target
		if (auto swap_chain = static_cast<IDXGISwapChain*>(m_swap_chain_view))
		{
			ID3D11Texture2D* backbuffer = nullptr;
			auto result = swap_chain->GetBuffer(0, IID_PPV_ARGS(&backbuffer));
			if (FAILED(result))
			{
				LOG_ERROR("%s", d3d11_utility::dxgi_error_to_string(result));
				return;
			}

			auto render_target_view = static_cast<ID3D11RenderTargetView*>(m_resource_view_renderTarget);
			result = rhi_device->GetContextRhi()->device->CreateRenderTargetView(backbuffer, nullptr, &render_target_view);
			backbuffer->Release();
			if (FAILED(result))
			{
				LOG_ERROR("%s", d3d11_utility::dxgi_error_to_string(result));
				return;
			}
			m_resource_view_renderTarget = static_cast<void*>(render_target_view);
		}

        // Create command lists
        for (uint32_t i = 0; i < m_buffer_count; i++)
        {
            m_cmd_lists.emplace_back(make_shared<RHI_CommandList>(i, this, rhi_device->GetContext()));
        }

		m_initialized = true;
	}

	RHI_SwapChain::~RHI_SwapChain()
	{
		auto swap_chain = static_cast<IDXGISwapChain*>(m_swap_chain_view);

		// Before shutting down set to windowed mode to avoid swap chain exception
		if (swap_chain && !m_windowed)
		{
			swap_chain->SetFullscreenState(false, nullptr);
		}

        m_cmd_lists.clear();

		safe_release(swap_chain);
		safe_release(*reinterpret_cast<ID3D11RenderTargetView**>(&m_resource_view_renderTarget));
	}

	bool RHI_SwapChain::Resize(const uint32_t width, const uint32_t height, const bool force /*= false*/)
	{
		if (!m_swap_chain_view)
		{
			LOG_ERROR_INVALID_INTERNALS();
			return false;
		}

        // Validate resolution
        m_present = m_rhi_device->ValidateResolution(width, height);
        if (!m_present)
        {
            // Return true as when minimizing, a resolution
            // of 0,0 can be passed in, and this is fine.
            return true;
        }

        // Only resize if needed
        if (!force)
        {
            if (m_width == width && m_height == height)
                return true;
        }

		auto swap_chain			= static_cast<IDXGISwapChain*>(m_swap_chain_view);
		auto render_target_view	= static_cast<ID3D11RenderTargetView*>(m_resource_view_renderTarget);

		// Release previous stuff
		safe_release(render_target_view);

		// Set this flag to enable an application to switch modes by calling IDXGISwapChain::ResizeTarget.
		// When switching from windowed to full-screen mode, the display mode (or monitor resolution)
		// will be changed to match the dimensions of the application window.
		if (m_flags & RHI_SwapChain_Allow_Mode_Switch)
		{
            const DisplayMode& display_mode = m_rhi_device->GetActiveDisplayMode();

            // Resize swapchain target
            DXGI_MODE_DESC dxgi_mode_desc   = {};
            dxgi_mode_desc.Width			= static_cast<UINT>(width);
            dxgi_mode_desc.Height			= static_cast<UINT>(height);
            dxgi_mode_desc.Format			= d3d11_format[m_format];
            dxgi_mode_desc.RefreshRate		= DXGI_RATIONAL{ display_mode.numerator, display_mode.denominator };
            dxgi_mode_desc.Scaling			= DXGI_MODE_SCALING_UNSPECIFIED;
            dxgi_mode_desc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;

            // Resize swapchain target
            const auto result = swap_chain->ResizeTarget(&dxgi_mode_desc);
            if (FAILED(result))
            {
            	LOG_ERROR("Failed to resize swapchain target, %s.", d3d11_utility::dxgi_error_to_string(result));
            	return false;
            }
		}

		// Resize swapchain buffers
		const UINT d3d11_flags = d3d11_utility::swap_chain::get_flags(d3d11_utility::swap_chain::validate_flags(m_flags));
		auto result = swap_chain->ResizeBuffers(m_buffer_count, static_cast<UINT>(width), static_cast<UINT>(height), d3d11_format[m_format], d3d11_flags);
		if (FAILED(result))
		{
			LOG_ERROR("Failed to resize swapchain buffers, %s.", d3d11_utility::dxgi_error_to_string(result));
			return false;
		}

		// Get swapchain back-buffer
		ID3D11Texture2D* backbuffer = nullptr;
		result = swap_chain->GetBuffer(0, IID_PPV_ARGS(&backbuffer));
		if (FAILED(result))
		{
			LOG_ERROR("Failed to get swapchain buffer, %s.", d3d11_utility::dxgi_error_to_string(result));
			return false;
		}

		// Create render target view
		result = m_rhi_device->GetContextRhi()->device->CreateRenderTargetView(backbuffer, nullptr, &render_target_view);
		safe_release(backbuffer);
		if (FAILED(result))
		{
			LOG_ERROR("Failed to create render target view, %s.", d3d11_utility::dxgi_error_to_string(result));
			return false;
		}
		m_resource_view_renderTarget = static_cast<void*>(render_target_view);

		return true;
	}

    bool RHI_SwapChain::AcquireNextImage()
    {
        return true;
    }

	bool RHI_SwapChain::Present()
    {
        if (!m_present)
            return true;

		if (!m_swap_chain_view)
		{
			LOG_ERROR_INVALID_INTERNALS();
			return false;
		}

        // Build flags
		const bool tearing_allowed	= m_flags & RHI_Present_Immediate;
        const UINT sync_interval    = tearing_allowed ? 0 : 1; // sync interval can go up to 4, so this could be improved
		const UINT flags			= (tearing_allowed && m_windowed) ? DXGI_PRESENT_ALLOW_TEARING : 0;

        // Present
        auto ptr_swap_chain = static_cast<IDXGISwapChain*>(m_swap_chain_view);
        const auto result = ptr_swap_chain->Present(sync_interval, flags);
        if (FAILED(result))
        {
            LOG_ERROR("Failed to present, %s.", d3d11_utility::dxgi_error_to_string(result));
            return false;
        }

		return true;
	}
}
#endif
<|endoftext|>