started to devide contact and MUC, license, readme, attach icon

This commit is contained in:
Blue 2019-08-14 17:54:46 +03:00
parent eda96e138d
commit c8a33710e6
39 changed files with 1501 additions and 373 deletions

View file

@ -12,6 +12,7 @@ set(squawkCORE_SRC
squawk.cpp
account.cpp
archive.cpp
rosteritem.cpp
contact.cpp
)

View file

@ -1,3 +1,21 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "account.h"
#include <QXmppMessage.h>
#include <QDateTime>
@ -555,9 +573,9 @@ void Core::Account::requestArchive(const QString& jid, int count, const QString&
qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the contact with such id wasn't found, skipping";
return;
}
Contact* contact = itr->second;
RosterItem* contact = itr->second;
if (contact->getArchiveState() == Contact::empty && before.size() == 0) {
if (contact->getArchiveState() == RosterItem::empty && before.size() == 0) {
QXmppMessage msg(getFullJid(), jid, "", "");
QString last = Shared::generateUUID();
msg.setId(last);
@ -579,7 +597,7 @@ void Core::Account::requestArchive(const QString& jid, int count, const QString&
void Core::Account::onContactNeedHistory(const QString& before, const QString& after, const QDateTime& at)
{
Contact* contact = static_cast<Contact*>(sender());
RosterItem* contact = static_cast<RosterItem*>(sender());
QXmppResultSetQuery query;
query.setMax(100);
if (before.size() > 0) {
@ -708,7 +726,7 @@ Shared::SubscriptionState Core::Account::castSubscriptionState(QXmppRosterIq::It
void Core::Account::onContactHistoryResponse(const std::list<Shared::Message>& list)
{
Contact* contact = static_cast<Contact*>(sender());
RosterItem* contact = static_cast<RosterItem*>(sender());
qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements";
emit responseArchive(contact->jid, list);

View file

@ -1,3 +1,21 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CORE_ACCOUNT_H
#define CORE_ACCOUNT_H

View file

@ -1,5 +1,5 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify

View file

@ -1,5 +1,5 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify

View file

@ -1,5 +1,5 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
@ -20,39 +20,13 @@
#include <QDebug>
Core::Contact::Contact(const QString& pJid, const QString& account, QObject* parent):
QObject(parent),
jid(pJid),
name(),
groups(),
archiveState(empty),
archive(new Archive(jid)),
subscriptionState(Shared::unknown),
syncronizing(false),
requestedCount(0),
requestedBefore(),
hisoryCache(),
appendCache(),
responseCache(),
requestCache()
RosterItem(pJid, account, parent),
groups()
{
archive->open(account);
if (archive->isFromTheBeginning()) {
archiveState = beginning;
} else {
if (archive->size() != 0) {
archiveState = chunk;
}
}
}
Core::Contact::~Contact()
{
delete archive;
}
Core::Contact::ArchiveState Core::Contact::getArchiveState() const
{
return archiveState;
}
QSet<QString> Core::Contact::getGroups() const
@ -60,22 +34,9 @@ QSet<QString> Core::Contact::getGroups() const
return groups;
}
QString Core::Contact::getName() const
unsigned int Core::Contact::groupsCount() const
{
return name;
}
void Core::Contact::setName(const QString& n)
{
if (name != n) {
name = n;
emit nameChanged(name);
}
}
Shared::SubscriptionState Core::Contact::getSubscriptionState() const
{
return subscriptionState;
return groups.size();
}
void Core::Contact::setGroups(const QSet<QString>& set)
@ -93,267 +54,3 @@ void Core::Contact::setGroups(const QSet<QString>& set)
emit groupAdded(*itr);
}
}
void Core::Contact::setSubscriptionState(Shared::SubscriptionState state)
{
if (subscriptionState != state) {
subscriptionState = state;
emit subscriptionStateChanged(subscriptionState);
}
}
unsigned int Core::Contact::groupsCount() const
{
return groups.size();
}
void Core::Contact::addMessageToArchive(const Shared::Message& msg)
{
if (msg.getId().size() > 0 && msg.getBody().size() > 0) {
hisoryCache.push_back(msg);
}
}
void Core::Contact::requestHistory(int count, const QString& before)
{
if (syncronizing) {
requestCache.emplace_back(count, before);
} else {
performRequest(count, before);
}
}
void Core::Contact::nextRequest()
{
if (syncronizing) {
if (requestedCount != -1) {
emit historyResponse(responseCache);
}
}
if (requestCache.size() > 0) {
std::pair<int, QString> request = requestCache.front();
requestCache.pop_front();
performRequest(request.first, request.second);
} else {
syncronizing = false;
requestedCount = 0;
requestedBefore = "";
hisoryCache.clear();
responseCache.clear();
}
}
void Core::Contact::performRequest(int count, const QString& before)
{
syncronizing = true;
requestedCount = count;
requestedBefore = before;
hisoryCache.clear();
responseCache.clear();
switch (archiveState) {
case empty:
emit needHistory(before, "");
break;
case chunk:
case beginning: {
if (count != -1) {
requestCache.emplace_back(requestedCount, before);
requestedCount = -1;
}
Shared::Message msg = archive->newest();
emit needHistory("", msg.getId(), msg.getTime());
}
break;
case end:
if (count != -1) {
QString lBefore;
if (responseCache.size() > 0) {
lBefore = responseCache.front().getId();
} else {
lBefore = before;
}
bool found = false;
try {
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), lBefore);
responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
found = true;
} catch (Archive::NotFound e) {
requestCache.emplace_back(requestedCount, before);
requestedCount = -1;
emit needHistory(archive->oldestId(), "");
} catch (Archive::Empty e) {
requestCache.emplace_back(requestedCount, before);
requestedCount = -1;
emit needHistory(archive->oldestId(), "");
}
if (found) {
int rSize = responseCache.size();
if (rSize < count) {
if (rSize != 0) {
emit needHistory(responseCache.front().getId(), "");
} else {
emit needHistory(before, "");
}
} else {
nextRequest();
}
}
} else {
emit needHistory(archive->oldestId(), "");
}
break;
case complete:
try {
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before);
responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
} catch (Archive::NotFound e) {
qDebug("requesting id hasn't been found in archive, skipping");
} catch (Archive::Empty e) {
qDebug("requesting id hasn't been found in archive, skipping");
}
nextRequest();
break;
}
}
void Core::Contact::appendMessageToArchive(const Shared::Message& msg)
{
const QString& id = msg.getId();
if (id.size() > 0) {
if (msg.getBody().size() > 0) {
switch (archiveState) {
case empty:
if (archive->addElement(msg)) {
archiveState = end;
}
if (!syncronizing) {
requestHistory(-1, id);
}
break;
case beginning:
appendCache.push_back(msg);
if (!syncronizing) {
requestHistory(-1, id);
}
break;
case end:
archive->addElement(msg);
break;
case chunk:
appendCache.push_back(msg);
if (!syncronizing) {
requestHistory(-1, id);
}
break;
case complete:
archive->addElement(msg);
break;
}
} else if (!syncronizing && archiveState == empty) {
requestHistory(-1, id);
}
}
}
void Core::Contact::flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId)
{
unsigned int added(0);
if (hisoryCache.size() > 0) {
added = archive->addElements(hisoryCache);
qDebug() << "Added" << added << "messages to the archive";
hisoryCache.clear();
}
bool wasEmpty = false;
switch (archiveState) {
case beginning:
if (finished) {
archiveState = complete;
archive->addElements(appendCache);
appendCache.clear();
nextRequest();
} else {
emit needHistory("", lastId);
}
break;
case chunk:
if (finished) {
archiveState = end;
archive->addElements(appendCache);
appendCache.clear();
nextRequest();
} else {
emit needHistory("", lastId);
}
break;
case empty:
wasEmpty = true;
archiveState = end;
case end:
if (finished && (added > 0 || !wasEmpty)) {
archiveState = complete;
archive->setFromTheBeginning(true);
}
if (requestedCount != -1) {
QString before;
if (responseCache.size() > 0) {
before = responseCache.front().getId();
} else {
before = requestedBefore;
}
bool found = false;
try {
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before);
responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
found = true;
} catch (Archive::NotFound e) {
} catch (Archive::Empty e) {
}
if (!found || requestedCount > responseCache.size()) {
if (archiveState == complete) {
nextRequest();
} else {
emit needHistory(firstId, "");
}
} else {
nextRequest();
}
} else {
if (added != 0) {
nextRequest();
} else {
emit needHistory(firstId, "");
}
}
break;
case complete:
nextRequest();
break;
}
}
void Core::Contact::requestFromEmpty(int count, const QString& before)
{
if (syncronizing) {
qDebug("perform from empty didn't work, another request queued");
} else {
if (archiveState != empty) {
qDebug("perform from empty didn't work, the state is not empty");
requestHistory(count, before);
} else {
syncronizing = true;
requestedCount = count;
requestedBefore = "";
hisoryCache.clear();
responseCache.clear();
emit needHistory(before, "");
}
}
}

