From 6b88d80b5e78c42fed233a6a74b10d48face4d9d Mon Sep 17 00:00:00 2001 From: defnax Date: Thu, 10 Jul 2008 10:34:49 +0000 Subject: [PATCH] redesigned Friends Dialog git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@642 b45a01b8-16f6-495d-af2f-9b41ad6348cc --- retroshare-gui/src/gui/MainWindow.cpp | 6 +- retroshare-gui/src/gui/PeersDialog.cpp | 433 +++++++++++++++- retroshare-gui/src/gui/PeersDialog.h | 47 +- retroshare-gui/src/gui/PeersDialog.ui | 489 +++++++++++++++--- retroshare-gui/src/gui/images.qrc | 3 + .../src/gui/images/user/identityoffline24.png | Bin 0 -> 1205 bytes 6 files changed, 875 insertions(+), 103 deletions(-) create mode 100644 retroshare-gui/src/gui/images/user/identityoffline24.png diff --git a/retroshare-gui/src/gui/MainWindow.cpp b/retroshare-gui/src/gui/MainWindow.cpp index ebf6b5a1d..c015236e9 100644 --- a/retroshare-gui/src/gui/MainWindow.cpp +++ b/retroshare-gui/src/gui/MainWindow.cpp @@ -59,7 +59,7 @@ /* Images for toolbar icons */ #define IMAGE_NETWORK ":/images/network32.png" -#define IMAGE_PEERS ":/images/peers_24x24.png" +#define IMAGE_PEERS ":/images/groupchat.png" #define IMAGE_SEARCH ":/images/filefind.png" #define IMAGE_TRANSFERS ":/images/ktorrent32.png" #define IMAGE_LINKS ":/images/ktorrent.png" @@ -169,8 +169,8 @@ MainWindow::MainWindow(QWidget* parent, Qt::WFlags flags) ui.stackPages->add(sharedfilesDialog = new SharedFilesDialog(ui.stackPages), createPageAction(QIcon(IMAGE_FILES), tr("Files"), grp)); - ui.stackPages->add(chatDialog = new ChatDialog(ui.stackPages), - createPageAction(QIcon(IMAGE_CHAT), tr("Chat"), grp)); + //ui.stackPages->add(chatDialog = new ChatDialog(ui.stackPages), + // createPageAction(QIcon(IMAGE_CHAT), tr("Chat"), grp)); ui.stackPages->add(messagesDialog = new MessagesDialog(ui.stackPages), createPageAction(QIcon(IMAGE_MESSAGES), tr("Messages"), grp)); diff --git a/retroshare-gui/src/gui/PeersDialog.cpp b/retroshare-gui/src/gui/PeersDialog.cpp index bb1136240..cda57b121 100644 --- a/retroshare-gui/src/gui/PeersDialog.cpp +++ b/retroshare-gui/src/gui/PeersDialog.cpp @@ -28,6 +28,7 @@ #include "rsiface/rsiface.h" #include "rsiface/rspeers.h" #include "rsiface/rsstatus.h" +#include "rsiface/rsmsgs.h" #include "chat/PopupChatDialog.h" #include "msgs/ChanMsgDialog.h" @@ -37,6 +38,13 @@ #include #include +#include +#include +#include +#include +#include +#include + #include #include #include @@ -45,6 +53,7 @@ #include #include #include +#include /* Images for context menu icons */ @@ -54,8 +63,8 @@ #define IMAGE_CHAT ":/images/chat.png" #define IMAGE_MSG ":/images/message-mail.png" /* Images for Status icons */ -#define IMAGE_ONLINE ":/images/donline.png" -#define IMAGE_OFFLINE ":/images/dhidden.png" +#define IMAGE_ONLINE ":/images/user/identity24.png" +#define IMAGE_OFFLINE ":/images/user/identityoffline24.png" /****** * #define PEERS_DEBUG 1 @@ -79,21 +88,62 @@ PeersDialog::PeersDialog(QWidget *parent) _header->setResizeMode (0, QHeaderView::Custom); _header->setResizeMode (1, QHeaderView::Interactive); _header->setResizeMode (2, QHeaderView::Interactive); - _header->setResizeMode (3, QHeaderView::Interactive); + /*_header->setResizeMode (3, QHeaderView::Interactive); _header->setResizeMode (4, QHeaderView::Interactive); _header->setResizeMode (5, QHeaderView::Interactive); _header->setResizeMode (6, QHeaderView::Interactive); - _header->setResizeMode (7, QHeaderView::Interactive); + _header->setResizeMode (7, QHeaderView::Interactive);*/ _header->resizeSection ( 0, 25 ); _header->resizeSection ( 1, 100 ); _header->resizeSection ( 2, 100 ); - _header->resizeSection ( 3, 120 ); + /*_header->resizeSection ( 3, 120 ); _header->resizeSection ( 4, 100 ); _header->resizeSection ( 5, 230 ); _header->resizeSection ( 6, 120 ); - _header->resizeSection ( 7, 220 ); + _header->resizeSection ( 7, 220 );*/ + + + loadEmoticonsgroupchat(); + + //setWindowIcon(QIcon(QString(":/images/rstray3.png"))); + + connect(ui.lineEdit, SIGNAL(textChanged ( ) ), this, SLOT(checkChat( ) )); + connect(ui.Sendbtn, SIGNAL(clicked()), this, SLOT(sendMsg())); + connect(ui.emoticonBtn, SIGNAL(clicked()), this, SLOT(smileyWidgetgroupchat())); + + + //connect( ui.msgSendList, SIGNAL( customContextMenuRequested( QPoint ) ), this, SLOT( msgSendListCostumPopupMenu( QPoint ) ) ); + connect( ui.msgText, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayInfoChatMenu(const QPoint&))); + + connect(ui.textboldChatButton, SIGNAL(clicked()), this, SLOT(setFont())); + connect(ui.textunderlineChatButton, SIGNAL(clicked()), this, SLOT(setFont())); + connect(ui.textitalicChatButton, SIGNAL(clicked()), this, SLOT(setFont())); + connect(ui.fontsButton, SIGNAL(clicked()), this, SLOT(getFont())); + connect(ui.colorChatButton, SIGNAL(clicked()), this, SLOT(setColor())); + + ui.fontsButton->setIcon(QIcon(QString(":/images/fonts.png"))); + + _currentColor = Qt::black; + QPixmap pxm(24,24); + pxm.fill(_currentColor); + ui.colorChatButton->setIcon(pxm); + + mCurrentFont = QFont("Comic Sans MS", 12); + ui.lineEdit->setFont(mCurrentFont); + + setChatInfo(tr("Welcome to RetroShare's group chat."), QString::fromUtf8("blue")); + + QMenu * grpchatmenu = new QMenu(); + grpchatmenu->addAction(ui.actionClearChat); + ui.menuButton->setMenu(grpchatmenu); + + _underline = false; + + QTimer *timer = new QTimer(this); + timer->connect(timer, SIGNAL(timeout()), this, SLOT(insertChat())); + timer->start(500); /* half a second */ @@ -176,7 +226,7 @@ void PeersDialog::insertPeers() /* remove old items ??? */ peerWidget->clear(); - peerWidget->setColumnCount(8); + peerWidget->setColumnCount(3); QList items; @@ -259,7 +309,7 @@ void PeersDialog::insertPeers() /* bright green */ for(i = 1; i < 8; i++) { - item -> setBackground(i,QBrush(Qt::green)); + //item -> setBackground(i,QBrush(Qt::green)); item -> setIcon(0,(QIcon(IMAGE_ONLINE))); } } @@ -287,7 +337,7 @@ void PeersDialog::insertPeers() { for(i = 1; i < 8; i++) { - item -> setBackground(i,QBrush(Qt::lightGray)); + //item -> setBackground(i,QBrush(Qt::lightGray)); item -> setIcon(0,(QIcon(IMAGE_OFFLINE))); } } @@ -373,26 +423,26 @@ void PeersDialog::chatfriend() return; } - if (detail.state & RS_PEER_STATE_CONNECTED) - { + /*if (detail.state & RS_PEER_STATE_CONNECTED) + {*/ /* must reference ChatDialog */ if (chatDialog) { chatDialog->getPrivateChat(id, name, true); } - } + /*} else - { + {*/ /* info dialog */ - QMessageBox::StandardButton sb = QMessageBox::question ( NULL, - "Friend Not Online", - "Your Friend is offline \nDo you want to send them a Message instead", - (QMessageBox::Yes | QMessageBox::No)); - if (sb == QMessageBox::Yes) + // QMessageBox::StandardButton sb = QMessageBox::question ( NULL, + // "Friend Not Online", + //"Your Friend is offline \nDo you want to send them a Message instead", + // (QMessageBox::Yes | QMessageBox::No)); + /*if (sb == QMessageBox::Yes) { msgfriend(); } - } + }*/ return; } @@ -572,4 +622,349 @@ void PeersDialog::configurefriend() } +void PeersDialog::insertChat() +{ + if (!rsMsgs->chatAvailable()) + { + return; + } + + std::list newchat; + if (!rsMsgs->getNewChat(newchat)) + { + return; + } + + QTextEdit *msgWidget = ui.msgText; + std::list::iterator it; + + + /* add in lines at the bottom */ + for(it = newchat.begin(); it != newchat.end(); it++) + { + std::string msg(it->msg.begin(), it->msg.end()); + std::cerr << "ChatDialog::insertChat(): " << msg << std::endl; + + /* are they private? */ + if (it->chatflags & RS_CHAT_PRIVATE) + { + PopupChatDialog *pcd = getPrivateChat(it->rsid, it->name, true); + pcd->addChatMsg(&(*it)); + continue; + } + + std::ostringstream out; + QString currenttxt = msgWidget->toHtml(); + QString extraTxt; + + QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss"); + QString name = QString::fromStdString(it->name); + QString line = "" + timestamp + "" + + "" + " " + name + ""; + + extraTxt += line; + + extraTxt += QString::fromStdWString(it->msg); + + /* add it everytime */ + currenttxt += extraTxt; + + QHashIterator i(smileys); + while(i.hasNext()) + { + i.next(); + currenttxt.replace(i.key(), ""); + } + + + msgWidget->setHtml(currenttxt); + + + QScrollBar *qsb = msgWidget->verticalScrollBar(); + qsb -> setValue(qsb->maximum()); + } +} + +void PeersDialog::checkChat() +{ + /* if at the end of the text -> we can send it! */ + QTextEdit *chatWidget = ui.lineEdit; + std::string txt = chatWidget->toPlainText().toStdString(); + if ('\n' == txt[txt.length()-1]) + { + //std::cerr << "Found found at end of :" << txt << ": should send!"; + //std::cerr << std::endl; + if (txt.length()-1 == txt.find('\n')) /* only if on first line! */ + { + /* should remove last char ... */ + sendMsg(); + } + } + else + { + //std::cerr << "No found in :" << txt << ":"; + //std::cerr << std::endl; + } +} + +void PeersDialog::sendMsg() +{ + QTextEdit *lineWidget = ui.lineEdit; + + ChatInfo ci; + //ci.msg = lineWidget->Text().toStdWString(); + ci.msg = lineWidget->toHtml().toStdWString(); + ci.chatflags = RS_CHAT_PUBLIC; + + std::string msg(ci.msg.begin(), ci.msg.end()); + std::cerr << "ChatDialog::sendMsg(): " << msg << std::endl; + + rsMsgs -> ChatSend(ci); + ui.lineEdit->clear(); + setFont(); + + /* redraw send list */ + insertSendList(); + +} + +void PeersDialog::insertSendList() +{ + std::list peers; + std::list::iterator it; + + if (!rsPeers) + { + /* not ready yet! */ + return; + } + + rsPeers->getOnlineList(peers); + + /* get a link to the table */ + //QTreeWidget *sendWidget = ui.msgSendList; + QList items; + + for(it = peers.begin(); it != peers.end(); it++) + { + + RsPeerDetails details; + if (!rsPeers->getPeerDetails(*it, details)) + { + continue; /* BAD */ + } + + /* make a widget per friend */ + QTreeWidgetItem *item = new QTreeWidgetItem((QTreeWidget*)0); + + /* add all the labels */ + /* (0) Person */ + item -> setText(0, QString::fromStdString(details.name)); + + item -> setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + //item -> setFlags(Qt::ItemIsUserCheckable); + + item -> setCheckState(0, Qt::Checked); + + if (rsicontrol->IsInChat(*it)) + { + item -> setCheckState(0, Qt::Checked); + } + else + { + item -> setCheckState(0, Qt::Unchecked); + } + + /* disable for the moment */ + item -> setFlags(Qt::ItemIsUserCheckable); + item -> setCheckState(0, Qt::Checked); + + /* add to the list */ + items.append(item); + } + + /* remove old items */ + //sendWidget->clear(); + //sendWidget->setColumnCount(1); + + /* add the items in! */ + //sendWidget->insertTopLevelItems(0, items); + + //sendWidget->update(); /* update display */ +} + + +/* to toggle the state */ + + +void PeersDialog::toggleSendItem( QTreeWidgetItem *item, int col ) +{ + std::cerr << "ToggleSendItem()" << std::endl; + + /* extract id */ + std::string id = (item -> text(4)).toStdString(); + + /* get state */ + bool inChat = (Qt::Checked == item -> checkState(0)); /* alway column 0 */ + + /* call control fns */ + + rsicontrol -> SetInChat(id, inChat); + return; +} + +PopupChatDialog *PeersDialog::getPrivateChat(std::string id, std::string name, bool show) +{ + /* see if it exists already */ + PopupChatDialog *popupchatdialog = NULL; + + std::map::iterator it; + if (chatDialogs.end() != (it = chatDialogs.find(id))) + { + /* exists already */ + popupchatdialog = it->second; + } + else + { + popupchatdialog = new PopupChatDialog(id, name); + chatDialogs[id] = popupchatdialog; + } + + if (show) + { + popupchatdialog->show(); + } + + return popupchatdialog; + +} + +void PeersDialog::clearOldChats() +{ + /* nothing yet */ + +} + +void PeersDialog::setColor() +{ + + bool ok; + QRgb color = QColorDialog::getRgba(ui.lineEdit->textColor().rgba(), &ok, this); + if (ok) { + _currentColor = QColor(color); + QPixmap pxm(24,24); + pxm.fill(_currentColor); + ui.colorChatButton->setIcon(pxm); + } + setFont(); +} + +void PeersDialog::getFont() +{ + bool ok; + mCurrentFont = QFontDialog::getFont(&ok, mCurrentFont, this); + setFont(); +} + +void PeersDialog::setFont() +{ + mCurrentFont.setBold(ui.textboldChatButton->isChecked()); + mCurrentFont.setUnderline(ui.textunderlineChatButton->isChecked()); + mCurrentFont.setItalic(ui.textitalicChatButton->isChecked()); + ui.lineEdit->setFont(mCurrentFont); + ui.lineEdit->setTextColor(_currentColor); + + ui.lineEdit->setFocus(); + +} + +void PeersDialog::underline() +{ + _underline = !_underline; + ui.lineEdit->setFontUnderline(_underline); +} + + +// Update Chat Info information +void PeersDialog::setChatInfo(QString info, QColor color) +{ + static unsigned int nbLines = 0; + ++nbLines; + // Check log size, clear it if too big + if(nbLines > 200) { + ui.msgText->clear(); + nbLines = 1; + } + ui.msgText->append(QString::fromUtf8("")+ QTime::currentTime().toString(QString::fromUtf8("hh:mm:ss")) + QString::fromUtf8(" - ") + info + QString::fromUtf8("")); +} + +void PeersDialog::on_actionClearChat_triggered() +{ + ui.msgText->clear(); +} + +void PeersDialog::displayInfoChatMenu(const QPoint& pos) +{ + // Log Menu + QMenu myChatMenu(this); + myChatMenu.addAction(ui.actionClearChat); + // XXX: Why mapToGlobal() is not enough? + myChatMenu.exec(mapToGlobal(pos)+QPoint(0,80)); +} + +void PeersDialog::loadEmoticonsgroupchat() +{ + QDir smdir(QApplication::applicationDirPath() + "/emoticons/kopete"); + QFileInfoList sminfo = smdir.entryInfoList(QStringList() << "*.gif" << "*.png", QDir::Files, QDir::Name); + foreach(QFileInfo info, sminfo) + { + QString smcode = info.fileName().replace(".gif", ""); + QString smstring; + for(int i = 0; i < 9; i+=3) + { + smstring += QString((char)smcode.mid(i,3).toInt()); + } + //qDebug(smstring.toAscii()); + smileys.insert(smstring, info.absoluteFilePath()); + } +} + +void PeersDialog::smileyWidgetgroupchat() +{ + qDebug("MainWindow::smileyWidget()"); + QWidget *smWidget = new QWidget; + smWidget->setWindowTitle("Emoticons"); + smWidget->setWindowIcon(QIcon(QString(":/images/rstray3.png"))); + smWidget->setFixedSize(256,256); + + + + int x = 0, y = 0; + + QHashIterator i(smileys); + while(i.hasNext()) + { + i.next(); + QPushButton *smButton = new QPushButton("", smWidget); + smButton->setGeometry(x*24, y*24, 24,24); + smButton->setIconSize(QSize(24,24)); + smButton->setIcon(QPixmap(i.value())); + smButton->setToolTip(i.key()); + //smButton->setFixedSize(24,24); + ++x; + if(x > 4) + { + x = 0; + y++; + } + connect(smButton, SIGNAL(clicked()), this, SLOT(addSmileys())); + } + + smWidget->show(); +} + +void PeersDialog::addSmileys() +{ + ui.lineEdit->setText(ui.lineEdit->toHtml() + qobject_cast(sender())->toolTip()); +} diff --git a/retroshare-gui/src/gui/PeersDialog.h b/retroshare-gui/src/gui/PeersDialog.h index 752232433..2aab7eed6 100644 --- a/retroshare-gui/src/gui/PeersDialog.h +++ b/retroshare-gui/src/gui/PeersDialog.h @@ -24,12 +24,15 @@ #include -//#include +#include "chat/PopupChatDialog.h" #include "mainpage.h" #include "ui_PeersDialog.h" - +class QFont; +class QAction; +class QTextEdit; +class QTextCharFormat; class ChatDialog; class PeersDialog : public MainPage @@ -39,13 +42,29 @@ class PeersDialog : public MainPage public: /** Default Constructor */ PeersDialog(QWidget *parent = 0); - /** Default Destructor */ + /** Default Destructor */ + + PopupChatDialog *getPrivateChat(std::string id, std::string name, bool show); + void clearOldChats(); + + void loadEmoticonsgroupchat(); void insertPeers(); void setChatDialog(ChatDialog *cd); - +public slots: + + void toggleSendItem( QTreeWidgetItem *item, int col ); + + void insertChat(); + void setChatInfo(QString info, QColor color=QApplication::palette().color(QPalette::WindowText)); + + void smileyWidgetgroupchat(); + void addSmileys(); + + void on_actionClearChat_triggered(); + void displayInfoChatMenu(const QPoint& pos); private slots: @@ -68,6 +87,17 @@ private slots: void connectfriend(); void setaddressfriend(); void trustfriend(); + + void setColor(); + void insertSendList(); + void checkChat(); + void sendMsg(); + + //void privchat(); + + void setFont(); + void getFont(); + void underline(); private: @@ -92,6 +122,15 @@ private: QAction* removefriendAct; QTreeWidget *peertreeWidget; + + QColor _currentColor; + bool _underline; + + QHash smileys; + + std::map chatDialogs; + + QFont mCurrentFont; /* how the text will come out */ /** Qt Designer generated object */ Ui::PeersDialog ui; diff --git a/retroshare-gui/src/gui/PeersDialog.ui b/retroshare-gui/src/gui/PeersDialog.ui index f56c87840..df34bd513 100644 --- a/retroshare-gui/src/gui/PeersDialog.ui +++ b/retroshare-gui/src/gui/PeersDialog.ui @@ -5,8 +5,8 @@ 0 0 - 436 - 261 + 825 + 516 @@ -493,57 +493,11 @@ Qt::NoContextMenu - - 6 - - - 1 - - - 6 - - - 1 - - - 1 - - - 1 - - + - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - 0 - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -570,7 +524,7 @@ - :/images/peers_16x16.png + :/images/user/friends24.png @@ -586,6 +540,19 @@ p, li { white-space: pre-wrap; } + + + + Qt::Horizontal + + + + 201 + 20 + + + + @@ -596,8 +563,8 @@ p, li { white-space: pre-wrap; } - 16 - 16 + 24 + 24 @@ -621,36 +588,404 @@ p, li { white-space: pre-wrap; } Person - - - Auto Connect - - - - - Trust Level - - - - - Peer Address - - - - - Last Contact - - - - - Person Id - - + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + 6 + + + + + Qt::Horizontal + + + + 181 + 20 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + 6 + + + + + + + + :/images/chat.png + + + + + + + + 10 + 75 + true + + + + Live Group Chat: + + + + + + + + + + + + 0 + 0 + + + + Qt::DefaultContextMenu + + + false + + + true + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + 6 + + + + + Qt::Horizontal + + + + 321 + 20 + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + Underline + + + + + + :/images/edit-italic.png + + + true + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + Italic + + + + + + :/images/edit-underline.png + + + true + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + Bold + + + + + + :/images/edit-bold.png + + + true + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + Text Color + + + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + Font + + + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + + + + :/images/emoticons/kopete/kopete020.png + + + + 24 + 24 + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 100 + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + + + + + Qt::Horizontal + + + + 321 + 20 + + + + + + + + Send + + + + Clear Chat History + + diff --git a/retroshare-gui/src/gui/images.qrc b/retroshare-gui/src/gui/images.qrc index e6ccad28d..6688e781e 100644 --- a/retroshare-gui/src/gui/images.qrc +++ b/retroshare-gui/src/gui/images.qrc @@ -219,6 +219,9 @@ images/toaster/toaster-background.png images/toaster/toaster-backrs3.png images/toaster/toaster-backrs4.png + images/user/friends24.png + images/user/identity24.png + images/user/identityoffline24.png images/up.png images/user.png images/webcache_24x24.png diff --git a/retroshare-gui/src/gui/images/user/identityoffline24.png b/retroshare-gui/src/gui/images/user/identityoffline24.png new file mode 100644 index 0000000000000000000000000000000000000000..3dc3ef1555f412ebd0613ec54681997127bd5042 GIT binary patch literal 1205 zcmV;m1WNmfP)g-Xk^;CUWA&x7l_a2yAe(#3;fcs;?eLjY?+ z2m^p9rBJKYP_Ng~Xf)7hG+^5{j4{hY6%0EB5HrT^JB~Bc>-8X|gl*fX)oO4Y2U5yC zLdahSewCLFe-AXdWQgN0+wJyuVHmzDJI*15fRqxoTJ7ti(GEwSf}fpcD} zD##Bn)@rr-Hk4AJl!6cfW6U+nvc8&_nE0^SY(fZuBuQY~HayRp2!h~p{wl}Ij4^1f z|KmX)fiVVImN81{C!F&;mSx>=9B0zDZH$kPqt$9*Y-|jaQgk{U7-L#Oh--|oPxCzI zTI(f3NZMCX7cX8c!Ie^P&d$!>mr}YbD=WzJ98yYDDiu_#RrtOS&N-exe~v86pp+^{ z$U&HHw|kxt@@wfqG4-_8Xf~TSuV26JE-x=*YikR}7ziQ27z3rW9CWsA_Z31M$4Ju@ zj4`6M{(=zlQK8~!C%=8`)-5Qd;QM|tYx+vBc#KjCT5IHa4##njBngZ$2!i0PXV0FM zJ;8+#006Bumr^!&c6QKiw=p?6S(;v4K1`D5Ig%vlOOY`KQ53Z|H#e)4(jEZtqRa>( zr$$Fd86gBh2)M3WN-tC%HqRJ?YPAZjH2`3JeH~hBjE|3FWo6|IfF(i*`2PNWq2N3r z1f5O?l}ZI+7$S-y9Gv%V`BqjV`B)z z5J3=tbKVE|y2;BqM-)X!(-cxlJbCg2K@e<|9#Be)6#e}0;lopo<9rO@LLA3))6>(o z=XvD_I7*{v@mS*MgzrBoD#;r5FcFP`q~?5u`i_yEA)BO@b7k_6RiwZ!}f-w#&` TZPksC00000NkvXXu0mjfTW2Xo literal 0 HcmV?d00001