#include "CalamariWindow.h" #include "DiffWidget.h" #include "MergeWidget.h" #include "ColorOptsDialog.h" #include <QAction> #include <QApplication> #include <QButtonGroup> #include <QCheckBox> #include <QComboBox> #include <QLabel> #include <QLayout> #include <QMenu> #include <QMenuBar> #include <QMessageBox> #include <QPainter> #include <QSettings> #include <QStatusBar> #include <QTextBrowser> #include <QToolBar> #include <QToolButton> #include "Version" #include <math.h> CalamariWindow::CalamariWindow(char* file1, char* file2) : QMainWindow(0, 0) { rgbWidget = 0x0; setWindowTitle( QString() + "CALAMARI - Diff " + file1 + " and " + file2 ); setWindowIcon( QIcon( ":/icons/calamari.png" ) ); save = 0x0; baseWidget = diffWidget = new DiffWidget( file1, file2 ); mergeWidget = 0x0; loadOpts(); initDiffToolbar(); setCentralWidget( baseWidget ); zoomFactor = 0; baseWidget->setScale( 0 ); connect( baseWidget, SIGNAL( showMessage( QString, int ) ), statusBar(), SLOT( showMessage( QString, int ) ) ); } CalamariWindow::CalamariWindow(QImage file1, QImage file2, QString title) : QMainWindow(0, 0) { rgbWidget = 0x0; setWindowTitle( QString() + "CALAMARI - Diff " + title ); setWindowIcon( QIcon( ":/icons/calamari.png" ) ); save = 0x0; baseWidget = diffWidget = new DiffWidget( file1, file2 ); mergeWidget = 0x0; loadOpts(); initDiffToolbar(); setCentralWidget( baseWidget ); zoomFactor = 0; baseWidget->setScale( 0 ); connect( baseWidget, SIGNAL( showMessage( QString, int ) ), statusBar(), SLOT( showMessage( QString, int ) ) ); } CalamariWindow::CalamariWindow(char* file1, char* file2, char* file3, char* file4) : QMainWindow(0,0) { rgbWidget = 0x0; setWindowTitle( QString() + "CALAMARI - Merge " + file2 + " and " + file3 ); setWindowIcon( QIcon( ":/icons/calamari.png" ) ); save = 0x0; baseWidget = mergeWidget = new MergeWidget( file1, file2, file3 ); diffWidget = 0x0; loadOpts(); initMergeToolbar(); setCentralWidget( baseWidget ); mergeWidget->autoMerge(); mergeWidget->setBrushSize( brushSize ); mergeWidget->setResult( file4 ); zoomFactor = 0; baseWidget->setScale( 0 ); connect( baseWidget, SIGNAL( showMessage( QString, int ) ), statusBar(), SLOT( showMessage( QString, int ) ) ); } CalamariWindow::CalamariWindow(QImage b, QImage t, QImage y, MergeWidget* p) { rgbWidget = p; setWindowTitle( QString() + "CALAMARI - Merge Alpha Channels" ); setWindowIcon( QIcon( ":/icons/calamari.png" ) ); save = 0x0; baseWidget = mergeWidget = new MergeWidget( b, t, y ); diffWidget = 0x0; loadOpts(); initMergeToolbar(); setCentralWidget( baseWidget ); mergeWidget->autoMerge(); mergeWidget->setBrushSize( brushSize ); zoomFactor = 0; baseWidget->setScale( 0 ); connect( baseWidget, SIGNAL( showMessage( QString, int ) ), statusBar(), SLOT( showMessage( QString, int ) ) ); } CalamariWindow::~CalamariWindow() { } QImage CalamariWindow::getResult() { if ( !mergeWidget ) return QImage(); return mergeWidget->mergeResult(); } void CalamariWindow::closeEvent( QCloseEvent* e ) { if ( rgbWidget ) { if ( !rgbWidget->done() ) { e->setAccepted( false ); showMinimized(); return; } else { QMainWindow::closeEvent( e ); return; } } saveOpts(); if ( save && save->isEnabled() ) { switch( QMessageBox::question( this, "Mrh?", "Save merge result before closing?", QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel ) ) { case QMessageBox::Yes: if ( !mergeWidget->save() ) { e->setAccepted( false ); return; } break; case QMessageBox::No: break; case QMessageBox::Cancel: e->setAccepted( false ); return; } } if ( mergeWidget ) mergeWidget->beDone(); QMainWindow::closeEvent( e ); } void CalamariWindow::loadOpts() { QSettings s( QSettings::UserScope, "Perforce", "CALAMARI" ); QColor m = palette().dark().color(); m.setAlpha( 240 ); m.setRgba( s.value( "stencil", m.rgba() ).toUInt() ); QColor c; c.setRgba( s.value( "conflict", QColor("red").rgba() ).toUInt() ); QColor e; e.setRgba( s.value( "eraser", QColor("black").rgba() ).toUInt() ); baseWidget->setMaskColor( m ); baseWidget->setConflictColor( c ); baseWidget->setEraserColor( e ); baseWidget->setTolerance( s.value( "tolerance", 0 ).toInt() ); brushSize = s.value( "brushSize", 16 ).toInt(); eraserSize = s.value( "eraserSize", 16 ).toInt(); } void CalamariWindow::colorOpts() { ColorOptsDialog* d = new ColorOptsDialog ( baseWidget->getMaskColor(), baseWidget->getConflictColor(), baseWidget->getEraserColor(), baseWidget->getTolerance() ); connect( d, SIGNAL( setMaskColor(QColor) ), baseWidget, SLOT( setMaskColor(QColor) ) ); connect( d, SIGNAL( setConflictColor(QColor) ), baseWidget, SLOT( setConflictColor(QColor) ) ); connect( d, SIGNAL( setEraserColor(QColor) ), baseWidget, SLOT( setEraserColor(QColor) ) ); connect( d, SIGNAL( setTolerance(int) ), baseWidget, SLOT( setTolerance(int) ) ); d->exec(); saveOpts(); } void CalamariWindow::saveOpts() { QSettings s( QSettings::UserScope, "Perforce", "CALAMARI" ); s.setValue( "stencil", baseWidget->getMaskColor().rgba() ); s.setValue( "conflict", baseWidget->getConflictColor().rgba() ); s.setValue( "eraser", baseWidget->getEraserColor().rgba() ); s.setValue( "tolerance", baseWidget->getTolerance() ); s.setValue( "brushSize", brushSize ); s.setValue( "eraserSize", eraserSize ); } void CalamariWindow::setAutoSize( bool a ) { if ( a ) { baseWidget->setScale( 0 ); } else { baseWidget->setScale( pow( 2.0, zoomFactor ) ); } } void CalamariWindow::setZoom( int index ) { zoomFactor = zoomList->itemData( index ).toInt(); double scale = pow( 2.0, zoomFactor ); baseWidget->setScale( scale ); } void CalamariWindow::addMaskOpts( QToolBar* tbar ) { QAction* colOpts = new QAction( QIcon( ":/icons/color.png" ), "Set color options", 0x0 ); tbar->addAction( colOpts ); connect( colOpts, SIGNAL( triggered() ), this, SLOT( colorOpts() ) ); colOpts->setEnabled( true ); } void CalamariWindow::maskToggled( bool toggled ) { //If one mask button just got toggled on, toggle off the rest. if ( toggled ) { for ( QList<QAction*>::iterator i = maskActions.begin() ; i != maskActions.end() ; i++ ) { if ( (*i) != sender() ) (*i)->setChecked( false ); } } } void CalamariWindow::setBrushSize( bool on ) { if ( !on ) return; //A hack - the paintbrush size is stored in the objectName //of the sender, which is one of many QActions in a menu. brushSize = sender()->objectName().toInt(); mergeWidget->setBrushSize( brushSize ); } void CalamariWindow::setEraserSize( bool on ) { if ( !on ) return; //Same as brush size. eraserSize = sender()->objectName().toInt(); mergeWidget->setEraserSize( eraserSize ); } void CalamariWindow::diffBaseTheirs() { CalamariWindow* cw = new CalamariWindow( mergeWidget->getBase(), mergeWidget->getTheirs(), "Base and Theirs" ); cw->setGeometry( cw->x(), cw->y(), width(), height() ); cw->show(); } void CalamariWindow::diffBaseYours() { CalamariWindow* cw = new CalamariWindow( mergeWidget->getBase(), mergeWidget->getYours(), "Base and Yours" ); cw->setGeometry( cw->x(), cw->y(), width(), height() ); cw->show(); } void CalamariWindow::help() { QMainWindow* helpWindow = new QMainWindow; helpWindow->setAttribute( Qt::WA_DeleteOnClose ); QTextBrowser* help = new QTextBrowser; helpWindow->setCentralWidget( help ); QStringList paths; paths.append( "qrc:/doc/help/" ); help->setSearchPaths( paths ); help->setSource( QUrl( "qrc:/doc/help/main.html" ) ); help->setMinimumSize( 500, 400 ); helpWindow->setWindowTitle( "CALAMARI Help" ); QToolBar* ht = helpWindow->addToolBar( "Navigation" ); ht->setMovable( false ); ht->toggleViewAction()->setEnabled( false ); QToolButton* back = new QToolButton; ht->addWidget( back ); back->setArrowType( Qt::LeftArrow ); back->setMaximumSize( 18, 18 ); connect( back, SIGNAL( pressed() ), help, SLOT( backward() ) ); connect( help, SIGNAL( backwardAvailable(bool) ), back, SLOT( setEnabled(bool) ) ); QToolButton* fwd = new QToolButton; ht->addWidget( fwd ); fwd->setArrowType( Qt::RightArrow ); fwd->setMaximumSize( 18, 18 ); connect( fwd, SIGNAL( pressed() ), help, SLOT( forward() ) ); connect( help, SIGNAL( forwardAvailable(bool) ), fwd, SLOT( setEnabled(bool) ) ); back->setEnabled( false ); fwd->setEnabled( false ); helpWindow->show(); } void CalamariWindow::about() { QString change = VERSION_CHANGE; change = change.simplified(); change.remove( "$Change: " ); change.remove( " $" ); QString date = VERSION_DATE; date = date.simplified(); date.remove( "$Date: " ); date.remove( " $" ); QMessageBox::about ( this, "About CALAMARI", QString() + "CALAMARI - Compare, Analyze, Liken And Merge Associated Raster Images\n" + "from the Perforce Public Depot (http://public.perforce.com)\n" + "Built on " + date + " (change " + change + ")" ); } void CalamariWindow::helpAlpha() { QMessageBox::information( this, "What gives?", "This window is dedicated to merging the alpha channel of your image, with lighter\n colors corresponding to opacity and darker colors corresponding to transparency.\n\nThere's no Save button here because when you press Save in the main window,\n the current merge result from this one will automatically be used as the alpha\n channel for the saved image.\n\nWhen you close the main window, this one will close automatically as well." ); } void CalamariWindow::addZoomOpts( QToolBar* tbar ) { QLabel* zLabel = new QLabel( "Zoom:" ); tbar->addWidget( zLabel ); zoomList = new QComboBox; tbar->addWidget( zoomList ); int d; int off = 3; zoomList->setInsertPolicy( QComboBox::InsertAtBottom ); for ( int z = -3 ; z <= 3 ; z++ ) { if ( z < 0 ) { d = pow( 2.0, -z ); zoomList->insertItem( z + off, "1/" + QString::number( d ) + " x", z ); } else { d = pow( 2.0, z ); zoomList->insertItem( z + off, QString::number( d ) + " x", z ); } } zoomList->setCurrentIndex( 0 + off ); connect( zoomList, SIGNAL(activated(int)), this, SLOT(setZoom(int)) ); QCheckBox* asize = new QCheckBox( "Fit to window" ); tbar->addWidget( asize ); connect( asize, SIGNAL( toggled(bool) ), this, SLOT( setAutoSize(bool) ) ); connect( asize, SIGNAL( toggled(bool) ), zoomList, SLOT( setDisabled(bool) ) ); asize->setChecked( true ); } void CalamariWindow::initDiffToolbar() { QToolBar* tbar = addToolBar( "CALAMARI" ); //All stencil buttons should follow this template. Note the special hookups //that enable the one-or-none toggle behavior. QAction* diffMask = new QAction( QIcon( ":/icons/diff.png" ), "Diff stencil", 0x0 ); diffMask->setCheckable( true ); tbar->addAction( diffMask ); maskActions.append( diffMask ); connect( diffMask, SIGNAL( toggled(bool) ), this, SLOT(maskToggled(bool) ) ); connect( diffMask, SIGNAL( toggled(bool) ), diffWidget, SLOT( setDiffMask(bool) ) ); addMaskOpts( tbar ); tbar->addSeparator(); addZoomOpts( tbar ); tbar->setMovable( false ); tbar->toggleViewAction()->setEnabled( false ); tbar->addSeparator(); tbar->addAction( QIcon( QMessageBox::standardIcon( QMessageBox::Question ).scaled( 18, 18 ) ), "CALAMARI Help", this, SLOT( help() ) ); tbar->addAction( QIcon( QMessageBox::standardIcon( QMessageBox::Information ).scaled( 18, 18 ) ), "About CALAMARI", this, SLOT( about() ) ); } void CalamariWindow::initMergeToolbar() { QToolBar* tbar = addToolBar( "CALAMARI" ); //All stencil buttons should follow this template. Note the special hookups //that enable the one-or-none toggle behavior. QAction* theirMask = new QAction( QIcon( ":/icons/theirs.png" ), "Theirs stencil", 0x0 ); theirMask->setCheckable( true ); tbar->addAction( theirMask ); maskActions.append( theirMask ); connect( theirMask, SIGNAL( toggled(bool) ), this, SLOT( maskToggled(bool) ) ); connect( theirMask, SIGNAL( toggled(bool) ), mergeWidget, SLOT( setTheirsMask(bool) ) ); QAction* yourMask = new QAction( QIcon( ":/icons/yours.png" ), "Yours stencil", 0x0 ); yourMask->setCheckable( true ); tbar->addAction( yourMask ); maskActions.append( yourMask ); connect( yourMask, SIGNAL( toggled(bool) ), this, SLOT( maskToggled(bool) ) ); connect( yourMask, SIGNAL( toggled(bool) ), mergeWidget, SLOT( setYoursMask(bool) ) ); QAction* conflictMask = new QAction( QIcon( ":/icons/conflict.png" ), "Conflict stencil", 0x0 ); conflictMask->setCheckable( true ); tbar->addAction( conflictMask ); maskActions.append( conflictMask ); connect( conflictMask, SIGNAL( toggled(bool) ), this, SLOT( maskToggled(bool) ) ); connect( conflictMask, SIGNAL( toggled(bool) ), mergeWidget, SLOT( setConflictMask(bool) ) ); addMaskOpts( tbar ); tbar->addSeparator(); addZoomOpts( tbar ); QWidget* s = new QWidget; s->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); tbar->addWidget( s ); tbar->setMovable( false ); tbar->toggleViewAction()->setEnabled( false ); setMinimumWidth( tbar->sizeHint().width() ); addToolBarBreak(); tbar = addToolBar( "Merge tools" ); tbar->setMovable( false ); QAction* autoMerge = new QAction( QIcon( ":/icons/auto.png" ), "Auto Merge", 0x0 ); tbar->addAction( autoMerge ); connect( autoMerge, SIGNAL( triggered() ), mergeWidget, SLOT( autoMerge() ) ); tbar->addSeparator(); QButtonGroup* tools = new QButtonGroup( this ); QToolButton* paintBrush = new QToolButton; tools->addButton( paintBrush ); paintBrush->setIcon( QIcon( ":/icons/paint.png" ) ); paintBrush->setToolTip( "Paint" ); paintBrush->setCheckable( true ); paintBrush->setPopupMode( QToolButton::MenuButtonPopup ); tbar->addWidget( paintBrush ); connect( paintBrush, SIGNAL( toggled(bool) ), mergeWidget, SLOT( paintToggle(bool) ) ); paintBrush->setMenu( new QMenu( "Brush size", paintBrush ) ); QString px; QAction* a; QPixmap i( QSize( 18, 18 ) ); QPainter pt; QActionGroup* bs = new QActionGroup( this ); for ( int p = 0 ; p < 7 ; p++ ) { i.fill( QColor( 0, 0, 0, 0 ) ); pt.begin( &i ); pt.setBrush( QColor( 0, 0, 0 ) ); pt.setRenderHint( QPainter::Antialiasing ); pt.drawPoint( 9, 9 ); pt.drawEllipse( 9 - p, 9 - p, p * 2, p * 2 ); pt.end(); px = QString::number( pow( 2.0, p ) ); a = paintBrush->menu()->addAction( QIcon( i ), px + QString( " pt" ) ); a->setCheckable( true ); bs->addAction( a ); a->setObjectName( px ); connect( a, SIGNAL( toggled(bool) ), this, SLOT( setBrushSize(bool) ) ); if ( pow( 2.0, p ) == brushSize ) a->setChecked( true ); } QToolButton* spray = new QToolButton; tools->addButton( spray ); spray->setIcon( QIcon( ":/icons/spray.png" ) ); spray->setToolTip( "Spray" ); spray->setCheckable( true ); tbar->addWidget( spray ); connect( spray, SIGNAL( toggled(bool) ), mergeWidget, SLOT( sprayToggle(bool) ) ); QToolButton* eraser = new QToolButton; tools->addButton( eraser ); eraser->setIcon( QIcon( ":/icons/eraser.png" ) ); eraser->setToolTip( "Erase" ); eraser->setCheckable( true ); eraser->setPopupMode( QToolButton::MenuButtonPopup ); tbar->addWidget( eraser ); connect( eraser, SIGNAL( toggled(bool) ), mergeWidget, SLOT( eraserToggle(bool) ) ); eraser->setMenu( new QMenu( "Eraser size", eraser ) ); QActionGroup* es = new QActionGroup( this ); for ( int p = 0 ; p < 7 ; p++ ) { i.fill( QColor( 0, 0, 0, 0 ) ); pt.begin( &i ); pt.setPen( QColor( 0, 0, 0 ) ); pt.setBrush( QColor( 255, 255, 255 ) ); pt.drawRect( 9 - p, 9 - p, p * 2, p * 2 ); pt.end(); px = QString::number( pow( 2.0, p ) ); a = eraser->menu()->addAction( QIcon( i ), px + QString( " pt" ) ); a->setCheckable( true ); es->addAction( a ); a->setObjectName( px ); connect( a, SIGNAL( toggled(bool) ), this, SLOT( setEraserSize(bool) ) ); if ( pow( 2.0, p ) == eraserSize ) a->setChecked( true ); } tbar->addSeparator(); save = new QAction( QIcon( ":/icons/save.png" ), "&Save", this ); save->setShortcut( QKeySequence( "Ctrl+S" ) ); tbar->addAction( save ); connect( save, SIGNAL( triggered() ), mergeWidget, SLOT( save() ) ); connect( mergeWidget, SIGNAL( enableSave(bool) ), save, SLOT( setEnabled(bool) ) ); if ( rgbWidget ) { save->setVisible( false ); tbar->addAction( QMessageBox::standardIcon( QMessageBox::Question ), "What's this window for and why can't I save?", this, SLOT( helpAlpha() ) ); } QMenu* file = menuBar()->addMenu( "&File" ); file->addAction( save ); file->addAction( "Save &As...", mergeWidget, SLOT( saveAs() ) ); file->addSeparator(); file->addAction( "Export Stencils", mergeWidget, SLOT( exportStencils() ) ); file->addSeparator(); file->addAction( "E&xit", qApp, SLOT( closeAllWindows() ) ); QMenu* view = menuBar()->addMenu( "&View" ); view->addAction( "Diff Base vs &Theirs...", this, SLOT( diffBaseTheirs() ) ); view->addAction( "Diff Base vs &Yours...", this, SLOT( diffBaseYours() ) ); QMenu* help = menuBar()->addMenu( "&Help" ); help->addAction( "CALAMARI Help", this, SLOT( help() ), QKeySequence( "F1" ) ); help->addAction( "About CALAMARI...", this, SLOT( about() ) ); }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#14 | 6281 | Sam Stafford | CALAMARI now builds with VS2005 and Qt 4.3.3. | ||
#13 | 5539 | Sam Stafford | Kick off another window to merge alpha channels, if present. | ||
#12 | 5534 | Sam Stafford |
A brand new help file, a bit of cosmetic sprucing, one critical bug fix, and a partridge in a pear tree. |
||
#11 | 5527 | Sam Stafford |
Diff options in merge mode, and an "export stencils" command for people who like that sort of thing. Also, a fresh build. |
||
#10 | 5524 | Sam Stafford | Eraser and spraypaint tools, and "Save" command. | ||
#9 | 5520 | Sam Stafford |
Configurable brush sizes, complete with procedurally generated icons. Spiffy. |
||
#8 | 5519 | Sam Stafford |
Added the paintbrush tool. Now we're getting somewhere. |
||
#7 | 5516 | Sam Stafford | The M is for Merge. | ||
#6 | 5515 | Sam Stafford |
A little more groundwork for merging. Color options are now saved automatically, and may include alpha channels (semitransparent stencils are nifty). |
||
#5 | 5512 | Sam Stafford | UI reshuffling - pushed stencil-related options into a dialog. | ||
#4 | 5511 | Sam Stafford | Continued infrastructure work. | ||
#3 | 5505 | Sam Stafford | Add "diff stencil". | ||
#2 | 5502 | Sam Stafford |
Status bar with handy information, and infrastructure to allow ImageProjectors to have frames. |
||
#1 | 5501 | Sam Stafford |
Codename CALAMARI. Built with Qt 4.1.3 - needs QtCore4.dll and QtGui4.dll to run. |