View file

@ -1,5 +1,5 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
@ -21,72 +21,28 @@
#include <QObject>
#include <QSet>
#include <QString>
#include "archive.h"
#include "../global.h"
#include <list>
#include "rosteritem.h"
namespace Core {
class Contact : public QObject
class Contact : public RosterItem
{
Q_OBJECT
public:
enum ArchiveState {
empty, //have no messages stored for this contact
chunk, //have some chunk of history, don't have the beginning nor have the end
beginning, //have the history from the very beginning, don't have the end
end, //have the history to the end, but don't have the beginning
complete //have full history locally stored
};
Contact(const QString& pJid, const QString& account, QObject* parent = 0);
~Contact();
ArchiveState getArchiveState() const;
QString getName() const;
void setName(const QString& n);
QSet<QString> getGroups() const;
void setGroups(const QSet<QString>& set);
void setSubscriptionState(Shared::SubscriptionState state);
Shared::SubscriptionState getSubscriptionState() const;
unsigned int groupsCount() const;
void addMessageToArchive(const Shared::Message& msg);
void appendMessageToArchive(const Shared::Message& msg);
void flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId);
void requestHistory(int count, const QString& before);
void requestFromEmpty(int count, const QString& before);
signals:
void groupAdded(const QString& name);
void groupRemoved(const QString& name);
void nameChanged(const QString& name);
void subscriptionStateChanged(Shared::SubscriptionState state);
void historyResponse(const std::list<Shared::Message>& messages);
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime());
public:
const QString jid;
private:
QString name;
QSet<QString> groups;
ArchiveState archiveState;
Archive* archive;
Shared::SubscriptionState subscriptionState;
bool syncronizing;
int requestedCount;
QString requestedBefore;
std::list<Shared::Message> hisoryCache;
std::list<Shared::Message> appendCache;
std::list<Shared::Message> responseCache;
std::list<std::pair<int, QString>> requestCache;
private:
void nextRequest();
void performRequest(int count, const QString& before);
};
}
#endif // CORE_CONTACT_H

