//////////////////////////////////////////////////////////////////////
//
// BeeBEEP Copyright (C) 2010-2021 Marco Mastroddi
//
// BeeBEEP 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.
//
// BeeBEEP 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 BeeBEEP. If not, see .
//
// Author: Marco Mastroddi
//
// $Id: EmoticonManager.cpp 1455 2020-12-23 10:17:53Z mastroddi $
//
//////////////////////////////////////////////////////////////////////
#include "EmoticonManager.h"
EmoticonManager* EmoticonManager::mp_instance = Q_NULLPTR;
EmoticonManager::EmoticonManager()
: m_emoticons(), m_oneCharEmoticons(), m_uniqueKeys(),
m_maxTextSize( 2 ), m_favoriteEmoticons(), m_recentEmoticons(),
m_recentEmoticonsCount( 48 )
{
#ifdef BEEBEEP_DEBUG
//createEmojiFiles();
#endif
addTextEmoticon();
addEmojis();
m_uniqueKeys = m_emoticons.uniqueKeys();
#ifdef BEEBEEP_DEBUG
QString s_debug = "";
qSort( m_uniqueKeys );
foreach( QChar c, m_uniqueKeys )
{
s_debug.append( c );
s_debug.append( " " );
}
qDebug() << "Emoticon manager has" << m_uniqueKeys.size() << "unique keys:" << qPrintable( s_debug );
qDebug() << "Emoticon manager has" << m_oneCharEmoticons.size() << "single char emoticons";
#endif
qDebug() << "Emoticon manager loads" << m_emoticons.size() << "emojis";
}
void EmoticonManager::addTextEmoticon()
{
addEmoticon( ":)", "1f603", Emoticon::Text );
addEmoticon( ":-)", "1f603", Emoticon::Text );
addEmoticon( ":p", "1f61c", Emoticon::Text );
addEmoticon( ":-p", "1f61c", Emoticon::Text );
addEmoticon( ":P", "1f61d", Emoticon::Text );
addEmoticon( ":-P", "1f61d", Emoticon::Text );
addEmoticon( ":'", "1f62d", Emoticon::Text );
addEmoticon( ":'(", "1f62d", Emoticon::Text );
addEmoticon( ":D", "1f604", Emoticon::Text );
addEmoticon( ":-D", "1f604", Emoticon::Text );
addEmoticon( ":@", "1f621", Emoticon::Text );
addEmoticon( ":x", "1f61a", Emoticon::Text );
addEmoticon( ":*", "1f618", Emoticon::Text );
addEmoticon( ":-*", "1f618", Emoticon::Text );
addEmoticon( ":z", "1f634", Emoticon::Text );
addEmoticon( ":o", "1f628", Emoticon::Text );
addEmoticon( ":O", "1f631", Emoticon::Text );
addEmoticon( ":|", "1f613", Emoticon::Text );
addEmoticon( ":L", "1f60d", Emoticon::Text );
addEmoticon( ":w", "whistle", Emoticon::Unknown );
addEmoticon( ":$", "bandit", Emoticon::Unknown );
addEmoticon( ":!", "wizard", Emoticon::Unknown );
addEmoticon( ";)", "1f609", Emoticon::Text );
addEmoticon( ";-)", "1f609", Emoticon::Text );
addEmoticon( ":(", "1f614", Emoticon::Text );
addEmoticon( ":-(", "1f614", Emoticon::Text );
addEmoticon( ":T", "1f60b", Emoticon::Text );
addEmoticon( ":%", "1f616", Emoticon::Text );
addEmoticon( ":\"D", "1f602", Emoticon::Text );
addEmoticon( ":"D", "1f602", Emoticon::Text ); // for html
addEmoticon( "B)", "1f60e", Emoticon::Text );
addEmoticon( "B-)", "1f60e", Emoticon::Text );
addEmoticon( "<3", "2764", Emoticon::Text );
addEmoticon( "<3", "2764", Emoticon::Text ); // for html
addEmoticon( "3", "1f494", Emoticon::Text );
addEmoticon( "</3", "1f494", Emoticon::Text ); // for html
addEmoticon( ">_<", "1f616", Emoticon::Text );
addEmoticon( ">_<", "1f616", Emoticon::Text ); // for html
addEmoticon( "=)", "1f60a", Emoticon::Text );
addEmoticon( "}:)", "1f608", Emoticon::Text );
addEmoticon( "o:)", "1f608", Emoticon::Text );
addEmoticon( "x(", "1f637", Emoticon::Text );
addEmoticon( "x-(", "1f637", Emoticon::Text );
addEmoticon( "X|", "1f632", Emoticon::Text );
addEmoticon( "X-|", "1f632", Emoticon::Text );
addEmoticon( "^_^", "1f601", Emoticon::Text );
addEmoticon( "O.o", "1f633", Emoticon::Text );
addEmoticon( "o.O", "1f633", Emoticon::Text );
}
void EmoticonManager::addEmoticon( const QString& e_text, const QString& e_name, int emoticon_group, int sort_order )
{
int emoticon_key_size = e_text.size();
QChar key_char = e_text.at( 0 );
m_emoticons.insert( key_char, Emoticon( e_text, e_name, emoticon_group, sort_order ) );
if( emoticon_key_size == 1 && !m_oneCharEmoticons.contains( key_char ) )
m_oneCharEmoticons.append( key_char );
if( emoticon_key_size > m_maxTextSize )
m_maxTextSize = emoticon_key_size;
}
static bool SortEmoticon( const Emoticon& e1, const Emoticon& e2 )
{
if( e1.sortOrder() < 0 || e2.sortOrder() < 0 )
return e1.name() < e2.name();
else
return e1.sortOrder() < e2.sortOrder();
}
QList EmoticonManager::textEmoticons( bool remove_names_duplicated ) const
{
QList emoticon_list;
bool emoticon_to_add = false;
QMultiHash::const_iterator it = m_emoticons.begin();
while( it != m_emoticons.end() )
{
if( !it.value().isInGroup() )
{
if( !remove_names_duplicated )
emoticon_list << *it;
else
{
emoticon_to_add = true;
QList::iterator it2 = emoticon_list.begin();
while( it2 != emoticon_list.end() )
{
if( (*it2).name() == (*it).name() )
{
emoticon_to_add = false;
break;
}
++it2;
}
if( emoticon_to_add )
emoticon_list << *it;
}
}
++it;
}
std::sort( emoticon_list.begin(), emoticon_list.end(), SortEmoticon );
return emoticon_list;
}
QList EmoticonManager::emoticonsByGroup( int group_id ) const
{
QList emoticon_list;
QMultiHash::const_iterator it = m_emoticons.begin();
while( it != m_emoticons.end() )
{
if( it.value().group() == group_id )
emoticon_list << *it;
++it;
}
std::sort( emoticon_list.begin(), emoticon_list.end(), SortEmoticon );
return emoticon_list;
}
Emoticon EmoticonManager::emoticon( const QString& e_text ) const
{
if( !e_text.isEmpty() )
{
QChar emoticon_key = e_text.at( 0 );
QMultiHash::const_iterator it = m_emoticons.find( emoticon_key );
while( it != m_emoticons.end() && it.key() == emoticon_key )
{
if( it.value().textToMatch() == e_text )
return it.value();
++it;
}
}
return Emoticon();
}
Emoticon EmoticonManager::emoticonSelected( const QString& e_text )
{
if( !e_text.isEmpty() )
{
QChar emoticon_key = e_text.at( 0 );
QMultiHash::iterator it = m_emoticons.find( emoticon_key );
while( it != m_emoticons.end() && it.key() == emoticon_key )
{
if( it.value().textToMatch() == e_text )
{
it.value().addToCount();
return it.value();
}
++it;
}
}
return Emoticon();
}
Emoticon EmoticonManager::textEmoticon( const QString& e_text ) const
{
if( !e_text.isEmpty() )
{
QChar emoticon_key = e_text.at( 0 );
QMultiHash::const_iterator it = m_emoticons.find( emoticon_key );
while( it != m_emoticons.end() && it.key() == emoticon_key )
{
if( !it.value().isInGroup() && it.value().textToMatch() == e_text )
return it.value();
++it;
}
}
return Emoticon();
}
Emoticon EmoticonManager::emoticonByFile( const QString& e_file_name ) const
{
if( !e_file_name.isEmpty() )
{
QMultiHash::const_iterator it = m_emoticons.begin();
while( it != m_emoticons.end() )
{
if( it.value().fileName() == e_file_name && it.value().isInGroup() )
return it.value();
++it;
}
}
return Emoticon();
}
QString EmoticonManager::parseEmoticons( const QString& msg, int emoticon_size, bool use_native_emoticons ) const
{
QString s = "";
QString text_to_match = "";
QChar c;
bool parse_emoticons = true;
for( int pos = 0; pos < msg.size(); pos++ )
{
c = msg[ pos ];
if( c.isSpace() )
{
if( text_to_match.size() > 0 )
{
s += text_to_match;
text_to_match = "";
}
s += c;
parse_emoticons = true;
}
else if( text_to_match.size() > 0 )
{
text_to_match += c;
Emoticon e;
if( use_native_emoticons )
{
Emoticon text_emoticon = textEmoticon( text_to_match );
if( text_emoticon.isValid() )
{
Emoticon native_emoticon = emoticonByFile( text_emoticon.fileName() );
if( native_emoticon.isValid() )
{
s += native_emoticon.textToMatch();
text_to_match = "";
parse_emoticons = false;
}
}
}
else
e = emoticon( text_to_match );
if( e.isValid() )
{
s += e.toHtml( emoticon_size );
text_to_match = "";
parse_emoticons = true;
}
else
{
if( text_to_match.size() >= m_maxTextSize )
{
s += text_to_match;
text_to_match = "";
parse_emoticons = false;
}
}
}
else if( m_uniqueKeys.contains( c ) )
{
if( !use_native_emoticons && isOneCharEmoticon( c ) )
{
Emoticon e = emoticon( c );
if( e.isValid() )
{
s += e.toHtml( emoticon_size );
text_to_match = "";
parse_emoticons = true;
}
}
else if( parse_emoticons )
{
text_to_match = c;
parse_emoticons = false;
}
else
s += c;
}
else
{
s += c;
parse_emoticons = false;
}
}
if( text_to_match.size() > 0 )
s += text_to_match;
return s;
}
bool EmoticonManager::setEmoticonCount( const QString& e_text, int e_count )
{
if( !e_text.isEmpty() )
{
QChar emoticon_key = e_text.at( 0 );
QMultiHash::iterator it = m_emoticons.find( emoticon_key );
while( it != m_emoticons.end() && it.key() == emoticon_key )
{
if( it.value().textToMatch() == e_text )
{
it.value().setCount( e_count );
return true;
}
++it;
}
}
return false;
}
void EmoticonManager::clearFavoriteEmoticons()
{
m_favoriteEmoticons.clear();
QMultiHash::iterator it = m_emoticons.begin();
while( it != m_emoticons.end() )
{
it.value().resetCount();
++it;
}
}
QList EmoticonManager::favoriteEmoticonsToSort() const
{
QList favorite_emoticons_to_sort;
QMultiHash::const_iterator it = m_emoticons.begin();
while( it != m_emoticons.end() )
{
if( it.value().count() > 0 )
favorite_emoticons_to_sort.append( it.value() );
++it;
}
return favorite_emoticons_to_sort;
}
static bool SortFavoriteEmoticon( const Emoticon& fe1, const Emoticon& fe2 )
{
if( fe1.count() <= 0 && fe2.count() <= 0 )
return SortEmoticon( fe1, fe2 );
else
return fe1.count() > fe2.count();
}
int EmoticonManager::loadFavoriteEmoticons( const QStringList& sl )
{
bool ok = false;
m_favoriteEmoticons.clear();
if( !sl.isEmpty() )
{
foreach( QString s, sl )
{
QStringList pieces = s.split( " " );
if( pieces.size() >= 2 )
{
QString text_to_match = pieces.at( 0 );
int emoticon_count = pieces.at( 1 ).toInt( &ok );
if( !ok )
emoticon_count = 0;
setEmoticonCount( text_to_match, emoticon_count );
}
}
}
m_favoriteEmoticons = favoriteEmoticonsToSort();
std::sort( m_favoriteEmoticons.begin(), m_favoriteEmoticons.end(), SortFavoriteEmoticon );
return m_favoriteEmoticons.size();
}
QStringList EmoticonManager::saveFavoriteEmoticons() const
{
QList favorite_emoticon_to_sort = favoriteEmoticonsToSort();
QStringList sl;
foreach( Emoticon e, favorite_emoticon_to_sort )
sl.append( QString( "%1 %2" ).arg( e.textToMatch() ).arg( e.count() ) );
return sl;
}
int EmoticonManager::loadRecentEmoticons( const QStringList& sl )
{
m_recentEmoticons.clear();
foreach( QString s, sl )
{
Emoticon e = emoticon( s );
if( e.isValid() )
{
m_recentEmoticons.append( e );
if( m_recentEmoticons.size() == m_recentEmoticonsCount )
break;
}
}
return m_recentEmoticons.size();
}
QStringList EmoticonManager::saveRencentEmoticons() const
{
QStringList sl;
foreach( Emoticon e, m_recentEmoticons )
sl.append( e.textToMatch() );
return sl;
}
bool EmoticonManager::addToRecentEmoticons( const Emoticon& e )
{
if( m_recentEmoticons.contains( e ) )
return false;
m_recentEmoticons.prepend( e );
if( m_recentEmoticons.size() > m_recentEmoticonsCount )
m_recentEmoticons.removeLast();
return true;
}
void EmoticonManager::createEmojiFiles()
{
QFile emoji_list_file( "../misc/emoji_list.txt" );
if( !emoji_list_file.open( QFile::ReadOnly | QFile::Text ) )
return;
QStringList emoji_parts;
QString emoji_key;
QString emoji_file;
QString emoji_line;
int sort_order = 0;
QList emoji_list;
QTextStream text_stream_in( &emoji_list_file );
text_stream_in.setCodec( "UTF-8" );
while( !text_stream_in.atEnd() )
{
emoji_line = text_stream_in.readLine();
emoji_parts = emoji_line.split( "\t", QString::SkipEmptyParts );
if( emoji_parts.count() < 2 )
continue;
emoji_file = emoji_parts.at( 0 ).trimmed();
emoji_file.remove( ".png" );
emoji_key = emoji_parts.at( 1 ).trimmed();
sort_order++;
emoji_list.append( Emoticon( emoji_key, emoji_file, Emoticon::Unknown, sort_order ) );
#ifdef BEEBEEP_DEBUG
qDebug() << "Load Emoji: char" << qPrintable( emoji_key ) << "and file" << emoji_file;
#endif
}
emoji_list_file.close();
qDebug() << emoji_list.size() << "emojis load from list";
QStringList emoji_group_names;
emoji_group_names << "";
emoji_group_names << "Text";
emoji_group_names << "People";
emoji_group_names << "Objects";
emoji_group_names << "Nature";
emoji_group_names << "Places";
emoji_group_names << "Symbols";
QList::iterator emoji_it = emoji_list.begin();
QString emoji_file_name;
while( emoji_it != emoji_list.end() )
{
for( int i = Emoticon::People; i < Emoticon::NumGroups; i++ )
{
emoji_file_name = QString( "../src/emojis/" ) + emoji_group_names.at( i ).toLower() + QString( "/" ) + emoji_it->name() + ".png";
if( QFile::exists( emoji_file_name ) )
{
qDebug() << "Found emoji" << qPrintable( emoji_it->textToMatch() ) << "in file" << emoji_file_name;
emoji_it->setGroup( i );
}
}
++emoji_it;
}
int emoji_in_group = 0;
int emoji_not_in_group = 0;
int emoji_in_twitter = 0;
qDebug() << "Checking missed emoji in twitter folder";
foreach( Emoticon e, emoji_list )
{
if( !e.isInGroup() )
{
emoji_not_in_group++;
emoji_file_name = QString( "../src/emojis/twitter/%1.png" ).arg( e.name() );
if( QFile::exists( emoji_file_name ) )
{
qDebug() << qPrintable( QString( "cp twitter/%1.png ." ).arg( e.name() ) );
emoji_in_twitter++;
}
}
else
emoji_in_group++;
}
qDebug() << "Emoji in group:" << emoji_in_group;
qDebug() << "Emoji not in group:" << emoji_not_in_group;
qDebug() << "Emoji missed in twitter:" << emoji_in_twitter;
for( int i = Emoticon::People; i < Emoticon::NumGroups; i++ )
{
QString emoji_folder_name = QString( "../src/emojis/" ) + emoji_group_names.at( i ).toLower();
QDir emoji_folder( emoji_folder_name );
QStringList file_list = emoji_folder.entryList( QDir::NoDotAndDotDot | QDir::NoSymLinks );
foreach( QString s, file_list )
{
if( !s.contains( ".png" ) )
continue;
bool emoji_exists = false;
foreach( Emoticon e, emoji_list )
{
if( QString( "%1.png" ).arg( e.name() ) == s )
{
emoji_exists = true;
break;
}
}
if( !emoji_exists )
qDebug() << "Emoji not in text list:" << qPrintable( QString( "%1/%2" ).arg( emoji_folder_name ).arg( s ) );
}
}
QFile file_to_save( "../src/Emojis.cpp" );
if( file_to_save.exists() )
file_to_save.remove();
if( !file_to_save.open( QFile::ReadWrite ) )
{
qWarning() << file_to_save.fileName() << "is not writeable";
return;
}
QTextStream text_stream_out( &file_to_save );
text_stream_out.setCodec( "UTF-8" );
text_stream_out << "//////////////////////////////////////////////////////////////////////\n"
"//\n"
"// BeeBEEP Copyright (C) 2010-2021 Marco Mastroddi\n"
"//\n"
"// BeeBEEP is free software: you can redistribute it and/or modify\n"
"// it under the terms of the GNU General Public License as published\n"
"// by the Free Software Foundation, either version 3 of the License,\n"
"// or (at your option) any later version.\n"
"//\n"
"// BeeBEEP is distributed in the hope that it will be useful,\n"
"// but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"// GNU General Public License for more details.\n"
"//\n"
"// You should have received a copy of the GNU General Public License\n"
"// along with BeeBEEP. If not, see .\n"
"//\n"
"// Author: Marco Mastroddi \n"
"//\n"
"// Emojis.cpp is generated in " << QDateTime::currentDateTime().toString( "yyyy-MM-dd hh:mm:ss" ) << "\n"
"//\n"
"//////////////////////////////////////////////////////////////////////\n"
"\n";
text_stream_out << "#include \"EmoticonManager.h\"\n\n\n";
text_stream_out << "void EmoticonManager::addEmojis()\n{\n";
foreach( Emoticon e, emoji_list )
{
if( e.isInGroup() )
text_stream_out << " addEmoticon( QString::fromUtf8( \"" << e.textToMatch() << "\" ), \"" << e.name() << "\", Emoticon::" << emoji_group_names.at( e.group() ) << ", " << e.sortOrder() << " ); \n";
}
text_stream_out << "\n}\n\n";
file_to_save.close();
QFile emoji_resource_file( "../src/emojis.qrc" );
if( emoji_resource_file.exists() )
emoji_resource_file.remove();
if( !emoji_resource_file.open( QFile::ReadWrite ) )
{
qWarning() << emoji_resource_file.fileName() << "is not writeable";
return;
}
QTextStream text_stream_resource( &emoji_resource_file );
text_stream_resource << "\n\t\n";
foreach( Emoticon e, emoji_list )
{
if( e.isInGroup() )
text_stream_resource << "\t\temojis/" << emoji_group_names.at( e.group() ).toLower() << "/" << e.name() << ".png\n";
}
text_stream_resource << "\t\n\n\n";
emoji_resource_file.close();
}