<commit_before>/*
 * Guidage.cpp
 *
 *  Created on: 16 juin 2014
 *      Author: Maël Valais
 *
 *	Classe d'interaction utilisateur correspondant à la fenetre principale. Il s'agit de la
 *	partie graphique du logiciel. La classe WidgetImage (widgetimage.cpp) fait aussi partie
 *	des classes d'interaction utilisateur.
 *
 *  La classe communique avec les classes Capture (capture.cpp) et Guidage (guidage.cpp) grâce au
 *  système de signaux-slots. Par exemple, lorsqu'on appuie sur le bonton "Reset de consigne",
 *  le signal clicked() provenant du bouton ui->consigneReset est envoyé au slot consigneReset().
 *  Ici les noms sont idientiques.
 *
 *  Pour que la connexion entre le signal et le slot se fasse, on écrit :
 *  	QObject::connect(ui->consigneReset,SIGNAL(clicked()),guidage,SLOT(consigneReset()));
 *
 *	Toutes les fonctions de fenetreprincipale sont liées à de l'intéraction avec l'utilisateur.
 *	Tous les traitements se font dans Capture ou Guidage.
 */

#include "fenetreprincipale.h"
#include "fenetreprincipale_ui.h"
#include <QtCore/QTextCodec>

/*
 * ATTENTION, dans les constructeurs de Capture et Guidage on ne peut pas envoyer de signal car
 * l'appel du constructeur est effectué avant que la connexion soit effectuée
 */
FenetrePrincipale::FenetrePrincipale(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::FenetrePrincipale)
{
    // Lancement de l'interface utilisateur (setupUi provient de fenetreprincipale_ui.h
	// généré à partir de fenetreprincipale.ui, qui permet de modifier la disposition)
	ui->setupUi(this);

    // Couleurs de texte
    paletteTexteVert.setColor(QPalette::WindowText, Qt::green);
    paletteTexteRouge.setColor(QPalette::WindowText, Qt::red);
    paletteTexteJaune.setColor(QPalette::WindowText, Qt::yellow);

    // Faire en sorte que les chaines statiques "C-strings" soient considérées comme UTF-8
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));

    guidage = new Guidage; // ATTENTION: pas d'envoi de signaux dans le constructeur !
    guidage->moveToThread(&threadGuidage);
    capture = new Capture; // ATTENTION: pas d'envoi de signaux dans le constructeur !
    capture->moveToThread(&threadCapture);

    // Signaux-slots entre fenetreprincipale et guidage
    QObject::connect(guidage,SIGNAL(etatArduino(int)),this,SLOT(statutArduino(int)));
    QObject::connect(guidage,SIGNAL(etatGuidage(bool)),this,SLOT(statutGuidage(bool)));
    QObject::connect(guidage,SIGNAL(message(QString)),this,SLOT(afficherMessage(QString)));
    QObject::connect(this,SIGNAL(lancerGuidage()),guidage,SLOT(lancerGuidage()));
    QObject::connect(this,SIGNAL(stopperGuidage()),guidage,SLOT(stopperGuidage()));
    QObject::connect(this,SIGNAL(modificationConsigne(int,int,int)),guidage,SLOT(modifierConsigne(int,int,int)));
    QObject::connect(ui->consigneReset,SIGNAL(clicked()),guidage,SLOT(consigneReset()));
    QObject::connect(guidage,SIGNAL(signalBruit(double)),ui->ratioSignalBruit,SLOT(setNum(double)));
    QObject::connect(&threadGuidage,SIGNAL(finished()),guidage,SLOT(deleteLater()));

    // Signaux-slots entre fenetreprincipale et capture
    QObject::connect(capture,SIGNAL(etatCamera(int)),this,SLOT(statutCamera(int)));
    QObject::connect(capture,SIGNAL(diametreSoleil(int)),ui->valeurDiametreSoleil,SLOT(setValue(int)));
    QObject::connect(ui->valeurDiametreSoleil,SIGNAL(valueChanged(int)),capture,SLOT(modifierDiametre(int)));
    QObject::connect(this,SIGNAL(initialiserCapture()),capture,SLOT(initialiserObjetCapture()));
    QObject::connect(&threadCapture,SIGNAL(finished()),capture,SLOT(deleteLater()));

    // Signaux-slots entre capture et guidage
    QObject::connect(capture,SIGNAL(resultats(Image*,double,double,int,double)),guidage, SLOT(traiterResultatsCapture(Image*,double,double,int,double)));
    QObject::connect(capture,SIGNAL(stopperGuidage()),guidage, SLOT(stopperGuidage()));

    // Signaux-slots entre les éléments de l'interface
    QObject::connect(guidage,SIGNAL(imageSoleil(Image*)),ui->imageCamera,SLOT(afficherImageSoleil(Image*)));
    QObject::connect(guidage,SIGNAL(repereConsigne(float,float,float,QColor)),ui->imageCamera,SLOT(afficherRepereConsigne(float,float,float,QColor)));
    QObject::connect(guidage,SIGNAL(repereSoleil(float,float,float,QColor)),ui->imageCamera,SLOT(afficherRepereSoleil(float,float,float,QColor)));

    // Lancement des threads
    threadGuidage.start();
    threadCapture.start();
    emit initialiserCapture(); // En plus du constructeur Capture() on appelle initialiserCapture()
    // à cause du fait que les signaux ne peuvent pas être envoyés depuis un constructeur.

    // Le mode de décalage rapide est par défaut
    ui->vitesseDecalageRapide->setChecked(true);

    // Pour capturer les touches directionnelles du clavier pour controler la consigne,
    // on met un filtre sur toutes les parties de l'interface, et on redéfinit la méthode
    // eventFilter() (voir plus bas)
    ui->messages->installEventFilter(this);
    ui->consigneDroite->installEventFilter(this);
    ui->centralWidget->installEventFilter(this);
	ui->centralWidget->installEventFilter(this);
	ui->consigneBas->installEventFilter(this);
	ui->consigneDroite->installEventFilter(this);
	ui->consigneGauche->installEventFilter(this);
	ui->consigneHaut->installEventFilter(this);
	ui->valeurDiametreSoleil->installEventFilter(this);
	ui->imageCamera->installEventFilter(this);
	ui->consigneReset->installEventFilter(this);
	ui->lancerGuidage->installEventFilter(this);
	ui->messages->installEventFilter(this);
	ui->vitesseDecalageLent->installEventFilter(this);
	ui->vitesseDecalageRapide->installEventFilter(this);
}

FenetrePrincipale::~FenetrePrincipale() {
    delete ui;
}

void FenetrePrincipale::afficherMessage(QString msg) {
    ui->messages->append(msg);
}

void FenetrePrincipale::statutCamera(int etat) {
	if(etat & CAMERA_CONNEXION_ON) {
		ui->statutCamera->setPalette(paletteTexteVert);
		ui->statutCamera->setText("Caméra connectée");
	}
	else if(etat & CAMERA_CONNEXION_OFF){
		ui->statutCamera->setPalette(paletteTexteRouge);
		ui->statutCamera->setText("Caméra déconnectée");
	}
}

void FenetrePrincipale::statutArduino(int etat) {
	switch(etat) {
		case ARDUINO_CONNEXION_ON:
			ui->statutArduino->setPalette(paletteTexteVert);
			ui->statutArduino->setText("Arduino connecté");
			break;
		case ARDUINO_CONNEXION_OFF:
			ui->statutArduino->setPalette(paletteTexteRouge);
			ui->statutArduino->setText("Arduino déconnecté");
			break;
		case ARDUINO_FICHIER_INTROUV:
			ui->statutArduino->setPalette(paletteTexteRouge);
			ui->statutArduino->setText("Arduino introuvable");
	}
}

/**
 * Slot permettant de modifier l'affichage de l'état du guidage
 * @param statut True pour guidage en marche
 */
void FenetrePrincipale::statutGuidage(bool statut) {
	if(statut) {
		ui->statutGuidage->setPalette(paletteTexteVert);
		ui->statutGuidage->setText("Marche");
	}
	else {
		ui->statutGuidage->setPalette(paletteTexteRouge);
		ui->statutGuidage->setText("Arrêt");
	}
}

/*
 * NOTE SUR LES METHODES on_elementInterface_clicked() (par exemple) :
 *
 * Ces méthodes permettent de s'affranchir de l'étape signal-slot,
 * la méthode on_consigneHaut_clicked est appelée à chaque fois que
 * le bouton consigneHaut est pressé.
 *
 * Il s'agit d'un "raccourci" pour éviter de passer par un
 * signal -> slot -> appel de fonction.
 */
void FenetrePrincipale::on_vitesseDecalageLent_toggled() {
	modeVitesse = (ui->vitesseDecalageLent->isChecked())?VITESSE_LENTE:VITESSE_RAPIDE;
}
void FenetrePrincipale::on_consigneHaut_clicked() {
	emit modificationConsigne(-1,0,(ui->vitesseDecalageLent->isChecked()));
}
void FenetrePrincipale::on_consigneBas_clicked() {
	emit modificationConsigne(1,0,(ui->vitesseDecalageLent->isChecked()));
}
void FenetrePrincipale::on_consigneDroite_clicked() {
	emit modificationConsigne(0,1,(ui->vitesseDecalageLent->isChecked()));
}
void FenetrePrincipale::on_consigneGauche_clicked() {
	emit modificationConsigne(0,-1,(ui->vitesseDecalageLent->isChecked()));
}
void FenetrePrincipale::on_lancerGuidage_clicked() {
    emit lancerGuidage();
}
void FenetrePrincipale::on_stopperGuidage_clicked() {
	emit stopperGuidage();
}

void FenetrePrincipale::closeEvent(QCloseEvent* event) {
	threadCapture.quit(); // termine la boucle d'event de l'objet QThread
	threadGuidage.quit();
	threadGuidage.wait(); // Attend que le QThread soit terminé
	threadCapture.wait();
	cout << "Application terminée" << endl;
	// FIXME: quant on quitte comme ça, deleteLater() n'a pas le temps
	// de supprimer l'objet
}

void FenetrePrincipale::keyPressEvent(QKeyEvent* event) {
	switch(event->key()) {
		case Qt::Key_Up:
			emit modificationConsigne(-1,0,modeVitesse);
			break;
		case Qt::Key_Down:
			emit modificationConsigne(1,0,modeVitesse);
			break;
		case Qt::Key_Right:
			emit modificationConsigne(0,1,modeVitesse);
			break;
		case Qt::Key_Left:
			emit modificationConsigne(0,-1,modeVitesse);
			break;
	}
}

