2018-09-29 20:13:07 +01:00
# include "tileseteditor.h"
# include "ui_tileseteditor.h"
2018-12-20 23:30:35 +00:00
# include "log.h"
2018-09-30 20:54:38 +01:00
# include "imageproviders.h"
2019-01-09 00:04:41 +00:00
# include "metatileparser.h"
2019-01-07 15:12:01 +00:00
# include "paletteparser.h"
2019-01-11 14:52:47 +00:00
# include "imageexport.h"
2018-10-03 01:01:24 +01:00
# include <QFileDialog>
# include <QMessageBox>
2018-10-03 01:01:37 +01:00
# include <QDialogButtonBox>
2018-10-08 04:17:20 +01:00
# include <QCloseEvent>
2019-01-07 15:31:48 +00:00
# include <QImageReader>
2018-09-29 20:13:07 +01:00
2018-09-29 21:24:35 +01:00
TilesetEditor : : TilesetEditor ( Project * project , QString primaryTilesetLabel , QString secondaryTilesetLabel , QWidget * parent ) :
2018-09-29 20:13:07 +01:00
QMainWindow ( parent ) ,
ui ( new Ui : : TilesetEditor )
{
2018-10-03 01:01:31 +01:00
this - > init ( project , primaryTilesetLabel , secondaryTilesetLabel ) ;
}
TilesetEditor : : ~ TilesetEditor ( )
{
delete ui ;
2018-12-26 22:49:43 +00:00
delete metatileSelector ;
delete tileSelector ;
delete metatileLayersItem ;
delete paletteEditor ;
delete metatile ;
delete primaryTileset ;
delete secondaryTileset ;
delete metatilesScene ;
delete tilesScene ;
delete selectedTilePixmapItem ;
delete selectedTileScene ;
delete metatileLayersScene ;
2018-10-03 01:01:31 +01:00
}
void TilesetEditor : : init ( Project * project , QString primaryTilesetLabel , QString secondaryTilesetLabel ) {
2018-09-29 20:13:07 +01:00
ui - > setupUi ( this ) ;
this - > project = project ;
2018-10-03 01:01:31 +01:00
this - > hasUnsavedChanges = false ;
2018-09-30 20:54:38 +01:00
this - > tileXFlip = ui - > checkBox_xFlip - > isChecked ( ) ;
this - > tileYFlip = ui - > checkBox_yFlip - > isChecked ( ) ;
this - > paletteId = ui - > spinBox_paletteSelector - > value ( ) ;
2018-10-03 01:01:15 +01:00
Tileset * primaryTileset = project - > getTileset ( primaryTilesetLabel ) ;
Tileset * secondaryTileset = project - > getTileset ( secondaryTilesetLabel ) ;
2018-10-03 01:01:31 +01:00
if ( this - > primaryTileset ) delete this - > primaryTileset ;
if ( this - > secondaryTileset ) delete this - > secondaryTileset ;
2018-10-03 01:01:15 +01:00
this - > primaryTileset = primaryTileset - > copy ( ) ;
this - > secondaryTileset = secondaryTileset - > copy ( ) ;
2018-09-29 21:24:35 +01:00
2018-10-03 01:01:09 +01:00
QList < QString > sortedBehaviors ;
for ( int num : project - > metatileBehaviorMapInverse . keys ( ) ) {
this - > ui - > comboBox_metatileBehaviors - > addItem ( project - > metatileBehaviorMapInverse [ num ] , num ) ;
}
this - > ui - > comboBox_layerType - > addItem ( " Normal - Middle/Top " , 0 ) ;
this - > ui - > comboBox_layerType - > addItem ( " Covered - Bottom/Middle " , 1 ) ;
this - > ui - > comboBox_layerType - > addItem ( " Split - Bottom/Top " , 2 ) ;
this - > ui - > spinBox_paletteSelector - > setMinimum ( 0 ) ;
this - > ui - > spinBox_paletteSelector - > setMaximum ( Project : : getNumPalettesTotal ( ) - 1 ) ;
2018-09-30 20:54:38 +01:00
this - > initMetatileSelector ( ) ;
2018-09-30 22:15:04 +01:00
this - > initMetatileLayersItem ( ) ;
2018-09-30 20:54:38 +01:00
this - > initTileSelector ( ) ;
this - > initSelectedTileItem ( ) ;
2018-09-30 22:15:04 +01:00
this - > metatileSelector - > select ( 0 ) ;
2018-10-06 23:07:36 +01:00
MetatileHistoryItem * commit = new MetatileHistoryItem ( 0 , nullptr , this - > metatile - > copy ( ) ) ;
metatileHistory . push ( commit ) ;
2018-09-29 20:13:07 +01:00
}
2018-10-03 01:01:18 +01:00
void TilesetEditor : : setTilesets ( QString primaryTilesetLabel , QString secondaryTilesetLabel ) {
delete this - > primaryTileset ;
delete this - > secondaryTileset ;
Tileset * primaryTileset = project - > getTileset ( primaryTilesetLabel ) ;
Tileset * secondaryTileset = project - > getTileset ( secondaryTilesetLabel ) ;
this - > primaryTileset = primaryTileset - > copy ( ) ;
this - > secondaryTileset = secondaryTileset - > copy ( ) ;
2018-10-03 01:01:24 +01:00
this - > refresh ( ) ;
}
2018-10-03 01:01:18 +01:00
2018-10-03 01:01:24 +01:00
void TilesetEditor : : refresh ( ) {
2019-01-07 15:12:01 +00:00
this - > metatileLayersItem - > setTilesets ( this - > primaryTileset , this - > secondaryTileset ) ;
this - > tileSelector - > setTilesets ( this - > primaryTileset , this - > secondaryTileset ) ;
2018-10-03 01:01:18 +01:00
this - > metatileSelector - > setTilesets ( this - > primaryTileset , this - > secondaryTileset ) ;
2019-01-06 16:38:47 +00:00
this - > metatileSelector - > select ( this - > metatileSelector - > getSelectedMetatile ( ) ) ;
2018-10-03 01:01:27 +01:00
this - > drawSelectedTiles ( ) ;
2018-10-03 01:01:21 +01:00
this - > ui - > graphicsView_Tiles - > setSceneRect ( 0 , 0 , this - > tileSelector - > pixmap ( ) . width ( ) + 2 , this - > tileSelector - > pixmap ( ) . height ( ) + 2 ) ;
this - > ui - > graphicsView_Tiles - > setFixedSize ( this - > tileSelector - > pixmap ( ) . width ( ) + 2 , this - > tileSelector - > pixmap ( ) . height ( ) + 2 ) ;
this - > ui - > graphicsView_Metatiles - > setSceneRect ( 0 , 0 , this - > metatileSelector - > pixmap ( ) . width ( ) + 2 , this - > metatileSelector - > pixmap ( ) . height ( ) + 2 ) ;
this - > ui - > graphicsView_Metatiles - > setFixedSize ( this - > metatileSelector - > pixmap ( ) . width ( ) + 2 , this - > metatileSelector - > pixmap ( ) . height ( ) + 2 ) ;
2018-10-08 20:42:51 +01:00
this - > ui - > graphicsView_selectedTile - > setFixedSize ( this - > selectedTilePixmapItem - > pixmap ( ) . width ( ) + 2 , this - > selectedTilePixmapItem - > pixmap ( ) . height ( ) + 2 ) ;
2018-10-03 01:01:18 +01:00
}
2018-09-30 18:33:58 +01:00
void TilesetEditor : : initMetatileSelector ( )
2018-09-29 20:13:07 +01:00
{
2018-10-03 01:01:15 +01:00
this - > metatileSelector = new TilesetEditorMetatileSelector ( this - > primaryTileset , this - > secondaryTileset ) ;
2018-09-29 21:24:35 +01:00
connect ( this - > metatileSelector , SIGNAL ( hoveredMetatileChanged ( uint16_t ) ) ,
this , SLOT ( onHoveredMetatileChanged ( uint16_t ) ) ) ;
connect ( this - > metatileSelector , SIGNAL ( hoveredMetatileCleared ( ) ) ,
this , SLOT ( onHoveredMetatileCleared ( ) ) ) ;
connect ( this - > metatileSelector , SIGNAL ( selectedMetatileChanged ( uint16_t ) ) ,
this , SLOT ( onSelectedMetatileChanged ( uint16_t ) ) ) ;
this - > metatilesScene = new QGraphicsScene ;
this - > metatilesScene - > addItem ( this - > metatileSelector ) ;
this - > metatileSelector - > draw ( ) ;
this - > ui - > graphicsView_Metatiles - > setScene ( this - > metatilesScene ) ;
this - > ui - > graphicsView_Metatiles - > setFixedSize ( this - > metatileSelector - > pixmap ( ) . width ( ) + 2 , this - > metatileSelector - > pixmap ( ) . height ( ) + 2 ) ;
}
2018-09-30 18:33:58 +01:00
void TilesetEditor : : initTileSelector ( )
{
2018-10-03 01:01:15 +01:00
this - > tileSelector = new TilesetEditorTileSelector ( this - > primaryTileset , this - > secondaryTileset ) ;
2018-09-30 18:33:58 +01:00
connect ( this - > tileSelector , SIGNAL ( hoveredTileChanged ( uint16_t ) ) ,
this , SLOT ( onHoveredTileChanged ( uint16_t ) ) ) ;
connect ( this - > tileSelector , SIGNAL ( hoveredTileCleared ( ) ) ,
this , SLOT ( onHoveredTileCleared ( ) ) ) ;
2018-10-03 01:01:27 +01:00
connect ( this - > tileSelector , SIGNAL ( selectedTilesChanged ( ) ) ,
this , SLOT ( onSelectedTilesChanged ( ) ) ) ;
2018-09-30 18:33:58 +01:00
this - > tilesScene = new QGraphicsScene ;
this - > tilesScene - > addItem ( this - > tileSelector ) ;
this - > tileSelector - > select ( 0 ) ;
this - > tileSelector - > draw ( ) ;
this - > ui - > graphicsView_Tiles - > setScene ( this - > tilesScene ) ;
this - > ui - > graphicsView_Tiles - > setFixedSize ( this - > tileSelector - > pixmap ( ) . width ( ) + 2 , this - > tileSelector - > pixmap ( ) . height ( ) + 2 ) ;
}
2018-09-30 20:54:38 +01:00
void TilesetEditor : : initSelectedTileItem ( ) {
this - > selectedTileScene = new QGraphicsScene ;
2018-10-03 01:01:27 +01:00
this - > drawSelectedTiles ( ) ;
2018-09-30 20:54:38 +01:00
this - > ui - > graphicsView_selectedTile - > setScene ( this - > selectedTileScene ) ;
2018-10-08 20:42:51 +01:00
this - > ui - > graphicsView_selectedTile - > setFixedSize ( this - > selectedTilePixmapItem - > pixmap ( ) . width ( ) + 2 , this - > selectedTilePixmapItem - > pixmap ( ) . height ( ) + 2 ) ;
2018-09-30 20:54:38 +01:00
}
2018-10-03 01:01:27 +01:00
void TilesetEditor : : drawSelectedTiles ( ) {
2018-09-30 20:54:38 +01:00
if ( ! this - > selectedTileScene ) {
return ;
}
2018-09-30 22:15:04 +01:00
this - > selectedTileScene - > clear ( ) ;
2018-10-06 21:49:26 +01:00
QList < Tile > tiles = this - > tileSelector - > getSelectedTiles ( ) ;
2018-10-03 01:01:27 +01:00
QPoint dimensions = this - > tileSelector - > getSelectionDimensions ( ) ;
2018-10-03 01:01:34 +01:00
QImage selectionImage ( 16 * dimensions . x ( ) , 16 * dimensions . y ( ) , QImage : : Format_RGBA8888 ) ;
2018-10-03 01:01:27 +01:00
QPainter painter ( & selectionImage ) ;
int tileIndex = 0 ;
for ( int j = 0 ; j < dimensions . y ( ) ; j + + ) {
for ( int i = 0 ; i < dimensions . x ( ) ; i + + ) {
2018-10-06 21:49:26 +01:00
QImage tileImage = getColoredTileImage ( tiles . at ( tileIndex ) . tile , this - > primaryTileset , this - > secondaryTileset , tiles . at ( tileIndex ) . palette )
. mirrored ( tiles . at ( tileIndex ) . xflip , tiles . at ( tileIndex ) . yflip )
2018-10-03 01:01:34 +01:00
. scaled ( 16 , 16 ) ;
2018-10-03 01:01:27 +01:00
tileIndex + + ;
2018-10-03 01:01:34 +01:00
painter . drawImage ( i * 16 , j * 16 , tileImage ) ;
2018-10-03 01:01:27 +01:00
}
}
this - > selectedTilePixmapItem = new QGraphicsPixmapItem ( QPixmap : : fromImage ( selectionImage ) ) ;
2018-09-30 20:54:38 +01:00
this - > selectedTileScene - > addItem ( this - > selectedTilePixmapItem ) ;
2018-10-08 20:42:51 +01:00
this - > ui - > graphicsView_selectedTile - > setFixedSize ( this - > selectedTilePixmapItem - > pixmap ( ) . width ( ) + 2 , this - > selectedTilePixmapItem - > pixmap ( ) . height ( ) + 2 ) ;
2018-09-30 20:54:38 +01:00
}
2018-09-30 22:15:04 +01:00
void TilesetEditor : : initMetatileLayersItem ( ) {
2018-10-03 01:01:15 +01:00
Metatile * metatile = Tileset : : getMetatile ( this - > metatileSelector - > getSelectedMetatile ( ) , this - > primaryTileset , this - > secondaryTileset ) ;
this - > metatileLayersItem = new MetatileLayersItem ( metatile , this - > primaryTileset , this - > secondaryTileset ) ;
2018-10-03 01:01:27 +01:00
connect ( this - > metatileLayersItem , SIGNAL ( tileChanged ( int , int ) ) ,
this , SLOT ( onMetatileLayerTileChanged ( int , int ) ) ) ;
2018-10-06 21:49:26 +01:00
connect ( this - > metatileLayersItem , SIGNAL ( selectedTilesChanged ( QPoint , int , int ) ) ,
this , SLOT ( onMetatileLayerSelectionChanged ( QPoint , int , int ) ) ) ;
2018-09-30 22:15:04 +01:00
this - > metatileLayersScene = new QGraphicsScene ;
this - > metatileLayersScene - > addItem ( this - > metatileLayersItem ) ;
this - > ui - > graphicsView_metatileLayers - > setScene ( this - > metatileLayersScene ) ;
}
2018-09-29 21:24:35 +01:00
void TilesetEditor : : onHoveredMetatileChanged ( uint16_t metatileId ) {
QString message = QString ( " Metatile: 0x%1 " )
. arg ( QString ( " %1 " ) . arg ( metatileId , 3 , 16 , QChar ( ' 0 ' ) ) . toUpper ( ) ) ;
this - > ui - > statusbar - > showMessage ( message ) ;
}
void TilesetEditor : : onHoveredMetatileCleared ( ) {
this - > ui - > statusbar - > clearMessage ( ) ;
}
2018-09-30 22:15:04 +01:00
void TilesetEditor : : onSelectedMetatileChanged ( uint16_t metatileId ) {
2018-10-03 01:01:15 +01:00
this - > metatile = Tileset : : getMetatile ( metatileId , this - > primaryTileset , this - > secondaryTileset ) ;
2018-09-30 22:15:04 +01:00
this - > metatileLayersItem - > setMetatile ( metatile ) ;
this - > metatileLayersItem - > draw ( ) ;
2018-10-03 01:01:09 +01:00
this - > ui - > comboBox_metatileBehaviors - > setCurrentIndex ( this - > ui - > comboBox_metatileBehaviors - > findData ( this - > metatile - > behavior ) ) ;
this - > ui - > comboBox_layerType - > setCurrentIndex ( this - > ui - > comboBox_layerType - > findData ( this - > metatile - > layerType ) ) ;
2018-09-29 20:13:07 +01:00
}
2018-09-30 18:33:58 +01:00
void TilesetEditor : : onHoveredTileChanged ( uint16_t tile ) {
QString message = QString ( " Tile: 0x%1 " )
. arg ( QString ( " %1 " ) . arg ( tile , 3 , 16 , QChar ( ' 0 ' ) ) . toUpper ( ) ) ;
this - > ui - > statusbar - > showMessage ( message ) ;
}
void TilesetEditor : : onHoveredTileCleared ( ) {
this - > ui - > statusbar - > clearMessage ( ) ;
}
2018-10-03 01:01:27 +01:00
void TilesetEditor : : onSelectedTilesChanged ( ) {
this - > drawSelectedTiles ( ) ;
}
void TilesetEditor : : onMetatileLayerTileChanged ( int x , int y ) {
2018-10-06 23:07:36 +01:00
Metatile * prevMetatile = this - > metatile - > copy ( ) ;
2018-10-03 01:01:27 +01:00
QPoint dimensions = this - > tileSelector - > getSelectionDimensions ( ) ;
2018-10-06 21:49:26 +01:00
QList < Tile > tiles = this - > tileSelector - > getSelectedTiles ( ) ;
2018-10-03 01:01:27 +01:00
int selectedTileIndex = 0 ;
for ( int j = 0 ; j < dimensions . y ( ) ; j + + ) {
for ( int i = 0 ; i < dimensions . x ( ) ; i + + ) {
int tileIndex = ( ( x + i ) / 2 * 4 ) + ( ( y + j ) * 2 ) + ( ( x + i ) % 2 ) ;
2018-10-09 23:31:12 +01:00
if ( tileIndex < 8 ) {
Tile * tile = & ( * this - > metatile - > tiles ) [ tileIndex ] ;
tile - > tile = tiles . at ( selectedTileIndex ) . tile ;
tile - > xflip = tiles . at ( selectedTileIndex ) . xflip ;
tile - > yflip = tiles . at ( selectedTileIndex ) . yflip ;
tile - > palette = tiles . at ( selectedTileIndex ) . palette ;
}
2018-10-03 01:01:27 +01:00
selectedTileIndex + + ;
}
}
2018-09-30 18:33:58 +01:00
2018-09-30 22:15:04 +01:00
this - > metatileSelector - > draw ( ) ;
this - > metatileLayersItem - > draw ( ) ;
2018-10-03 01:01:31 +01:00
this - > hasUnsavedChanges = true ;
2018-10-06 23:07:36 +01:00
MetatileHistoryItem * commit = new MetatileHistoryItem ( metatileSelector - > getSelectedMetatile ( ) , prevMetatile , this - > metatile - > copy ( ) ) ;
metatileHistory . push ( commit ) ;
2018-09-30 22:15:04 +01:00
}
2018-10-06 21:49:26 +01:00
void TilesetEditor : : onMetatileLayerSelectionChanged ( QPoint selectionOrigin , int width , int height ) {
QList < Tile > tiles ;
int x = selectionOrigin . x ( ) ;
int y = selectionOrigin . y ( ) ;
for ( int j = 0 ; j < height ; j + + ) {
for ( int i = 0 ; i < width ; i + + ) {
int tileIndex = ( ( x + i ) / 2 * 4 ) + ( ( y + j ) * 2 ) + ( ( x + i ) % 2 ) ;
if ( tileIndex < 8 ) {
tiles . append ( this - > metatile - > tiles - > at ( tileIndex ) ) ;
}
}
}
2018-10-08 20:42:51 +01:00
if ( width = = 1 & & height = = 1 ) {
this - > tileSelector - > select ( static_cast < uint16_t > ( tiles [ 0 ] . tile ) ) ;
ui - > spinBox_paletteSelector - > setValue ( tiles [ 0 ] . palette ) ;
QPoint pos = tileSelector - > getTileCoordsOnWidget ( static_cast < uint16_t > ( tiles [ 0 ] . tile ) ) ;
ui - > scrollArea_Tiles - > ensureVisible ( pos . x ( ) , pos . y ( ) ) ;
}
else {
this - > tileSelector - > setExternalSelection ( width , height , tiles ) ;
}
2018-10-06 23:07:36 +01:00
this - > metatileLayersItem - > clearLastModifiedCoords ( ) ;
2018-10-06 21:49:26 +01:00
}
2018-09-30 20:54:38 +01:00
void TilesetEditor : : on_spinBox_paletteSelector_valueChanged ( int paletteId )
{
2018-10-03 01:01:41 +01:00
this - > ui - > spinBox_paletteSelector - > blockSignals ( true ) ;
this - > ui - > spinBox_paletteSelector - > setValue ( paletteId ) ;
this - > ui - > spinBox_paletteSelector - > blockSignals ( false ) ;
2018-09-30 20:54:38 +01:00
this - > paletteId = paletteId ;
this - > tileSelector - > setPaletteId ( paletteId ) ;
2018-10-03 01:01:27 +01:00
this - > drawSelectedTiles ( ) ;
2018-10-03 01:01:41 +01:00
if ( this - > paletteEditor ) {
this - > paletteEditor - > setPaletteId ( paletteId ) ;
}
2018-10-06 23:07:36 +01:00
this - > metatileLayersItem - > clearLastModifiedCoords ( ) ;
2018-09-30 20:54:38 +01:00
}
void TilesetEditor : : on_checkBox_xFlip_stateChanged ( int checked )
{
this - > tileXFlip = checked ;
2018-10-03 01:01:27 +01:00
this - > tileSelector - > setTileFlips ( this - > tileXFlip , this - > tileYFlip ) ;
this - > drawSelectedTiles ( ) ;
2018-10-06 23:07:36 +01:00
this - > metatileLayersItem - > clearLastModifiedCoords ( ) ;
2018-09-30 20:54:38 +01:00
}
void TilesetEditor : : on_checkBox_yFlip_stateChanged ( int checked )
{
this - > tileYFlip = checked ;
2018-10-03 01:01:27 +01:00
this - > tileSelector - > setTileFlips ( this - > tileXFlip , this - > tileYFlip ) ;
this - > drawSelectedTiles ( ) ;
2018-10-06 23:07:36 +01:00
this - > metatileLayersItem - > clearLastModifiedCoords ( ) ;
2018-09-30 18:33:58 +01:00
}
2018-10-03 01:01:09 +01:00
2018-10-06 23:07:36 +01:00
void TilesetEditor : : on_comboBox_metatileBehaviors_activated ( const QString & metatileBehavior )
2018-10-03 01:01:09 +01:00
{
if ( this - > metatile ) {
2018-10-06 23:07:36 +01:00
Metatile * prevMetatile = this - > metatile - > copy ( ) ;
2018-10-03 01:01:09 +01:00
this - > metatile - > behavior = static_cast < uint8_t > ( project - > metatileBehaviorMap [ metatileBehavior ] ) ;
2018-10-06 23:07:36 +01:00
MetatileHistoryItem * commit = new MetatileHistoryItem ( metatileSelector - > getSelectedMetatile ( ) , prevMetatile , this - > metatile - > copy ( ) ) ;
metatileHistory . push ( commit ) ;
2018-10-03 01:01:09 +01:00
}
}
2018-10-06 23:07:36 +01:00
void TilesetEditor : : on_comboBox_layerType_activated ( int layerType )
2018-10-03 01:01:09 +01:00
{
if ( this - > metatile ) {
2018-10-06 23:07:36 +01:00
Metatile * prevMetatile = this - > metatile - > copy ( ) ;
2018-10-03 01:01:09 +01:00
this - > metatile - > layerType = static_cast < uint8_t > ( layerType ) ;
2018-10-06 23:07:36 +01:00
MetatileHistoryItem * commit = new MetatileHistoryItem ( metatileSelector - > getSelectedMetatile ( ) , prevMetatile , this - > metatile - > copy ( ) ) ;
metatileHistory . push ( commit ) ;
2018-10-03 01:01:09 +01:00
}
}
void TilesetEditor : : on_actionSave_Tileset_triggered ( )
{
2018-10-03 01:01:15 +01:00
this - > project - > saveTilesets ( this - > primaryTileset , this - > secondaryTileset ) ;
emit this - > tilesetsSaved ( this - > primaryTileset - > name , this - > secondaryTileset - > name ) ;
2019-01-07 15:12:01 +00:00
if ( this - > paletteEditor ) {
this - > paletteEditor - > setTilesets ( this - > primaryTileset , this - > secondaryTileset ) ;
}
2018-10-03 01:01:21 +01:00
this - > ui - > statusbar - > showMessage ( QString ( " Saved primary and secondary Tilesets! " ) , 5000 ) ;
2018-10-03 01:01:31 +01:00
this - > hasUnsavedChanges = false ;
2018-10-03 01:01:09 +01:00
}
2018-10-03 01:01:24 +01:00
void TilesetEditor : : on_actionImport_Primary_Tiles_triggered ( )
{
this - > importTilesetTiles ( this - > primaryTileset , true ) ;
}
void TilesetEditor : : on_actionImport_Secondary_Tiles_triggered ( )
{
this - > importTilesetTiles ( this - > secondaryTileset , false ) ;
}
void TilesetEditor : : importTilesetTiles ( Tileset * tileset , bool primary ) {
QString descriptor = primary ? " primary " : " secondary " ;
QString descriptorCaps = primary ? " Primary " : " Secondary " ;
QString filepath = QFileDialog : : getOpenFileName (
this ,
QString ( " Import %1 Tileset Tiles Image " ) . arg ( descriptorCaps ) ,
this - > project - > root ,
2019-01-07 15:12:01 +00:00
" Image Files (*.png *.bmp *.jpg *.dib) " ) ;
2018-10-03 01:01:24 +01:00
if ( filepath . isEmpty ( ) ) {
return ;
}
2018-12-20 23:30:35 +00:00
logInfo ( QString ( " Importing %1 tileset tiles '%2' " ) . arg ( descriptor ) . arg ( filepath ) ) ;
2018-10-03 01:01:24 +01:00
2019-01-07 15:31:48 +00:00
// Read image data from buffer so that the built-in QImage doesn't try to detect file format
// purely from the extension name. Advance Map exports ".png" files that are actually BMP format, for example.
QFile file ( filepath ) ;
QImage image ;
if ( file . open ( QIODevice : : ReadOnly ) ) {
QByteArray imageData = file . readAll ( ) ;
image = QImage : : fromData ( imageData ) ;
} else {
logError ( QString ( " Failed to open image file: '%1' " ) . arg ( filepath ) ) ;
}
2018-10-03 01:01:24 +01:00
if ( image . width ( ) = = 0 | | image . height ( ) = = 0 | | image . width ( ) % 8 ! = 0 | | image . height ( ) % 8 ! = 0 ) {
QMessageBox msgBox ( this ) ;
msgBox . setText ( " Failed to import tiles. " ) ;
msgBox . setInformativeText ( QString ( " The image dimensions (%1 x %2) are invalid. Width and height must be multiples of 8 pixels. " )
. arg ( image . width ( ) )
. arg ( image . height ( ) ) ) ;
msgBox . setDefaultButton ( QMessageBox : : Ok ) ;
msgBox . setIcon ( QMessageBox : : Icon : : Critical ) ;
msgBox . exec ( ) ;
return ;
}
// Validate total number of tiles in image.
2019-01-05 21:52:55 +00:00
int numTilesWide = image . width ( ) / 8 ;
int numTilesHigh = image . height ( ) / 8 ;
2018-10-03 01:01:24 +01:00
int totalTiles = numTilesHigh * numTilesWide ;
int maxAllowedTiles = primary ? Project : : getNumTilesPrimary ( ) : Project : : getNumTilesTotal ( ) - Project : : getNumTilesPrimary ( ) ;
if ( totalTiles > maxAllowedTiles ) {
QMessageBox msgBox ( this ) ;
msgBox . setText ( " Failed to import tiles. " ) ;
msgBox . setInformativeText ( QString ( " The maximum number of tiles allowed in the %1 tileset is %2, but the provided image contains %3 total tiles. " )
. arg ( descriptor )
. arg ( maxAllowedTiles )
. arg ( totalTiles ) ) ;
msgBox . setDefaultButton ( QMessageBox : : Ok ) ;
2019-01-07 15:12:01 +00:00
msgBox . setIcon ( QMessageBox : : Icon : : Critical ) ;
msgBox . exec ( ) ;
return ;
}
// Ask user to provide a palette for the un-indexed image.
if ( image . colorCount ( ) = = 0 ) {
QMessageBox msgBox ( this ) ;
msgBox . setText ( " Select Palette for Tiles " ) ;
msgBox . setInformativeText ( QString ( " The provided image is not indexed. Please select a palette file to for the image. An indexed image will be generated using the provided image and palette. " )
. arg ( image . colorCount ( ) ) ) ;
msgBox . setDefaultButton ( QMessageBox : : Ok ) ;
msgBox . setIcon ( QMessageBox : : Icon : : Warning ) ;
msgBox . exec ( ) ;
QString filepath = QFileDialog : : getOpenFileName (
this ,
QString ( " Select Palette for Tiles Image " ) . arg ( descriptorCaps ) ,
this - > project - > root ,
" Palette Files (*.pal *.act *tpl *gpl) " ) ;
if ( filepath . isEmpty ( ) ) {
return ;
}
PaletteParser parser ;
bool error = false ;
QList < QRgb > palette = parser . parse ( filepath , & error ) ;
if ( error ) {
QMessageBox msgBox ( this ) ;
msgBox . setText ( " Failed to import palette. " ) ;
QString message = QString ( " The palette file could not be processed. View porymap.log for specific errors. " ) ;
msgBox . setInformativeText ( message ) ;
msgBox . setDefaultButton ( QMessageBox : : Ok ) ;
msgBox . setIcon ( QMessageBox : : Icon : : Critical ) ;
msgBox . exec ( ) ;
return ;
} else if ( palette . length ( ) ! = 16 ) {
QMessageBox msgBox ( this ) ;
msgBox . setText ( " Failed to import palette. " ) ;
QString message = QString ( " The palette must have exactly 16 colors, but it has %1. " ) . arg ( palette . length ( ) ) ;
msgBox . setInformativeText ( message ) ;
msgBox . setDefaultButton ( QMessageBox : : Ok ) ;
msgBox . setIcon ( QMessageBox : : Icon : : Critical ) ;
msgBox . exec ( ) ;
return ;
}
QVector < QRgb > colorTable = palette . toVector ( ) ;
image = image . convertToFormat ( QImage : : Format : : Format_Indexed8 , colorTable ) ;
}
// Validate image is properly indexed to 16 colors.
if ( image . colorCount ( ) ! = 16 ) {
QMessageBox msgBox ( this ) ;
msgBox . setText ( " Failed to import tiles. " ) ;
msgBox . setInformativeText ( QString ( " The image must be indexed and contain 16 total colors, or it must be un-indexed. The provided image has %1 indexed colors. " )
. arg ( image . colorCount ( ) ) ) ;
msgBox . setDefaultButton ( QMessageBox : : Ok ) ;
2018-10-03 01:01:24 +01:00
msgBox . setIcon ( QMessageBox : : Icon : : Critical ) ;
msgBox . exec ( ) ;
return ;
}
this - > project - > loadTilesetTiles ( tileset , image ) ;
this - > refresh ( ) ;
2018-10-03 01:01:31 +01:00
this - > hasUnsavedChanges = true ;
}
void TilesetEditor : : closeEvent ( QCloseEvent * event )
{
if ( this - > hasUnsavedChanges ) {
2018-10-11 00:07:55 +01:00
QMessageBox : : StandardButton result = QMessageBox : : question (
this ,
" porymap " ,
" Tileset has been modified, save changes? " ,
QMessageBox : : No | QMessageBox : : Yes | QMessageBox : : Cancel ,
QMessageBox : : Yes ) ;
if ( result = = QMessageBox : : Yes ) {
this - > on_actionSave_Tileset_triggered ( ) ;
event - > accept ( ) ;
} else if ( result = = QMessageBox : : No ) {
event - > accept ( ) ;
} else if ( result = = QMessageBox : : Cancel ) {
event - > ignore ( ) ;
}
} else {
2018-10-03 01:01:31 +01:00
event - > accept ( ) ;
}
2018-10-03 01:01:24 +01:00
}
2018-10-03 01:01:37 +01:00
void TilesetEditor : : on_actionChange_Metatiles_Count_triggered ( )
{
QDialog dialog ( this , Qt : : WindowTitleHint | Qt : : WindowCloseButtonHint ) ;
dialog . setWindowTitle ( " Change Number of Metatiles " ) ;
dialog . setWindowModality ( Qt : : NonModal ) ;
QFormLayout form ( & dialog ) ;
QSpinBox * primarySpinBox = new QSpinBox ( ) ;
QSpinBox * secondarySpinBox = new QSpinBox ( ) ;
primarySpinBox - > setMinimum ( 1 ) ;
secondarySpinBox - > setMinimum ( 1 ) ;
primarySpinBox - > setMaximum ( Project : : getNumMetatilesPrimary ( ) ) ;
secondarySpinBox - > setMaximum ( Project : : getNumMetatilesTotal ( ) - Project : : getNumMetatilesPrimary ( ) ) ;
primarySpinBox - > setValue ( this - > primaryTileset - > metatiles - > length ( ) ) ;
secondarySpinBox - > setValue ( this - > secondaryTileset - > metatiles - > length ( ) ) ;
form . addRow ( new QLabel ( " Primary Tileset " ) , primarySpinBox ) ;
form . addRow ( new QLabel ( " Secondary Tileset " ) , secondarySpinBox ) ;
QDialogButtonBox buttonBox ( QDialogButtonBox : : Ok | QDialogButtonBox : : Cancel , Qt : : Horizontal , & dialog ) ;
connect ( & buttonBox , SIGNAL ( accepted ( ) ) , & dialog , SLOT ( accept ( ) ) ) ;
connect ( & buttonBox , SIGNAL ( rejected ( ) ) , & dialog , SLOT ( reject ( ) ) ) ;
form . addRow ( & buttonBox ) ;
if ( dialog . exec ( ) = = QDialog : : Accepted ) {
int numPrimaryMetatiles = primarySpinBox - > value ( ) ;
int numSecondaryMetatiles = secondarySpinBox - > value ( ) ;
while ( this - > primaryTileset - > metatiles - > length ( ) > numPrimaryMetatiles ) {
Metatile * metatile = this - > primaryTileset - > metatiles - > takeLast ( ) ;
delete metatile ;
}
while ( this - > primaryTileset - > metatiles - > length ( ) < numPrimaryMetatiles ) {
Tile tile ;
tile . palette = 0 ;
tile . tile = 0 ;
2018-10-06 21:49:26 +01:00
tile . xflip = false ;
tile . yflip = false ;
2018-10-03 01:01:37 +01:00
Metatile * metatile = new Metatile ;
metatile - > behavior = 0 ;
metatile - > layerType = 0 ;
for ( int i = 0 ; i < 8 ; i + + ) {
metatile - > tiles - > append ( tile ) ;
}
this - > primaryTileset - > metatiles - > append ( metatile ) ;
}
while ( this - > secondaryTileset - > metatiles - > length ( ) > numSecondaryMetatiles ) {
Metatile * metatile = this - > secondaryTileset - > metatiles - > takeLast ( ) ;
delete metatile ;
}
while ( this - > secondaryTileset - > metatiles - > length ( ) < numSecondaryMetatiles ) {
Tile tile ;
tile . palette = 0 ;
tile . tile = 0 ;
tile . xflip = 0 ;
tile . yflip = 0 ;
Metatile * metatile = new Metatile ;
metatile - > behavior = 0 ;
metatile - > layerType = 0 ;
for ( int i = 0 ; i < 8 ; i + + ) {
metatile - > tiles - > append ( tile ) ;
}
this - > secondaryTileset - > metatiles - > append ( metatile ) ;
}
this - > refresh ( ) ;
this - > hasUnsavedChanges = true ;
}
}
2018-10-03 01:01:41 +01:00
void TilesetEditor : : on_actionChange_Palettes_triggered ( )
{
if ( ! this - > paletteEditor ) {
this - > paletteEditor = new PaletteEditor ( this - > project , this - > primaryTileset , this - > secondaryTileset , this ) ;
connect ( this - > paletteEditor , SIGNAL ( changedPaletteColor ( ) ) , this , SLOT ( onPaletteEditorChangedPaletteColor ( ) ) ) ;
connect ( this - > paletteEditor , SIGNAL ( changedPalette ( int ) ) , this , SLOT ( onPaletteEditorChangedPalette ( int ) ) ) ;
}
if ( ! this - > paletteEditor - > isVisible ( ) ) {
this - > paletteEditor - > show ( ) ;
} else if ( this - > paletteEditor - > isMinimized ( ) ) {
this - > paletteEditor - > showNormal ( ) ;
} else {
this - > paletteEditor - > activateWindow ( ) ;
}
}
void TilesetEditor : : onPaletteEditorChangedPaletteColor ( ) {
this - > refresh ( ) ;
this - > hasUnsavedChanges = true ;
}
void TilesetEditor : : onPaletteEditorChangedPalette ( int paletteId ) {
this - > on_spinBox_paletteSelector_valueChanged ( paletteId ) ;
}
2018-10-06 23:07:36 +01:00
void TilesetEditor : : on_actionUndo_triggered ( )
{
MetatileHistoryItem * commit = this - > metatileHistory . current ( ) ;
if ( ! commit ) return ;
Metatile * prev = commit - > prevMetatile ;
if ( ! prev ) return ;
this - > metatileHistory . back ( ) ;
Metatile * temp = Tileset : : getMetatile ( commit - > metatileId , this - > primaryTileset , this - > secondaryTileset ) ;
if ( temp ) {
this - > metatile = temp ;
this - > metatile - > copyInPlace ( prev ) ;
this - > metatileSelector - > select ( commit - > metatileId ) ;
this - > metatileSelector - > draw ( ) ;
this - > metatileLayersItem - > draw ( ) ;
this - > metatileLayersItem - > clearLastModifiedCoords ( ) ;
}
}
void TilesetEditor : : on_actionRedo_triggered ( )
{
MetatileHistoryItem * commit = this - > metatileHistory . next ( ) ;
if ( ! commit ) return ;
Metatile * next = commit - > newMetatile ;
if ( ! next ) return ;
Metatile * temp = Tileset : : getMetatile ( commit - > metatileId , this - > primaryTileset , this - > secondaryTileset ) ;
if ( temp ) {
this - > metatile = Tileset : : getMetatile ( commit - > metatileId , this - > primaryTileset , this - > secondaryTileset ) ;
this - > metatile - > copyInPlace ( next ) ;
this - > metatileSelector - > select ( commit - > metatileId ) ;
this - > metatileSelector - > draw ( ) ;
this - > metatileLayersItem - > draw ( ) ;
this - > metatileLayersItem - > clearLastModifiedCoords ( ) ;
}
}
2019-01-05 21:52:55 +00:00
void TilesetEditor : : on_actionExport_Primary_Tiles_Image_triggered ( )
{
QString defaultName = QString ( " %1_Tiles_Pal%2 " ) . arg ( this - > primaryTileset - > name ) . arg ( this - > paletteId ) ;
QString defaultFilepath = QString ( " %1/%2.png " ) . arg ( this - > project - > root ) . arg ( defaultName ) ;
QString filepath = QFileDialog : : getSaveFileName ( this , " Export Primary Tiles Image " , defaultFilepath , " Image Files (*.png) " ) ;
if ( ! filepath . isEmpty ( ) ) {
QImage image = this - > tileSelector - > buildPrimaryTilesIndexedImage ( ) ;
2019-01-11 14:52:47 +00:00
exportIndexed4BPPPng ( image , filepath ) ;
2019-01-05 21:52:55 +00:00
}
}
void TilesetEditor : : on_actionExport_Secondary_Tiles_Image_triggered ( )
{
QString defaultName = QString ( " %1_Tiles_Pal%2 " ) . arg ( this - > secondaryTileset - > name ) . arg ( this - > paletteId ) ;
QString defaultFilepath = QString ( " %1/%2.png " ) . arg ( this - > project - > root ) . arg ( defaultName ) ;
QString filepath = QFileDialog : : getSaveFileName ( this , " Export Secondary Tiles Image " , defaultFilepath , " Image Files (*.png) " ) ;
if ( ! filepath . isEmpty ( ) ) {
QImage image = this - > tileSelector - > buildSecondaryTilesIndexedImage ( ) ;
2019-01-11 14:52:47 +00:00
exportIndexed4BPPPng ( image , filepath ) ;
2019-01-05 21:52:55 +00:00
}
}
2019-01-09 00:04:41 +00:00
void TilesetEditor : : on_actionImport_Primary_Metatiles_triggered ( )
{
this - > importTilesetMetatiles ( this - > primaryTileset , true ) ;
}
void TilesetEditor : : on_actionImport_Secondary_Metatiles_triggered ( )
{
this - > importTilesetMetatiles ( this - > secondaryTileset , false ) ;
}
void TilesetEditor : : importTilesetMetatiles ( Tileset * tileset , bool primary )
{
QString descriptor = primary ? " primary " : " secondary " ;
QString descriptorCaps = primary ? " Primary " : " Secondary " ;
QString filepath = QFileDialog : : getOpenFileName (
this ,
QString ( " Import %1 Tileset Metatiles from Advance Map 1.92 " ) . arg ( descriptorCaps ) ,
this - > project - > root ,
" Advance Map 1.92 Metatile Files (*.bvd) " ) ;
if ( filepath . isEmpty ( ) ) {
return ;
}
MetatileParser parser ;
bool error = false ;
QList < Metatile * > * metatiles = parser . parse ( filepath , & error , primary ) ;
if ( error ) {
QMessageBox msgBox ( this ) ;
msgBox . setText ( " Failed to import metatiles from Advance Map 1.92 .bvd file. " ) ;
QString message = QString ( " The .bvd file could not be processed. View porymap.log for specific errors. " ) ;
msgBox . setInformativeText ( message ) ;
msgBox . setDefaultButton ( QMessageBox : : Ok ) ;
msgBox . setIcon ( QMessageBox : : Icon : : Critical ) ;
msgBox . exec ( ) ;
return ;
}
\
// TODO: This is crude because it makes a history entry for every newly-imported metatile.
// Revisit this when tiles and num metatiles are added to tileset editory history.
int metatileIdBase = primary ? 0 : Project : : getNumMetatilesPrimary ( ) ;
for ( int i = 0 ; i < metatiles - > length ( ) ; i + + ) {
if ( i > = tileset - > metatiles - > length ( ) ) {
break ;
}
Metatile * prevMetatile = tileset - > metatiles - > at ( i ) - > copy ( ) ;
MetatileHistoryItem * commit = new MetatileHistoryItem ( static_cast < uint16_t > ( metatileIdBase + i ) , prevMetatile , metatiles - > at ( i ) - > copy ( ) ) ;
metatileHistory . push ( commit ) ;
}
tileset - > metatiles = metatiles ;
this - > refresh ( ) ;
this - > hasUnsavedChanges = true ;
}