333
core/rosteritem.cpp Normal file
View file

@ -0,0 +1,333 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rosteritem.h"
#include <QDebug>
Core::RosterItem::RosterItem(const QString& pJid, const QString& account, QObject* parent):
QObject(parent),
jid(pJid),
name(),
archiveState(empty),
archive(new Archive(jid)),
subscriptionState(Shared::unknown),
syncronizing(false),
requestedCount(0),
requestedBefore(),
hisoryCache(),
appendCache(),
responseCache(),
requestCache()
{
archive->open(account);
if (archive->isFromTheBeginning()) {
archiveState = beginning;
} else {
if (archive->size() != 0) {
archiveState = chunk;
}
}
}
Core::RosterItem::~RosterItem()
{
delete archive;
}
Core::RosterItem::ArchiveState Core::RosterItem::getArchiveState() const
{
return archiveState;
}
Shared::SubscriptionState Core::RosterItem::getSubscriptionState() const
{
return subscriptionState;
}
QString Core::RosterItem::getName() const
{
return name;
}
void Core::RosterItem::setSubscriptionState(Shared::SubscriptionState state)
{
if (subscriptionState != state) {
subscriptionState = state;
emit subscriptionStateChanged(subscriptionState);
}
}
void Core::RosterItem::setName(const QString& n)
{
if (name != n) {
name = n;
emit nameChanged(name);
}
}
void Core::RosterItem::addMessageToArchive(const Shared::Message& msg)
{
if (msg.getId().size() > 0 && msg.getBody().size() > 0) {
hisoryCache.push_back(msg);
}
}
void Core::RosterItem::requestHistory(int count, const QString& before)
{
if (syncronizing) {
requestCache.emplace_back(count, before);
} else {
performRequest(count, before);
}
}
void Core::RosterItem::nextRequest()
{
if (syncronizing) {
if (requestedCount != -1) {
emit historyResponse(responseCache);
}
}
if (requestCache.size() > 0) {
std::pair<int, QString> request = requestCache.front();
requestCache.pop_front();
performRequest(request.first, request.second);
} else {
syncronizing = false;
requestedCount = 0;
requestedBefore = "";
hisoryCache.clear();
responseCache.clear();
}
}
void Core::RosterItem::performRequest(int count, const QString& before)
{
syncronizing = true;
requestedCount = count;
requestedBefore = before;
hisoryCache.clear();
responseCache.clear();
switch (archiveState) {
case empty:
emit needHistory(before, "");
break;
case chunk:
case beginning: {
if (count != -1) {
requestCache.emplace_back(requestedCount, before);
requestedCount = -1;
}
Shared::Message msg = archive->newest();
emit needHistory("", msg.getId(), msg.getTime());
}
break;
case end:
if (count != -1) {
QString lBefore;
if (responseCache.size() > 0) {
lBefore = responseCache.front().getId();
} else {
lBefore = before;
}
bool found = false;
try {
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), lBefore);
responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
found = true;
} catch (Archive::NotFound e) {
requestCache.emplace_back(requestedCount, before);
requestedCount = -1;
emit needHistory(archive->oldestId(), "");
} catch (Archive::Empty e) {
requestCache.emplace_back(requestedCount, before);
requestedCount = -1;
emit needHistory(archive->oldestId(), "");
}
if (found) {
int rSize = responseCache.size();
if (rSize < count) {
if (rSize != 0) {
emit needHistory(responseCache.front().getId(), "");
} else {
emit needHistory(before, "");
}
} else {
nextRequest();
}
}
} else {
emit needHistory(archive->oldestId(), "");
}
break;
case complete:
try {
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before);
responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
} catch (Archive::NotFound e) {
qDebug("requesting id hasn't been found in archive, skipping");
} catch (Archive::Empty e) {
qDebug("requesting id hasn't been found in archive, skipping");
}
nextRequest();
break;
}
}
void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg)
{
const QString& id = msg.getId();
if (id.size() > 0) {
if (msg.getBody().size() > 0) {
switch (archiveState) {
case empty:
if (archive->addElement(msg)) {
archiveState = end;
}
if (!syncronizing) {
requestHistory(-1, id);
}
break;
case beginning:
appendCache.push_back(msg);
if (!syncronizing) {
requestHistory(-1, id);
}
break;
case end:
archive->addElement(msg);
break;
case chunk:
appendCache.push_back(msg);
if (!syncronizing) {
requestHistory(-1, id);
}
break;
case complete:
archive->addElement(msg);
break;
}
} else if (!syncronizing && archiveState == empty) {
requestHistory(-1, id);
}
}
}
void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId)
{
unsigned int added(0);
if (hisoryCache.size() > 0) {
added = archive->addElements(hisoryCache);
qDebug() << "Added" << added << "messages to the archive";
hisoryCache.clear();
}
bool wasEmpty = false;
switch (archiveState) {
case beginning:
if (finished) {
archiveState = complete;
archive->addElements(appendCache);
appendCache.clear();
nextRequest();
} else {
emit needHistory("", lastId);
}
break;
case chunk:
if (finished) {
archiveState = end;
archive->addElements(appendCache);
appendCache.clear();
nextRequest();
} else {
emit needHistory("", lastId);
}
break;
case empty:
wasEmpty = true;
archiveState = end;
case end:
if (finished && (added > 0 || !wasEmpty)) {
archiveState = complete;
archive->setFromTheBeginning(true);
}
if (requestedCount != -1) {
QString before;
if (responseCache.size() > 0) {
before = responseCache.front().getId();
} else {
before = requestedBefore;
}
bool found = false;
try {
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before);
responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
found = true;
} catch (Archive::NotFound e) {
} catch (Archive::Empty e) {
}
if (!found || requestedCount > responseCache.size()) {
if (archiveState == complete) {
nextRequest();
} else {
emit needHistory(firstId, "");
}
} else {
nextRequest();
}
} else {
if (added != 0) {
nextRequest();
} else {
emit needHistory(firstId, "");
}
}
break;
case complete:
nextRequest();
break;
}
}
void Core::RosterItem::requestFromEmpty(int count, const QString& before)
{
if (syncronizing) {
qDebug("perform from empty didn't work, another request queued");
} else {
if (archiveState != empty) {
qDebug("perform from empty didn't work, the state is not empty");
requestHistory(count, before);
} else {
syncronizing = true;
requestedCount = count;
requestedBefore = "";
hisoryCache.clear();
responseCache.clear();
emit needHistory(before, "");
}
}
}