// Pour capturer les touches directionnelles du clavier pour controler la consigne
bool FenetrePrincipale::eventFilter(QObject *obj, QEvent *event)
 {
     if ((obj ==  ui->centralWidget
    		 || obj ==  ui->consigneBas
    		 || obj ==  ui->consigneDroite
    		 || obj ==  ui->consigneGauche
    		 || obj ==  ui->consigneHaut
    		 || obj ==  ui->valeurDiametreSoleil
    		 || obj ==  ui->imageCamera
    		 || obj ==  ui->consigneReset
    		 || obj ==  ui->lancerGuidage
    		 || obj ==  ui->messages
    		 || obj ==  ui->vitesseDecalageRapide
    		 || obj ==  ui->vitesseDecalageLent)
    		 && event->type() == QEvent::KeyPress) {
    		 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
    		 if(keyEvent->key() == Qt::Key_Up
    				 || keyEvent->key() == Qt::Key_Right
    				 || keyEvent->key() == Qt::Key_Left
    				 || keyEvent->key() == Qt::Key_Down) {

    			 keyPressEvent(keyEvent);
    			 return true;
         } else {
             return false;
         }
     } else {
         //passer l'événement à la classe parent
         return QMainWindow::eventFilter(obj, event);
     }
}
<commit_msg>Suppression de modeVitesse au profit de decalageLent<commit_after>/*
 * Guidage.cpp
 *
 *  Created on: 16 juin 2014
 *      Author: Maël Valais
 *
 *	Classe d'interaction utilisateur correspondant à la fenetre principale. Il s'agit de la
 *	partie graphique du logiciel. La classe WidgetImage (widgetimage.cpp) fait aussi partie
 *	des classes d'interaction utilisateur.
 *
 *  La classe communique avec les classes Capture (capture.cpp) et Guidage (guidage.cpp) grâce au
 *  système de signaux-slots. Par exemple, lorsqu'on appuie sur le bonton "Reset de consigne",
 *  le signal clicked() provenant du bouton ui->consigneReset est envoyé au slot consigneReset().
 *  Ici les noms sont idientiques.
 *
 *  Pour que la connexion entre le signal et le slot se fasse, on écrit :
 *  	QObject::connect(ui->consigneReset,SIGNAL(clicked()),guidage,SLOT(consigneReset()));
 *
 *	Toutes les fonctions de fenetreprincipale sont liées à de l'intéraction avec l'utilisateur.
 *	Tous les traitements se font dans Capture ou Guidage.
 */

#include "fenetreprincipale.h"
#include "fenetreprincipale_ui.h"
#include <QtCore/QTextCodec>

/*
 * ATTENTION, dans les constructeurs de Capture et Guidage on ne peut pas envoyer de signal car
 * l'appel du constructeur est effectué avant que la connexion soit effectuée
 */
FenetrePrincipale::FenetrePrincipale(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::FenetrePrincipale)
{
    // Lancement de l'interface utilisateur (setupUi provient de fenetreprincipale_ui.h
	// généré à partir de fenetreprincipale.ui, qui permet de modifier la disposition)
	ui->setupUi(this);

    // Couleurs de texte
    paletteTexteVert.setColor(QPalette::WindowText, Qt::green);
    paletteTexteRouge.setColor(QPalette::WindowText, Qt::red);
    paletteTexteJaune.setColor(QPalette::WindowText, Qt::yellow);

    // Faire en sorte que les chaines statiques "C-strings" soient considérées comme UTF-8
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));

    guidage = new Guidage; // ATTENTION: pas d'envoi de signaux dans le constructeur !
    guidage->moveToThread(&threadGuidage);
    capture = new Capture; // ATTENTION: pas d'envoi de signaux dans le constructeur !
    capture->moveToThread(&threadCapture);

    // Signaux-slots entre fenetreprincipale et guidage
    QObject::connect(guidage,SIGNAL(etatArduino(int)),this,SLOT(statutArduino(int)));
    QObject::connect(guidage,SIGNAL(etatGuidage(bool)),this,SLOT(statutGuidage(bool)));
    QObject::connect(guidage,SIGNAL(message(QString)),this,SLOT(afficherMessage(QString)));
    QObject::connect(this,SIGNAL(lancerGuidage()),guidage,SLOT(lancerGuidage()));
    QObject::connect(this,SIGNAL(stopperGuidage()),guidage,SLOT(stopperGuidage()));
    QObject::connect(this,SIGNAL(modificationConsigne(int,int,int)),guidage,SLOT(modifierConsigne(int,int,int)));
    QObject::connect(ui->consigneReset,SIGNAL(clicked()),guidage,SLOT(consigneReset()));
    QObject::connect(guidage,SIGNAL(signalBruit(double)),ui->ratioSignalBruit,SLOT(setNum(double)));
    QObject::connect(&threadGuidage,SIGNAL(finished()),guidage,SLOT(deleteLater()));

    // Signaux-slots entre fenetreprincipale et capture
    QObject::connect(capture,SIGNAL(etatCamera(int)),this,SLOT(statutCamera(int)));
    QObject::connect(capture,SIGNAL(diametreSoleil(int)),ui->valeurDiametreSoleil,SLOT(setValue(int)));
    QObject::connect(ui->valeurDiametreSoleil,SIGNAL(valueChanged(int)),capture,SLOT(modifierDiametre(int)));
    QObject::connect(this,SIGNAL(initialiserCapture()),capture,SLOT(initialiserObjetCapture()));
    QObject::connect(&threadCapture,SIGNAL(finished()),capture,SLOT(deleteLater()));

    // Signaux-slots entre capture et guidage
    QObject::connect(capture,SIGNAL(resultats(Image*,double,double,int,double)),guidage, SLOT(traiterResultatsCapture(Image*,double,double,int,double)));
    QObject::connect(capture,SIGNAL(stopperGuidage()),guidage, SLOT(stopperGuidage()));

    // Signaux-slots entre les éléments de l'interface
    QObject::connect(guidage,SIGNAL(imageSoleil(Image*)),ui->imageCamera,SLOT(afficherImageSoleil(Image*)));
    QObject::connect(guidage,SIGNAL(repereConsigne(float,float,float,QColor)),ui->imageCamera,SLOT(afficherRepereConsigne(float,float,float,QColor)));
    QObject::connect(guidage,SIGNAL(repereSoleil(float,float,float,QColor)),ui->imageCamera,SLOT(afficherRepereSoleil(float,float,float,QColor)));

    // Lancement des threads
    threadGuidage.start();
    threadCapture.start();
    emit initialiserCapture(); // En plus du constructeur Capture() on appelle initialiserCapture()
    // à cause du fait que les signaux ne peuvent pas être envoyés depuis un constructeur.

    // Le mode de décalage rapide est par défaut
    ui->vitesseDecalageRapide->setChecked(true);

    // Pour capturer les touches directionnelles du clavier pour controler la consigne,
    // on met un filtre sur toutes les parties de l'interface, et on redéfinit la méthode
    // eventFilter() (voir plus bas)
    ui->messages->installEventFilter(this);
    ui->consigneDroite->installEventFilter(this);
    ui->centralWidget->installEventFilter(this);
	ui->centralWidget->installEventFilter(this);
	ui->consigneBas->installEventFilter(this);
	ui->consigneDroite->installEventFilter(this);
	ui->consigneGauche->installEventFilter(this);
	ui->consigneHaut->installEventFilter(this);
	ui->valeurDiametreSoleil->installEventFilter(this);
	ui->imageCamera->installEventFilter(this);
	ui->consigneReset->installEventFilter(this);
	ui->lancerGuidage->installEventFilter(this);
	ui->messages->installEventFilter(this);
	ui->vitesseDecalageLent->installEventFilter(this);
	ui->vitesseDecalageRapide->installEventFilter(this);
}

FenetrePrincipale::~FenetrePrincipale() {
    delete ui;
}

void FenetrePrincipale::afficherMessage(QString msg) {
    ui->messages->append(msg);
}

void FenetrePrincipale::statutCamera(int etat) {
	if(etat & CAMERA_CONNEXION_ON) {
		ui->statutCamera->setPalette(paletteTexteVert);
		ui->statutCamera->setText("Caméra connectée");
	}
	else if(etat & CAMERA_CONNEXION_OFF){
		ui->statutCamera->setPalette(paletteTexteRouge);
		ui->statutCamera->setText("Caméra déconnectée");
	}
}

void FenetrePrincipale::statutArduino(int etat) {
	switch(etat) {
		case ARDUINO_CONNEXION_ON:
			ui->statutArduino->setPalette(paletteTexteVert);
			ui->statutArduino->setText("Arduino connecté");
			break;
		case ARDUINO_CONNEXION_OFF:
			ui->statutArduino->setPalette(paletteTexteRouge);
			ui->statutArduino->setText("Arduino déconnecté");
			break;
		case ARDUINO_FICHIER_INTROUV:
			ui->statutArduino->setPalette(paletteTexteRouge);
			ui->statutArduino->setText("Arduino introuvable");
	}
}

/**
 * Slot permettant de modifier l'affichage de l'état du guidage
 * @param statut True pour guidage en marche
 */
void FenetrePrincipale::statutGuidage(bool statut) {
	if(statut) {
		ui->statutGuidage->setPalette(paletteTexteVert);
		ui->statutGuidage->setText("Marche");
	}
	else {
		ui->statutGuidage->setPalette(paletteTexteRouge);
		ui->statutGuidage->setText("Arrêt");
	}
}

/*
 * NOTE SUR LES METHODES on_elementInterface_clicked() (par exemple) :
 *
 * Ces méthodes permettent de s'affranchir de l'étape signal-slot,
 * la méthode on_consigneHaut_clicked est appelée à chaque fois que
 * le bouton consigneHaut est pressé.
 *
 * Il s'agit d'un "raccourci" pour éviter de passer par un
 * signal -> slot -> appel de fonction.
 */

void FenetrePrincipale::on_consigneHaut_clicked() {
	emit modificationConsigne(-1,0,(ui->vitesseDecalageLent->isChecked()));
}
void FenetrePrincipale::on_consigneBas_clicked() {
	emit modificationConsigne(1,0,(ui->vitesseDecalageLent->isChecked()));
}
void FenetrePrincipale::on_consigneDroite_clicked() {
	emit modificationConsigne(0,1,(ui->vitesseDecalageLent->isChecked()));
}
void FenetrePrincipale::on_consigneGauche_clicked() {
	emit modificationConsigne(0,-1,(ui->vitesseDecalageLent->isChecked()));
}
void FenetrePrincipale::on_lancerGuidage_clicked() {
    emit lancerGuidage();
}
void FenetrePrincipale::on_stopperGuidage_clicked() {
	emit stopperGuidage();
}

void FenetrePrincipale::closeEvent(QCloseEvent* event) {
	threadCapture.quit(); // termine la boucle d'event de l'objet QThread
	threadGuidage.quit();
	threadGuidage.wait(); // Attend que le QThread soit terminé
	threadCapture.wait();
	cout << "Application terminée" << endl;
	// FIXME: quant on quitte comme ça, deleteLater() n'a pas le temps
	// de supprimer l'objet
}

void FenetrePrincipale::keyPressEvent(QKeyEvent* event) {
	switch(event->key()) {
		case Qt::Key_Up:
			emit modificationConsigne(-1,0,(ui->vitesseDecalageLent->isChecked()));
			break;
		case Qt::Key_Down:
			emit modificationConsigne(1,0,(ui->vitesseDecalageLent->isChecked()));
			break;
		case Qt::Key_Right:
			emit modificationConsigne(0,1,(ui->vitesseDecalageLent->isChecked()));
			break;
		case Qt::Key_Left:
			emit modificationConsigne(0,-1,(ui->vitesseDecalageLent->isChecked()));
			break;
	}
}

