1 //////////////////////////////////////////////////////////////////////
2 //
3 // BeeBEEP Copyright (C) 2010-2021 Marco Mastroddi
4 //
5 // BeeBEEP is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published
7 // by the Free Software Foundation, either version 3 of the License,
8 // or (at your option) any later version.
9 //
10 // BeeBEEP is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with BeeBEEP. If not, see <http://www.gnu.org/licenses/>.
17 //
18 // Author: Marco Mastroddi <marco.mastroddi(AT)gmail.com>
19 //
20 // $Id: EmoticonManager.cpp 1455 2020-12-23 10:17:53Z mastroddi $
21 //
22 //////////////////////////////////////////////////////////////////////
23 
24 #include "EmoticonManager.h"
25 
26 EmoticonManager* EmoticonManager::mp_instance = Q_NULLPTR;
27 
28 
EmoticonManager()29 EmoticonManager::EmoticonManager()
30  : m_emoticons(), m_oneCharEmoticons(), m_uniqueKeys(),
31    m_maxTextSize( 2 ), m_favoriteEmoticons(), m_recentEmoticons(),
32    m_recentEmoticonsCount( 48 )
33 {
34 #ifdef BEEBEEP_DEBUG
35   //createEmojiFiles();
36 #endif
37 
38   addTextEmoticon();
39   addEmojis();
40   m_uniqueKeys = m_emoticons.uniqueKeys();
41 
42 #ifdef BEEBEEP_DEBUG
43   QString s_debug = "";
44   qSort( m_uniqueKeys );
45   foreach( QChar c, m_uniqueKeys )
46   {
47     s_debug.append( c );
48     s_debug.append( " " );
49   }
50   qDebug() << "Emoticon manager has" << m_uniqueKeys.size() << "unique keys:" << qPrintable( s_debug );
51   qDebug() << "Emoticon manager has" << m_oneCharEmoticons.size() << "single char emoticons";
52 #endif
53 
54   qDebug() << "Emoticon manager loads" << m_emoticons.size() << "emojis";
55 }
56 
addTextEmoticon()57 void EmoticonManager::addTextEmoticon()
58 {
59   addEmoticon( ":)", "1f603", Emoticon::Text );
60   addEmoticon( ":-)", "1f603", Emoticon::Text );
61   addEmoticon( ":p", "1f61c", Emoticon::Text );
62   addEmoticon( ":-p", "1f61c", Emoticon::Text );
63   addEmoticon( ":P", "1f61d", Emoticon::Text );
64   addEmoticon( ":-P", "1f61d", Emoticon::Text );
65   addEmoticon( ":'", "1f62d", Emoticon::Text );
66   addEmoticon( ":'(", "1f62d", Emoticon::Text );
67   addEmoticon( ":D", "1f604", Emoticon::Text );
68   addEmoticon( ":-D", "1f604", Emoticon::Text );
69   addEmoticon( ":@", "1f621", Emoticon::Text );
70   addEmoticon( ":x", "1f61a", Emoticon::Text );
71   addEmoticon( ":*", "1f618", Emoticon::Text );
72   addEmoticon( ":-*", "1f618", Emoticon::Text );
73   addEmoticon( ":z", "1f634", Emoticon::Text );
74   addEmoticon( ":o", "1f628", Emoticon::Text );
75   addEmoticon( ":O", "1f631", Emoticon::Text );
76   addEmoticon( ":|", "1f613", Emoticon::Text );
77   addEmoticon( ":L", "1f60d", Emoticon::Text );
78   addEmoticon( ":w", "whistle", Emoticon::Unknown );
79   addEmoticon( ":$", "bandit", Emoticon::Unknown );
80   addEmoticon( ":!", "wizard", Emoticon::Unknown );
81   addEmoticon( ";)", "1f609", Emoticon::Text );
82   addEmoticon( ";-)", "1f609", Emoticon::Text );
83   addEmoticon( ":(", "1f614", Emoticon::Text );
84   addEmoticon( ":-(", "1f614", Emoticon::Text );
85   addEmoticon( ":T", "1f60b", Emoticon::Text );
86   addEmoticon( ":%", "1f616", Emoticon::Text );
87   addEmoticon( ":\"D", "1f602", Emoticon::Text );
88   addEmoticon( ":&quot;D", "1f602", Emoticon::Text ); // for html
89   addEmoticon( "B)", "1f60e", Emoticon::Text );
90   addEmoticon( "B-)", "1f60e", Emoticon::Text );
91   addEmoticon( "<3", "2764", Emoticon::Text );
92   addEmoticon( "&lt;3", "2764", Emoticon::Text );  // for html
93   addEmoticon( "</3", "1f494", Emoticon::Text );
94   addEmoticon( "&lt;/3", "1f494", Emoticon::Text ); // for html
95   addEmoticon( ">_<", "1f616", Emoticon::Text );
96   addEmoticon( "&gt;_&lt;", "1f616", Emoticon::Text ); // for html
97   addEmoticon( "=)", "1f60a", Emoticon::Text );
98   addEmoticon( "}:)", "1f608", Emoticon::Text );
99   addEmoticon( "o:)", "1f608", Emoticon::Text );
100   addEmoticon( "x(", "1f637", Emoticon::Text );
101   addEmoticon( "x-(", "1f637", Emoticon::Text );
102   addEmoticon( "X|", "1f632", Emoticon::Text );
103   addEmoticon( "X-|", "1f632", Emoticon::Text );
104   addEmoticon( "^_^", "1f601", Emoticon::Text );
105   addEmoticon( "O.o", "1f633", Emoticon::Text );
106   addEmoticon( "o.O", "1f633", Emoticon::Text );
107 
108 }
109 
addEmoticon(const QString & e_text,const QString & e_name,int emoticon_group,int sort_order)110 void EmoticonManager::addEmoticon( const QString& e_text, const QString& e_name, int emoticon_group, int sort_order )
111 {
112   int emoticon_key_size = e_text.size();
113   QChar key_char = e_text.at( 0 );
114 
115   m_emoticons.insert( key_char, Emoticon( e_text, e_name, emoticon_group, sort_order ) );
116 
117   if( emoticon_key_size == 1 && !m_oneCharEmoticons.contains( key_char ) )
118     m_oneCharEmoticons.append( key_char );
119 
120   if( emoticon_key_size > m_maxTextSize )
121     m_maxTextSize = emoticon_key_size;
122 }
123 
SortEmoticon(const Emoticon & e1,const Emoticon & e2)124 static bool SortEmoticon( const Emoticon& e1, const Emoticon& e2 )
125 {
126   if( e1.sortOrder() < 0 || e2.sortOrder() < 0 )
127     return e1.name() < e2.name();
128   else
129     return e1.sortOrder() < e2.sortOrder();
130 }
131 
textEmoticons(bool remove_names_duplicated) const132 QList<Emoticon> EmoticonManager::textEmoticons( bool remove_names_duplicated ) const
133 {
134   QList<Emoticon> emoticon_list;
135   bool emoticon_to_add = false;
136   QMultiHash<QChar, Emoticon>::const_iterator it = m_emoticons.begin();
137   while( it != m_emoticons.end() )
138   {
139     if( !it.value().isInGroup() )
140     {
141       if( !remove_names_duplicated )
142         emoticon_list << *it;
143       else
144       {
145         emoticon_to_add = true;
146         QList<Emoticon>::iterator it2 = emoticon_list.begin();
147         while( it2 != emoticon_list.end() )
148         {
149           if( (*it2).name() == (*it).name() )
150           {
151             emoticon_to_add = false;
152             break;
153           }
154           ++it2;
155         }
156         if( emoticon_to_add )
157           emoticon_list << *it;
158       }
159     }
160     ++it;
161   }
162 
163   std::sort( emoticon_list.begin(), emoticon_list.end(), SortEmoticon );
164 
165   return emoticon_list;
166 }
167 
emoticonsByGroup(int group_id) const168 QList<Emoticon> EmoticonManager::emoticonsByGroup( int group_id ) const
169 {
170   QList<Emoticon> emoticon_list;
171   QMultiHash<QChar, Emoticon>::const_iterator it = m_emoticons.begin();
172   while( it != m_emoticons.end() )
173   {
174     if( it.value().group() == group_id )
175       emoticon_list << *it;
176     ++it;
177   }
178 
179   std::sort( emoticon_list.begin(), emoticon_list.end(), SortEmoticon );
180 
181   return emoticon_list;
182 }
183 
emoticon(const QString & e_text) const184 Emoticon EmoticonManager::emoticon( const QString& e_text ) const
185 {
186   if( !e_text.isEmpty() )
187   {
188     QChar emoticon_key = e_text.at( 0 );
189     QMultiHash<QChar, Emoticon>::const_iterator it = m_emoticons.find( emoticon_key );
190     while( it != m_emoticons.end() && it.key() == emoticon_key )
191     {
192       if( it.value().textToMatch() == e_text )
193         return it.value();
194       ++it;
195     }
196   }
197   return Emoticon();
198 }
199 
emoticonSelected(const QString & e_text)200 Emoticon EmoticonManager::emoticonSelected( const QString& e_text )
201 {
202   if( !e_text.isEmpty() )
203   {
204     QChar emoticon_key = e_text.at( 0 );
205     QMultiHash<QChar, Emoticon>::iterator it = m_emoticons.find( emoticon_key );
206     while( it != m_emoticons.end() && it.key() == emoticon_key )
207     {
208       if( it.value().textToMatch() == e_text )
209       {
210         it.value().addToCount();
211         return it.value();
212       }
213       ++it;
214     }
215   }
216   return Emoticon();
217 }
218 
textEmoticon(const QString & e_text) const219 Emoticon EmoticonManager::textEmoticon( const QString& e_text ) const
220 {
221   if( !e_text.isEmpty() )
222   {
223     QChar emoticon_key = e_text.at( 0 );
224     QMultiHash<QChar, Emoticon>::const_iterator it = m_emoticons.find( emoticon_key );
225     while( it != m_emoticons.end() && it.key() == emoticon_key )
226     {
227       if( !it.value().isInGroup() && it.value().textToMatch() == e_text )
228         return it.value();
229       ++it;
230     }
231   }
232   return Emoticon();
233 }
234 
emoticonByFile(const QString & e_file_name) const235 Emoticon EmoticonManager::emoticonByFile( const QString& e_file_name ) const
236 {
237   if( !e_file_name.isEmpty() )
238   {
239     QMultiHash<QChar, Emoticon>::const_iterator it = m_emoticons.begin();
240     while( it != m_emoticons.end() )
241     {
242       if( it.value().fileName() == e_file_name && it.value().isInGroup() )
243         return it.value();
244       ++it;
245     }
246   }
247   return Emoticon();
248 }
249 
parseEmoticons(const QString & msg,int emoticon_size,bool use_native_emoticons) const250 QString EmoticonManager::parseEmoticons( const QString& msg, int emoticon_size, bool use_native_emoticons ) const
251 {
252   QString s = "";
253   QString text_to_match = "";
254   QChar c;
255   bool parse_emoticons = true;
256 
257   for( int pos = 0; pos < msg.size(); pos++ )
258   {
259     c = msg[ pos ];
260 
261     if( c.isSpace() )
262     {
263       if( text_to_match.size() > 0 )
264       {
265         s += text_to_match;
266         text_to_match = "";
267       }
268       s += c;
269       parse_emoticons = true;
270     }
271     else if( text_to_match.size() > 0 )
272     {
273       text_to_match += c;
274 
275       Emoticon e;
276       if( use_native_emoticons )
277       {
278         Emoticon text_emoticon = textEmoticon( text_to_match );
279         if( text_emoticon.isValid() )
280         {
281           Emoticon native_emoticon = emoticonByFile( text_emoticon.fileName() );
282           if( native_emoticon.isValid() )
283           {
284             s += native_emoticon.textToMatch();
285             text_to_match = "";
286             parse_emoticons = false;
287           }
288         }
289       }
290       else
291         e = emoticon( text_to_match );
292 
293       if( e.isValid() )
294       {
295         s += e.toHtml( emoticon_size );
296         text_to_match = "";
297         parse_emoticons = true;
298       }
299       else
300       {
301         if( text_to_match.size() >= m_maxTextSize )
302         {
303           s += text_to_match;
304           text_to_match = "";
305           parse_emoticons = false;
306         }
307       }
308     }
309     else if( m_uniqueKeys.contains( c ) )
310     {
311       if( !use_native_emoticons && isOneCharEmoticon( c ) )
312       {
313         Emoticon e = emoticon( c );
314         if( e.isValid() )
315         {
316           s += e.toHtml( emoticon_size );
317           text_to_match = "";
318           parse_emoticons = true;
319         }
320       }
321       else if( parse_emoticons )
322       {
323         text_to_match = c;
324         parse_emoticons = false;
325       }
326       else
327         s += c;
328     }
329     else
330     {
331       s += c;
332       parse_emoticons = false;
333     }
334   }
335 
336   if( text_to_match.size() > 0 )
337     s += text_to_match;
338 
339   return s;
340 }
341 
setEmoticonCount(const QString & e_text,int e_count)342 bool EmoticonManager::setEmoticonCount( const QString& e_text, int e_count )
343 {
344   if( !e_text.isEmpty() )
345   {
346     QChar emoticon_key = e_text.at( 0 );
347     QMultiHash<QChar, Emoticon>::iterator it = m_emoticons.find( emoticon_key );
348     while( it != m_emoticons.end() && it.key() == emoticon_key )
349     {
350       if( it.value().textToMatch() == e_text )
351       {
352         it.value().setCount( e_count );
353         return true;
354       }
355       ++it;
356     }
357   }
358   return false;
359 }
360 
clearFavoriteEmoticons()361 void EmoticonManager::clearFavoriteEmoticons()
362 {
363   m_favoriteEmoticons.clear();
364   QMultiHash<QChar, Emoticon>::iterator it = m_emoticons.begin();
365   while( it != m_emoticons.end() )
366   {
367     it.value().resetCount();
368     ++it;
369   }
370 }
371 
favoriteEmoticonsToSort() const372 QList<Emoticon> EmoticonManager::favoriteEmoticonsToSort() const
373 {
374   QList<Emoticon> favorite_emoticons_to_sort;
375   QMultiHash<QChar, Emoticon>::const_iterator it = m_emoticons.begin();
376   while( it != m_emoticons.end() )
377   {
378     if( it.value().count() > 0 )
379       favorite_emoticons_to_sort.append( it.value() );
380     ++it;
381   }
382   return favorite_emoticons_to_sort;
383 }
384 
SortFavoriteEmoticon(const Emoticon & fe1,const Emoticon & fe2)385 static bool SortFavoriteEmoticon( const Emoticon& fe1, const Emoticon& fe2 )
386 {
387   if( fe1.count() <= 0 && fe2.count() <= 0 )
388     return SortEmoticon( fe1, fe2 );
389   else
390     return fe1.count() > fe2.count();
391 }
392 
loadFavoriteEmoticons(const QStringList & sl)393 int EmoticonManager::loadFavoriteEmoticons( const QStringList& sl )
394 {
395   bool ok = false;
396   m_favoriteEmoticons.clear();
397   if( !sl.isEmpty() )
398   {
399     foreach( QString s, sl )
400     {
401       QStringList pieces = s.split( " " );
402       if( pieces.size() >= 2 )
403       {
404         QString text_to_match = pieces.at( 0 );
405         int emoticon_count = pieces.at( 1 ).toInt( &ok );
406         if( !ok )
407           emoticon_count = 0;
408         setEmoticonCount( text_to_match, emoticon_count );
409       }
410     }
411   }
412 
413   m_favoriteEmoticons = favoriteEmoticonsToSort();
414   std::sort( m_favoriteEmoticons.begin(), m_favoriteEmoticons.end(), SortFavoriteEmoticon );
415   return m_favoriteEmoticons.size();
416 }
417 
saveFavoriteEmoticons() const418 QStringList EmoticonManager::saveFavoriteEmoticons() const
419 {
420   QList<Emoticon> favorite_emoticon_to_sort = favoriteEmoticonsToSort();
421   QStringList sl;
422   foreach( Emoticon e, favorite_emoticon_to_sort )
423     sl.append( QString( "%1 %2" ).arg( e.textToMatch() ).arg( e.count() ) );
424   return sl;
425 }
426 
loadRecentEmoticons(const QStringList & sl)427 int EmoticonManager::loadRecentEmoticons( const QStringList& sl )
428 {
429   m_recentEmoticons.clear();
430   foreach( QString s, sl )
431   {
432     Emoticon e = emoticon( s );
433     if( e.isValid() )
434     {
435       m_recentEmoticons.append( e );
436       if( m_recentEmoticons.size() == m_recentEmoticonsCount )
437         break;
438     }
439   }
440   return  m_recentEmoticons.size();
441 }
442 
saveRencentEmoticons() const443 QStringList EmoticonManager::saveRencentEmoticons() const
444 {
445   QStringList sl;
446   foreach( Emoticon e, m_recentEmoticons )
447     sl.append( e.textToMatch() );
448   return sl;
449 }
450 
addToRecentEmoticons(const Emoticon & e)451 bool EmoticonManager::addToRecentEmoticons( const Emoticon& e )
452 {
453   if( m_recentEmoticons.contains( e ) )
454     return false;
455   m_recentEmoticons.prepend( e );
456   if( m_recentEmoticons.size() > m_recentEmoticonsCount )
457     m_recentEmoticons.removeLast();
458   return true;
459 }
460 
createEmojiFiles()461 void EmoticonManager::createEmojiFiles()
462 {
463   QFile emoji_list_file( "../misc/emoji_list.txt" );
464   if( !emoji_list_file.open( QFile::ReadOnly | QFile::Text ) )
465     return;
466 
467   QStringList emoji_parts;
468   QString emoji_key;
469   QString emoji_file;
470   QString emoji_line;
471   int sort_order = 0;
472   QList<Emoticon> emoji_list;
473 
474   QTextStream text_stream_in( &emoji_list_file );
475   text_stream_in.setCodec( "UTF-8" );
476   while( !text_stream_in.atEnd() )
477   {
478     emoji_line = text_stream_in.readLine();
479     emoji_parts = emoji_line.split( "\t", QString::SkipEmptyParts );
480     if( emoji_parts.count() < 2 )
481       continue;
482 
483     emoji_file = emoji_parts.at( 0 ).trimmed();
484     emoji_file.remove( ".png" );
485     emoji_key = emoji_parts.at( 1 ).trimmed();
486     sort_order++;
487 
488     emoji_list.append( Emoticon( emoji_key, emoji_file, Emoticon::Unknown, sort_order ) );
489 #ifdef BEEBEEP_DEBUG
490     qDebug() << "Load Emoji: char" << qPrintable( emoji_key ) << "and file" << emoji_file;
491 #endif
492   }
493 
494   emoji_list_file.close();
495   qDebug() << emoji_list.size() << "emojis load from list";
496 
497   QStringList emoji_group_names;
498   emoji_group_names << "";
499   emoji_group_names << "Text";
500   emoji_group_names << "People";
501   emoji_group_names << "Objects";
502   emoji_group_names << "Nature";
503   emoji_group_names << "Places";
504   emoji_group_names << "Symbols";
505 
506   QList<Emoticon>::iterator emoji_it = emoji_list.begin();
507   QString emoji_file_name;
508   while( emoji_it != emoji_list.end() )
509   {
510     for( int i = Emoticon::People; i < Emoticon::NumGroups; i++ )
511     {
512       emoji_file_name = QString( "../src/emojis/" ) + emoji_group_names.at( i ).toLower() + QString( "/" ) + emoji_it->name() + ".png";
513       if( QFile::exists( emoji_file_name ) )
514       {
515         qDebug() << "Found emoji" << qPrintable( emoji_it->textToMatch() ) << "in file" << emoji_file_name;
516         emoji_it->setGroup( i );
517       }
518     }
519     ++emoji_it;
520   }
521 
522   int emoji_in_group = 0;
523   int emoji_not_in_group = 0;
524   int emoji_in_twitter = 0;
525   qDebug() << "Checking missed emoji in twitter folder";
526   foreach( Emoticon e, emoji_list )
527   {
528     if( !e.isInGroup() )
529     {
530       emoji_not_in_group++;
531       emoji_file_name = QString( "../src/emojis/twitter/%1.png" ).arg( e.name() );
532       if( QFile::exists( emoji_file_name ) )
533       {
534         qDebug() << qPrintable( QString( "cp twitter/%1.png ." ).arg( e.name() ) );
535         emoji_in_twitter++;
536       }
537     }
538     else
539       emoji_in_group++;
540   }
541 
542   qDebug() << "Emoji in group:" << emoji_in_group;
543   qDebug() << "Emoji not in group:" << emoji_not_in_group;
544   qDebug() << "Emoji missed in twitter:" << emoji_in_twitter;
545 
546   for( int i = Emoticon::People; i < Emoticon::NumGroups; i++ )
547   {
548     QString emoji_folder_name = QString( "../src/emojis/" ) + emoji_group_names.at( i ).toLower();
549     QDir emoji_folder( emoji_folder_name );
550     QStringList file_list = emoji_folder.entryList( QDir::NoDotAndDotDot | QDir::NoSymLinks );
551     foreach( QString s, file_list )
552     {
553       if( !s.contains( ".png" ) )
554         continue;
555 
556       bool emoji_exists = false;
557       foreach( Emoticon e, emoji_list )
558       {
559         if( QString( "%1.png" ).arg( e.name() ) == s )
560         {
561           emoji_exists = true;
562           break;
563         }
564       }
565       if( !emoji_exists )
566         qDebug() << "Emoji not in text list:" << qPrintable( QString( "%1/%2" ).arg( emoji_folder_name ).arg( s ) );
567     }
568   }
569 
570   QFile file_to_save( "../src/Emojis.cpp" );
571   if( file_to_save.exists() )
572     file_to_save.remove();
573 
574   if( !file_to_save.open( QFile::ReadWrite ) )
575   {
576     qWarning() << file_to_save.fileName() << "is not writeable";
577     return;
578   }
579 
580   QTextStream text_stream_out( &file_to_save );
581   text_stream_out.setCodec( "UTF-8" );
582 
583   text_stream_out << "//////////////////////////////////////////////////////////////////////\n"
584                      "//\n"
585                      "// BeeBEEP Copyright (C) 2010-2021 Marco Mastroddi\n"
586                      "//\n"
587                      "// BeeBEEP is free software: you can redistribute it and/or modify\n"
588                      "// it under the terms of the GNU General Public License as published\n"
589                      "// by the Free Software Foundation, either version 3 of the License,\n"
590                      "// or (at your option) any later version.\n"
591                      "//\n"
592                      "// BeeBEEP is distributed in the hope that it will be useful,\n"
593                      "// but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
594                      "// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
595                      "// GNU General Public License for more details.\n"
596                      "//\n"
597                      "// You should have received a copy of the GNU General Public License\n"
598                      "// along with BeeBEEP. If not, see <http://www.gnu.org/licenses/>.\n"
599                      "//\n"
600                      "// Author: Marco Mastroddi <marco.mastroddi(AT)gmail.com>\n"
601                      "//\n"
602                      "// Emojis.cpp is generated in " << QDateTime::currentDateTime().toString( "yyyy-MM-dd hh:mm:ss" ) << "\n"
603                      "//\n"
604                      "//////////////////////////////////////////////////////////////////////\n"
605                      "\n";
606 
607   text_stream_out << "#include \"EmoticonManager.h\"\n\n\n";
608   text_stream_out << "void EmoticonManager::addEmojis()\n{\n";
609 
610   foreach( Emoticon e, emoji_list )
611   {
612     if( e.isInGroup() )
613       text_stream_out << "  addEmoticon( QString::fromUtf8( \"" << e.textToMatch() << "\" ), \"" << e.name() << "\", Emoticon::" << emoji_group_names.at( e.group() ) << ", " << e.sortOrder() << " ); \n";
614   }
615 
616   text_stream_out << "\n}\n\n";
617   file_to_save.close();
618 
619   QFile emoji_resource_file( "../src/emojis.qrc" );
620 
621   if( emoji_resource_file.exists() )
622     emoji_resource_file.remove();
623 
624   if( !emoji_resource_file.open( QFile::ReadWrite ) )
625   {
626     qWarning() << emoji_resource_file.fileName() << "is not writeable";
627     return;
628   }
629 
630   QTextStream text_stream_resource( &emoji_resource_file );
631 
632   text_stream_resource << "<RCC>\n\t<qresource prefix=\"/\">\n";
633 
634   foreach( Emoticon e, emoji_list )
635   {
636     if( e.isInGroup() )
637       text_stream_resource << "\t\t<file>emojis/" << emoji_group_names.at( e.group() ).toLower() << "/" << e.name() << ".png</file>\n";
638   }
639 
640   text_stream_resource << "\t</qresource>\n</RCC>\n\n";
641   emoji_resource_file.close();
642 }
643