92
core/rosteritem.h Normal file
View file

@ -0,0 +1,92 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CORE_ROSTERITEM_H
#define CORE_ROSTERITEM_H
#include <QObject>
#include <QString>
#include <list>
#include "../global.h"
#include "archive.h"
namespace Core {
/**
* @todo write docs
*/
class RosterItem : public QObject
{
Q_OBJECT
public:
enum ArchiveState {
empty, //have no messages stored for this contact
chunk, //have some chunk of history, don't have the beginning nor have the end
beginning, //have the history from the very beginning, don't have the end
end, //have the history to the end, but don't have the beginning
complete //have full history locally stored
};
RosterItem(const QString& pJid, const QString& account, QObject* parent = 0);
~RosterItem();
ArchiveState getArchiveState() const;
QString getName() const;
void setName(const QString& n);
void setSubscriptionState(Shared::SubscriptionState state);
Shared::SubscriptionState getSubscriptionState() const;
void addMessageToArchive(const Shared::Message& msg);
void appendMessageToArchive(const Shared::Message& msg);
void flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId);
void requestHistory(int count, const QString& before);
void requestFromEmpty(int count, const QString& before);
signals:
void nameChanged(const QString& name);
void subscriptionStateChanged(Shared::SubscriptionState state);
void historyResponse(const std::list<Shared::Message>& messages);
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime());
public:
const QString jid;
protected:
QString name;
ArchiveState archiveState;
Archive* archive;
Shared::SubscriptionState subscriptionState;
bool syncronizing;
int requestedCount;
QString requestedBefore;
std::list<Shared::Message> hisoryCache;
std::list<Shared::Message> appendCache;
std::list<Shared::Message> responseCache;
std::list<std::pair<int, QString>> requestCache;
private:
void nextRequest();
void performRequest(int count, const QString& before);
};
}
#endif // CORE_ROSTERITEM_H

View file

@ -1,3 +1,21 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "squawk.h"
#include <QDebug>
#include <QSettings>

View file

@ -1,3 +1,21 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CORE_SQUAWK_H
#define CORE_SQUAWK_H