// Pour capturer les touches directionnelles du clavier pour controler la consigne
bool FenetrePrincipale::eventFilter(QObject *obj, QEvent *event)
 {
     if ((obj ==  ui->centralWidget
    		 || obj ==  ui->consigneBas
    		 || obj ==  ui->consigneDroite
    		 || obj ==  ui->consigneGauche
    		 || obj ==  ui->consigneHaut
    		 || obj ==  ui->valeurDiametreSoleil
    		 || obj ==  ui->imageCamera
    		 || obj ==  ui->consigneReset
    		 || obj ==  ui->lancerGuidage
    		 || obj ==  ui->messages
    		 || obj ==  ui->vitesseDecalageRapide
    		 || obj ==  ui->vitesseDecalageLent)
    		 && event->type() == QEvent::KeyPress) {
    		 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
    		 if(keyEvent->key() == Qt::Key_Up
    				 || keyEvent->key() == Qt::Key_Right
    				 || keyEvent->key() == Qt::Key_Left
    				 || keyEvent->key() == Qt::Key_Down) {

    			 keyPressEvent(keyEvent);
    			 return true;
         } else {
             return false;
         }
     } else {
         //passer l'événement à la classe parent
         return QMainWindow::eventFilter(obj, event);
     }
}
<|endoftext|>


<commit_before>#include "RuleMiner.h"

namespace Analytics {
    /**
 * An exact implementation of algorithm 6.2 on page 351 in the textbook.
 */
    QList<AssociationRule> RuleMiner::mineAssociationRules(QList<ItemList> frequentItemsets, float minimumConfidence) {
        QList<AssociationRule> associationRules;
        QList<ItemList> consequents;

        QList<SupportCount> frequentItemsetsSupportCounts = RuleMiner::calculateSupportCountsForFrequentItemsets(frequentItemsets);

        // Iterate over all frequent itemsets.
        foreach (ItemList frequentItemset, frequentItemsets) {
            // It's only possible to generate an association rule if there are at
            // least two items in the frequent itemset.
            if (frequentItemset.size() >= 2) {
                // Generate all 1-item consequents.
                consequents.clear();
                foreach (Item item, frequentItemset) {
                    ItemList consequent;
                    consequent.append(item);
                    consequents.append(consequent);
                }

#ifdef RULEMINER_DEBUG
                qDebug() << "Generating rules for frequent itemset" << frequentItemset << " and consequents " << consequents;
#endif

                // Attempt to generate association rules for this frequent
                // itemset and store the results.
                associationRules.append(RuleMiner::generateAssociationRulesForFrequentItemset(frequentItemset, consequents, frequentItemsets, frequentItemsetsSupportCounts, minimumConfidence));
            }
        }
        return associationRules;
    }

    //------------------------------------------------------------------------
    // Protected static methods.

    //------------------------------------------------------------------------
    // Public static methods.

    /**
     * Runs RuleMiner::calculateSupportForFrequentItemset() on a list of
     * frequent itemsets.
     *
     * @see RuleMiner::calculateSupportForFrequentItemset()
     */
    QList<SupportCount> RuleMiner::calculateSupportCountsForFrequentItemsets(QList<ItemList> frequentItemsets) {
        QList<SupportCount> supportCounts;
        foreach (ItemList frequentItemset, frequentItemsets)
            supportCounts.append(RuleMiner::calculateSupportCountForFrequentItemset(frequentItemset));
        return supportCounts;
    }

    /**
 * Given a frequent itemset, calculate its support count.
 * Do this by finding the minimum support count of all items in the frequent
 * itemset.
 */
    SupportCount RuleMiner::calculateSupportCountForFrequentItemset(ItemList frequentItemset) {
        SupportCount supportCount = MAX_SUPPORT;
        foreach (Item item, frequentItemset)
            if (item.supportCount < supportCount)
                supportCount = item.supportCount;
        return supportCount;
    }

    /**
     * A.k.a. "ap-genrules", but slightly different to fix a bug in that
     * algorithm: it accepts consequents of size 1, but doesn't generate
     * antecedents for these, instead it immediately generates consequents of
     * size 2. Algorithm 6.3 on page 352 in the textbook.
     * This variation of that algorithm fixes that.
     */
    QList<AssociationRule> RuleMiner::generateAssociationRulesForFrequentItemset(ItemList frequentItemset, QList<ItemList> consequents, QList<ItemList> frequentItemsets, QList<SupportCount> frequentItemsetsSupportCounts, float minimumConfidence) {
        QList<AssociationRule> associationRules;
        SupportCount frequentItemsetSupportCount, antecedentSupportCount;
        ItemList antecedent;
        float confidence;
        unsigned int k = frequentItemset.size(); // Size of the frequent itemset.
        unsigned int m = consequents[0].size(); // Size of a single consequent.

        // Iterate over all given consequents.
        foreach (ItemList consequent, consequents) {
            // Get the antecedent for the current consequent, so we effectively
            // get a candidate rule.
            antecedent = RuleMiner::getAntecedent(frequentItemset, consequent);

            // Calculate the confidence of this rule.
            frequentItemsetSupportCount = RuleMiner::calculateSupportCountForFrequentItemset(frequentItemset);
            // Take advantage of precalculated frequent itemset support counts
            // when they are available, and calculate the support count for
            // the antecedent on-the-fly otherwise.
            antecedentSupportCount = (frequentItemsetsSupportCounts.size() > 0) ? frequentItemsetsSupportCounts[frequentItemsets.indexOf(antecedent)] : RuleMiner::calculateSupportCountForFrequentItemset(antecedent);
            confidence = 1.0 * frequentItemsetSupportCount / antecedentSupportCount;

            // If the confidence is sufficiently high, we've found an
            // association rule that meets our requirements.
#ifdef RULEMINER_DEBUG
            qDebug () << "confidence" << confidence << ", frequent itemset support" << frequentItemsetSupportCount << ", antecedent support" << antecedentSupportCount << ", antecedent" << antecedent << ", consequent" << consequent;
#endif
            if (confidence >= minimumConfidence) {
                AssociationRule rule(antecedent, consequent, confidence);
                associationRules.append(rule);
            }
            // If the confidence is not sufficiently high, delete this
            // consequent, to prevent other consequents to be generated that
            // build upon this one since those consequents would have the same
            // insufficiently high confidence in the best case.
            else
                consequents.removeAll(consequent);
        }

        // If there are still consequents left (i.e. consequents with a
        // sufficiently high confidence), and the size of the consequents
        // still alows them to be expanded, expand the consequents and attempt
        // to generate more association rules with them.
        if (consequents.size() > 0 && k > m + 1) {
            QList<ItemList> candidateConsequents = RuleMiner::generateCandidateItemsets(consequents);
            associationRules.append(RuleMiner::generateAssociationRulesForFrequentItemset(frequentItemset, candidateConsequents, frequentItemsets, frequentItemsetsSupportCounts, minimumConfidence));
        }

        return associationRules;
    }

    /**
 * Build the antecedent for this candidate consequent, which are all items in
 * the frequent itemset except for those in the candidate consequent.
 */
    ItemList RuleMiner::getAntecedent(ItemList frequentItemset, ItemList consequent) {
        ItemList antecedent;
        foreach (Item item, frequentItemset)
            if (!consequent.contains(item))
                antecedent.append(item);
        return antecedent;
    }

    /**
     * A.k.a. "apriori-gen", but without pruning because we're already working
     * with frequent itemsets, which were generated by the FP-growth
     * algorithm. We solely need this function for association rule mining,
     * not for frequent itemset generation.
     */
    QList<ItemList> RuleMiner::generateCandidateItemsets(QList<ItemList> frequentItemsubsets) {
        // Phase 1: candidate generation.
        QList<ItemList> candidateItemsets;
        int allButOne = frequentItemsubsets[0].size() - 1;
        foreach (ItemList frequentItemsubset, frequentItemsubsets) {
            foreach (ItemList otherFrequentItemsubset, frequentItemsubsets) {
                if (allButOne == 0) {
                    if (frequentItemsubset[0] == otherFrequentItemsubset[0]) {
                        break;
                    }
                }
                else {
                    // Iterate over all but the last item in this frequent
                    // itemsubset.
                    for (int i = 0; i < allButOne; i++) {
                        if (frequentItemsubset[i] != otherFrequentItemsubset[i])
                            break;
                    }
                }

                // The first all-but-one items of the two frequent itemsubsets
                // matched! Now generate a candidate itemset, whose:
                // - first k-1 == allButOne items are copied from the first
                //   frequent itemsubset (outer)
                // - last (k == allButOne + 1) item is copied from the second
                //   frequent itemsubset (inner)
                ItemList candidateItemset;
                candidateItemset.append(frequentItemsubset);
                candidateItemset.append(otherFrequentItemsubset[allButOne]);
                // Store this candidate set.
                candidateItemsets.append(candidateItemset);
            }
        }

        // Phase 2: candidate pruning.
        // Not necessary!

        return candidateItemsets;
    }
}
<commit_msg>More comment indentation fixes.<commit_after>#include "RuleMiner.h"

namespace Analytics {
    /**
     * An exact implementation of algorithm 6.2 on page 351 in the textbook.
     */
    QList<AssociationRule> RuleMiner::mineAssociationRules(QList<ItemList> frequentItemsets, float minimumConfidence) {
        QList<AssociationRule> associationRules;
        QList<ItemList> consequents;

        QList<SupportCount> frequentItemsetsSupportCounts = RuleMiner::calculateSupportCountsForFrequentItemsets(frequentItemsets);

        // Iterate over all frequent itemsets.
        foreach (ItemList frequentItemset, frequentItemsets) {
            // It's only possible to generate an association rule if there are at
            // least two items in the frequent itemset.
            if (frequentItemset.size() >= 2) {
                // Generate all 1-item consequents.
                consequents.clear();
                foreach (Item item, frequentItemset) {
                    ItemList consequent;
                    consequent.append(item);
                    consequents.append(consequent);
                }

#ifdef RULEMINER_DEBUG
                qDebug() << "Generating rules for frequent itemset" << frequentItemset << " and consequents " << consequents;
#endif

                // Attempt to generate association rules for this frequent
                // itemset and store the results.
                associationRules.append(RuleMiner::generateAssociationRulesForFrequentItemset(frequentItemset, consequents, frequentItemsets, frequentItemsetsSupportCounts, minimumConfidence));
            }
        }
        return associationRules;
    }

    //------------------------------------------------------------------------
    // Protected static methods.

    //------------------------------------------------------------------------
    // Public static methods.

    /**
     * Runs RuleMiner::calculateSupportForFrequentItemset() on a list of
     * frequent itemsets.
     *
     * @see RuleMiner::calculateSupportForFrequentItemset()
     */
    QList<SupportCount> RuleMiner::calculateSupportCountsForFrequentItemsets(QList<ItemList> frequentItemsets) {
        QList<SupportCount> supportCounts;
        foreach (ItemList frequentItemset, frequentItemsets)
            supportCounts.append(RuleMiner::calculateSupportCountForFrequentItemset(frequentItemset));
        return supportCounts;
    }

    /**
     * Given a frequent itemset, calculate its support count.
     * Do this by finding the minimum support count of all items in the
     * frequent itemset.
     */
    SupportCount RuleMiner::calculateSupportCountForFrequentItemset(ItemList frequentItemset) {
        SupportCount supportCount = MAX_SUPPORT;
        foreach (Item item, frequentItemset)
            if (item.supportCount < supportCount)
                supportCount = item.supportCount;
        return supportCount;
    }

