#pragma once
#include "FractoriumPch.h"
///
/// Fractorium global utility functions.
///
///
/// Setup a spinner to be placed in a table cell.
/// Due to a serious compiler bug in MSVC, this must be declared as an outside function instead of a static member of Fractorium.
/// The reason is that the default arguments of type valType will not be interpreted correctly by the compiler.
/// If the bug is ever fixed, put it back as a static member function.
///
/// The table the spinner belongs to
/// The receiver object
/// The row in the table where this spinner resides
/// The col in the table where this spinner resides
/// Double pointer to spin box which will hold the spinner upon exit
/// The height of the spinner
/// The minimum value of the spinner
/// The maximum value of the spinner
/// The step of the spinner
/// The signal the spinner emits
/// The slot to receive the signal
/// Whether to increment the row value
/// The default value for the spinner
/// When the spinner has a value of zero and is double clicked, assign this value
/// When the spinner has a value of non-zero and is double clicked, assign this value
template
static void SetupSpinner(QTableWidget* table, const QObject* receiver, int& row, int col, spinType*& spinBox, int height, valType min, valType max, valType step, const char* signal, const char* slot, bool incRow = true, valType val = 0, valType doubleClickZero = -999, valType doubleClickNonZero = -999)
{
spinBox = new spinType(table, height, step);
spinBox->setRange(min, max);
spinBox->setValue(val);
if (col >= 0)
table->setCellWidget(row, col, spinBox);
if (string(signal) != "" && string(slot) != "")
receiver->connect(spinBox, signal, receiver, slot, Qt::QueuedConnection);
if (doubleClickNonZero != -999 && doubleClickZero != -999)
{
spinBox->DoubleClick(true);
spinBox->DoubleClickZero(valType(doubleClickZero));
spinBox->DoubleClickNonZero(valType(doubleClickNonZero));
}
if (incRow)
row++;
}
///
/// Wrapper around QWidget::setTabOrder() to return the second widget.
/// This makes it easy to chain multiple calls without having to retype
/// all of them if the order changes or if a new widget is inserted.
///
/// The parent widget that w1 and w2 belong to
/// The widget to come first in the tab order
/// The widget to come second in the tab order
static QWidget* SetTabOrder(QWidget* p, QWidget* w1, QWidget* w2)
{
p->setTabOrder(w1, w2);
return w2;
}
///
/// Wrapper around QLocale::system().toDouble().
///
/// The string to convert
/// Pointer to boolean which stores the success value of the conversion
/// The converted value if successful, else 0.
static double ToDouble(const QString &s, bool *ok)
{
return QLocale::system().toDouble(s, ok);
}
///
/// Wrapper around QLocale::system().toString().
///
/// The value to convert
/// The string value if successful, else "".
template
static QString ToString(T val)
{
return QLocale::system().toString(val);
}
///
/// Force a QString to end with the specified value.
///
/// The string to append a suffix to
/// The suffix to append
/// The original string value if it already ended in e, else the original value appended with e.
template
static QString MakeEnd(const QString& s, T e)
{
if (!s.endsWith(e))
return s + e;
else
return s;
}
///
/// Check if a path is not empty and exists on the file system.
///
/// The path to check
/// True if s was not empty and existed, else false.
static bool Exists(const QString& s)
{
return s != "" && QDir(s).exists();
}
///
/// Convert a color to one that is displayable on any background.
///
/// The color to convert
/// The converted color
static QColor VisibleColor(const QColor& color)
{
int threshold = 105;
int delta = (color.red() * 0.299) + //Magic numbers gotten from a Stack Overflow post.
(color.green() * 0.587) +
(color.blue() * 0.114);
QColor textColor = (255 - delta < threshold) ? QColor(0, 0, 0) : QColor(255, 255, 255);
return textColor;
}
///
/// Determine whether an xform in an ember is linked to any other xform
/// in the ember.
///
/// The ember which contains the xform
/// The xform to inspect
/// The index of the xform that the xform argument is linked to, else -1
template
static intmax_t IsXformLinked(Ember& ember, Xform* xform)
{
auto count = ember.XformCount();
auto index = ember.GetXformIndex(xform);
intmax_t linked = -1;
size_t toOneCount = 0;
size_t toZeroCount = 0;
size_t toOneIndex = 0;
size_t fromOneCount = 0;
size_t fromZeroCount = 0;
size_t fromOneIndex = 0;
if (index >= 0)
{
for (auto i = 0; i < count; i++)
{
if (xform->Xaos(i) == 0)
toZeroCount++;
else if (xform->Xaos(i) == 1)
{
toOneIndex = i;
toOneCount++;
}
}
if ((toZeroCount == (count - 1)) && toOneCount == 1)
{
for (auto i = 0; i < count; i++)
{
if (auto fromXform = ember.GetXform(i))
{
if (fromXform->Xaos(toOneIndex) == 0)
fromZeroCount++;
else if (fromXform->Xaos(toOneIndex) == 1)
{
fromOneIndex = i;
fromOneCount++;
}
}
}
if ((fromZeroCount == (count - 1)) && fromOneCount == 1)
{
linked = toOneIndex;
}
}
}
return linked;
}