<commit_before>/**
 * syntaxhighlighter.cpp
 *
 * Copyright (c) 2003 Trolltech AS
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "syntaxhighlighter.h"
#include <klocale.h>
#include <qcolor.h>
#include <qregexp.h>
#include <qsyntaxhighlighter.h>
#include <qtimer.h>
#include <kconfig.h>
#include <kdebug.h>
#include <kglobal.h>
#include <kspell.h>
#include <kmkernel.h>
#include <kapplication.h>

static int dummy, dummy2, dummy3;
static int *Okay = &dummy;
static int *NotOkay = &dummy2;
static int *Ignore = &dummy3;

namespace KMail {

MessageHighlighter::MessageHighlighter( QTextEdit *textEdit, SyntaxMode mode )
    : QSyntaxHighlighter( textEdit ), sMode( mode )
{
  KConfig *config = KMKernel::config();

  // block defines the lifetime of KConfigGroupSaver
  KConfigGroupSaver saver(config, "Reader");
  QColor defaultColor1( 0x00, 0x80, 0x00 ); // defaults from kmreaderwin.cpp
  QColor defaultColor2( 0x00, 0x70, 0x00 );
  QColor defaultColor3( 0x00, 0x60, 0x00 );
  col1 = QColor(kapp->palette().active().text());
  col2 = config->readColorEntry( "QuotedText3", &defaultColor3 );
  col3 = config->readColorEntry( "QuotedText2", &defaultColor2 );
  col4 = config->readColorEntry( "QuotedText1", &defaultColor1 );
  col5 = col1;
}

MessageHighlighter::~MessageHighlighter()
{
}

int MessageHighlighter::highlightParagraph( const QString &text, int )
{
    QString simplified = text;
    simplified = simplified.replace( QRegExp( "\\s" ), "" ).replace( QRegExp( "\\|" ), ">" );
    if ( simplified.startsWith( ">>>>" ) )
	setFormat( 0, text.length(), col1 );
    else if	( simplified.startsWith( ">>>" ) || simplified.startsWith( "> >	>" ) )
	setFormat( 0, text.length(), col2 );
    else if	( simplified.startsWith( ">>" )	|| simplified.startsWith( "> >"	) )
	setFormat( 0, text.length(), col3 );
    else if	( simplified.startsWith( ">" ) )
	setFormat( 0, text.length(), col4 );
    else
	setFormat( 0, text.length(), col5 );
    return 0;
}

SpellChecker::SpellChecker( QTextEdit *textEdit )
: MessageHighlighter( textEdit ), alwaysEndsWithSpace( TRUE )
{
  KConfig *config = KMKernel::config();
  KConfigGroupSaver saver(config, "Reader");
  QColor c = QColor("red");
  mColor = config->readColorEntry("NewMessage", &c);
}

SpellChecker::~SpellChecker()
{
}

int SpellChecker::highlightParagraph( const QString& text,
				      int endStateOfLastPara )
{
    // leave #includes, diffs, and quoted replies alone
    QString diffAndCo( ">|" );

    bool isCode = diffAndCo.find(text[0]) != -1;

    if ( !text.endsWith(" ") )
	alwaysEndsWithSpace = FALSE;

    MessageHighlighter::highlightParagraph( text, endStateOfLastPara );

    if ( !isCode ) {
        int para, index;
	textEdit()->getCursorPosition( &para, &index );
	QString paraText = textEdit()->text( para );
	int len = text.length();
	if ( alwaysEndsWithSpace )
	    len--;

	currentPos = 0;
	currentWord = "";
	for ( int i = 0; i < len; i++ ) {
	    if ( text[i].isSpace() || text[i] == '-' ) {
		flushCurrentWord();
		currentPos = i + 1;
	    } else {
		currentWord += text[i];
	    }
	}
	if ( !text[len - 1].isLetter() ||
	     index + 1 != text.length() ||
	     text != paraText)
	    flushCurrentWord();
    }
    return endStateOfLastPara;
}

QStringList SpellChecker::personalWords()
{
    QStringList l;
    l.append( "KMail" );
    l.append( "KOrganizer" );
    l.append( "KHTML" );
    l.append( "KIO" );
    l.append( "KJS" );
    l.append( "Konqueror" );
    l.append( "KSpell" );
    l.append( "Kontact" );
    return l;
}

void SpellChecker::flushCurrentWord()
{
    while ( currentWord[0].isPunct() ) {
	currentWord = currentWord.mid( 1 );
	currentPos++;
    }

    QChar ch;
    while ( (ch = currentWord[(int) currentWord.length() - 1]).isPunct() &&
	     ch != '(' && ch != '@' )
	currentWord.truncate( currentWord.length() - 1 );

    if ( !currentWord.isEmpty() ) {
	if ( isMisspelled(currentWord) )
	    setFormat( currentPos, currentWord.length(), mColor );
//	    setMisspelled( currentPos, currentWord.length(), true );
    }
    currentWord = "";
}

QDict<int> DictSpellChecker::dict( 50021 );
QObject *DictSpellChecker::sDictionaryMonitor = 0;

DictSpellChecker::DictSpellChecker( QTextEdit *textEdit )
    : SpellChecker( textEdit )
{
    mAutoReady = false;
    mWordCount = 0;
    mErrorCount = 0;
    mActive = true;
    mAutomatic = true;
    textEdit->installEventFilter( this );
    textEdit->viewport()->installEventFilter( this );
    mInitialMove = true;
    mRehighlightRequested = false;
    mSpell = 0;
    mSpellKey = spellKey();
    if (!sDictionaryMonitor)
	sDictionaryMonitor = new QObject();
    slotDictionaryChanged();
    startTimer(2*1000);
}

DictSpellChecker::~DictSpellChecker()
{
    delete mSpell;
}

void DictSpellChecker::slotSpellReady( KSpell *spell )
{
    connect( sDictionaryMonitor, SIGNAL( destroyed() ),
	     this, SLOT( slotDictionaryChanged() ));
    mSpell = spell;
    QStringList l = SpellChecker::personalWords();
    for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
        mSpell->addPersonal( *it );
    }
    connect( spell, SIGNAL( misspelling (const QString &, const QStringList &, unsigned int) ),
	     this, SLOT( slotMisspelling (const QString &, const QStringList &, unsigned int)));
    if (!mRehighlightRequested) {
	mRehighlightRequested = true;
	QTimer::singleShot(0, this, SLOT(slotRehighlight()));
    }
}

bool DictSpellChecker::isMisspelled( const QString& word )
{
    // Normally isMisspelled would look up a dictionary and return
    // true or false, but kspell is asynchronous and slow so things
    // get tricky...

    // For auto detection ignore signature and reply prefix
    if (!mAutoReady)
	mAutoIgnoreDict.replace( word, Ignore );

    // "dict" is used as a cache to store the results of KSpell
    if (!dict.isEmpty() && dict[word] == NotOkay) {
	if (mAutoReady && (mAutoDict[word] != NotOkay)) {
	    if (!mAutoIgnoreDict[word]) {
		++mErrorCount;
		if (mAutoDict[word] != Okay)
		    ++mWordCount;
	    }
	    mAutoDict.replace( word, NotOkay );
	}
	return true && mActive;
    }
    if (!dict.isEmpty() && dict[word] == Okay) {
	if (mAutoReady && !mAutoDict[word]) {
	    mAutoDict.replace( word, Okay );
	    if (!mAutoIgnoreDict[word])
		++mWordCount;
	}
	return false;
    }

    // there is no 'spelt correctly' signal so default to Okay
    dict.replace( word, Okay );

    // yes I tried checkWord, the docs lie and it didn't give useful signals :-(
    if (mSpell)
	mSpell->check( word, false );
    return false;
}

void DictSpellChecker::slotMisspelling (const QString & originalword, const QStringList & suggestions, unsigned int)
{
    kdDebug(5006) << suggestions.join(" ").latin1() << endl;
    dict.replace( originalword, NotOkay );

    // this is slow but since kspell is async this will have to do for now
    if (!mRehighlightRequested) {
	mRehighlightRequested = true;
	QTimer::singleShot(0, this, SLOT(slotRehighlight()));
    }
}

void DictSpellChecker::dictionaryChanged()
{
    QObject *oldMonitor = sDictionaryMonitor;
    sDictionaryMonitor = new QObject();
    dict.clear();
    delete oldMonitor;
}

void DictSpellChecker::slotRehighlight()
{
    mRehighlightRequested = false;
    rehighlight();
    QTimer::singleShot(0, this, SLOT(slotAutoDetection()));
}

void DictSpellChecker::slotDictionaryChanged()
{
    delete mSpell;
    mSpell = 0;
    mWordCount = 0;
    mErrorCount = 0;
    mAutoDict.clear();
    new KSpell(0, i18n("Incremental Spellcheck - KMail"), this,
			 SLOT(slotSpellReady(KSpell*)));
}

QString DictSpellChecker::spellKey()
{
    //Note: Yes polling with timerEvent and reading directly from kglobals
    //Note: is evil. It would be nice if there was some kind of inter-process
    //Note: signal emitted when spelling configuration options are changed.
    KConfig *config = KGlobal::config();
    KConfigGroupSaver cs(config,"KSpell");
    config->reparseConfiguration();
    QString key;
    key += QString::number(config->readNumEntry("KSpell_NoRootAffix", 0));
    key += '/';
    key += QString::number(config->readNumEntry("KSpell_RunTogether", 0));
    key += '/';
    key += config->readEntry("KSpell_Dictionary", "");
    key += '/';
    key += QString::number(config->readNumEntry("KSpell_DictFromList", FALSE));
    key += '/';
    key += QString::number(config->readNumEntry ("KSpell_Encoding", KS_E_ASCII));
    key += '/';
    key += QString::number(config->readNumEntry ("KSpell_Client", KS_CLIENT_ISPELL));
    return key;
}

// Automatic spell checking support
// In auto spell checking mode disable as-you-type spell checking
// iff more than one third of words are spelt incorrectly.
//
// Words in the signature and reply prefix are ignored.
// Only unique words are counted.
void DictSpellChecker::slotAutoDetection()
{
    if (!mAutoReady)
	return;
    bool savedActive = mActive;
    if (mAutomatic) {
	if (mActive && (mErrorCount * 3 >= mWordCount))
	    mActive = false;
	else if (!mActive && (mErrorCount * 3 < mWordCount))
	    mActive = true;
    }
    if (mActive != savedActive && !mRehighlightRequested) {
	emit activeChanged( mActive );
	mRehighlightRequested = true;
	QTimer::singleShot(100, this, SLOT(slotRehighlight()));
    }
}

bool DictSpellChecker::eventFilter(QObject* o, QEvent* e)
{
    //TODO mouse moves, pgup, home ctrl etc.
    if (o == textEdit() && (e->type() == QEvent::FocusIn)) {
	if (mSpell && mSpellKey != spellKey()) {
	    mSpellKey = spellKey();
	    DictSpellChecker::dictionaryChanged();
	}
    }

    if (o == textEdit() && (e->type() == QEvent::KeyPress)) {
	QKeyEvent *k = (QKeyEvent*)e;
	mAutoReady = true;
	if (k->key() == Key_Enter ||
	    k->key() == Key_Return ||
	    k->key() == Key_Up ||
	    k->key() == Key_Down ||
	    k->key() == Key_Left ||
	    k->key() == Key_Right ||
	    k->key() == Key_PageUp ||
	    k->key() == Key_PageDown ||
	    k->key() == Key_Home) {
	    if (mInitialMove) {
		if (!mRehighlightRequested) {
		    mRehighlightRequested = true;
		    QTimer::singleShot(0, this, SLOT(slotRehighlight()));
		}
		mInitialMove = false;
	    }
	} else {
	    mInitialMove = true;
	}
	if (k->key() == Key_Space ||
	    k->key() == Key_Enter ||
	    k->key() == Key_Return)
	    QTimer::singleShot(0, this, SLOT(slotAutoDetection()));
    }

    if (o == textEdit()->viewport() &&
	(e->type() == QEvent::MouseButtonPress)) {
	if (mInitialMove) {
	    if (!mRehighlightRequested) {
		mRehighlightRequested = true;
		QTimer::singleShot(0, this, SLOT(slotRehighlight()));
	    }
	    mInitialMove = false;
	}
    }

    return false;
}

} //namespace KMail

#include "syntaxhighlighter.moc"
<commit_msg>Optimization.<commit_after>/**
 * syntaxhighlighter.cpp
 *
 * Copyright (c) 2003 Trolltech AS
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "syntaxhighlighter.h"
#include <klocale.h>
#include <qcolor.h>
#include <qregexp.h>
#include <qsyntaxhighlighter.h>
#include <qtimer.h>
#include <kconfig.h>
#include <kdebug.h>
#include <kglobal.h>
#include <kspell.h>
#include <kmkernel.h>
#include <kapplication.h>

static int dummy, dummy2, dummy3;
static int *Okay = &dummy;
static int *NotOkay = &dummy2;
static int *Ignore = &dummy3;

namespace KMail {

MessageHighlighter::MessageHighlighter( QTextEdit *textEdit, SyntaxMode mode )
    : QSyntaxHighlighter( textEdit ), sMode( mode )
{
  KConfig *config = KMKernel::config();

  // block defines the lifetime of KConfigGroupSaver
  KConfigGroupSaver saver(config, "Reader");
  QColor defaultColor1( 0x00, 0x80, 0x00 ); // defaults from kmreaderwin.cpp
  QColor defaultColor2( 0x00, 0x70, 0x00 );
  QColor defaultColor3( 0x00, 0x60, 0x00 );
  col1 = QColor(kapp->palette().active().text());
  col2 = config->readColorEntry( "QuotedText3", &defaultColor3 );
  col3 = config->readColorEntry( "QuotedText2", &defaultColor2 );
  col4 = config->readColorEntry( "QuotedText1", &defaultColor1 );
  col5 = col1;
}

MessageHighlighter::~MessageHighlighter()
{
}

int MessageHighlighter::highlightParagraph( const QString &text, int )
{
    QString simplified = text;
    simplified = simplified.replace( QRegExp( "\\s" ), "" ).replace( QRegExp( "\\|" ), ">" );
    if ( simplified.startsWith( ">>>>" ) )
	setFormat( 0, text.length(), col1 );
    else if	( simplified.startsWith( ">>>" ) || simplified.startsWith( "> >	>" ) )
	setFormat( 0, text.length(), col2 );
    else if	( simplified.startsWith( ">>" )	|| simplified.startsWith( "> >"	) )
	setFormat( 0, text.length(), col3 );
    else if	( simplified.startsWith( ">" ) )
	setFormat( 0, text.length(), col4 );
    else
	setFormat( 0, text.length(), col5 );
    return 0;
}

SpellChecker::SpellChecker( QTextEdit *textEdit )
: MessageHighlighter( textEdit ), alwaysEndsWithSpace( TRUE )
{
  KConfig *config = KMKernel::config();
  KConfigGroupSaver saver(config, "Reader");
  QColor c = QColor("red");
  mColor = config->readColorEntry("NewMessage", &c);
}

SpellChecker::~SpellChecker()
{
}

int SpellChecker::highlightParagraph( const QString& text,
				      int parano )
{
    if (parano == -2)
	parano = 0;
    // leave #includes, diffs, and quoted replies alone
    QString diffAndCo( ">|" );

    bool isCode = diffAndCo.find(text[0]) != -1;

    if ( !text.endsWith(" ") )
	alwaysEndsWithSpace = FALSE;

    MessageHighlighter::highlightParagraph( text, -2 );

    if ( !isCode ) {
        int para, index;
	textEdit()->getCursorPosition( &para, &index );
	int len = text.length();
	if ( alwaysEndsWithSpace )
	    len--;

	currentPos = 0;
	currentWord = "";
	for ( int i = 0; i < len; i++ ) {
	    if ( text[i].isSpace() || text[i] == '-' ) {
		flushCurrentWord();
		currentPos = i + 1;
	    } else {
		currentWord += text[i];
	    }
	}
	if ( !text[len - 1].isLetter() ||
	     index + 1 != text.length() ||
	     para != parano)
	    flushCurrentWord();
    }
    return ++parano;
}

QStringList SpellChecker::personalWords()
{
    QStringList l;
    l.append( "KMail" );
    l.append( "KOrganizer" );
    l.append( "KHTML" );
    l.append( "KIO" );
    l.append( "KJS" );
    l.append( "Konqueror" );
    l.append( "KSpell" );
    l.append( "Kontact" );
    return l;
}

void SpellChecker::flushCurrentWord()
{
    while ( currentWord[0].isPunct() ) {
	currentWord = currentWord.mid( 1 );
	currentPos++;
    }

    QChar ch;
    while ( (ch = currentWord[(int) currentWord.length() - 1]).isPunct() &&
	     ch != '(' && ch != '@' )
	currentWord.truncate( currentWord.length() - 1 );

    if ( !currentWord.isEmpty() ) {
	if ( isMisspelled(currentWord) )
	    setFormat( currentPos, currentWord.length(), mColor );
//	    setMisspelled( currentPos, currentWord.length(), true );
    }
    currentWord = "";
}

QDict<int> DictSpellChecker::dict( 50021 );
QObject *DictSpellChecker::sDictionaryMonitor = 0;

DictSpellChecker::DictSpellChecker( QTextEdit *textEdit )
    : SpellChecker( textEdit )
{
    mAutoReady = false;
    mWordCount = 0;
    mErrorCount = 0;
    mActive = true;
    mAutomatic = true;
    textEdit->installEventFilter( this );
    textEdit->viewport()->installEventFilter( this );
    mInitialMove = true;
    mRehighlightRequested = false;
    mSpell = 0;
    mSpellKey = spellKey();
    if (!sDictionaryMonitor)
	sDictionaryMonitor = new QObject();
    slotDictionaryChanged();
    startTimer(2*1000);
}

DictSpellChecker::~DictSpellChecker()
{
    delete mSpell;
}

void DictSpellChecker::slotSpellReady( KSpell *spell )
{
    connect( sDictionaryMonitor, SIGNAL( destroyed() ),
	     this, SLOT( slotDictionaryChanged() ));
    mSpell = spell;
    QStringList l = SpellChecker::personalWords();
    for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
        mSpell->addPersonal( *it );
    }
    connect( spell, SIGNAL( misspelling (const QString &, const QStringList &, unsigned int) ),
	     this, SLOT( slotMisspelling (const QString &, const QStringList &, unsigned int)));
    if (!mRehighlightRequested) {
	mRehighlightRequested = true;
	QTimer::singleShot(0, this, SLOT(slotRehighlight()));
    }
}

bool DictSpellChecker::isMisspelled( const QString& word )
{
    // Normally isMisspelled would look up a dictionary and return
    // true or false, but kspell is asynchronous and slow so things
    // get tricky...

    // For auto detection ignore signature and reply prefix
    if (!mAutoReady)
	mAutoIgnoreDict.replace( word, Ignore );

    // "dict" is used as a cache to store the results of KSpell
    if (!dict.isEmpty() && dict[word] == NotOkay) {
	if (mAutoReady && (mAutoDict[word] != NotOkay)) {
	    if (!mAutoIgnoreDict[word]) {
		++mErrorCount;
		if (mAutoDict[word] != Okay)
		    ++mWordCount;
	    }
	    mAutoDict.replace( word, NotOkay );
	}
	return true && mActive;
    }
    if (!dict.isEmpty() && dict[word] == Okay) {
	if (mAutoReady && !mAutoDict[word]) {
	    mAutoDict.replace( word, Okay );
	    if (!mAutoIgnoreDict[word])
		++mWordCount;
	}
	return false;
    }

    // there is no 'spelt correctly' signal so default to Okay
    dict.replace( word, Okay );

    // yes I tried checkWord, the docs lie and it didn't give useful signals :-(
    if (mSpell)
	mSpell->check( word, false );
    return false;
}

void DictSpellChecker::slotMisspelling (const QString & originalword, const QStringList & suggestions, unsigned int)
{
    kdDebug(5006) << suggestions.join(" ").latin1() << endl;
    dict.replace( originalword, NotOkay );

    // this is slow but since kspell is async this will have to do for now
    if (!mRehighlightRequested) {
	mRehighlightRequested = true;
	QTimer::singleShot(0, this, SLOT(slotRehighlight()));
    }
}

void DictSpellChecker::dictionaryChanged()
{
    QObject *oldMonitor = sDictionaryMonitor;
    sDictionaryMonitor = new QObject();
    dict.clear();
    delete oldMonitor;
}

void DictSpellChecker::slotRehighlight()
{
    mRehighlightRequested = false;
    rehighlight();
    QTimer::singleShot(0, this, SLOT(slotAutoDetection()));
}

void DictSpellChecker::slotDictionaryChanged()
{
    delete mSpell;
    mSpell = 0;
    mWordCount = 0;
    mErrorCount = 0;
    mAutoDict.clear();
    new KSpell(0, i18n("Incremental Spellcheck - KMail"), this,
			 SLOT(slotSpellReady(KSpell*)));
}

QString DictSpellChecker::spellKey()
{
    //Note: Yes polling with timerEvent and reading directly from kglobals
    //Note: is evil. It would be nice if there was some kind of inter-process
    //Note: signal emitted when spelling configuration options are changed.
    KConfig *config = KGlobal::config();
    KConfigGroupSaver cs(config,"KSpell");
    config->reparseConfiguration();
    QString key;
    key += QString::number(config->readNumEntry("KSpell_NoRootAffix", 0));
    key += '/';
    key += QString::number(config->readNumEntry("KSpell_RunTogether", 0));
    key += '/';
    key += config->readEntry("KSpell_Dictionary", "");
    key += '/';
    key += QString::number(config->readNumEntry("KSpell_DictFromList", FALSE));
    key += '/';
    key += QString::number(config->readNumEntry ("KSpell_Encoding", KS_E_ASCII));
    key += '/';
    key += QString::number(config->readNumEntry ("KSpell_Client", KS_CLIENT_ISPELL));
    return key;
}

// Automatic spell checking support
// In auto spell checking mode disable as-you-type spell checking
// iff more than one third of words are spelt incorrectly.
//
// Words in the signature and reply prefix are ignored.
// Only unique words are counted.
void DictSpellChecker::slotAutoDetection()
{
    if (!mAutoReady)
	return;
    bool savedActive = mActive;
    if (mAutomatic) {
	if (mActive && (mErrorCount * 3 >= mWordCount))
	    mActive = false;
	else if (!mActive && (mErrorCount * 3 < mWordCount))
	    mActive = true;
    }
    if (mActive != savedActive && !mRehighlightRequested) {
	emit activeChanged( mActive );
	mRehighlightRequested = true;
	QTimer::singleShot(100, this, SLOT(slotRehighlight()));
    }
}

bool DictSpellChecker::eventFilter(QObject* o, QEvent* e)
{
    //TODO mouse moves, pgup, home ctrl etc.
    if (o == textEdit() && (e->type() == QEvent::FocusIn)) {
	if (mSpell && mSpellKey != spellKey()) {
	    mSpellKey = spellKey();
	    DictSpellChecker::dictionaryChanged();
	}
    }

    if (o == textEdit() && (e->type() == QEvent::KeyPress)) {
	QKeyEvent *k = (QKeyEvent*)e;
	mAutoReady = true;
	if (k->key() == Key_Enter ||
	    k->key() == Key_Return ||
	    k->key() == Key_Up ||
	    k->key() == Key_Down ||
	    k->key() == Key_Left ||
	    k->key() == Key_Right ||
	    k->key() == Key_PageUp ||
	    k->key() == Key_PageDown ||
	    k->key() == Key_Home) {
	    if (mInitialMove) {
		if (!mRehighlightRequested) {
		    mRehighlightRequested = true;
		    QTimer::singleShot(0, this, SLOT(slotRehighlight()));
		}
		mInitialMove = false;
	    }
	} else {
	    mInitialMove = true;
	}
	if (k->key() == Key_Space ||
	    k->key() == Key_Enter ||
	    k->key() == Key_Return)
	    QTimer::singleShot(0, this, SLOT(slotAutoDetection()));
    }

    if (o == textEdit()->viewport() &&
	(e->type() == QEvent::MouseButtonPress)) {
	if (mInitialMove) {
	    if (!mRehighlightRequested) {
		mRehighlightRequested = true;
		QTimer::singleShot(0, this, SLOT(slotRehighlight()));
	    }
	    mInitialMove = false;
	}
    }

    return false;
}

} //namespace KMail

#include "syntaxhighlighter.moc"
<|endoftext|>


<commit_before>#include "common.h"

#include <benchmark/benchmark.h>

#include <fmt/format.h>

#include <array>
#include <bitset>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

using namespace std;

// See discussion at
// https://stackoverflow.com/questions/40122141/preventing-compiler-optimizations-while-benchmarking
// about correct use of benchmark::DoNotOptimize and benchmark::ClobberMemory

static void BM_empty(benchmark::State& state)
{
  {
    for (auto _ : state)
      ;
  }
}
BENCHMARK(BM_empty);

static void BM_add(benchmark::State& state)
{
  count_t a = 0;

  for (auto _ : state)
  {
    a += 1;
  }

  benchmark::DoNotOptimize(a);
}
BENCHMARK(BM_add);

count_t non_inline_func(count_t x)
{
  return x;
}

static void BM_func_call(benchmark::State& state)
{
  count_t a = 0;
  count_t i{0};
  for (auto _ : state)
  {
    a += non_inline_func(i++);
  }

  benchmark::DoNotOptimize(a);
}
BENCHMARK(BM_func_call);

inline count_t inline_func(count_t x)
{
  return x;
}

static void BM_inline_func_call(benchmark::State& state)
{
  count_t a{0};
  count_t i{0};
  for (auto _ : state)
  {
    a += inline_func(i++);
  }

  benchmark::DoNotOptimize(a);
}
BENCHMARK(BM_inline_func_call);

static void BM_deref_ptr(benchmark::State& state)
{
  count_t a = 0;
  count_t* b = &a;
  count_t c;
  for (auto _ : state)
  {
    c = *b;
    benchmark::DoNotOptimize(c);
  }
}
BENCHMARK(BM_deref_ptr);

static void BM_write_64k(benchmark::State& state)
{
  const int memSize = 1024 * 64;
  unsigned char mem[memSize];
  for (auto _ : state)
  {
    for (int j = 0; j < memSize; ++j)
    {
      mem[j] = j;
      benchmark::DoNotOptimize(mem[memSize - 1]);
      benchmark::ClobberMemory();
    }
  }
}
BENCHMARK(BM_write_64k);

static void BM_read_64k(benchmark::State& state)
{
  const int memSize = 1024 * 64;
  unsigned char mem[memSize];
  for (int j = 0; j < memSize; ++j)
  {
    mem[j] = j;
    benchmark::DoNotOptimize(mem[j]);
  }
  benchmark::ClobberMemory();

  int result = 1;
  for (auto _ : state)
  {
    for (int j = 0; j < memSize; ++j)
    {
      result &= mem[j];
      benchmark::DoNotOptimize(mem[j]);
    }
  }
}

BENCHMARK(BM_read_64k);

static void BM_read_64k_vec(benchmark::State& state)
{
  const int memSize = 1024 * 64;
  vector<unsigned char> mem(memSize);
  for (int j = 0; j < memSize; ++j)
  {
    mem[j] = j;
    benchmark::DoNotOptimize(mem[j]);
  }
  benchmark::ClobberMemory();

  int result = 1;
  for (auto _ : state)
  {
    for (int j = 0; j < memSize; ++j)
    {
      result &= mem[j];
      benchmark::DoNotOptimize(mem[j]);
    }
  }
}
BENCHMARK(BM_read_64k_vec);

static void BM_read_64k_vec_at(benchmark::State& state)
{
  const int memSize = 1024 * 64;
  vector<unsigned char> mem(memSize);
  for (int j = 0; j < memSize; ++j)
  {
    mem[j] = j;
    benchmark::DoNotOptimize(mem[j]);
  }
  benchmark::ClobberMemory();

  int result = 1;
  for (auto _ : state)
  {
    for (int j = 0; j < memSize; ++j)
    {
      result &= mem.at(j);
      benchmark::DoNotOptimize(mem.at(j));
    }
  }
}
BENCHMARK(BM_read_64k_vec_at);

static void BM_read_64k_array_at(benchmark::State& state)
{
  const int memSize = 1024 * 64;
  array<unsigned char, memSize> mem;
  for (int j = 0; j < memSize; ++j)
  {
    mem[j] = j;
    benchmark::DoNotOptimize(mem[j]);
  }
  benchmark::ClobberMemory();

  int result = 1;
  for (auto _ : state)
  {
    for (int j = 0; j < memSize; ++j)
    {
      result &= mem.at(j);
    }
  }
}
BENCHMARK(BM_read_64k_array_at);

static void BM_try_catch(benchmark::State& state)
{
  int result = 0;
  for (auto _ : state)
  {
    try
    {
      throw exception();
    }
    catch (exception& e)
    {
      ++result;
    }
  }
  benchmark::DoNotOptimize(result);
}
BENCHMARK(BM_try_catch);

int a_func(int a)
{
  return 2 * a;
}

static void BM_func_ptr(benchmark::State& state)
{
  int result = 0;
  auto f_ptr = a_func;
  for (auto _ : state)
  {
    result += f_ptr(1);
  }
  benchmark::DoNotOptimize(result);
}
BENCHMARK(BM_func_ptr);

class C
{
public:
  int a_method(int a) { return 2 * a; }
  virtual int a_virtual_method(int a) { return 2 * a; }
};

class C2 : public C
{
public:
  virtual int a_virtual_method(int a) { return 3 * a; }
};

static void BM_nonvirt_method(benchmark::State& state)
{
  int result = 0;
  C c;
  for (auto _ : state)
  {
    result += c.a_method(1);
  }
  benchmark::DoNotOptimize(result);
}
BENCHMARK(BM_nonvirt_method);

static void BM_virt_method(benchmark::State& state)
{
  int result = 0;
  C2 c2;
  for (auto _ : state)
  {
    result += c2.a_virtual_method(1);
  }
  benchmark::DoNotOptimize(result);
}
BENCHMARK(BM_virt_method);

vector<string> many_strings()
{
  return vector<string>(10000, "abcdefghijklmnopqrstuvwxyz");
}

vector<string> many_strings_static()
{
  static vector<string> result(10000, "abcdefghijklmnopqrstuvwxyz");
  return result;
}

static void BM_string_concat_plus(benchmark::State& state)
{
  for (auto _ : state)
  {
    string result;
    for (const string& s : many_strings())
    {
      result += s;
    }
    benchmark::DoNotOptimize(result);
  }
}
BENCHMARK(BM_string_concat_plus);

static void BM_string_concat_ostringstream(benchmark::State& state)
{
  for (auto _ : state)
  {
    ostringstream result;
    for (const string& s : many_strings())
    {
      result << s;
    }
    benchmark::DoNotOptimize(result);
  }
}
BENCHMARK(BM_string_concat_ostringstream);

static void BM_lambda(benchmark::State& state)
{
  long result{0};
  long a{0};
  long i{0};
  auto lambda = [](long x, long y) -> long { return x + y; };
  for (auto _ : state)
  {
    result += lambda(a, i);
    ++a;
    ++i;
  }
  benchmark::DoNotOptimize(result);
}
BENCHMARK(BM_lambda);

static void BM_xor(benchmark::State& state)
{
  unsigned char c = 1;
  int i{0};
  for (auto _ : state)
  {
    c ^= i++;
  }
  benchmark::DoNotOptimize(c);
}
BENCHMARK(BM_xor);

void BM_xor_bitset(benchmark::State& state)
{
  bitset<8> c = 1;
  int i{0};
  for (auto _ : state)
  {
    c ^= i++;
  }
  benchmark::DoNotOptimize(c.to_ulong());
}
BENCHMARK(BM_xor_bitset);

BENCHMARK_MAIN();
<commit_msg>Fully qualify std::<commit_after>#include "common.h"

#include <benchmark/benchmark.h>

#include <fmt/format.h>

#include <array>
#include <bitset>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

// See discussion at
// https://stackoverflow.com/questions/40122141/preventing-compiler-optimizations-while-benchmarking
// about correct use of benchmark::DoNotOptimize and benchmark::ClobberMemory

static void BM_empty(benchmark::State& state)
{
  {
    for (auto _ : state)
      ;
  }
}
BENCHMARK(BM_empty);

static void BM_add(benchmark::State& state)
{
  count_t a = 0;

  for (auto _ : state)
  {
    a += 1;
  }

  benchmark::DoNotOptimize(a);
}
BENCHMARK(BM_add);

count_t non_inline_func(count_t x)
{
  return x;
}

static void BM_func_call(benchmark::State& state)
{
  count_t a = 0;
  count_t i{0};
  for (auto _ : state)
  {
    a += non_inline_func(i++);
  }

  benchmark::DoNotOptimize(a);
}
BENCHMARK(BM_func_call);

inline count_t inline_func(count_t x)
{
  return x;
}

static void BM_inline_func_call(benchmark::State& state)
{
  count_t a{0};
  count_t i{0};
  for (auto _ : state)
  {
    a += inline_func(i++);
  }

  benchmark::DoNotOptimize(a);
}
BENCHMARK(BM_inline_func_call);

static void BM_deref_ptr(benchmark::State& state)
{
  count_t a = 0;
  count_t* b = &a;
  count_t c;
  for (auto _ : state)
  {
    c = *b;
    benchmark::DoNotOptimize(c);
  }
}
BENCHMARK(BM_deref_ptr);

static void BM_write_64k(benchmark::State& state)
{
  const int memSize = 1024 * 64;
  unsigned char mem[memSize];
  for (auto _ : state)
  {
    for (int j = 0; j < memSize; ++j)
    {
      mem[j] = j;
      benchmark::DoNotOptimize(mem[memSize - 1]);
      benchmark::ClobberMemory();
    }
  }
}
BENCHMARK(BM_write_64k);

static void BM_read_64k(benchmark::State& state)
{
  const int memSize = 1024 * 64;
  unsigned char mem[memSize];
  for (int j = 0; j < memSize; ++j)
  {
    mem[j] = j;
    benchmark::DoNotOptimize(mem[j]);
  }
  benchmark::ClobberMemory();

  int result = 1;
  for (auto _ : state)
  {
    for (int j = 0; j < memSize; ++j)
    {
      result &= mem[j];
      benchmark::DoNotOptimize(mem[j]);
    }
  }
}

BENCHMARK(BM_read_64k);

static void BM_read_64k_vec(benchmark::State& state)
{
  const int memSize = 1024 * 64;
  std::vector<unsigned char> mem(memSize);
  for (int j = 0; j < memSize; ++j)
  {
    mem[j] = j;
    benchmark::DoNotOptimize(mem[j]);
  }
  benchmark::ClobberMemory();

  int result = 1;
  for (auto _ : state)
  {
    for (int j = 0; j < memSize; ++j)
    {
      result &= mem[j];
      benchmark::DoNotOptimize(mem[j]);
    }
  }
}
BENCHMARK(BM_read_64k_vec);

static void BM_read_64k_vec_at(benchmark::State& state)
{
  const int memSize = 1024 * 64;
  std::vector<unsigned char> mem(memSize);
  for (int j = 0; j < memSize; ++j)
  {
    mem[j] = j;
    benchmark::DoNotOptimize(mem[j]);
  }
  benchmark::ClobberMemory();

  int result = 1;
  for (auto _ : state)
  {
    for (int j = 0; j < memSize; ++j)
    {
      result &= mem.at(j);
      benchmark::DoNotOptimize(mem.at(j));
    }
  }
}
BENCHMARK(BM_read_64k_vec_at);

static void BM_read_64k_array_at(benchmark::State& state)
{
  const int memSize = 1024 * 64;
  std::array<unsigned char, memSize> mem;
  for (int j = 0; j < memSize; ++j)
  {
    mem[j] = j;
    benchmark::DoNotOptimize(mem[j]);
  }
  benchmark::ClobberMemory();

  int result = 1;
  for (auto _ : state)
  {
    for (int j = 0; j < memSize; ++j)
    {
      result &= mem.at(j);
    }
  }
}
BENCHMARK(BM_read_64k_array_at);

static void BM_try_catch(benchmark::State& state)
{
  int result = 0;
  for (auto _ : state)
  {
    try
    {
      throw std::exception();
    }
    catch (std::exception& e)
    {
      ++result;
    }
  }
  benchmark::DoNotOptimize(result);
}
BENCHMARK(BM_try_catch);

int a_func(int a)
{
  return 2 * a;
}

static void BM_func_ptr(benchmark::State& state)
{
  int result = 0;
  auto f_ptr = a_func;
  for (auto _ : state)
  {
    result += f_ptr(1);
  }
  benchmark::DoNotOptimize(result);
}
BENCHMARK(BM_func_ptr);

class C
{
public:
  int a_method(int a) { return 2 * a; }
  virtual int a_virtual_method(int a) { return 2 * a; }
};

class C2 : public C
{
public:
  virtual int a_virtual_method(int a) { return 3 * a; }
};

static void BM_nonvirt_method(benchmark::State& state)
{
  int result = 0;
  C c;
  for (auto _ : state)
  {
    result += c.a_method(1);
  }
  benchmark::DoNotOptimize(result);
}
BENCHMARK(BM_nonvirt_method);

static void BM_virt_method(benchmark::State& state)
{
  int result = 0;
  C2 c2;
  for (auto _ : state)
  {
    result += c2.a_virtual_method(1);
  }
  benchmark::DoNotOptimize(result);
}
BENCHMARK(BM_virt_method);

std::vector<std::string> many_strings()
{
  return std::vector<std::string>(10000, "abcdefghijklmnopqrstuvwxyz");
}

std::vector<std::string> many_strings_static()
{
  static std::vector<std::string> result(10000, "abcdefghijklmnopqrstuvwxyz");
  return result;
}

static void BM_string_concat_plus(benchmark::State& state)
{
  for (auto _ : state)
  {
    std::string result;
    for (const std::string& s : many_strings())
    {
      result += s;
    }
    benchmark::DoNotOptimize(result);
  }
}
BENCHMARK(BM_string_concat_plus);

static void BM_string_concat_ostringstream(benchmark::State& state)
{
  for (auto _ : state)
  {
    std::ostringstream result;
    for (const std::string& s : many_strings())
    {
      result << s;
    }
    benchmark::DoNotOptimize(result);
  }
}
BENCHMARK(BM_string_concat_ostringstream);

static void BM_lambda(benchmark::State& state)
{
  long result{0};
  long a{0};
  long i{0};
  auto lambda = [](long x, long y) -> long { return x + y; };
  for (auto _ : state)
  {
    result += lambda(a, i);
    ++a;
    ++i;
  }
  benchmark::DoNotOptimize(result);
}
BENCHMARK(BM_lambda);

static void BM_xor(benchmark::State& state)
{
  unsigned char c = 1;
  int i{0};
  for (auto _ : state)
  {
    c ^= i++;
  }
  benchmark::DoNotOptimize(c);
}
BENCHMARK(BM_xor);

void BM_xor_bitset(benchmark::State& state)
{
  std::bitset<8> c = 1;
  int i{0};
  for (auto _ : state)
  {
    c ^= i++;
  }
  benchmark::DoNotOptimize(c.to_ulong());
}
BENCHMARK(BM_xor_bitset);

BENCHMARK_MAIN();
<|endoftext|>


<commit_before>/*
    This file is part of KOrganizer.

    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include "resourceview.h"

#include <klistview.h>
#include <klocale.h>
#include <kdebug.h>
#include <kglobal.h>
#include <kmessagebox.h>
#include <kresources/resource.h>
#include <kresources/resourceconfigdlg.h>

#include <qinputdialog.h>
#include <qlayout.h>

using namespace KCal;

ResourceItem::ResourceItem( ResourceCalendar *resource, ResourceView *view,
                            KListView *parent ) :
      QCheckListItem( parent, resource->resourceName(), CheckBox ),
      mResource( resource ), mView( view )
{
  setOn( mResource->isActive() );
}

void ResourceItem::stateChange( bool active )
{
  if ( active ) {
    mResource->open();
    mResource->load();
  } else {
    mResource->save();
    mResource->close();
  }
  mResource->setActive( active );

  mView->emitResourcesChanged();
}

ResourceView::ResourceView( KCal::CalendarResourceManager *manager,
                            QWidget *parent, const char *name )
  : QWidget( parent, name ),
    mManager( manager )
{
  mListView = new KListView( this );
  mListView->addColumn( i18n("Calendar") );

  QBoxLayout *topLayout = new QVBoxLayout( this );
  topLayout->addWidget( mListView );

  QBoxLayout *buttonLayout = new QHBoxLayout( this );

  add = new QPushButton( QString("add"), this, "add" );
  del = new QPushButton( QString("del"), this, "del" );
  edit = new QPushButton( QString("edit"), this, "edit" );
  del->setDisabled( true );
  edit->setDisabled( true );
  buttonLayout->addWidget( add );
  buttonLayout->addWidget( del );
  buttonLayout->addWidget( edit );
  topLayout->addLayout( buttonLayout );

  connect( mListView, SIGNAL(clicked(QListViewItem*)),
           SLOT(currentChanged(QListViewItem*)) );
  connect( add, SIGNAL(clicked()), SLOT(addResource()) );
  connect( del, SIGNAL(clicked()), SLOT(removeResource()) );
  connect( edit, SIGNAL(clicked()), SLOT(editResource()) );

  updateView();
}

ResourceView::~ResourceView()
{
}

void ResourceView::updateView()
{
  mListView->clear();

  KCal::CalendarResourceManager::Iterator it;
  for( it = mManager->begin(); it != mManager->end(); ++it ) {
    new ResourceItem(  (*it), this, mListView );
  }
}

void ResourceView::emitResourcesChanged()
{
  emit resourcesChanged();
}

void ResourceView::addResource()
{
  kdDebug() << "ResourceView::addResource()" << endl;
  QStringList types = mManager->resourceTypeNames();
  bool ok = false;
  QString type = QInputDialog::getItem( i18n( "Resource Configuration" ),
      i18n( "Please select type of the new resource:" ), types, 0, false, &ok, this );
  if ( !ok )
    return;

  // Create new resource
  ResourceCalendar *resource = mManager->createResource( type );
  if( !resource ) {
    KMessageBox::error( this, i18n("Unable to create resource of type '%1'.")
                              .arg( type ) );
    return;
  }

  resource->setResourceName( type + "-resource" );

  KRES::ResourceConfigDlg dlg( this, QString("calendar"), resource, "ResourceConfigDlg" );

  if ( dlg.exec() ) {
    mManager->add( resource );
    ResourceItem *item = new ResourceItem( resource, this, mListView );
  } else {
    delete resource;
    resource = 0;
  }
}

void ResourceView::removeResource()
{
  QListViewItem *item = mListView->currentItem();
  ResourceItem *rItem = static_cast<ResourceItem*>( item );

  if ( !rItem )
    return;

  int km = KMessageBox::warningYesNo(this,
        i18n("Do you really want to delete the resource")+ " '" +
             rItem->resource()->resourceName() + "'?" );
  if ( km == KMessageBox::No ) return;

  if ( rItem->resource() == mManager->standardResource() ) {
    KMessageBox::sorry( this, i18n( "You cannot remove your standard resource!" ) );
    return;
  }

  mManager->remove( rItem->resource() );

  mListView->takeItem( item );
  delete item;

}

void ResourceView::editResource()
{
  QListViewItem *item = mListView->currentItem();
  ResourceItem *rItem = static_cast<ResourceItem*>( item );
  if ( !rItem )
    return;

  ResourceCalendar *resource = rItem->resource();

  KRES::ResourceConfigDlg dlg( this, QString("calendar"), resource, "ResourceConfigDlg" );

  if ( dlg.exec() ) {
    rItem->setText( 0, resource->resourceName() );

    mManager->resourceChanged( resource );
  }
}

void ResourceView::currentChanged( QListViewItem *item)
{
  bool selected = true;
  if ( !item ) selected = false;
  del->setEnabled( selected );
  edit->setEnabled( selected );
}

#include "resourceview.moc"
<commit_msg>String correction<commit_after>/*
    This file is part of KOrganizer.

    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include "resourceview.h"

#include <klistview.h>
#include <klocale.h>
#include <kdebug.h>
#include <kglobal.h>
#include <kmessagebox.h>
#include <kresources/resource.h>
#include <kresources/resourceconfigdlg.h>

#include <qinputdialog.h>
#include <qlayout.h>

using namespace KCal;

ResourceItem::ResourceItem( ResourceCalendar *resource, ResourceView *view,
                            KListView *parent ) :
      QCheckListItem( parent, resource->resourceName(), CheckBox ),
      mResource( resource ), mView( view )
{
  setOn( mResource->isActive() );
}

void ResourceItem::stateChange( bool active )
{
  if ( active ) {
    mResource->open();
    mResource->load();
  } else {
    mResource->save();
    mResource->close();
  }
  mResource->setActive( active );

  mView->emitResourcesChanged();
}

ResourceView::ResourceView( KCal::CalendarResourceManager *manager,
                            QWidget *parent, const char *name )
  : QWidget( parent, name ),
    mManager( manager )
{
  mListView = new KListView( this );
  mListView->addColumn( i18n("Calendar") );

  QBoxLayout *topLayout = new QVBoxLayout( this );
  topLayout->addWidget( mListView );

  QBoxLayout *buttonLayout = new QHBoxLayout( this );

  add = new QPushButton( i18n("Add"), this, "add" );
  del = new QPushButton( i18n("Remove"), this, "del" );
  edit = new QPushButton( i18n("Edit"), this, "edit" );
  del->setDisabled( true );
  edit->setDisabled( true );
  buttonLayout->addWidget( add );
  buttonLayout->addWidget( del );
  buttonLayout->addWidget( edit );
  topLayout->addLayout( buttonLayout );

  connect( mListView, SIGNAL(clicked(QListViewItem*)),
           SLOT(currentChanged(QListViewItem*)) );
  connect( add, SIGNAL(clicked()), SLOT(addResource()) );
  connect( del, SIGNAL(clicked()), SLOT(removeResource()) );
  connect( edit, SIGNAL(clicked()), SLOT(editResource()) );

  updateView();
}

ResourceView::~ResourceView()
{
}

void ResourceView::updateView()
{
  mListView->clear();

  KCal::CalendarResourceManager::Iterator it;
  for( it = mManager->begin(); it != mManager->end(); ++it ) {
    new ResourceItem(  (*it), this, mListView );
  }
}

void ResourceView::emitResourcesChanged()
{
  emit resourcesChanged();
}

void ResourceView::addResource()
{
  kdDebug() << "ResourceView::addResource()" << endl;
  QStringList types = mManager->resourceTypeNames();
  bool ok = false;
  QString type = QInputDialog::getItem( i18n( "Resource Configuration" ),
      i18n( "Please select type of the new resource:" ), types, 0, false, &ok, this );
  if ( !ok )
    return;

  // Create new resource
  ResourceCalendar *resource = mManager->createResource( type );
  if( !resource ) {
    KMessageBox::error( this, i18n("Unable to create resource of type '%1'.")
                              .arg( type ) );
    return;
  }

  resource->setResourceName( type + "-resource" );

  KRES::ResourceConfigDlg dlg( this, QString("calendar"), resource, "ResourceConfigDlg" );

  if ( dlg.exec() ) {
    mManager->add( resource );
    ResourceItem *item = new ResourceItem( resource, this, mListView );
  } else {
    delete resource;
    resource = 0;
  }
}

void ResourceView::removeResource()
{
  QListViewItem *item = mListView->currentItem();
  ResourceItem *rItem = static_cast<ResourceItem*>( item );

  if ( !rItem )
    return;

  int km = KMessageBox::warningYesNo(this,
        i18n("Do you really want to delete the resource")+ " '" +
             rItem->resource()->resourceName() + "'?" );
  if ( km == KMessageBox::No ) return;

  if ( rItem->resource() == mManager->standardResource() ) {
    KMessageBox::sorry( this, i18n( "You cannot remove your standard resource!" ) );
    return;
  }

  mManager->remove( rItem->resource() );

  mListView->takeItem( item );
  delete item;

}

void ResourceView::editResource()
{
  QListViewItem *item = mListView->currentItem();
  ResourceItem *rItem = static_cast<ResourceItem*>( item );
  if ( !rItem )
    return;

  ResourceCalendar *resource = rItem->resource();

  KRES::ResourceConfigDlg dlg( this, QString("calendar"), resource, "ResourceConfigDlg" );

  if ( dlg.exec() ) {
    rItem->setText( 0, resource->resourceName() );

    mManager->resourceChanged( resource );
  }
}

void ResourceView::currentChanged( QListViewItem *item)
{
  bool selected = true;
  if ( !item ) selected = false;
  del->setEnabled( selected );
  edit->setEnabled( selected );
}

#include "resourceview.moc"
<|endoftext|>


<commit_before>//
// Copyright (c) 2008-2017 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include <Urho3D/Core/CoreEvents.h>
#include <Urho3D/Scene/Component.h>
#include <Toolbox/SystemUI/AttributeInspector.h>
#include "UndoManager.h"

namespace Urho3D
{

namespace Undo
{

AttributeState::AttributeState(Serializable* item, const String& name, const Variant& value)
    : item_(item)
    , name_(name)
    , value_(value)
{
}

void AttributeState::Apply()
{
    item_->SetAttribute(name_, value_);
    item_->ApplyAttributes();
}

bool AttributeState::Equals(State* other) const
{
    auto other_ = dynamic_cast<AttributeState*>(other);
    if (!other_)
        return false;

    if (item_ != other_->item_)
        return false;

    return value_ == other_->value_;
}

String AttributeState::ToString() const
{
    return Urho3D::ToString("AttributeState %s = %s", name_.CString(), value_.ToString().CString());
}

ElementParentState::ElementParentState(UIElement* item, UIElement* parent) : item_(item), parent_(parent)
{
    if (parent)
        index_ = parent->FindChild(item);
}

void ElementParentState::Apply()
{
    if (parent_.NotNull())
        item_->SetParent(parent_, index_);
    else
        item_->Remove();
}

bool ElementParentState::Equals(State* other) const
{
    auto other_ = dynamic_cast<ElementParentState*>(other);

    if (other_ == nullptr)
        return false;

    return item_ == other_->item_ && parent_ == other_->parent_ && index_ == other_->index_;
}

String ElementParentState::ToString() const
{
    return Urho3D::ToString("ElementParentState parent = %p child = %p index = %d", parent_.Get(), item_.Get(), index_);
}

NodeParentState::NodeParentState(Node* item, Node* parent) : item_(item), parent_(parent)
{
    if (parent)
        index_ = parent->GetChildren().IndexOf(SharedPtr<Node>(item));
}

void NodeParentState::Apply()
{
    if (parent_.NotNull())
        parent_->AddChild(item_, index_);
    else
        item_->Remove();
}

bool NodeParentState::Equals(State* other) const
{
    auto other_ = dynamic_cast<NodeParentState*>(other);

    if (other_ == nullptr)
        return false;

    return item_ == other_->item_ && parent_ == other_->parent_ && index_ == other_->index_;
}

String NodeParentState::ToString() const
{
    return Urho3D::ToString("NodeParentState parent = %p child = %p index = %d", parent_.Get(), item_.Get(), index_);
}

ComponentParentState::ComponentParentState(Component* item, Node* parent) : item_(item), parent_(parent)
{
    id_ = item->GetID();
}

void ComponentParentState::Apply()
{
    if (parent_.NotNull())
        parent_->AddComponent(item_, id_, LOCAL);   // Replication mode is irrelevant, because it is used only for ID
                                                    // creation.
    else
        item_->Remove();
}

bool ComponentParentState::Equals(State* other) const
{
    auto other_ = dynamic_cast<ComponentParentState*>(other);

    if (other_ == nullptr)
        return false;

    return item_ == other_->item_ && parent_ == other_->parent_ && id_ == other_->id_;
}

String ComponentParentState::ToString() const
{
    return Urho3D::ToString("ComponentParentState parent = %p child = %p id = %d", parent_.Get(), item_.Get(), id_);
}

XMLVariantState::XMLVariantState(const XMLElement& item, const Variant& value) : item_(item), value_(value)
{
}

void XMLVariantState::Apply()
{
    item_.SetVariant(value_);
}

bool XMLVariantState::Equals(State* other) const
{
    auto other_ = dynamic_cast<XMLVariantState*>(other);

    if (other_ == nullptr)
        return false;

    return item_ == other_->item_ && value_ == other_->value_;
}

String XMLVariantState::ToString() const
{
    return Urho3D::ToString("XMLVariantState value = %s", value_.ToString().CString());
}

XMLParentState::XMLParentState(const XMLElement& item, const XMLElement& parent) : item_(item), parent_(parent)
{
}

void XMLParentState::Apply()
{
    if (parent_.NotNull())
        parent_.AppendChild(item_);
    else
        item_.GetParent().RemoveChild(item_);
}

bool XMLParentState::Equals(State* other) const
{
    auto other_ = dynamic_cast<XMLParentState*>(other);

    if (other_ == nullptr)
        return false;

    return item_.GetNode() == other_->item_.GetNode() && parent_.GetNode() == other_->parent_.GetNode();
}

String XMLParentState::ToString() const
{
    return Urho3D::ToString("XMLParentState parent = %s", parent_.IsNull() ? "null" : "set");
}

void StateCollection::Apply()
{
    for (auto& state : states_)
        state->Apply();
}

bool StateCollection::Contains(State* other) const
{
    for (const auto& state : states_)
    {
        if (state->Equals(other))
            return true;
    }
    return false;
}

bool StateCollection::PushUnique(const SharedPtr<State>& state)
{
    if (!Contains(state))
    {
        states_.Push(state);
        return true;
    }
    return false;
}

Manager::Manager(Context* ctx) : Object(ctx)
{
    SubscribeToEvent(E_ENDFRAME, [&](StringHash, VariantMap&)
    {
        if (!trackingSuspended_)
        {
            assert(previous_.Size() == next_.Size());

            if (previous_.Size())
            {
                // When stack is empty we insert two items - old state and new state. When stack already has states
                // saved - we save old state to the state collection at the end of the stack and insert new
                // collection for a new state.
                if (stack_.Empty())
                {
                    stack_.Resize(1);
                    index_++;
                }

                for (auto& state : previous_)
                {
                    if (stack_.Back().PushUnique(state))
                        URHO3D_LOGDEBUGF("UNDO: Save %d: %s", index_, state->ToString().CString());
                }

                index_++;
                stack_.Resize(index_ + 1);

                for (auto& state : next_)
                {
                    if (stack_.Back().PushUnique(state))
                        URHO3D_LOGDEBUGF("UNDO: Save %d: %s", index_, state->ToString().CString());
                }
                previous_.Clear();
                next_.Clear();
            }
        }
    });
}

void Manager::Undo()
{
    ApplyStateFromStack(false);
}

void Manager::Redo()
{
    ApplyStateFromStack(true);
}

void Manager::Clear()
{
    previous_.Clear();
    next_.Clear();
    stack_.Clear();
    index_ = -1;
}

void Manager::ApplyStateFromStack(bool forward)
{
    trackingSuspended_ = true;
    int direction = forward ? 1 : -1;
    for (;;)
    {
        index_ += direction;
        if (index_ < 0 || index_ >= stack_.Size())
            break;

        stack_[index_].Apply();
        URHO3D_LOGDEBUGF("Undo: apply %d", index_);
    }
    index_ = Clamp<int32_t>(index_, 0, stack_.Size() - 1);
    trackingSuspended_ = false;
}

void Manager::TrackState(Serializable* item, const String& name, const Variant& value, const Variant& oldValue)
{
    // Item has it's state already modified, manually track the change.
    TrackBefore<AttributeState>(item, name, oldValue);
    TrackAfter<AttributeState>(item, name, value);
}

XMLElement Manager::XMLCreate(XMLElement& parent, const String& name)
{
    auto element = parent.CreateChild(name);
    TrackBefore<XMLParentState>(element);                       // "Removed" element has empty variant value.
    TrackAfter<XMLParentState>(element, element.GetParent());   // When value is set element exists.
    return element;
}

void Manager::XMLRemove(XMLElement& element)
{
    TrackBefore<XMLParentState>(element, element.GetParent());  // When value is set element exists.
    TrackAfter<XMLParentState>(element);                        // "Removed" element has empty variant value.
    element.Remove();
}

void Manager::XMLSetVariantValue(XMLElement& element, const Variant& value)
{
    TrackBefore<XMLVariantState>(element, element.GetVariant());
    TrackAfter<XMLVariantState>(element, value);
    element.SetVariantValue(value);
}

template<typename T, typename... Args>
void Manager::TrackBefore(Args... args)
{
    previous_.Push(SharedPtr<State>(new T(args...)));
}

template<typename T, typename... Args>
void Manager::TrackAfter(Args... args)
{
    next_.Push(SharedPtr<State>(new T(args...)));
}
void Manager::Connect(Scene* scene)
{
    SubscribeToEvent(scene, E_NODEADDED, [&](StringHash, VariantMap& args) {
        if (trackingSuspended_)
            return;

        using namespace NodeRemoved;
        auto node = dynamic_cast<Node*>(args[P_NODE].GetPtr());
        auto parent = dynamic_cast<Node*>(args[P_PARENT].GetPtr());

        TrackBefore<NodeParentState>(node, nullptr);        // Removed from the scene state
        TrackAfter<NodeParentState>(node, parent);          // Present in the scene state
    });

    SubscribeToEvent(scene, E_NODEREMOVED, [&](StringHash, VariantMap& args) {
        if (trackingSuspended_)
            return;

        using namespace NodeRemoved;
        auto node = dynamic_cast<Node*>(args[P_NODE].GetPtr());
        auto parent = dynamic_cast<Node*>(args[P_PARENT].GetPtr());

        TrackBefore<NodeParentState>(node, parent);        // Present in the scene state
        TrackAfter<NodeParentState>(node, nullptr);        // Removed from the scene state
    });

    SubscribeToEvent(scene, E_COMPONENTADDED, [&](StringHash, VariantMap& args) {
        if (trackingSuspended_)
            return;

        using namespace ComponentAdded;
        auto component = dynamic_cast<Component*>(args[P_COMPONENT].GetPtr());
        auto parent = dynamic_cast<Node*>(args[P_NODE].GetPtr());

        TrackBefore<ComponentParentState>(component, nullptr);
        TrackAfter<ComponentParentState>(component, parent);
    });

    SubscribeToEvent(scene, E_COMPONENTREMOVED, [&](StringHash, VariantMap& args) {
        if (trackingSuspended_)
            return;

        using namespace ComponentAdded;
        auto component = dynamic_cast<Component*>(args[P_COMPONENT].GetPtr());
        auto parent = dynamic_cast<Node*>(args[P_NODE].GetPtr());

        TrackBefore<ComponentParentState>(component, parent);
        TrackAfter<ComponentParentState>(component, nullptr);
    });
}

void Manager::Connect(AttributeInspector* inspector)
{
    SubscribeToEvent(inspector, E_ATTRIBUTEINSPECTVALUEMODIFIED, [&](StringHash, VariantMap& args) {
        if (trackingSuspended_)
            return;

        using namespace AttributeInspectorValueModified;
        auto item = dynamic_cast<Serializable*>(args[P_SERIALIZABLE].GetPtr());

        auto attributeName = reinterpret_cast<AttributeInfo*>(args[P_ATTRIBUTEINFO].GetVoidPtr())->name_;
        TrackBefore<AttributeState>(item, attributeName, args[P_OLDVALUE]);
        TrackAfter<AttributeState>(item, attributeName, args[P_NEWVALUE]);
    });
}

void Manager::Connect(UIElement* root)
{
    SubscribeToEvent(E_ELEMENTADDED, [&, root](StringHash, VariantMap& args) {
        if (trackingSuspended_)
            return;

        using namespace ElementAdded;
        auto element = dynamic_cast<UIElement*>(args[P_ELEMENT].GetPtr());
        auto parent = dynamic_cast<UIElement*>(args[P_PARENT].GetPtr());
        auto eventRoot = dynamic_cast<UIElement*>(args[P_ROOT].GetPtr());

        if (root != eventRoot)
            return;

        TrackBefore<ElementParentState>(element, nullptr);        // Removed from the scene state
        TrackAfter<ElementParentState>(element, parent);          // Present in the scene state
    });

    SubscribeToEvent(E_ELEMENTREMOVED, [&, root](StringHash, VariantMap& args) {
        if (trackingSuspended_)
            return;

        using namespace ElementRemoved;
        auto element = dynamic_cast<UIElement*>(args[P_ELEMENT].GetPtr());
        auto parent = dynamic_cast<UIElement*>(args[P_PARENT].GetPtr());
        auto eventRoot = dynamic_cast<UIElement*>(args[P_ROOT].GetPtr());

        if (root != eventRoot)
            return;

        TrackBefore<ElementParentState>(element, parent);        // Removed from the scene state
        TrackAfter<ElementParentState>(element, nullptr);        // Present in the scene state
    });
}

void Manager::Connect(Gizmo* gizmo)
{
    SubscribeToEvent(gizmo, E_GIZMONODEMODIFIED, [&](StringHash, VariantMap& args) {
        using namespace GizmoNodeModified;
        auto node = dynamic_cast<Node*>(args[P_NODE].GetPtr());
        auto oldTransform = args[P_OLDTRANSFORM].GetMatrix3x4();
        auto newTransform = args[P_NEWTRANSFORM].GetMatrix3x4();

        TrackBefore<AttributeState>(node, "Position", oldTransform.Translation());
        TrackBefore<AttributeState>(node, "Rotation", oldTransform.Rotation());
        TrackBefore<AttributeState>(node, "Scale", oldTransform.Scale());

        TrackAfter<AttributeState>(node, "Position", newTransform.Translation());
        TrackAfter<AttributeState>(node, "Rotation", newTransform.Rotation());
        TrackAfter<AttributeState>(node, "Scale", newTransform.Scale());
    });
}

}

}
<commit_msg>Fix state skipping bug in Undo::Manager<commit_after>//
// Copyright (c) 2008-2017 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include <Urho3D/Core/CoreEvents.h>
#include <Urho3D/Scene/Component.h>
#include <Toolbox/SystemUI/AttributeInspector.h>
#include "UndoManager.h"

namespace Urho3D
{

namespace Undo
{

AttributeState::AttributeState(Serializable* item, const String& name, const Variant& value)
    : item_(item)
    , name_(name)
    , value_(value)
{
}

void AttributeState::Apply()
{
    item_->SetAttribute(name_, value_);
    item_->ApplyAttributes();
}

bool AttributeState::Equals(State* other) const
{
    auto other_ = dynamic_cast<AttributeState*>(other);
    if (!other_)
        return false;

    if (item_ != other_->item_)
        return false;

    return value_ == other_->value_;
}

String AttributeState::ToString() const
{
    return Urho3D::ToString("AttributeState %s = %s", name_.CString(), value_.ToString().CString());
}

ElementParentState::ElementParentState(UIElement* item, UIElement* parent) : item_(item), parent_(parent)
{
    if (parent)
        index_ = parent->FindChild(item);
}

void ElementParentState::Apply()
{
    if (parent_.NotNull())
        item_->SetParent(parent_, index_);
    else
        item_->Remove();
}

bool ElementParentState::Equals(State* other) const
{
    auto other_ = dynamic_cast<ElementParentState*>(other);

    if (other_ == nullptr)
        return false;

    return item_ == other_->item_ && parent_ == other_->parent_ && index_ == other_->index_;
}

String ElementParentState::ToString() const
{
    return Urho3D::ToString("ElementParentState parent = %p child = %p index = %d", parent_.Get(), item_.Get(), index_);
}

NodeParentState::NodeParentState(Node* item, Node* parent) : item_(item), parent_(parent)
{
    if (parent)
        index_ = parent->GetChildren().IndexOf(SharedPtr<Node>(item));
}

void NodeParentState::Apply()
{
    if (parent_.NotNull())
        parent_->AddChild(item_, index_);
    else
        item_->Remove();
}

bool NodeParentState::Equals(State* other) const
{
    auto other_ = dynamic_cast<NodeParentState*>(other);

    if (other_ == nullptr)
        return false;

    return item_ == other_->item_ && parent_ == other_->parent_ && index_ == other_->index_;
}

String NodeParentState::ToString() const
{
    return Urho3D::ToString("NodeParentState parent = %p child = %p index = %d", parent_.Get(), item_.Get(), index_);
}

ComponentParentState::ComponentParentState(Component* item, Node* parent) : item_(item), parent_(parent)
{
    id_ = item->GetID();
}

void ComponentParentState::Apply()
{
    if (parent_.NotNull())
        parent_->AddComponent(item_, id_, LOCAL);   // Replication mode is irrelevant, because it is used only for ID
                                                    // creation.
    else
        item_->Remove();
}

bool ComponentParentState::Equals(State* other) const
{
    auto other_ = dynamic_cast<ComponentParentState*>(other);

    if (other_ == nullptr)
        return false;

    return item_ == other_->item_ && parent_ == other_->parent_ && id_ == other_->id_;
}

String ComponentParentState::ToString() const
{
    return Urho3D::ToString("ComponentParentState parent = %p child = %p id = %d", parent_.Get(), item_.Get(), id_);
}

XMLVariantState::XMLVariantState(const XMLElement& item, const Variant& value) : item_(item), value_(value)
{
}

void XMLVariantState::Apply()
{
    item_.SetVariant(value_);
}

bool XMLVariantState::Equals(State* other) const
{
    auto other_ = dynamic_cast<XMLVariantState*>(other);

    if (other_ == nullptr)
        return false;

    return item_ == other_->item_ && value_ == other_->value_;
}

String XMLVariantState::ToString() const
{
    return Urho3D::ToString("XMLVariantState value = %s", value_.ToString().CString());
}

XMLParentState::XMLParentState(const XMLElement& item, const XMLElement& parent) : item_(item), parent_(parent)
{
}

void XMLParentState::Apply()
{
    if (parent_.NotNull())
        parent_.AppendChild(item_);
    else
        item_.GetParent().RemoveChild(item_);
}

bool XMLParentState::Equals(State* other) const
{
    auto other_ = dynamic_cast<XMLParentState*>(other);

    if (other_ == nullptr)
        return false;

    return item_.GetNode() == other_->item_.GetNode() && parent_.GetNode() == other_->parent_.GetNode();
}

String XMLParentState::ToString() const
{
    return Urho3D::ToString("XMLParentState parent = %s", parent_.IsNull() ? "null" : "set");
}

void StateCollection::Apply()
{
    for (auto& state : states_)
        state->Apply();
}

bool StateCollection::Contains(State* other) const
{
    for (const auto& state : states_)
    {
        if (state->Equals(other))
            return true;
    }
    return false;
}

bool StateCollection::PushUnique(const SharedPtr<State>& state)
{
    if (!Contains(state))
    {
        states_.Push(state);
        return true;
    }
    return false;
}

Manager::Manager(Context* ctx) : Object(ctx)
{
    SubscribeToEvent(E_ENDFRAME, [&](StringHash, VariantMap&)
    {
        if (!trackingSuspended_)
        {
            assert(previous_.Size() == next_.Size());

            if (previous_.Size())
            {
                // When stack is empty we insert two items - old state and new state. When stack already has states
                // saved - we save old state to the state collection at the end of the stack and insert new
                // collection for a new state.
                if (stack_.Empty())
                {
                    stack_.Resize(1);
                    index_++;
                }

                for (auto& state : previous_)
                {
                    if (stack_.Back().PushUnique(state))
                        URHO3D_LOGDEBUGF("UNDO: Save %d: %s", index_, state->ToString().CString());
                }

                index_++;
                stack_.Resize(index_ + 1);

                for (auto& state : next_)
                {
                    if (stack_.Back().PushUnique(state))
                        URHO3D_LOGDEBUGF("UNDO: Save %d: %s", index_, state->ToString().CString());
                }
                previous_.Clear();
                next_.Clear();
            }
        }
    });
}

void Manager::Undo()
{
    ApplyStateFromStack(false);
}

void Manager::Redo()
{
    ApplyStateFromStack(true);
}

void Manager::Clear()
{
    previous_.Clear();
    next_.Clear();
    stack_.Clear();
    index_ = -1;
}

void Manager::ApplyStateFromStack(bool forward)
{
    trackingSuspended_ = true;
    int direction = forward ? 1 : -1;
    index_ += direction;
    if (index_ >= 0 && index_ < stack_.Size())
    {
        stack_[index_].Apply();
        URHO3D_LOGDEBUGF("Undo: apply %d", index_);
    }
    index_ = Clamp<int32_t>(index_, 0, stack_.Size() - 1);
    trackingSuspended_ = false;
}

void Manager::TrackState(Serializable* item, const String& name, const Variant& value, const Variant& oldValue)
{
    // Item has it's state already modified, manually track the change.
    TrackBefore<AttributeState>(item, name, oldValue);
    TrackAfter<AttributeState>(item, name, value);
}

XMLElement Manager::XMLCreate(XMLElement& parent, const String& name)
{
    auto element = parent.CreateChild(name);
    TrackBefore<XMLParentState>(element);                       // "Removed" element has empty variant value.
    TrackAfter<XMLParentState>(element, element.GetParent());   // When value is set element exists.
    return element;
}

void Manager::XMLRemove(XMLElement& element)
{
    TrackBefore<XMLParentState>(element, element.GetParent());  // When value is set element exists.
    TrackAfter<XMLParentState>(element);                        // "Removed" element has empty variant value.
    element.Remove();
}

void Manager::XMLSetVariantValue(XMLElement& element, const Variant& value)
{
    TrackBefore<XMLVariantState>(element, element.GetVariant());
    TrackAfter<XMLVariantState>(element, value);
    element.SetVariantValue(value);
}

template<typename T, typename... Args>
void Manager::TrackBefore(Args... args)
{
    previous_.Push(SharedPtr<State>(new T(args...)));
}

template<typename T, typename... Args>
void Manager::TrackAfter(Args... args)
{
    next_.Push(SharedPtr<State>(new T(args...)));
}
void Manager::Connect(Scene* scene)
{
    SubscribeToEvent(scene, E_NODEADDED, [&](StringHash, VariantMap& args) {
        if (trackingSuspended_)
            return;

        using namespace NodeRemoved;
        auto node = dynamic_cast<Node*>(args[P_NODE].GetPtr());
        auto parent = dynamic_cast<Node*>(args[P_PARENT].GetPtr());

        TrackBefore<NodeParentState>(node, nullptr);        // Removed from the scene state
        TrackAfter<NodeParentState>(node, parent);          // Present in the scene state
    });

    SubscribeToEvent(scene, E_NODEREMOVED, [&](StringHash, VariantMap& args) {
        if (trackingSuspended_)
            return;

        using namespace NodeRemoved;
        auto node = dynamic_cast<Node*>(args[P_NODE].GetPtr());
        auto parent = dynamic_cast<Node*>(args[P_PARENT].GetPtr());

        TrackBefore<NodeParentState>(node, parent);        // Present in the scene state
        TrackAfter<NodeParentState>(node, nullptr);        // Removed from the scene state
    });

    SubscribeToEvent(scene, E_COMPONENTADDED, [&](StringHash, VariantMap& args) {
        if (trackingSuspended_)
            return;

        using namespace ComponentAdded;
        auto component = dynamic_cast<Component*>(args[P_COMPONENT].GetPtr());
        auto parent = dynamic_cast<Node*>(args[P_NODE].GetPtr());

        TrackBefore<ComponentParentState>(component, nullptr);
        TrackAfter<ComponentParentState>(component, parent);
    });

    SubscribeToEvent(scene, E_COMPONENTREMOVED, [&](StringHash, VariantMap& args) {
        if (trackingSuspended_)
            return;

        using namespace ComponentAdded;
        auto component = dynamic_cast<Component*>(args[P_COMPONENT].GetPtr());
        auto parent = dynamic_cast<Node*>(args[P_NODE].GetPtr());

        TrackBefore<ComponentParentState>(component, parent);
        TrackAfter<ComponentParentState>(component, nullptr);
    });
}

void Manager::Connect(AttributeInspector* inspector)
{
    SubscribeToEvent(inspector, E_ATTRIBUTEINSPECTVALUEMODIFIED, [&](StringHash, VariantMap& args) {
        if (trackingSuspended_)
            return;

        using namespace AttributeInspectorValueModified;
        auto item = dynamic_cast<Serializable*>(args[P_SERIALIZABLE].GetPtr());

        auto attributeName = reinterpret_cast<AttributeInfo*>(args[P_ATTRIBUTEINFO].GetVoidPtr())->name_;
        TrackBefore<AttributeState>(item, attributeName, args[P_OLDVALUE]);
        TrackAfter<AttributeState>(item, attributeName, args[P_NEWVALUE]);
    });
}

void Manager::Connect(UIElement* root)
{
    SubscribeToEvent(E_ELEMENTADDED, [&, root](StringHash, VariantMap& args) {
        if (trackingSuspended_)
            return;

        using namespace ElementAdded;
        auto element = dynamic_cast<UIElement*>(args[P_ELEMENT].GetPtr());
        auto parent = dynamic_cast<UIElement*>(args[P_PARENT].GetPtr());
        auto eventRoot = dynamic_cast<UIElement*>(args[P_ROOT].GetPtr());

        if (root != eventRoot)
            return;

        TrackBefore<ElementParentState>(element, nullptr);        // Removed from the scene state
        TrackAfter<ElementParentState>(element, parent);          // Present in the scene state
    });

    SubscribeToEvent(E_ELEMENTREMOVED, [&, root](StringHash, VariantMap& args) {
        if (trackingSuspended_)
            return;

        using namespace ElementRemoved;
        auto element = dynamic_cast<UIElement*>(args[P_ELEMENT].GetPtr());
        auto parent = dynamic_cast<UIElement*>(args[P_PARENT].GetPtr());
        auto eventRoot = dynamic_cast<UIElement*>(args[P_ROOT].GetPtr());

        if (root != eventRoot)
            return;

        TrackBefore<ElementParentState>(element, parent);        // Removed from the scene state
        TrackAfter<ElementParentState>(element, nullptr);        // Present in the scene state
    });
}

void Manager::Connect(Gizmo* gizmo)
{
    SubscribeToEvent(gizmo, E_GIZMONODEMODIFIED, [&](StringHash, VariantMap& args) {
        using namespace GizmoNodeModified;
        auto node = dynamic_cast<Node*>(args[P_NODE].GetPtr());
        auto oldTransform = args[P_OLDTRANSFORM].GetMatrix3x4();
        auto newTransform = args[P_NEWTRANSFORM].GetMatrix3x4();

        TrackBefore<AttributeState>(node, "Position", oldTransform.Translation());
        TrackBefore<AttributeState>(node, "Rotation", oldTransform.Rotation());
        TrackBefore<AttributeState>(node, "Scale", oldTransform.Scale());

        TrackAfter<AttributeState>(node, "Position", newTransform.Translation());
        TrackAfter<AttributeState>(node, "Rotation", newTransform.Rotation());
        TrackAfter<AttributeState>(node, "Scale", newTransform.Scale());
    });
}

}

}
<|endoftext|>


<commit_before>//
//  Created by Robert Segal on 2014-04-01.
//  Copyright (c) 2016 Get Set Games Inc. All rights reserved.
//

#include "UpsightPrivatePCH.h"

#if PLATFORM_IOS
#import <UpsightKit/UpsightKit.h>
#elif PLATFORM_ANDROID
#include "Android/AndroidJNI.h"
#include "AndroidApplication.h"
#endif

bool ValidateValues(TArray<FString> &Keys, TArray<FString> &Values)
{
    const int32 kNumKeys   = Keys.Num();
    const int32 kNumValues = Values.Num();

    if (kNumKeys == 0 || kNumValues == 0)
    {
        return false;
    }

    if (kNumKeys != kNumValues)
    {
        return false;
    }

    return true;
}

#if PLATFORM_IOS
NSDictionary* CreateNSDictionary(TArray<FString> &Keys, TArray<FString> &Values)
{
    const int32 kNumKeys = Keys.Num();

    NSMutableDictionary* d = [NSMutableDictionary dictionaryWithCapacity:kNumKeys];

    for (uint32 i = 0; i < kNumKeys; i++)
    {
        FString &k = Keys[i];
        FString &v = Values[i];

        d[k.GetNSString()] = v.GetNSString();
    }

    return d;
}

int validateData(const void* data, const int dataLength, unsigned char bID[128], unsigned char bVersion[16], void **dOpaque, void **dHash)
{
    // iOS receipt validation based off code detailed at https://www.objc.io/issues/17-security/receipt-validation/
    //

    char isValid = 0;

    return isValid;
}

#endif

#if PLATFORM_ANDROID
void CreateJavaKeyValueArrays(JNIEnv *Env, jobjectArray &jKeysArray, jobjectArray &jValuesArray, TArray<FString> keys, TArray<FString> values)
{
    for (uint32 Param = 0; Param < keys.Num(); Param++)
    {
        jstring StringValue = Env->NewStringUTF(TCHAR_TO_UTF8(*keys[Param]));

        Env->SetObjectArrayElement(jKeysArray, Param, StringValue);
        Env->DeleteLocalRef(StringValue);

        StringValue = Env->NewStringUTF(TCHAR_TO_UTF8(*values[Param]));

        Env->SetObjectArrayElement(jValuesArray, Param, StringValue);
        Env->DeleteLocalRef(StringValue);
    }
}
#endif

bool UUpsightFunctions::UpsightClientValidateInAppPurchase(FString receiptData)
{
#if PLATFORM_IOS
    unsigned char bundleId[128]     = { 0 };
    unsigned char bundleVersion[16] = { 0 };

    void *opaque = NULL;
    void *hash   = NULL;

    const int result = validateData([receipt bytes], (int)[receipt length], bundleId, bundleVersion, &opaque, &hash);
    return result == 0;
#else
    return true;
#endif
}

void UUpsightFunctions::UpsightRecordAnalyticsEventWithName(FString eventName, TArray<FString> eventKeys, TArray<FString> eventValues)
{
    if ( ValidateValues(eventKeys, eventValues) )
    {
#if PLATFORM_IOS
        NSDictionary *p = CreateNSDictionary(eventKeys, eventValues);

        [Upsight recordAnalyticsEventWithName:eventName.GetNSString() properties: p];

#elif PLATFORM_ANDROID
        if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
        {
            static jmethodID Method = FJavaWrapper::FindMethod(Env,
                                                               FJavaWrapper::GameActivityClassID,
                                                               "AndroidThunkJava_UpsightRecordAnalyticsEventWithName",
                                                               "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
                                                               false);

            jobjectArray jKeys   = (jobjectArray)Env->NewObjectArray(eventKeys.Num(),   FJavaWrapper::JavaStringClass, NULL);
            jobjectArray jValues = (jobjectArray)Env->NewObjectArray(eventValues.Num(), FJavaWrapper::JavaStringClass, NULL);

            CreateJavaKeyValueArrays(Env, jKeys, jValues, eventKeys, eventValues);

            jstring jEventName = Env->NewStringUTF(TCHAR_TO_UTF8(*eventName));

            FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis, Method, jEventName, jKeys, jValues);

            Env->DeleteLocalRef(jKeys);
            Env->DeleteLocalRef(jValues);
            Env->DeleteLocalRef(jEventName);
        }
#endif
    }
    else
    {
        UE_LOG(LogUpsight, Log, TEXT("keys and/or value arguments are empty or nil"));
    }
}

void UUpsightFunctions::UpsightRecordMilestoneEventForScope(FString scope, TArray<FString> eventKeys, TArray<FString> eventValues)
{
    if ( ValidateValues(eventKeys, eventValues) )
    {
#if PLATFORM_IOS
        NSDictionary *p = CreateNSDictionary(eventKeys, eventValues);

        [Upsight recordMilestoneEventForScope:scope.GetNSString() properties:p];

#elif PLATFORM_ANDROID

        if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
        {
            static jmethodID Method = FJavaWrapper::FindMethod(Env,
                                                               FJavaWrapper::GameActivityClassID,
                                                               "AndroidThunkJava_UpsightRecordMilestoneEventForScope",
                                                               "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
                                                               false);

            jobjectArray jKeys   = (jobjectArray)Env->NewObjectArray(eventKeys.Num(),   FJavaWrapper::JavaStringClass, NULL);
            jobjectArray jValues = (jobjectArray)Env->NewObjectArray(eventValues.Num(), FJavaWrapper::JavaStringClass, NULL);

            CreateJavaKeyValueArrays(Env, jKeys, jValues, eventKeys, eventValues);

            jstring jEventName = Env->NewStringUTF(TCHAR_TO_UTF8(*scope));

            FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis, Method, jEventName, jKeys, jValues);

            Env->DeleteLocalRef(jKeys);
            Env->DeleteLocalRef(jValues);
            Env->DeleteLocalRef(jEventName);
        }
#endif
    }
    else
    {
        UE_LOG(LogUpsight, Log, TEXT("keys and/or value arguments are empty or nil"));
    }
}

void UUpsightFunctions::UpsightRecordMonetizationEvent(int resolution, FString productID, int quantity, FString currency, float price, TArray<FString> keys, TArray<FString> values)
{
#if PLATFORM_IOS
    NSDictionary *p = CreateNSDictionary(keys, values);

    [Upsight recordMonetizationEventWithResolution:(USPurchaseResolution)resolution
                                          currency:currency.GetNSString()
                                          quantity:quantity
                                           product:productID.GetNSString()
                                             price:price
                                        properties:p];

#elif PLATFORM_ANDROID
    if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
    {
        static jmethodID Method = FJavaWrapper::FindMethod(Env,
                                                           FJavaWrapper::GameActivityClassID,
                                                           "AndroidThunkJava_UpsightRecordMonetizationEvent",
                                                           "(ILjava/lang/String;ILjava/lang/String;F[Ljava/lang/String;[Ljava/lang/String;)V",
                                                           false);

        jobjectArray jKeys   = (jobjectArray)Env->NewObjectArray(keys.Num(),   FJavaWrapper::JavaStringClass, NULL);
        jobjectArray jValues = (jobjectArray)Env->NewObjectArray(values.Num(), FJavaWrapper::JavaStringClass, NULL);

        CreateJavaKeyValueArrays(Env, jKeys, jValues, keys, values);

        jint    jResolution = (jint)resolution;
        jstring jProductID  = Env->NewStringUTF(TCHAR_TO_UTF8(*productID));
        jint    jQuantity   = (jint)quantity;
        jstring jCurrency   = Env->NewStringUTF(TCHAR_TO_UTF8(*currency));
        jfloat  jPrice      = (jfloat)price;

        FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis, Method, jResolution, jProductID, jQuantity, jCurrency, jPrice, jKeys, jValues);

        Env->DeleteLocalRef(jKeys);
        Env->DeleteLocalRef(jValues);

        Env->DeleteLocalRef(jProductID);
        Env->DeleteLocalRef(jCurrency);
    }
#endif
}

void UUpsightFunctions::UpsightRecordMonetizationEventWithTotalPrice(int resolution, FString productID, int quantity, FString currency, float price, float totalPrice, TArray<FString> keys, TArray<FString> values)
{
#if PLATFORM_IOS
    NSDictionary *p = CreateNSDictionary(keys, values);

    [Upsight recordMonetizationEventWithResolution:(USPurchaseResolution)resolution totalPrice:totalPrice currency:currency.GetNSString() product:productID.GetNSString() properties:p];
#elif PLATFORM_ANDROID
    if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
    {
        static jmethodID Method = FJavaWrapper::FindMethod(Env,
                                                           FJavaWrapper::GameActivityClassID,
                                                           "AndroidThunkJava_UpsightRecordMonetizationEventWithTotalPrice",
                                                           "(ILjava/lang/String;ILjava/lang/String;FF[Ljava/lang/String;[Ljava/lang/String;)V",
                                                           false);

        jobjectArray jKeys   = (jobjectArray)Env->NewObjectArray(keys.Num(),   FJavaWrapper::JavaStringClass, NULL);
        jobjectArray jValues = (jobjectArray)Env->NewObjectArray(values.Num(), FJavaWrapper::JavaStringClass, NULL);

        CreateJavaKeyValueArrays(Env, jKeys, jValues, keys, values);

        jint    jResolution = (jint)resolution;
        jstring jProductID  = Env->NewStringUTF(TCHAR_TO_UTF8(*productID));
        jint    jQuantity   = (jint)quantity;
        jstring jCurrency   = Env->NewStringUTF(TCHAR_TO_UTF8(*currency));
        jfloat  jPrice      = (jfloat)price;
        jfloat  jTotalPrice = (jfloat)totalPrice;

        FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis, Method, jResolution, jProductID, jQuantity, jCurrency, jPrice, jTotalPrice, jKeys, jValues);

        Env->DeleteLocalRef(jKeys);
        Env->DeleteLocalRef(jValues);
        Env->DeleteLocalRef(jProductID);
        Env->DeleteLocalRef(jCurrency);
    }
#endif
}

void UUpsightFunctions::UpsightRecordInAppPurchaseEventWithResolution(int resolution, FString productID, int quantity, FString currency, float price, FString bundle, FString transactionIdentifier, TArray<FString> keys, TArray<FString> values)
{
#if PLATFORM_IOS
    NSDictionary *p = CreateNSDictionary(keys, values);

    [Upsight recordInAppPurchaseEventWithResolution:(USPurchaseResolution)resolution
                                            product:productID.GetNSString()
                                           quantity:quantity
                                              price:price
                                           currency:currency.GetNSString()
                              transactionIdentifier:transactionIdentifier.GetNSString()
                                         properties:p];

#elif PLATFORM_ANDROID
    if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
    {
        static jmethodID Method = FJavaWrapper::FindMethod(Env,
                                                           FJavaWrapper::GameActivityClassID,
                                                           "AndroidThunkJava_UpsightRecordInAppPurchaseEvent",
                                                           "(ILjava/lang/String;ILjava/lang/String;FLjava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
                                                           false);

        jobjectArray jKeys   = (jobjectArray)Env->NewObjectArray(keys.Num(),   FJavaWrapper::JavaStringClass, NULL);
        jobjectArray jValues = (jobjectArray)Env->NewObjectArray(values.Num(), FJavaWrapper::JavaStringClass, NULL);

        CreateJavaKeyValueArrays(Env, jKeys, jValues, keys, values);

        jint    jResolution = (jint)resolution;
        jstring jProductID  = Env->NewStringUTF(TCHAR_TO_UTF8(*productID));
        jint    jQuantity   = (jint)quantity;
        jstring jCurrency   = Env->NewStringUTF(TCHAR_TO_UTF8(*currency));
        jfloat  jPrice      = (jfloat)price;
        jstring jBundle     = Env->NewStringUTF(TCHAR_TO_UTF8(*bundle));

        FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis, Method, jResolution, jProductID, jQuantity, jCurrency, jPrice, jBundle, jKeys, jValues);

        Env->DeleteLocalRef(jKeys);
        Env->DeleteLocalRef(jValues);

        Env->DeleteLocalRef(jBundle);
        Env->DeleteLocalRef(jProductID);
        Env->DeleteLocalRef(jCurrency);
    }
#endif
}

<commit_msg>Defines receipt string object resolve build issue<commit_after>//
//  Created by Robert Segal on 2014-04-01.
//  Copyright (c) 2016 Get Set Games Inc. All rights reserved.
//

#include "UpsightPrivatePCH.h"

#if PLATFORM_IOS
#import <UpsightKit/UpsightKit.h>
#elif PLATFORM_ANDROID
#include "Android/AndroidJNI.h"
#include "AndroidApplication.h"
#endif

bool ValidateValues(TArray<FString> &Keys, TArray<FString> &Values)
{
    const int32 kNumKeys   = Keys.Num();
    const int32 kNumValues = Values.Num();

    if (kNumKeys == 0 || kNumValues == 0)
    {
        return false;
    }

    if (kNumKeys != kNumValues)
    {
        return false;
    }

    return true;
}

#if PLATFORM_IOS
NSDictionary* CreateNSDictionary(TArray<FString> &Keys, TArray<FString> &Values)
{
    const int32 kNumKeys = Keys.Num();

    NSMutableDictionary* d = [NSMutableDictionary dictionaryWithCapacity:kNumKeys];

    for (uint32 i = 0; i < kNumKeys; i++)
    {
        FString &k = Keys[i];
        FString &v = Values[i];

        d[k.GetNSString()] = v.GetNSString();
    }

    return d;
}

int validateData(const void* data, const int dataLength, unsigned char bID[128], unsigned char bVersion[16], void **dOpaque, void **dHash)
{
    // iOS receipt validation based off code detailed at https://www.objc.io/issues/17-security/receipt-validation/
    //

    char isValid = 0;

    return isValid;
}

#endif

#if PLATFORM_ANDROID
void CreateJavaKeyValueArrays(JNIEnv *Env, jobjectArray &jKeysArray, jobjectArray &jValuesArray, TArray<FString> keys, TArray<FString> values)
{
    for (uint32 Param = 0; Param < keys.Num(); Param++)
    {
        jstring StringValue = Env->NewStringUTF(TCHAR_TO_UTF8(*keys[Param]));

        Env->SetObjectArrayElement(jKeysArray, Param, StringValue);
        Env->DeleteLocalRef(StringValue);

        StringValue = Env->NewStringUTF(TCHAR_TO_UTF8(*values[Param]));

        Env->SetObjectArrayElement(jValuesArray, Param, StringValue);
        Env->DeleteLocalRef(StringValue);
    }
}
#endif

bool UUpsightFunctions::UpsightClientValidateInAppPurchase(FString receiptData)
{
#if PLATFORM_IOS
    unsigned char bundleId[128]     = { 0 };
    unsigned char bundleVersion[16] = { 0 };

    void *opaque = NULL;
    void *hash   = NULL;

    NSString *receipt = receiptData.GetNSString();

    const int result = validateData([receipt bytes], (int)[receipt length], bundleId, bundleVersion, &opaque, &hash);
    return result == 0;
#else
    return true;
#endif
}

void UUpsightFunctions::UpsightRecordAnalyticsEventWithName(FString eventName, TArray<FString> eventKeys, TArray<FString> eventValues)
{
    if ( ValidateValues(eventKeys, eventValues) )
    {
#if PLATFORM_IOS
        NSDictionary *p = CreateNSDictionary(eventKeys, eventValues);

        [Upsight recordAnalyticsEventWithName:eventName.GetNSString() properties: p];

#elif PLATFORM_ANDROID
        if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
        {
            static jmethodID Method = FJavaWrapper::FindMethod(Env,
                                                               FJavaWrapper::GameActivityClassID,
                                                               "AndroidThunkJava_UpsightRecordAnalyticsEventWithName",
                                                               "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
                                                               false);

            jobjectArray jKeys   = (jobjectArray)Env->NewObjectArray(eventKeys.Num(),   FJavaWrapper::JavaStringClass, NULL);
            jobjectArray jValues = (jobjectArray)Env->NewObjectArray(eventValues.Num(), FJavaWrapper::JavaStringClass, NULL);

            CreateJavaKeyValueArrays(Env, jKeys, jValues, eventKeys, eventValues);

            jstring jEventName = Env->NewStringUTF(TCHAR_TO_UTF8(*eventName));

            FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis, Method, jEventName, jKeys, jValues);

            Env->DeleteLocalRef(jKeys);
            Env->DeleteLocalRef(jValues);
            Env->DeleteLocalRef(jEventName);
        }
#endif
    }
    else
    {
        UE_LOG(LogUpsight, Log, TEXT("keys and/or value arguments are empty or nil"));
    }
}

void UUpsightFunctions::UpsightRecordMilestoneEventForScope(FString scope, TArray<FString> eventKeys, TArray<FString> eventValues)
{
    if ( ValidateValues(eventKeys, eventValues) )
    {
#if PLATFORM_IOS
        NSDictionary *p = CreateNSDictionary(eventKeys, eventValues);

        [Upsight recordMilestoneEventForScope:scope.GetNSString() properties:p];

#elif PLATFORM_ANDROID

        if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
        {
            static jmethodID Method = FJavaWrapper::FindMethod(Env,
                                                               FJavaWrapper::GameActivityClassID,
                                                               "AndroidThunkJava_UpsightRecordMilestoneEventForScope",
                                                               "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
                                                               false);

            jobjectArray jKeys   = (jobjectArray)Env->NewObjectArray(eventKeys.Num(),   FJavaWrapper::JavaStringClass, NULL);
            jobjectArray jValues = (jobjectArray)Env->NewObjectArray(eventValues.Num(), FJavaWrapper::JavaStringClass, NULL);

            CreateJavaKeyValueArrays(Env, jKeys, jValues, eventKeys, eventValues);

            jstring jEventName = Env->NewStringUTF(TCHAR_TO_UTF8(*scope));

            FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis, Method, jEventName, jKeys, jValues);

            Env->DeleteLocalRef(jKeys);
            Env->DeleteLocalRef(jValues);
            Env->DeleteLocalRef(jEventName);
        }
#endif
    }
    else
    {
        UE_LOG(LogUpsight, Log, TEXT("keys and/or value arguments are empty or nil"));
    }
}

void UUpsightFunctions::UpsightRecordMonetizationEvent(int resolution, FString productID, int quantity, FString currency, float price, TArray<FString> keys, TArray<FString> values)
{
#if PLATFORM_IOS
    NSDictionary *p = CreateNSDictionary(keys, values);

    [Upsight recordMonetizationEventWithResolution:(USPurchaseResolution)resolution
                                          currency:currency.GetNSString()
                                          quantity:quantity
                                           product:productID.GetNSString()
                                             price:price
                                        properties:p];

#elif PLATFORM_ANDROID
    if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
    {
        static jmethodID Method = FJavaWrapper::FindMethod(Env,
                                                           FJavaWrapper::GameActivityClassID,
                                                           "AndroidThunkJava_UpsightRecordMonetizationEvent",
                                                           "(ILjava/lang/String;ILjava/lang/String;F[Ljava/lang/String;[Ljava/lang/String;)V",
                                                           false);

        jobjectArray jKeys   = (jobjectArray)Env->NewObjectArray(keys.Num(),   FJavaWrapper::JavaStringClass, NULL);
        jobjectArray jValues = (jobjectArray)Env->NewObjectArray(values.Num(), FJavaWrapper::JavaStringClass, NULL);

        CreateJavaKeyValueArrays(Env, jKeys, jValues, keys, values);

        jint    jResolution = (jint)resolution;
        jstring jProductID  = Env->NewStringUTF(TCHAR_TO_UTF8(*productID));
        jint    jQuantity   = (jint)quantity;
        jstring jCurrency   = Env->NewStringUTF(TCHAR_TO_UTF8(*currency));
        jfloat  jPrice      = (jfloat)price;

        FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis, Method, jResolution, jProductID, jQuantity, jCurrency, jPrice, jKeys, jValues);

        Env->DeleteLocalRef(jKeys);
        Env->DeleteLocalRef(jValues);

        Env->DeleteLocalRef(jProductID);
        Env->DeleteLocalRef(jCurrency);
    }
#endif
}

void UUpsightFunctions::UpsightRecordMonetizationEventWithTotalPrice(int resolution, FString productID, int quantity, FString currency, float price, float totalPrice, TArray<FString> keys, TArray<FString> values)
{
#if PLATFORM_IOS
    NSDictionary *p = CreateNSDictionary(keys, values);

    [Upsight recordMonetizationEventWithResolution:(USPurchaseResolution)resolution totalPrice:totalPrice currency:currency.GetNSString() product:productID.GetNSString() properties:p];
#elif PLATFORM_ANDROID
    if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
    {
        static jmethodID Method = FJavaWrapper::FindMethod(Env,
                                                           FJavaWrapper::GameActivityClassID,
                                                           "AndroidThunkJava_UpsightRecordMonetizationEventWithTotalPrice",
                                                           "(ILjava/lang/String;ILjava/lang/String;FF[Ljava/lang/String;[Ljava/lang/String;)V",
                                                           false);

        jobjectArray jKeys   = (jobjectArray)Env->NewObjectArray(keys.Num(),   FJavaWrapper::JavaStringClass, NULL);
        jobjectArray jValues = (jobjectArray)Env->NewObjectArray(values.Num(), FJavaWrapper::JavaStringClass, NULL);

        CreateJavaKeyValueArrays(Env, jKeys, jValues, keys, values);

        jint    jResolution = (jint)resolution;
        jstring jProductID  = Env->NewStringUTF(TCHAR_TO_UTF8(*productID));
        jint    jQuantity   = (jint)quantity;
        jstring jCurrency   = Env->NewStringUTF(TCHAR_TO_UTF8(*currency));
        jfloat  jPrice      = (jfloat)price;
        jfloat  jTotalPrice = (jfloat)totalPrice;

        FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis, Method, jResolution, jProductID, jQuantity, jCurrency, jPrice, jTotalPrice, jKeys, jValues);

        Env->DeleteLocalRef(jKeys);
        Env->DeleteLocalRef(jValues);
        Env->DeleteLocalRef(jProductID);
        Env->DeleteLocalRef(jCurrency);
    }
#endif
}

void UUpsightFunctions::UpsightRecordInAppPurchaseEventWithResolution(int resolution, FString productID, int quantity, FString currency, float price, FString bundle, FString transactionIdentifier, TArray<FString> keys, TArray<FString> values)
{
#if PLATFORM_IOS
    NSDictionary *p = CreateNSDictionary(keys, values);

    [Upsight recordInAppPurchaseEventWithResolution:(USPurchaseResolution)resolution
                                            product:productID.GetNSString()
                                           quantity:quantity
                                              price:price
                                           currency:currency.GetNSString()
                              transactionIdentifier:transactionIdentifier.GetNSString()
                                         properties:p];

#elif PLATFORM_ANDROID
    if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
    {
        static jmethodID Method = FJavaWrapper::FindMethod(Env,
                                                           FJavaWrapper::GameActivityClassID,
                                                           "AndroidThunkJava_UpsightRecordInAppPurchaseEvent",
                                                           "(ILjava/lang/String;ILjava/lang/String;FLjava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
                                                           false);

        jobjectArray jKeys   = (jobjectArray)Env->NewObjectArray(keys.Num(),   FJavaWrapper::JavaStringClass, NULL);
        jobjectArray jValues = (jobjectArray)Env->NewObjectArray(values.Num(), FJavaWrapper::JavaStringClass, NULL);

        CreateJavaKeyValueArrays(Env, jKeys, jValues, keys, values);

        jint    jResolution = (jint)resolution;
        jstring jProductID  = Env->NewStringUTF(TCHAR_TO_UTF8(*productID));
        jint    jQuantity   = (jint)quantity;
        jstring jCurrency   = Env->NewStringUTF(TCHAR_TO_UTF8(*currency));
        jfloat  jPrice      = (jfloat)price;
        jstring jBundle     = Env->NewStringUTF(TCHAR_TO_UTF8(*bundle));

        FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis, Method, jResolution, jProductID, jQuantity, jCurrency, jPrice, jBundle, jKeys, jValues);

        Env->DeleteLocalRef(jKeys);
        Env->DeleteLocalRef(jValues);

        Env->DeleteLocalRef(jBundle);
        Env->DeleteLocalRef(jProductID);
        Env->DeleteLocalRef(jCurrency);
    }
#endif
}

<|endoftext|>