    /**
     * A.k.a. "ap-genrules", but slightly different to fix a bug in that
     * algorithm: it accepts consequents of size 1, but doesn't generate
     * antecedents for these, instead it immediately generates consequents of
     * size 2. Algorithm 6.3 on page 352 in the textbook.
     * This variation of that algorithm fixes that.
     */
    QList<AssociationRule> RuleMiner::generateAssociationRulesForFrequentItemset(ItemList frequentItemset, QList<ItemList> consequents, QList<ItemList> frequentItemsets, QList<SupportCount> frequentItemsetsSupportCounts, float minimumConfidence) {
        QList<AssociationRule> associationRules;
        SupportCount frequentItemsetSupportCount, antecedentSupportCount;
        ItemList antecedent;
        float confidence;
        unsigned int k = frequentItemset.size(); // Size of the frequent itemset.
        unsigned int m = consequents[0].size(); // Size of a single consequent.

        // Iterate over all given consequents.
        foreach (ItemList consequent, consequents) {
            // Get the antecedent for the current consequent, so we effectively
            // get a candidate rule.
            antecedent = RuleMiner::getAntecedent(frequentItemset, consequent);

            // Calculate the confidence of this rule.
            frequentItemsetSupportCount = RuleMiner::calculateSupportCountForFrequentItemset(frequentItemset);
            // Take advantage of precalculated frequent itemset support counts
            // when they are available, and calculate the support count for
            // the antecedent on-the-fly otherwise.
            antecedentSupportCount = (frequentItemsetsSupportCounts.size() > 0) ? frequentItemsetsSupportCounts[frequentItemsets.indexOf(antecedent)] : RuleMiner::calculateSupportCountForFrequentItemset(antecedent);
            confidence = 1.0 * frequentItemsetSupportCount / antecedentSupportCount;

            // If the confidence is sufficiently high, we've found an
            // association rule that meets our requirements.
#ifdef RULEMINER_DEBUG
            qDebug () << "confidence" << confidence << ", frequent itemset support" << frequentItemsetSupportCount << ", antecedent support" << antecedentSupportCount << ", antecedent" << antecedent << ", consequent" << consequent;
#endif
            if (confidence >= minimumConfidence) {
                AssociationRule rule(antecedent, consequent, confidence);
                associationRules.append(rule);
            }
            // If the confidence is not sufficiently high, delete this
            // consequent, to prevent other consequents to be generated that
            // build upon this one since those consequents would have the same
            // insufficiently high confidence in the best case.
            else
                consequents.removeAll(consequent);
        }

        // If there are still consequents left (i.e. consequents with a
        // sufficiently high confidence), and the size of the consequents
        // still alows them to be expanded, expand the consequents and attempt
        // to generate more association rules with them.
        if (consequents.size() > 0 && k > m + 1) {
            QList<ItemList> candidateConsequents = RuleMiner::generateCandidateItemsets(consequents);
            associationRules.append(RuleMiner::generateAssociationRulesForFrequentItemset(frequentItemset, candidateConsequents, frequentItemsets, frequentItemsetsSupportCounts, minimumConfidence));
        }

        return associationRules;
    }

    /**
     * Build the antecedent for this candidate consequent, which are all items
     * in the frequent itemset except for those in the candidate consequent.
     */
    ItemList RuleMiner::getAntecedent(ItemList frequentItemset, ItemList consequent) {
        ItemList antecedent;
        foreach (Item item, frequentItemset)
            if (!consequent.contains(item))
                antecedent.append(item);
        return antecedent;
    }

    /**
     * A.k.a. "apriori-gen", but without pruning because we're already working
     * with frequent itemsets, which were generated by the FP-growth
     * algorithm. We solely need this function for association rule mining,
     * not for frequent itemset generation.
     */
    QList<ItemList> RuleMiner::generateCandidateItemsets(QList<ItemList> frequentItemsubsets) {
        // Phase 1: candidate generation.
        QList<ItemList> candidateItemsets;
        int allButOne = frequentItemsubsets[0].size() - 1;
        foreach (ItemList frequentItemsubset, frequentItemsubsets) {
            foreach (ItemList otherFrequentItemsubset, frequentItemsubsets) {
                if (allButOne == 0) {
                    if (frequentItemsubset[0] == otherFrequentItemsubset[0]) {
                        break;
                    }
                }
                else {
                    // Iterate over all but the last item in this frequent
                    // itemsubset.
                    for (int i = 0; i < allButOne; i++) {
                        if (frequentItemsubset[i] != otherFrequentItemsubset[i])
                            break;
                    }
                }

                // The first all-but-one items of the two frequent itemsubsets
                // matched! Now generate a candidate itemset, whose:
                // - first k-1 == allButOne items are copied from the first
                //   frequent itemsubset (outer)
                // - last (k == allButOne + 1) item is copied from the second
                //   frequent itemsubset (inner)
                ItemList candidateItemset;
                candidateItemset.append(frequentItemsubset);
                candidateItemset.append(otherFrequentItemsubset[allButOne]);
                // Store this candidate set.
                candidateItemsets.append(candidateItemset);
            }
        }

        // Phase 2: candidate pruning.
        // Not necessary!

        return candidateItemsets;
    }
}
<|endoftext|>


<commit_before>/***************************************************************************
    copyright            : (C) 2003-2004 by Allan Sandfeld Jensen
    email                : kde@carewolf.org
 ***************************************************************************/

/***************************************************************************
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License version   *
 *   2.1 as published by the Free Software Foundation.                     *
 *                                                                         *
 *   This library 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     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
 *   USA                                                                   *
 *                                                                         *
 *   Alternatively, this file is available under the Mozilla Public        *
 *   License Version 1.1.  You may obtain a copy of the License at         *
 *   http://www.mozilla.org/MPL/                                           *
 ***************************************************************************/

#include <tbytevector.h>
#include <tstring.h>
#include <tlist.h>
#include <tdebug.h>

#include <id3v2header.h>
#include <id3v2tag.h>
#include <id3v1tag.h>

#include "flacfile.h"
#include "flactag.h"

using namespace TagLib;

namespace TagLib {
  namespace FLAC {
    enum BlockType { StreamInfo = 0, Padding, Application, SeekTable, VorbisComment, CueSheet };
  }
}

class FLAC::File::FilePrivate
{
public:
  FilePrivate() :
    ID3v2FrameFactory(ID3v2::FrameFactory::instance()),
    ID3v2Tag(0),
    ID3v2Location(-1),
    ID3v2OriginalSize(0),
    ID3v1Tag(0),
    ID3v1Location(-1),
    comment(0),
    tag(0),
    properties(0),
    flacStart(0),
    streamStart(0),
    streamLength(0),
    scanned(false),
    hasXiphComment(false),
    hasID3v2(false),
    hasID3v1(false) {}

  ~FilePrivate()
  {
    delete ID3v2Tag;
    delete ID3v1Tag;
    delete comment;
    delete tag;
    delete properties;
  }

  const ID3v2::FrameFactory *ID3v2FrameFactory;
  ID3v2::Tag *ID3v2Tag;
  long ID3v2Location;
  uint ID3v2OriginalSize;

  ID3v1::Tag *ID3v1Tag;
  long ID3v1Location;

  Ogg::XiphComment *comment;

  FLAC::Tag *tag;

  Properties *properties;
  ByteVector streamInfoData;
  ByteVector xiphCommentData;

  long flacStart;
  long streamStart;
  long streamLength;
  bool scanned;

  bool hasXiphComment;
  bool hasID3v2;
  bool hasID3v1;
};

////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

FLAC::File::File(FileName file, bool readProperties,
                 Properties::ReadStyle propertiesStyle) :
  TagLib::File(file)
{
  d = new FilePrivate;
  read(readProperties, propertiesStyle);
}

FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
                 bool readProperties, Properties::ReadStyle propertiesStyle) :
  TagLib::File(file)
{
  d = new FilePrivate;
  d->ID3v2FrameFactory = frameFactory;
  read(readProperties, propertiesStyle);
}

FLAC::File::~File()
{
  delete d;
}

TagLib::Tag *FLAC::File::tag() const
{
  return d->tag;
}

FLAC::Properties *FLAC::File::audioProperties() const
{
  return d->properties;
}

bool FLAC::File::save()
{
  if(readOnly()) {
    debug("FLAC::File::save() - Cannot save to a read only file.");
    return false;
  }

  // Create new vorbis comments

  if(!d->comment) {
    d->comment = new Ogg::XiphComment;
    if(d->tag)
      Tag::duplicate(d->tag, d->comment, true);
  }

  d->xiphCommentData = d->comment->render(false);

  // A Xiph comment portion of the data stream starts with a 4-byte descriptor.
  // The first byte indicates the frame type.  The last three bytes are used
  // to give the length of the data segment.  Here we start

  ByteVector data = ByteVector::fromUInt(d->xiphCommentData.size());

  data[0] = char(VorbisComment);
  data.append(d->xiphCommentData);

   // If file already have comment => find and update it
   // if not => insert one

   // TODO: Search for padding and use that

  if(d->hasXiphComment) {

    long nextBlockOffset = d->flacStart;
    bool isLastBlock = false;

    while(!isLastBlock) {
      seek(nextBlockOffset);

      ByteVector header = readBlock(4);
      char blockType = header[0] & 0x7f;
      isLastBlock = (header[0] & 0x80) != 0;
      uint blockLength = header.mid(1, 3).toUInt();

      if(blockType == VorbisComment) {
        data[0] = header[0];
        insert(data, nextBlockOffset, blockLength + 4);
        break;
      }

      nextBlockOffset += blockLength + 4;
    }
  }
  else {

    const long firstBlockOffset = d->flacStart;
    seek(firstBlockOffset);

    ByteVector header = readBlock(4);
    bool isLastBlock = (header[0] & 0x80) != 0;
    uint blockLength = header.mid(1, 3).toUInt();

    if(isLastBlock) {

      // If the first block was previously also the last block, then we want to
      // mark it as no longer being the first block (the writeBlock() call) and
      // then set the data for the block that we're about to write to mark our
      // new block as the last block.

      seek(firstBlockOffset);
      writeBlock(static_cast<char>(header[0] & 0x7F));
      data[0] |= 0x80;
    }

    insert(data, firstBlockOffset + blockLength + 4, 0);
    d->hasXiphComment = true;
  }

  // Update ID3 tags

  if(d->ID3v2Tag) {
    if(d->hasID3v2) {
      if(d->ID3v2Location < d->flacStart)
        debug("FLAC::File::save() -- This can't be right -- an ID3v2 tag after the "
              "start of the FLAC bytestream?  Not writing the ID3v2 tag.");
      else
        insert(d->ID3v2Tag->render(), d->ID3v2Location, d->ID3v2OriginalSize);
    }
    else
      insert(d->ID3v2Tag->render(), 0, 0);
  }

  if(d->ID3v1Tag) {
    seek(d->ID3v1Tag ? -128 : 0, End);
    writeBlock(d->ID3v1Tag->render());
  }

  return true;
}

ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
{
  if(!create || d->ID3v2Tag)
    return d->ID3v2Tag;

  // no ID3v2 tag exists and we've been asked to create one

  d->ID3v2Tag = new ID3v2::Tag;
  return d->ID3v2Tag;
}

ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
{
  if(!create || d->ID3v1Tag)
    return d->ID3v1Tag;

  // no ID3v1 tag exists and we've been asked to create one

  d->ID3v1Tag = new ID3v1::Tag;
  return d->ID3v1Tag;
}

Ogg::XiphComment *FLAC::File::xiphComment(bool create)
{
  if(!create || d->comment)
    return d->comment;

  // no XiphComment exists and we've been asked to create one

  d->comment = new Ogg::XiphComment;
  return d->comment;
}

