2023-04-25 19:59:54 -04:00
# include " FractoriumPch.h "
# include "Fractorium.h"
# include "QssDialog.h"
// X11 headers on Linux define this, causing build errors.
# ifdef KeyRelease
# undef KeyRelease
# endif
/// <summary>
/// Constructor that initializes the entire program.
/// The setup process is very lengthy because it requires many custom modifications
/// to the GUI widgets that are not possible to do through the designer. So if something
/// is present here, it's safe to assume it can't be done in the designer.
/// </summary>
/// <param name="p">The parent widget of this item</param>
Fractorium : : Fractorium ( QWidget * p )
: QMainWindow ( p )
{
const auto iconSize_ = 9 ;
size_t i = 0 ;
string s ;
Timing t ;
ui . setupUi ( this ) ;
m_Info = OpenCLInfo : : Instance ( ) ;
qRegisterMetaType < size_t > ( " size_t " ) ;
qRegisterMetaType < QVector < int > > ( " QVector<int> " ) ; //For previews.
qRegisterMetaType < vector < byte > > ( " vector<byte> " ) ;
qRegisterMetaType < vv4F > ( " vv4F " ) ;
qRegisterMetaType < EmberTreeWidgetItemBase * > ( " EmberTreeWidgetItemBase* " ) ;
tabifyDockWidget ( ui . LibraryDockWidget , ui . FlameDockWidget ) ;
tabifyDockWidget ( ui . FlameDockWidget , ui . XformsDockWidget ) ;
tabifyDockWidget ( ui . XformsDockWidget , ui . XaosDockWidget ) ;
tabifyDockWidget ( ui . XaosDockWidget , ui . PaletteDockWidget ) ;
tabifyDockWidget ( ui . PaletteDockWidget , ui . InfoDockWidget ) ;
setTabPosition ( Qt : : LeftDockWidgetArea | Qt : : RightDockWidgetArea , QTabWidget : : TabPosition : : North ) ;
//setTabShape(QTabWidget::TabShape::Rounded);
m_Docks . reserve ( 8 ) ;
m_Docks . push_back ( ui . LibraryDockWidget ) ;
m_Docks . push_back ( ui . FlameDockWidget ) ;
m_Docks . push_back ( ui . XformsDockWidget ) ;
m_Docks . push_back ( ui . XaosDockWidget ) ;
m_Docks . push_back ( ui . PaletteDockWidget ) ;
m_Docks . push_back ( ui . InfoDockWidget ) ;
for ( auto dock : m_Docks ) //Prevents a dock from ever getting accidentally hidden.
{
dock - > setWindowFlags ( dock - > windowFlags ( ) & Qt : : WindowStaysOnTopHint ) ;
dock - > setAllowedAreas ( Qt : : DockWidgetArea : : LeftDockWidgetArea
//#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
| Qt : : DockWidgetArea : : RightDockWidgetArea
//#endif
) ;
}
m_Urls < < QUrl : : fromLocalFile ( QStandardPaths : : standardLocations ( QStandardPaths : : DesktopLocation ) . first ( ) )
< < QUrl : : fromLocalFile ( QStandardPaths : : standardLocations ( QStandardPaths : : DownloadLocation ) . first ( ) )
< < QUrl : : fromLocalFile ( QStandardPaths : : standardLocations ( QStandardPaths : : HomeLocation ) . first ( ) )
< < QUrl : : fromLocalFile ( QStandardPaths : : standardLocations ( QStandardPaths : : DocumentsLocation ) . first ( ) )
< < QUrl : : fromLocalFile ( QStandardPaths : : standardLocations ( QStandardPaths : : PicturesLocation ) . first ( ) )
;
m_FontSize = 9 ;
m_VarSortMode = 1 ; //Sort by weight by default.
m_PaletteSortMode = 0 ; //Sort by palette ascending by default.
m_ColorDialog = new QColorDialog ( this ) ;
m_Settings = FractoriumSettings : : Instance ( ) ;
m_QssDialog = new QssDialog ( this ) ;
m_OptionsDialog = new FractoriumOptionsDialog ( this ) ;
m_VarDialog = new FractoriumVariationsDialog ( this ) ;
m_AboutDialog = new FractoriumAboutDialog ( this ) ;
//Put the about dialog in the screen center.
2023-07-18 21:50:43 -04:00
m_AboutDialogCentered = false ;
2023-04-25 19:59:54 -04:00
connect ( m_ColorDialog , SIGNAL ( colorSelected ( const QColor & ) ) , this , SLOT ( OnColorSelected ( const QColor & ) ) , Qt : : QueuedConnection ) ;
m_XformComboColors [ i + + ] = QColor ( 0XFF , 0X00 , 0X00 ) ;
m_XformComboColors [ i + + ] = QColor ( 0XCC , 0XCC , 0X00 ) ;
m_XformComboColors [ i + + ] = QColor ( 0X00 , 0XCC , 0X00 ) ;
m_XformComboColors [ i + + ] = QColor ( 0X00 , 0XCC , 0XCC ) ;
m_XformComboColors [ i + + ] = QColor ( 0X40 , 0X40 , 0XFF ) ;
m_XformComboColors [ i + + ] = QColor ( 0XCC , 0X00 , 0XCC ) ;
m_XformComboColors [ i + + ] = QColor ( 0XCC , 0X80 , 0X00 ) ;
m_XformComboColors [ i + + ] = QColor ( 0X80 , 0X00 , 0X4F ) ;
m_XformComboColors [ i + + ] = QColor ( 0X80 , 0X80 , 0X22 ) ;
m_XformComboColors [ i + + ] = QColor ( 0X60 , 0X80 , 0X60 ) ;
m_XformComboColors [ i + + ] = QColor ( 0X50 , 0X80 , 0X80 ) ;
m_XformComboColors [ i + + ] = QColor ( 0X4F , 0X4F , 0X80 ) ;
m_XformComboColors [ i + + ] = QColor ( 0X80 , 0X50 , 0X80 ) ;
m_XformComboColors [ i + + ] = QColor ( 0X80 , 0X60 , 0X22 ) ;
m_FinalXformComboColor = QColor ( 0x7F , 0x7F , 0x7F ) ;
for ( i = 0 ; i < XFORM_COLOR_COUNT ; i + + )
{
QPixmap pixmap ( iconSize_ , iconSize_ ) ;
pixmap . fill ( m_XformComboColors [ i ] ) ;
m_XformComboIcons [ i ] = QIcon ( pixmap ) ;
}
QPixmap pixmap ( iconSize_ , iconSize_ ) ;
pixmap . fill ( m_FinalXformComboColor ) ;
m_FinalXformComboIcon = QIcon ( pixmap ) ;
InitToolbarUI ( ) ;
InitParamsUI ( ) ;
InitXformsUI ( ) ;
InitXformsColorUI ( ) ;
InitXformsAffineUI ( ) ;
InitXformsVariationsUI ( ) ;
InitXformsSelectUI ( ) ;
InitXaosUI ( ) ;
InitPaletteUI ( ) ;
InitLibraryUI ( ) ;
InitInfoUI ( ) ;
InitMenusUI ( ) ;
//This will init the controller and fill in the variations and palette tables with template specific instances
//of their respective objects.
# ifdef DO_DOUBLE
if ( m_Settings - > Double ( ) )
m_Controller = unique_ptr < FractoriumEmberControllerBase > ( new FractoriumEmberController < double > ( this ) ) ;
else
# endif
m_Controller = unique_ptr < FractoriumEmberControllerBase > ( new FractoriumEmberController < float > ( this ) ) ;
m_Controller - > SetupVariationsTree ( ) ;
m_Controller - > FilteredVariations ( ) ;
if ( m_Info - > Ok ( ) & & m_Settings - > OpenCL ( ) & & m_QualitySpin - > value ( ) < ( m_Settings - > OpenClQuality ( ) * m_Settings - > Devices ( ) . size ( ) ) )
m_QualitySpin - > setValue ( m_Settings - > OpenClQuality ( ) * m_Settings - > Devices ( ) . size ( ) ) ;
const auto statusBarHeight = 20 ; // *devicePixelRatio();
ui . StatusBar - > setMinimumHeight ( statusBarHeight ) ;
ui . StatusBar - > setMaximumHeight ( statusBarHeight ) ;
m_RenderStatusLabel = new QLabel ( this ) ;
m_RenderStatusLabel - > setMinimumWidth ( 200 ) ;
m_RenderStatusLabel - > setAlignment ( Qt : : AlignRight ) ;
ui . StatusBar - > addPermanentWidget ( m_RenderStatusLabel ) ;
m_CoordinateStatusLabel = new QLabel ( this ) ;
m_CoordinateStatusLabel - > setMinimumWidth ( 300 ) ;
m_CoordinateStatusLabel - > setMaximumWidth ( 300 ) ;
m_CoordinateStatusLabel - > setAlignment ( Qt : : AlignLeft ) ;
ui . StatusBar - > addWidget ( m_CoordinateStatusLabel ) ;
const auto progressBarHeight = 15 ;
const auto progressBarWidth = 300 ;
m_ProgressBar = new QProgressBar ( this ) ;
m_ProgressBar - > setRange ( 0 , 100 ) ;
m_ProgressBar - > setValue ( 0 ) ;
m_ProgressBar - > setMinimumHeight ( progressBarHeight ) ;
m_ProgressBar - > setMaximumHeight ( progressBarHeight ) ;
m_ProgressBar - > setMinimumWidth ( progressBarWidth ) ;
m_ProgressBar - > setMaximumWidth ( progressBarWidth ) ;
2023-06-22 21:25:53 -04:00
m_ProgressBar - > setAlignment ( Qt : : AlignCenter ) ;
2023-04-25 19:59:54 -04:00
ui . StatusBar - > addPermanentWidget ( m_ProgressBar ) ;
//Setup pointer in the GL window to point back to here.
ui . GLDisplay - > SetMainWindow ( this ) ;
bool restored = restoreState ( m_Settings - > value ( " windowState " ) . toByteArray ( ) ) ;
showMaximized ( ) ; //This won't fully set things up and show them until after this constructor exits.
connect ( ui . LibraryDockWidget , SIGNAL ( dockLocationChanged ( Qt : : DockWidgetArea ) ) , this , SLOT ( dockLocationChanged ( Qt : : DockWidgetArea ) ) ) ;
connect ( ui . LibraryDockWidget , SIGNAL ( topLevelChanged ( bool ) ) , this , SLOT ( OnDockTopLevelChanged ( bool ) ) ) ;
//Always ensure the library tab is selected if not restoring, which will show preview renders.
if ( ! restored )
{
ui . LibraryDockWidget - > raise ( ) ;
ui . LibraryDockWidget - > show ( ) ;
ui . XformsTabWidget - > setCurrentIndex ( 2 ) ; //Make variations tab the currently selected one under the Xforms tab.
}
m_PreviousPaletteRow = - 1 ; //Force click handler the first time through.
SetCoordinateStatus ( 0 , 0 , 0 , 0 ) ;
SetTabOrders ( ) ;
m_SettingsPath = QFileInfo ( m_Settings - > fileName ( ) ) . absoluteDir ( ) . absolutePath ( ) ;
ifstream ifs ( ( m_SettingsPath + " /default.qss " ) . toStdString ( ) . c_str ( ) , ifstream : : in ) ;
if ( ifs . is_open ( ) )
{
string total , qs ;
total . reserve ( 20 * 1024 ) ;
while ( std : : getline ( ifs , qs ) )
total + = qs + " \n " ;
m_Style = QString : : fromStdString ( total ) ;
}
else
m_Style = BaseStyle ( ) ;
setStyleSheet ( m_Style ) ;
if ( ! m_Settings - > Theme ( ) . isEmpty ( ) )
{
if ( auto theme = QStyleFactory : : create ( m_Settings - > Theme ( ) ) )
{
m_Theme = theme ;
setStyle ( m_Theme ) ;
}
}
else
{
if ( ! QStyleFactory : : keys ( ) . empty ( ) )
{
2023-06-22 21:25:53 -04:00
auto foundFusion = false ;
for ( auto & s : QStyleFactory : : keys ( ) )
{
if ( s . compare ( " fusion " , Qt : : CaseInsensitive ) = = 0 ) //Default to fusion if it exists and the style has not been set yet.
{
m_Theme = QStyleFactory : : create ( s ) ;
setStyle ( m_Theme ) ;
foundFusion = true ;
break ;
}
}
if ( ! foundFusion )
{
m_Theme = QStyleFactory : : create ( qApp - > style ( ) - > objectName ( ) ) ;
setStyle ( m_Theme ) ;
}
2023-04-25 19:59:54 -04:00
}
}
# ifdef __APPLE__
for ( auto dock : m_Docks ) //Fixes focus problem on OSX.
{
if ( ! dock - > isHidden ( ) )
{
dock - > setFloating ( ! dock - > isFloating ( ) ) ;
dock - > setFloating ( ! dock - > isFloating ( ) ) ;
break ;
}
}
# endif
//At this point, everything has been setup except the renderer. Shortly after
//this constructor exits, GLWidget::InitGL() will create the initial flock and start the rendering timer
//which executes whenever the program is idle. Upon starting the timer, the renderer
//will be initialized.
//auto cdc = wglGetCurrentDC();
//auto cc = wglGetCurrentContext();
//qDebug() << "Fractorium::Fractorium():";
//qDebug() << "Current DC: " << cdc;
//qDebug() << "Current Context: " << cc;
QTimer : : singleShot ( 1000 , [ & ] ( ) { ui . GLDisplay - > InitGL ( ) ; } ) ;
}
/// <summary>
/// Destructor which saves out the settings file.
/// All other memory is cleared automatically through the use of STL.
/// </summary>
Fractorium : : ~ Fractorium ( )
{
SyncSequenceSettings ( ) ;
m_VarDialog - > SyncSettings ( ) ;
m_Settings - > ShowXforms ( ui . ActionDrawPreAffines - > isChecked ( ) | | ui . ActionDrawPostAffines - > isChecked ( ) ) ;
m_Settings - > ShowGrid ( ui . ActionDrawGrid - > isChecked ( ) ) ;
m_Settings - > setValue ( " windowState " , saveState ( ) ) ;
m_Settings - > sync ( ) ;
if ( m_Settings - > LoadLast ( ) )
m_Controller - > SaveCurrentFileOnShutdown ( ) ;
}
/// <summary>
/// Return the URLs used to determine the icons that show up in the location bar in all file/folder dialogs.
/// </summary>
QList < QUrl > Fractorium : : Urls ( )
{
return m_Urls ;
}
/// <summary>
/// Set the coordinate text in the status bar.
/// </summary>
/// <param name="rasX">The raster x coordinate</param>
/// <param name="rasY">The raster y coordinate</param>
/// <param name="worldX">The cartesian world x coordinate</param>
/// <param name="worldY">The cartesian world y coordinate</param>
void Fractorium : : SetCoordinateStatus ( int rasX , int rasY , float worldX , float worldY )
{
static QString coords ;
auto str = coords . asprintf ( " Window: %4d, %4d World: %2.2f, %2.2f " , rasX , rasY , worldX , worldY ) ;
m_CoordinateStatusLabel - > setText ( str ) ;
}
/// <summary>
/// Center the scroll area.
/// Called in response to a resizing, or setting of new ember.
/// </summary>
void Fractorium : : CenterScrollbars ( )
{
const auto w = ui . GLParentScrollArea - > horizontalScrollBar ( ) ;
const auto h = ui . GLParentScrollArea - > verticalScrollBar ( ) ;
if ( w & & h )
{
w - > setValue ( w - > maximum ( ) / 2 ) ;
h - > setValue ( h - > maximum ( ) / 2 ) ;
}
}
/// <summary>
/// Apply the settings for saving an ember to an Xml file to an ember (presumably about to be saved).
/// </summary>
/// <param name="ember">The ember to apply the settings to</param>
template < typename T >
void FractoriumEmberController < T > : : ApplyXmlSavingTemplate ( Ember < T > & ember )
{
ember . m_Quality = m_Fractorium - > m_Settings - > XmlQuality ( ) ;
ember . m_Supersample = m_Fractorium - > m_Settings - > XmlSupersample ( ) ;
ember . m_TemporalSamples = m_Fractorium - > m_Settings - > XmlTemporalSamples ( ) ;
}
/// <summary>
/// Return whether the current ember contains a final xform and the GUI is aware of it.
/// Note this can be true even if the final is empty, as long as they've added one and have
/// not explicitly deleted it.
/// </summary>
/// <returns>True if the current ember contains a final xform, else false.</returns>
bool Fractorium : : HaveFinal ( )
{
const auto combo = ui . CurrentXformCombo ;
return ( combo - > count ( ) > 0 & & combo - > itemText ( combo - > count ( ) - 1 ) = = " Final " ) ;
}
/// <summary>
/// Slots.
/// </summary>
/// <summary>
/// Empty placeholder for now.
/// Qt has a severe bug where the dock gets hidden behind the window.
/// Perhaps this will be used in the future if Qt ever fixes that bug.
/// Called when the top level dock is changed.
/// </summary>
/// <param name="topLevel">True if top level, else false.</param>
void Fractorium : : OnDockTopLevelChanged ( bool topLevel )
{
//setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::TabPosition::North);
//if (topLevel)
//{
// if (ui.DockWidget->y() <= 0)
// ui.DockWidget->setGeometry(ui.DockWidget->x(), ui.DockWidget->y() + 100, ui.DockWidget->width(), ui.DockWidget->height());
//
// ui.DockWidget->setFloating(true);
//}
//else
// ui.DockWidget->setFloating(false);
}
/// <summary>
/// Empty placeholder for now.
/// Qt has a severe bug where the dock gets hidden behind the window.
/// Perhaps this will be used in the future if Qt ever fixes that bug.
/// Called when the dock location is changed.
/// </summary>
/// <param name="area">The dock widget area</param>
void Fractorium : : dockLocationChanged ( Qt : : DockWidgetArea area )
{
//setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::TabPosition::North);
//ui.DockWidget->resize(500, ui.DockWidget->height());
//ui.DockWidget->update();
//ui.dockWidget->setFloating(true);
//ui.dockWidget->setFloating(false);
}
/// <summary>
/// Virtual event overrides.
/// </summary>
/// <summary>
/// Event filter for taking special action on:
/// Dock widget resize events, which in turn trigger GLParentScrollArea events.
/// Library tree key events, specifically delete.
/// Library tree drag n drop events.
/// </summary>
/// <param name="o">The object</param>
/// <param name="e">The eevent</param>
/// <returns>false</returns>
bool Fractorium : : eventFilter ( QObject * o , QEvent * e )
{
static int fcount = 0 ; //Qt seems to deliver three events for every key press. So a count must be kept to only respond to the third event.
static int xfupcount = 0 ;
static int xfdncount = 0 ;
static int wcount = 0 ;
static int scount = 0 ;
static int acount = 0 ;
static int dcount = 0 ;
static int qcount = 0 ;
static int ecount = 0 ;
static int gcount = 0 ;
static int hcount = 0 ;
static int pcount = 0 ;
static int commacount = 0 ;
static int periodcount = 0 ;
static int lcount = 0 ;
const bool shift = QGuiApplication : : keyboardModifiers ( ) . testFlag ( Qt : : ShiftModifier ) ;
const bool ctrl = QGuiApplication : : keyboardModifiers ( ) . testFlag ( Qt : : ControlModifier ) ;
if ( o = = ui . GLParentScrollArea & & e - > type ( ) = = QEvent : : Resize )
{
m_WidthSpin - > DoubleClickNonZero ( ui . GLParentScrollArea - > width ( ) * ui . GLDisplay - > devicePixelRatioF ( ) ) ;
m_HeightSpin - > DoubleClickNonZero ( ui . GLParentScrollArea - > height ( ) * ui . GLDisplay - > devicePixelRatioF ( ) ) ;
}
else if ( const auto ke = dynamic_cast < QKeyEvent * > ( e ) )
{
auto combo = ui . CurrentXformCombo ;
const auto times = 3 ;
const auto ftimes = 2 ;
if ( ke - > key ( ) > = Qt : : Key_F1 & & ke - > key ( ) < = Qt : : Key_F32 )
{
fcount + + ;
if ( fcount > = ftimes )
{
const auto val = ke - > key ( ) - ( int ) Qt : : Key_F1 ;
if ( shift )
{
if ( val < ui . LibraryTree - > topLevelItem ( 0 ) - > childCount ( ) )
m_Controller - > SetEmber ( val , true ) ;
}
else if ( val < combo - > count ( ) )
combo - > setCurrentIndex ( val ) ;
fcount = 0 ;
//qDebug() << "global function key press: " << ke->key() << " " << o->metaObject()->className() << " " << o->objectName();
}
return true ;
}
else if ( o = = ui . LibraryTree )
{
//Require shift for deleting to prevent it from triggering when the user enters delete in the edit box.
if ( ke - > key ( ) = = Qt : : Key_Delete & & e - > type ( ) = = QEvent : : KeyRelease & & shift )
{
auto v = GetCurrentEmberIndex ( false ) ;
if ( ui . LibraryTree - > topLevelItem ( 0 ) - > childCount ( ) > 1 )
OnDelete ( v ) ;
return true ;
}
}
else if ( o = = this )
{
const auto focusedctrlEdit = dynamic_cast < QLineEdit * > ( this - > focusWidget ( ) ) ;
const auto focusedctrlSpin = dynamic_cast < QSpinBox * > ( this - > focusWidget ( ) ) ;
const auto focusedctrlDblSpin = dynamic_cast < QDoubleSpinBox * > ( this - > focusWidget ( ) ) ;
const auto focusedctrlCombo = dynamic_cast < QComboBox * > ( this - > focusWidget ( ) ) ;
if ( ! focusedctrlEdit & &
! focusedctrlSpin & &
! focusedctrlDblSpin & &
! focusedctrlCombo & &
! QGuiApplication : : keyboardModifiers ( ) . testFlag ( Qt : : AltModifier ) ) //Must exclude these because otherwise, typing a minus key in any of the spinners will switch the xform. Also exclude alt.
{
size_t index = 0 ;
double vdist = 0.01 ;
double hdist = 0.01 ;
double zoom = 1 ;
double rot = 1 ;
double grow = 0.01 ;
bool pre = true ;
if ( shift )
{
auto v = GetCurrentEmberIndex ( true ) ;
if ( v . size ( ) > 0 )
index = v [ 0 ] . first ;
else
index = 0 ;
}
else
index = combo - > currentIndex ( ) ;
if ( const auto r = m_Controller - > Renderer ( ) )
{
hdist = std : : abs ( r - > UpperRightX ( ) - r - > LowerLeftX ( ) ) * 0.01 * m_Controller - > AffineScaleLockedToCurrent ( ) ;
vdist = std : : abs ( r - > UpperRightY ( ) - r - > LowerLeftY ( ) ) * 0.01 * m_Controller - > AffineScaleLockedToCurrent ( ) ;
if ( shift )
{
hdist * = 0.1 ;
vdist * = 0.1 ;
rot * = 0.1 ;
grow * = 0.1 ;
zoom * = 0.1 ;
}
else if ( ctrl )
{
hdist * = 10 ;
vdist * = 10 ;
rot * = 10 ;
grow * = 10 ;
zoom * = 10 ;
}
}
if ( m_Controller . get ( ) & & m_Controller - > GLController ( ) )
pre = m_Controller - > GLController ( ) - > AffineType ( ) = = eAffineType : : AffinePre ;
if ( ke - > key ( ) = = Qt : : Key_Plus | | ke - > key ( ) = = Qt : : Key_Equal )
{
xfupcount + + ;
if ( xfupcount > = times )
{
xfupcount = 0 ;
if ( shift )
m_Controller - > SetEmber ( ( index + 1 ) % ui . LibraryTree - > topLevelItem ( 0 ) - > childCount ( ) , true ) ;
else
combo - > setCurrentIndex ( ( index + 1 ) % combo - > count ( ) ) ;
//qDebug() << "global arrow plus key press: " << ke->key() << " " << o->metaObject()->className() << " " << o->objectName();
}
return true ;
}
else if ( ke - > key ( ) = = Qt : : Key_Minus | | ke - > key ( ) = = Qt : : Key_Underscore )
{
xfdncount + + ;
if ( xfdncount > = times )
{
xfdncount = 0 ;
if ( shift )
{
if ( index = = 0 )
index = ui . LibraryTree - > topLevelItem ( 0 ) - > childCount ( ) ;
m_Controller - > SetEmber ( ( index - 1 ) % ui . LibraryTree - > topLevelItem ( 0 ) - > childCount ( ) , true ) ;
}
else
{
if ( index = = 0 )
index = combo - > count ( ) ;
combo - > setCurrentIndex ( ( index - 1 ) % combo - > count ( ) ) ;
}
//qDebug() << "global arrow minus key press: " << ke->key() << " " << o->metaObject()->className() << " " << o->objectName();
}
return true ;
}
else if ( ke - > key ( ) = = Qt : : Key_P )
{
if ( ! ctrl )
{
pcount + + ;
if ( pcount > = times )
{
pcount = 0 ;
if ( ! shift )
{
ui . ActionDrawPreAffines - > setChecked ( ! ui . ActionDrawPreAffines - > isChecked ( ) ) ;
OnActionDrawAffines ( ui . ActionDrawPreAffines - > isChecked ( ) ) ;
}
else
{
ui . ActionDrawPostAffines - > setChecked ( ! ui . ActionDrawPostAffines - > isChecked ( ) ) ;
OnActionDrawAffines ( ui . ActionDrawPostAffines - > isChecked ( ) ) ;
}
}
return true ;
}
}
else if ( ke - > key ( ) = = Qt : : Key_L )
{
if ( ! ctrl )
{
lcount + + ;
if ( lcount > = times )
{
lcount = 0 ;
if ( ! shift )
{
if ( DrawPreAffines ( ) )
{
ui . ActionDrawAllPreAffines - > setChecked ( ! ui . ActionDrawAllPreAffines - > isChecked ( ) ) ;
OnActionDrawAllAffines ( ui . ActionDrawAllPreAffines - > isChecked ( ) ) ;
}
}
else if ( DrawPostAffines ( ) )
{
ui . ActionDrawAllPostAffines - > setChecked ( ! ui . ActionDrawAllPostAffines - > isChecked ( ) ) ;
OnActionDrawAllAffines ( ui . ActionDrawAllPostAffines - > isChecked ( ) ) ;
}
}
return true ;
}
}
else if ( ke - > key ( ) = = Qt : : Key_Comma | | ke - > key ( ) = = Qt : : Key_Less )
{
commacount + + ;
if ( commacount > = times )
{
commacount = 0 ;
m_ScaleSpin - > setValue ( m_ScaleSpin - > value ( ) - zoom ) ;
}
return true ;
}
else if ( ke - > key ( ) = = Qt : : Key_Period | | ke - > key ( ) = = Qt : : Key_Greater )
{
periodcount + + ;
if ( periodcount > = times )
{
periodcount = 0 ;
m_ScaleSpin - > setValue ( m_ScaleSpin - > value ( ) + zoom ) ;
}
return true ;
}
else if ( ( ! DrawPreAffines ( ) & & pre ) | | ( ! DrawPostAffines ( ) & & ! pre ) ) //Everything below this must be for editing xforms via key press.
{
return true ;
}
else if ( ke - > key ( ) = = Qt : : Key_W )
{
wcount + + ;
if ( wcount > = times )
{
wcount = 0 ;
m_Controller - > MoveXforms ( 0 , vdist , pre ) ;
}
return true ;
}
else if ( ke - > key ( ) = = Qt : : Key_S )
{
scount + + ;
if ( scount > = times )
{
scount = 0 ;
m_Controller - > MoveXforms ( 0 , - vdist , pre ) ;
}
return true ;
}
else if ( ke - > key ( ) = = Qt : : Key_A )
{
acount + + ;
if ( acount > = times )
{
acount = 0 ;
m_Controller - > MoveXforms ( - hdist , 0 , pre ) ;
}
return true ;
}
else if ( ke - > key ( ) = = Qt : : Key_D )
{
dcount + + ;
if ( dcount > = times )
{
dcount = 0 ;
m_Controller - > MoveXforms ( hdist , 0 , pre ) ;
}
return true ;
}
else if ( ke - > key ( ) = = Qt : : Key_Q )
{
qcount + + ;
if ( qcount > = times )
{
qcount = 0 ;
m_Controller - > RotateXformsByAngle ( - rot , pre ) ;
}
return true ;
}
else if ( ke - > key ( ) = = Qt : : Key_E )
{
ecount + + ;
if ( ecount > = times )
{
ecount = 0 ;
m_Controller - > RotateXformsByAngle ( rot , pre ) ;
}
return true ;
}
else if ( ke - > key ( ) = = Qt : : Key_G )
{
gcount + + ;
if ( gcount > = times )
{
gcount = 0 ;
m_Controller - > ScaleXforms ( 1 - grow , pre ) ;
}
return true ;
}
else if ( ke - > key ( ) = = Qt : : Key_H )
{
hcount + + ;
if ( hcount > = times )
{
hcount = 0 ;
m_Controller - > ScaleXforms ( 1 + grow , pre ) ;
}
return true ;
}
}
}
}
return QMainWindow : : eventFilter ( o , e ) ;
}
/// <summary>
/// Respond to a resize event which will set the double click default value
/// in the width and height spinners.
/// Note, this does not change the size of the ember being rendered or
/// the OpenGL texture it's being drawn on.
/// <param name="e">The event</param>
void Fractorium : : resizeEvent ( QResizeEvent * e )
{
m_WidthSpin - > DoubleClickNonZero ( ui . GLParentScrollArea - > width ( ) * ui . GLDisplay - > devicePixelRatioF ( ) ) ;
m_HeightSpin - > DoubleClickNonZero ( ui . GLParentScrollArea - > height ( ) * ui . GLDisplay - > devicePixelRatioF ( ) ) ;
QMainWindow : : resizeEvent ( e ) ;
}
/// <summary>
/// Respond to a show event to ensure Qt updates the native menubar.
/// On first create, Qt can fail to create the native menu bar properly,
/// but telling it that this window has become the focus window forces
/// it to refresh this.
/// <param name="e">The event</param>
void Fractorium : : showEvent ( QShowEvent * e )
{
//Tell Qt to refresh the native menubar from this widget.
emit qGuiApp - > focusWindowChanged ( windowHandle ( ) ) ;
QMainWindow : : showEvent ( e ) ;
2023-07-18 21:50:43 -04:00
if ( ! m_AboutDialogCentered )
{
m_AboutDialogCentered = true ;
QTimer : : singleShot ( 100 , this , SLOT ( WindowShown ( ) ) ) ;
}
}
/// <summary>
/// Center About Dialog window.
/// Called on program starts after show is completed.
/// See https://doc.qt.io/qt-6/application-windows.html#window-geometry for more information
/// </summary>
void Fractorium : : WindowShown ( )
{
//Put the about dialog in the screen center.
auto screen = QGuiApplication : : screenAt ( pos ( ) ) ;
auto geom = screen - > availableGeometry ( ) ;
m_AboutDialog - > move ( geom . center ( ) - m_AboutDialog - > rect ( ) . center ( ) ) ;
2023-04-25 19:59:54 -04:00
}
/// <summary>
/// Stop rendering and block before exiting.
/// Called on program exit.
/// </summary>
/// <param name="e">The event</param>
void Fractorium : : closeEvent ( QCloseEvent * e )
{
if ( m_Controller . get ( ) )
{
m_Controller - > StopRenderTimer ( true ) ; //Will wait until fully exited and stopped.
m_Controller - > StopAllPreviewRenderers ( ) ;
}
if ( e )
e - > accept ( ) ;
}
/// <summary>
/// Examine the files dragged when it first enters the window area.
/// Ok if at least one file is .flam3, .flam3 or .xml, else not ok.
/// Also traverse folders recursively if a folder is included in the list of dragged items.
/// Called when the user first drags files in.
/// </summary>
/// <param name="e">The event</param>
void Fractorium : : dragEnterEvent ( QDragEnterEvent * e )
{
if ( e - > mimeData ( ) - > hasUrls ( ) )
{
auto urls = e - > mimeData ( ) - > urls ( ) ;
for ( auto & url : urls )
{
auto localFile = url . toLocalFile ( ) ;
if ( QDir ( localFile ) . exists ( ) )
{
QDirIterator it ( localFile , QDirIterator : : Subdirectories ) ;
while ( it . hasNext ( ) )
{
auto next = it . next ( ) ;
qDebug ( ) < < next ;
QFileInfo fileInfo ( next ) ;
auto suf = fileInfo . suffix ( ) ;
if ( suf = = " flam3 " | | suf = = " flame " | | suf = = " xml " | | suf = = " chaos " )
{
e - > accept ( ) ;
return ;
}
}
}
else
{
QFileInfo fileInfo ( localFile ) ;
auto suf = fileInfo . suffix ( ) ;
if ( suf = = " flam3 " | | suf = = " flame " | | suf = = " xml " | | suf = = " chaos " )
{
e - > accept ( ) ;
break ;
}
}
}
}
}
/// <summary>
/// Always accept drag when moving, so that the drop event will correctly be called.
/// </summary>
/// <param name="e">The event</param>
void Fractorium : : dragMoveEvent ( QDragMoveEvent * e )
{
e - > accept ( ) ;
}
/// <summary>
/// Examine and open the dropped files and/or folders.
/// Called when the user drops a file or folder in.
/// </summary>
/// <param name="e">The event</param>
void Fractorium : : dropEvent ( QDropEvent * e )
{
QStringList filenames ;
const auto mod = e - > modifiers ( ) ;
const auto append = mod . testFlag ( Qt : : ControlModifier ) ? false : true ;
if ( e - > mimeData ( ) - > hasUrls ( ) )
{
auto urls = e - > mimeData ( ) - > urls ( ) ;
for ( auto & url : urls )
{
auto localFile = url . toLocalFile ( ) ;
if ( QDir ( localFile ) . exists ( ) )
{
QDirIterator it ( localFile , QDirIterator : : Subdirectories ) ;
while ( it . hasNext ( ) )
{
auto next = it . next ( ) ;
qDebug ( ) < < next ;
QFileInfo fileInfo ( next ) ;
if ( fileInfo . isFile ( ) & & fileInfo . exists ( ) )
{
auto suf = fileInfo . suffix ( ) ;
if ( suf = = " flam3 " | | suf = = " flame " | | suf = = " xml " | | suf = = " chaos " )
filenames < < next ;
}
}
}
else
{
QFileInfo fileInfo ( localFile ) ;
auto suf = fileInfo . suffix ( ) ;
if ( suf = = " flam3 " | | suf = = " flame " | | suf = = " xml " | | suf = = " chaos " )
filenames < < localFile ;
}
}
}
if ( ! filenames . empty ( ) )
m_Controller - > OpenAndPrepFiles ( filenames , append ) ;
}
/// <summary>
/// Setup a combo box to be placed in a table cell.
/// </summary>
/// <param name="table">The table the combo box belongs to</param>
/// <param name="receiver">The receiver object</param>
/// <param name="row">The row in the table where this combo box resides</param>
/// <param name="col">The col in the table where this combo box resides</param>
/// <param name="comboBox">Double pointer to combo box which will hold the spinner upon exit</param>
/// <param name="vals">The string values to populate the combo box with</param>
/// <param name="signal">The signal the combo box emits</param>
/// <param name="slot">The slot to receive the signal</param>
/// <param name="connectionType">Type of the connection. Default: Qt::QueuedConnection.</param>
void Fractorium : : SetupCombo ( QTableWidget * table , const QObject * receiver , int & row , int col , StealthComboBox * & comboBox , const vector < string > & vals , const char * signal , const char * slot , Qt : : ConnectionType connectionType )
{
comboBox = new StealthComboBox ( table ) ;
for ( auto & s : vals ) comboBox - > addItem ( s . c_str ( ) ) ;
table - > setCellWidget ( row , col , comboBox ) ;
connect ( comboBox , signal , receiver , slot , connectionType ) ;
row + + ;
}
/// <summary>
/// Set the header of a table to be fixed.
/// </summary>
/// <param name="header">The header to set</param>
/// <param name="mode">The resizing mode to use. Default: QHeaderView::Fixed.</param>
void Fractorium : : SetFixedTableHeader ( QHeaderView * header , QHeaderView : : ResizeMode mode )
{
header - > setVisible ( true ) ; //For some reason, the designer keeps clobbering this value, so force it here.
header - > setSectionsClickable ( false ) ;
header - > setSectionResizeMode ( mode ) ;
}
/// <summary>
/// Setup and show the open XML dialog.
/// This will perform lazy instantiation.
/// </summary>
/// <param name="openExamples">true to open files in examples folder</param>
/// <returns>The list of filenames selected</returns>
QStringList Fractorium : : SetupOpenXmlDialog ( bool openExamples )
{
# ifndef __APPLE__
//Lazy instantiate since it takes a long time.
if ( ! m_OpenFileDialog . get ( ) )
{
m_OpenFileDialog = std : : make_unique < QFileDialog > ( this ) ;
m_OpenFileDialog - > setViewMode ( QFileDialog : : List ) ;
m_OpenFileDialog - > setOption ( QFileDialog : : DontUseNativeDialog , true ) ;
connect ( m_OpenFileDialog . get ( ) , & QFileDialog : : filterSelected , [ & ] ( const QString & filter ) { m_Settings - > OpenXmlExt ( filter ) ; } ) ;
m_OpenFileDialog - > setFileMode ( QFileDialog : : ExistingFiles ) ;
m_OpenFileDialog - > setAcceptMode ( QFileDialog : : AcceptOpen ) ;
m_OpenFileDialog - > setNameFilter ( " flam3 (*.flam3);;flame (*.flame);;xml (*.xml);;chaos (*.chaos) " ) ;
m_OpenFileDialog - > setWindowTitle ( " Open Flame " ) ;
m_OpenFileDialog - > setSidebarUrls ( m_Urls ) ;
}
QStringList filenames ;
if ( openExamples )
{
m_OpenFileDialog - > selectFile ( " * " ) ;
# ifndef _WIN32
if ( QDir ( " /usr/share/fractorium/examples " ) . exists ( ) )
m_OpenFileDialog - > setDirectory ( " /usr/share/fractorium/examples " ) ;
else
m_OpenFileDialog - > setDirectory ( QCoreApplication : : applicationDirPath ( ) + " /examples " ) ;
# else
m_OpenFileDialog - > setDirectory ( QCoreApplication : : applicationDirPath ( ) + " /examples " ) ;
# endif
m_OpenFileDialog - > selectNameFilter ( " flame (*.flame) " ) ;
}
else
{
m_OpenFileDialog - > setDirectory ( m_Settings - > OpenFolder ( ) ) ;
m_OpenFileDialog - > selectNameFilter ( m_Settings - > OpenXmlExt ( ) ) ;
}
if ( m_OpenFileDialog - > exec ( ) = = QDialog : : Accepted )
{
filenames = m_OpenFileDialog - > selectedFiles ( ) ;
if ( ! openExamples & & ! filenames . empty ( ) )
m_Settings - > OpenFolder ( QFileInfo ( filenames [ 0 ] ) . canonicalPath ( ) ) ;
else
m_OpenFileDialog - > selectFile ( " * " ) ;
}
# else
QString defaultFilter ;
QStringList filenames ;
if ( openExamples )
{
defaultFilter = " flame (*.flame) " ;
filenames = QFileDialog : : getOpenFileNames ( this , tr ( " Open Flame " ) , QCoreApplication : : applicationDirPath ( ) + " /Examples " , tr ( " flame(*.flame) " ) , & defaultFilter ) ;
}
else
{
defaultFilter = m_Settings - > OpenXmlExt ( ) ;
filenames = QFileDialog : : getOpenFileNames ( this , tr ( " Open Flame " ) , m_Settings - > OpenFolder ( ) , tr ( " flam3(*.flam3);; flame(*.flame);; xml(*.xml);; chaos (*.chaos) " ) , & defaultFilter ) ;
m_Settings - > OpenXmlExt ( defaultFilter ) ;
if ( ! filenames . empty ( ) )
m_Settings - > OpenFolder ( QFileInfo ( filenames [ 0 ] ) . canonicalPath ( ) ) ;
}
# endif
return filenames ;
}
/// <summary>
/// Setup and show the save XML dialog.
/// This will perform lazy instantiation.
/// </summary>
/// <param name="defaultFilename">The default filename to populate the text box with</param>
/// <returns>The filename selected</returns>
QString Fractorium : : SetupSaveXmlDialog ( const QString & defaultFilename )
{
# ifndef __APPLE__
//Lazy instantiate since it takes a long time.
//QS
if ( ! m_SaveFileDialog . get ( ) )
{
m_SaveFileDialog = std : : make_unique < QFileDialog > ( this ) ;
m_SaveFileDialog - > setViewMode ( QFileDialog : : List ) ;
m_SaveFileDialog - > setFileMode ( QFileDialog : : FileMode : : AnyFile ) ;
m_SaveFileDialog - > setOption ( QFileDialog : : DontUseNativeDialog , true ) ;
//This must be done once here because clears various internal states which allow the file text to be properly set.
//This is most likely a bug in QFileDialog.
m_SaveFileDialog - > setAcceptMode ( QFileDialog : : AcceptSave ) ;
connect ( m_SaveFileDialog . get ( ) , & QFileDialog : : filterSelected , [ & ] ( const QString & filter )
{
m_Settings - > SaveXmlExt ( filter ) ;
m_SaveFileDialog - > setDefaultSuffix ( filter ) ;
} ) ;
m_SaveFileDialog - > setNameFilter ( " flam3 (*.flam3);;flame (*.flame);;xml (*.xml) " ) ;
m_SaveFileDialog - > setWindowTitle ( " Save flame as xml " ) ;
m_SaveFileDialog - > setSidebarUrls ( m_Urls ) ;
}
QString filename ;
m_SaveFileDialog - > selectFile ( defaultFilename ) ;
m_SaveFileDialog - > setDirectory ( m_Settings - > SaveFolder ( ) ) ;
m_SaveFileDialog - > selectNameFilter ( m_Settings - > SaveXmlExt ( ) ) ;
m_SaveFileDialog - > setDefaultSuffix ( m_Settings - > SaveXmlExt ( ) ) ;
if ( m_SaveFileDialog - > exec ( ) = = QDialog : : Accepted )
{
filename = m_SaveFileDialog - > selectedFiles ( ) . value ( 0 ) ;
auto filenames = filename . split ( " (* " ) ; //This is a total hack, but Qt has the unfortunate behavior of including the description with the extension. It's probably a bug.
filename = filenames [ 0 ] ;
m_Settings - > SaveXmlExt ( m_SaveFileDialog - > selectedNameFilter ( ) ) ;
}
# else
auto defaultFilter ( m_Settings - > SaveXmlExt ( ) ) ;
auto filename = QFileDialog : : getSaveFileName ( this , tr ( " Save flame as xml " ) , m_Settings - > SaveFolder ( ) + " / " + defaultFilename , tr ( " flam3 (*.flam3);;flame (*.flame);;xml (*.xml) " ) , & defaultFilter ) ;
m_Settings - > SaveXmlExt ( defaultFilter ) ;
# endif
return filename ;
}
/// <summary>
/// Setup and show the save image dialog.
/// This will perform lazy instantiation.
/// </summary>
/// <param name="defaultFilename">The default filename to populate the text box with</param>
/// <returns>The filename selected</returns>
QString Fractorium : : SetupSaveImageDialog ( const QString & defaultFilename )
{
# ifndef __APPLE__
//Lazy instantiate since it takes a long time.
if ( ! m_SaveImageDialog . get ( ) )
{
m_SaveImageDialog = std : : make_unique < QFileDialog > ( this ) ;
m_SaveImageDialog - > setViewMode ( QFileDialog : : List ) ;
m_SaveImageDialog - > setFileMode ( QFileDialog : : FileMode : : AnyFile ) ;
m_SaveImageDialog - > setOption ( QFileDialog : : DontUseNativeDialog , true ) ;
//This must be done once here because clears various internal states which allow the file text to be properly set.
//This is most likely a bug in QFileDialog.
m_SaveImageDialog - > setAcceptMode ( QFileDialog : : AcceptSave ) ;
connect ( m_SaveImageDialog . get ( ) , & QFileDialog : : filterSelected , [ & ] ( const QString & filter )
{
m_Settings - > SaveImageExt ( filter ) ;
m_SaveImageDialog - > setDefaultSuffix ( filter ) ;
} ) ;
# ifdef _WIN32
m_SaveImageDialog - > setNameFilter ( " .bmp;;.jpg;;.png;;.exr " ) ;
# else
m_SaveImageDialog - > setNameFilter ( " .jpg;;.png;;.exr " ) ;
# endif
m_SaveImageDialog - > setWindowTitle ( " Save image " ) ;
m_SaveImageDialog - > setSidebarUrls ( m_Urls ) ;
}
QString filename ;
m_SaveImageDialog - > selectFile ( defaultFilename ) ;
m_SaveImageDialog - > setDirectory ( m_Settings - > SaveFolder ( ) ) ;
m_SaveImageDialog - > selectNameFilter ( m_Settings - > SaveImageExt ( ) ) ;
m_SaveImageDialog - > setDefaultSuffix ( m_Settings - > SaveImageExt ( ) ) ;
if ( m_SaveImageDialog - > exec ( ) = = QDialog : : Accepted )
filename = m_SaveImageDialog - > selectedFiles ( ) . value ( 0 ) ;
# else
auto defaultFilter ( m_Settings - > SaveImageExt ( ) ) ;
auto filename = QFileDialog : : getSaveFileName ( this , tr ( " Save image " ) , m_Settings - > SaveFolder ( ) + " / " + defaultFilename , tr ( " Jpg (*.jpg);;Png (*.png);;Exr (*.exr) " ) , & defaultFilter ) ;
m_Settings - > SaveImageExt ( defaultFilter ) ;
# endif
return filename ;
}
/// <summary>
/// Setup and show the save folder dialog.
/// This will perform lazy instantiation.
/// </summary>
/// <returns>The folder selected, with '/' appended to the end</returns>
QString Fractorium : : SetupSaveFolderDialog ( )
{
# ifndef __APPLE__
//Lazy instantiate since it takes a long time.
if ( ! m_FolderDialog . get ( ) )
{
m_FolderDialog = std : : make_unique < QFileDialog > ( this ) ;
m_FolderDialog - > setViewMode ( QFileDialog : : List ) ;
m_FolderDialog - > setOption ( QFileDialog : : DontUseNativeDialog , true ) ;
//This must come first because it clears various internal states which allow the file text to be properly set.
//This is most likely a bug in QFileDialog.
m_FolderDialog - > setAcceptMode ( QFileDialog : : AcceptSave ) ;
m_FolderDialog - > setFileMode ( QFileDialog : : Directory ) ;
m_FolderDialog - > setOption ( QFileDialog : : ShowDirsOnly , true ) ;
m_FolderDialog - > setWindowTitle ( " Save to folder " ) ;
m_FolderDialog - > setSidebarUrls ( m_Urls ) ;
}
QString filename ;
m_FolderDialog - > selectFile ( " " ) ;
m_FolderDialog - > setDirectory ( m_Settings - > SaveFolder ( ) ) ;
if ( m_FolderDialog - > exec ( ) = = QDialog : : Accepted )
{
filename = MakeEnd ( m_FolderDialog - > selectedFiles ( ) . value ( 0 ) , ' / ' ) ;
}
# else
auto filename = QFileDialog : : getExistingDirectory ( this , tr ( " Save to folder " ) ,
m_Settings - > SaveFolder ( ) ,
QFileDialog : : ShowDirsOnly | QFileDialog : : DontResolveSymlinks ) ;
if ( filename . size ( ) > 0 )
filename = MakeEnd ( filename , ' / ' ) ;
# endif
return filename ;
}
/// <summary>
/// Setup the final render dialog.
/// Note this deletes the existing instance before creating the new one.
/// This must be called every time the final render dialog is shown because
/// there are problems with reusing it.
/// </summary>
/// <returns>True if created successfully, else false</returns>
bool Fractorium : : SetupFinalRenderDialog ( )
{
if ( m_FinalRenderDialog = std : : make_unique < FractoriumFinalRenderDialog > ( this ) )
{
connect ( m_FinalRenderDialog . get ( ) , SIGNAL ( finished ( int ) ) , this , SLOT ( OnFinalRenderClose ( int ) ) , Qt : : QueuedConnection ) ;
return true ;
}
return false ;
}
/// <summary>
/// Thin wrapper around QMessageBox::critical() to allow it to be invoked from another thread.
/// </summary>
/// <param name="title">The title of the message box</param>
/// <param name="text">The text displayed on the message box</param>
/// <param name="invokeRequired">True if running on another thread, else false. Default: false.</param>
void Fractorium : : ShowCritical ( const QString & title , const QString & text , bool invokeRequired )
{
if ( ! invokeRequired )
QMessageBox : : critical ( this , title , text ) ;
else
QMetaObject : : invokeMethod ( this , " ShowCritical " , Qt : : QueuedConnection , Q_ARG ( const QString & , title ) , Q_ARG ( const QString & , text ) , Q_ARG ( bool , false ) ) ;
}
/// <summary>
/// Explicitly set the tab orders for the entire program.
/// Qt has a facility to do this, but it fails when using custom widgets in
/// tables, so it must be done manually here.
/// This list must be kept in sync with any UI changes.
/// </summary>
void Fractorium : : SetTabOrders ( )
{
QWidget * w = SetTabOrder ( this , ui . ColorTable , m_BrightnessSpin ) ; //Flame color.
w = SetTabOrder ( this , w , m_GammaSpin ) ;
w = SetTabOrder ( this , w , m_GammaThresholdSpin ) ;
w = SetTabOrder ( this , w , m_VibrancySpin ) ;
w = SetTabOrder ( this , w , m_HighlightSpin ) ;
w = SetTabOrder ( this , w , m_BackgroundColorButton ) ;
w = SetTabOrder ( this , w , m_PaletteModeCombo ) ;
w = SetTabOrder ( this , w , m_WidthSpin ) ; //Flame geometry.
w = SetTabOrder ( this , w , m_HeightSpin ) ;
w = SetTabOrder ( this , w , m_CenterXSpin ) ;
w = SetTabOrder ( this , w , m_CenterYSpin ) ;
w = SetTabOrder ( this , w , m_ScaleSpin ) ;
w = SetTabOrder ( this , w , m_ZoomSpin ) ;
w = SetTabOrder ( this , w , m_RotateSpin ) ;
w = SetTabOrder ( this , w , m_ZPosSpin ) ;
w = SetTabOrder ( this , w , m_PerspectiveSpin ) ;
w = SetTabOrder ( this , w , m_PitchSpin ) ;
w = SetTabOrder ( this , w , m_YawSpin ) ;
w = SetTabOrder ( this , w , m_DepthBlurSpin ) ;
w = SetTabOrder ( this , w , m_BlurCurveSpin ) ;
w = SetTabOrder ( this , w , m_SpatialFilterWidthSpin ) ; //Flame filter.
w = SetTabOrder ( this , w , m_SpatialFilterTypeCombo ) ;
w = SetTabOrder ( this , w , m_DEFilterMinRadiusSpin ) ;
w = SetTabOrder ( this , w , m_DEFilterMaxRadiusSpin ) ;
w = SetTabOrder ( this , w , m_DECurveSpin ) ;
w = SetTabOrder ( this , w , m_SbsSpin ) ; //Flame iteration.
w = SetTabOrder ( this , w , m_FuseSpin ) ;
w = SetTabOrder ( this , w , m_RandRangeSpin ) ;
w = SetTabOrder ( this , w , m_QualitySpin ) ;
w = SetTabOrder ( this , w , m_SupersampleSpin ) ;
w = SetTabOrder ( this , w , m_InterpTypeCombo ) ; //Flame animation.
w = SetTabOrder ( this , w , m_AffineInterpTypeCombo ) ;
w = SetTabOrder ( this , w , m_TemporalFilterWidthSpin ) ;
w = SetTabOrder ( this , w , m_TemporalFilterTypeCombo ) ;
w = SetTabOrder ( this , w , m_TemporalFilterExpSpin ) ;
w = SetTabOrder ( this , ui . LibraryTree , ui . SequenceStartCountSpinBox ) ; //Library.
w = SetTabOrder ( this , w , ui . SequenceStartPreviewsButton ) ;
w = SetTabOrder ( this , w , ui . SequenceStopPreviewsButton ) ;
w = SetTabOrder ( this , w , ui . SequenceStartFlameSpinBox ) ;
w = SetTabOrder ( this , w , ui . SequenceStopFlameSpinBox ) ;
w = SetTabOrder ( this , w , ui . SequenceAllButton ) ;
w = SetTabOrder ( this , w , ui . SequenceRandomizeStaggerCheckBox ) ;
w = SetTabOrder ( this , w , ui . SequenceStaggerSpinBox ) ;
w = SetTabOrder ( this , w , ui . SequenceRandomStaggerMaxSpinBox ) ;
w = SetTabOrder ( this , w , ui . SequenceRandomizeRotationsCheckBox ) ;
w = SetTabOrder ( this , w , ui . SequenceRotationsSpinBox ) ;
w = SetTabOrder ( this , w , ui . SequenceRotationsCWCheckBox ) ;
w = SetTabOrder ( this , w , ui . SequenceRandomRotationsMaxSpinBox ) ;
w = SetTabOrder ( this , w , ui . SequenceRandomizeFramesPerRotCheckBox ) ;
w = SetTabOrder ( this , w , ui . SequenceFramesPerRotSpinBox ) ;
w = SetTabOrder ( this , w , ui . SequenceRandomFramesPerRotMaxSpinBox ) ;
w = SetTabOrder ( this , w , ui . SequenceRandomizeBlendFramesCheckBox ) ;
w = SetTabOrder ( this , w , ui . SequenceBlendFramesSpinBox ) ;
w = SetTabOrder ( this , w , ui . SequenceRandomBlendMaxFramesSpinBox ) ;
w = SetTabOrder ( this , w , ui . SequenceRandomizeRotationsPerBlendCheckBox ) ;
w = SetTabOrder ( this , w , ui . SequenceRotationsPerBlendSpinBox ) ;
w = SetTabOrder ( this , w , ui . SequenceRotationsPerBlendCWCheckBox ) ;
w = SetTabOrder ( this , w , ui . SequenceRotationsPerBlendMaxSpinBox ) ;
w = SetTabOrder ( this , w , ui . SequenceLinearCheckBox ) ;
w = SetTabOrder ( this , w , ui . SequenceAnimationFpsSpinBox ) ;
w = SetTabOrder ( this , w , ui . SequenceGenerateButton ) ;
w = SetTabOrder ( this , w , ui . SequenceRenderButton ) ;
w = SetTabOrder ( this , w , ui . SequenceSaveButton ) ;
w = SetTabOrder ( this , w , ui . SequenceOpenButton ) ;
w = SetTabOrder ( this , w , ui . SequenceAnimateButton ) ;
w = SetTabOrder ( this , w , ui . SequenceClearButton ) ;
w = SetTabOrder ( this , w , ui . SequenceTree ) ;
w = SetTabOrder ( this , ui . CurrentXformCombo , ui . AddXformButton ) ; //Xforms.
w = SetTabOrder ( this , w , ui . AddLinkedXformButton ) ;
w = SetTabOrder ( this , w , ui . DuplicateXformButton ) ;
w = SetTabOrder ( this , w , ui . ClearXformButton ) ;
w = SetTabOrder ( this , w , ui . DeleteXformButton ) ;
w = SetTabOrder ( this , w , ui . AddFinalXformButton ) ;
w = SetTabOrder ( this , w , m_XformWeightSpin ) ;
w = SetTabOrder ( this , w , m_XformWeightSpinnerButtonWidget - > m_Button ) ;
w = SetTabOrder ( this , w , m_XformNameEdit ) ;
w = SetTabOrder ( this , m_XformColorIndexSpin , ui . XformColorScroll ) ; //Xforms color.
w = SetTabOrder ( this , w , ui . RandomColorIndicesButton ) ;
w = SetTabOrder ( this , w , ui . ToggleColorIndicesButton ) ;
w = SetTabOrder ( this , w , m_XformColorSpeedSpin ) ;
w = SetTabOrder ( this , w , m_XformOpacitySpin ) ;
w = SetTabOrder ( this , w , m_XformDirectColorSpin ) ;
w = SetTabOrder ( this , w , ui . SoloXformCheckBox ) ;
w = SetTabOrder ( this , w , m_PreX1Spin ) ;
w = SetTabOrder ( this , w , m_PreX2Spin ) ;
w = SetTabOrder ( this , w , m_PreY1Spin ) ;
w = SetTabOrder ( this , w , m_PreY2Spin ) ;
w = SetTabOrder ( this , w , m_PreO1Spin ) ;
w = SetTabOrder ( this , w , m_PreO2Spin ) ;
w = SetTabOrder ( this , w , ui . PreCopyButton ) ;
w = SetTabOrder ( this , w , ui . PreFlipVerticalButton ) ;
w = SetTabOrder ( this , w , ui . PreResetButton ) ;
w = SetTabOrder ( this , w , ui . PreFlipHorizontalButton ) ;
w = SetTabOrder ( this , w , ui . PrePasteButton ) ;
w = SetTabOrder ( this , w , ui . PreRotate90CcButton ) ;
w = SetTabOrder ( this , w , ui . PreRotateCcButton ) ;
w = SetTabOrder ( this , w , ui . PreRotateCombo ) ;
w = SetTabOrder ( this , w , ui . PreRotateCButton ) ;
w = SetTabOrder ( this , w , ui . PreRotate90CButton ) ;
w = SetTabOrder ( this , w , ui . PreMoveUpButton ) ;
w = SetTabOrder ( this , w , ui . PreMoveDownButton ) ;
w = SetTabOrder ( this , w , ui . PreMoveCombo ) ;
w = SetTabOrder ( this , w , ui . PreMoveLeftButton ) ;
w = SetTabOrder ( this , w , ui . PreMoveRightButton ) ;
w = SetTabOrder ( this , w , ui . PreScaleDownButton ) ;
w = SetTabOrder ( this , w , ui . PreScaleCombo ) ;
w = SetTabOrder ( this , w , ui . PreScaleUpButton ) ;
w = SetTabOrder ( this , w , ui . PreRandomButton ) ;
w = SetTabOrder ( this , w , ui . SwapAffinesButton ) ;
w = SetTabOrder ( this , w , ui . PostAffineGroupBox ) ;
w = SetTabOrder ( this , w , m_PostX1Spin ) ;
w = SetTabOrder ( this , w , m_PostX2Spin ) ;
w = SetTabOrder ( this , w , m_PostY1Spin ) ;
w = SetTabOrder ( this , w , m_PostY2Spin ) ;
w = SetTabOrder ( this , w , m_PostO1Spin ) ;
w = SetTabOrder ( this , w , m_PostO2Spin ) ;
w = SetTabOrder ( this , w , ui . PostCopyButton ) ;
w = SetTabOrder ( this , w , ui . PostFlipVerticalButton ) ;
w = SetTabOrder ( this , w , ui . PostResetButton ) ;
w = SetTabOrder ( this , w , ui . PostFlipHorizontalButton ) ;
w = SetTabOrder ( this , w , ui . PostPasteButton ) ;
w = SetTabOrder ( this , w , ui . PostRotate90CcButton ) ;
w = SetTabOrder ( this , w , ui . PostRotateCcButton ) ;
w = SetTabOrder ( this , w , ui . PostRotateCombo ) ;
w = SetTabOrder ( this , w , ui . PostRotateCButton ) ;
w = SetTabOrder ( this , w , ui . PostRotate90CButton ) ;
w = SetTabOrder ( this , w , ui . PostMoveUpButton ) ;
w = SetTabOrder ( this , w , ui . PostMoveDownButton ) ;
w = SetTabOrder ( this , w , ui . PostMoveCombo ) ;
w = SetTabOrder ( this , w , ui . PostMoveLeftButton ) ;
w = SetTabOrder ( this , w , ui . PostMoveRightButton ) ;
w = SetTabOrder ( this , w , ui . PostScaleDownButton ) ;
w = SetTabOrder ( this , w , ui . PostScaleCombo ) ;
w = SetTabOrder ( this , w , ui . PostScaleUpButton ) ;
w = SetTabOrder ( this , w , ui . PostRandomButton ) ;
w = SetTabOrder ( this , w , ui . PolarAffineCheckBox ) ;
w = SetTabOrder ( this , w , ui . LocalPivotRadio ) ;
w = SetTabOrder ( this , w , ui . WorldPivotRadio ) ;
w = SetTabOrder ( this , ui . VariationsFilterLineEdit , ui . VariationsFilterClearButton ) ; //Xforms variation.
w = SetTabOrder ( this , w , ui . VariationsTree ) ;
w = SetTabOrder ( this , w , ui . ClearXaosButton ) ;
w = SetTabOrder ( this , w , ui . RandomXaosButton ) ;
w = SetTabOrder ( this , w , ui . AddLayerButton ) ;
w = SetTabOrder ( this , w , ui . AddLayerSpinBox ) ;
w = SetTabOrder ( this , w , ui . TransposeXaosButton ) ;
//Xforms xaos is done dynamically every time.
w = SetTabOrder ( this , ui . PaletteFilenameCombo , m_PaletteHueSpin ) ; //Palette.
w = SetTabOrder ( this , w , m_PaletteContrastSpin ) ;
w = SetTabOrder ( this , w , m_PaletteSaturationSpin ) ;
w = SetTabOrder ( this , w , m_PaletteBlurSpin ) ;
w = SetTabOrder ( this , w , m_PaletteBrightnessSpin ) ;
w = SetTabOrder ( this , w , m_PaletteFrequencySpin ) ;
w = SetTabOrder ( this , w , ui . PaletteRandomSelectButton ) ;
w = SetTabOrder ( this , w , ui . PaletteRandomAdjustButton ) ;
w = SetTabOrder ( this , w , ui . PaletteEditorButton ) ;
w = SetTabOrder ( this , w , ui . PaletteFilterLineEdit ) ;
w = SetTabOrder ( this , w , ui . PaletteFilterClearButton ) ;
w = SetTabOrder ( this , w , ui . PaletteListTable ) ;
w = SetTabOrder ( this , w , ui . ResetCurvesButton ) ; //Palette curves.
w = SetTabOrder ( this , w , ui . CurvesView ) ;
w = SetTabOrder ( this , w , ui . CurvesGroupBox ) ;
w = SetTabOrder ( this , w , ui . CurvesAllRadio ) ;
w = SetTabOrder ( this , w , ui . CurvesRedRadio ) ;
w = SetTabOrder ( this , w , ui . CurvesGreenRadio ) ;
w = SetTabOrder ( this , w , ui . CurvesBlueRadio ) ;
w = SetTabOrder ( this , ui . SummaryTable , ui . SummaryTree ) ; //Info summary.
w = SetTabOrder ( this , ui . InfoBoundsGroupBox , ui . InfoBoundsFrame ) ; //Info bounds.
w = SetTabOrder ( this , w , ui . InfoBoundsTable ) ;
w = SetTabOrder ( this , w , ui . InfoFileOpeningGroupBox ) ;
w = SetTabOrder ( this , w , ui . InfoFileOpeningTextEdit ) ;
w = SetTabOrder ( this , w , ui . InfoRenderingGroupBox ) ;
w = SetTabOrder ( this , w , ui . InfoRenderingTextEdit ) ;
}
/// <summary>
/// Toggle all table spinner values in one row.
/// The logic is:
/// If any cell in the row is non zero, set all cells to zero, else 1.
/// If shift is held down, reverse the logic.
/// If ctrl is held down, set each cell to a random 0 or 1.
/// Resets the rendering process.
/// </summary>
/// <param name="table">The QTableWidget or QTableView whose row will be toggled</param>
/// <param name="logicalIndex">The index of the row that was double clicked</param>
void Fractorium : : ToggleTableRow ( QTableView * table , int logicalIndex )
{
auto allZero = true ;
const auto model = table - > model ( ) ;
const auto cols = model - > columnCount ( ) ;
const auto shift = QGuiApplication : : keyboardModifiers ( ) . testFlag ( Qt : : ShiftModifier ) ;
const auto ctrl = QGuiApplication : : keyboardModifiers ( ) . testFlag ( Qt : : ControlModifier ) ;
const auto tableWidget = qobject_cast < QTableWidget * > ( table ) ;
if ( tableWidget )
{
for ( int i = 0 ; i < cols ; i + + )
{
if ( auto spinBox = qobject_cast < DoubleSpinBox * > ( tableWidget - > cellWidget ( logicalIndex , i ) ) )
{
if ( ! IsNearZero ( spinBox - > value ( ) ) )
{
allZero = false ;
break ;
}
}
}
if ( shift )
allZero = ! allZero ;
const auto val = allZero ? 1.0 : 0.0 ;
for ( int i = 0 ; i < cols ; i + + )
if ( auto spinBox = qobject_cast < DoubleSpinBox * > ( tableWidget - > cellWidget ( logicalIndex , i ) ) )
if ( ctrl )
spinBox - > setValue ( static_cast < double > ( QTIsaac < ISAAC_SIZE , ISAAC_INT > : : LockedRandBit ( ) ) ) ;
else
spinBox - > setValue ( val ) ;
}
else
{
for ( int i = 0 ; i < cols ; i + + )
{
if ( ! IsNearZero ( model - > data ( model - > index ( logicalIndex , i , QModelIndex ( ) ) ) . toDouble ( ) ) )
{
allZero = false ;
break ;
}
}
if ( shift )
allZero = ! allZero ;
const auto val = allZero ? 1.0 : 0.0 ;
for ( int i = 0 ; i < cols ; i + + )
if ( ctrl )
model - > setData ( model - > index ( logicalIndex , i ) , double ( QTIsaac < ISAAC_SIZE , ISAAC_INT > : : LockedRandBit ( ) ) , Qt : : EditRole ) ;
else
model - > setData ( model - > index ( logicalIndex , i ) , val , Qt : : EditRole ) ;
}
}
/// <summary>
/// Toggle all table spinner values in one column.
/// The logic is:
/// If any cell in the column is non zero, set all cells to zero, else 1.
/// If shift is held down, reverse the logic.
/// If ctrl is held down, set each cell to a random 0 or 1.
/// Resets the rendering process.
/// </summary>
/// <param name="table">The QTableWidget or QTableView whose column will be toggled</param>
/// <param name="logicalIndex">The index of the column that was double clicked</param>
void Fractorium : : ToggleTableCol ( QTableView * table , int logicalIndex )
{
auto allZero = true ;
const auto model = table - > model ( ) ;
const auto rows = model - > rowCount ( ) ;
const auto shift = QGuiApplication : : keyboardModifiers ( ) . testFlag ( Qt : : ShiftModifier ) ;
const auto ctrl = QGuiApplication : : keyboardModifiers ( ) . testFlag ( Qt : : ControlModifier ) ;
const auto tableWidget = qobject_cast < QTableWidget * > ( table ) ;
if ( tableWidget )
{
for ( int i = 0 ; i < rows ; i + + )
{
if ( auto spinBox = qobject_cast < DoubleSpinBox * > ( tableWidget - > cellWidget ( i , logicalIndex ) ) )
{
if ( ! IsNearZero ( spinBox - > value ( ) ) )
{
allZero = false ;
break ;
}
}
}
if ( shift )
allZero = ! allZero ;
const auto val = allZero ? 1.0 : 0.0 ;
for ( int i = 0 ; i < rows ; i + + )
if ( auto spinBox = qobject_cast < DoubleSpinBox * > ( tableWidget - > cellWidget ( i , logicalIndex ) ) )
if ( ctrl )
spinBox - > setValue ( static_cast < double > ( QTIsaac < ISAAC_SIZE , ISAAC_INT > : : LockedRandBit ( ) ) ) ;
else
spinBox - > setValue ( val ) ;
}
else
{
for ( int i = 0 ; i < rows ; i + + )
{
if ( ! IsNearZero ( model - > data ( model - > index ( i , logicalIndex , QModelIndex ( ) ) ) . toDouble ( ) ) )
{
allZero = false ;
break ;
}
}
if ( shift )
allZero = ! allZero ;
const auto val = allZero ? 1.0 : 0.0 ;
for ( int i = 0 ; i < rows ; i + + )
if ( ctrl )
model - > setData ( model - > index ( i , logicalIndex ) , double ( QTIsaac < ISAAC_SIZE , ISAAC_INT > : : LockedRandBit ( ) ) , Qt : : EditRole ) ;
else
model - > setData ( model - > index ( i , logicalIndex ) , val , Qt : : EditRole ) ;
}
}
template class FractoriumEmberController < float > ;
# ifdef DO_DOUBLE
template class FractoriumEmberController < double > ;
# endif