From 7f26d7b5e6ae5759bb11942a69a9ada134744e98 Mon Sep 17 00:00:00 2001 From: Tom Chen Date: Sat, 26 Jun 2010 09:49:42 +0800 Subject: [PATCH] first release --- groupsms.pro | 3 + history | 17 + sms/abstractpage.cpp | 11 + sms/abstractpage.h | 33 ++ sms/addcontacttogroup.cpp | 49 +++ sms/addcontacttogroup.h | 26 ++ sms/addcontacttogroupdialog.ui | 97 ++++++ sms/common.h | 8 + sms/contactinterface.cpp | 204 +++++++++++ sms/contactinterface.h | 75 ++++ sms/contactpage.cpp | 89 +++++ sms/contactpage.h | 29 ++ sms/contactwidget.cpp | 451 ++++++++++++++++++++++++ sms/contactwidget.h | 69 ++++ sms/contactwidgetitem.cpp | 129 +++++++ sms/contactwidgetitem.h | 78 +++++ sms/data/26x26/groupsms.png | Bin 0 -> 826 bytes sms/data/40x40/groupsms.png | Bin 0 -> 1182 bytes sms/data/64x64/groupsms.png | Bin 0 -> 2100 bytes sms/data/icon.svg | 129 +++++++ sms/data/maemo/groupsms.xpm | 324 +++++++++++++++++ sms/groupsms.desktop | 13 + sms/groupsms.qrc | 16 + sms/groupwidgetitem.cpp | 110 ++++++ sms/groupwidgetitem.h | 31 ++ sms/images/addcontacttogroup.png | Bin 0 -> 3592 bytes sms/images/allselect.png | Bin 0 -> 4729 bytes sms/images/editgroup.png | Bin 0 -> 3261 bytes sms/images/female.png | Bin 0 -> 3485 bytes sms/images/male.png | Bin 0 -> 3230 bytes sms/images/newgroup.png | Bin 0 -> 2955 bytes sms/images/partselect.png | Bin 0 -> 3671 bytes sms/images/plus.png | Bin 0 -> 2672 bytes sms/images/recycle.png | Bin 0 -> 3077 bytes sms/images/select.png | Bin 0 -> 4729 bytes sms/images/sub.png | Bin 0 -> 1248 bytes sms/images/unselect.png | Bin 0 -> 4005 bytes sms/item.h | 27 ++ sms/itemobserver.h | 26 ++ sms/main.cpp | 56 +++ sms/mainwindow.cpp | 303 ++++++++++++++++ sms/mainwindow.h | 74 ++++ sms/mainwindow.ui | 133 +++++++ sms/newgroupdialog.cpp | 42 +++ sms/newgroupdialog.h | 19 + sms/newgroupdialog.ui | 97 ++++++ sms/selectcontactwidget.cpp | 89 +++++ sms/selectcontactwidget.h | 28 ++ sms/sendsmssession.cpp | 98 ++++++ sms/sendsmssession.h | 43 +++ sms/sms.pro | 85 +++++ sms/tpsession/tpsession.cpp | 265 ++++++++++++++ sms/tpsession/tpsession.h | 99 ++++++ sms/tpsession/tpsessionaccount.cpp | 309 +++++++++++++++++ sms/tpsession/tpsessionaccount.h | 81 +++++ sms/tpsession/tpsessionchannel.cpp | 185 ++++++++++ sms/tpsession/tpsessionchannel.h | 60 ++++ sms/tpsession/tpsessionobserver.cpp | 37 ++ sms/tpsession/tpsessionobserver.h | 31 ++ sms/utility.cpp | 35 ++ sms/utility.h | 18 + sms/xmlcontroler.cpp | 656 +++++++++++++++++++++++++++++++++++ sms/xmlcontroler.h | 85 +++++ sms/xmlstring.h | 20 ++ welcome | 3 + 65 files changed, 4995 insertions(+) create mode 100644 groupsms.pro create mode 100644 history create mode 100644 sms/abstractpage.cpp create mode 100644 sms/abstractpage.h create mode 100644 sms/addcontacttogroup.cpp create mode 100644 sms/addcontacttogroup.h create mode 100644 sms/addcontacttogroupdialog.ui create mode 100644 sms/common.h create mode 100644 sms/contactinterface.cpp create mode 100644 sms/contactinterface.h create mode 100644 sms/contactpage.cpp create mode 100644 sms/contactpage.h create mode 100644 sms/contactwidget.cpp create mode 100644 sms/contactwidget.h create mode 100644 sms/contactwidgetitem.cpp create mode 100644 sms/contactwidgetitem.h create mode 100644 sms/data/26x26/groupsms.png create mode 100644 sms/data/40x40/groupsms.png create mode 100644 sms/data/64x64/groupsms.png create mode 100644 sms/data/icon.svg create mode 100644 sms/data/maemo/groupsms.xpm create mode 100644 sms/groupsms.desktop create mode 100644 sms/groupsms.qrc create mode 100644 sms/groupwidgetitem.cpp create mode 100644 sms/groupwidgetitem.h create mode 100644 sms/images/addcontacttogroup.png create mode 100644 sms/images/allselect.png create mode 100644 sms/images/editgroup.png create mode 100644 sms/images/female.png create mode 100644 sms/images/male.png create mode 100644 sms/images/newgroup.png create mode 100644 sms/images/partselect.png create mode 100644 sms/images/plus.png create mode 100644 sms/images/recycle.png create mode 100644 sms/images/select.png create mode 100644 sms/images/sub.png create mode 100644 sms/images/unselect.png create mode 100644 sms/item.h create mode 100644 sms/itemobserver.h create mode 100644 sms/main.cpp create mode 100644 sms/mainwindow.cpp create mode 100644 sms/mainwindow.h create mode 100644 sms/mainwindow.ui create mode 100644 sms/newgroupdialog.cpp create mode 100644 sms/newgroupdialog.h create mode 100644 sms/newgroupdialog.ui create mode 100644 sms/selectcontactwidget.cpp create mode 100644 sms/selectcontactwidget.h create mode 100644 sms/sendsmssession.cpp create mode 100644 sms/sendsmssession.h create mode 100644 sms/sms.pro create mode 100755 sms/tpsession/tpsession.cpp create mode 100755 sms/tpsession/tpsession.h create mode 100755 sms/tpsession/tpsessionaccount.cpp create mode 100755 sms/tpsession/tpsessionaccount.h create mode 100755 sms/tpsession/tpsessionchannel.cpp create mode 100755 sms/tpsession/tpsessionchannel.h create mode 100755 sms/tpsession/tpsessionobserver.cpp create mode 100755 sms/tpsession/tpsessionobserver.h create mode 100644 sms/utility.cpp create mode 100644 sms/utility.h create mode 100644 sms/xmlcontroler.cpp create mode 100644 sms/xmlcontroler.h create mode 100644 sms/xmlstring.h diff --git a/groupsms.pro b/groupsms.pro new file mode 100644 index 0000000..b3a2142 --- /dev/null +++ b/groupsms.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = sms +#SUBDIRS = tpsession-0.1 diff --git a/history b/history new file mode 100644 index 0000000..63fbac0 --- /dev/null +++ b/history @@ -0,0 +1,17 @@ +Need to do/fix: +* some GLIB CRITICAL when use libebook, libosso, libosso-abook in Qt4.(2010.06.08) + +Done: +* fixed some display problem.(2010.06.17) +* implement that remove seleted group/contact. (2010.06.12 / 2010.06.13) +* implement add contacts to group.(2010.06.12) +* display prolem after new group.(2010.06.11 / 2010.06.12) +* display prolem after sent message.(2010.06.11) +* use Q_SLOTS, Q_SIGNALS, Q_EMIT macro in TpSession for avoid build error when use together GTK/libosso.(2010.06.10) +* implement send sms. maybe use ( TpSession & TelepathyQt4 ) or maemo lib.(2010.06.07 / 2010.06.10) +* modify the icon of UI.(2010.06.10) +* modify the UI position.(2010.06.10) +* make a .DEB file(v0.0.1).(2010.06.09) +* implement that get all contacts from DB of devices.(2010.06.03 / 2010.06.09) +* add dialog to add contacts to groups.(2010.06.03 / 2010.06.07) +* add dialog to add new group.(2010.06.03 / 2010.06.04) diff --git a/sms/abstractpage.cpp b/sms/abstractpage.cpp new file mode 100644 index 0000000..c43a04f --- /dev/null +++ b/sms/abstractpage.cpp @@ -0,0 +1,11 @@ +#include "abstractpage.h" + +AbstractPage::AbstractPage(QWidget *parent) : + QWidget(parent) +{ + ////qDebug() << "AbstractPage::AbstractPage(QWidget *parent) , Entry"; +} + +void AbstractPage::clear() +{ +} diff --git a/sms/abstractpage.h b/sms/abstractpage.h new file mode 100644 index 0000000..c58947f --- /dev/null +++ b/sms/abstractpage.h @@ -0,0 +1,33 @@ +#ifndef ABSTRACTPAGE_H +#define ABSTRACTPAGE_H + +#include +#include +#include + +#include "contactwidget.h" + +class AbstractPage : public QWidget +{ + Q_OBJECT +public: + AbstractPage(QWidget *parent = 0); + + virtual void update() = 0; + virtual QString title() = 0; + virtual void updateSize() = 0; + virtual void clear(); + +public: + QScrollArea *scrollArea; + bool isVisible; + + ContactWidget *contactWidget; + +Q_SIGNALS: + +public Q_SLOTS: + +}; + +#endif // ABSTRACTPAGE_H diff --git a/sms/addcontacttogroup.cpp b/sms/addcontacttogroup.cpp new file mode 100644 index 0000000..3281bc2 --- /dev/null +++ b/sms/addcontacttogroup.cpp @@ -0,0 +1,49 @@ +#include "addcontacttogroup.h" +#include "contactinterface.h" +#include "xmlstring.h" + +AddContactToGroup::AddContactToGroup(QWidget *parent) : + QDialog(parent) +{ + //qDebug() << "AddContactToGroup::AddContactToGroup(QWidget *parent), Entry"; + + setupUi(this); + setWindowModality( Qt::ApplicationModal ); + + comboBox_groupname->addItems( ContactInterface::getInstance()->getAllGroupNames() ); + + m_SelectContactWidget = new SelectContactWidget(this); + m_SelectContactWidget->initContactWidget(); + m_SelectContactWidget->sizePolicy().setHorizontalPolicy(QSizePolicy::Maximum); + m_SelectContactWidget->setGeometry(0,0,600,600); + m_SelectContactWidget->update(); + + + scrollArea = new QScrollArea(this); + scrollArea->setWidget( m_SelectContactWidget ); + scrollArea->setBackgroundRole(QPalette::Light); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + + this->verticalLayout->addWidget(scrollArea); + + connect( btngroup_ok_cancel, SIGNAL( clicked(QAbstractButton*) ), this, SLOT( btn_clicked(QAbstractButton*) ) ); +} + +void AddContactToGroup::btn_clicked(QAbstractButton *button) +{ + if( QDialogButtonBox::AcceptRole == btngroup_ok_cancel->buttonRole( button ) ) + { + //qDebug() << "add contact to group "; + + QString groupname = comboBox_groupname->currentText(); + if( STR_XML_ALLCONTACTS != groupname ) + { + m_SelectContactWidget->addContactsToGroup(groupname); + } + done( QDialog::Accepted ); + }else // button cancel + { + done( QDialog::Rejected ); + } +} diff --git a/sms/addcontacttogroup.h b/sms/addcontacttogroup.h new file mode 100644 index 0000000..870a9e5 --- /dev/null +++ b/sms/addcontacttogroup.h @@ -0,0 +1,26 @@ +#ifndef ADDCONTACTTOGROUP_H +#define ADDCONTACTTOGROUP_H + +#include +#include "ui_addcontacttogroupdialog.h" +#include "selectcontactwidget.h" + +class AddContactToGroup : public QDialog, Ui::AddContactToGroupDialog +{ + Q_OBJECT +public: + AddContactToGroup(QWidget *parent = 0); + +private: + SelectContactWidget* m_SelectContactWidget; + + QScrollArea *scrollArea; + +Q_SIGNALS: + +public Q_SLOTS: + void btn_clicked(QAbstractButton * button); + +}; + +#endif // ADDCONTACTTOGROUP_H diff --git a/sms/addcontacttogroupdialog.ui b/sms/addcontacttogroupdialog.ui new file mode 100644 index 0000000..48c1912 --- /dev/null +++ b/sms/addcontacttogroupdialog.ui @@ -0,0 +1,97 @@ + + + AddContactToGroupDialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + 310 + 20 + 81 + 241 + + + + Qt::Vertical + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 10 + 10 + 291 + 281 + + + + + + + + + Add To: + + + false + + + + + + + + + + + + + + + btngroup_ok_cancel + accepted() + AddContactToGroupDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + btngroup_ok_cancel + rejected() + AddContactToGroupDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/sms/common.h b/sms/common.h new file mode 100644 index 0000000..f836d0d --- /dev/null +++ b/sms/common.h @@ -0,0 +1,8 @@ +#ifndef COMMON_H +#define COMMON_H + +#include + +const QString HOME_DIR = QDir::homePath() + "/.groupsms/"; + +#endif // COMMON_H diff --git a/sms/contactinterface.cpp b/sms/contactinterface.cpp new file mode 100644 index 0000000..64cf4ee --- /dev/null +++ b/sms/contactinterface.cpp @@ -0,0 +1,204 @@ +#include "contactinterface.h" +#include "xmlcontroler.h" +#include "xmlstring.h" + +ContactInterface* ContactInterface::instance = 0; + +ContactInterface* ContactInterface::getInstance() +{ + if( !instance ) + { + instance = new ContactInterface(); + } + return instance; +} + +ContactInterface::ContactInterface(QObject *parent) : + QObject(parent) +{ + instance = this; + + initEbook(); +} + +ContactInterface::~ContactInterface() +{ + for( int i = 0; i < all_contacts_items_db.size(); i++ ) + { + delete all_contacts_items_db.at(i); + } + all_contacts_items_db.clear(); +} + +void ContactInterface::setItemObserver(ItemObserver *observer) +{ + XmlControler::getInstance()->setItemObserver( observer ); +} + +void ContactInterface::setItemSelectObserver( ItemSelectObserver *observer ) +{ + XmlControler::getInstance()->setItemSelectObserver( observer ); +} + +bool ContactInterface::initEbook() +{ + //qDebug() << "ContactInterface::initEbook(), Entry"; + +#ifdef ONLY_FOR_EBOOK + GError *error = NULL; + roster = osso_abook_aggregator_get_default(&error); + if( error ) + { + //qDebug() << "Couldn't open roster %s\n" << error->message; + g_error_free( error ); + } + osso_abook_roster_start(roster); + + //qDebug() << "osso_abook_roster_start"; + + osso_abook_waitable_run(OSSO_ABOOK_WAITABLE(roster), g_main_context_default(), &error); + + if (!osso_abook_waitable_is_ready(OSSO_ABOOK_WAITABLE(roster), &error) ) + g_critical("osso_abook_waitable_is_ready: %s\n", error->message); + + //* for test +// OssoABookAggregator *_aggr; +// GList *list, *c; +// GError *err; + +// osso_abook_waitable_run(OSSO_ABOOK_WAITABLE(roster), g_main_context_default(), &err); + +// if (! osso_abook_waitable_is_ready(OSSO_ABOOK_WAITABLE(roster), &err)) +// g_critical("osso_abook_waitable_is_ready: %s\n", err->message); + +// _aggr = OSSO_ABOOK_AGGREGATOR(roster); +// list = osso_abook_aggregator_list_master_contacts(_aggr); + +// printf("count = %d\n", g_list_length(list)); // list is not empty ! + +// g_list_free(list); + //* test end +#endif + return true; +} + +bool ContactInterface::updateContactsFromEbookToXml() +{ + removeGroup( STR_XML_ALLCONTACTS ); + getAllContacts(); + addContactToGroup( all_contacts_items_db, STR_XML_ALLCONTACTS ); + return true; +} + +ItemListPtr ContactInterface::getAllContacts() +{ + //qDebug() << "ContactInterface::getAllContacts() from EBook, Entry"; + +#ifdef ONLY_FOR_EBOOK + GList *list = NULL; + GList *it = NULL; + EContact *contact = NULL; + OssoABookAggregator *aggregator = OSSO_ABOOK_AGGREGATOR(roster); + + list = osso_abook_aggregator_list_master_contacts( aggregator ); + + qDebug() << "get list from EBook"; + //qDebug() << "list length is :" << g_list_length(list); + + QString str; + + for( it = list; it; it = it->next ) + { + //qDebug() << "into list loop"; + + contact = (EContact *)it->data; + Item *item = new Item(); + + str = QString::fromLocal8Bit( (char*)e_contact_get(contact, E_CONTACT_FULL_NAME) ); + item->full_name = str; + qDebug() << "fullname is : " << item->full_name; + + str = QString::fromLocal8Bit( (char*)e_contact_get(contact, E_CONTACT_PHONE_MOBILE) ); + item->mobile_number = str; + qDebug() << "mobile_number is : " << item->mobile_number; + + item->group_owner = ""; + item->user_pic_uri = ":/images/male.png"; + + item->uid = QString::fromLocal8Bit( (char*)e_contact_get(contact, E_CONTACT_UID) ); + qDebug() << "uid is : " << item->uid; + + all_contacts_items_db.append( item ); + } + + g_list_free( list ); +#endif + return all_contacts_items_db; +} + +void ContactInterface::getAllContactsFromXml() +{ + //qDebug() << "ContactInterface::getAllContactsFromXml(), Entry"; + + XmlControler::getInstance()->getAllContactItems(); +} + +void ContactInterface::getAllContactsFromXml(const QString &groupname) +{ + //qDebug() << "ContactInterface::getAllContactsFromXml(const QString &groupname), Entry"; + + XmlControler::getInstance()->getAllContactItemsFromGroup(groupname); +} + +QStringList ContactInterface::getAllGroupNames() +{ + //qDebug() << "ContactInterface::getAllGroupNames(), Entry"; + return XmlControler::getInstance()->getAllGroupNames(); +} + +bool ContactInterface::createGroup(const QString &groupname) +{ + return XmlControler::getInstance()->createGroup(groupname); +} + +bool ContactInterface::removeGroup(const QString &groupname) +{ + return XmlControler::getInstance()->removeGroup( groupname ); +} + +bool ContactInterface::addContactToGroup(ItemListPtr items, const QString &groupname) +{ + return XmlControler::getInstance()->createContact( groupname, items ); +} + +bool ContactInterface::removeContactToGroup( ItemListPtr items, const QString &groupname ) +{ + return XmlControler::getInstance()->removeContactFromGroup( items, groupname ); +} + +bool ContactInterface::removeContactToGroup( ItemListPtr items ) +{ + return XmlControler::getInstance()->removeContactFromGroup( items ); +} + + + + + + + + + + + + + + + + + + + + + + diff --git a/sms/contactinterface.h b/sms/contactinterface.h new file mode 100644 index 0000000..dd4b840 --- /dev/null +++ b/sms/contactinterface.h @@ -0,0 +1,75 @@ +#ifndef CONTACTINTERFACE_H +#define CONTACTINTERFACE_H + +#include +#include +#include + +#include "itemobserver.h" + +#ifdef ONLY_FOR_EBOOK + +extern "C" { +#include +#include +#include +#include +} + +#endif + +class ContactInterface : public QObject +{ + Q_OBJECT +public: + static ContactInterface* getInstance(); + ~ContactInterface(); + + bool initEbook(); + + bool updateContactsFromEbookToXml(); + + void setItemObserver( ItemObserver *observer ); + void setItemSelectObserver( ItemSelectObserver *observer ); + + /** + get all contacts from libosso-abook + */ + ItemListPtr getAllContacts(); + /** + get all contacts from xml file + */ + void getAllContactsFromXml(); + void getAllContactsFromXml(const QString &groupname); + + QStringList getAllGroupNames(); + + bool createGroup(const QString &groupname); + bool removeGroup(const QString &groupname); + bool addContactToGroup( ItemListPtr items, const QString &groupname ); + bool removeContactToGroup( ItemListPtr items, const QString &groupname ); + bool removeContactToGroup( ItemListPtr items ); + +private: + static ContactInterface* instance; + ContactInterface(QObject *parent = 0); + +private: + ItemListPtr all_contacts_items; + ItemListPtr all_contacts_items_db; + Item current_contact_item; + QString current_uid; + QString current_fullname; + QString current_mobilenumber; + +#ifdef ONLY_FOR_EBOOK + OssoABookRoster *roster; +#endif + +Q_SIGNALS: + +public Q_SLOTS: + +}; + +#endif // CONTACTINTERFACE_H diff --git a/sms/contactpage.cpp b/sms/contactpage.cpp new file mode 100644 index 0000000..f7dae16 --- /dev/null +++ b/sms/contactpage.cpp @@ -0,0 +1,89 @@ +#include +#include + +#include "contactpage.h" + +ContactPage::ContactPage(QWidget* parent) + : AbstractPage(parent) +{ + //qDebug() << "ContactPage::ContactPage(), Entry"; + + contactWidget = new ContactWidget(this); + contactWidget->sizePolicy().setHorizontalPolicy(QSizePolicy::Maximum); + connect( contactWidget, SIGNAL( validRecycle(bool) ), this, SLOT( onValidRecyele(bool) ) ); + //contactWidget->setGeometry(0,0,600,600); + + QGridLayout *layout = new QGridLayout(this); + + scrollArea = new QScrollArea(this); + scrollArea->setBackgroundRole(QPalette::Light); + + // for test +// QLineEdit *edit = new QLineEdit("Line Edit"); +// edit->setGeometry(0,0,1000,1000); +// scrollArea->setWidget(edit); + // + + + scrollArea->setWidget(contactWidget); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + //scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + //scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + + layout->addWidget(scrollArea, 0, 0, 1, 1); + setLayout(layout); +} + +void ContactPage::updateSize() +{ + //qDebug() << "ContactPage::updateSize()"; + contactWidget->resize( ( scrollArea->width() - scrollArea->verticalScrollBar()->width() - 5 ), + ( scrollArea->height() - scrollArea->horizontalScrollBar()->height() - 5 )); +} + +void ContactPage::initContactWidget() +{ + contactWidget->initContactWidget(); +} + +void ContactPage::update() +{ + //qDebug() << "ContactPage::update()"; + contactWidget->update(); +} + +void ContactPage::refreshContactList() +{ + contactWidget->refreshContactList(); +} + +QString ContactPage::title() +{ + return tr("ContactPage"); +} + +void ContactPage::cleanSelectedContactList() +{ + contactWidget->cleanSelectedContactList(); +} + +QVector* ContactPage::getSelectedContacts() +{ + return contactWidget->getSelectedContacts(); +} + +void ContactPage::onValidRecyele(bool valid) +{ + Q_EMIT validRecycle(valid); +} + +void ContactPage::removeSelectedContact() +{ + contactWidget->removeSelectedContact(); +} + +void ContactPage::setContactItemObserver() +{ + contactWidget->setItemObserver(); +} diff --git a/sms/contactpage.h b/sms/contactpage.h new file mode 100644 index 0000000..169a28a --- /dev/null +++ b/sms/contactpage.h @@ -0,0 +1,29 @@ +#ifndef CONTACTPAGE_H +#define CONTACTPAGE_H + +#include "abstractpage.h" + +class ContactPage : public AbstractPage +{ + Q_OBJECT +public: + ContactPage(QWidget* parent = 0); + + void updateSize(); + void update(); + void initContactWidget(); + void refreshContactList(); + void cleanSelectedContactList(); + void removeSelectedContact(); + void setContactItemObserver(); + QVector* getSelectedContacts(); + QString title(); + +Q_SIGNALS: + void validRecycle(bool valid); + +public Q_SLOTS: + void onValidRecyele(bool valid); +}; + +#endif // CONTACTPAGE_H diff --git a/sms/contactwidget.cpp b/sms/contactwidget.cpp new file mode 100644 index 0000000..6464865 --- /dev/null +++ b/sms/contactwidget.cpp @@ -0,0 +1,451 @@ +#include +#include + +#include "contactwidget.h" +#include "contactwidgetitem.h" +#include "groupwidgetitem.h" +#include "contactinterface.h" +#include "utility.h" + + +ContactWidget::ContactWidget(QWidget *parent) : + QWidget(parent) +{ + //qDebug() << "ContactWidget::ContactWidget(QWidget *parent), Entry"; + contact_items = new QVector; + contact_items_selected = new QVector; +} + +ContactWidget::~ContactWidget() +{ + //qDebug() << "ContactWidget::~ContactWidget(), Entry"; + destroyContactWidget(); +} + +void ContactWidget::setItemObserver() +{ + ContactInterface::getInstance()->setItemObserver( this ); +} + +void ContactWidget::initContactWidget() +{ + //qDebug() << "ContactWidget::initContactWidget(), Entry"; + + setItemObserver(); + ContactInterface::getInstance()->getAllContactsFromXml(); +} + +void ContactWidget::destroyContactWidget() +{ + //qDebug() << ("ContactWidget::destroyContactWidget(), Entry"); + + for (int i = 0; i < contact_items->size(); ++i) + { + contact_items->remove(i); + } + contact_items->clear(); + delete contact_items; + contact_items = NULL; + + for (int i = 0; i < contact_items_selected->size(); ++i) + { + contact_items_selected->remove(i); + } + contact_items_selected->clear(); + delete contact_items_selected; + contact_items_selected = NULL; + + //qDebug() << ("ContactWidget::destroyContactWidget(), Exit"); +} + +int ContactWidget::findGroup(const QString &groupname) +{ + for( int i = 0; i < contact_items->size(); i++ ) + { + if( contact_items->at(i)->m_isGroup ) + { + if( groupname == (static_cast(contact_items->at(i)))->group_name ) + return i; + } + } + return -1; +} + +// from ItemObserver +void ContactWidget::refreshContactsList() +{ + update(); +} + +// from ItemObserver +void ContactWidget::removeContact(Item *contact) +{ + for (int i = 0; i < contact_items->size(); ++i) + { + if( contact->uid == contact_items->at(i)->uid && contact->group_owner == contact_items->at(i)->group_owner ) + { + disconnect( contact_items->at(i), SIGNAL( itemUpdate() ), this, SLOT( update() ) ); + disconnect( contact_items->at(i), SIGNAL( itemSelected( ContactWidgetItem*, bool) ), this, SLOT( contactItemSelected( ContactWidgetItem*, bool ) ) ); + contact_items->remove(i); + break; + } + } +} + +// from ItemObserver +void ContactWidget::removeAllContacts() +{ + for (int i = 0; i < contact_items->size(); ++i) + { + delete contact_items->at(i); + } + contact_items->clear(); +} + +// from ItemObserver +void ContactWidget::addGroup( Item *item ) +{ + //qDebug() << "ContactWidget::addGroup( Item *item ), Entry"; + GroupWidgetItem *group = new GroupWidgetItem(this); + group->group_name = item->group_name; + group->label_group_name->setText( item->group_name ); + + contact_items->append( group ); + + connect( group, SIGNAL( itemUpdate() ), this, SLOT( update() ) ); + connect( group, SIGNAL( itemSelected( ContactWidgetItem*, bool) ), this, SLOT( contactItemSelected( ContactWidgetItem*, bool ) ) ); + + update(); +} + +// from ItemObserver +void ContactWidget::addContact( Item *item ) +{ + addContact(item, -1); +} + +void ContactWidget::addContact(ContactWidgetItem *contact, int index) +{ + //qDebug() << ("ContactWidget::addContact(ContactWidgetItem *contact), Entry"); + //qDebug() << "contact is" << (int)contact; + if( -1 == index || index >= contact_items->size() ) + { + contact_items->append(contact); + }else + { + contact_items->insert( ++index, contact); + } + + connect( contact, SIGNAL( itemUpdate() ), this, SLOT( update() ) ); + connect( contact, SIGNAL( itemSelected( ContactWidgetItem*, bool) ), this, SLOT( contactItemSelected( ContactWidgetItem*, bool ) ) ); +} + +void ContactWidget::addContact( Item *item, int groupindex ) +{ + //qDebug() << "ContactWidget::addContact( Item *item, int groupindex ), Entry"; + if( item->m_isGroup ) + { + GroupWidgetItem *contact = new GroupWidgetItem(this); + + contact->label_group_name->setText( item->group_name ); + contact->group_name = item->group_name; + + addContact( (static_cast(contact)), groupindex ); + }else + { + ContactWidgetItem *contact = new ContactWidgetItem(this); + + contact->label_fullname->setText( item->full_name ); + contact->full_name = item->full_name; + + contact->label_mobile_number->setText( item->mobile_number ); + contact->mobile_number = item->mobile_number; + + contact->group_owner = item->group_owner; + + contact->uid = item->uid; + + addContact(contact, groupindex); + } +} + +// from ItemObserver +void ContactWidget::addContact( Item *item, const QString &groupname ) +{ + int groupindex = findGroup( groupname ); + addContact( item, groupindex); +} + +// from ItemObserver +void ContactWidget::addContact( ItemListPtr items, const QString &groupname ) +{ + //qDebug() << "addContact(const QString &groupname, Item &item)"; + int groupindex = findGroup( groupname ); + + for( int i = 0; i < items.size(); i++ ) + { + Item *item = items.at(i); + + addContact( item, groupindex); + } + update(); +} + +// from ItemObserver +void ContactWidget::addContact( ItemList items, const QString &groupname ) +{ + //qDebug() << "addContact(const QString &groupname, Item &item)"; + int groupindex = findGroup( groupname ); + + for( int i = 0; i < items.size(); i++ ) + { + Item item = items.at(i); + + addContact( &item, groupindex); + } + update(); +} + +void ContactWidget::cleanContactList() +{ + for (int i = 0; i < contact_items->size(); ++i) + { + disconnect( contact_items->at(i), SIGNAL( itemUpdate() ), this, SLOT( update() ) ); + disconnect( contact_items->at(i), SIGNAL( itemSelected( ContactWidgetItem*, bool) ), this, SLOT( contactItemSelected( ContactWidgetItem*, bool ) ) ); + } + contact_items->clear(); +} + +void ContactWidget::cleanSelectedContactList() +{ + contact_items_selected->clear(); + + resetContacts(); + update(); +} + +void ContactWidget::refreshContactList() +{ + //qDebug() << "ContactWidget::refreshContactList()"; + cleanContactList(); + + ContactInterface::getInstance()->getAllContactsFromXml(); + for (int i = 0; i < contact_items->size(); ++i) + { + connect( contact_items->at(i), SIGNAL( itemUpdate() ), this, SLOT( update() ) ); + connect( contact_items->at(i), SIGNAL( itemSelected( ContactWidgetItem*, bool) ), this, SLOT( contactItemSelected( ContactWidgetItem*, bool ) ) ); + } + + update(); +} + +void ContactWidget::resetContacts() +{ + //qDebug() << ("ContactWidget::resetContacts(), Entry"); + for (int i = 0; i < contact_items->size(); ++i) + { + if( contact_items->at(i)->m_isGroup ) + { + (static_cast(contact_items->at(i)))->reSet(); + }else + { + contact_items->at(i)->reSet(); + } + } +} + +void ContactWidget::update() +{ + update( contact_items ); +} + +void ContactWidget::update(QVector *items) +{ + //qDebug() << ("ContactWidget::update(), Entry"); + + bool group_open = false; + int size = items->size(); + //qDebug() << "size is" << items->size(); + int x = 0; + int y = 0; + for( int i = 0; i < size; i++ ) + { + ContactWidgetItem *item = items->at(i); + if( item->m_isGroup ) + { + //qDebug() << "ContactWidget::update(), item is group"; + group_open = false; + x = 0; + y = (static_cast(item))->move(x, y, this); + if( (static_cast(item))->m_isOpenContactList ) + { + x += CONTACT_ITEM_MARGE_WIDTH; + group_open = true; + //qDebug() << "group open is true. x =" << x; + } + (static_cast(item))->showAll(); + y += MARGE_HEIGH; + }else + { + //qDebug() << "ContactWidget::update(), item is contact"; + if( group_open ) + { + y = item->move(x, y, this); + item->showAll(); + y += MARGE_HEIGH; + }else + { + item->hideAll(); + } + } + } + resize( qApp->desktop()->rect().width(), y ); + + //qDebug() << ("ContactWidget::update(), Exit"); +} + +void ContactWidget::paintEvent(QPaintEvent *event) +{ + //qDebug() << "ContactWidget::paintEvent(QPaintEvent *event), Entry"; + + QPainter painter(this); + for (int i = 0; i < contact_items->size(); i++) { + //ContactWidgetItem *item = contact_items->at(i); + //painter.fillRect(0, item->m_x, width(), item->m_height, QBrush(item->m_color)); + //qDebug() << "paint item at:" << i << "m_x=" << item->m_x << "m_height=" << item->m_height; + } + event->accept(); +} + +void ContactWidget::resizeEvent(QResizeEvent *event) +{ + Q_UNUSED( event ) + //qDebug() << ("ContactWidget::resizeEvent(QResizeEvent *event), Entry"); +} + +void ContactWidget::contactItemSelected( ContactWidgetItem *item, bool selected ) +{ + //qDebug() << ("ContactWidget::contactItemSelected( ContactWidgetItem *item, bool selected ), Entry"); + //qDebug() << "item is" << (int)item; + + if( selected ) + { + //qDebug() << "contact_items_selected->append(item)" << item->full_name; + if( item->m_isGroup ) + { + QString str = (static_cast(item))->group_name; + for( int i = 0; i < contact_items->size(); i++ ) + { + if( contact_items->at(i)->group_owner == str ) + { + contact_items_selected->append( contact_items->at(i) ); + contact_items->at(i)->setSelected(true); + } + } + }else + { + contact_items_selected->append(item); + checkGroupPartOfSelected(item); + } + }else + { + if( item->m_isGroup ) + { + QString str = (static_cast(item))->group_name; + for( int i = 0; i < contact_items_selected->size(); i++ ) + { + if( contact_items_selected->at(i)->group_owner == str ) + { + contact_items_selected->remove( i ); + contact_items->at(i)->setSelected(false); + } + } + }else + { + for( int i = 0; i < contact_items_selected->size(); i++ ) + { + if( contact_items_selected->at(i)->uid == item->uid ) + { + contact_items_selected->remove( i ); + checkGroupPartOfSelected( item ); + break; + } + } + } + } + Q_EMIT validRecycle( isValidRecycle() ); +} + +void ContactWidget::checkGroupPartOfSelected(ContactWidgetItem* item) +{ + bool partselected = false; + GroupWidgetItem* group = NULL; + QString str = item->group_owner; + for( int i = 0; i < contact_items->size(); i++ ) + { + if( contact_items->at(i)->m_isGroup && str == (static_cast(contact_items->at(i)))->group_name ) + { + group = (static_cast(contact_items->at(i))); + break; + } + } + + for( int i = 0; i < contact_items_selected->size(); i++ ) + { + if( group->group_name == contact_items_selected->at(i)->group_owner ) + { + partselected = true; + break; + } + } + if( partselected ) + { + group->partOfAllSeleted(); + }else + { + group->setSelected(false); + } +} + +QVector* ContactWidget::getSelectedContacts() +{ + return contact_items_selected; +} + +bool ContactWidget::isValidRecycle() +{ + if( contact_items_selected->size() > 0 ) + return true; + return false; +} + +void ContactWidget::removeSelectedContact() +{ + ItemListPtr list; + for( int i = 0; i < contact_items_selected->size(); i++ ) + { + ContactWidgetItem *contact = contact_items_selected->at(i); + if( contact->m_isGroup ) + { + continue; + } + + Item *item = new Item(); + item->uid = contact->uid; + item->full_name = contact->full_name; + item->mobile_number = contact->mobile_number; + item->group_owner = contact->group_owner; + + list.append(item); + } + ContactInterface::getInstance()->removeContactToGroup(list); + contact_items_selected->clear(); +} + + + + + + + + diff --git a/sms/contactwidget.h b/sms/contactwidget.h new file mode 100644 index 0000000..9a27ed4 --- /dev/null +++ b/sms/contactwidget.h @@ -0,0 +1,69 @@ +#ifndef CONTACTWIDGET_H +#define CONTACTWIDGET_H + +#include +#include + +#include +#include "itemobserver.h" + +class ContactWidget : public QWidget, public ItemObserver +{ + Q_OBJECT +public: + ContactWidget(QWidget *parent = 0); + ~ContactWidget(); + + // from ItemObserver + void addGroup(Item *item); + void addContact( Item *item ); + void addContact( Item *item, const QString &groupname ); + void addContact( ItemList items, const QString &groupname ); + void addContact( ItemListPtr items, const QString &groupname ); + void removeContact(Item *contact); + void removeAllContacts(); + void refreshContactsList(); + + void addContact( Item *item, int groupindex ); + void addContact(ContactWidgetItem *contact, int index); + void removeSelectedContact(); + + virtual void initContactWidget(); + void setItemObserver(); + + QVector* getSelectedContacts(); + + void cleanSelectedContactList(); + + bool isValidRecycle(); + +protected: + void resizeEvent(QResizeEvent *event); + void paintEvent(QPaintEvent *event); + void update( QVector* items ); + + void cleanContactList(); + + virtual void destroyContactWidget(); + + int findGroup(const QString &groupname); + + void checkGroupPartOfSelected(ContactWidgetItem* item); + +protected: + QVector *contact_items; + QVector *contact_items_selected; + + int last_height; + +Q_SIGNALS: + void validRecycle(bool valid); + +public Q_SLOTS: + void update(); + void contactItemSelected(ContactWidgetItem *item, bool selected); + void refreshContactList(); + void resetContacts(); +}; + +#endif // CONTACTWIDGET_H diff --git a/sms/contactwidgetitem.cpp b/sms/contactwidgetitem.cpp new file mode 100644 index 0000000..5ddb673 --- /dev/null +++ b/sms/contactwidgetitem.cpp @@ -0,0 +1,129 @@ +#include + +#include "contactwidgetitem.h" +#include "utility.h" + + +ContactWidgetItem::ContactWidgetItem(QObject *parent) : QObject(parent) +{ + btn_selected = new QToolButton(); + btn_selected->setText(""); + btn_selected->setIcon( Utility::getToolButtonIcon(":/images/unselect.png", true) ); + btn_selected->setToolButtonStyle(Qt::ToolButtonIconOnly); + btn_selected->setAutoRaise(true); + + user_pic_uri = ":/images/male.png"; + label_fullname = new QLabel(); + label_mobile_number = new QLabel(); + label_user_pic = new QLabel(); + label_user_pic->setPixmap(Utility::getIconPixmap(user_pic_uri)); + + m_isSelected = false; + m_isGroup = false; + m_x = 0; + m_height = 0; + m_color.setRgb(255,0,0); + + connect(btn_selected, SIGNAL( clicked() ), this, SLOT( btn_selected_clicked() ) ); + + //qDebug() << "ContactWidgetItem" << (int)this; +} + +ContactWidgetItem::~ContactWidgetItem() +{ + //qDebug() << "~ContactWidgetItem" << (int)this; + delete btn_selected; + delete label_fullname; + delete label_mobile_number; + delete label_user_pic; +} + +void ContactWidgetItem::loadUserPic() +{ + QPixmap pixmap(user_pic_uri); + if (!pixmap.isNull()) { + label_user_pic->setPixmap(pixmap.scaled(ICON_SIZE, ICON_SIZE)); + } + label_user_pic->resize(ICON_SIZE, ICON_SIZE); +} + +void ContactWidgetItem::reSet() +{ + m_isSelected = false; + m_isGroup = false; + btn_selected->setIcon( Utility::getToolButtonIcon(":/images/unselect.png", true) ); +} + +int ContactWidgetItem::move(int x, int y, QWidget *parent) +{ + //qDebug() << "ContactWidgetItem::move(int x, int y), Entry"; + //qDebug() << "x=" << x << "y=" << y << "screen_width=" << QApplication::desktop()->width(); + //int screen_width = QApplication::desktop()->width(); + m_x = x; + m_height = 0; + int _y = y; + + btn_selected->setParent(parent); + btn_selected->move( ( x + BTN_SELECTED_OFFSET_X ), ( _y + BTN_SELECTED_OFFSET_Y ) ); + //qDebug() << "btn_selected x=" << ( x + BTN_SELECTED_OFFSET_X ) << "btn_selected y=" << ( _y + BTN_SELECTED_OFFSET_Y ); + + label_user_pic->setParent(parent); + label_user_pic->move( ( x + USER_PIC_OFFSET_X ), ( _y + USER_PIC_OFFSET_Y ) ); + //qDebug() << "label_user_pic x=" << ( x + USER_PIC_OFFSET_X ) << "label_user_pic y=" << ( _y + USER_PIC_OFFSET_Y ); + + label_fullname->setParent(parent); + label_fullname->move( ( x + FULL_NAME_OFFSET_X ), ( _y + FULL_NAME_OFFSET_Y ) ); + //qDebug() << "label_fullname x=" << ( x + FULL_NAME_OFFSET_X ) << "label_fullname y=" << ( _y + FULL_NAME_OFFSET_Y ); + + label_mobile_number->setParent(parent); + label_mobile_number->move( ( x + MOBILE_NUMBER_OFFSET_X ), ( _y + MOBILE_NUMBER_OFFSET_Y ) ); + //qDebug() << "label_mobile_number x=" << ( x + MOBILE_NUMBER_OFFSET_X ) << "label_mobile_number y=" << ( _y + MOBILE_NUMBER_OFFSET_Y ); + + m_height = y + ITEM_HEIGHT; + //qDebug() << "m_height=" << m_height; + //qDebug() << "ContactWidgetItem::move(int x, int y), Exit"; + + return m_height; +} + +void ContactWidgetItem::showAll() +{ + btn_selected->show(); + label_fullname->show(); + label_mobile_number->show(); + label_user_pic->show(); +} + +void ContactWidgetItem::hideAll() +{ + btn_selected->hide(); + label_fullname->hide(); + label_mobile_number->hide(); + label_user_pic->hide(); +} + +void ContactWidgetItem::btn_selected_clicked() +{ + m_isSelected = !m_isSelected; + if( m_isSelected ) + { + btn_selected->setIcon( Utility::getToolButtonIcon(":/images/select.png", true) ); + }else + { + btn_selected->setIcon( Utility::getToolButtonIcon(":/images/unselect.png", true) ); + } + Q_EMIT itemSelected( this, m_isSelected ); + //qDebug() << "ContactWidgetItem::btn_selected_clicked(), Exit"; +} + +void ContactWidgetItem::setSelected(bool selected) +{ + if( selected ) + { + btn_selected->setIcon( Utility::getToolButtonIcon(":/images/select.png", true) ); + }else + { + btn_selected->setIcon( Utility::getToolButtonIcon(":/images/unselect.png", true) ); + } + //qDebug() << "ContactWidgetItem::setSelected(bool selected), Exit"; +} diff --git a/sms/contactwidgetitem.h b/sms/contactwidgetitem.h new file mode 100644 index 0000000..f589413 --- /dev/null +++ b/sms/contactwidgetitem.h @@ -0,0 +1,78 @@ +#ifndef CONTACTWIDGETITEM_H +#define CONTACTWIDGETITEM_H + +#include +#include +#include "item.h" + + +const int ICON_SIZE = 38; +const int MARGE_HEIGH = 4; +const int CONTACT_ITEM_MARGE_WIDTH = 50; +const int BTN_SELECTED_OFFSET_X = 10; +const int BTN_SELECTED_OFFSET_Y = 5; +const int USER_PIC_OFFSET_X = BTN_SELECTED_OFFSET_X + 80; +const int USER_PIC_OFFSET_Y = BTN_SELECTED_OFFSET_Y + 20; +const int FULL_NAME_OFFSET_X = USER_PIC_OFFSET_X + 50; +const int FULL_NAME_OFFSET_Y = 5; +const int MOBILE_NUMBER_OFFSET_X = FULL_NAME_OFFSET_X; +const int MOBILE_NUMBER_OFFSET_Y = FULL_NAME_OFFSET_Y + 30; + +const int BTN_SELECTED_MARGE_WIDTH = 130; + +// GroupWidgetItem +const int BTN_OPEN_GROUP_OFFSET_X = 10; +const int BTN_OPEN_GROUP_OFFSET_Y = 5; +const int BTN_GROUP_SELECTED_OFFSET_X = BTN_OPEN_GROUP_OFFSET_X + 60; +const int BTN_GROUP_SELECTED_OFFSET_Y = 5; +const int GROUP_USER_PIC_OFFSET_X = BTN_GROUP_SELECTED_OFFSET_X + 80; +const int GROUP_USER_PIC_OFFSET_Y = BTN_OPEN_GROUP_OFFSET_Y + 25; +const int GROUP_NAME_OFFSET_X = GROUP_USER_PIC_OFFSET_X + 50; +const int GROUP_NAME_OFFSET_Y = 20; + +const int ITEM_HEIGHT = 60; + +const int BTN_TOOL_WIDTH = 60; +const int BTN_TOOL_HEIGHT = 60; + +class ContactWidgetItem : public QObject +{ + Q_OBJECT +public: + ContactWidgetItem(QObject *parent = 0); + ~ContactWidgetItem(); + + void loadUserPic(); + int move(int x, int y, QWidget *parent = 0); + void showAll(); + void hideAll(); + void reSet(); + void setData( Item &item ); + void setSelected(bool selected); + + QToolButton *btn_selected; + QLabel *label_fullname; + QLabel *label_mobile_number; + QLabel *label_user_pic; + QString user_pic_uri; + + QString full_name; + QString mobile_number; + QString group_owner; + QString uid; + + int m_x; + int m_height; + bool m_isSelected; + bool m_isGroup; + QColor m_color; + +Q_SIGNALS: + void itemUpdate(); + void itemSelected( ContactWidgetItem *item, bool selected ); + +public Q_SLOTS: + void btn_selected_clicked(); +}; + +#endif // CONTACTWIDGETITEM_H diff --git a/sms/data/26x26/groupsms.png b/sms/data/26x26/groupsms.png new file mode 100644 index 0000000000000000000000000000000000000000..ac1d10176656a6b25a46da984321799b9789ffd8 GIT binary patch literal 826 zcmV-A1I7G_P)H@#sCi%1Zp=!N_Aib7#r@wAxp4A0D+ zvF3VzaCm0^|KIcfp7WpQoM)tz)JOZk{$=e0`^Wm@)tH?&K09sOuzdEc=#Pz=ujlaK zfOw^+v1?eeY!(xEzCYdrnrXMja-k=O6uBVh8fCLsF@&`Xk7sXJMM}v`yY&Pf!PU3} zb2x+&uTM@jj%3gdwDP?#gqWstd_8t!Io9JMe2(wY3Nh`iR`f1TX9!$~t@x&b)Mi_$ zyXp|r7x)Ad83G$}oqgRE(QVuBxI7#8PEIu*tF>(vzC*nw_lB50t9e@3k-z?c)gh*{ z^!`TIWXMtcft&T}sXj;*t81-JyT8RMTv{dSpgZv*)@8`?nRaVwp6S?tqOh}U7MEnt zN_>Qya64YX-IZ}k?WXr8ry41LF0nJW6CT3&0MJrAig)mHh^aoAm+}NbF*|J|uEMfB zt{89~&qW967aYmgGGFVX#IH+mG2T+^=ZLxz+yBG!a*nhN5)`ggU!vi{PT&TXnJ$1-D zP=@`52KFof@&Pu5n11R{^t7(7vRM@P6PNu@$O+sSVwxM29P~b_v}ObdtUOn}bAdLE z0HL!V*DVCJc|;IwTnK2`ic0*mD4={2U)vW>coR41oX($~H^}_99moA4re{M;Cqqof za33Q6uK7aQEUp^`RJH94Ud7rF)8V=y#B?^q^crr($F(5(=G)j1Vmdku&<9H)rZ;d4P7dt<807*qoM6N<$ Ef(f04VgLXD literal 0 HcmV?d00001 diff --git a/sms/data/40x40/groupsms.png b/sms/data/40x40/groupsms.png new file mode 100644 index 0000000000000000000000000000000000000000..0f8a5afc08321a08d1090f3417db3922fd4acbed GIT binary patch literal 1182 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 evHJ5Y<^B)+frzUwUI+sK0000 + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/sms/data/maemo/groupsms.xpm b/sms/data/maemo/groupsms.xpm new file mode 100644 index 0000000..c3dfdb8 --- /dev/null +++ b/sms/data/maemo/groupsms.xpm @@ -0,0 +1,324 @@ +/* XPM */ +static char * qt4_xpm[] = { +"26 26 295 2", +" c None", +". c #85B623", +"+ c #425911", +"@ c #99D029", +"# c #9ED32C", +"$ c #98CF28", +"% c #92C826", +"& c #85B723", +"* c #7EAC21", +"= c #455D12", +"- c #9ED42E", +"; c #CEF24F", +"> c #CBF14B", +", c #C3EB43", +"' c #B8E43B", +") c #ADDD34", +"! c #A2D62E", +"~ c #95CB28", +"{ c #88BA24", +"] c #80AF22", +"^ c #36490E", +"/ c #A4D735", +"( c #CDF05D", +"_ c #CDF05B", +": c #CDF158", +"< c #CEF156", +"[ c #CEF253", +"} c #CFF350", +"| c #CDF14B", +"1 c #B7E33B", +"2 c #AADC33", +"3 c #9CD02B", +"4 c #8ABD25", +"5 c #82B222", +"6 c #A6D83B", +"7 c #CAED66", +"8 c #CBED64", +"9 c #CBEE63", +"0 c #CCEE62", +"a c #CEF062", +"b c #D1F163", +"c c #D3F363", +"d c #D4F361", +"e c #D3F35D", +"f c #D1F357", +"g c #CFF250", +"h c #C8EF49", +"i c #BCE73F", +"j c #ADDD35", +"k c #84B424", +"l c #A6D73E", +"m c #C7EA6B", +"n c #C9EB6A", +"o c #CCED6C", +"p c #D2EF71", +"q c #CDE771", +"r c #B4CE67", +"s c #AAC661", +"t c #AFCB60", +"u c #C3E066", +"v c #D3F167", +"w c #CEF05F", +"x c #CCEF5A", +"y c #C4E653", +"z c #5C6C33", +"A c #3B2A55", +"B c #9360E4", +"C c #8858D0", +"D c #A5D63E", +"E c #C2E66C", +"F c #C4E76C", +"G c #C8E970", +"H c #D0ED76", +"I c #B7CC74", +"J c #92A56E", +"K c #93A670", +"L c #93A86E", +"M c #93A96B", +"N c #91A967", +"O c #A6BF68", +"P c #CDEB6E", +"Q c #C1E062", +"R c #889A4E", +"S c #3F3152", +"T c #8861C7", +"U c #A675F3", +"V c #9367D7", +"W c #A3D53D", +"X c #BEE36B", +"Y c #C0E46D", +"Z c #C9E973", +"` c #B9CE77", +" . c #929D7D", +".. c #95A080", +"+. c #9BAA77", +"@. c #B3C877", +"#. c #B6CA77", +"$. c #A0B175", +"%. c #94A378", +"&. c #A1B471", +"*. c #9FB75C", +"=. c #534660", +"-. c #785CA4", +";. c #9772D2", +">. c #AB82EC", +",. c #9E78D9", +"'. c #A1D43B", +"). c #B8DF65", +"!. c #BEE269", +"~. c #C8E573", +"{. c #939C80", +"]. c #9AA08F", +"^. c #A0AF7A", +"/. c #C9E674", +"(. c #C7E771", +"_. c #CBEA75", +":. c #CFE77A", +"<. c #AAB97B", +"[. c #858D77", +"}. c #645C69", +"|. c #705794", +"1. c #B593EB", +"2. c #432F65", +"3. c #B694EC", +"4. c #A98ADA", +"5. c #9ED337", +"6. c #B2DC5C", +"7. c #BBE163", +"8. c #AEC36E", +"9. c #989B93", +"0. c #939887", +"a. c #C0DE6C", +"b. c #BBE164", +"c. c #BEE267", +"d. c #B6CE70", +"e. c #8C9478", +"f. c #BCD36F", +"g. c #6B715D", +"h. c #442E66", +"i. c #AC92D6", +"j. c #AE95D8", +"k. c #766099", +"l. c #C7ACF3", +"m. c #C0A7EA", +"n. c #9BD131", +"o. c #ABD84F", +"p. c #B7DF59", +"q. c #95A467", +"r. c #8E8F8C", +"s. c #93A06F", +"t. c #B9E05B", +"u. c #B5DE58", +"v. c #B3D062", +"w. c #878B7D", +"x. c #848781", +"y. c #99B05A", +"z. c #646F42", +"A. c #271449", +"B. c #B8A3DB", +"C. c #D5C1F7", +"D. c #D4C0F6", +"E. c #D7C3F9", +"F. c #D3BFF4", +"G. c #98CF2A", +"H. c #A3D53E", +"I. c #B2DC49", +"J. c #808C57", +"K. c #7B7B7B", +"L. c #8C9D59", +"M. c #B3DD4A", +"N. c #AED24D", +"O. c #727763", +"P. c #808180", +"Q. c #879262", +"R. c #ACD149", +"S. c #71813E", +"T. c #363635", +"U. c #434E2A", +"V. c #312446", +"W. c #38205F", +"X. c #DCCAF6", +"Y. c #C8BBDE", +"Z. c #97CF28", +"`. c #9BD12B", +" + c #AAD937", +".+ c #758542", +"++ c #636363", +"@+ c #768547", +"#+ c #B1D83F", +"$+ c #5B633F", +"%+ c #5F5F5F", +"&+ c #626262", +"*+ c #5C5D5A", +"=+ c #94AF3F", +"-+ c #7A8B40", +";+ c #4E5143", +">+ c #6B8C20", +",+ c #38441E", +"'+ c #351F58", +")+ c #C3B6D6", +"!+ c #A6D733", +"~+ c #869E3B", +"{+ c #515151", +"]+ c #515543", +"^+ c #B4D544", +"/+ c #545C39", +"(+ c #575E3E", +"_+ c #97AF43", +":+ c #4E4F4B", +"<+ c #505247", +"[+ c #595D46", +"}+ c #616D40", +"|+ c #8FBE29", +"1+ c #668B1B", +"2+ c #98D028", +"3+ c #A0D42F", +"4+ c #AAD13C", +"5+ c #41423B", +"6+ c #454545", +"7+ c #75853B", +"8+ c #B2D443", +"9+ c #A5CA3B", +"0+ c #B5DF3E", +"a+ c #8CA13F", +"b+ c #464645", +"c+ c #474747", +"d+ c #849E3A", +"e+ c #9ACF2B", +"f+ c #7FAF22", +"g+ c #36480E", +"h+ c #AAD936", +"i+ c #758935", +"j+ c #373737", +"k+ c #353534", +"l+ c #5A6630", +"m+ c #90AC38", +"n+ c #93B038", +"o+ c #647131", +"p+ c #323332", +"q+ c #313131", +"r+ c #526227", +"s+ c #9CD12B", +"t+ c #84B523", +"u+ c #35470E", +"v+ c #98D029", +"w+ c #9FD32E", +"x+ c #AED93A", +"y+ c #5D6C2C", +"z+ c #252525", +"A+ c #202020", +"B+ c #1E1E1E", +"C+ c #1D1D1D", +"D+ c #363C21", +"E+ c #4D5924", +"F+ c #759429", +"G+ c #9AD02A", +"H+ c #33450D", +"I+ c #ADDA39", +"J+ c #84A131", +"K+ c #46521F", +"L+ c #2D3417", +"M+ c #363F19", +"N+ c #677D26", +"O+ c #A6D137", +"P+ c #A4D333", +"Q+ c #9ED22D", +"R+ c #81B122", +"S+ c #33440D", +"T+ c #9DD22C", +"U+ c #A4D631", +"V+ c #ACDA38", +"W+ c #A9D835", +"X+ c #A1D430", +"Y+ c #9BD12A", +"Z+ c #7AA820", +"`+ c #9AD12A", +" @ c #8EC326", +".@ c #77A21F", +"+@ c #5D8019", +"@@ c #2A380B", +"#@ c #96CE28", +"$@ c #8BBF25", +"%@ c #719A1E", +"&@ c #547316", +"*@ c #2F410D", +"=@ c #405711", +"-@ c #95CD28", +";@ c #87B924", +">@ c #6B931C", +",@ c #4E6B15", +"'@ c #28370B", +")@ c #2E3F0C", +"!@ c #678D1B", +"~@ c #486213", +"{@ c #202C08", +" ", +" . ", +" + @ # $ % & * ", +" = - ; > , ' ) ! ~ { ] ", +" ^ = / ( _ : < [ } | , 1 2 3 4 5 ", +" ^ = 6 7 8 9 0 a b c d e f g h i j k ", +" ^ = l m m n o p q r s t u v w x y z A B C ", +" ^ = D E F G H I J K L M N O P Q R S T U V ", +" ^ = W X Y Z ` ...+.@.#.$.%.&.*.=.-.;.>.,. ", +" ^ = '.).!.~.{.].^./.(._.:.<.[.}.|.1.2.3.4. ", +" ^ = 5.6.7.8.9.0.a.b.c.d.e.f.g.h.i.j.k.l.m. ", +" ^ = n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.E.F. ", +" ^ = G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y. ", +" ^ = Z.`. +.+++@+#+$+%+&+*+=+-+;+>+,+'+)+ ", +" ^ = Z.@ !+~+{+]+^+/+(+_+:+<+[+}+|+1+ ", +" ^ = Z.2+3+4+5+6+7+8+9+0+a+b+c+d+e+f+ ", +" g+= Z.Z.`.h+i+j+k+l+m+n+o+p+q+r+s+t+ ", +" u+= Z.Z.v+w+x+y+z+z+A+B+C+D+E+F+G+t+ ", +" H+= Z.Z.Z.@ 3+I+J+K+L+M+N+O+P+Q+Z.R+ ", +" S+= Z.Z.Z.Z.@ T+U+h+V+W+X+Y+@ Z.Z.Z+ ", +" S+= Z.Z.Z.Z.Z.$ @ G+`+@ $ Z. @.@+@ ", +" @@= Z.Z.Z.Z.Z.Z.Z.#@$@%@&@*@ ", +" =@Z.Z.Z.-@;@>@,@'@ ", +" )@t+!@~@{@ ", +" ", +" "}; diff --git a/sms/groupsms.desktop b/sms/groupsms.desktop new file mode 100644 index 0000000..b0c67eb --- /dev/null +++ b/sms/groupsms.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Encoding=UTF-8 +Version=0.1 +Type=Application +Name=Group SMS +Exec=/usr/bin/groupsms +Icon=groupsms +StartupWMClass=groupsms +X-Window-Icon=groupsms +X-HildonDesk-ShowInToolbar=true +X-Osso-Type=application/x-executable +Terminal=false + diff --git a/sms/groupsms.qrc b/sms/groupsms.qrc new file mode 100644 index 0000000..430b3cc --- /dev/null +++ b/sms/groupsms.qrc @@ -0,0 +1,16 @@ + + + images/addcontacttogroup.png + images/female.png + images/male.png + images/newgroup.png + images/editgroup.png + images/plus.png + images/sub.png + images/recycle.png + images/select.png + images/allselect.png + images/unselect.png + images/partselect.png + + diff --git a/sms/groupwidgetitem.cpp b/sms/groupwidgetitem.cpp new file mode 100644 index 0000000..e7f3e67 --- /dev/null +++ b/sms/groupwidgetitem.cpp @@ -0,0 +1,110 @@ +#include "groupwidgetitem.h" +#include "utility.h" + +GroupWidgetItem::GroupWidgetItem(QObject *parent) + : ContactWidgetItem(parent) +{ + btn_selected = new QToolButton(); + btn_selected->setText(""); + btn_selected->setIcon( Utility::getToolButtonIcon(":/images/unselect.png", true) ); + btn_selected->setToolButtonStyle(Qt::ToolButtonIconOnly); + btn_selected->setAutoRaise(true); + btn_selected->adjustSize(); + + btn_open_group = new QToolButton(); + btn_open_group->setText(""); + btn_open_group->setIcon( Utility::getToolButtonIcon(":/images/plus.png", true) ); + btn_open_group->setToolButtonStyle(Qt::ToolButtonIconOnly); + btn_open_group->setAutoRaise(true); + btn_open_group->adjustSize(); + + label_group_name = new QLabel(); + + + m_isSelected = false; + m_isOpenContactList = false; + m_isGroup = true; + + m_x = 0; + m_height = 0; + + connect( btn_open_group, SIGNAL( clicked() ), this, SLOT( btn_open_group_clicked() ) ); + connect(btn_selected, SIGNAL( clicked() ), this, SLOT( btn_selected_clicked() ) ); + //qDebug() << "GroupWidgetItem::GroupWidgetItem(QObject *parent), Exit" << (int)this; +} + +GroupWidgetItem::~GroupWidgetItem() +{ + //qDebug() << "GroupWidgetItem::~GroupWidgetItem()" << (int)this; + delete btn_open_group; + delete label_group_name; +} + +int GroupWidgetItem::move(int x, int y, QWidget *parent) +{ + //qDebug() << "GroupWidgetItem::move(int x, int y), Entry"; + m_x = x; + m_height = 0; + int _y = y; + + btn_open_group->setParent(parent); + btn_open_group->move( ( x + BTN_OPEN_GROUP_OFFSET_X ), ( _y + BTN_OPEN_GROUP_OFFSET_Y ) ); + //qDebug() << "btn_open_group x=" << ( x + BTN_OPEN_GROUP_OFFSET_X ) << "btn_open_group y=" << ( _y + BTN_OPEN_GROUP_OFFSET_Y ); + + btn_selected->setParent(parent); + btn_selected->move( ( x + BTN_GROUP_SELECTED_OFFSET_X ), ( _y + BTN_GROUP_SELECTED_OFFSET_Y ) ); + //qDebug() << "btn_selected x=" << ( x + BTN_GROUP_SELECTED_OFFSET_X ) << "btn_selected y=" << ( _y + BTN_GROUP_SELECTED_OFFSET_Y ); + + label_user_pic->setParent(parent); + label_user_pic->move( ( x + GROUP_USER_PIC_OFFSET_X ), ( _y + GROUP_USER_PIC_OFFSET_Y ) ); + //qDebug() << "label_user_pic x=" << ( x + GROUP_USER_PIC_OFFSET_X ) << "label_user_pic y=" << ( _y + GROUP_USER_PIC_OFFSET_Y ); + + label_group_name->setParent(parent); + label_group_name->move( ( x + GROUP_NAME_OFFSET_X ), ( _y + GROUP_NAME_OFFSET_Y ) ); + //qDebug() << "label_fullname x=" << ( x + GROUP_NAME_OFFSET_X ) << "label_fullname y=" << ( _y + GROUP_NAME_OFFSET_Y ); + + m_height = y + ITEM_HEIGHT; + + showAll(); + + //qDebug() << "m_height=" << m_height; + //qDebug() << "GroupWidgetItem::move(int x, int y), Exit"; + + return m_height; +} + +void GroupWidgetItem::reSet() +{ + m_isSelected = false; + m_isGroup = true; + btn_selected->setIcon( Utility::getToolButtonIcon(":/images/unselect.png", true) ); + btn_open_group->setIcon( Utility::getToolButtonIcon(":/images/plus.png", true) ); +} + +void GroupWidgetItem::btn_open_group_clicked() +{ + //qDebug() << "GroupWidgetItem::btn_open_group_clicked(), Entry"; + m_isOpenContactList = !m_isOpenContactList; + if( m_isOpenContactList ) + { + btn_open_group->setIcon( Utility::getToolButtonIcon(":/images/sub.png", true) ); + }else + { + btn_open_group->setIcon( Utility::getToolButtonIcon(":/images/plus.png", true) ); + } + Q_EMIT itemUpdate(); + //qDebug() << "GroupWidgetItem::btn_open_group_clicked(), Exit" << (int)this; +} + +void GroupWidgetItem::showAll() +{ + btn_selected->show(); + btn_open_group->show(); + label_group_name->show(); + label_user_pic->show(); +} + +void GroupWidgetItem::partOfAllSeleted() +{ + btn_selected->setIcon( Utility::getToolButtonIcon(":/images/partselect.png", true) ); +} diff --git a/sms/groupwidgetitem.h b/sms/groupwidgetitem.h new file mode 100644 index 0000000..48b8ff9 --- /dev/null +++ b/sms/groupwidgetitem.h @@ -0,0 +1,31 @@ +#ifndef GROUPWIDGETITEM_H +#define GROUPWIDGETITEM_H + +#include +#include + +#include "contactwidgetitem.h" + +class GroupWidgetItem : public ContactWidgetItem +{ + Q_OBJECT +public: + GroupWidgetItem(QObject *parent = 0); + ~GroupWidgetItem(); + + int move(int x, int y, QWidget *parent = 0); + void showAll(); + void reSet(); + void partOfAllSeleted(); + + QToolButton *btn_open_group; + QLabel *label_group_name; + QString group_name; + + bool m_isOpenContactList; + +public Q_SLOTS: + void btn_open_group_clicked(); +}; + +#endif // GROUPWIDGETITEM_H diff --git a/sms/images/addcontacttogroup.png b/sms/images/addcontacttogroup.png new file mode 100644 index 0000000000000000000000000000000000000000..1bc6a2077eec18662afd1ce6d83665a2d25b2aea GIT binary patch literal 3592 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 BjWqxO literal 0 HcmV?d00001 diff --git a/sms/images/partselect.png b/sms/images/partselect.png new file mode 100644 index 0000000000000000000000000000000000000000..f7299847fe25536689e14a0600a6c6e57371fb48 GIT binary patch literal 3671 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 +#include + +class Item +{ +public: + QString group_name; + QString full_name; + QString mobile_number; + QString group_owner; + QString user_pic_uri; + QString uid; + + bool m_isSelected; + bool m_isGroup; + bool m_isOpenContactList; +}; +typedef QList ItemList; +typedef QList ItemListPtr; +Q_DECLARE_METATYPE(Item) +Q_DECLARE_METATYPE(ItemList) +Q_DECLARE_METATYPE(ItemListPtr) + +#endif // ITEM_H diff --git a/sms/itemobserver.h b/sms/itemobserver.h new file mode 100644 index 0000000..1cea9f9 --- /dev/null +++ b/sms/itemobserver.h @@ -0,0 +1,26 @@ +#ifndef ITEMOBSERVER_H +#define ITEMOBSERVER_H + +#include +#include "item.h" + +class ItemObserver +{ +public: + virtual void addGroup( Item *item ) = 0; + virtual void addContact( ItemList items, const QString &groupname ) = 0; + virtual void addContact( ItemListPtr items, const QString &groupname ) = 0; + virtual void addContact( Item *item, const QString &groupname ) = 0; + virtual void addContact( Item *item) = 0; + virtual void removeContact(Item *contact) = 0; + virtual void removeAllContacts() = 0; + virtual void refreshContactsList() = 0; +}; + +class ItemSelectObserver +{ +public: + virtual void getGroupContacts( ItemListPtr items ) = 0; +}; + +#endif // ITEMOBSERVER_H diff --git a/sms/main.cpp b/sms/main.cpp new file mode 100644 index 0000000..5b0df3d --- /dev/null +++ b/sms/main.cpp @@ -0,0 +1,56 @@ +#include +#include "mainwindow.h" +#include "contactinterface.h" +#include "common.h" +#include "item.h" + +#ifdef ONLY_FOR_EBOOK + +#include + +#endif + +static void registerTypes() +{ + static bool registered = false; + if( !registered ) + { + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + registered = true; + } +} + +int main(int argc, char *argv[]) +{ +#ifdef ONLY_FOR_EBOOK +// gtk_init( &argc, &argv ); + + /* Initialize the osso context */ + osso_context_t *osso_context = osso_initialize( "GroupSMS", "1.0", TRUE, NULL ); + if( !osso_context ) + { + //qDebug() << "Couldn't initialize osso context"; + return false; + } + + /* Initialize abook, which also initializes all the + * libraries it needs (GTK+, Galago, Gnome-VFS) */ + osso_abook_init( &argc, &argv, osso_context); +#endif + + QDir dir( QDir::homePath() ); + dir.mkdir( HOME_DIR ); + + QApplication a(argc, argv); + + registerTypes(); + +#if defined(Q_WS_S60) + MainWindow::getInstance()->show(); +#else + MainWindow::getInstance()->show(); +#endif + return a.exec(); +} diff --git a/sms/mainwindow.cpp b/sms/mainwindow.cpp new file mode 100644 index 0000000..4471e01 --- /dev/null +++ b/sms/mainwindow.cpp @@ -0,0 +1,303 @@ +#include "mainwindow.h" +#include "newgroupdialog.h" +#include "addcontacttogroup.h" +#include "contactinterface.h" +#include "common.h" +#include "ui_mainwindow.h" +#include "utility.h" + +#ifdef ONLY_FOR_TELEPATHYQT4 +#include "tpsession/tpsession.h" +#endif + +MainWindow* MainWindow::instance = 0; + +MainWindow* MainWindow::getInstance() +{ + if (!instance) { + instance = new MainWindow(); + } + return instance; +} + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + instance = this; + + ui->setupUi(this); + init(); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::changeEvent(QEvent *e) +{ + //qDebug() << "MainWindow::changeEvent(QEvent *e)"; + + QMainWindow::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void MainWindow::init() +{ + //qDebug() << "MainWindow::init()"; + + ui->btn_send_message->setDisabled(true); + ui->textEdit_phone_numbers->setReadOnly(true); + connect( ui->textEdit_message, SIGNAL( textChanged() ), this, SLOT( send_message_enabled() ) ); + connect( ui->textEdit_phone_numbers, SIGNAL( textChanged() ), this, SLOT( send_message_enabled() ) ); + connect( ui->btn_send_to, SIGNAL( clicked() ), this, SLOT( send_to_clicked() ) ); + connect( ui->btn_send_message, SIGNAL( clicked() ), this, SLOT( send_message_clicked() ) ); + connect( ui->btn_cancel, SIGNAL( clicked() ), this, SLOT( cancel_clicked() ) ); + + hbox_layout = new QHBoxLayout(this); + + btn_new_group = new QToolButton(this); + btn_new_group->setToolTip( tr("new group") ); + btn_new_group->setText(""); + btn_new_group->setIcon(Utility::getToolButtonIcon(":/images/newgroup.png", true)); + btn_new_group->setToolButtonStyle(Qt::ToolButtonIconOnly); + btn_new_group->setAutoRaise(true); + btn_new_group->show(); + connect( btn_new_group, SIGNAL( clicked() ), this, SLOT( new_group() ) ); + + + btn_add_contact_to_group = new QToolButton(this); + btn_add_contact_to_group->setToolTip( tr("add contact to group") ); + btn_add_contact_to_group->setText(""); + btn_add_contact_to_group->setIcon(Utility::getToolButtonIcon(":/images/addcontacttogroup.png", true)); + btn_add_contact_to_group->setToolButtonStyle(Qt::ToolButtonIconOnly); + btn_add_contact_to_group->setAutoRaise(true); + btn_add_contact_to_group->show(); + connect( btn_add_contact_to_group, SIGNAL( clicked() ), this, SLOT( add_contact_to_group() ) ); + + btn_delete_contact = new QToolButton(this); + btn_delete_contact->setToolTip( tr("delete contacts") ); + btn_delete_contact->setText(""); + btn_delete_contact->setIcon(Utility::getToolButtonIcon(":/images/recycle.png")); + btn_delete_contact->setToolButtonStyle(Qt::ToolButtonIconOnly); + btn_delete_contact->setAutoRaise(true); + btn_delete_contact->setDisabled(true); + btn_delete_contact->show(); + connect( btn_delete_contact, SIGNAL(clicked()), this, SLOT(removeSelectedContact()) ); + +// btn_delete_group = new QToolButton(this); +// btn_delete_group->setToolTip( tr("delete group") ); +// btn_delete_group->setText(""); +// btn_delete_group->setIcon(Utility::getToolButtonIcon(":/images/recycle.png")); +// btn_delete_group->setToolButtonStyle(Qt::ToolButtonIconOnly); +// btn_delete_group->setAutoRaise(true); +// btn_delete_group->show(); + + btn_sync_contacts = new QToolButton(this); + btn_sync_contacts->setToolTip( tr("sync contacts") ); + btn_sync_contacts->setText(""); + btn_sync_contacts->setIcon(Utility::getToolButtonIcon(":/images/editgroup.png", true)); + btn_sync_contacts->setToolButtonStyle(Qt::ToolButtonIconOnly); + btn_sync_contacts->setAutoRaise(true); + btn_sync_contacts->show(); + connect( btn_sync_contacts, SIGNAL( clicked() ), this, SLOT( sync_contacts() ) ); + + hbox_layout->addWidget( btn_new_group ); + hbox_layout->addWidget( btn_add_contact_to_group ); + hbox_layout->addWidget( btn_delete_contact ); +// hbox_layout->addWidget( btn_delete_group ); + hbox_layout->addWidget( btn_sync_contacts ); + + ui->groupBox->setTitle(""); + ui->groupBox->setLayout( hbox_layout ); + ui->groupBox->setEnabled(true); + + contactPage = new ContactPage(this); + ui->tabWidget->insertTab(0, contactPage, contactPage->title()); + ui->tabWidget->setCurrentIndex(0); + connect( ui->tabWidget, SIGNAL( currentChanged(int) ), this, SLOT( tab_changed(int) ) ); + connect( contactPage, SIGNAL( validRecycle(bool) ), this, SLOT( onValidRecyele(bool) ) ); + + ui->vLayout_main->setAlignment(Qt::AlignCenter); + ui->sendSMS_tab->setLayout(ui->vLayout_main); + + QString filename( HOME_DIR + "/group.xml" ); + QFileInfo fileinfo(filename); + if( !fileinfo.exists() ) + { + XmlControler::getInstance()->newXml( filename ); +#ifdef ONLY_FOR_EBOOK + contactPage->setContactItemObserver(); + ContactInterface::getInstance()->updateContactsFromEbookToXml(); +#endif + }else + { + XmlControler::getInstance()->readXml( filename ); + // for test xmlcontroler +// XmlControler::getInstance()->createGroup( "second_group" ); +// XmlControler::getInstance()->createContact( "second_group", new ContactWidgetItem); +// XmlControler::getInstance()->createGroup( "one_group" ); +// XmlControler::getInstance()->removeContact( "one_group", NULL); +// XmlControler::getInstance()->removeGroup( "one_group" ); + } + contactPage->initContactWidget(); + contactPage->update(); + + //qDebug() << "new SendSMSSession"; +#ifdef ONLY_FOR_TELEPATHYQT4 + sendSMS = new SendSMSSession( false, this ); + connect( sendSMS, SIGNAL( smsSent(QString) ), this, SLOT( onSMSSent(QString) ) ); +#endif +} + +void MainWindow::onSMSSent(QString msg) +{ + Q_UNUSED( msg ) + //qDebug() << "MainWindow::onSMSSent" << msg << "OK"; +} + +void MainWindow::setSendToText(QVector *contacts) +{ + int count = contacts->size(); + if( count < 1 ) + return; + QString text; + ui->textEdit_phone_numbers->clear(); + for( int i = 0; i < count; i++ ) + { + text = contacts->at(i)->full_name + ";"; + name_list.append( text ); + ui->textEdit_phone_numbers->append( text ); + } +} + +void MainWindow::sync_contacts() +{ +#ifdef ONLY_FOR_EBOOK + ContactInterface::getInstance()->updateContactsFromEbookToXml(); +#endif +} + +void MainWindow::tab_changed(int index) +{ + if( 0 == index ) //contactpage + { + ui->groupBox->setShown(true); + }else //sendsms + { + ui->groupBox->setHidden(true); + + setSendToText( contactPage->getSelectedContacts() ); + } +} + +void MainWindow::cancel_clicked() +{ + ui->textEdit_phone_numbers->document()->clear(); + ui->textEdit_message->document()->clear(); + ui->btn_send_message->setDisabled(true); + contactPage->cleanSelectedContactList(); +} + +void MainWindow::send_to_clicked() +{ + //qDebug() << "MainWindow::send_to_clicked()"; +} + +void MainWindow::send_message_clicked() +{ + //qDebug() << "MainWindow::send_message_clicked()"; + + QTextDocument *doc_phone_number = ui->textEdit_phone_numbers->document(); + QTextDocument *doc_message = ui->textEdit_message->document(); + if( !doc_message->isEmpty() && !doc_phone_number->isEmpty() ) + { +#ifdef ONLY_FOR_TELEPATHYQT4 + QString txt = doc_message->toPlainText(); + QVector *contacts = contactPage->getSelectedContacts(); + QStringList addrs; + QStringList msgs; + for( int i = 0; i < contacts->size(); i++ ) + { + //qDebug() << " send sms:" << txt << "to" << contacts->at(i)->mobile_number; + addrs.append( contacts->at(i)->mobile_number ); + msgs.append( txt ); + + } + sendSMS->setSMSToSend( addrs, msgs ); +#endif + // for test +// TpSession *tps =new TpSession("ring",true); +// tps->sendMessageToAddress("ring",contacts->at(0)->mobile_number,txt); + } + cancel_clicked(); +} + +void MainWindow::send_message_enabled() +{ + //qDebug() << "MainWindow::send_message_enabled()"; + + QTextDocument *doc_phone_number = ui->textEdit_phone_numbers->document(); + QTextDocument *doc_message = ui->textEdit_message->document(); + if( !doc_message->isEmpty() && !doc_phone_number->isEmpty() ) + { + ui->btn_send_message->setEnabled(true); + }else + { + ui->btn_send_message->setDisabled(true); + } +} + +void MainWindow::new_group() +{ + //qDebug() << "MainWindow::new_group()"; + + NewGroupDialog dlg; + int result = dlg.exec(); + if( result == QDialog::Accepted ) + { + //qDebug() << "new group is ok"; + } +} + +void MainWindow::add_contact_to_group() +{ + //qDebug() << "MainWindow::add_contact_to_group()"; + AddContactToGroup dlg; + int result = dlg.exec(); + if( result == QDialog::Accepted ) + { + //qDebug() << "add contact is ok"; + } +} + +void MainWindow::onValidRecyele(bool valid) +{ + if( valid ) + { + btn_delete_contact->setEnabled(true); + }else + { + btn_delete_contact->setDisabled(true); + } +} + +void MainWindow::removeSelectedContact() +{ + //TODO : add confirm dialog + QMessageBox msgBox; + msgBox.setText("Do you want to delete that's selected contacts?"); + msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + int ret = msgBox.exec(); + if( ret == QDialog::Accepted ) + { + contactPage->removeSelectedContact(); + } +} diff --git a/sms/mainwindow.h b/sms/mainwindow.h new file mode 100644 index 0000000..cc6ca92 --- /dev/null +++ b/sms/mainwindow.h @@ -0,0 +1,74 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include + +#include "contactpage.h" +#include "xmlcontroler.h" +#include "contactwidgetitem.h" + +#ifdef ONLY_FOR_TELEPATHYQT4 +#include "sendsmssession.h" +#endif + +namespace Ui { + class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + static MainWindow* getInstance(); + ~MainWindow(); + +public Q_SLOTS: + void send_to_clicked(); + void cancel_clicked(); + void send_message_clicked(); + void send_message_enabled(); + void sync_contacts(); + void new_group(); + void add_contact_to_group(); + void removeSelectedContact(); + void tab_changed(int index); + + void onSMSSent( QString msg ); + + void onValidRecyele(bool valid); + +protected: + void changeEvent(QEvent *e); + void init(); + +private: + static MainWindow* instance; + MainWindow(QWidget *parent = 0); + + void setSendToText(QVector* contacts); + +private: + Ui::MainWindow *ui; + + QHBoxLayout *hbox_layout; + + QToolButton *btn_new_group; + QToolButton *btn_add_contact_to_group; + QToolButton *btn_delete_contact; + QToolButton *btn_delete_group; + QToolButton *btn_sync_contacts; + + ContactPage *contactPage; + +#ifdef ONLY_FOR_TELEPATHYQT4 + SendSMSSession *sendSMS; +#endif + + QStringList name_list; +}; + +#endif // MAINWINDOW_H diff --git a/sms/mainwindow.ui b/sms/mainwindow.ui new file mode 100644 index 0000000..64abb67 --- /dev/null +++ b/sms/mainwindow.ui @@ -0,0 +1,133 @@ + + + MainWindow + + + + 0 + 0 + 800 + 480 + + + + GroupSMS + + + + + + + 0 + + + + SendSMS + + + + + 20 + 20 + 571 + 331 + + + + + + + Qt::Vertical + + + + 20 + 5 + + + + + + + + + + Send To: + + + + + + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + + + + + + + + Send + + + + + + + Cancel + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 779 + 20 + + + + + + + + GroupBox + + + + + + + + + + diff --git a/sms/newgroupdialog.cpp b/sms/newgroupdialog.cpp new file mode 100644 index 0000000..b2be83c --- /dev/null +++ b/sms/newgroupdialog.cpp @@ -0,0 +1,42 @@ +#include + +#include "newgroupdialog.h" +#include "contactinterface.h" + +NewGroupDialog::NewGroupDialog(QDialog *parent) : + QDialog(parent) +{ + setupUi(this); + setWindowModality( Qt::ApplicationModal ); + btngroup_ok_cancel->button(QDialogButtonBox::Ok)->setDisabled(true); + groupNmaeEdit->setFocus(); + + connect( btngroup_ok_cancel, SIGNAL( clicked(QAbstractButton*) ), this, SLOT( btn_clicked(QAbstractButton*) ) ); + connect( groupNmaeEdit, SIGNAL( textEdited(QString) ), this, SLOT( btn_ok_enabled(QString) ) ); +} + +void NewGroupDialog::btn_clicked(QAbstractButton *button) +{ + if( QDialogButtonBox::AcceptRole == btngroup_ok_cancel->buttonRole( button ) ) + { + //qDebug() << "new group dialog : new group is" << groupNmaeEdit->text(); + + QString str = groupNmaeEdit->text(); + ContactInterface::getInstance()->createGroup( str ); + done( QDialog::Accepted ); + }else // button cancel + { + done( QDialog::Rejected ); + } +} + +void NewGroupDialog::btn_ok_enabled(QString str) +{ + if( str.length() > 0 ) + { + btngroup_ok_cancel->button(QDialogButtonBox::Ok)->setEnabled(true); + }else + { + btngroup_ok_cancel->button(QDialogButtonBox::Ok)->setDisabled(true); + } +} diff --git a/sms/newgroupdialog.h b/sms/newgroupdialog.h new file mode 100644 index 0000000..77517bc --- /dev/null +++ b/sms/newgroupdialog.h @@ -0,0 +1,19 @@ +#ifndef NEWGROUPDIALOG_H +#define NEWGROUPDIALOG_H + +#include +#include "ui_newgroupdialog.h" + + +class NewGroupDialog : public QDialog, Ui::NewGroupDialog +{ + Q_OBJECT +public: + NewGroupDialog(QDialog *parent = 0); + +public Q_SLOTS: + void btn_clicked(QAbstractButton * button); + void btn_ok_enabled(QString str); +}; + +#endif // NEWGROUPDIALOG_H diff --git a/sms/newgroupdialog.ui b/sms/newgroupdialog.ui new file mode 100644 index 0000000..0a23ddb --- /dev/null +++ b/sms/newgroupdialog.ui @@ -0,0 +1,97 @@ + + + NewGroupDialog + + + + 0 + 0 + 400 + 102 + + + + Dialog + + + + + 300 + 30 + 81 + 61 + + + + Qt::Vertical + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 30 + 30 + 261 + 61 + + + + + + + 110 + 0 + 151 + 31 + + + + + AlArabiya + 14 + + + + New Group Name + + + + + + + btngroup_ok_cancel + accepted() + NewGroupDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + btngroup_ok_cancel + rejected() + NewGroupDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/sms/selectcontactwidget.cpp b/sms/selectcontactwidget.cpp new file mode 100644 index 0000000..5fef9a0 --- /dev/null +++ b/sms/selectcontactwidget.cpp @@ -0,0 +1,89 @@ +#include "selectcontactwidget.h" +#include "contactinterface.h" +#include "xmlstring.h" + +SelectContactWidget::SelectContactWidget( QWidget *parent ) : + ContactWidget( parent ) +{ + //qDebug() << "SelectContactWidget::SelectContactWidget(QWidget *parent), Entry"; +} + +SelectContactWidget::~SelectContactWidget() +{ + //qDebug() << "SelectContactWidget::~SelectContactWidget(), Entry"; +} + +void SelectContactWidget::initContactWidget() +{ + //qDebug() << "SelectContactWidget::initContactWidget(), Entry"; + + ContactInterface::getInstance()->setItemSelectObserver(this); + ContactInterface::getInstance()->getAllContactsFromXml( STR_XML_ALLCONTACTS ); + + for (int i = 0; i < contact_items->size(); ++i) + { + connect( contact_items->at(i), SIGNAL( itemUpdate() ), this, SLOT( update() ) ); + connect( contact_items->at(i), SIGNAL( itemSelected( ContactWidgetItem*, bool) ), this, SLOT( contactItemSelected( ContactWidgetItem*, bool ) ) ); + } +} + +void SelectContactWidget::destroyContactWidget() +{ + //qDebug() << "SelectContactWidget::destroyContactWidget(), Entry"; +} + +void SelectContactWidget::getGroupContacts( ItemListPtr items ) +{ + for( int i = 0; i < items.size(); i++ ) + { + Item *item = items.at(i); + + if( item->m_isGroup ) + { + GroupWidgetItem *contact = new GroupWidgetItem(this); + + contact->label_group_name->setText( item->group_name ); + contact->group_name = item->group_name; + + contact_items->append( contact ); + }else + { + ContactWidgetItem *contact = new ContactWidgetItem(this); + + contact->label_fullname->setText( item->full_name ); + contact->full_name = item->full_name; + + contact->label_mobile_number->setText( item->mobile_number ); + contact->mobile_number = item->mobile_number; + + contact->group_owner = item->group_owner; + + contact->uid = item->uid; + + contact_items->append( contact ); + } + } +} + +QVector* SelectContactWidget::getSelectedContacts() +{ + return contact_items_selected; +} + +void SelectContactWidget::addContactsToGroup(const QString &groupname) +{ + //qDebug() << "SelectContactWidget::addContactsToGroup(const QString &groupname)"; + ItemListPtr list; + for( int i = 0; i < contact_items_selected->size(); i++ ) + { + ContactWidgetItem *contact = contact_items_selected->at(i); + Item *item = new Item(); + item->full_name = contact->full_name; + item->mobile_number = contact->mobile_number; + item->group_owner = groupname; + item->uid = contact->uid; + + list.append(item); + } + ContactInterface::getInstance()->addContactToGroup(list, groupname); +} diff --git a/sms/selectcontactwidget.h b/sms/selectcontactwidget.h new file mode 100644 index 0000000..49b9a15 --- /dev/null +++ b/sms/selectcontactwidget.h @@ -0,0 +1,28 @@ +#ifndef SELECTCONTACTWIDGET_H +#define SELECTCONTACTWIDGET_H + +#include +#include "contactwidget.h" +#include "groupwidgetitem.h" +#include "itemobserver.h" + +class SelectContactWidget : public ContactWidget, public ItemSelectObserver +{ + Q_OBJECT +public: + SelectContactWidget( QWidget *parent = 0 ); + ~SelectContactWidget(); + void initContactWidget(); + + void getGroupContacts( ItemListPtr items ); + + QVector* getSelectedContacts(); + + void addContactsToGroup(const QString &groupname); + +protected: + void destroyContactWidget(); + +}; + +#endif // SELECTCONTACTWIDGET_H diff --git a/sms/sendsmssession.cpp b/sms/sendsmssession.cpp new file mode 100644 index 0000000..04c0d97 --- /dev/null +++ b/sms/sendsmssession.cpp @@ -0,0 +1,98 @@ +#include +#include "sendsmssession.h" + +SendSMSSession::SendSMSSession( bool sync, QObject *parent ) : + QObject(parent) +{ + syncSend = sync; + isReady = false; + tps = NULL; +} + +void SendSMSSession::initTpSession() +{ + qDebug() << __PRETTY_FUNCTION__ ; + + if( tps == NULL ) + { + tps = new TpSession( "ring", syncSend ); + + if( !isReady && !syncSend ) + { + connect(tps,SIGNAL(accountReady(TpSessionAccount *)),SLOT(onAccountReady(TpSessionAccount *))); + }else + { + SendSMS(); + } + + connect(tps,SIGNAL(messageSent(const Tp::Message &,TpSessionAccount *)), + SLOT(onSMSSent(const Tp::Message &,TpSessionAccount *))); + + connect(tps,SIGNAL(messageReceived(const Tp::ReceivedMessage &,TpSessionAccount *)), + SLOT(onMessageReceived(const Tp::ReceivedMessage &,TpSessionAccount *))); + }else + { + if( !isReady && !syncSend ) + { + connect(tps,SIGNAL(accountReady(TpSessionAccount *)),SLOT(onAccountReady(TpSessionAccount *))); + }else + { + SendSMS(); + } + } +} + +void SendSMSSession::setSMSToSend(QString addr, QString msg) +{ + qDebug() << __PRETTY_FUNCTION__ ; + addresses.append( addr ); + messages.append( msg ); + + initTpSession(); +} + +void SendSMSSession::setSMSToSend( QStringList addrs, QStringList msgs ) +{ + qDebug() << __PRETTY_FUNCTION__ ; + addresses = addrs ; + messages = msgs ; + + initTpSession(); +} + +void SendSMSSession::SendSMS() +{ + qDebug() << __PRETTY_FUNCTION__ ; + for( int i = 0; i < addresses.size(); i++ ) + { + tps->sendMessageToAddress( "ring", addresses.at(i), messages.at(i) ); + } + addresses.clear(); + messages.clear(); +} + +void SendSMSSession::onAccountReady(TpSessionAccount *tpsa) +{ + qDebug() << __PRETTY_FUNCTION__ ; + + isReady = true; + for( int i = 0; i < addresses.size(); i++ ) + { + tpsa->sendMessageToAddress( addresses.at(i), messages.at(i) ); + } + addresses.clear(); + messages.clear(); +} + +void SendSMSSession::onSMSSent( const Tp::Message &msg, TpSessionAccount *acc ) +{ + qDebug() << "SendSMSSession::onSMSSent :" << msg.text(); + Q_EMIT smsSent( msg.text() ); +} + +void SendSMSSession::onMessageReceived(const Tp::ReceivedMessage &msg,TpSessionAccount *acc) +{ + qDebug() << "SendSMSSession::onMessageReceived " << msg.text() << "from " << msg.sender()->id(); +} + + diff --git a/sms/sendsmssession.h b/sms/sendsmssession.h new file mode 100644 index 0000000..0074160 --- /dev/null +++ b/sms/sendsmssession.h @@ -0,0 +1,43 @@ +#ifndef SENDSMSSESSION_H +#define SENDSMSSESSION_H + +#include +#include +#include "tpsession/tpsession.h" +#include "tpsession/tpsessionaccount.h" + +class SendSMSSession : public QObject +{ + Q_OBJECT +public: + SendSMSSession( bool sync = false, QObject *parent = 0); + + void setSMSToSend( QString addr,QString msg ); + void setSMSToSend( QStringList addrs,QStringList msgs ); + +private: + void initTpSession(); + void SendSMS(); + +private: + TpSession *tps; + + QString sender; + QStringList addresses; + QStringList messages; + + bool syncSend; + bool isReady; + +Q_SIGNALS: + void smsSent( QString msg, QString addr ); + void smsSent( QString msg ); + +public Q_SLOTS: + void onAccountReady(TpSessionAccount *); + void onSMSSent(const Tp::Message &,TpSessionAccount *); + void onMessageReceived(const Tp::ReceivedMessage &,TpSessionAccount *); + +}; + +#endif // SENDSMSSESSION_H diff --git a/sms/sms.pro b/sms/sms.pro new file mode 100644 index 0000000..7d96479 --- /dev/null +++ b/sms/sms.pro @@ -0,0 +1,85 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2010-05-24T19:59:46 +# +#------------------------------------------------- + +QT += core gui xml + +TARGET = groupsms +TEMPLATE = app + +# DEFINES += ONLY_FOR_EBOOK # include libabook ets.. +# DEFINES += ONLY_FOR_TELEPATHYQT4 + +CONFIG += no_keywords +CONFIG += link_pkgconfig +PKGCONFIG += glib-2.0 gtk+-2.0 # libosso libebook-1.2 libosso-abook-1.0 TelepathyQt4 +CONFIG += qdbus + +INCLUDEPATH += /usr/local/include/telepathy-1.0 + +LIBS += -L/usr/lib + +SOURCES += main.cpp\ + mainwindow.cpp \ + abstractpage.cpp \ + contactwidget.cpp \ + contactwidgetitem.cpp \ + xmlcontroler.cpp \ + contactpage.cpp \ + groupwidgetitem.cpp \ + utility.cpp \ + contactinterface.cpp \ + newgroupdialog.cpp \ + addcontacttogroup.cpp \ + selectcontactwidget.cpp #\ +# tpsession/tpsessionobserver.cpp \ +# tpsession/tpsessionchannel.cpp \ +# tpsession/tpsessionaccount.cpp \ +# tpsession/tpsession.cpp \ +# sendsmssession.cpp + +HEADERS += mainwindow.h \ + abstractpage.h \ + contactwidget.h \ + contactwidgetitem.h \ + xmlcontroler.h \ + xmlstring.h \ + common.h \ + contactpage.h \ + groupwidgetitem.h \ + utility.h \ + contactinterface.h \ + newgroupdialog.h \ + addcontacttogroup.h \ + selectcontactwidget.h \ + itemobserver.h \ + item.h #\ +# tpsession/tpsessionobserver.h \ +# tpsession/tpsessionchannel.h \ +# tpsession/tpsessionaccount.h \ +# tpsession/tpsession.h \ +# sendsmssession.h + + +FORMS += mainwindow.ui \ + newgroupdialog.ui \ + addcontacttogroupdialog.ui + +RESOURCES = groupsms.qrc + +CONFIG += mobility +MOBILITY = + +symbian { + TARGET.UID3 = 0xe9d84f35 + # TARGET.CAPABILITY += + TARGET.EPOCSTACKSIZE = 0x14000 + TARGET.EPOCHEAPSIZE = 0x020000 0x800000 +} + +# pkg.path = /usr/local/lib/pkgconfig +# pkg.files = GroupSMS.pc +# target.path += $$[QT_INSTALL_LIBS] +# INSTALLS += target pkg diff --git a/sms/tpsession/tpsession.cpp b/sms/tpsession/tpsession.cpp new file mode 100755 index 0000000..1b230cd --- /dev/null +++ b/sms/tpsession/tpsession.cpp @@ -0,0 +1,265 @@ +/* + * This file is part of TpSession + * + * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Contact Kate Alhola kate.alhola(a)nokia.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "tpsession.h" +#include + + + + + +/** + * \class TpSession + * \headerfile + * + * Top level class, counterpart of Account Manager. TpSession connects to account manager and requests accounts from it. TpSession creates TpSessionAccount for all accounts . + * As top level class TpSession provides simålified interface to send and receive messages via any account. TpSession provides signal when it has accounts ready. + * If you require some specific account in constructor, you will receive signal only when this account is ready. If you use constructor without any parameters, you will get one + * signal for every account. If synchronous is true, constructor is executed as synchronous and it does return after transactions to set up accounts are done. + */ +/** + * \fn void TpSession::accountReady(TpSessionAccount *); + * + * Emitted when the account becomes ready + * + * \param TpSessionAccount pointer to account become ready + */ +/** + * \fn void TpSession::amReady(TpSession *); + * + * Emitted when the account Manager becomes ready + * + * \param TpSession pointer to TpSession class + */ +/** + * \fn void TpSession::messageReceived(const Tp::ReceivedMessage &,TpSessionAccount *); + * + * Emitted when any of Account Managers recived message + * + * \param Tp::ReceivedMessage Message received + * \param TpSessionAccount pointer to account received message + */ + + +/** + * Construct a new TpSession object. + * + * \param cmname Name of the default connection manager. Can be empty or omnitted, then there is no default connection manager + * \param synchronous if false, asynchronous behavior, function returns immediately and accountReady signals are emitted when accounts are ready + * if True, synchronous behavior and function returns when accounts are ready + */ +TpSession::TpSession(QString cmname,bool synchronous) +{ + Tp::registerTypes(); + Tp::enableDebug(false); + Tp::enableWarnings(false); + + mAM = Tp::AccountManager::create(); + reqCm=cmname; + sync=synchronous; + connect(mAM->becomeReady(), + SIGNAL(finished(Tp::PendingOperation *)), + SLOT(onAMReady(Tp::PendingOperation *))); + connect(mAM.data(), + SIGNAL(accountCreated(const QString &)), + SLOT(onAccountCreated(const QString &))); + + // createObserver(); + if(synchronous) loop.exec(); // Loop locally untill accounts are initialized + reqCm=cmname; + +} +TpSession* TpSession::instancePtr=NULL; +/** + * Returns pointer to TpSession singleton. If there is not yet TpSession Object, creates it with "Ring" connection manager as default + * + * \param synchronous if false, asynchronous behavior, function returns immediately and accountReady signals are emitted when accounts are ready + * if True, synchronous behavior and function returns when accounts are ready + */ +TpSession* TpSession::instance(bool synchronous) +{ + if(instancePtr==NULL) instancePtr=new TpSession("ring",synchronous); + return instancePtr; +}; + +void TpSession::onAMReady(Tp::PendingOperation *op) +{ + // qDebug() << "TpSession::onAMReady"; + TpSessionAccount *tpacc; + + Q_FOREACH (const QString &path, mAM->allAccountPaths()) { + accounts+=tpacc=new TpSessionAccount(mAM, path); + connect(tpacc,SIGNAL(accountReady(TpSessionAccount*)), + SLOT(onAccountReady(TpSessionAccount *))); + } + +} + +void TpSession::onReady(Tp::PendingOperation *) +{ +}; + +void TpSession::onAccountCreated(const QString &path) +{ + + accounts+=new TpSessionAccount(mAM, path); +} + +void TpSession::onAccountReady(TpSessionAccount *tpacc) +{ + qDebug() << "TpSession::onAccountReady:Account " << tpacc->acc->cmName() << "is Ready sync=" << sync << "waiting:" << reqCm; + connect(tpacc,SIGNAL(messageReceived(const Tp::ReceivedMessage &,TpSessionAccount *)), + SLOT(onMessageReceived(const Tp::ReceivedMessage &,TpSessionAccount *))); + // Tom add + connect(tpacc,SIGNAL(messageSent(const Tp::Message &,TpSessionAccount *)), + SLOT(onMessageSent(const Tp::Message &,TpSessionAccount *))); + // Tom end + if(!reqCm.isEmpty() && tpacc->acc->cmName()==reqCm) { + if(sync) { + sync=false; + loop.quit(); + qDebug() << "sync eventloop exit"; + } + Q_EMIT accountReady(tpacc); + if(!reqMsg.isEmpty()) tpacc->sendMessageToAddress(reqAddress,reqMsg); + } +} + +void TpSession::onMessageReceived(const Tp::ReceivedMessage &msg,TpSessionAccount *acc) +{ + // qDebug() << "TestProg::onMessageReceived " << msg.text() << "from " << msg.sender()->id(); + Q_EMIT messageReceived(msg,acc); +} + +// Tom add +void TpSession::onMessageSent(const Tp::Message &msg,TpSessionAccount *acc) +{ + // qDebug() << "TpSession::onMessageSent " << msg.text() << "from " << msg.sender()->id(); + Q_EMIT messageSent(msg,acc); +} +// Tom end + +/** + * Send message using specified connection manager to address + * + * \param connectionMgr Name of the connection manager + * \param address Valid address for this connection manager type. Asexample telephone number to Ring, GoogleTalk address for Gabble + * \param message Message body + */ +void TpSession::sendMessageToAddress(QString connectionMgr,QString address,QString message) +{ + qDebug() << "TpSession::sendMessageToAddress(QString connectionMgr,QString address,QString message)"; + TpSessionAccount *tpsa=getAccount(connectionMgr); + if(tpsa) + { + tpsa->sendMessageToAddress(address,message); + } +} +/** + * Returns pointer to TpSessionAccout object with specified connection manager or protocol, returns NULL if no match found + * + * \param cm Name of the connection manager, or iniqueIdentifier (dbus path to cm) if left empty matches every entry + * \param protocol Name of the protocol manager, if left empty matches every entry + */ +TpSessionAccount* TpSession::getAccount(const QString cm,QString protocol) +{ + // qDebug() << "TpSession::getAccount" << cm << " " << protocol; + Q_FOREACH (TpSessionAccount *tpacc, accounts) { + if((!cm.isEmpty() && ((tpacc->acc->cmName()==cm) || (tpacc->acc->uniqueIdentifier()==cm))) || (!protocol.isEmpty() && tpacc->acc->protocol()==protocol)) { + // qDebug() << "TpSession::getAccount found" << tpacc->acc->cmName() << " " << tpacc->acc->protocol() << " " << tpacc->acc->uniqueIdentifier(); + return tpacc; + } + } + return NULL; +} + +void TpSession::createObserver() +{ + + qDebug() << __PRETTY_FUNCTION__ ; + + registrar = Tp::ClientRegistrar::create(); + + Tp::ChannelClassList channelFilters; + QMap textFilter, mediaFilter; + // Registering Text channel observer + textFilter.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"), + QDBusVariant(TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT)); + textFilter.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"), + QDBusVariant(Tp::HandleTypeContact)); + channelFilters.append(textFilter); + + // Registering Media channel observer + mediaFilter.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"), + QDBusVariant(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA)); + mediaFilter.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"), + QDBusVariant(Tp::HandleTypeContact)); + channelFilters.append(mediaFilter); + + TpSessionObserver* observer = new TpSessionObserver( channelFilters, this ); + bool registered = registrar->registerClient( + Tp::AbstractClientPtr::dynamicCast(Tp::SharedPtr(observer)), + "TpSessionChannelObserver"); + + // qDebug() << "TpSession::createObserver" << (registered ? "started" : "failed"); + +} + + +void TpSession::createChannelListener(const QString &channelType, + const Tp::MethodInvocationContextPtr<> &context, + const Tp::AccountPtr &account, + const Tp::ChannelPtr &channel) +{ + qDebug() << "TpSession::createChannelListener"; + + QString channelObjectPath = channel->objectPath(); + + + if ( channels.contains( channelObjectPath ) && + !channelType.isEmpty() && + !channelObjectPath.isEmpty() ) { + qDebug() << "TELEPATHY_ERROR_INVALID_ARGUMENT"; + return; + } + qDebug() << "creating listener for: " << channelObjectPath << " type " << channelType; +#if 0 + ChannelListener* listener = 0; + if( channelType == TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT ) { + listener = new TextChannelListener(account, channel, context); + } else if ( channelType == TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ) { + listener = new StreamChannelListener(account, channel, context); + } + + if(listener) { + connect(listener, SIGNAL(channelClosed(ChannelListener *)), + this, SLOT(channelClosed(ChannelListener *))); + Channels.append( channelObjectPath ); + } +#endif +} + + + + + + + + diff --git a/sms/tpsession/tpsession.h b/sms/tpsession/tpsession.h new file mode 100755 index 0000000..2061640 --- /dev/null +++ b/sms/tpsession/tpsession.h @@ -0,0 +1,99 @@ +/* + * This file is part of TpSession + * + * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Contact Kate Alhola kate.alholanokia.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef TPSESSION_H +#define TPSESSION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tpsessionaccount.h" +#include "tpsessionobserver.h" + +class TpSession:public QObject +{ + Q_OBJECT +public: + TpSession(QString cmname=QString(),bool synchronous=FALSE); + + + static TpSession* instance(bool synchronous=TRUE); + void sendMessageToAddress(QString connectionMgr,QString address,QString message); + TpSessionAccount* getAccount(const QString cm, const QString protocol=QString()); + void createChannelListener(const QString &channelType, + const Tp::MethodInvocationContextPtr<> &context, + const Tp::AccountPtr &account, + const Tp::ChannelPtr &channel); + void createObserver(); + +Q_SIGNALS: + void amReady(TpSession *); + void accountReady(TpSessionAccount *); + void channeReady(TpSessionAccount *); + void messageReceived(const Tp::ReceivedMessage &,TpSessionAccount *); + + // Tom add + void messageSent(const Tp::Message &,TpSessionAccount *); + // Tom end +private Q_SLOTS: + void onAMReady(Tp::PendingOperation *); + void onAccountCreated(const QString &); + void onReady(Tp::PendingOperation *); + void onAccountReady(TpSessionAccount *tpacc); + void onMessageReceived(const Tp::ReceivedMessage &,TpSessionAccount *); + + // Tom add + void onMessageSent(const Tp::Message &,TpSessionAccount *); + // Tom end +public: + QVector accounts; + +private: + static TpSession *instancePtr; + //TpSession *instancePtr; + QString reqCm; + QString reqAddress; + QString reqMsg; + + bool sync; // Synchronous initialization + QEventLoop loop; + Tp::AccountManagerPtr mAM; + QStringList channels; + Tp::ClientRegistrarPtr registrar; +}; + + + +#endif // TPSESSION_H diff --git a/sms/tpsession/tpsessionaccount.cpp b/sms/tpsession/tpsessionaccount.cpp new file mode 100755 index 0000000..0626737 --- /dev/null +++ b/sms/tpsession/tpsessionaccount.cpp @@ -0,0 +1,309 @@ +/* + * This file is part of TpSession + * + * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Contact Kate Alhola kate.alholanokia.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "tpsessionaccount.h" +#include + +/** + * \class TpSessionAccount + * \headerfile + * + * TpSessionAccount class represents every account you have. As example account for “Ring” connection manager represents your cellular + * account and you may send and receive SMS with it. Gabble represents your GoogleTalk account if you have defined them. + * TpSessionAccounts are created by TpSession class,they are not intended to be created stand-alone + + */ +/** + * \fn void TpSessionAccount::accountReady(TpSessionAccount *); + * + * Emitted when the account becomes ready + * + * \param TpSessionAccount pointer to account become ready + */ +/** + * \fn void TpSessionAccount::channelReady(TpSessionAccount *); + * + * Emitted when the account Manager becomes ready + * + * \param TpSession pointer to TpSession class + */ +/** + * \fn void TpSessionAccount::messageReceived(const Tp::ReceivedMessage &,TpSessionAccount *); + * + * Emitted when any of Account Managers recived message + * + * \param Tp::ReceivedMessage Message received + * \param TpSessionAccount pointer to account received message + */ + +/** + * \fn void TpSessionAccount::newChannel(TpSessionAccount *,QString CjhannelType,QString peerId,const Tp::ChannelDetails &); + * \param TpSession pointer to TpSession class + * \param ChannelType type of Channel, TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT for text channel, TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA for steram media, as exmple for incoming call + * \param peedId PeerId, as example caller telephone number + * \param channeDetails needed if you would like to create a channel. For text chanels TpSession creates channel automatically. For calls, Maemo Call UI handles callcreation + */ + +/** + * Construct a new TpSessionAccount object. This constructor is called by TpSession class when new account is created or fetched from account manager. It is not inended to be used stand alone + * + * \param am Telepathy-Qt4 account manager for this account + * \param objectPath Dbus object path tonew account + */ +TpSessionAccount::TpSessionAccount(Tp::AccountManagerPtr am,const QString &objectPath): + mAcc(Tp::Account::create(am->dbusConnection(),am->busName(), objectPath)) + +{ + connect(mAcc->becomeReady(),SIGNAL(finished(Tp::PendingOperation *)),SLOT(onReady(Tp::PendingOperation *))); + ready=false; + // qDebug() << "TpSessionAccount::TpSessionAccount objectPath=" << objectPath; +}; + + +void TpSessionAccount::onReady(Tp::PendingOperation *op) +{ + + acc = mAcc.data(); + qDebug() << "TpSessionAccount::onReady cmName=" << acc->cmName() << "haveConnection=" << + (acc->haveConnection()? ( acc->connection()->isReady() ? "Ready":"notReady"):"no"); + + if(acc->haveConnection()) + { + + connect(acc->connection()->becomeReady(Tp::Connection::FeatureRoster | Tp::Connection::FeatureSelfContact ), + SIGNAL(finished(Tp::PendingOperation *)), + SLOT(onContactsConnectionReady(Tp::PendingOperation *))); + if (acc->connection()->isReady() && acc->connection()->interfaces().contains(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_REQUESTS)) + { + qDebug() << "TpSessionAccount::onReady: connecting to Connection.Interface.NewChannels"; + connect(acc->connection()->requestsInterface(), + SIGNAL(NewChannels(const Tp::ChannelDetailsList&)), + SLOT(onNewChannels(const Tp::ChannelDetailsList&))); + } + } + else + { // If there is no connection, we are ready now, else we are ready when contacts connection is ready + qDebug() << "If there is no connection, we are ready now, else we are ready when contacts connection is ready"; + ready=true; + Q_EMIT accountReady(this); + } +} + +void TpSessionAccount::onContactsConnectionReady(Tp::PendingOperation *op) +{ + if (op->isError()) { + qWarning() << "Connection cannot become ready" << acc->cmName(); + return; + } + + if (acc->connection()->interfaces().contains(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_REQUESTS)) { + qDebug() << "TpSessionAccount::onContactsConectionReady: connecting to Connection.Interface.NewChannels"; + connect(acc->connection()->requestsInterface(), + SIGNAL(NewChannels(const Tp::ChannelDetailsList&)), + SLOT(onNewChannels(const Tp::ChannelDetailsList&))); + } else qDebug() << "TpSessionAccount::onectionReady: does NO have CONNECTION_INTERFACE_REQUESTS"; + Tp::PendingReady *pr = qobject_cast(op); + contactsConn = Tp::ConnectionPtr(qobject_cast(pr->object())); +#if 0 + connect(contactsConn->contactManager(), + SIGNAL(presencePublicationRequested(const Tp::Contacts &)), + SLOT(onPresencePublicationRequested(const Tp::Contacts &))); +#endif + qDebug() << "TpSessionAccount::onContactsConnectionReady "<< acc->cmName() ; + // RosterItem *item; + bool exists; + myContacts=contactsConn->contactManager()->allKnownContacts(); + Q_FOREACH (const Tp::ContactPtr &contact, myContacts) { + qDebug() << "id=" <id() << " alias=" << contact->alias() << " presence=" << contact->presenceStatus() ; + if(contact->id()==reqContact) { + addOutgoingChannel(contact); + reqContact=""; + } + }; + if(!reqContact.isEmpty() ) makeContactFromAddress(reqContact); + ready=true; + Q_EMIT accountReady(this); +} + + +/** + * Fetch Tp::ContactPtr for contact with given address. Contact is searched among contacts returned by contact manager for ths account. + * All connecions managers does not return contacts, as example Ring telephony contact manager does not. Gabble for Googletalk or Spirit for Skype does + * return contacts- + * + * \param id Contact address/id, as example email address, telephone number etc. Only exact matches + * \return TpContactPtr, if nontact is not returned TpContactPtr.isNull() is true + */ + +Tp::ContactPtr TpSessionAccount::getContactFromAddress(QString id) +{ + Tp::ContactPtr p; + Q_FOREACH (const Tp::ContactPtr &contact, myContacts) { + if(contact->id()==reqContact) return p=contact; + } + return p; +} +/** + * Fetch TpSessionChannel for with given address. Contact is searched among active channels for this account. + * + * + * \param id Contact address/id, as example email address, telephone number etc. Only exact matches + * \return Pointer to TpSessionChannel or NULL if nit found + */ + +TpSessionChannel* TpSessionAccount::getChannelFromPeerAddress(QString id) +{ + TpSessionChannel* p=NULL; + Q_FOREACH (TpSessionChannel* channel, myChannels) { + if(channel->peerId()==id) return p=channel; + } + return p; +} +/** + * Creates new contact with given address. This function is Acynchronous, it sends request to contact manager for contact creation, + * + * \param address Contact address/id, as example email address, telephone number etc. + */ + +void TpSessionAccount::makeContactFromAddress(QString address) +{ + qDebug() << "TpSessionAccount::makeContactFromAddress(QString address)"; + reqContact=address; // When we get retrieved signal, we check if it is this one + Tp::PendingContacts *pc = contactsConn->contactManager()->contactsForIdentifiers( QStringList(address) ); + qDebug() << "111111111111111111"; + connect(pc,SIGNAL(finished(Tp::PendingOperation *)),SLOT(onNewContactRetrieved(Tp::PendingOperation *))); +} + +void TpSessionAccount::onNewContactRetrieved(Tp::PendingOperation *op) +{ + Tp::PendingContacts *pcontacts = qobject_cast(op); + QList contacts = pcontacts->contacts(); + QString username = pcontacts->identifiers().first(); + if (contacts.size() != 1 || !contacts.first()) { + qDebug() << "Unable to add contact " <sendMessage(message); // We have already channel + Q_EMIT messageQueued(this); + } + else { + reqMessage=message; + p=getContactFromAddress(address); // Do we have contact ready ? + if(p.isNull()) // If not, create it + { + makeContactFromAddress(address); // Create and after created, send + }else + { + addOutgoingChannel(p); // Create channel and when ready, send + } + }; +} + +void TpSessionAccount::addOutgoingChannel(const Tp::ContactPtr &contact) +{ + + + // qDebug() << "TpSessionAccount::addOutgoingChannel"; + + TpSessionChannel* newChannel=new TpSessionChannel(contact->manager()->connection(),contact); + connect(newChannel,SIGNAL(messageReceived(const Tp::ReceivedMessage &,TpSessionChannel *)), + SLOT(onMessageReceived(const Tp::ReceivedMessage &,TpSessionChannel *))); + connect(newChannel,SIGNAL(channelReady(TpSessionChannel *)), + SLOT(onOutgoingChannelReady(TpSessionChannel*))); + myChannels+=newChannel; + +} + +void TpSessionAccount::onOutgoingChannelReady(TpSessionChannel *ch) +{ + // qDebug() << "TpSessionAccoiunt::onOutgoingChannelReady"; + Q_EMIT channelReady(this); + if(!reqMessage.isEmpty()) { + ch->sendMessage(reqMessage); + Q_EMIT messageQueued(this); + }; + reqMessage.clear(); +} + + +void TpSessionAccount::onMessageSent(const Tp::Message &msg,Tp::MessageSendingFlags, const QString &flags) +{ + // qDebug() << "TpSessionAccount::onMessageSent"; + Q_EMIT messageSent(msg,this); +}; + +void TpSessionAccount::onMessageReceived(const Tp::ReceivedMessage &msg,TpSessionChannel *ch) +{ + // qDebug() << "TpSessionAccount::onMessageReceived " << msg.text(); + Q_EMIT messageReceived(msg,this); +}; + +void TpSessionAccount::onNewChannels(const Tp::ChannelDetailsList &channels) +{ + + Tp::TextChannelPtr myIngoingTextChannel; + // qDebug() << "TpSessionAccount::onNewChannels"; + Q_FOREACH (const Tp::ChannelDetails &details, channels) { + QString channelType = details.properties.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString(); + QString targetId = details.properties.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID")).toString(); + bool requested = details.properties.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Requested")).toBool(); + // qDebug() << " channelType:" << channelType <<" requested :" << requested << " targetId" << targetId; + + Q_EMIT newChannel(this,channelType,targetId,details); + if (channelType == TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT && !requested) { + + myIngoingTextChannel = Tp::TextChannel::create(acc->connection(),details.channel.path(),details.properties); + // qDebug() << "TpSessionAccount::onNewChannels path=" <<"path " << myIngoingTextChannel->objectPath(); + + TpSessionChannel* newChannel=new TpSessionChannel( myIngoingTextChannel); + connect(newChannel,SIGNAL(messageReceived(const Tp::ReceivedMessage &,TpSessionChannel *)), + SLOT(onMessageReceived(const Tp::ReceivedMessage &,TpSessionChannel *))); + myChannels+=newChannel; + } + if (channelType == TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA && !requested) { + // qDebug() << "Incoming call" ; + } + } +} diff --git a/sms/tpsession/tpsessionaccount.h b/sms/tpsession/tpsessionaccount.h new file mode 100755 index 0000000..3e00670 --- /dev/null +++ b/sms/tpsession/tpsessionaccount.h @@ -0,0 +1,81 @@ +/* + * This file is part of TpSession + * + * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Contact Kate Alhola kate.alholanokia.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef TPSESSIONACCOUNT_H +#define TPSESSIONACCOUNT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "tpsessionchannel.h" + +class TpSessionAccount:public QObject +{ + + Q_OBJECT +public: + TpSessionAccount(Tp::AccountManagerPtr am,const QString &objectPath); + void makeContactFromAddress(QString address); + void sendMessageToAddress(QString address,QString message); + Tp::ContactPtr getContactFromAddress(QString address); + void addOutgoingChannel(const Tp::ContactPtr &contact); + void addOutgoingChannel(QString address); + TpSessionChannel *getChannelFromPeerAddress(QString id); +Q_SIGNALS: + void accountReady(TpSessionAccount *); + void channelReady(TpSessionAccount *); + void messageQueued(TpSessionAccount *); + void messageReceived(const Tp::ReceivedMessage &,TpSessionAccount *); + void messageSent(const Tp::Message &,TpSessionAccount *); + void newChannel(TpSessionAccount *,QString,QString,const Tp::ChannelDetails &); + +private Q_SLOTS: + void onReady(Tp::PendingOperation *op); + void onOutgoingChannelReady(TpSessionChannel *ch); + void onContactsConnectionReady(Tp::PendingOperation *op); + void onNewContactRetrieved(Tp::PendingOperation *op); + void onMessageReceived(const Tp::ReceivedMessage &,TpSessionChannel *); + void onMessageSent(const Tp::Message &,Tp::MessageSendingFlags, const QString &); + void onNewChannels(const Tp::ChannelDetailsList&); +public: + bool ready; + QString reqContact; + QString reqMessage; + Tp::AccountPtr mAcc; + Tp::Account *acc; + Tp::ConnectionPtr contactsConn; + QSet myContacts; + QSet myChannels; +}; + +#endif // TPSESSIONACCOUNT_H diff --git a/sms/tpsession/tpsessionchannel.cpp b/sms/tpsession/tpsessionchannel.cpp new file mode 100755 index 0000000..930cf3d --- /dev/null +++ b/sms/tpsession/tpsessionchannel.cpp @@ -0,0 +1,185 @@ +/* + * This file is part of TpSession + * + * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Contact Kate Alhola kate.alholanokia.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "tpsessionchannel.h" + + +/** + * \class TpSessionChannel + * \headerfile + * + * + * When you start chat session or call with your buddy, channel is established with your buddy. + * TpSessionChannel represents this connection. TpSession account makes automatically channel when + * you send message to your buddy's address. If you send successive messages to same buddy with + * TpSessionAccount, it automatically reuses existing connection. + */ +/** + * \fn void TpSessionChannel::channelReady(TpSessionChannel *); + * + * Emitted when the channel becomes ready + * + * \param TpSessionChannel pointer to channel become ready + */ +/** + * \fn void TpSessionChannel::channelDestroyed(TpSessionChannel *); + * + * Emitted when the channel is destroyed + * + * \param TpSessionChannel pointer to channel destroyed. The pointer is only for referenc to remove + * it from some possible places where it could be stored. It is not guaranteed to point any more valid TpSessionChannel object + */ +/** + * \fn void TpSessionChannel::messageReceived(const Tp::ReceivedMessage &,TpSessionConnection *); + * + * Emitted when any of Account Managers recived message + * + * \param Tp::ReceivedMessage Message received + * \param TpSessionChannel pointer to channel received message + */ +/** + * \fn void TpSessionChannel::messageSent(const Tp::Message &,Tp::MessageSendingFlags, const QString &,TpSessionChannel *); + * + * \param Tp::Message message sent + */ + +/** + * Construct a new TpSessionChannel object. This constructor is called by TpSessionAccount class when + * new channel is created . It is not inended to be used stand alone + * This varient with connection and contact as parameter is intented for creationg new connection from origginator side to your peer + * + * + * \param conn connection where this channel is created + * \param contact Contacto to your peer to establish channel + */ + + +TpSessionChannel::TpSessionChannel(Tp::ConnectionPtr conn,const Tp::ContactPtr &contact) +{ + QVariantMap request; + // qDebug() << "TpSessionChannel::TpSessionChannel" <<"contact.id() " << contact->id(); + request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"), + TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT); + request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"), + (uint) Tp::HandleTypeContact); + request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle"), + contact->handle()[0]); + + connect(conn->ensureChannel(request), + SIGNAL(finished(Tp::PendingOperation*)), + SLOT(onChannelCreated(Tp::PendingOperation*))); + peerContact=contact; +} + +/** + * Construct a new TpSessionChannel object. This constructor is called by TpSessionAccount class when + * new channel is created . It is not inended to be used stand alone + * This varient with connection only parameter is intented for receiving new connection from your peer + * + * + * \param conn connection where this channel is created + */ + + +TpSessionChannel::TpSessionChannel(Tp::TextChannelPtr ch) +{ + // qDebug() << "TpSessionChannel::TpSessionChannel" <<"path " << ch->objectPath(); + channel=ch; + connect(channel->becomeReady(Tp::TextChannel::FeatureMessageQueue|Tp::TextChannel::FeatureMessageSentSignal), + SIGNAL(finished(Tp::PendingOperation *)), + SLOT(onChannelReady(Tp::PendingOperation *))); + +} + +void TpSessionChannel::onChannelCreated(Tp::PendingOperation *op) +{ + // qDebug() << "TpSessionChannel::onOutgoingChannelCreated" ; + if (op->isError()) { + qWarning() << "Connection cannot become connected" ; + return; + } + Tp::PendingChannel *pc = qobject_cast(op); + + channel = Tp::TextChannel::create(pc->connection(),pc->objectPath(), pc->immutableProperties()); + + connect(channel->becomeReady(Tp::TextChannel::FeatureMessageQueue | Tp::TextChannel::FeatureMessageSentSignal), + SIGNAL(finished(Tp::PendingOperation*)), + SLOT(onChannelReady(Tp::PendingOperation*))); +} + +void TpSessionChannel::onChannelReady(Tp::PendingOperation *op) +{ + // qDebug() << "TpSessionChannel::onChannelReady type=" << channel->channelType() <<"path " << channel->objectPath() << + // "initiatorContact=" << (channel->initiatorContact() ? channel->initiatorContact()->id():"NULL") ; + ; + connect(channel.data(), + SIGNAL(messageReceived(const Tp::ReceivedMessage &)), + SLOT(onMessageReceived(const Tp::ReceivedMessage &))); + connect(channel.data(), + SIGNAL(messageSent(const Tp::Message &,Tp::MessageSendingFlags, const QString &)), + SLOT(onMessageSent(const Tp::Message &,Tp::MessageSendingFlags, const QString &))); + connect(channel.data(),SIGNAL(destroyed(QObject *)),SLOT(onChannelDestroyed(QObject *))); + Q_EMIT channelReady(this); + peerContact=channel->initiatorContact(); + QList queuedMessages = channel->messageQueue(); + Q_FOREACH(Tp::ReceivedMessage message, queuedMessages) { + // qDebug() << "received " << message.text(); + Q_EMIT messageReceived(message,this); + } +} +/** + * Send message to to ths channel + * + * + * \param message message to send + */ + +void TpSessionChannel::sendMessage(QString message) +{ + channel->send(message); +} +void TpSessionChannel::onMessageReceived(const Tp::ReceivedMessage &msg) +{ + // qDebug() << "TpSessionChannel::onMessageReceived " << msg.text(); + Q_EMIT messageReceived(msg,this); +}; +void TpSessionChannel::onMessageSent(const Tp::Message &msg,Tp::MessageSendingFlags sflags, const QString &flags) +{ + // qDebug() << "TpSessionChannel::onMessageSent"; + Q_EMIT messageSent(msg,sflags,flags,this); +}; +/** + * Get id ( address of your peer ) + * + * + * \returns your peer id/address ir empty QString + */ +QString TpSessionChannel::peerId() +{ + return peerContact ? peerContact->id():""; +} + +void TpSessionChannel::onChannelDestroyed(QObject *obj) +{ + // qDebug() << "TpSessionChannel::onChannelDestroyed"; + //TpSessionChannel *call = (TpSessionChannel *) obj; + Q_EMIT channelDestroyed(this); +} + diff --git a/sms/tpsession/tpsessionchannel.h b/sms/tpsession/tpsessionchannel.h new file mode 100755 index 0000000..182eea4 --- /dev/null +++ b/sms/tpsession/tpsessionchannel.h @@ -0,0 +1,60 @@ +/* + * This file is part of TpSession + * + * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Contact Kate Alhola kate.alholanokia.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef TPSESSIONCHANNEL_H +#define TPSESSIONCHANNEL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class TpSessionChannel : public QObject +{ + Q_OBJECT +public: + TpSessionChannel(Tp::TextChannelPtr); + TpSessionChannel(Tp::ConnectionPtr conn,const Tp::ContactPtr &contact); + void sendMessage(QString message); + QString peerId(); +Q_SIGNALS: + void channelReady(TpSessionChannel *); + void channelDestroyed(TpSessionChannel *); + void messageReceived(const Tp::ReceivedMessage &,TpSessionChannel *); + void messageSent(const Tp::Message &,Tp::MessageSendingFlags, const QString &,TpSessionChannel *); +public Q_SLOTS: + void onChannelCreated(Tp::PendingOperation *op); + void onChannelReady(Tp::PendingOperation *op); + void onChannelDestroyed(QObject *); + void onMessageReceived(const Tp::ReceivedMessage &); + void onMessageSent(const Tp::Message &,Tp::MessageSendingFlags, const QString &); +public: + Tp::ContactPtr peerContact; + Tp::TextChannelPtr channel; +}; + +#endif // TPSESSIONCHANNEL_H diff --git a/sms/tpsession/tpsessionobserver.cpp b/sms/tpsession/tpsessionobserver.cpp new file mode 100755 index 0000000..211cd80 --- /dev/null +++ b/sms/tpsession/tpsessionobserver.cpp @@ -0,0 +1,37 @@ +#include "tpsessionobserver.h" +#include "tpsession.h" +#include + +TpSessionObserver::TpSessionObserver(const Tp::ChannelClassList &channelFilter,TpSession *session):Tp::AbstractClientObserver(channelFilter) +{ + tpSession=session; + qDebug() << __PRETTY_FUNCTION__ ; +} + +void TpSessionObserver::observeChannels(const Tp::MethodInvocationContextPtr<> &context, + const Tp::AccountPtr &account, + const Tp::ConnectionPtr &connection, + const QList &channels, + const Tp::ChannelDispatchOperationPtr &dispatchOperation, + const QList &requestsSatisfied, + const QVariantMap &observerInfo) +{ + Q_UNUSED(dispatchOperation) + Q_UNUSED(requestsSatisfied) + Q_UNUSED(observerInfo) + Q_UNUSED(connection) + + qDebug() << "TpSessionObserver::observeChannels"; + + Q_FOREACH( Tp::ChannelPtr channel, channels ) + { + QVariantMap properties = channel->immutableProperties(); + QString channelType = properties.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString(); + if( !channelType.isNull() && !channelType.isEmpty()) + { + qDebug() << "ChannelType=" << channelType; + tpSession->createChannelListener(channelType, context, account, channel); + } + } +} + diff --git a/sms/tpsession/tpsessionobserver.h b/sms/tpsession/tpsessionobserver.h new file mode 100755 index 0000000..80cb661 --- /dev/null +++ b/sms/tpsession/tpsessionobserver.h @@ -0,0 +1,31 @@ +#ifndef TPSESSIONOBSERVER_H +#define TPSESSIONOBSERVER_H + +#include +#include +#include +#include + +class TpSession; + +class TpSessionObserver : public QObject , public Tp::AbstractClientObserver +{ + Q_OBJECT +public: + TpSessionObserver(const Tp::ChannelClassList &channelfilter,TpSession *session); + TpSession *tpSession; + + + /*! + * \brief Realisation of Tp::AbstractClientObserver + */ + virtual void observeChannels(const Tp::MethodInvocationContextPtr<> &context, + const Tp::AccountPtr &account, + const Tp::ConnectionPtr &connection, + const QList &channels, + const Tp::ChannelDispatchOperationPtr &dispatchOperation, + const QList &requestsSatisfied, + const QVariantMap &observerInfo); +}; + +#endif // TPSESSIONOBSERVER_H diff --git a/sms/utility.cpp b/sms/utility.cpp new file mode 100644 index 0000000..c3da34d --- /dev/null +++ b/sms/utility.cpp @@ -0,0 +1,35 @@ +#include "utility.h" + +Utility::Utility(QObject *parent) : + QObject(parent) +{ +} + +QIcon Utility::getToolButtonIcon(const QString &iconFileName, bool active) +{ + QIcon icon(iconFileName); + if (!active) + { + QPixmap normalPixmap = icon.pixmap(16, 16, QIcon::Disabled, QIcon::Off); + QPixmap activePixmap = icon.pixmap(16, 16, QIcon::Normal, QIcon::Off); + icon.addPixmap(normalPixmap, QIcon::Normal, QIcon::Off); + icon.addPixmap(activePixmap, QIcon::Active, QIcon::Off); + } else + { + QPixmap activePixmap = icon.pixmap(16, 16, QIcon::Disabled, QIcon::Off); + icon.addPixmap(activePixmap, QIcon::Active, QIcon::Off); + } + return icon; +} + +QPixmap Utility::getIconPixmap(const QString &iconFileName, bool active) +{ + QIcon icon(iconFileName); + if (!active) + { + return icon.pixmap(16, 16, QIcon::Normal, QIcon::Off); + } else + { + return icon.pixmap(16, 16, QIcon::Disabled, QIcon::Off); + } +} diff --git a/sms/utility.h b/sms/utility.h new file mode 100644 index 0000000..9402c90 --- /dev/null +++ b/sms/utility.h @@ -0,0 +1,18 @@ +#ifndef UTILITY_H +#define UTILITY_H + +#include +#include + +class Utility : public QObject +{ + Q_OBJECT +public: + Utility(QObject *parent = 0); + + static QIcon getToolButtonIcon(const QString &iconFileName, bool active = false); + static QPixmap getIconPixmap(const QString &iconFileName, bool active = false); + +}; + +#endif // UTILITY_H diff --git a/sms/xmlcontroler.cpp b/sms/xmlcontroler.cpp new file mode 100644 index 0000000..8946da8 --- /dev/null +++ b/sms/xmlcontroler.cpp @@ -0,0 +1,656 @@ +#include +#include + +#include "xmlcontroler.h" +#include "xmlstring.h" +#include "groupwidgetitem.h" + +XmlControler* XmlControler::instance = 0; + +XmlControler* XmlControler::getInstance() +{ + if( !instance ) + { + instance = new XmlControler(); + } + return instance; +} + +XmlControler::XmlControler(QObject *parent) : + QObject(parent) +{ + instance = this; +} + +XmlControler::~XmlControler() +{ + closeXmlFile(); +} + +void XmlControler::closeXmlFile() +{ + if( m_XmlFileOut->isOpen() ) + { + m_XmlFileOut->close(); + delete m_XmlFileOut; + m_XmlFileOut = NULL; + } + + if( m_XmlFileIn->isOpen() ) + { + writeXml( m_XmlFileIn ); + m_XmlFileIn->close(); + delete m_XmlFileIn; + m_XmlFileIn = NULL; + } +} + +bool XmlControler::newXml( const QString &filename ) +{ + //qDebug() << "XmlControler::newXml( const QString &filename ), Entry"; + + m_filename = filename; + m_XmlFileOut = new QFile( filename ); + if( !m_XmlFileOut->open( QFile::ReadWrite | QFile::Text ) ) + { + //qDebug() << "open file is failed:" << m_XmlFileOut->errorString(); + return false; + } + + newXml( m_XmlFileOut ); + m_XmlFileOut->close(); + delete m_XmlFileOut; + m_XmlFileOut = NULL; + + return true; +} + +bool XmlControler::newXml( QIODevice *device ) +{ + //qDebug() << "XmlControler::newXml( QIODevice *device ), Entry"; + + const int Indent = 4; + + QTextStream out( device ); + QDomNode xmlNode = m_DomDoc.createProcessingInstruction( "xml", + "version=\"1.0\" encoding=\"UTF-8\""); + m_DomDoc.insertBefore( xmlNode, m_DomDoc.firstChild() ); + + QDomElement root = m_DomDoc.createElement( STR_XML_GROUPSMS ); + root.setAttribute( STR_XML_VERSION, STR_XML_VERSION_NUMBER ); + m_DomDoc.appendChild( root ); + + m_DomDoc.save( out, Indent ); + return true; +} + +bool XmlControler::readXml( const QString &filename ) +{ + //qDebug() << "XmlControler::readXml( const QString &filename ), Entry"; + + m_filename = filename; + m_XmlFileOut = new QFile( filename ); + if( !m_XmlFileOut->open( QFile::ReadOnly | QFile::Text ) ) + { + //qDebug() << "open file is failed:" << m_XmlFileOut->errorString(); + return false; + } + + if( !readXml( m_XmlFileOut ) ) + { + return false; + } + + return true; +} + +bool XmlControler::readXml( QIODevice *device ) +{ + //qDebug() << "XmlControler::readXml( QIODevice *device ), Entry"; + + QString errorStr; + int errorLine; + int errorColumn; + + if( !m_DomDoc.setContent( device, true, &errorStr, &errorLine, &errorColumn ) ) + { + QMessageBox::information( NULL, tr("XML file"), + tr("Parse error at line %1, column %2:\n%3") + .arg(errorLine) + .arg(errorColumn) + .arg(errorStr) ); + return false; + } + + return true; +} + +bool XmlControler::writeXml( QIODevice *device ) +{ + //qDebug() << "XmlControler::writeXml( QIODevice *device ), Entry"; + + const int IndentSize = 4; + + QTextStream out(device); + m_DomDoc.save( out, IndentSize ); + return true; +} + +bool XmlControler::writeXml() +{ + //qDebug() << "XmlControler::writeXml(), Entry"; + + const int IndentSize = 4; + + m_XmlFileIn = new QFile( m_filename ); + if( !m_XmlFileIn->open( QFile::WriteOnly | QFile::Text ) ) + { + //qDebug() << "open file is failed:" << m_XmlFileIn->errorString(); + return false; + } + + QTextStream out(m_XmlFileIn); + m_DomDoc.save( out, IndentSize ); + m_XmlFileIn->close(); + delete m_XmlFileIn; + m_XmlFileIn = NULL; + return true; +} + +bool XmlControler::createAllContacts() +{ + //qDebug() << "XmlControler::createAllContacts, Entry"; + + QDomElement root = m_DomDoc.documentElement(); + QDomElement allcontacts = m_DomDoc.createElement( STR_XML_ALLCONTACTS ); + root.appendChild( allcontacts ); + + QDomElement node_contact = m_DomDoc.createElement( STR_XML_CONTACT ); + allcontacts.appendChild( node_contact ); + QDomText contact_fullname = m_DomDoc.createTextNode( "Tom for Test" ); + node_contact.appendChild( contact_fullname ); + QDomText contact_mobilenumber = m_DomDoc.createTextNode( "number for Test" ); + node_contact.appendChild( contact_mobilenumber ); + + m_DomDoc.appendChild( root ); + + return true; +} + +bool XmlControler::createGroup(const QString &groupname) +{ + //qDebug() << "XmlControler::createGroup(const QString &groupname), Entry"; + + findGroup( groupname ); + if( foundgroup ) + return true; + + QDomElement root = m_DomDoc.documentElement(); + QDomElement group = m_DomDoc.createElement( STR_XML_GROUP ); + group.setAttribute( STR_XML_GROUP_NAME, groupname ); + + + root.appendChild( group ); + writeXml(); + + Item *item = new Item(); + + item->group_name = groupname; + item->m_isGroup = true; + + itemObserver->addGroup( item ); + + return true; +} + +bool XmlControler::createContact(const QString &group, ItemListPtr contacts) +{ + QDomNode root = findGroup(group); + if( !foundgroup ) + { + qDebug() << "could not find this" << group << "existed. create it."; + createGroup( group ); + root = findGroup(group); + } + + for( int i = 0; i < contacts.size(); i++ ) + { + contacts.at(i)->group_owner = group; + createContact( &root, contacts.at(i) ); + } + itemObserver->addContact( contacts, group); + return true; +} + +bool XmlControler::createContact(const QString &group, Item *contact) +{ + //qDebug() << "XmlControler::createContact(const QString &group, Item *contact), Entry"; + + QDomNode root = findGroup(group); + if( !foundgroup ) + { + //qDebug() << "could not find this" << group << "existed. create it."; + createGroup( group ); + root = findGroup(group); + } + + contact->group_owner = group; + createContact( &root, contact ); + + itemObserver->addContact( contact, group); + + return true; +} + +bool XmlControler::createContact(QDomNode *group, Item *contact) +{ + //qDebug() << "XmlControler::createContact(QDomElement *group, Item *contact), Entry"; + Q_ASSERT(contact); + + QDomElement node = m_DomDoc.createElement(STR_XML_CONTACT); + node.setAttribute( STR_XML_CONTACT_UID, contact->uid); + node.setAttribute( STR_XML_CONTACT_FULL_NAME, contact->full_name); + node.setAttribute( STR_XML_CONTACT_MOBILE_NUMBER, contact->mobile_number); + node.setAttribute( STR_XML_CONTACT_PIC_URI, contact->user_pic_uri); + node.setAttribute( STR_XML_CONTACT_GROUP_OWNER, contact->group_owner); + +// node.setAttribute( STR_XML_CONTACT_UID, "1"); +// node.setAttribute( STR_XML_CONTACT_FULL_NAME, "Tom 1"); +// node.setAttribute( STR_XML_CONTACT_MOBILE_NUMBER, "12345678901"); +// node.setAttribute( STR_XML_CONTACT_PIC_URI, "/alsdj/a;sdj/pic"); +// node.setAttribute( STR_XML_CONTACT_GROUP_OWNER, "groupowner"); + + group->appendChild(node); + writeXml(); + + return true; +} + +QDomNode XmlControler::findGroup(const QString &groupname) +{ + //qDebug() << "XmlControler::findGroup(const QString &group), Entry"; + + QDomNode root; + QDomNode node; + foundgroup = false; + + node = m_DomDoc.firstChild(); + while( !node.isNull() && !foundgroup ) + { + //qDebug() << "node1 name : " << node.toElement().tagName(); + //qDebug() << "node1 attr : " << node.toElement().attribute(STR_XML_VERSION); + if( node.hasChildNodes() ) + { + QDomNode node2 = node.firstChild(); + while( !node2.isNull() ) + { + //qDebug() << "node2 name : " << node2.toElement().tagName(); + //qDebug() << "node2 attr : " << node2.toElement().attribute(STR_XML_GROUP_NAME); + + if( groupname == node2.toElement().attribute(STR_XML_GROUP_NAME) ) + { + //qDebug() << "group is found"; + root = node2; + foundgroup = true; + break; + } + + node2 = node2.nextSibling(); + } + } + + node = node.nextSibling(); + } + return root; +} + +QDomNode XmlControler::findContact(const QString &groupname, Item *contact) +{ + //qDebug() << "XmlControler::findContact(const QString &groupname, ContactWidgetItem *contact), Entry"; + + QDomNode element; + QDomNode group = findGroup(groupname); + if( !foundgroup ) + return group; + element = findContact(group, contact); + + return element; +} + +QDomNode XmlControler::findContact(QDomNode group, Item *contact) +{ + //qDebug() << "XmlControler::findContact(QDomNode *group, ContactWidgetItem *contact), Entry"; + + QDomNode element; + QDomNode node = group; + foundcontact = false; + while( !node.isNull() && !foundcontact ) + { + if( node.hasChildNodes() ) + { + QDomNode node2 = node.firstChild(); + while( !node2.isNull() ) + { + //qDebug() << "node2 name : " << node2.toElement().tagName(); + //qDebug() << "node2 uid : " << node2.toElement().attribute(STR_XML_CONTACT_UID); + + if( contact->uid == node2.toElement().attribute(STR_XML_CONTACT_UID) ) + //if( "1" == node2.toElement().attribute(STR_XML_CONTACT_UID) ) // for Test + { + //qDebug() << "contact is found"; + element = node2; + foundcontact = true; + break; + } + + node2 = node2.nextSibling(); + } + } + + node = node.nextSibling(); + } + + return element; +} + +bool XmlControler::removeGroup(const QString &groupname) +{ + //qDebug() << "XmlControler::removeGroup(const QString &groupname), Entry"; + + QDomNode group = findGroup(groupname); + if( !foundgroup ) + return false; + QDomElement root = m_DomDoc.documentElement(); + root.removeChild(group); + writeXml(); + + return true; +} + +bool XmlControler::removeContact( Item *contact ) +{ + //qDebug() << "XmlControler::removeContact(const QString &group, ContactWidgetItem *contact), Entry"; + + QDomNode root = findGroup( contact->group_owner ); + if( !foundgroup ) + { + //qDebug() << "root is NULL"; + return false; + } + removeContact( root, contact ); + + return true; +} + +bool XmlControler::removeContact(const QString &group, Item *contact) +{ + //qDebug() << "XmlControler::removeContact(const QString &group, ContactWidgetItem *contact), Entry"; + + QDomNode root = findGroup(group); + if( !foundgroup ) + { + //qDebug() << "root is NULL"; + return false; + } + removeContact( root, contact ); + + return true; +} + +bool XmlControler::removeContact(QDomNode group, Item *contact) +{ + //qDebug() << "XmlControler::removeContact(QDomNode *group, ContactWidgetItem *contact), Entry"; + + QDomNode node = findContact(group, contact); + if( node.isNull() ) + { + //qDebug() << "node is NULL"; + return false; + } + QDomNode del = group.removeChild(node); + if( del.isNull() ) + { + //qDebug() << "del node is NULL"; + return false; + } + writeXml(); + itemObserver->removeContact(contact); + + return true; +} + +bool XmlControler::cleanAllContacts() +{ + //TODO : clean all contacts in observer. + itemObserver->removeAllContacts(); + + return true; +} + +bool XmlControler::cleanAllContactsGroup() +{ + for( int i = 0; i < all_contacts_items_group.size(); i++ ) + { + delete all_contacts_items_group.at(i); + } + all_contacts_items_group.clear(); + + return true; +} + +void XmlControler::setItemObserver(ItemObserver *observer) +{ + itemObserver = observer; +} + +void XmlControler::setItemSelectObserver( ItemSelectObserver *observer ) +{ + itemSelectObserver = observer; +} + +void XmlControler::getAllContactItems() +{ + //qDebug() << "XmlControler::getAllContactItems(), Entry"; + + QDomElement root = m_DomDoc.documentElement(); + QDomNode node = root.firstChild(); + QString tagname; + + cleanAllContacts(); + + while( !node.isNull() ) + { + tagname = node.toElement().tagName(); + //qDebug() << "node tagname : " << tagname; + + if( STR_XML_GROUP == tagname ) + { + //qDebug() << "node is : " << STR_XML_GROUP; + + Item *item = new Item(); + + item->group_name = node.toElement().attribute(STR_XML_GROUP_NAME); + item->m_isGroup = true; + + itemObserver->addGroup( item ); + + if( node.hasChildNodes() ) + { + QDomNode node2 = node.firstChild(); + while( !node2.isNull() ) + { + tagname = node2.toElement().tagName(); + if( STR_XML_CONTACT == tagname ) + { + //qDebug() << "node is : " << STR_XML_CONTACT; + + Item *item = new Item(); + item->m_isGroup = false; + + item->full_name = node2.toElement().attribute(STR_XML_CONTACT_FULL_NAME); + //qDebug() << "fullname is : " << node2.toElement().attribute(STR_XML_CONTACT_FULL_NAME); + + item->mobile_number = node2.toElement().attribute(STR_XML_CONTACT_MOBILE_NUMBER); + //qDebug() << "mobile number is : " << node2.toElement().attribute(STR_XML_CONTACT_MOBILE_NUMBER); + + item->group_owner = node2.toElement().attribute(STR_XML_CONTACT_GROUP_OWNER); + //qDebug() << "group owner is : " << node2.toElement().attribute(STR_XML_CONTACT_GROUP_OWNER); + + item->uid = node2.toElement().attribute(STR_XML_CONTACT_UID); + //qDebug() << "uid is : " << node2.toElement().attribute(STR_XML_CONTACT_UID); + + itemObserver->addContact( item ); + } + node2 = node2.nextSibling(); + } + } + } + node = node.nextSibling(); + } + //qDebug() << "XmlControler::getAllContactItems(), Exit"; +} + +QStringList XmlControler::getAllGroupNames() +{ + //qDebug() << "XmlControler::getAllGroupNames(), Entry"; + + QDomElement root = m_DomDoc.documentElement(); + QDomNode node = root.firstChild(); + QString tagname; + QStringList strlist; + + while( !node.isNull() ) + { + tagname = node.toElement().tagName(); + + if( STR_XML_GROUP == tagname ) + { + //qDebug() << "group name : " << node.toElement().attribute(STR_XML_GROUP_NAME); + strlist.append( node.toElement().attribute(STR_XML_GROUP_NAME) ); + } + node = node.nextSibling(); + } + //qDebug() << "XmlControler::getAllGroupNames(), Exit"; + + return strlist; +} + +void XmlControler::getAllContactItemsFromGroup( QString groupname ) +{ + //qDebug() << "XmlControler::getAllContactItemsFromGroup( QString groupname ), Entry"; + + QDomElement root = m_DomDoc.documentElement(); + QDomNode node = root.firstChild(); + QString tagname; + QString group; + + if( old_groupname == groupname ) + { + itemSelectObserver->getGroupContacts( all_contacts_items_group ); + return; + } + + cleanAllContactsGroup(); + + while( !node.isNull() ) + { + tagname = node.toElement().tagName(); + group = node.toElement().attribute(STR_XML_GROUP_NAME); + //qDebug() << "node tagname : " << tagname << "group:" << group; + + if( (STR_XML_GROUP == tagname) && (group == groupname) ) + { + //qDebug() << "node is : " << STR_XML_GROUP; + + Item *item = new Item(); + + item->group_name = node.toElement().attribute(STR_XML_GROUP_NAME); + item->m_isGroup = true; + + all_contacts_items_group.append( item ); + + if( node.hasChildNodes() ) + { + QDomNode node2 = node.firstChild(); + while( !node2.isNull() ) + { + tagname = node2.toElement().tagName(); + if( STR_XML_CONTACT == tagname ) + { + //qDebug() << "node is : " << STR_XML_CONTACT; + + Item *item = new Item(); + //qDebug() << "new ContactWidgetItem"; + + item->full_name = node2.toElement().attribute(STR_XML_CONTACT_FULL_NAME); + //qDebug() << "fullname is : " << node2.toElement().attribute(STR_XML_CONTACT_FULL_NAME); + + item->mobile_number = node2.toElement().attribute(STR_XML_CONTACT_MOBILE_NUMBER); + //qDebug() << "mobile number is : " << node2.toElement().attribute(STR_XML_CONTACT_MOBILE_NUMBER); + + item->group_owner = node2.toElement().attribute(STR_XML_CONTACT_GROUP_OWNER); + //qDebug() << "group owner is : " << node2.toElement().attribute(STR_XML_CONTACT_GROUP_OWNER); + + item->uid = node2.toElement().attribute(STR_XML_CONTACT_UID); + //qDebug() << "uid is : " << node2.toElement().attribute(STR_XML_CONTACT_UID); + + all_contacts_items_group.append( item ); + } + node2 = node2.nextSibling(); + } + } + } + node = node.nextSibling(); + } + itemSelectObserver->getGroupContacts( all_contacts_items_group ); + old_groupname = groupname; + //qDebug() << "XmlControler::getAllContactItemsFromGroup( QString groupname ), Exit"; +} + +bool XmlControler::removeContactFromGroup( ItemListPtr items, const QString &groupname ) +{ + QDomNode root = findGroup(groupname); + if( !foundgroup ) + { + //qDebug() << "root is NULL"; + return false; + } + for( int i = 0; items.size(); i++ ) + { + removeContact( root, items.at(i) ); + } + itemObserver->refreshContactsList(); + + return true; +} + +bool XmlControler::removeContactFromGroup( ItemListPtr items ) +{ + for( int i = 0; i < items.size(); i++ ) + { + removeContact( items.at(i) ); + } + itemObserver->refreshContactsList(); + + return true; +} + + + + + + + + + + + + + + + + + + + + diff --git a/sms/xmlcontroler.h b/sms/xmlcontroler.h new file mode 100644 index 0000000..d53f0dd --- /dev/null +++ b/sms/xmlcontroler.h @@ -0,0 +1,85 @@ +#ifndef XMLCONTROLER_H +#define XMLCONTROLER_H + +#include +#include + +#include "contactwidgetitem.h" +#include "itemobserver.h" + +class XmlControler : public QObject +{ + Q_OBJECT +public: + static XmlControler* getInstance(); + ~XmlControler(); + + void setItemObserver( ItemObserver *observer ); + void setItemSelectObserver( ItemSelectObserver *observer ); + + bool newXml( const QString &filename ); + bool newXml( QIODevice *device ); + + bool readXml( const QString &filename ); + bool readXml( QIODevice *device ); + + bool writeXml( QIODevice *device ); + bool writeXml(); + + void closeXmlFile(); + + bool createGroup(const QString &groupname); + bool createContact(const QString &group, ItemListPtr contacts); + bool createContact(const QString &group, Item *contact); + bool createContact(QDomNode *group, Item *contact); + + bool removeGroup(const QString &groupname); + bool removeContact(const QString &group, Item *contact); + bool removeContact(QDomNode group, Item *contact); + bool removeContact( Item *contact ); + bool removeContactFromGroup( ItemListPtr items, const QString &groupname ); + bool removeContactFromGroup( ItemListPtr items ); + + void getAllContactItems(); + void getAllContactItemsFromGroup( QString groupname ); + + QStringList getAllGroupNames(); + + +private: + static XmlControler* instance; + XmlControler( QObject *parent = 0 ); + + bool createAllContacts(); + QDomNode findGroup(const QString &groupname); + QDomNode findContact(const QString &groupname, Item *contact); + QDomNode findContact(QDomNode group, Item *contact); + + bool cleanAllContacts(); + bool cleanAllContactsGroup(); + +private: + ItemObserver *itemObserver; + ItemSelectObserver *itemSelectObserver; + + QDomDocument m_DomDoc; + + ItemListPtr all_contacts_items; + ItemListPtr all_contacts_items_group; + + QFile *m_XmlFileOut; + QFile *m_XmlFileIn; + QString m_filename; + + bool foundgroup; + bool foundcontact; + + QString old_groupname; + +Q_SIGNALS: + +public Q_SLOTS: + +}; + +#endif // XMLCONTROLER_H diff --git a/sms/xmlstring.h b/sms/xmlstring.h new file mode 100644 index 0000000..e11932c --- /dev/null +++ b/sms/xmlstring.h @@ -0,0 +1,20 @@ +#ifndef XMLSTRING_H +#define XMLSTRING_H + +#include + +const QString STR_XML_GROUPSMS = "GroupSMS"; +const QString STR_XML_VERSION = "Version"; +const QString STR_XML_VERSION_NUMBER = "0.1"; + +const QString STR_XML_GROUP = "Group"; +const QString STR_XML_GROUP_NAME = "GroupName"; +const QString STR_XML_ALLCONTACTS = "All_Contacts"; +const QString STR_XML_CONTACT = "Contact"; +const QString STR_XML_CONTACT_UID = "Uid"; +const QString STR_XML_CONTACT_FULL_NAME = "Full_Name"; +const QString STR_XML_CONTACT_MOBILE_NUMBER = "Mobile_Number"; +const QString STR_XML_CONTACT_GROUP_OWNER = "Group_Owner"; +const QString STR_XML_CONTACT_PIC_URI = "Pic_Uri"; + +#endif // XMLSTRING_H diff --git a/welcome b/welcome index e69de29..a0c312c 100644 --- a/welcome +++ b/welcome @@ -0,0 +1,3 @@ +It's a Qt application. It manage group contacts by personal XML file. it 's base on local contacts DB (libosso-abook). It could be add/remove group contacts. Another, It's could send SMS to group contacts. that's use TpSession and TelepathyQt4 lib. + +Author: Tom Chen -- 1.7.9.5