void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
{
  d->ID3v2FrameFactory = factory;
}

////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
  // Look for an ID3v2 tag

  d->ID3v2Location = findID3v2();

  if(d->ID3v2Location >= 0) {

    d->ID3v2Tag = new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory);

    d->ID3v2OriginalSize = d->ID3v2Tag->header()->completeTagSize();

    if(d->ID3v2Tag->header()->tagSize() <= 0) {
      delete d->ID3v2Tag;
      d->ID3v2Tag = 0;
    }
    else
      d->hasID3v2 = true;
  }

  // Look for an ID3v1 tag

  d->ID3v1Location = findID3v1();

  if(d->ID3v1Location >= 0) {
    d->ID3v1Tag = new ID3v1::Tag(this, d->ID3v1Location);
    d->hasID3v1 = true;
  }

  // Look for FLAC metadata, including vorbis comments

  scan();

  if (!isValid()) return;

  if(d->hasXiphComment)
    d->comment = new Ogg::XiphComment(xiphCommentData());

  if(d->hasXiphComment || d->hasID3v2 || d->hasID3v1)
    d->tag = new FLAC::Tag(d->comment, d->ID3v2Tag, d->ID3v1Tag);
  else
    d->tag = new FLAC::Tag(new Ogg::XiphComment);

  if(readProperties)
    d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle);
}

ByteVector FLAC::File::streamInfoData()
{
  if (isValid())
    return d->streamInfoData;
  else
    return ByteVector();
}

ByteVector FLAC::File::xiphCommentData()
{
  if (isValid() && d->hasXiphComment)
    return d->xiphCommentData;
  else
    return ByteVector();
}

long FLAC::File::streamLength()
{
  return d->streamLength;
}

void FLAC::File::scan()
{
  // Scan the metadata pages

  if(d->scanned)
    return;

  if(!isValid())
    return;

  long nextBlockOffset;

  if(d->hasID3v2)
    nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
  else
    nextBlockOffset = find("fLaC");

  if(nextBlockOffset < 0) {
    debug("FLAC::File::scan() -- FLAC stream not found");
    setValid(false);
    return;
  }

  nextBlockOffset += 4;
  d->flacStart = nextBlockOffset;

  seek(nextBlockOffset);

  ByteVector header = readBlock(4);

  // Header format (from spec):
  // <1> Last-metadata-block flag
  // <7> BLOCK_TYPE
  //    0 : STREAMINFO
  //    1 : PADDING
  //    ..
  //    4 : VORBIS_COMMENT
  //    ..
  // <24> Length of metadata to follow

  char blockType = header[0] & 0x7f;
  bool isLastBlock = (header[0] & 0x80) != 0;
  uint length = header.mid(1, 3).toUInt();

  // First block should be the stream_info metadata

  if(blockType != StreamInfo) {
    debug("FLAC::File::scan() -- invalid FLAC stream");
    setValid(false);
    return;
  }

  d->streamInfoData = readBlock(length);
  nextBlockOffset += length + 4;

  // Search through the remaining metadata

  while(!isLastBlock) {
    header = readBlock(4);
    blockType = header[0] & 0x7f;
    isLastBlock = (header[0] & 0x80) != 0;
    length = header.mid(1, 3).toUInt();

    if(blockType == Padding) {
      // debug("FLAC::File::scan() -- Padding found");
    }
    // Found the vorbis-comment
    else if(blockType == VorbisComment) {
      d->xiphCommentData = readBlock(length);
      d->hasXiphComment = true;
    }

    nextBlockOffset += length + 4;

    if(nextBlockOffset >= File::length()) {
      debug("FLAC::File::scan() -- FLAC stream corrupted");
      setValid(false);
      return;
    }
    seek(nextBlockOffset);
  }

  // End of metadata, now comes the datastream
  d->streamStart = nextBlockOffset;
  d->streamLength = File::length() - d->streamStart;
  if (d->hasID3v1)
    d->streamLength -= 128;

  d->scanned = true;
}

long FLAC::File::findID3v1()
{
  if(!isValid())
    return -1;

  seek(-128, End);
  long p = tell();

  if(readBlock(3) == ID3v1::Tag::fileIdentifier())
    return p;

  return -1;
}

long FLAC::File::findID3v2()
{
  if(!isValid())
    return -1;

  seek(0);

  if(readBlock(3) == ID3v2::Header::fileIdentifier())
    return 0;

  return -1;
}
<commit_msg>SVN_SILENT a little prettier<commit_after>/***************************************************************************
    copyright            : (C) 2003-2004 by Allan Sandfeld Jensen
    email                : kde@carewolf.org
 ***************************************************************************/

/***************************************************************************
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License version   *
 *   2.1 as published by the Free Software Foundation.                     *
 *                                                                         *
 *   This library 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     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
 *   USA                                                                   *
 *                                                                         *
 *   Alternatively, this file is available under the Mozilla Public        *
 *   License Version 1.1.  You may obtain a copy of the License at         *
 *   http://www.mozilla.org/MPL/                                           *
 ***************************************************************************/

#include <tbytevector.h>
#include <tstring.h>
#include <tlist.h>
#include <tdebug.h>

#include <id3v2header.h>
#include <id3v2tag.h>
#include <id3v1tag.h>

#include "flacfile.h"
#include "flactag.h"

using namespace TagLib;

namespace
{
  enum BlockType { StreamInfo = 0, Padding, Application, SeekTable, VorbisComment, CueSheet };
}

class FLAC::File::FilePrivate
{
public:
  FilePrivate() :
    ID3v2FrameFactory(ID3v2::FrameFactory::instance()),
    ID3v2Tag(0),
    ID3v2Location(-1),
    ID3v2OriginalSize(0),
    ID3v1Tag(0),
    ID3v1Location(-1),
    comment(0),
    tag(0),
    properties(0),
    flacStart(0),
    streamStart(0),
    streamLength(0),
    scanned(false),
    hasXiphComment(false),
    hasID3v2(false),
    hasID3v1(false) {}

  ~FilePrivate()
  {
    delete ID3v2Tag;
    delete ID3v1Tag;
    delete comment;
    delete tag;
    delete properties;
  }

  const ID3v2::FrameFactory *ID3v2FrameFactory;
  ID3v2::Tag *ID3v2Tag;
  long ID3v2Location;
  uint ID3v2OriginalSize;

  ID3v1::Tag *ID3v1Tag;
  long ID3v1Location;

  Ogg::XiphComment *comment;

  FLAC::Tag *tag;

  Properties *properties;
  ByteVector streamInfoData;
  ByteVector xiphCommentData;

  long flacStart;
  long streamStart;
  long streamLength;
  bool scanned;

  bool hasXiphComment;
  bool hasID3v2;
  bool hasID3v1;
};

////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

FLAC::File::File(FileName file, bool readProperties,
                 Properties::ReadStyle propertiesStyle) :
  TagLib::File(file)
{
  d = new FilePrivate;
  read(readProperties, propertiesStyle);
}

FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
                 bool readProperties, Properties::ReadStyle propertiesStyle) :
  TagLib::File(file)
{
  d = new FilePrivate;
  d->ID3v2FrameFactory = frameFactory;
  read(readProperties, propertiesStyle);
}

FLAC::File::~File()
{
  delete d;
}

TagLib::Tag *FLAC::File::tag() const
{
  return d->tag;
}

FLAC::Properties *FLAC::File::audioProperties() const
{
  return d->properties;
}

bool FLAC::File::save()
{
  if(readOnly()) {
    debug("FLAC::File::save() - Cannot save to a read only file.");
    return false;
  }

  // Create new vorbis comments

  if(!d->comment) {
    d->comment = new Ogg::XiphComment;
    if(d->tag)
      Tag::duplicate(d->tag, d->comment, true);
  }

  d->xiphCommentData = d->comment->render(false);

  // A Xiph comment portion of the data stream starts with a 4-byte descriptor.
  // The first byte indicates the frame type.  The last three bytes are used
  // to give the length of the data segment.  Here we start

  ByteVector data = ByteVector::fromUInt(d->xiphCommentData.size());

  data[0] = char(VorbisComment);
  data.append(d->xiphCommentData);

   // If file already have comment => find and update it
   // if not => insert one

   // TODO: Search for padding and use that

  if(d->hasXiphComment) {

    long nextBlockOffset = d->flacStart;
    bool isLastBlock = false;

    while(!isLastBlock) {
      seek(nextBlockOffset);

      ByteVector header = readBlock(4);
      char blockType = header[0] & 0x7f;
      isLastBlock = (header[0] & 0x80) != 0;
      uint blockLength = header.mid(1, 3).toUInt();

      if(blockType == VorbisComment) {
        data[0] = header[0];
        insert(data, nextBlockOffset, blockLength + 4);
        break;
      }

      nextBlockOffset += blockLength + 4;
    }
  }
  else {

    const long firstBlockOffset = d->flacStart;
    seek(firstBlockOffset);

    ByteVector header = readBlock(4);
    bool isLastBlock = (header[0] & 0x80) != 0;
    uint blockLength = header.mid(1, 3).toUInt();

    if(isLastBlock) {

      // If the first block was previously also the last block, then we want to
      // mark it as no longer being the first block (the writeBlock() call) and
      // then set the data for the block that we're about to write to mark our
      // new block as the last block.

      seek(firstBlockOffset);
      writeBlock(static_cast<char>(header[0] & 0x7F));
      data[0] |= 0x80;
    }

    insert(data, firstBlockOffset + blockLength + 4, 0);
    d->hasXiphComment = true;
  }

  // Update ID3 tags

  if(d->ID3v2Tag) {
    if(d->hasID3v2) {
      if(d->ID3v2Location < d->flacStart)
        debug("FLAC::File::save() -- This can't be right -- an ID3v2 tag after the "
              "start of the FLAC bytestream?  Not writing the ID3v2 tag.");
      else
        insert(d->ID3v2Tag->render(), d->ID3v2Location, d->ID3v2OriginalSize);
    }
    else
      insert(d->ID3v2Tag->render(), 0, 0);
  }

  if(d->ID3v1Tag) {
    seek(d->ID3v1Tag ? -128 : 0, End);
    writeBlock(d->ID3v1Tag->render());
  }

  return true;
}

ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
{
  if(!create || d->ID3v2Tag)
    return d->ID3v2Tag;

  // no ID3v2 tag exists and we've been asked to create one

  d->ID3v2Tag = new ID3v2::Tag;
  return d->ID3v2Tag;
}

ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
{
  if(!create || d->ID3v1Tag)
    return d->ID3v1Tag;

  // no ID3v1 tag exists and we've been asked to create one

  d->ID3v1Tag = new ID3v1::Tag;
  return d->ID3v1Tag;
}

Ogg::XiphComment *FLAC::File::xiphComment(bool create)
{
  if(!create || d->comment)
    return d->comment;

  // no XiphComment exists and we've been asked to create one

  d->comment = new Ogg::XiphComment;
  return d->comment;
}

void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
{
  d->ID3v2FrameFactory = factory;
}

////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
  // Look for an ID3v2 tag

  d->ID3v2Location = findID3v2();

  if(d->ID3v2Location >= 0) {

    d->ID3v2Tag = new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory);

    d->ID3v2OriginalSize = d->ID3v2Tag->header()->completeTagSize();

    if(d->ID3v2Tag->header()->tagSize() <= 0) {
      delete d->ID3v2Tag;
      d->ID3v2Tag = 0;
    }
    else
      d->hasID3v2 = true;
  }

  // Look for an ID3v1 tag

  d->ID3v1Location = findID3v1();

  if(d->ID3v1Location >= 0) {
    d->ID3v1Tag = new ID3v1::Tag(this, d->ID3v1Location);
    d->hasID3v1 = true;
  }

  // Look for FLAC metadata, including vorbis comments

  scan();

  if (!isValid()) return;

  if(d->hasXiphComment)
    d->comment = new Ogg::XiphComment(xiphCommentData());

  if(d->hasXiphComment || d->hasID3v2 || d->hasID3v1)
    d->tag = new FLAC::Tag(d->comment, d->ID3v2Tag, d->ID3v1Tag);
  else
    d->tag = new FLAC::Tag(new Ogg::XiphComment);

  if(readProperties)
    d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle);
}

ByteVector FLAC::File::streamInfoData()
{
  if (isValid())
    return d->streamInfoData;
  else
    return ByteVector();
}

ByteVector FLAC::File::xiphCommentData()
{
  if (isValid() && d->hasXiphComment)
    return d->xiphCommentData;
  else
    return ByteVector();
}

long FLAC::File::streamLength()
{
  return d->streamLength;
}

void FLAC::File::scan()
{
  // Scan the metadata pages

  if(d->scanned)
    return;

  if(!isValid())
    return;

  long nextBlockOffset;

  if(d->hasID3v2)
    nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
  else
    nextBlockOffset = find("fLaC");

  if(nextBlockOffset < 0) {
    debug("FLAC::File::scan() -- FLAC stream not found");
    setValid(false);
    return;
  }

  nextBlockOffset += 4;
  d->flacStart = nextBlockOffset;

  seek(nextBlockOffset);

  ByteVector header = readBlock(4);

  // Header format (from spec):
  // <1> Last-metadata-block flag
  // <7> BLOCK_TYPE
  //    0 : STREAMINFO
  //    1 : PADDING
  //    ..
  //    4 : VORBIS_COMMENT
  //    ..
  // <24> Length of metadata to follow

  char blockType = header[0] & 0x7f;
  bool isLastBlock = (header[0] & 0x80) != 0;
  uint length = header.mid(1, 3).toUInt();

  // First block should be the stream_info metadata

  if(blockType != StreamInfo) {
    debug("FLAC::File::scan() -- invalid FLAC stream");
    setValid(false);
    return;
  }

  d->streamInfoData = readBlock(length);
  nextBlockOffset += length + 4;

  // Search through the remaining metadata

  while(!isLastBlock) {
    header = readBlock(4);
    blockType = header[0] & 0x7f;
    isLastBlock = (header[0] & 0x80) != 0;
    length = header.mid(1, 3).toUInt();

    if(blockType == Padding) {
      // debug("FLAC::File::scan() -- Padding found");
    }
    // Found the vorbis-comment
    else if(blockType == VorbisComment) {
      d->xiphCommentData = readBlock(length);
      d->hasXiphComment = true;
    }

    nextBlockOffset += length + 4;

    if(nextBlockOffset >= File::length()) {
      debug("FLAC::File::scan() -- FLAC stream corrupted");
      setValid(false);
      return;
    }
    seek(nextBlockOffset);
  }

  // End of metadata, now comes the datastream
  d->streamStart = nextBlockOffset;
  d->streamLength = File::length() - d->streamStart;
  if (d->hasID3v1)
    d->streamLength -= 128;

  d->scanned = true;
}

long FLAC::File::findID3v1()
{
  if(!isValid())
    return -1;

  seek(-128, End);
  long p = tell();

  if(readBlock(3) == ID3v1::Tag::fileIdentifier())
    return p;

  return -1;
}

long FLAC::File::findID3v2()
{
  if(!isValid())
    return -1;

  seek(0);

  if(readBlock(3) == ID3v2::Header::fileIdentifier())
    return 0;

  return -1;
}
<|endoftext|>


<commit_before>/*
MIT License

Copyright (c) 2017 Eren Okka

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 <set>
#include <string>

#include <windows.h>

#include "window_title.h"

#include "../util.h"

namespace anisthesia {
namespace win {

std::wstring GetWindowClassName(HWND hwnd) {
  // The maximum size for lpszClassName, according to the documentation of
  // WNDCLASSEX structure
  constexpr int kMaxSize = 256;

  WCHAR buffer[kMaxSize];
  const auto size = ::GetClassName(hwnd, buffer, kMaxSize);
  return std::wstring(buffer, size);
}

std::wstring GetWindowText(HWND hwnd) {
  // We could learn the actual size with GetWindowTextLength, but this arbitrary
  // value suffices for our purpose.
  constexpr int kMaxSize = 1024;

  WCHAR buffer[kMaxSize];
  const auto size = ::GetWindowText(hwnd, buffer, kMaxSize);
  return std::wstring(buffer, size);
}

DWORD GetWindowProcessId(HWND hwnd) {
  DWORD process_id = 0;
  ::GetWindowThreadProcessId(hwnd, &process_id);
  return process_id;
}

std::wstring GetProcessPath(DWORD process_id) {
  // If we try to open a SYSTEM process, this function fails and the last error
  // code is ERROR_ACCESS_DENIED.
  //
  // Note that if we requested PROCESS_QUERY_INFORMATION access right instead
  // of PROCESS_QUERY_LIMITED_INFORMATION, this function would fail when used
  // to open an elevated process.
  Handle process_handle(::OpenProcess(
      PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id));

  if (!process_handle)
    return std::wstring();

  WCHAR buffer[MAX_PATH];
  DWORD buffer_size = MAX_PATH;

  // Note that this function requires Windows Vista or above. You may use
  // GetProcessImageFileName or GetModuleFileNameEx on earlier versions.
  if (!::QueryFullProcessImageName(process_handle.get(), 0,
                                   buffer, &buffer_size)) {
    return std::wstring();
  }

  return std::wstring(buffer, buffer_size);
}

////////////////////////////////////////////////////////////////////////////////

bool VerifyWindowStyle(HWND hwnd) {
  const auto window_ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE);

  auto has_ex_style = [&window_ex_style](DWORD ex_style) {
    return (window_ex_style & ex_style) != 0;
  };

  // Toolbars, tooltips and similar topmost windows
  if (has_ex_style(WS_EX_TOPMOST) && has_ex_style(WS_EX_TOOLWINDOW))
    return false;

  return true;
}

bool VerifyClassName(const std::wstring& name) {
  static const std::set<std::wstring> invalid_names = {
    // System classes
    L"#32770",         // Dialog box
    L"CabinetWClass",  // Windows Explorer
    L"ComboLBox",
    L"DDEMLEvent",
    L"DDEMLMom",
    L"DirectUIHWND",
    L"GDI+ Hook Window Class",
    L"IME",
    L"Internet Explorer_Hidden",
    L"MSCTFIME UI",
    L"tooltips_class32",
  };

  return !name.empty() && !invalid_names.count(name);
}

bool VerifyProcessPath(const std::wstring& path) {
  return !path.empty() && !IsSystemDirectory(path);
}

bool VerifyProcessFileName(const std::wstring& name) {
  static const std::set<std::wstring> invalid_names = {
    // System files
    L"explorer",    // Windows Explorer
    L"taskeng",     // Task Scheduler Engine
    L"taskhost",    // Host Process for Windows Tasks
    L"taskhostex",  // Host Process for Windows Tasks
    L"Taskmgr",     // Task Manager
  };

  return !name.empty() && !invalid_names.count(name);
}

////////////////////////////////////////////////////////////////////////////////

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM param) {
  if (!VerifyWindowStyle(hwnd))
    return TRUE;

  Window window;
  window.handle = hwnd;
  window.process_id = GetWindowProcessId(hwnd);
  window.text = GetWindowText(hwnd);

  window.class_name = GetWindowClassName(hwnd);
  if (!VerifyClassName(window.class_name))
    return TRUE;

  const auto process_path = GetProcessPath(window.process_id);
  if (!VerifyProcessPath(process_path))
    return TRUE;

  window.process_file_name = GetFileNameWithoutExtension(
      GetFileNameFromPath(process_path));
  if (!VerifyProcessFileName(window.process_file_name))
    return TRUE;

  auto& enum_windows_proc = *reinterpret_cast<enum_windows_proc_t*>(param);
  if (!enum_windows_proc(window))
    return FALSE;

  return TRUE;
}

bool EnumerateWindows(enum_windows_proc_t enum_windows_proc) {
  if (!enum_windows_proc)
    return false;

  const auto param = reinterpret_cast<LPARAM>(&enum_windows_proc);

  // Note that EnumWindows enumerates only top-level windows of desktop apps
  // (as opposed to UWP apps) on Windows 8 and above.
  return ::EnumWindows(EnumWindowsProc, param) != FALSE;
}

}  // namespace win
}  // namespace anisthesia
<commit_msg>Ignore invisible windows<commit_after>/*
MIT License

Copyright (c) 2017 Eren Okka

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 <set>
#include <string>

#include <windows.h>

#include "window_title.h"

#include "../util.h"

namespace anisthesia {
namespace win {

std::wstring GetWindowClassName(HWND hwnd) {
  // The maximum size for lpszClassName, according to the documentation of
  // WNDCLASSEX structure
  constexpr int kMaxSize = 256;

  WCHAR buffer[kMaxSize];
  const auto size = ::GetClassName(hwnd, buffer, kMaxSize);
  return std::wstring(buffer, size);
}

std::wstring GetWindowText(HWND hwnd) {
  // We could learn the actual size with GetWindowTextLength, but this arbitrary
  // value suffices for our purpose.
  constexpr int kMaxSize = 1024;

  WCHAR buffer[kMaxSize];
  const auto size = ::GetWindowText(hwnd, buffer, kMaxSize);
  return std::wstring(buffer, size);
}

DWORD GetWindowProcessId(HWND hwnd) {
  DWORD process_id = 0;
  ::GetWindowThreadProcessId(hwnd, &process_id);
  return process_id;
}

std::wstring GetProcessPath(DWORD process_id) {
  // If we try to open a SYSTEM process, this function fails and the last error
  // code is ERROR_ACCESS_DENIED.
  //
  // Note that if we requested PROCESS_QUERY_INFORMATION access right instead
  // of PROCESS_QUERY_LIMITED_INFORMATION, this function would fail when used
  // to open an elevated process.
  Handle process_handle(::OpenProcess(
      PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id));

  if (!process_handle)
    return std::wstring();

  WCHAR buffer[MAX_PATH];
  DWORD buffer_size = MAX_PATH;

  // Note that this function requires Windows Vista or above. You may use
  // GetProcessImageFileName or GetModuleFileNameEx on earlier versions.
  if (!::QueryFullProcessImageName(process_handle.get(), 0,
                                   buffer, &buffer_size)) {
    return std::wstring();
  }

  return std::wstring(buffer, buffer_size);
}

////////////////////////////////////////////////////////////////////////////////

bool VerifyWindowStyle(HWND hwnd) {
  const auto window_ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE);

  auto has_ex_style = [&window_ex_style](DWORD ex_style) {
    return (window_ex_style & ex_style) != 0;
  };

  // Toolbars, tooltips and similar topmost windows
  if (has_ex_style(WS_EX_TOPMOST) && has_ex_style(WS_EX_TOOLWINDOW))
    return false;

  return true;
}

bool VerifyClassName(const std::wstring& name) {
  static const std::set<std::wstring> invalid_names = {
    // System classes
    L"#32770",         // Dialog box
    L"CabinetWClass",  // Windows Explorer
    L"ComboLBox",
    L"DDEMLEvent",
    L"DDEMLMom",
    L"DirectUIHWND",
    L"GDI+ Hook Window Class",
    L"IME",
    L"Internet Explorer_Hidden",
    L"MSCTFIME UI",
    L"tooltips_class32",
  };

  return !name.empty() && !invalid_names.count(name);
}

bool VerifyProcessPath(const std::wstring& path) {
  return !path.empty() && !IsSystemDirectory(path);
}

bool VerifyProcessFileName(const std::wstring& name) {
  static const std::set<std::wstring> invalid_names = {
    // System files
    L"explorer",    // Windows Explorer
    L"taskeng",     // Task Scheduler Engine
    L"taskhost",    // Host Process for Windows Tasks
    L"taskhostex",  // Host Process for Windows Tasks
    L"Taskmgr",     // Task Manager
  };

  return !name.empty() && !invalid_names.count(name);
}

////////////////////////////////////////////////////////////////////////////////

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM param) {
  if (!::IsWindowVisible(hwnd))
    return TRUE;

  if (!VerifyWindowStyle(hwnd))
    return TRUE;

  Window window;
  window.handle = hwnd;
  window.process_id = GetWindowProcessId(hwnd);
  window.text = GetWindowText(hwnd);

  window.class_name = GetWindowClassName(hwnd);
  if (!VerifyClassName(window.class_name))
    return TRUE;

  const auto process_path = GetProcessPath(window.process_id);
  if (!VerifyProcessPath(process_path))
    return TRUE;

  window.process_file_name = GetFileNameWithoutExtension(
      GetFileNameFromPath(process_path));
  if (!VerifyProcessFileName(window.process_file_name))
    return TRUE;

  auto& enum_windows_proc = *reinterpret_cast<enum_windows_proc_t*>(param);
  if (!enum_windows_proc(window))
    return FALSE;

  return TRUE;
}

bool EnumerateWindows(enum_windows_proc_t enum_windows_proc) {
  if (!enum_windows_proc)
    return false;

  const auto param = reinterpret_cast<LPARAM>(&enum_windows_proc);

  // Note that EnumWindows enumerates only top-level windows of desktop apps
  // (as opposed to UWP apps) on Windows 8 and above.
  return ::EnumWindows(EnumWindowsProc, param) != FALSE;
}

}  // namespace win
}  // namespace anisthesia
<|endoftext|>


<commit_before>// sqlite3pp.h
//
// The MIT License
//
// Copyright (c) 2012 Wongoo Lee (iwongu at gmail dot com)
//
// 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.
#pragma once

#include <string>
#include <stdexcept>
#include <sqlite3.h>
#include <boost/utility.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/function.hpp>
#include <cstdint>

namespace sqlite3pp
{
    namespace ext
    {
        class function;
        class aggregate;
    }

    template <class T>
    class nullable_wrapper
    {
        mutable T& val_;
        const T& null_value_;
    public:
        explicit nullable_wrapper(T& val, const T& null_value) : val_(val), null_value_(null_value) {}
        void set(const T& val) const { val_ = val; }
        const T& null_value() const { return null_value_; }
    };

    template <class T>
    inline nullable_wrapper<T> nullable(T& val, const T& null_value) { return nullable_wrapper<T>(val, null_value); }

    int enable_shared_cache(bool fenable);

    class database
    {
        friend class statement;
        friend class database_error;
        friend class ext::function;
        friend class ext::aggregate;

     public:
        typedef boost::function<int (int)> busy_handler;
        typedef boost::function<int ()> commit_handler;
        typedef boost::function<void ()> rollback_handler;
        typedef boost::function<void (int, char const*, char const*, long long int)> update_handler;
        typedef boost::function<int (int, char const*, char const*, char const*, char const*)> authorize_handler;

        explicit database(char const* dbname = nullptr);
        database(const database&) = delete;
        database& operator=(const database&) = delete;
        database(database&&);
        database& operator=(database&&);
        ~database();

        int connect(char const* dbname);
        int connect_v2(char const* dbname, int flags, char const* vfs = nullptr);
        int disconnect();

        int attach(char const* dbname, char const* name);
        int detach(char const* name);

        int64_t last_insert_rowid() const;
        int changes()
        {
            return sqlite3_changes(db_);
        }

        int error_code() const;
        char const* error_msg() const;

        void execute(char const* sql);
        int eexecute(char const* sql);
        int executef(char const* sql, ...);

        int set_busy_timeout(int ms);

        void set_busy_handler(busy_handler h);
        void set_commit_handler(commit_handler h);
        void set_rollback_handler(rollback_handler h);
        void set_update_handler(update_handler h);
        void set_authorize_handler(authorize_handler h);

     private:
        sqlite3* db_;

        busy_handler bh_;
        commit_handler ch_;
        rollback_handler rh_;
        update_handler uh_;
        authorize_handler ah_;
    };

    class database_error : public std::runtime_error
    {
     public:
        explicit database_error(char const* msg);
        explicit database_error(database& db);
    };

    class statement
    {
     public:
        void prepare(char const* stmt);
        int eprepare(char const* stmt);

        // finish statement, @throws database_error
        void finish();
        // finish statements, @returns error code
        int efinish();

        statement& bind(int idx, int32_t value);
        statement& bind(int idx, uint32_t value);
        statement& bind(int idx, double value);
        statement& bind(int idx, int64_t value);
        statement& bind(int idx, uint64_t value);
        statement& bind(int idx, const std::string&, bool blob = false, bool fstatic = true);
        statement& bind(int idx, char const* value, bool fstatic = true);
        statement& bind(int idx, void const* value, int n, bool fstatic = true);
        statement& bind(int idx);
        statement& bind(int idx, std::nullptr_t);

        statement& bind(char const* name, int value);
        statement& bind(char const* name, double value);
        statement& bind(char const* name, int64_t value);
        statement& bind(char const* name, uint64_t value);
        statement& bind(char const* name, const std::string&, bool blob = false, bool fstatic = true);
        statement& bind(char const* name, char const* value, bool fstatic = true);
        statement& bind(char const* name, void const* value, int n, bool fstatic = true);
        statement& bind(char const* name);
        statement& bind(char const* name, std::nullptr_t);

        int step();

        /// reset a prepared statement ready to be re-executed, doesn't reset bindings
        statement& reset();

        statement(statement&&);

     private:
        statement(const statement&) = delete;
        statement& operator=(const statement&) = delete;
        statement& operator=(statement&&) = delete;

     protected:
        statement(database& db, char const* stmt = nullptr);
        ~statement();

        int prepare_impl(char const* stmt);
        int finish_impl(sqlite3_stmt* stmt);

     protected:
        database& db_;
        sqlite3_stmt* stmt_;
        std::string statement_;
        char const* tail_;
    };

    class command : public statement
    {
     public:
        class bindstream
        {
         public:
            bindstream(command& cmd, int idx);

            template <class T>
            bindstream& operator << (T value) {
                int rc = cmd_.bind(idx_, value);
                if (rc != SQLITE_OK) {
                    throw database_error(cmd_.db_);
                }
                ++idx_;
                return *this;
            }

         private:
            command& cmd_;
            int idx_;
        };

        explicit command(database& db, char const* stmt = nullptr);

        bindstream binder(int idx = 1);

        /// @throws database_error if execute fails
        void execute();
        int eexecute();
        int execute_all();
    };

    class query : public statement
    {
     public:
        class rows
        {
         public:
            class getstream
            {
             public:
                getstream(rows* rws, int idx);

                template <class T>
                getstream& operator >> (T& value) {
                    value = rws_->get(idx_, T());
                    ++idx_;
                    return *this;
                }

                template <class T>
                getstream& operator >> (const nullable_wrapper<T>& nullable_value) {
                    nullable_value.set(rws_->get_nullable<T>(idx_, nullable_value.null_value()));
                    ++idx_;
                    return *this;
                }

             private:
                rows* rws_;
                int idx_;
            };

            explicit rows(sqlite3_stmt* stmt);

            int data_count() const;
            int column_type(int idx) const;
            int column_count() const;

            int column_bytes(int idx) const;

            template <class T> T get_nullable(int idx, const T& null_value) const
            {
                if (column_type(idx) == SQLITE_NULL) {
                    return null_value;
                }
                return get<T>(idx);
            }

            template <class T> T get(int idx) const {
                return get(idx, T());
            }

            template <class T1>
            boost::tuple<T1> get_columns(int idx1) const {
                return boost::make_tuple(get(idx1, T1()));
            }

            template <class T1, class T2>
            boost::tuple<T1, T2> get_columns(int idx1, int idx2) const {
                return boost::make_tuple(get(idx1, T1()), get(idx2, T2()));
            }

            template <class T1, class T2, class T3>
            boost::tuple<T1, T2, T3> get_columns(int idx1, int idx2, int idx3) const {
                return boost::make_tuple(get(idx1, T1()), get(idx2, T2()), get(idx3, T3()));
            }

            template <class T1, class T2, class T3, class T4>
            boost::tuple<T1, T2, T3, T4> get_columns(int idx1, int idx2, int idx3, int idx4) const {
                return boost::make_tuple(get(idx1, T1()), get(idx2, T2()), get(idx3, T3()), get(idx4, T4()));
            }

            template <class T1, class T2, class T3, class T4, class T5>
            boost::tuple<T1, T2, T3, T4, T5> get_columns(int idx1, int idx2, int idx3, int idx4, int idx5) const {
                return boost::make_tuple(get(idx1, T1()), get(idx2, T2()), get(idx3, T3()), get(idx4, T4()), get(idx5, T5()));
            }

            template <class T1, class T2, class T3, class T4, class T5, class T6>
            boost::tuple<T1, T2, T3, T4, T5, T6> get_columns(int idx1, int idx2, int idx3, int idx4, int idx5, int idx6) const {
                return boost::make_tuple(get(idx1, T1()), get(idx2, T2()), get(idx3, T3()), get(idx4, T4()), get(idx5, T5()), get(idx6, T6()));
            }

            template <class T1, class T2, class T3, class T4, class T5, class T6, class T7>
            boost::tuple<T1, T2, T3, T4, T5, T6, T7> get_columns(int idx1, int idx2, int idx3, int idx4, int idx5, int idx6, int idx7) const {
                return boost::make_tuple(get(idx1, T1()), get(idx2, T2()), get(idx3, T3()), get(idx4, T4()), get(idx5, T5()), get(idx6, T6()), get(idx7, T7()));
            }

            template <class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
            boost::tuple<T1, T2, T3, T4, T5, T6, T7, T8> get_columns(int idx1, int idx2, int idx3, int idx4, int idx5, int idx6, int idx7, int idx8) const {
                return boost::make_tuple(get(idx1, T1()), get(idx2, T2()), get(idx3, T3()), get(idx4, T4()), get(idx5, T5()), get(idx6, T6()), get(idx7, T7()), get(idx8, T8()));
            }

            getstream getter(int idx = 0);

         private:
            bool get(int idx, bool) const;
            double get(int idx, double) const;
            int32_t get(int idx, int32_t) const;
            uint32_t get(int idx, uint32_t) const;
            int64_t get(int idx, int64_t) const;
            uint64_t get(int idx, uint64_t) const;
            char const* get(int idx, char const*) const;
            std::string get(int idx, std::string) const;
            void const* get(int idx, void const*) const;
            std::nullptr_t get(int idx, std::nullptr_t) const;

         private:
            sqlite3_stmt* stmt_;
        };

        class query_iterator
	: public boost::iterator_facade<query_iterator, rows, boost::single_pass_traversal_tag, rows>
        {
         public:
            query_iterator();
            explicit query_iterator(query* cmd);
            void set_query(query* cmd);

         private:
            friend class boost::iterator_core_access;

            void increment();
            bool equal(query_iterator const& other) const;

            rows dereference() const;

            query* cmd_;
            int rc_;
        };

        explicit query(database& db, char const* stmt = nullptr);

        int column_count() const;

        char const* column_name(int idx) const;
        char const* column_decltype(int idx) const;

        typedef query_iterator iterator;

        rows fetchone();

        iterator begin();
        iterator end();
    };

    class transaction : boost::noncopyable
    {
     public:
        explicit transaction(database& db, bool fcommit = false, bool freserve = false);
        ~transaction();

        int commit();
        int rollback();

     private:
        database* db_;
        bool fcommit_;
    };

} // namespace sqlite3pp

<commit_msg>remove mutable<commit_after>// sqlite3pp.h
//
// The MIT License
//
// Copyright (c) 2012 Wongoo Lee (iwongu at gmail dot com)
//
// 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.
#pragma once

#include <string>
#include <stdexcept>
#include <sqlite3.h>
#include <boost/utility.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/function.hpp>
#include <cstdint>

namespace sqlite3pp
{
    namespace ext
    {
        class function;
        class aggregate;
    }

    template <class T>
    class nullable_wrapper
    {
        T& val_;
        const T& null_value_;
    public:
        explicit nullable_wrapper(T& val, const T& null_value) : val_(val), null_value_(null_value) {}
        void set(const T& val) const { val_ = val; }
        const T& null_value() const { return null_value_; }
    };

    template <class T>
    inline nullable_wrapper<T> nullable(T& val, const T& null_value) { return nullable_wrapper<T>(val, null_value); }

    int enable_shared_cache(bool fenable);

    class database
    {
        friend class statement;
        friend class database_error;
        friend class ext::function;
        friend class ext::aggregate;

     public:
        typedef boost::function<int (int)> busy_handler;
        typedef boost::function<int ()> commit_handler;
        typedef boost::function<void ()> rollback_handler;
        typedef boost::function<void (int, char const*, char const*, long long int)> update_handler;
        typedef boost::function<int (int, char const*, char const*, char const*, char const*)> authorize_handler;

        explicit database(char const* dbname = nullptr);
        database(const database&) = delete;
        database& operator=(const database&) = delete;
        database(database&&);
        database& operator=(database&&);
        ~database();

        int connect(char const* dbname);
        int connect_v2(char const* dbname, int flags, char const* vfs = nullptr);
        int disconnect();

        int attach(char const* dbname, char const* name);
        int detach(char const* name);

        int64_t last_insert_rowid() const;
        int changes()
        {
            return sqlite3_changes(db_);
        }

        int error_code() const;
        char const* error_msg() const;

        void execute(char const* sql);
        int eexecute(char const* sql);
        int executef(char const* sql, ...);

        int set_busy_timeout(int ms);

        void set_busy_handler(busy_handler h);
        void set_commit_handler(commit_handler h);
        void set_rollback_handler(rollback_handler h);
        void set_update_handler(update_handler h);
        void set_authorize_handler(authorize_handler h);

     private:
        sqlite3* db_;

        busy_handler bh_;
        commit_handler ch_;
        rollback_handler rh_;
        update_handler uh_;
        authorize_handler ah_;
    };

    class database_error : public std::runtime_error
    {
     public:
        explicit database_error(char const* msg);
        explicit database_error(database& db);
    };

    class statement
    {
     public:
        void prepare(char const* stmt);
        int eprepare(char const* stmt);

        // finish statement, @throws database_error
        void finish();
        // finish statements, @returns error code
        int efinish();

        statement& bind(int idx, int32_t value);
        statement& bind(int idx, uint32_t value);
        statement& bind(int idx, double value);
        statement& bind(int idx, int64_t value);
        statement& bind(int idx, uint64_t value);
        statement& bind(int idx, const std::string&, bool blob = false, bool fstatic = true);
        statement& bind(int idx, char const* value, bool fstatic = true);
        statement& bind(int idx, void const* value, int n, bool fstatic = true);
        statement& bind(int idx);
        statement& bind(int idx, std::nullptr_t);

        statement& bind(char const* name, int value);
        statement& bind(char const* name, double value);
        statement& bind(char const* name, int64_t value);
        statement& bind(char const* name, uint64_t value);
        statement& bind(char const* name, const std::string&, bool blob = false, bool fstatic = true);
        statement& bind(char const* name, char const* value, bool fstatic = true);
        statement& bind(char const* name, void const* value, int n, bool fstatic = true);
        statement& bind(char const* name);
        statement& bind(char const* name, std::nullptr_t);

        int step();

        /// reset a prepared statement ready to be re-executed, doesn't reset bindings
        statement& reset();

        statement(statement&&);

     private:
        statement(const statement&) = delete;
        statement& operator=(const statement&) = delete;
        statement& operator=(statement&&) = delete;

     protected:
        statement(database& db, char const* stmt = nullptr);
        ~statement();

        int prepare_impl(char const* stmt);
        int finish_impl(sqlite3_stmt* stmt);

     protected:
        database& db_;
        sqlite3_stmt* stmt_;
        std::string statement_;
        char const* tail_;
    };

    class command : public statement
    {
     public:
        class bindstream
        {
         public:
            bindstream(command& cmd, int idx);

            template <class T>
            bindstream& operator << (T value) {
                int rc = cmd_.bind(idx_, value);
                if (rc != SQLITE_OK) {
                    throw database_error(cmd_.db_);
                }
                ++idx_;
                return *this;
            }

         private:
            command& cmd_;
            int idx_;
        };

        explicit command(database& db, char const* stmt = nullptr);

        bindstream binder(int idx = 1);

        /// @throws database_error if execute fails
        void execute();
        int eexecute();
        int execute_all();
    };

    class query : public statement
    {
     public:
        class rows
        {
         public:
            class getstream
            {
             public:
                getstream(rows* rws, int idx);

                template <class T>
                getstream& operator >> (T& value) {
                    value = rws_->get(idx_, T());
                    ++idx_;
                    return *this;
                }

                template <class T>
                getstream& operator >> (const nullable_wrapper<T>& nullable_value) {
                    nullable_value.set(rws_->get_nullable<T>(idx_, nullable_value.null_value()));
                    ++idx_;
                    return *this;
                }

             private:
                rows* rws_;
                int idx_;
            };

            explicit rows(sqlite3_stmt* stmt);

            int data_count() const;
            int column_type(int idx) const;
            int column_count() const;

            int column_bytes(int idx) const;

            template <class T> T get_nullable(int idx, const T& null_value) const
            {
                if (column_type(idx) == SQLITE_NULL) {
                    return null_value;
                }
                return get<T>(idx);
            }

            template <class T> T get(int idx) const {
                return get(idx, T());
            }

            template <class T1>
            boost::tuple<T1> get_columns(int idx1) const {
                return boost::make_tuple(get(idx1, T1()));
            }

            template <class T1, class T2>
            boost::tuple<T1, T2> get_columns(int idx1, int idx2) const {
                return boost::make_tuple(get(idx1, T1()), get(idx2, T2()));
            }

            template <class T1, class T2, class T3>
            boost::tuple<T1, T2, T3> get_columns(int idx1, int idx2, int idx3) const {
                return boost::make_tuple(get(idx1, T1()), get(idx2, T2()), get(idx3, T3()));
            }

            template <class T1, class T2, class T3, class T4>
            boost::tuple<T1, T2, T3, T4> get_columns(int idx1, int idx2, int idx3, int idx4) const {
                return boost::make_tuple(get(idx1, T1()), get(idx2, T2()), get(idx3, T3()), get(idx4, T4()));
            }

            template <class T1, class T2, class T3, class T4, class T5>
            boost::tuple<T1, T2, T3, T4, T5> get_columns(int idx1, int idx2, int idx3, int idx4, int idx5) const {
                return boost::make_tuple(get(idx1, T1()), get(idx2, T2()), get(idx3, T3()), get(idx4, T4()), get(idx5, T5()));
            }

            template <class T1, class T2, class T3, class T4, class T5, class T6>
            boost::tuple<T1, T2, T3, T4, T5, T6> get_columns(int idx1, int idx2, int idx3, int idx4, int idx5, int idx6) const {
                return boost::make_tuple(get(idx1, T1()), get(idx2, T2()), get(idx3, T3()), get(idx4, T4()), get(idx5, T5()), get(idx6, T6()));
            }

            template <class T1, class T2, class T3, class T4, class T5, class T6, class T7>
            boost::tuple<T1, T2, T3, T4, T5, T6, T7> get_columns(int idx1, int idx2, int idx3, int idx4, int idx5, int idx6, int idx7) const {
                return boost::make_tuple(get(idx1, T1()), get(idx2, T2()), get(idx3, T3()), get(idx4, T4()), get(idx5, T5()), get(idx6, T6()), get(idx7, T7()));
            }

            template <class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
            boost::tuple<T1, T2, T3, T4, T5, T6, T7, T8> get_columns(int idx1, int idx2, int idx3, int idx4, int idx5, int idx6, int idx7, int idx8) const {
                return boost::make_tuple(get(idx1, T1()), get(idx2, T2()), get(idx3, T3()), get(idx4, T4()), get(idx5, T5()), get(idx6, T6()), get(idx7, T7()), get(idx8, T8()));
            }

            getstream getter(int idx = 0);

         private:
            bool get(int idx, bool) const;
            double get(int idx, double) const;
            int32_t get(int idx, int32_t) const;
            uint32_t get(int idx, uint32_t) const;
            int64_t get(int idx, int64_t) const;
            uint64_t get(int idx, uint64_t) const;
            char const* get(int idx, char const*) const;
            std::string get(int idx, std::string) const;
            void const* get(int idx, void const*) const;
            std::nullptr_t get(int idx, std::nullptr_t) const;

         private:
            sqlite3_stmt* stmt_;
        };

        class query_iterator
	: public boost::iterator_facade<query_iterator, rows, boost::single_pass_traversal_tag, rows>
        {
         public:
            query_iterator();
            explicit query_iterator(query* cmd);
            void set_query(query* cmd);

         private:
            friend class boost::iterator_core_access;

            void increment();
            bool equal(query_iterator const& other) const;

            rows dereference() const;

            query* cmd_;
            int rc_;
        };

        explicit query(database& db, char const* stmt = nullptr);

        int column_count() const;

        char const* column_name(int idx) const;
        char const* column_decltype(int idx) const;

        typedef query_iterator iterator;

        rows fetchone();

        iterator begin();
        iterator end();
    };

    class transaction : boost::noncopyable
    {
     public:
        explicit transaction(database& db, bool fcommit = false, bool freserve = false);
        ~transaction();

        int commit();
        int rollback();

     private:
        database* db_;
        bool fcommit_;
    };

} // namespace sqlite3pp

<|endoftext|>