Merge branch 'master' into fileUpload
@ -1,16 +1,19 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(squawk)
|
||||
|
||||
# Find includes in corresponding build directories
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
# Instruct CMake to run moc automatically when needed.
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
# Instruct CMake to create code from Qt designer ui files
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include_directories(.)
|
||||
|
||||
find_package(Qt5Widgets CONFIG REQUIRED)
|
||||
find_package(Qt5LinguistTools)
|
||||
|
||||
set(squawk_SRC
|
||||
main.cpp
|
||||
global.cpp
|
||||
@ -18,16 +21,46 @@ set(squawk_SRC
|
||||
signalcatcher.cpp
|
||||
)
|
||||
|
||||
add_executable(squawk ${squawk_SRC} resources/resources.qrc)
|
||||
configure_file(resources/images/logo.svg squawk.svg COPYONLY)
|
||||
execute_process(COMMAND convert -background none -size 48x48 squawk.svg squawk48.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
execute_process(COMMAND convert -background none -size 64x64 squawk.svg squawk64.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
execute_process(COMMAND convert -background none -size 128x128 squawk.svg squawk128.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
execute_process(COMMAND convert -background none -size 256x256 squawk.svg squawk256.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
configure_file(packaging/squawk.desktop squawk.desktop COPYONLY)
|
||||
|
||||
set(TS_FILES
|
||||
translations/squawk.ru.ts
|
||||
)
|
||||
qt5_add_translation(QM_FILES ${TS_FILES})
|
||||
add_custom_target(translations ALL DEPENDS ${QM_FILES})
|
||||
|
||||
qt5_add_resources(RCC resources/resources.qrc)
|
||||
|
||||
add_executable(squawk ${squawk_SRC} ${RCC})
|
||||
target_link_libraries(squawk Qt5::Widgets)
|
||||
|
||||
option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
|
||||
|
||||
if(NOT SYSTEM_QXMPP)
|
||||
add_subdirectory(external/qxmpp)
|
||||
endif()
|
||||
|
||||
add_subdirectory(ui)
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(external/qxmpp)
|
||||
|
||||
target_link_libraries(squawk squawkUI)
|
||||
target_link_libraries(squawk squawkCORE)
|
||||
target_link_libraries(squawk uuid)
|
||||
|
||||
add_dependencies(${CMAKE_PROJECT_NAME} translations)
|
||||
|
||||
# Install the executable
|
||||
install(TARGETS squawk DESTINATION bin)
|
||||
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/squawk/l10n)
|
||||
install(FILES squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
|
||||
install(FILES squawk48.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/48x48/apps RENAME squawk.png)
|
||||
install(FILES squawk64.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps RENAME squawk.png)
|
||||
install(FILES squawk128.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps RENAME squawk.png)
|
||||
install(FILES squawk256.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/256x256/apps RENAME squawk.png)
|
||||
install(FILES squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
||||
|
@ -1,12 +1,12 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(squawkCORE)
|
||||
|
||||
# Instruct CMake to run moc automatically when needed.
|
||||
set (CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
find_package(Qt5Widgets CONFIG REQUIRED)
|
||||
find_package(Qt5Core CONFIG REQUIRED)
|
||||
find_package(Qt5Gui CONFIG REQUIRED)
|
||||
find_package(Qt5Network CONFIG REQUIRED)
|
||||
find_package(Qt5Xml CONFIG REQUIRED)
|
||||
|
||||
set(squawkCORE_SRC
|
||||
squawk.cpp
|
||||
@ -17,16 +17,23 @@ set(squawkCORE_SRC
|
||||
conference.cpp
|
||||
storage.cpp
|
||||
networkaccess.cpp
|
||||
adapterFuctions.cpp
|
||||
)
|
||||
|
||||
# Tell CMake to create the helloworld executable
|
||||
add_library(squawkCORE ${squawkCORE_SRC})
|
||||
|
||||
if(SYSTEM_QXMPP)
|
||||
find_package(QXmpp CONFIG REQUIRED)
|
||||
get_target_property(QXMPP_INTERFACE_INCLUDE_DIRECTORIES QXmpp::QXmpp INTERFACE_INCLUDE_DIRECTORIES)
|
||||
target_include_directories(squawkCORE PUBLIC ${QXMPP_INTERFACE_INCLUDE_DIRECTORIES})
|
||||
endif()
|
||||
|
||||
|
||||
# Use the Widgets module from Qt 5.
|
||||
target_link_libraries(squawkCORE Qt5::Core)
|
||||
target_link_libraries(squawkCORE Qt5::Network)
|
||||
target_link_libraries(squawkCORE Qt5::Gui)
|
||||
target_link_libraries(squawkCORE Qt5::Xml)
|
||||
target_link_libraries(squawkCORE qxmpp)
|
||||
target_link_libraries(squawkCORE lmdb)
|
||||
|
||||
# Install the executable
|
||||
install(TARGETS squawkCORE DESTINATION lib)
|
||||
|
528
core/account.cpp
@ -36,49 +36,99 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
|
||||
am(new QXmppMamManager()),
|
||||
mm(new QXmppMucManager()),
|
||||
bm(new QXmppBookmarkManager()),
|
||||
rm(client.findExtension<QXmppRosterManager>()),
|
||||
vm(client.findExtension<QXmppVCardManager>()),
|
||||
contacts(),
|
||||
conferences(),
|
||||
maxReconnectTimes(0),
|
||||
reconnectTimes(0),
|
||||
queuedContacts(),
|
||||
outOfRosterContacts()
|
||||
outOfRosterContacts(),
|
||||
avatarHash(),
|
||||
avatarType(),
|
||||
ownVCardRequestInProgress(false)
|
||||
{
|
||||
config.setUser(p_login);
|
||||
config.setDomain(p_server);
|
||||
config.setPassword(p_password);
|
||||
config.setAutoAcceptSubscriptions(true);
|
||||
|
||||
QObject::connect(&client, SIGNAL(connected()), this, SLOT(onClientConnected()));
|
||||
QObject::connect(&client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected()));
|
||||
QObject::connect(&client, SIGNAL(presenceReceived(const QXmppPresence&)), this, SLOT(onPresenceReceived(const QXmppPresence&)));
|
||||
QObject::connect(&client, SIGNAL(messageReceived(const QXmppMessage&)), this, SLOT(onMessageReceived(const QXmppMessage&)));
|
||||
QObject::connect(&client, SIGNAL(error(QXmppClient::Error)), this, SLOT(onClientError(QXmppClient::Error)));
|
||||
QObject::connect(&client, &QXmppClient::connected, this, &Account::onClientConnected);
|
||||
QObject::connect(&client, &QXmppClient::disconnected, this, &Account::onClientDisconnected);
|
||||
QObject::connect(&client, &QXmppClient::presenceReceived, this, &Account::onPresenceReceived);
|
||||
QObject::connect(&client, &QXmppClient::messageReceived, this, &Account::onMessageReceived);
|
||||
QObject::connect(&client, &QXmppClient::error, this, &Account::onClientError);
|
||||
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
|
||||
QObject::connect(&rm, SIGNAL(rosterReceived()), this, SLOT(onRosterReceived()));
|
||||
QObject::connect(&rm, SIGNAL(itemAdded(const QString&)), this, SLOT(onRosterItemAdded(const QString&)));
|
||||
QObject::connect(&rm, SIGNAL(itemRemoved(const QString&)), this, SLOT(onRosterItemRemoved(const QString&)));
|
||||
QObject::connect(&rm, SIGNAL(itemChanged(const QString&)), this, SLOT(onRosterItemChanged(const QString&)));
|
||||
//QObject::connect(&rm, SIGNAL(presenceChanged(const QString&, const QString&)), this, SLOT(onRosterPresenceChanged(const QString&, const QString&)));
|
||||
QObject::connect(rm, &QXmppRosterManager::rosterReceived, this, &Account::onRosterReceived);
|
||||
QObject::connect(rm, &QXmppRosterManager::itemAdded, this, &Account::onRosterItemAdded);
|
||||
QObject::connect(rm, &QXmppRosterManager::itemRemoved, this, &Account::onRosterItemRemoved);
|
||||
QObject::connect(rm, &QXmppRosterManager::itemChanged, this, &Account::onRosterItemChanged);
|
||||
//QObject::connect(&rm, &QXmppRosterManager::presenceChanged, this, &Account::onRosterPresenceChanged);
|
||||
|
||||
client.addExtension(cm);
|
||||
|
||||
QObject::connect(cm, SIGNAL(messageReceived(const QXmppMessage&)), this, SLOT(onCarbonMessageReceived(const QXmppMessage&)));
|
||||
QObject::connect(cm, SIGNAL(messageSent(const QXmppMessage&)), this, SLOT(onCarbonMessageSent(const QXmppMessage&)));
|
||||
QObject::connect(cm, &QXmppCarbonManager::messageReceived, this, &Account::onCarbonMessageReceived);
|
||||
QObject::connect(cm, &QXmppCarbonManager::messageSent, this, &Account::onCarbonMessageSent);
|
||||
|
||||
client.addExtension(am);
|
||||
|
||||
QObject::connect(am, SIGNAL(logMessage(QXmppLogger::MessageType, const QString&)), this, SLOT(onMamLog(QXmppLogger::MessageType, const QString)));
|
||||
QObject::connect(am, SIGNAL(archivedMessageReceived(const QString&, const QXmppMessage&)), this, SLOT(onMamMessageReceived(const QString&, const QXmppMessage&)));
|
||||
QObject::connect(am, SIGNAL(resultsRecieved(const QString&, const QXmppResultSetReply&, bool)),
|
||||
this, SLOT(onMamResultsReceived(const QString&, const QXmppResultSetReply&, bool)));
|
||||
QObject::connect(am, &QXmppMamManager::logMessage, this, &Account::onMamLog);
|
||||
QObject::connect(am, &QXmppMamManager::archivedMessageReceived, this, &Account::onMamMessageReceived);
|
||||
QObject::connect(am, &QXmppMamManager::resultsRecieved, this, &Account::onMamResultsReceived);
|
||||
|
||||
client.addExtension(mm);
|
||||
QObject::connect(mm, SIGNAL(roomAdded(QXmppMucRoom*)), this, SLOT(onMucRoomAdded(QXmppMucRoom*)));
|
||||
QObject::connect(mm, &QXmppMucManager::roomAdded, this, &Account::onMucRoomAdded);
|
||||
|
||||
client.addExtension(bm);
|
||||
QObject::connect(bm, SIGNAL(bookmarksReceived(const QXmppBookmarkSet&)), this, SLOT(bookmarksReceived(const QXmppBookmarkSet&)));
|
||||
QObject::connect(bm, &QXmppBookmarkManager::bookmarksReceived, this, &Account::bookmarksReceived);
|
||||
|
||||
QObject::connect(vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived);
|
||||
//QObject::connect(&vm, &QXmppVCardManager::clientVCardReceived, this, &Account::onOwnVCardReceived); //for some reason it doesn't work, launching from common handler
|
||||
|
||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||
path += "/" + name;
|
||||
QDir dir(path);
|
||||
|
||||
if (!dir.exists()) {
|
||||
bool res = dir.mkpath(path);
|
||||
if (!res) {
|
||||
qDebug() << "Couldn't create a cache directory for account" << name;
|
||||
throw 22;
|
||||
}
|
||||
}
|
||||
|
||||
QFile* avatar = new QFile(path + "/avatar.png");
|
||||
QString type = "png";
|
||||
if (!avatar->exists()) {
|
||||
delete avatar;
|
||||
avatar = new QFile(path + "/avatar.jpg");
|
||||
type = "jpg";
|
||||
if (!avatar->exists()) {
|
||||
delete avatar;
|
||||
avatar = new QFile(path + "/avatar.jpeg");
|
||||
type = "jpeg";
|
||||
if (!avatar->exists()) {
|
||||
delete avatar;
|
||||
avatar = new QFile(path + "/avatar.gif");
|
||||
type = "gif";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (avatar->exists()) {
|
||||
if (avatar->open(QFile::ReadOnly)) {
|
||||
QCryptographicHash sha1(QCryptographicHash::Sha1);
|
||||
sha1.addData(avatar);
|
||||
avatarHash = sha1.result();
|
||||
avatarType = type;
|
||||
}
|
||||
}
|
||||
if (avatarType.size() != 0) {
|
||||
presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
|
||||
presence.setPhotoHash(avatarHash.toUtf8());
|
||||
} else {
|
||||
presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady);
|
||||
}
|
||||
}
|
||||
|
||||
Account::~Account()
|
||||
@ -189,8 +239,10 @@ QString Core::Account::getServer() const
|
||||
|
||||
void Core::Account::onRosterReceived()
|
||||
{
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
QStringList bj = rm.getRosterBareJids();
|
||||
vm->requestClientVCard(); //TODO need to make sure server actually supports vCards
|
||||
ownVCardRequestInProgress = true;
|
||||
|
||||
QStringList bj = rm->getRosterBareJids();
|
||||
for (int i = 0; i < bj.size(); ++i) {
|
||||
const QString& jid = bj[i];
|
||||
addedAccount(jid);
|
||||
@ -210,8 +262,7 @@ void Core::Account::onRosterItemAdded(const QString& bareJid)
|
||||
addedAccount(bareJid);
|
||||
std::map<QString, QString>::const_iterator itr = queuedContacts.find(bareJid);
|
||||
if (itr != queuedContacts.end()) {
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
rm.subscribe(bareJid, itr->second);
|
||||
rm->subscribe(bareJid, itr->second);
|
||||
queuedContacts.erase(itr);
|
||||
}
|
||||
}
|
||||
@ -224,8 +275,7 @@ void Core::Account::onRosterItemChanged(const QString& bareJid)
|
||||
return;
|
||||
}
|
||||
Contact* contact = itr->second;
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
QXmppRosterIq::Item re = rm.getRosterEntry(bareJid);
|
||||
QXmppRosterIq::Item re = rm->getRosterEntry(bareJid);
|
||||
|
||||
Shared::SubscriptionState state = castSubscriptionState(re.subscriptionType());
|
||||
|
||||
@ -254,9 +304,8 @@ void Core::Account::onRosterItemRemoved(const QString& bareJid)
|
||||
|
||||
void Core::Account::addedAccount(const QString& jid)
|
||||
{
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
||||
QXmppRosterIq::Item re = rm.getRosterEntry(jid);
|
||||
QXmppRosterIq::Item re = rm->getRosterEntry(jid);
|
||||
Contact* contact;
|
||||
bool newContact = false;
|
||||
if (itr == contacts.end()) {
|
||||
@ -279,6 +328,19 @@ void Core::Account::addedAccount(const QString& jid)
|
||||
{"name", re.name()},
|
||||
{"state", state}
|
||||
});
|
||||
|
||||
if (contact->hasAvatar()) {
|
||||
if (!contact->isAvatarAutoGenerated()) {
|
||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
|
||||
} else {
|
||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
|
||||
}
|
||||
cData.insert("avatarPath", contact->avatarPath());
|
||||
} else {
|
||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
|
||||
cData.insert("avatarPath", "");
|
||||
requestVCard(jid);
|
||||
}
|
||||
int grCount = 0;
|
||||
for (QSet<QString>::const_iterator itr = gr.begin(), end = gr.end(); itr != end; ++itr) {
|
||||
const QString& groupName = *itr;
|
||||
@ -296,39 +358,35 @@ void Core::Account::addedAccount(const QString& jid)
|
||||
|
||||
void Core::Account::handleNewRosterItem(Core::RosterItem* contact)
|
||||
{
|
||||
|
||||
QObject::connect(contact, SIGNAL(needHistory(const QString&, const QString&, const QDateTime&)), this, SLOT(onContactNeedHistory(const QString&, const QString&, const QDateTime&)));
|
||||
QObject::connect(contact, SIGNAL(historyResponse(const std::list<Shared::Message>&)), this, SLOT(onContactHistoryResponse(const std::list<Shared::Message>&)));
|
||||
QObject::connect(contact, SIGNAL(nameChanged(const QString&)), this, SLOT(onContactNameChanged(const QString&)));
|
||||
QObject::connect(contact, &RosterItem::needHistory, this, &Account::onContactNeedHistory);
|
||||
QObject::connect(contact, &RosterItem::historyResponse, this, &Account::onContactHistoryResponse);
|
||||
QObject::connect(contact, &RosterItem::nameChanged, this, &Account::onContactNameChanged);
|
||||
QObject::connect(contact, &RosterItem::avatarChanged, this, &Account::onContactAvatarChanged);
|
||||
}
|
||||
|
||||
void Core::Account::handleNewContact(Core::Contact* contact)
|
||||
{
|
||||
handleNewRosterItem(contact);
|
||||
QObject::connect(contact, SIGNAL(groupAdded(const QString&)), this, SLOT(onContactGroupAdded(const QString&)));
|
||||
QObject::connect(contact, SIGNAL(groupRemoved(const QString&)), this, SLOT(onContactGroupRemoved(const QString&)));
|
||||
QObject::connect(contact, SIGNAL(subscriptionStateChanged(Shared::SubscriptionState)),
|
||||
this, SLOT(onContactSubscriptionStateChanged(Shared::SubscriptionState)));
|
||||
QObject::connect(contact, &Contact::groupAdded, this, &Account::onContactGroupAdded);
|
||||
QObject::connect(contact, &Contact::groupRemoved, this, &Account::onContactGroupRemoved);
|
||||
QObject::connect(contact, &Contact::subscriptionStateChanged, this, &Account::onContactSubscriptionStateChanged);
|
||||
}
|
||||
|
||||
void Core::Account::handleNewConference(Core::Conference* contact)
|
||||
{
|
||||
handleNewRosterItem(contact);
|
||||
QObject::connect(contact, SIGNAL(nickChanged(const QString&)), this, SLOT(onMucNickNameChanged(const QString&)));
|
||||
QObject::connect(contact, SIGNAL(subjectChanged(const QString&)), this, SLOT(onMucSubjectChanged(const QString&)));
|
||||
QObject::connect(contact, SIGNAL(joinedChanged(bool)), this, SLOT(onMucJoinedChanged(bool)));
|
||||
QObject::connect(contact, SIGNAL(autoJoinChanged(bool)), this, SLOT(onMucAutoJoinChanged(bool)));
|
||||
QObject::connect(contact, SIGNAL(addParticipant(const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onMucAddParticipant(const QString&, const QMap<QString, QVariant>&)));
|
||||
QObject::connect(contact, SIGNAL(changeParticipant(const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onMucChangeParticipant(const QString&, const QMap<QString, QVariant>&)));
|
||||
QObject::connect(contact, SIGNAL(removeParticipant(const QString&)), this, SLOT(onMucRemoveParticipant(const QString&)));
|
||||
QObject::connect(contact, &Conference::nickChanged, this, &Account::onMucNickNameChanged);
|
||||
QObject::connect(contact, &Conference::subjectChanged, this, &Account::onMucSubjectChanged);
|
||||
QObject::connect(contact, &Conference::joinedChanged, this, &Account::onMucJoinedChanged);
|
||||
QObject::connect(contact, &Conference::autoJoinChanged, this, &Account::onMucAutoJoinChanged);
|
||||
QObject::connect(contact, &Conference::addParticipant, this, &Account::onMucAddParticipant);
|
||||
QObject::connect(contact, &Conference::changeParticipant, this, &Account::onMucChangeParticipant);
|
||||
QObject::connect(contact, &Conference::removeParticipant, this, &Account::onMucRemoveParticipant);
|
||||
}
|
||||
|
||||
|
||||
void Core::Account::onPresenceReceived(const QXmppPresence& presence)
|
||||
void Core::Account::onPresenceReceived(const QXmppPresence& p_presence)
|
||||
{
|
||||
QString id = presence.from();
|
||||
QString id = p_presence.from();
|
||||
QStringList comps = id.split("/");
|
||||
QString jid = comps.front();
|
||||
QString resource = comps.back();
|
||||
@ -337,25 +395,75 @@ void Core::Account::onPresenceReceived(const QXmppPresence& presence)
|
||||
|
||||
if (jid == myJid) {
|
||||
if (resource == getResource()) {
|
||||
emit availabilityChanged(presence.availableStatusType());
|
||||
emit availabilityChanged(p_presence.availableStatusType());
|
||||
} else {
|
||||
qDebug() << "Received a presence for another resource of my " << name << " account, skipping";
|
||||
if (!ownVCardRequestInProgress) {
|
||||
switch (p_presence.vCardUpdateType()) {
|
||||
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
|
||||
break;
|
||||
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
|
||||
break;
|
||||
case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any
|
||||
if (avatarType.size() > 0) {
|
||||
vm->requestClientVCard();
|
||||
ownVCardRequestInProgress = true;
|
||||
}
|
||||
break;
|
||||
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
|
||||
if (avatarHash != p_presence.photoHash()) {
|
||||
vm->requestClientVCard();
|
||||
ownVCardRequestInProgress = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
|
||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
||||
if (itr != contacts.end()) {
|
||||
Contact* cnt = itr->second;
|
||||
switch (p_presence.vCardUpdateType()) {
|
||||
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
|
||||
break;
|
||||
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
|
||||
break;
|
||||
case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any
|
||||
if (!cnt->hasAvatar() || (cnt->hasAvatar() && !cnt->isAvatarAutoGenerated())) {
|
||||
cnt->setAutoGeneratedAvatar();
|
||||
}
|
||||
break;
|
||||
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
|
||||
if (cnt->hasAvatar()) {
|
||||
if (cnt->isAvatarAutoGenerated()) {
|
||||
requestVCard(jid);
|
||||
} else {
|
||||
if (cnt->avatarHash() != p_presence.photoHash()) {
|
||||
requestVCard(jid);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requestVCard(jid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (presence.type()) {
|
||||
switch (p_presence.type()) {
|
||||
case QXmppPresence::Error:
|
||||
qDebug() << "An error reported by presence from " << id;
|
||||
qDebug() << "An error reported by presence from" << id << p_presence.error().text();
|
||||
break;
|
||||
case QXmppPresence::Available:{
|
||||
QDateTime lastInteraction = presence.lastUserInteraction();
|
||||
QDateTime lastInteraction = p_presence.lastUserInteraction();
|
||||
if (!lastInteraction.isValid()) {
|
||||
lastInteraction = QDateTime::currentDateTime();
|
||||
}
|
||||
emit addPresence(jid, resource, {
|
||||
{"lastActivity", lastInteraction},
|
||||
{"availability", presence.availableStatusType()}, //TODO check and handle invisible
|
||||
{"status", presence.statusText()}
|
||||
{"availability", p_presence.availableStatusType()}, //TODO check and handle invisible
|
||||
{"status", p_presence.statusText()}
|
||||
});
|
||||
}
|
||||
break;
|
||||
@ -380,7 +488,7 @@ void Core::Account::onRosterPresenceChanged(const QString& bareJid, const QStrin
|
||||
{
|
||||
//not used for now;
|
||||
qDebug() << "presence changed for " << bareJid << " resource " << resource;
|
||||
const QXmppPresence& presence = client.rosterManager().getPresence(bareJid, resource);
|
||||
const QXmppPresence& presence = rm->getPresence(bareJid, resource);
|
||||
}
|
||||
|
||||
void Core::Account::setLogin(const QString& p_login)
|
||||
@ -940,8 +1048,7 @@ void Core::Account::onClientError(QXmppClient::Error err)
|
||||
void Core::Account::subscribeToContact(const QString& jid, const QString& reason)
|
||||
{
|
||||
if (state == Shared::connected) {
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
rm.subscribe(jid, reason);
|
||||
rm->subscribe(jid, reason);
|
||||
} else {
|
||||
qDebug() << "An attempt to subscribe account " << name << " to contact " << jid << " but the account is not in the connected state, skipping";
|
||||
}
|
||||
@ -950,8 +1057,7 @@ void Core::Account::subscribeToContact(const QString& jid, const QString& reason
|
||||
void Core::Account::unsubscribeFromContact(const QString& jid, const QString& reason)
|
||||
{
|
||||
if (state == Shared::connected) {
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
rm.unsubscribe(jid, reason);
|
||||
rm->unsubscribe(jid, reason);
|
||||
} else {
|
||||
qDebug() << "An attempt to unsubscribe account " << name << " from contact " << jid << " but the account is not in the connected state, skipping";
|
||||
}
|
||||
@ -965,8 +1071,7 @@ void Core::Account::removeContactRequest(const QString& jid)
|
||||
outOfRosterContacts.erase(itr);
|
||||
onRosterItemRemoved(jid);
|
||||
} else {
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
rm.removeItem(jid);
|
||||
rm->removeItem(jid);
|
||||
}
|
||||
} else {
|
||||
qDebug() << "An attempt to remove contact " << jid << " from account " << name << " but the account is not in the connected state, skipping";
|
||||
@ -982,8 +1087,7 @@ void Core::Account::addContactRequest(const QString& jid, const QString& name, c
|
||||
qDebug() << "An attempt to add contact " << jid << " to account " << name << " but the account is already queued for adding, skipping";
|
||||
} else {
|
||||
queuedContacts.insert(std::make_pair(jid, "")); //TODO need to add reason here;
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
rm.addItem(jid, name, groups);
|
||||
rm->addItem(jid, name, groups);
|
||||
}
|
||||
} else {
|
||||
qDebug() << "An attempt to add contact " << jid << " to account " << name << " but the account is not in the connected state, skipping";
|
||||
@ -1153,3 +1257,297 @@ void Core::Account::addNewRoom(const QString& jid, const QString& nick, const QS
|
||||
{"name", conf->getName()}
|
||||
});
|
||||
}
|
||||
|
||||
void Core::Account::addContactToGroupRequest(const QString& jid, const QString& groupName)
|
||||
{
|
||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
||||
if (itr == contacts.end()) {
|
||||
qDebug() << "An attempt to add non existing contact" << jid << "of account" << name << "to the group" << groupName << ", skipping";
|
||||
} else {
|
||||
QXmppRosterIq::Item item = rm->getRosterEntry(jid);
|
||||
QSet<QString> groups = item.groups();
|
||||
if (groups.find(groupName) == groups.end()) { //TODO need to change it, I guess that sort of code is better in qxmpp lib
|
||||
groups.insert(groupName);
|
||||
item.setGroups(groups);
|
||||
|
||||
QXmppRosterIq iq;
|
||||
iq.setType(QXmppIq::Set);
|
||||
iq.addItem(item);
|
||||
client.sendPacket(iq);
|
||||
} else {
|
||||
qDebug() << "An attempt to add contact" << jid << "of account" << name << "to the group" << groupName << "but it's already in that group, skipping";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Core::Account::removeContactFromGroupRequest(const QString& jid, const QString& groupName)
|
||||
{
|
||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
||||
if (itr == contacts.end()) {
|
||||
qDebug() << "An attempt to remove non existing contact" << jid << "of account" << name << "from the group" << groupName << ", skipping";
|
||||
} else {
|
||||
QXmppRosterIq::Item item = rm->getRosterEntry(jid);
|
||||
QSet<QString> groups = item.groups();
|
||||
QSet<QString>::const_iterator gItr = groups.find(groupName);
|
||||
if (gItr != groups.end()) {
|
||||
groups.erase(gItr);
|
||||
item.setGroups(groups);
|
||||
|
||||
QXmppRosterIq iq;
|
||||
iq.setType(QXmppIq::Set);
|
||||
iq.addItem(item);
|
||||
client.sendPacket(iq);
|
||||
} else {
|
||||
qDebug() << "An attempt to remove contact" << jid << "of account" << name << "from the group" << groupName << "but it's not in that group, skipping";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Core::Account::renameContactRequest(const QString& jid, const QString& newName)
|
||||
{
|
||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
||||
if (itr == contacts.end()) {
|
||||
qDebug() << "An attempt to rename non existing contact" << jid << "of account" << name << ", skipping";
|
||||
} else {
|
||||
rm->renameItem(jid, newName);
|
||||
}
|
||||
}
|
||||
|
||||
void Core::Account::onVCardReceived(const QXmppVCardIq& card)
|
||||
{
|
||||
QString jid = card.from();
|
||||
pendingVCardRequests.erase(jid);
|
||||
RosterItem* item = 0;
|
||||
|
||||
std::map<QString, Contact*>::const_iterator contItr = contacts.find(jid);
|
||||
if (contItr == contacts.end()) {
|
||||
std::map<QString, Conference*>::const_iterator confItr = conferences.find(jid);
|
||||
if (confItr == conferences.end()) {
|
||||
if (jid == getLogin() + "@" + getServer()) {
|
||||
onOwnVCardReceived(card);
|
||||
} else {
|
||||
qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping";
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
item = confItr->second;
|
||||
}
|
||||
} else {
|
||||
item = contItr->second;
|
||||
}
|
||||
|
||||
QByteArray ava = card.photo();
|
||||
|
||||
if (ava.size() > 0) {
|
||||
item->setAvatar(ava);
|
||||
} else {
|
||||
if (!item->hasAvatar() || !item->isAvatarAutoGenerated()) {
|
||||
item->setAutoGeneratedAvatar();
|
||||
}
|
||||
}
|
||||
|
||||
Shared::VCard vCard;
|
||||
initializeVCard(vCard, card);
|
||||
|
||||
if (item->hasAvatar()) {
|
||||
if (!item->isAvatarAutoGenerated()) {
|
||||
vCard.setAvatarType(Shared::Avatar::valid);
|
||||
} else {
|
||||
vCard.setAvatarType(Shared::Avatar::autocreated);
|
||||
}
|
||||
vCard.setAvatarPath(item->avatarPath());
|
||||
} else {
|
||||
vCard.setAvatarType(Shared::Avatar::empty);
|
||||
}
|
||||
|
||||
QMap<QString, QVariant> cd = {
|
||||
{"avatarState", static_cast<quint8>(vCard.getAvatarType())},
|
||||
{"avatarPath", vCard.getAvatarPath()}
|
||||
};
|
||||
emit changeContact(jid, cd);
|
||||
emit receivedVCard(jid, vCard);
|
||||
}
|
||||
|
||||
void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card)
|
||||
{
|
||||
QByteArray ava = card.photo();
|
||||
bool avaChanged = false;
|
||||
QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/";
|
||||
if (ava.size() > 0) {
|
||||
QCryptographicHash sha1(QCryptographicHash::Sha1);
|
||||
sha1.addData(ava);
|
||||
QString newHash(sha1.result());
|
||||
QMimeDatabase db;
|
||||
QMimeType newType = db.mimeTypeForData(ava);
|
||||
if (avatarType.size() > 0) {
|
||||
if (avatarHash != newHash) {
|
||||
QString oldPath = path + "avatar." + avatarType;
|
||||
QFile oldAvatar(oldPath);
|
||||
bool oldToRemove = false;
|
||||
if (oldAvatar.exists()) {
|
||||
if (oldAvatar.rename(oldPath + ".bak")) {
|
||||
oldToRemove = true;
|
||||
} else {
|
||||
qDebug() << "Received new avatar for account" << name << "but can't get rid of the old one, doing nothing";
|
||||
}
|
||||
}
|
||||
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
|
||||
if (newAvatar.open(QFile::WriteOnly)) {
|
||||
newAvatar.write(ava);
|
||||
newAvatar.close();
|
||||
avatarHash = newHash;
|
||||
avatarType = newType.preferredSuffix();
|
||||
avaChanged = true;
|
||||
} else {
|
||||
qDebug() << "Received new avatar for account" << name << "but can't save it";
|
||||
if (oldToRemove) {
|
||||
qDebug() << "rolling back to the old avatar";
|
||||
if (!oldAvatar.rename(oldPath)) {
|
||||
qDebug() << "Couldn't roll back to the old avatar in account" << name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
|
||||
if (newAvatar.open(QFile::WriteOnly)) {
|
||||
newAvatar.write(ava);
|
||||
newAvatar.close();
|
||||
avatarHash = newHash;
|
||||
avatarType = newType.preferredSuffix();
|
||||
avaChanged = true;
|
||||
} else {
|
||||
qDebug() << "Received new avatar for account" << name << "but can't save it";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (avatarType.size() > 0) {
|
||||
QFile oldAvatar(path + "avatar." + avatarType);
|
||||
if (!oldAvatar.remove()) {
|
||||
qDebug() << "Received vCard for account" << name << "without avatar, but can't get rid of the file, doing nothing";
|
||||
} else {
|
||||
avatarType = "";
|
||||
avatarHash = "";
|
||||
avaChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (avaChanged) {
|
||||
QMap<QString, QVariant> change;
|
||||
if (avatarType.size() > 0) {
|
||||
presence.setPhotoHash(avatarHash.toUtf8());
|
||||
presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
|
||||
change.insert("avatarPath", path + "avatar." + avatarType);
|
||||
} else {
|
||||
presence.setPhotoHash("");
|
||||
presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto);
|
||||
change.insert("avatarPath", "");
|
||||
}
|
||||
client.setClientPresence(presence);
|
||||
emit changed(change);
|
||||
}
|
||||
|
||||
ownVCardRequestInProgress = false;
|
||||
|
||||
Shared::VCard vCard;
|
||||
initializeVCard(vCard, card);
|
||||
|
||||
if (avatarType.size() > 0) {
|
||||
vCard.setAvatarType(Shared::Avatar::valid);
|
||||
vCard.setAvatarPath(path + "avatar." + avatarType);
|
||||
} else {
|
||||
vCard.setAvatarType(Shared::Avatar::empty);
|
||||
}
|
||||
|
||||
emit receivedVCard(getLogin() + "@" + getServer(), vCard);
|
||||
}
|
||||
|
||||
QString Core::Account::getAvatarPath() const
|
||||
{
|
||||
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/" + "avatar." + avatarType;
|
||||
}
|
||||
|
||||
void Core::Account::onContactAvatarChanged(Shared::Avatar type, const QString& path)
|
||||
{
|
||||
RosterItem* item = static_cast<RosterItem*>(sender());
|
||||
QMap<QString, QVariant> cData({
|
||||
{"avatarState", static_cast<uint>(type)},
|
||||
{"avatarPath", path}
|
||||
});
|
||||
|
||||
emit changeContact(item->jid, cData);
|
||||
}
|
||||
|
||||
void Core::Account::requestVCard(const QString& jid)
|
||||
{
|
||||
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
|
||||
if (jid == getLogin() + "@" + getServer()) {
|
||||
if (!ownVCardRequestInProgress) {
|
||||
vm->requestClientVCard();
|
||||
ownVCardRequestInProgress = true;
|
||||
}
|
||||
} else {
|
||||
vm->requestVCard(jid);
|
||||
pendingVCardRequests.insert(jid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Core::Account::uploadVCard(const Shared::VCard& card)
|
||||
{
|
||||
QXmppVCardIq iq;
|
||||
initializeQXmppVCard(iq, card);
|
||||
|
||||
bool avatarChanged = false;
|
||||
if (card.getAvatarType() == Shared::Avatar::empty) {
|
||||
if (avatarType.size() > 0) {
|
||||
avatarChanged = true;
|
||||
}
|
||||
} else {
|
||||
QString newPath = card.getAvatarPath();
|
||||
QString oldPath = getAvatarPath();
|
||||
QByteArray data;
|
||||
QString type;
|
||||
if (newPath != oldPath) {
|
||||
QFile avatar(newPath);
|
||||
if (!avatar.open(QFile::ReadOnly)) {
|
||||
qDebug() << "An attempt to upload new vCard to account" << name
|
||||
<< "but it wasn't possible to read file" << newPath
|
||||
<< "which was supposed to be new avatar, uploading old avatar";
|
||||
if (avatarType.size() > 0) {
|
||||
QFile oA(oldPath);
|
||||
if (!oA.open(QFile::ReadOnly)) {
|
||||
qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar";
|
||||
avatarChanged = true;
|
||||
} else {
|
||||
data = oA.readAll();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
data = avatar.readAll();
|
||||
avatarChanged = true;
|
||||
}
|
||||
} else {
|
||||
if (avatarType.size() > 0) {
|
||||
QFile oA(oldPath);
|
||||
if (!oA.open(QFile::ReadOnly)) {
|
||||
qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar";
|
||||
avatarChanged = true;
|
||||
} else {
|
||||
data = oA.readAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.size() > 0) {
|
||||
QMimeDatabase db;
|
||||
type = db.mimeTypeForData(data).name();
|
||||
iq.setPhoto(data);
|
||||
iq.setPhotoType(type);
|
||||
}
|
||||
}
|
||||
|
||||
vm->setClientVCard(iq);
|
||||
onOwnVCardReceived(iq);
|
||||
}
|
||||
|
@ -19,7 +19,13 @@
|
||||
#ifndef CORE_ACCOUNT_H
|
||||
#define CORE_ACCOUNT_H
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QObject>
|
||||
#include <QCryptographicHash>
|
||||
#include <QFile>
|
||||
#include <QMimeDatabase>
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
@ -31,6 +37,8 @@
|
||||
#include <QXmppBookmarkManager.h>
|
||||
#include <QXmppBookmarkSet.h>
|
||||
#include <QXmppHttpUploadIq.h>
|
||||
#include <QXmppVCardIq.h>
|
||||
#include <QXmppVCardManager.h>
|
||||
#include "../global.h"
|
||||
#include "contact.h"
|
||||
#include "conference.h"
|
||||
@ -55,6 +63,7 @@ public:
|
||||
QString getServer() const;
|
||||
QString getPassword() const;
|
||||
QString getResource() const;
|
||||
QString getAvatarPath() const;
|
||||
Shared::Availability getAvailability() const;
|
||||
|
||||
void setName(const QString& p_name);
|
||||
@ -71,13 +80,19 @@ public:
|
||||
void unsubscribeFromContact(const QString& jid, const QString& reason);
|
||||
void removeContactRequest(const QString& jid);
|
||||
void addContactRequest(const QString& jid, const QString& name, const QSet<QString>& groups);
|
||||
void addContactToGroupRequest(const QString& jid, const QString& groupName);
|
||||
void removeContactFromGroupRequest(const QString& jid, const QString& groupName);
|
||||
void renameContactRequest(const QString& jid, const QString& newName);
|
||||
|
||||
void setRoomJoined(const QString& jid, bool joined);
|
||||
void setRoomAutoJoin(const QString& jid, bool joined);
|
||||
void removeRoomRequest(const QString& jid);
|
||||
void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
||||
void requestVCard(const QString& jid);
|
||||
void uploadVCard(const Shared::VCard& card);
|
||||
|
||||
signals:
|
||||
void changed(const QMap<QString, QVariant>& data);
|
||||
void connectionStateChanged(int);
|
||||
void availabilityChanged(int);
|
||||
void addGroup(const QString& name);
|
||||
@ -97,6 +112,7 @@ signals:
|
||||
void addRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
||||
void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
||||
void removeRoomParticipant(const QString& jid, const QString& nickName);
|
||||
void receivedVCard(const QString& jid, const Shared::VCard& card);
|
||||
|
||||
private:
|
||||
QString name;
|
||||
@ -110,6 +126,8 @@ private:
|
||||
QXmppMamManager* am;
|
||||
QXmppMucManager* mm;
|
||||
QXmppBookmarkManager* bm;
|
||||
QXmppRosterManager* rm;
|
||||
QXmppVCardManager* vm;
|
||||
std::map<QString, Contact*> contacts;
|
||||
std::map<QString, Conference*> conferences;
|
||||
unsigned int maxReconnectTimes;
|
||||
@ -117,6 +135,11 @@ private:
|
||||
|
||||
std::map<QString, QString> queuedContacts;
|
||||
std::set<QString> outOfRosterContacts;
|
||||
std::set<QString> pendingVCardRequests;
|
||||
|
||||
QString avatarHash;
|
||||
QString avatarType;
|
||||
bool ownVCardRequestInProgress;
|
||||
|
||||
private slots:
|
||||
void onClientConnected();
|
||||
@ -155,9 +178,13 @@ private slots:
|
||||
void onContactSubscriptionStateChanged(Shared::SubscriptionState state);
|
||||
void onContactHistoryResponse(const std::list<Shared::Message>& list);
|
||||
void onContactNeedHistory(const QString& before, const QString& after, const QDateTime& at);
|
||||
void onContactAvatarChanged(Shared::Avatar, const QString& path);
|
||||
|
||||
void onMamLog(QXmppLogger::MessageType type, const QString &msg);
|
||||
|
||||
void onVCardReceived(const QXmppVCardIq& card);
|
||||
void onOwnVCardReceived(const QXmppVCardIq& card);
|
||||
|
||||
private:
|
||||
void addedAccount(const QString &bareJid);
|
||||
void handleNewContact(Contact* contact);
|
||||
@ -176,6 +203,8 @@ private:
|
||||
|
||||
};
|
||||
|
||||
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);
|
||||
void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard);
|
||||
}
|
||||
|
||||
|
||||
|
275
core/adapterFuctions.cpp
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Squawk messenger.
|
||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef CORE_ADAPTER_FUNCTIONS_H
|
||||
#define CORE_ADAPTER_FUNCTIONS_H
|
||||
|
||||
#include "account.h"
|
||||
|
||||
void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card)
|
||||
{
|
||||
vCard.setFullName(card.fullName());
|
||||
vCard.setFirstName(card.firstName());
|
||||
vCard.setMiddleName(card.middleName());
|
||||
vCard.setLastName(card.lastName());
|
||||
vCard.setBirthday(card.birthday());
|
||||
vCard.setNickName(card.nickName());
|
||||
vCard.setDescription(card.description());
|
||||
vCard.setUrl(card.url());
|
||||
QXmppVCardOrganization org = card.organization();
|
||||
vCard.setOrgName(org.organization());
|
||||
vCard.setOrgRole(org.role());
|
||||
vCard.setOrgUnit(org.unit());
|
||||
vCard.setOrgTitle(org.title());
|
||||
|
||||
QList<QXmppVCardEmail> emails = card.emails();
|
||||
std::deque<Shared::VCard::Email>& myEmails = vCard.getEmails();
|
||||
for (const QXmppVCardEmail& em : emails) {
|
||||
QString addr = em.address();
|
||||
if (addr.size() != 0) {
|
||||
QXmppVCardEmail::Type et = em.type();
|
||||
bool prefered = false;
|
||||
bool accounted = false;
|
||||
if (et & QXmppVCardEmail::Preferred) {
|
||||
prefered = true;
|
||||
}
|
||||
if (et & QXmppVCardEmail::Home) {
|
||||
myEmails.emplace_back(addr, Shared::VCard::Email::home, prefered);
|
||||
accounted = true;
|
||||
}
|
||||
if (et & QXmppVCardEmail::Work) {
|
||||
myEmails.emplace_back(addr, Shared::VCard::Email::work, prefered);
|
||||
accounted = true;
|
||||
}
|
||||
if (!accounted) {
|
||||
myEmails.emplace_back(addr, Shared::VCard::Email::none, prefered);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QList<QXmppVCardPhone> phones = card.phones();
|
||||
std::deque<Shared::VCard::Phone>& myPhones = vCard.getPhones();
|
||||
for (const QXmppVCardPhone& ph : phones) {
|
||||
QString num = ph.number();
|
||||
if (num.size() != 0) {
|
||||
QXmppVCardPhone::Type pt = ph.type();
|
||||
bool prefered = false;
|
||||
bool accounted = false;
|
||||
if (pt & QXmppVCardPhone::Preferred) {
|
||||
prefered = true;
|
||||
}
|
||||
|
||||
bool home = false;
|
||||
bool work = false;
|
||||
|
||||
if (pt & QXmppVCardPhone::Home) {
|
||||
home = true;
|
||||
}
|
||||
if (pt & QXmppVCardPhone::Work) {
|
||||
work = true;
|
||||
}
|
||||
|
||||
|
||||
if (pt & QXmppVCardPhone::Fax) {
|
||||
if (home || work) {
|
||||
if (home) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::fax, Shared::VCard::Phone::home, prefered);
|
||||
}
|
||||
if (work) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::fax, Shared::VCard::Phone::work, prefered);
|
||||
}
|
||||
} else {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::fax, Shared::VCard::Phone::none, prefered);
|
||||
}
|
||||
accounted = true;
|
||||
}
|
||||
if (pt & QXmppVCardPhone::Voice) {
|
||||
if (home || work) {
|
||||
if (home) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::voice, Shared::VCard::Phone::home, prefered);
|
||||
}
|
||||
if (work) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::voice, Shared::VCard::Phone::work, prefered);
|
||||
}
|
||||
} else {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::voice, Shared::VCard::Phone::none, prefered);
|
||||
}
|
||||
accounted = true;
|
||||
}
|
||||
if (pt & QXmppVCardPhone::Pager) {
|
||||
if (home || work) {
|
||||
if (home) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::pager, Shared::VCard::Phone::home, prefered);
|
||||
}
|
||||
if (work) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::pager, Shared::VCard::Phone::work, prefered);
|
||||
}
|
||||
} else {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::pager, Shared::VCard::Phone::none, prefered);
|
||||
}
|
||||
accounted = true;
|
||||
}
|
||||
if (pt & QXmppVCardPhone::Cell) {
|
||||
if (home || work) {
|
||||
if (home) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::cell, Shared::VCard::Phone::home, prefered);
|
||||
}
|
||||
if (work) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::cell, Shared::VCard::Phone::work, prefered);
|
||||
}
|
||||
} else {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::cell, Shared::VCard::Phone::none, prefered);
|
||||
}
|
||||
accounted = true;
|
||||
}
|
||||
if (pt & QXmppVCardPhone::Video) {
|
||||
if (home || work) {
|
||||
if (home) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::video, Shared::VCard::Phone::home, prefered);
|
||||
}
|
||||
if (work) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::video, Shared::VCard::Phone::work, prefered);
|
||||
}
|
||||
} else {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::video, Shared::VCard::Phone::none, prefered);
|
||||
}
|
||||
accounted = true;
|
||||
}
|
||||
if (pt & QXmppVCardPhone::Modem) {
|
||||
if (home || work) {
|
||||
if (home) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::modem, Shared::VCard::Phone::home, prefered);
|
||||
}
|
||||
if (work) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::modem, Shared::VCard::Phone::work, prefered);
|
||||
}
|
||||
} else {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::modem, Shared::VCard::Phone::none, prefered);
|
||||
}
|
||||
accounted = true;
|
||||
}
|
||||
if (!accounted) {
|
||||
if (home || work) {
|
||||
if (home) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::other, Shared::VCard::Phone::home, prefered);
|
||||
}
|
||||
if (work) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::other, Shared::VCard::Phone::work, prefered);
|
||||
}
|
||||
} else {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::other, Shared::VCard::Phone::none, prefered);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Core::initializeQXmppVCard(QXmppVCardIq& iq, const Shared::VCard& card) {
|
||||
iq.setFullName(card.getFullName());
|
||||
iq.setFirstName(card.getFirstName());
|
||||
iq.setMiddleName(card.getMiddleName());
|
||||
iq.setLastName(card.getLastName());
|
||||
iq.setNickName(card.getNickName());
|
||||
iq.setBirthday(card.getBirthday());
|
||||
iq.setDescription(card.getDescription());
|
||||
iq.setUrl(card.getUrl());
|
||||
QXmppVCardOrganization org;
|
||||
org.setOrganization(card.getOrgName());
|
||||
org.setUnit(card.getOrgUnit());
|
||||
org.setRole(card.getOrgRole());
|
||||
org.setTitle(card.getOrgTitle());
|
||||
iq.setOrganization(org);
|
||||
|
||||
const std::deque<Shared::VCard::Email>& myEmails = card.getEmails();
|
||||
QList<QXmppVCardEmail> emails;
|
||||
for (const Shared::VCard::Email& mEm : myEmails) {
|
||||
QXmppVCardEmail em;
|
||||
QXmppVCardEmail::Type t = QXmppVCardEmail::Internet;
|
||||
if (mEm.prefered) {
|
||||
t = t | QXmppVCardEmail::Preferred;
|
||||
}
|
||||
if (mEm.role == Shared::VCard::Email::home) {
|
||||
t = t | QXmppVCardEmail::Home;
|
||||
} else if (mEm.role == Shared::VCard::Email::work) {
|
||||
t = t | QXmppVCardEmail::Work;
|
||||
}
|
||||
em.setType(t);
|
||||
em.setAddress(mEm.address);
|
||||
|
||||
emails.push_back(em);
|
||||
}
|
||||
|
||||
std::map<QString, QXmppVCardPhone> phones;
|
||||
QList<QXmppVCardPhone> phs;
|
||||
const std::deque<Shared::VCard::Phone>& myPhones = card.getPhones();
|
||||
for (const Shared::VCard::Phone& mPh : myPhones) {
|
||||
std::map<QString, QXmppVCardPhone>::iterator itr = phones.find(mPh.number);
|
||||
if (itr == phones.end()) {
|
||||
itr = phones.emplace(mPh.number, QXmppVCardPhone()).first;
|
||||
}
|
||||
QXmppVCardPhone& phone = itr->second;
|
||||
phone.setNumber(mPh.number);
|
||||
|
||||
switch (mPh.type) {
|
||||
case Shared::VCard::Phone::fax:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Fax);
|
||||
break;
|
||||
case Shared::VCard::Phone::pager:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Pager);
|
||||
break;
|
||||
case Shared::VCard::Phone::voice:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Voice);
|
||||
break;
|
||||
case Shared::VCard::Phone::cell:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Cell);
|
||||
break;
|
||||
case Shared::VCard::Phone::video:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Video);
|
||||
break;
|
||||
case Shared::VCard::Phone::modem:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Modem);
|
||||
break;
|
||||
case Shared::VCard::Phone::other:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::PCS); //loss of information, but I don't even know what the heck is this type of phone!
|
||||
break;
|
||||
}
|
||||
|
||||
switch (mPh.role) {
|
||||
case Shared::VCard::Phone::home:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Home);
|
||||
break;
|
||||
case Shared::VCard::Phone::work:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Work);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (mPh.prefered) {
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Preferred);
|
||||
}
|
||||
}
|
||||
for (const std::pair<QString, QXmppVCardPhone>& phone : phones) {
|
||||
phs.push_back(phone.second);
|
||||
}
|
||||
|
||||
iq.setEmails(emails);
|
||||
iq.setPhones(phs);
|
||||
}
|
||||
|
||||
#endif // CORE_ADAPTER_FUNCTIONS_H
|
323
core/archive.cpp
@ -32,7 +32,11 @@ Core::Archive::Archive(const QString& p_jid, QObject* parent):
|
||||
environment(),
|
||||
main(),
|
||||
order(),
|
||||
stats()
|
||||
stats(),
|
||||
hasAvatar(false),
|
||||
avatarAutoGenerated(false),
|
||||
avatarHash(),
|
||||
avatarType()
|
||||
{
|
||||
}
|
||||
|
||||
@ -57,7 +61,7 @@ void Core::Archive::open(const QString& account)
|
||||
}
|
||||
|
||||
mdb_env_set_maxdbs(environment, 4);
|
||||
mdb_env_set_mapsize(environment, 1UL * 1024UL * 1024UL * 1024UL);
|
||||
mdb_env_set_mapsize(environment, 512UL * 1024UL * 1024UL);
|
||||
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
||||
|
||||
MDB_txn *txn;
|
||||
@ -66,7 +70,49 @@ void Core::Archive::open(const QString& account)
|
||||
mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY, &order);
|
||||
mdb_dbi_open(txn, "stats", MDB_CREATE, &stats);
|
||||
mdb_txn_commit(txn);
|
||||
fromTheBeginning = _isFromTheBeginning();
|
||||
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
try {
|
||||
fromTheBeginning = getStatBoolValue("beginning", txn);
|
||||
} catch (NotFound e) {
|
||||
fromTheBeginning = false;
|
||||
}
|
||||
try {
|
||||
hasAvatar = getStatBoolValue("hasAvatar", txn);
|
||||
} catch (NotFound e) {
|
||||
hasAvatar = false;
|
||||
}
|
||||
if (hasAvatar) {
|
||||
try {
|
||||
avatarAutoGenerated = getStatBoolValue("avatarAutoGenerated", txn);
|
||||
} catch (NotFound e) {
|
||||
avatarAutoGenerated = false;
|
||||
}
|
||||
|
||||
avatarType = getStatStringValue("avatarType", txn).c_str();
|
||||
if (avatarAutoGenerated) {
|
||||
avatarHash = "";
|
||||
} else {
|
||||
avatarHash = getStatStringValue("avatarHash", txn).c_str();
|
||||
}
|
||||
} else {
|
||||
avatarAutoGenerated = false;
|
||||
avatarHash = "";
|
||||
avatarType = "";
|
||||
}
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
if (hasAvatar) {
|
||||
QFile ava(path + "/avatar." + avatarType);
|
||||
if (!ava.exists()) {
|
||||
bool success = dropAvatar();
|
||||
if (!success) {
|
||||
qDebug() << "error opening archive" << jid << "for account" << account
|
||||
<< ". There is supposed to be avatar but the file doesn't exist, couldn't even drop it, it surely will lead to an error";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
opened = true;
|
||||
}
|
||||
}
|
||||
@ -396,40 +442,6 @@ std::list<Shared::Message> Core::Archive::getBefore(int count, const QString& id
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Core::Archive::_isFromTheBeginning()
|
||||
{
|
||||
std::string strKey = "beginning";
|
||||
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey.mv_size = strKey.size();
|
||||
lmdbKey.mv_data = (char*)strKey.c_str();
|
||||
|
||||
MDB_txn *txn;
|
||||
int rc;
|
||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
||||
rc = mdb_get(txn, stats, &lmdbKey, &lmdbData);
|
||||
if (rc == MDB_NOTFOUND) {
|
||||
mdb_txn_abort(txn);
|
||||
return false;
|
||||
} else if (rc) {
|
||||
qDebug() <<"isFromTheBeginning error: " << mdb_strerror(rc);
|
||||
mdb_txn_abort(txn);
|
||||
throw NotFound(strKey, jid.toStdString());
|
||||
} else {
|
||||
uint8_t value = *(uint8_t*)(lmdbData.mv_data);
|
||||
bool is;
|
||||
if (value == 144) {
|
||||
is = false;
|
||||
} else if (value == 72) {
|
||||
is = true;
|
||||
} else {
|
||||
qDebug() <<"isFromTheBeginning error: stored value doesn't match any magic number, the answer is most probably wrong";
|
||||
}
|
||||
mdb_txn_abort(txn);
|
||||
return is;
|
||||
}
|
||||
}
|
||||
|
||||
bool Core::Archive::isFromTheBeginning()
|
||||
{
|
||||
if (!opened) {
|
||||
@ -445,28 +457,17 @@ void Core::Archive::setFromTheBeginning(bool is)
|
||||
}
|
||||
if (fromTheBeginning != is) {
|
||||
fromTheBeginning = is;
|
||||
const std::string& id = "beginning";
|
||||
uint8_t value = 144;
|
||||
if (is) {
|
||||
value = 72;
|
||||
}
|
||||
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey.mv_size = id.size();
|
||||
lmdbKey.mv_data = (char*)id.c_str();
|
||||
lmdbData.mv_size = sizeof value;
|
||||
lmdbData.mv_data = &value;
|
||||
MDB_txn *txn;
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
int rc;
|
||||
rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0);
|
||||
if (rc != 0) {
|
||||
qDebug() << "Couldn't store beginning key into stat database:" << mdb_strerror(rc);
|
||||
bool success = setStatValue("beginning", is, txn);
|
||||
if (success != 0) {
|
||||
mdb_txn_abort(txn);
|
||||
}
|
||||
} else {
|
||||
mdb_txn_commit(txn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Core::Archive::printOrder()
|
||||
{
|
||||
@ -508,3 +509,221 @@ void Core::Archive::printKeys()
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_txn_abort(txn);
|
||||
}
|
||||
|
||||
bool Core::Archive::getStatBoolValue(const std::string& id, MDB_txn* txn)
|
||||
{
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey.mv_size = id.size();
|
||||
lmdbKey.mv_data = (char*)id.c_str();
|
||||
|
||||
int rc;
|
||||
rc = mdb_get(txn, stats, &lmdbKey, &lmdbData);
|
||||
if (rc == MDB_NOTFOUND) {
|
||||
throw NotFound(id, jid.toStdString());
|
||||
} else if (rc) {
|
||||
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << mdb_strerror(rc);
|
||||
throw 15; //TODO proper exception
|
||||
} else {
|
||||
uint8_t value = *(uint8_t*)(lmdbData.mv_data);
|
||||
bool is;
|
||||
if (value == 144) {
|
||||
is = false;
|
||||
} else if (value == 72) {
|
||||
is = true;
|
||||
} else {
|
||||
qDebug() << "error retrieving boolean stat" << id.c_str() << ": stored value doesn't match any magic number, the answer is most probably wrong";
|
||||
throw NotFound(id, jid.toStdString());
|
||||
}
|
||||
return is;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Core::Archive::getStatStringValue(const std::string& id, MDB_txn* txn)
|
||||
{
|
||||
MDB_cursor* cursor;
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey.mv_size = id.size();
|
||||
lmdbKey.mv_data = (char*)id.c_str();
|
||||
|
||||
int rc;
|
||||
rc = mdb_get(txn, stats, &lmdbKey, &lmdbData);
|
||||
if (rc == MDB_NOTFOUND) {
|
||||
throw NotFound(id, jid.toStdString());
|
||||
} else if (rc) {
|
||||
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << mdb_strerror(rc);
|
||||
throw 15; //TODO proper exception
|
||||
} else {
|
||||
std::string value((char*)lmdbData.mv_data, lmdbData.mv_size);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
bool Core::Archive::setStatValue(const std::string& id, bool value, MDB_txn* txn)
|
||||
{
|
||||
uint8_t binvalue = 144;
|
||||
if (value) {
|
||||
binvalue = 72;
|
||||
}
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey.mv_size = id.size();
|
||||
lmdbKey.mv_data = (char*)id.c_str();
|
||||
lmdbData.mv_size = sizeof binvalue;
|
||||
lmdbData.mv_data = &binvalue;
|
||||
int rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0);
|
||||
if (rc != 0) {
|
||||
qDebug() << "Couldn't store" << id.c_str() << "key into stat database:" << mdb_strerror(rc);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Core::Archive::setStatValue(const std::string& id, const std::string& value, MDB_txn* txn)
|
||||
{
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey.mv_size = id.size();
|
||||
lmdbKey.mv_data = (char*)id.c_str();
|
||||
lmdbData.mv_size = value.size();
|
||||
lmdbData.mv_data = (char*)value.c_str();
|
||||
int rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0);
|
||||
if (rc != 0) {
|
||||
qDebug() << "Couldn't store" << id.c_str() << "key into stat database:" << mdb_strerror(rc);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Core::Archive::getHasAvatar() const
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("getHasAvatar", jid.toStdString());
|
||||
}
|
||||
|
||||
return hasAvatar;
|
||||
}
|
||||
|
||||
bool Core::Archive::getAutoAvatar() const
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("getAutoAvatar", jid.toStdString());
|
||||
}
|
||||
|
||||
return avatarAutoGenerated;
|
||||
}
|
||||
|
||||
QString Core::Archive::getAvatarHash() const
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("getAvatarHash", jid.toStdString());
|
||||
}
|
||||
|
||||
return avatarHash;
|
||||
}
|
||||
|
||||
QString Core::Archive::getAvatarType() const
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("getAvatarType", jid.toStdString());
|
||||
}
|
||||
|
||||
return avatarType;
|
||||
}
|
||||
|
||||
bool Core::Archive::dropAvatar()
|
||||
{
|
||||
MDB_txn *txn;
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
bool success = setStatValue("hasAvatar", false, txn);
|
||||
success = success && setStatValue("avatarAutoGenerated", false, txn);
|
||||
success = success && setStatValue("avatarHash", "", txn);
|
||||
success = success && setStatValue("avatarType", "", txn);
|
||||
if (!success) {
|
||||
mdb_txn_abort(txn);
|
||||
return false;
|
||||
} else {
|
||||
hasAvatar = false;
|
||||
avatarAutoGenerated = false;
|
||||
avatarHash = "";
|
||||
avatarType = "";
|
||||
mdb_txn_commit(txn);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Core::Archive::setAvatar(const QByteArray& data, bool generated)
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("setAvatar", jid.toStdString());
|
||||
}
|
||||
|
||||
if (data.size() == 0) {
|
||||
if (!hasAvatar) {
|
||||
return false;
|
||||
} else {
|
||||
return dropAvatar();
|
||||
}
|
||||
} else {
|
||||
const char* cep;
|
||||
mdb_env_get_path(environment, &cep);
|
||||
QString currentPath(cep);
|
||||
bool needToRemoveOld = false;
|
||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||
hash.addData(data);
|
||||
QString newHash(hash.result());
|
||||
if (hasAvatar) {
|
||||
if (!generated && !avatarAutoGenerated && avatarHash == newHash) {
|
||||
return false;
|
||||
}
|
||||
QFile oldAvatar(currentPath + "/avatar." + avatarType);
|
||||
if (oldAvatar.exists()) {
|
||||
if (oldAvatar.rename(currentPath + "/avatar." + avatarType + ".bak")) {
|
||||
needToRemoveOld = true;
|
||||
} else {
|
||||
qDebug() << "Can't change avatar: couldn't get rid of the old avatar" << oldAvatar.fileName();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
QMimeDatabase db;
|
||||
QMimeType type = db.mimeTypeForData(data);
|
||||
QString ext = type.preferredSuffix();
|
||||
QFile newAvatar(currentPath + "/avatar." + ext);
|
||||
if (newAvatar.open(QFile::WriteOnly)) {
|
||||
newAvatar.write(data);
|
||||
newAvatar.close();
|
||||
|
||||
MDB_txn *txn;
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
bool success = setStatValue("hasAvatar", true, txn);
|
||||
success = success && setStatValue("avatarAutoGenerated", generated, txn);
|
||||
success = success && setStatValue("avatarHash", newHash.toStdString(), txn);
|
||||
success = success && setStatValue("avatarType", ext.toStdString(), txn);
|
||||
if (!success) {
|
||||
qDebug() << "Can't change avatar: couldn't store changes to database for" << newAvatar.fileName() << "rolling back to the previous state";
|
||||
if (needToRemoveOld) {
|
||||
QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak");
|
||||
oldAvatar.rename(currentPath + "/avatar." + avatarType);
|
||||
}
|
||||
mdb_txn_abort(txn);
|
||||
return false;
|
||||
} else {
|
||||
hasAvatar = true;
|
||||
avatarAutoGenerated = generated;
|
||||
avatarHash = newHash;
|
||||
avatarType = ext;
|
||||
mdb_txn_commit(txn);
|
||||
if (needToRemoveOld) {
|
||||
QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak");
|
||||
oldAvatar.remove();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Can't change avatar: cant open file to write" << newAvatar.fileName() << "rolling back to the previous state";
|
||||
if (needToRemoveOld) {
|
||||
QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak");
|
||||
oldAvatar.rename(currentPath + "/avatar." + avatarType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,10 @@
|
||||
#define CORE_ARCHIVE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QCryptographicHash>
|
||||
#include <QMimeDatabase>
|
||||
#include <QMimeType>
|
||||
|
||||
#include "../global.h"
|
||||
#include <lmdb.h>
|
||||
#include "../exception.h"
|
||||
@ -49,6 +53,11 @@ public:
|
||||
std::list<Shared::Message> getBefore(int count, const QString& id);
|
||||
bool isFromTheBeginning();
|
||||
void setFromTheBeginning(bool is);
|
||||
bool getHasAvatar() const;
|
||||
bool getAutoAvatar() const;
|
||||
QString getAvatarHash() const;
|
||||
QString getAvatarType() const;
|
||||
bool setAvatar(const QByteArray& data, bool generated = false);
|
||||
|
||||
public:
|
||||
const QString jid;
|
||||
@ -131,10 +140,19 @@ private:
|
||||
MDB_dbi main;
|
||||
MDB_dbi order;
|
||||
MDB_dbi stats;
|
||||
bool hasAvatar;
|
||||
bool avatarAutoGenerated;
|
||||
QString avatarHash;
|
||||
QString avatarType;
|
||||
|
||||
bool _isFromTheBeginning();
|
||||
bool getStatBoolValue(const std::string& id, MDB_txn* txn);
|
||||
std::string getStatStringValue(const std::string& id, MDB_txn* txn);
|
||||
|
||||
bool setStatValue(const std::string& id, bool value, MDB_txn* txn);
|
||||
bool setStatValue(const std::string& id, const std::string& value, MDB_txn* txn);
|
||||
void printOrder();
|
||||
void printKeys();
|
||||
bool dropAvatar();
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -30,15 +30,15 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo
|
||||
muc = true;
|
||||
name = p_name;
|
||||
|
||||
connect(room, SIGNAL(joined()), this, SLOT(onRoomJoined()));
|
||||
connect(room, SIGNAL(left()), this, SLOT(onRoomLeft()));
|
||||
connect(room, SIGNAL(nameChanged(const QString&)), this, SLOT(onRoomNameChanged(const QString&)));
|
||||
connect(room, SIGNAL(subjectChanged(const QString&)), this, SLOT(onRoomSubjectChanged(const QString&)));
|
||||
connect(room, SIGNAL(participantAdded(const QString&)), this, SLOT(onRoomParticipantAdded(const QString&)));
|
||||
connect(room, SIGNAL(participantChanged(const QString&)), this, SLOT(onRoomParticipantChanged(const QString&)));
|
||||
connect(room, SIGNAL(participantRemoved(const QString&)), this, SLOT(onRoomParticipantRemoved(const QString&)));
|
||||
connect(room, SIGNAL(nickNameChanged(const QString&)), this, SLOT(onRoomNickNameChanged(const QString&)));
|
||||
connect(room, SIGNAL(error(const QXmppStanza::Error&)), this, SLOT(onRoomError(const QXmppStanza::Error&)));
|
||||
connect(room, &QXmppMucRoom::joined, this, &Conference::onRoomJoined);
|
||||
connect(room, &QXmppMucRoom::left, this, &Conference::onRoomLeft);
|
||||
connect(room, &QXmppMucRoom::nameChanged, this, &Conference::onRoomNameChanged);
|
||||
connect(room, &QXmppMucRoom::subjectChanged, this, &Conference::onRoomSubjectChanged);
|
||||
connect(room, &QXmppMucRoom::participantAdded, this, &Conference::onRoomParticipantAdded);
|
||||
connect(room, &QXmppMucRoom::participantChanged, this, &Conference::onRoomParticipantChanged);
|
||||
connect(room, &QXmppMucRoom::participantRemoved, this, &Conference::onRoomParticipantRemoved);
|
||||
connect(room, &QXmppMucRoom::nickNameChanged, this, &Conference::onRoomNickNameChanged);
|
||||
connect(room, &QXmppMucRoom::error, this, &Conference::onRoomError);
|
||||
|
||||
room->setNickName(nick);
|
||||
if (autoJoin) {
|
||||
|
@ -331,9 +331,9 @@ void Core::NetworkAccess::startDownload(const QString& messageId, const QString&
|
||||
Transfer* dwn = new Transfer({{messageId}, 0, 0, true, "", 0});
|
||||
QNetworkRequest req(url);
|
||||
dwn->reply = manager->get(req);
|
||||
connect(dwn->reply, SIGNAL(downloadProgress(qint64, qint64)), SLOT(onDownloadProgress(qint64, qint64)));
|
||||
connect(dwn->reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onDownloadError(QNetworkReply::NetworkError)));
|
||||
connect(dwn->reply, SIGNAL(finished()), SLOT(onDownloadFinished()));
|
||||
connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress);
|
||||
connect(dwn->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::error), this, &NetworkAccess::onDownloadError);
|
||||
connect(dwn->reply, &QNetworkReply::finished, this, &NetworkAccess::onDownloadFinished);
|
||||
downloads.insert(std::make_pair(url, dwn));
|
||||
emit downloadFileProgress(messageId, 0);
|
||||
}
|
||||
|
@ -20,9 +20,10 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
Core::RosterItem::RosterItem(const QString& pJid, const QString& account, QObject* parent):
|
||||
Core::RosterItem::RosterItem(const QString& pJid, const QString& pAccount, QObject* parent):
|
||||
QObject(parent),
|
||||
jid(pJid),
|
||||
account(pAccount),
|
||||
name(),
|
||||
archiveState(empty),
|
||||
archive(new Archive(jid)),
|
||||
@ -331,3 +332,63 @@ bool Core::RosterItem::isMuc() const
|
||||
{
|
||||
return muc;
|
||||
}
|
||||
|
||||
QString Core::RosterItem::avatarHash() const
|
||||
{
|
||||
return archive->getAvatarHash();
|
||||
}
|
||||
|
||||
bool Core::RosterItem::isAvatarAutoGenerated() const
|
||||
{
|
||||
return archive->getAutoAvatar();
|
||||
}
|
||||
|
||||
QString Core::RosterItem::avatarPath() const
|
||||
{
|
||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||
path += "/" + account + "/" + jid + "/avatar." + archive->getAvatarType();
|
||||
return path;
|
||||
}
|
||||
|
||||
bool Core::RosterItem::hasAvatar() const
|
||||
{
|
||||
return archive->getHasAvatar();
|
||||
}
|
||||
|
||||
void Core::RosterItem::setAvatar(const QByteArray& data)
|
||||
{
|
||||
if (archive->setAvatar(data, false)) {
|
||||
if (archive->getHasAvatar()) {
|
||||
emit avatarChanged(Shared::Avatar::empty, "");
|
||||
} else {
|
||||
emit avatarChanged(Shared::Avatar::valid, avatarPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Core::RosterItem::setAutoGeneratedAvatar()
|
||||
{
|
||||
QImage image(96, 96, QImage::Format_ARGB32_Premultiplied);
|
||||
QPainter painter(&image);
|
||||
quint8 colorIndex = rand() % Shared::colorPalette.size();
|
||||
const QColor& bg = Shared::colorPalette[colorIndex];
|
||||
painter.fillRect(image.rect(), bg);
|
||||
QFont f;
|
||||
f.setBold(true);
|
||||
f.setPixelSize(72);
|
||||
painter.setFont(f);
|
||||
if (bg.lightnessF() > 0.5) {
|
||||
painter.setPen(Qt::black);
|
||||
} else {
|
||||
painter.setPen(Qt::white);
|
||||
}
|
||||
painter.drawText(image.rect(), Qt::AlignCenter | Qt::AlignVCenter, jid.at(0).toUpper());
|
||||
QByteArray arr;
|
||||
QBuffer stream(&arr);
|
||||
stream.open(QBuffer::WriteOnly);
|
||||
image.save(&stream, "PNG");
|
||||
stream.close();
|
||||
if (archive->setAvatar(arr, true)) {
|
||||
emit avatarChanged(Shared::Avatar::autocreated, avatarPath());
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,10 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStandardPaths>
|
||||
#include <QImage>
|
||||
#include <QPainter>
|
||||
#include <QBuffer>
|
||||
|
||||
#include <list>
|
||||
|
||||
@ -58,15 +62,23 @@ public:
|
||||
void flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId);
|
||||
void requestHistory(int count, const QString& before);
|
||||
void requestFromEmpty(int count, const QString& before);
|
||||
bool hasAvatar() const;
|
||||
bool isAvatarAutoGenerated() const;
|
||||
QString avatarHash() const;
|
||||
QString avatarPath() const;
|
||||
void setAvatar(const QByteArray& data);
|
||||
void setAutoGeneratedAvatar();
|
||||
|
||||
signals:
|
||||
void nameChanged(const QString& name);
|
||||
void subscriptionStateChanged(Shared::SubscriptionState state);
|
||||
void historyResponse(const std::list<Shared::Message>& messages);
|
||||
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime());
|
||||
void avatarChanged(Shared::Avatar, const QString& path);
|
||||
|
||||
public:
|
||||
const QString jid;
|
||||
const QString account;
|
||||
|
||||
protected:
|
||||
QString name;
|
||||
|
123
core/squawk.cpp
@ -28,11 +28,11 @@ Core::Squawk::Squawk(QObject* parent):
|
||||
amap(),
|
||||
network()
|
||||
{
|
||||
connect(&network, SIGNAL(fileLocalPathResponse(const QString&, const QString&)), this, SIGNAL(fileLocalPathResponse(const QString&, const QString&)));
|
||||
connect(&network, SIGNAL(downloadFileProgress(const QString&, qreal)), this, SIGNAL(downloadFileProgress(const QString&, qreal)));
|
||||
connect(&network, SIGNAL(downloadFileError(const QString&, const QString&)), this, SIGNAL(downloadFileError(const QString&, const QString&)));
|
||||
connect(&network, SIGNAL(uploadFileProgress(const QString&, qreal)), this, SIGNAL(uploadFileProgress(const QString&, qreal)));
|
||||
connect(&network, SIGNAL(uploadFileError(const QString&, const QString&)), this, SIGNAL(uploadFileError(const QString&, const QString&)));
|
||||
connect(&network, &NetworkAccess::fileLocalPathResponse, this, &Squawk::fileLocalPathResponse);
|
||||
connect(&network, &NetworkAccess::downloadFileProgress, this, &Squawk::downloadFileProgress);
|
||||
connect(&network, &NetworkAccess::downloadFileError, this, &Squawk::downloadFileError);
|
||||
connect(&network, &NetworkAccess::uploadFileProgress, this, &Squawk::uploadFileProgress);
|
||||
connect(&network, &NetworkAccess::uploadFileError, this, &Squawk::uploadFileError);
|
||||
}
|
||||
|
||||
Core::Squawk::~Squawk()
|
||||
@ -103,42 +103,39 @@ void Core::Squawk::newAccountRequest(const QMap<QString, QVariant>& map)
|
||||
|
||||
void Core::Squawk::addAccount(const QString& login, const QString& server, const QString& password, const QString& name, const QString& resource)
|
||||
{
|
||||
QSettings settings;
|
||||
unsigned int reconnects = settings.value("reconnects", 2).toUInt();
|
||||
|
||||
Account* acc = new Account(login, server, password, name);
|
||||
acc->setResource(resource);
|
||||
acc->setReconnectTimes(reconnects);
|
||||
accounts.push_back(acc);
|
||||
amap.insert(std::make_pair(name, acc));
|
||||
|
||||
connect(acc, SIGNAL(connectionStateChanged(int)), this, SLOT(onAccountConnectionStateChanged(int)));
|
||||
connect(acc, SIGNAL(error(const QString&)), this, SLOT(onAccountError(const QString&)));
|
||||
connect(acc, SIGNAL(availabilityChanged(int)), this, SLOT(onAccountAvailabilityChanged(int)));
|
||||
connect(acc, SIGNAL(addContact(const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onAccountAddContact(const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(acc, SIGNAL(addGroup(const QString&)), this, SLOT(onAccountAddGroup(const QString&)));
|
||||
connect(acc, SIGNAL(removeGroup(const QString&)), this, SLOT(onAccountRemoveGroup(const QString&)));
|
||||
connect(acc, SIGNAL(removeContact(const QString&)), this, SLOT(onAccountRemoveContact(const QString&)));
|
||||
connect(acc, SIGNAL(removeContact(const QString&, const QString&)), this, SLOT(onAccountRemoveContact(const QString&, const QString&)));
|
||||
connect(acc, SIGNAL(changeContact(const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onAccountChangeContact(const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(acc, SIGNAL(addPresence(const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onAccountAddPresence(const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(acc, SIGNAL(removePresence(const QString&, const QString&)), this, SLOT(onAccountRemovePresence(const QString&, const QString&)));
|
||||
connect(acc, SIGNAL(message(const Shared::Message&)), this, SLOT(onAccountMessage(const Shared::Message&)));
|
||||
connect(acc, SIGNAL(responseArchive(const QString&, const std::list<Shared::Message>&)),
|
||||
this, SLOT(onAccountResponseArchive(const QString&, const std::list<Shared::Message>&)));
|
||||
connect(acc, &Account::connectionStateChanged, this, &Squawk::onAccountConnectionStateChanged);
|
||||
connect(acc, &Account::changed, this, &Squawk::onAccountChanged);
|
||||
connect(acc, &Account::error, this, &Squawk::onAccountError);
|
||||
connect(acc, &Account::availabilityChanged, this, &Squawk::onAccountAvailabilityChanged);
|
||||
connect(acc, &Account::addContact, this, &Squawk::onAccountAddContact);
|
||||
connect(acc, &Account::addGroup, this, &Squawk::onAccountAddGroup);
|
||||
connect(acc, &Account::removeGroup, this, &Squawk::onAccountRemoveGroup);
|
||||
connect(acc, qOverload<const QString&, const QString&>(&Account::removeContact), this, qOverload<const QString&, const QString&>(&Squawk::onAccountRemoveContact));
|
||||
connect(acc, qOverload<const QString&>(&Account::removeContact), this, qOverload<const QString&>(&Squawk::onAccountRemoveContact));
|
||||
connect(acc, &Account::changeContact, this, &Squawk::onAccountChangeContact);
|
||||
connect(acc, &Account::addPresence, this, &Squawk::onAccountAddPresence);
|
||||
connect(acc, &Account::removePresence, this, &Squawk::onAccountRemovePresence);
|
||||
connect(acc, &Account::message, this, &Squawk::onAccountMessage);
|
||||
connect(acc, &Account::responseArchive, this, &Squawk::onAccountResponseArchive);
|
||||
|
||||
connect(acc, SIGNAL(addRoom(const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onAccountAddRoom(const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(acc, SIGNAL(changeRoom(const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onAccountChangeRoom(const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(acc, SIGNAL(removeRoom(const QString&)), this, SLOT(onAccountRemoveRoom(const QString&)));
|
||||
connect(acc, &Account::addRoom, this, &Squawk::onAccountAddRoom);
|
||||
connect(acc, &Account::changeRoom, this, &Squawk::onAccountChangeRoom);
|
||||
connect(acc, &Account::removeRoom, this, &Squawk::onAccountRemoveRoom);
|
||||
|
||||
connect(acc, SIGNAL(addRoomParticipant(const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onAccountAddRoomPresence(const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(acc, SIGNAL(changeRoomParticipant(const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onAccountChangeRoomPresence(const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(acc, SIGNAL(removeRoomParticipant(const QString&, const QString&)),
|
||||
this, SLOT(onAccountRemoveRoomPresence(const QString&, const QString&)));
|
||||
connect(acc, &Account::addRoomParticipant, this, &Squawk::onAccountAddRoomPresence);
|
||||
connect(acc, &Account::changeRoomParticipant, this, &Squawk::onAccountChangeRoomPresence);
|
||||
connect(acc, &Account::removeRoomParticipant, this, &Squawk::onAccountRemoveRoomPresence);
|
||||
|
||||
connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard);
|
||||
|
||||
QMap<QString, QVariant> map = {
|
||||
{"login", login},
|
||||
@ -148,8 +145,10 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const
|
||||
{"resource", resource},
|
||||
{"state", Shared::disconnected},
|
||||
{"offline", Shared::offline},
|
||||
{"error", ""}
|
||||
{"error", ""},
|
||||
{"avatarPath", acc->getAvatarPath()}
|
||||
};
|
||||
|
||||
emit newAccount(map);
|
||||
}
|
||||
|
||||
@ -261,6 +260,12 @@ void Core::Squawk::onAccountAvailabilityChanged(int state)
|
||||
emit changeAccount(acc->getName(), {{"availability", state}});
|
||||
}
|
||||
|
||||
void Core::Squawk::onAccountChanged(const QMap<QString, QVariant>& data)
|
||||
{
|
||||
Account* acc = static_cast<Account*>(sender());
|
||||
emit changeAccount(acc->getName(), data);
|
||||
}
|
||||
|
||||
void Core::Squawk::onAccountMessage(const Shared::Message& data)
|
||||
{
|
||||
Account* acc = static_cast<Account*>(sender());
|
||||
@ -504,3 +509,53 @@ void Core::Squawk::downloadFileRequest(const QString& messageId, const QString&
|
||||
{
|
||||
network.downladFileRequest(messageId, url);
|
||||
}
|
||||
|
||||
void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName)
|
||||
{
|
||||
AccountsMap::const_iterator itr = amap.find(account);
|
||||
if (itr == amap.end()) {
|
||||
qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping";
|
||||
return;
|
||||
}
|
||||
itr->second->addContactToGroupRequest(jid, groupName);
|
||||
}
|
||||
|
||||
void Core::Squawk::removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName)
|
||||
{
|
||||
AccountsMap::const_iterator itr = amap.find(account);
|
||||
if (itr == amap.end()) {
|
||||
qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping";
|
||||
return;
|
||||
}
|
||||
itr->second->removeContactFromGroupRequest(jid, groupName);
|
||||
}
|
||||
|
||||
void Core::Squawk::renameContactRequest(const QString& account, const QString& jid, const QString& newName)
|
||||
{
|
||||
AccountsMap::const_iterator itr = amap.find(account);
|
||||
if (itr == amap.end()) {
|
||||
qDebug() << "An attempt to rename contact" << jid << "of non existing account" << account << ", skipping";
|
||||
return;
|
||||
}
|
||||
itr->second->renameContactRequest(jid, newName);
|
||||
}
|
||||
|
||||
void Core::Squawk::requestVCard(const QString& account, const QString& jid)
|
||||
{
|
||||
AccountsMap::const_iterator itr = amap.find(account);
|
||||
if (itr == amap.end()) {
|
||||
qDebug() << "An attempt to request" << jid << "vcard of non existing account" << account << ", skipping";
|
||||
return;
|
||||
}
|
||||
itr->second->requestVCard(jid);
|
||||
}
|
||||
|
||||
void Core::Squawk::uploadVCard(const QString& account, const Shared::VCard& card)
|
||||
{
|
||||
AccountsMap::const_iterator itr = amap.find(account);
|
||||
if (itr == amap.end()) {
|
||||
qDebug() << "An attempt to upload vcard to non existing account" << account << ", skipping";
|
||||
return;
|
||||
}
|
||||
itr->second->uploadVCard(card);
|
||||
}
|
||||
|
@ -23,7 +23,8 @@
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QMap>
|
||||
#include <deque>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "account.h"
|
||||
@ -67,6 +68,7 @@ signals:
|
||||
void downloadFileProgress(const QString& messageId, qreal value);
|
||||
void uploadFileError(const QString& messageId, const QString& error);
|
||||
void uploadFileProgress(const QString& messageId, qreal value);
|
||||
void responseVCard(const QString& jid, const Shared::VCard& card);
|
||||
|
||||
public slots:
|
||||
void start();
|
||||
@ -82,7 +84,10 @@ public slots:
|
||||
void requestArchive(const QString& account, const QString& jid, int count, const QString& before);
|
||||
void subscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||
void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
||||
void removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
||||
void removeContactRequest(const QString& account, const QString& jid);
|
||||
void renameContactRequest(const QString& account, const QString& jid, const QString& newName);
|
||||
void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups);
|
||||
void setRoomJoined(const QString& account, const QString& jid, bool joined);
|
||||
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
|
||||
@ -90,6 +95,8 @@ public slots:
|
||||
void removeRoomRequest(const QString& account, const QString& jid);
|
||||
void fileLocalPathRequest(const QString& messageId, const QString& url);
|
||||
void downloadFileRequest(const QString& messageId, const QString& url);
|
||||
void requestVCard(const QString& account, const QString& jid);
|
||||
void uploadVCard(const QString& account, const Shared::VCard& card);
|
||||
|
||||
private:
|
||||
typedef std::deque<Account*> Accounts;
|
||||
@ -106,6 +113,7 @@ private:
|
||||
private slots:
|
||||
void onAccountConnectionStateChanged(int state);
|
||||
void onAccountAvailabilityChanged(int state);
|
||||
void onAccountChanged(const QMap<QString, QVariant>& data);
|
||||
void onAccountAddGroup(const QString& name);
|
||||
void onAccountError(const QString& text);
|
||||
void onAccountRemoveGroup(const QString& name);
|
||||
|
2
external/qxmpp
vendored
@ -1 +1 @@
|
||||
Subproject commit e6eb0b78d0cb17fccd5ddb60966ba2a0a2d2b593
|
||||
Subproject commit b18a57daa33f0fefa5f4c63aa7f448b48d302e0d
|
277
global.cpp
@ -296,6 +296,281 @@ bool Shared::Message::storable() const
|
||||
return id.size() > 0 && (body.size() > 0 || oob.size()) > 0;
|
||||
}
|
||||
|
||||
Shared::VCard::Contact::Contact(Shared::VCard::Contact::Role p_role, bool p_prefered):
|
||||
role(p_role),
|
||||
prefered(p_prefered)
|
||||
{}
|
||||
|
||||
Shared::VCard::Email::Email(const QString& addr, Shared::VCard::Contact::Role p_role, bool p_prefered):
|
||||
Contact(p_role, p_prefered),
|
||||
address(addr)
|
||||
{}
|
||||
|
||||
Shared::VCard::Phone::Phone(const QString& nmbr, Shared::VCard::Phone::Type p_type, Shared::VCard::Contact::Role p_role, bool p_prefered):
|
||||
Contact(p_role, p_prefered),
|
||||
number(nmbr),
|
||||
type(p_type)
|
||||
{}
|
||||
|
||||
Shared::VCard::Address::Address(const QString& zCode, const QString& cntry, const QString& rgn, const QString& lclty, const QString& strt, const QString& ext, Shared::VCard::Contact::Role p_role, bool p_prefered):
|
||||
Contact(p_role, p_prefered),
|
||||
zipCode(zCode),
|
||||
country(cntry),
|
||||
region(rgn),
|
||||
locality(lclty),
|
||||
street(strt),
|
||||
external(ext)
|
||||
{}
|
||||
|
||||
Shared::VCard::VCard():
|
||||
fullName(),
|
||||
firstName(),
|
||||
middleName(),
|
||||
lastName(),
|
||||
nickName(),
|
||||
description(),
|
||||
url(),
|
||||
organizationName(),
|
||||
organizationUnit(),
|
||||
organizationRole(),
|
||||
jobTitle(),
|
||||
birthday(),
|
||||
photoType(Avatar::empty),
|
||||
photoPath(),
|
||||
receivingTime(QDateTime::currentDateTime()),
|
||||
emails(),
|
||||
phones(),
|
||||
addresses()
|
||||
{}
|
||||
|
||||
Shared::VCard::VCard(const QDateTime& creationTime):
|
||||
fullName(),
|
||||
firstName(),
|
||||
middleName(),
|
||||
lastName(),
|
||||
nickName(),
|
||||
description(),
|
||||
url(),
|
||||
organizationName(),
|
||||
organizationUnit(),
|
||||
organizationRole(),
|
||||
jobTitle(),
|
||||
birthday(),
|
||||
photoType(Avatar::empty),
|
||||
photoPath(),
|
||||
receivingTime(creationTime),
|
||||
emails(),
|
||||
phones(),
|
||||
addresses()
|
||||
{
|
||||
}
|
||||
|
||||
QString Shared::VCard::getAvatarPath() const
|
||||
{
|
||||
return photoPath;
|
||||
}
|
||||
|
||||
Shared::Avatar Shared::VCard::getAvatarType() const
|
||||
{
|
||||
return photoType;
|
||||
}
|
||||
|
||||
QDate Shared::VCard::getBirthday() const
|
||||
{
|
||||
return birthday;
|
||||
}
|
||||
|
||||
QString Shared::VCard::getDescription() const
|
||||
{
|
||||
return description;
|
||||
}
|
||||
|
||||
QString Shared::VCard::getFirstName() const
|
||||
{
|
||||
return firstName;
|
||||
}
|
||||
|
||||
QString Shared::VCard::getLastName() const
|
||||
{
|
||||
return lastName;
|
||||
}
|
||||
|
||||
QString Shared::VCard::getMiddleName() const
|
||||
{
|
||||
return middleName;
|
||||
}
|
||||
|
||||
QString Shared::VCard::getNickName() const
|
||||
{
|
||||
return nickName;
|
||||
}
|
||||
|
||||
void Shared::VCard::setAvatarPath(const QString& path)
|
||||
{
|
||||
if (path != photoPath) {
|
||||
photoPath = path;
|
||||
}
|
||||
}
|
||||
|
||||
void Shared::VCard::setAvatarType(Shared::Avatar type)
|
||||
{
|
||||
if (photoType != type) {
|
||||
photoType = type;
|
||||
}
|
||||
}
|
||||
|
||||
void Shared::VCard::setBirthday(const QDate& date)
|
||||
{
|
||||
if (date.isValid() && birthday != date) {
|
||||
birthday = date;
|
||||
}
|
||||
}
|
||||
|
||||
void Shared::VCard::setDescription(const QString& descr)
|
||||
{
|
||||
if (description != descr) {
|
||||
description = descr;
|
||||
}
|
||||
}
|
||||
|
||||
void Shared::VCard::setFirstName(const QString& first)
|
||||
{
|
||||
if (firstName != first) {
|
||||
firstName = first;
|
||||
}
|
||||
}
|
||||
|
||||
void Shared::VCard::setLastName(const QString& last)
|
||||
{
|
||||
if (lastName != last) {
|
||||
lastName = last;
|
||||
}
|
||||
}
|
||||
|
||||
void Shared::VCard::setMiddleName(const QString& middle)
|
||||
{
|
||||
if (middleName != middle) {
|
||||
middleName = middle;
|
||||
}
|
||||
}
|
||||
|
||||
void Shared::VCard::setNickName(const QString& nick)
|
||||
{
|
||||
if (nickName != nick) {
|
||||
nickName = nick;
|
||||
}
|
||||
}
|
||||
|
||||
QString Shared::VCard::getFullName() const
|
||||
{
|
||||
return fullName;
|
||||
}
|
||||
|
||||
QString Shared::VCard::getUrl() const
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
void Shared::VCard::setFullName(const QString& name)
|
||||
{
|
||||
if (fullName != name) {
|
||||
fullName = name;
|
||||
}
|
||||
}
|
||||
|
||||
void Shared::VCard::setUrl(const QString& u)
|
||||
{
|
||||
if (url != u) {
|
||||
url = u;
|
||||
}
|
||||
}
|
||||
|
||||
QString Shared::VCard::getOrgName() const
|
||||
{
|
||||
return organizationName;
|
||||
}
|
||||
|
||||
QString Shared::VCard::getOrgRole() const
|
||||
{
|
||||
return organizationRole;
|
||||
}
|
||||
|
||||
QString Shared::VCard::getOrgTitle() const
|
||||
{
|
||||
return jobTitle;
|
||||
}
|
||||
|
||||
QString Shared::VCard::getOrgUnit() const
|
||||
{
|
||||
return organizationUnit;
|
||||
}
|
||||
|
||||
void Shared::VCard::setOrgName(const QString& name)
|
||||
{
|
||||
if (organizationName != name) {
|
||||
organizationName = name;
|
||||
}
|
||||
}
|
||||
|
||||
void Shared::VCard::setOrgRole(const QString& role)
|
||||
{
|
||||
if (organizationRole != role) {
|
||||
organizationRole = role;
|
||||
}
|
||||
}
|
||||
|
||||
void Shared::VCard::setOrgTitle(const QString& title)
|
||||
{
|
||||
if (jobTitle != title) {
|
||||
jobTitle = title;
|
||||
}
|
||||
}
|
||||
|
||||
void Shared::VCard::setOrgUnit(const QString& unit)
|
||||
{
|
||||
if (organizationUnit != unit) {
|
||||
organizationUnit = unit;
|
||||
}
|
||||
}
|
||||
|
||||
QDateTime Shared::VCard::getReceivingTime() const
|
||||
{
|
||||
return receivingTime;
|
||||
}
|
||||
|
||||
std::deque<Shared::VCard::Email> & Shared::VCard::getEmails()
|
||||
{
|
||||
return emails;
|
||||
}
|
||||
|
||||
std::deque<Shared::VCard::Address> & Shared::VCard::getAddresses()
|
||||
{
|
||||
return addresses;
|
||||
}
|
||||
|
||||
std::deque<Shared::VCard::Phone> & Shared::VCard::getPhones()
|
||||
{
|
||||
return phones;
|
||||
}
|
||||
|
||||
const std::deque<Shared::VCard::Email> & Shared::VCard::getEmails() const
|
||||
{
|
||||
return emails;
|
||||
}
|
||||
|
||||
const std::deque<Shared::VCard::Address> & Shared::VCard::getAddresses() const
|
||||
{
|
||||
return addresses;
|
||||
}
|
||||
|
||||
const std::deque<Shared::VCard::Phone> & Shared::VCard::getPhones() const
|
||||
{
|
||||
return phones;
|
||||
}
|
||||
|
||||
const std::deque<QString>Shared::VCard::Contact::roleNames = {"Not specified", "Personal", "Business"};
|
||||
const std::deque<QString>Shared::VCard::Phone::typeNames = {"Fax", "Pager", "Voice", "Cell", "Video", "Modem", "Other"};
|
||||
|
||||
QIcon Shared::availabilityIcon(Shared::Availability av, bool big)
|
||||
{
|
||||
const std::deque<QString>& fallback = QApplication::palette().window().color().lightnessF() > 0.5 ?
|
||||
@ -350,6 +625,6 @@ QIcon Shared::icon(const QString& name, bool big)
|
||||
return QIcon::fromTheme(itr->second.first, QIcon(prefix + itr->second.second));
|
||||
} else {
|
||||
qDebug() << "Icon" << name << "not found";
|
||||
throw 1;
|
||||
return QIcon::fromTheme(name);
|
||||
}
|
||||
}
|
||||
|
176
global.h
@ -20,9 +20,11 @@
|
||||
#define GLOBAL_H
|
||||
|
||||
#include <QString>
|
||||
#include <QCoreApplication>
|
||||
#include <deque>
|
||||
#include <QDateTime>
|
||||
#include <QDataStream>
|
||||
#include <QColor>
|
||||
|
||||
namespace Shared {
|
||||
|
||||
@ -68,6 +70,12 @@ enum class Role {
|
||||
moderator
|
||||
};
|
||||
|
||||
enum class Avatar {
|
||||
empty,
|
||||
autocreated,
|
||||
valid
|
||||
};
|
||||
|
||||
static const Availability availabilityHighest = offline;
|
||||
static const Availability availabilityLowest = online;
|
||||
|
||||
@ -99,9 +107,47 @@ static const std::deque<QString> subscriptionStateNames = {"None", "From", "To",
|
||||
|
||||
static const std::deque<QString> affiliationNames = {"Unspecified", "Outcast", "Nobody", "Member", "Admin", "Owner"};
|
||||
static const std::deque<QString> roleNames = {"Unspecified", "Nobody", "Visitor", "Participant", "Moderator"};
|
||||
|
||||
QString generateUUID();
|
||||
|
||||
static const std::vector<QColor> colorPalette = {
|
||||
QColor(244, 27, 63),
|
||||
QColor(21, 104, 156),
|
||||
QColor(38, 156, 98),
|
||||
QColor(247, 103, 101),
|
||||
QColor(121, 37, 117),
|
||||
QColor(242, 202, 33),
|
||||
QColor(168, 22, 63),
|
||||
QColor(35, 100, 52),
|
||||
QColor(52, 161, 152),
|
||||
QColor(239, 53, 111),
|
||||
QColor(237, 234, 36),
|
||||
QColor(153, 148, 194),
|
||||
QColor(211, 102, 151),
|
||||
QColor(194, 63, 118),
|
||||
QColor(249, 149, 51),
|
||||
QColor(244, 206, 109),
|
||||
QColor(121, 105, 153),
|
||||
QColor(244, 199, 30),
|
||||
QColor(28, 112, 28),
|
||||
QColor(172, 18, 20),
|
||||
QColor(25, 66, 110),
|
||||
QColor(25, 149, 104),
|
||||
QColor(214, 148, 0),
|
||||
QColor(203, 47, 57),
|
||||
QColor(4, 54, 84),
|
||||
QColor(116, 161, 97),
|
||||
QColor(50, 68, 52),
|
||||
QColor(237, 179, 20),
|
||||
QColor(69, 114, 147),
|
||||
QColor(242, 212, 31),
|
||||
QColor(248, 19, 20),
|
||||
QColor(84, 102, 84),
|
||||
QColor(25, 53, 122),
|
||||
QColor(91, 91, 109),
|
||||
QColor(17, 17, 80),
|
||||
QColor(54, 54, 94)
|
||||
};
|
||||
|
||||
class Message {
|
||||
public:
|
||||
enum Type {
|
||||
@ -169,6 +215,125 @@ private:
|
||||
QString oob;
|
||||
};
|
||||
|
||||
class VCard {
|
||||
class Contact {
|
||||
public:
|
||||
enum Role {
|
||||
none,
|
||||
home,
|
||||
work
|
||||
};
|
||||
static const std::deque<QString> roleNames;
|
||||
|
||||
Contact(Role p_role = none, bool p_prefered = false);
|
||||
|
||||
Role role;
|
||||
bool prefered;
|
||||
};
|
||||
public:
|
||||
class Email : public Contact {
|
||||
public:
|
||||
Email(const QString& address, Role p_role = none, bool p_prefered = false);
|
||||
|
||||
QString address;
|
||||
};
|
||||
class Phone : public Contact {
|
||||
public:
|
||||
enum Type {
|
||||
fax,
|
||||
pager,
|
||||
voice,
|
||||
cell,
|
||||
video,
|
||||
modem,
|
||||
other
|
||||
};
|
||||
static const std::deque<QString> typeNames;
|
||||
Phone(const QString& number, Type p_type = voice, Role p_role = none, bool p_prefered = false);
|
||||
|
||||
QString number;
|
||||
Type type;
|
||||
};
|
||||
class Address : public Contact {
|
||||
public:
|
||||
Address(
|
||||
const QString& zCode = "",
|
||||
const QString& cntry = "",
|
||||
const QString& rgn = "",
|
||||
const QString& lclty = "",
|
||||
const QString& strt = "",
|
||||
const QString& ext = "",
|
||||
Role p_role = none,
|
||||
bool p_prefered = false
|
||||
);
|
||||
|
||||
QString zipCode;
|
||||
QString country;
|
||||
QString region;
|
||||
QString locality;
|
||||
QString street;
|
||||
QString external;
|
||||
};
|
||||
VCard();
|
||||
VCard(const QDateTime& creationTime);
|
||||
|
||||
QString getFullName() const;
|
||||
void setFullName(const QString& name);
|
||||
QString getFirstName() const;
|
||||
void setFirstName(const QString& first);
|
||||
QString getMiddleName() const;
|
||||
void setMiddleName(const QString& middle);
|
||||
QString getLastName() const;
|
||||
void setLastName(const QString& last);
|
||||
QString getNickName() const;
|
||||
void setNickName(const QString& nick);
|
||||
QString getDescription() const;
|
||||
void setDescription(const QString& descr);
|
||||
QString getUrl() const;
|
||||
void setUrl(const QString& u);
|
||||
QDate getBirthday() const;
|
||||
void setBirthday(const QDate& date);
|
||||
Avatar getAvatarType() const;
|
||||
void setAvatarType(Avatar type);
|
||||
QString getAvatarPath() const;
|
||||
void setAvatarPath(const QString& path);
|
||||
QString getOrgName() const;
|
||||
void setOrgName(const QString& name);
|
||||
QString getOrgUnit() const;
|
||||
void setOrgUnit(const QString& unit);
|
||||
QString getOrgRole() const;
|
||||
void setOrgRole(const QString& role);
|
||||
QString getOrgTitle() const;
|
||||
void setOrgTitle(const QString& title);
|
||||
QDateTime getReceivingTime() const;
|
||||
std::deque<Email>& getEmails();
|
||||
const std::deque<Email>& getEmails() const;
|
||||
std::deque<Phone>& getPhones();
|
||||
const std::deque<Phone>& getPhones() const;
|
||||
std::deque<Address>& getAddresses();
|
||||
const std::deque<Address>& getAddresses() const;
|
||||
|
||||
private:
|
||||
QString fullName;
|
||||
QString firstName;
|
||||
QString middleName;
|
||||
QString lastName;
|
||||
QString nickName;
|
||||
QString description;
|
||||
QString url;
|
||||
QString organizationName;
|
||||
QString organizationUnit;
|
||||
QString organizationRole;
|
||||
QString jobTitle;
|
||||
QDate birthday;
|
||||
Avatar photoType;
|
||||
QString photoPath;
|
||||
QDateTime receivingTime;
|
||||
std::deque<Email> emails;
|
||||
std::deque<Phone> phones;
|
||||
std::deque<Address> addresses;
|
||||
};
|
||||
|
||||
static const std::deque<QString> fallbackAvailabilityThemeIconsLightBig = {
|
||||
":images/fallback/light/big/online.svg",
|
||||
":images/fallback/light/big/away.svg",
|
||||
@ -292,7 +457,9 @@ static const std::map<QString, std::pair<QString, QString>> icons = {
|
||||
{"state-ok", {"state-ok", "state-ok"}},
|
||||
{"state-error", {"state-error", "state-error"}},
|
||||
|
||||
{"edit-copy", {"edit-copy", "copy"}},
|
||||
{"edit-delete", {"edit-delete", "edit-delete"}},
|
||||
{"edit-rename", {"edit-rename", "edit-rename"}},
|
||||
{"mail-message", {"mail-message", "mail-message"}},
|
||||
{"mail-attachment", {"mail-attachment", "mail-attachment"}},
|
||||
{"network-connect", {"network-connect", "network-connect"}},
|
||||
@ -302,6 +469,13 @@ static const std::map<QString, std::pair<QString, QString>> icons = {
|
||||
{"view-refresh", {"view-refresh", "view-refresh"}},
|
||||
{"send", {"document-send", "send"}},
|
||||
{"clean", {"edit-clear-all", "clean"}},
|
||||
{"user", {"user", "user"}},
|
||||
{"user-properties", {"user-properties", "user-properties"}},
|
||||
{"group", {"group", "group"}},
|
||||
{"group-new", {"resurce-group-new", "group-new"}},
|
||||
{"favorite", {"favorite", "favorite"}},
|
||||
{"unfavorite", {"draw-star", "unfavorite"}},
|
||||
{"list-add", {"list-add", "add"}},
|
||||
};
|
||||
|
||||
};
|
||||
|
150
main.cpp
@ -23,20 +23,42 @@
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QObject>
|
||||
#include <QSettings>
|
||||
#include <QTranslator>
|
||||
#include <QLibraryInfo>
|
||||
#include <QStandardPaths>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
qRegisterMetaType<Shared::Message>("Shared::Message");
|
||||
qRegisterMetaType<Shared::VCard>("Shared::VCard");
|
||||
qRegisterMetaType<std::list<Shared::Message>>("std::list<Shared::Message>");
|
||||
qRegisterMetaType<QSet<QString>>("QSet<QString>");
|
||||
|
||||
QApplication app(argc, argv);
|
||||
SignalCatcher sc(&app);
|
||||
|
||||
QCoreApplication::setOrganizationName("Macaw");
|
||||
QCoreApplication::setOrganizationDomain("macaw.me");
|
||||
QCoreApplication::setApplicationName("Squawk");
|
||||
QCoreApplication::setApplicationVersion("0.0.3");
|
||||
QApplication::setApplicationName("squawk");
|
||||
QApplication::setApplicationDisplayName("Squawk");
|
||||
QApplication::setApplicationVersion("0.0.5");
|
||||
|
||||
QTranslator qtTranslator;
|
||||
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
||||
app.installTranslator(&qtTranslator);
|
||||
|
||||
QTranslator myappTranslator;
|
||||
QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
||||
bool found = false;
|
||||
for (QString share : shares) {
|
||||
found = myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n");
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath());
|
||||
}
|
||||
|
||||
app.installTranslator(&myappTranslator);
|
||||
|
||||
QIcon icon;
|
||||
icon.addFile(":images/logo.svg", QSize(16, 16));
|
||||
@ -57,75 +79,59 @@ int main(int argc, char *argv[])
|
||||
QThread* coreThread = new QThread();
|
||||
squawk->moveToThread(coreThread);
|
||||
|
||||
QObject::connect(coreThread, SIGNAL(started()), squawk, SLOT(start()));
|
||||
QObject::connect(&app, SIGNAL(aboutToQuit()), squawk, SLOT(stop()));
|
||||
QObject::connect(squawk, SIGNAL(quit()), coreThread, SLOT(quit()));
|
||||
QObject::connect(coreThread, SIGNAL(finished()), squawk, SLOT(deleteLater()));
|
||||
QObject::connect(&w, &Squawk::newAccountRequest, squawk, &Core::Squawk::newAccountRequest);
|
||||
QObject::connect(&w, &Squawk::modifyAccountRequest, squawk, &Core::Squawk::modifyAccountRequest);
|
||||
QObject::connect(&w, &Squawk::removeAccountRequest, squawk, &Core::Squawk::removeAccountRequest);
|
||||
QObject::connect(&w, &Squawk::connectAccount, squawk, &Core::Squawk::connectAccount);
|
||||
QObject::connect(&w, &Squawk::disconnectAccount, squawk, &Core::Squawk::disconnectAccount);
|
||||
QObject::connect(&w, &Squawk::changeState, squawk, &Core::Squawk::changeState);
|
||||
QObject::connect(&w, qOverload<const QString&, const Shared::Message&>(&Squawk::sendMessage),
|
||||
squawk, qOverload<const QString&, const Shared::Message&>(&Core::Squawk::sendMessage));
|
||||
QObject::connect(&w, qOverload<const QString&, const Shared::Message&, const QString&>(&Squawk::sendMessage),
|
||||
squawk, qOverload<const QString&, const Shared::Message&, const QString&>(&Core::Squawk::sendMessage));
|
||||
QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive);
|
||||
QObject::connect(&w, &Squawk::subscribeContact, squawk, &Core::Squawk::subscribeContact);
|
||||
QObject::connect(&w, &Squawk::unsubscribeContact, squawk, &Core::Squawk::unsubscribeContact);
|
||||
QObject::connect(&w, &Squawk::addContactRequest, squawk, &Core::Squawk::addContactRequest);
|
||||
QObject::connect(&w, &Squawk::removeContactRequest, squawk, &Core::Squawk::removeContactRequest);
|
||||
QObject::connect(&w, &Squawk::setRoomJoined, squawk, &Core::Squawk::setRoomJoined);
|
||||
QObject::connect(&w, &Squawk::setRoomAutoJoin, squawk, &Core::Squawk::setRoomAutoJoin);
|
||||
QObject::connect(&w, &Squawk::removeRoomRequest, squawk, &Core::Squawk::removeRoomRequest);
|
||||
QObject::connect(&w, &Squawk::addRoomRequest, squawk, &Core::Squawk::addRoomRequest);
|
||||
QObject::connect(&w, &Squawk::fileLocalPathRequest, squawk, &Core::Squawk::fileLocalPathRequest);
|
||||
QObject::connect(&w, &Squawk::downloadFileRequest, squawk, &Core::Squawk::downloadFileRequest);
|
||||
QObject::connect(&w, &Squawk::addContactToGroupRequest, squawk, &Core::Squawk::addContactToGroupRequest);
|
||||
QObject::connect(&w, &Squawk::removeContactFromGroupRequest, squawk, &Core::Squawk::removeContactFromGroupRequest);
|
||||
QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest);
|
||||
QObject::connect(&w, &Squawk::requestVCard, squawk, &Core::Squawk::requestVCard);
|
||||
QObject::connect(&w, &Squawk::uploadVCard, squawk, &Core::Squawk::uploadVCard);
|
||||
|
||||
QObject::connect(&w, SIGNAL(newAccountRequest(const QMap<QString, QVariant>&)), squawk, SLOT(newAccountRequest(const QMap<QString, QVariant>&)));
|
||||
QObject::connect(&w, SIGNAL(modifyAccountRequest(const QString&, const QMap<QString, QVariant>&)),
|
||||
squawk, SLOT(modifyAccountRequest(const QString&, const QMap<QString, QVariant>&)));
|
||||
QObject::connect(&w, SIGNAL(removeAccountRequest(const QString&)), squawk, SLOT(removeAccountRequest(const QString&)));
|
||||
QObject::connect(&w, SIGNAL(connectAccount(const QString&)), squawk, SLOT(connectAccount(const QString&)));
|
||||
QObject::connect(&w, SIGNAL(disconnectAccount(const QString&)), squawk, SLOT(disconnectAccount(const QString&)));
|
||||
QObject::connect(&w, SIGNAL(changeState(int)), squawk, SLOT(changeState(int)));
|
||||
QObject::connect(&w, SIGNAL(sendMessage(const QString&, const Shared::Message&)), squawk, SLOT(sendMessage(const QString&, const Shared::Message&)));
|
||||
QObject::connect(&w, SIGNAL(sendMessage(const QString&, const Shared::Message&, const QString&)),
|
||||
squawk, SLOT(sendMessage(const QString&, const Shared::Message&, const QString&)));
|
||||
QObject::connect(&w, SIGNAL(requestArchive(const QString&, const QString&, int, const QString&)),
|
||||
squawk, SLOT(requestArchive(const QString&, const QString&, int, const QString&)));
|
||||
QObject::connect(&w, SIGNAL(subscribeContact(const QString&, const QString&, const QString&)),
|
||||
squawk, SLOT(subscribeContact(const QString&, const QString&, const QString&)));
|
||||
QObject::connect(&w, SIGNAL(unsubscribeContact(const QString&, const QString&, const QString&)),
|
||||
squawk, SLOT(unsubscribeContact(const QString&, const QString&, const QString&)));
|
||||
QObject::connect(&w, SIGNAL(addContactRequest(const QString&, const QString&, const QString&, const QSet<QString>&)),
|
||||
squawk, SLOT(addContactRequest(const QString&, const QString&, const QString&, const QSet<QString>&)));
|
||||
QObject::connect(&w, SIGNAL(removeContactRequest(const QString&, const QString&)),
|
||||
squawk, SLOT(removeContactRequest(const QString&, const QString&)));
|
||||
QObject::connect(&w, SIGNAL(setRoomJoined(const QString&, const QString&, bool)), squawk, SLOT(setRoomJoined(const QString&, const QString&, bool)));
|
||||
QObject::connect(&w, SIGNAL(setRoomAutoJoin(const QString&, const QString&, bool)), squawk, SLOT(setRoomAutoJoin(const QString&, const QString&, bool)));
|
||||
|
||||
QObject::connect(&w, SIGNAL(removeRoomRequest(const QString&, const QString&)),
|
||||
squawk, SLOT(removeRoomRequest(const QString&, const QString&)));
|
||||
QObject::connect(&w, SIGNAL(addRoomRequest(const QString&, const QString&, const QString&, const QString&, bool)),
|
||||
squawk, SLOT(addRoomRequest(const QString&, const QString&, const QString&, const QString&, bool)));
|
||||
QObject::connect(&w, SIGNAL(fileLocalPathRequest(const QString&, const QString&)), squawk, SLOT(fileLocalPathRequest(const QString&, const QString&)));
|
||||
QObject::connect(&w, SIGNAL(downloadFileRequest(const QString&, const QString&)), squawk, SLOT(downloadFileRequest(const QString&, const QString&)));
|
||||
|
||||
QObject::connect(squawk, SIGNAL(newAccount(const QMap<QString, QVariant>&)), &w, SLOT(newAccount(const QMap<QString, QVariant>&)));
|
||||
QObject::connect(squawk, SIGNAL(addContact(const QString&, const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
&w, SLOT(addContact(const QString&, const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
QObject::connect(squawk, SIGNAL(changeAccount(const QString&, const QMap<QString, QVariant>&)),
|
||||
&w, SLOT(changeAccount(const QString&, const QMap<QString, QVariant>&)));
|
||||
QObject::connect(squawk, SIGNAL(removeAccount(const QString&)), &w, SLOT(removeAccount(const QString&)));
|
||||
QObject::connect(squawk, SIGNAL(addGroup(const QString&, const QString&)), &w, SLOT(addGroup(const QString&, const QString&)));
|
||||
QObject::connect(squawk, SIGNAL(removeGroup(const QString&, const QString&)), &w, SLOT(removeGroup(const QString&, const QString&)));
|
||||
QObject::connect(squawk, SIGNAL(removeContact(const QString&, const QString&)), &w, SLOT(removeContact(const QString&, const QString&)));
|
||||
QObject::connect(squawk, SIGNAL(removeContact(const QString&, const QString&, const QString&)), &w, SLOT(removeContact(const QString&, const QString&, const QString&)));
|
||||
QObject::connect(squawk, SIGNAL(changeContact(const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
&w, SLOT(changeContact(const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
QObject::connect(squawk, SIGNAL(addPresence(const QString&, const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
&w, SLOT(addPresence(const QString&, const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
QObject::connect(squawk, SIGNAL(removePresence(const QString&, const QString&, const QString&)), &w, SLOT(removePresence(const QString&, const QString&, const QString&)));
|
||||
QObject::connect(squawk, SIGNAL(stateChanged(int)), &w, SLOT(stateChanged(int)));
|
||||
QObject::connect(squawk, SIGNAL(accountMessage(const QString&, const Shared::Message&)), &w, SLOT(accountMessage(const QString&, const Shared::Message&)));
|
||||
QObject::connect(squawk, SIGNAL(responseArchive(const QString&, const QString&, const std::list<Shared::Message>&)),
|
||||
&w, SLOT(responseArchive(const QString&, const QString&, const std::list<Shared::Message>&)));
|
||||
|
||||
QObject::connect(squawk, SIGNAL(addRoom(const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
&w, SLOT(addRoom(const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
QObject::connect(squawk, SIGNAL(changeRoom(const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
&w, SLOT(changeRoom(const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
QObject::connect(squawk, SIGNAL(removeRoom(const QString&, const QString&)), &w, SLOT(removeRoom(const QString&, const QString&)));
|
||||
QObject::connect(squawk, SIGNAL(addRoomParticipant(const QString&, const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
&w, SLOT(addRoomParticipant(const QString&, const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
QObject::connect(squawk, SIGNAL(changeRoomParticipant(const QString&, const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
&w, SLOT(changeRoomParticipant(const QString&, const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
QObject::connect(squawk, SIGNAL(removeRoomParticipant(const QString&, const QString&, const QString&)),
|
||||
&w, SLOT(removeRoomParticipant(const QString&, const QString&, const QString&)));
|
||||
QObject::connect(squawk, SIGNAL(fileLocalPathResponse(const QString&, const QString&)), &w, SLOT(fileLocalPathResponse(const QString&, const QString&)));
|
||||
QObject::connect(squawk, SIGNAL(downloadFileProgress(const QString&, qreal)), &w, SLOT(downloadFileProgress(const QString&, qreal)));
|
||||
QObject::connect(squawk, SIGNAL(downloadFileError(const QString&, const QString&)), &w, SLOT(downloadFileError(const QString&, const QString&)));
|
||||
QObject::connect(squawk, &Core::Squawk::newAccount, &w, &Squawk::newAccount);
|
||||
QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact);
|
||||
QObject::connect(squawk, &Core::Squawk::changeAccount, &w, &Squawk::changeAccount);
|
||||
QObject::connect(squawk, &Core::Squawk::removeAccount, &w, &Squawk::removeAccount);
|
||||
QObject::connect(squawk, &Core::Squawk::addGroup, &w, &Squawk::addGroup);
|
||||
QObject::connect(squawk, &Core::Squawk::removeGroup, &w, &Squawk::removeGroup);
|
||||
QObject::connect(squawk, qOverload<const QString&, const QString&>(&Core::Squawk::removeContact),
|
||||
&w, qOverload<const QString&, const QString&>(&Squawk::removeContact));
|
||||
QObject::connect(squawk, qOverload<const QString&, const QString&, const QString&>(&Core::Squawk::removeContact),
|
||||
&w, qOverload<const QString&, const QString&, const QString&>(&Squawk::removeContact));
|
||||
QObject::connect(squawk, &Core::Squawk::changeContact, &w, &Squawk::changeContact);
|
||||
QObject::connect(squawk, &Core::Squawk::addPresence, &w, &Squawk::addPresence);
|
||||
QObject::connect(squawk, &Core::Squawk::removePresence, &w, &Squawk::removePresence);
|
||||
QObject::connect(squawk, &Core::Squawk::stateChanged, &w, &Squawk::stateChanged);
|
||||
QObject::connect(squawk, &Core::Squawk::accountMessage, &w, &Squawk::accountMessage);
|
||||
QObject::connect(squawk, &Core::Squawk::responseArchive, &w, &Squawk::responseArchive);
|
||||
QObject::connect(squawk, &Core::Squawk::addRoom, &w, &Squawk::addRoom);
|
||||
QObject::connect(squawk, &Core::Squawk::changeRoom, &w, &Squawk::changeRoom);
|
||||
QObject::connect(squawk, &Core::Squawk::removeRoom, &w, &Squawk::removeRoom);
|
||||
QObject::connect(squawk, &Core::Squawk::addRoomParticipant, &w, &Squawk::addRoomParticipant);
|
||||
QObject::connect(squawk, &Core::Squawk::changeRoomParticipant, &w, &Squawk::changeRoomParticipant);
|
||||
QObject::connect(squawk, &Core::Squawk::removeRoomParticipant, &w, &Squawk::removeRoomParticipant);
|
||||
QObject::connect(squawk, &Core::Squawk::fileLocalPathResponse, &w, &Squawk::fileLocalPathResponse);
|
||||
QObject::connect(squawk, &Core::Squawk::downloadFileProgress, &w, &Squawk::downloadFileProgress);
|
||||
QObject::connect(squawk, &Core::Squawk::downloadFileError, &w, &Squawk::downloadFileError);
|
||||
QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard);
|
||||
|
||||
coreThread->start();
|
||||
|
||||
|
21
packaging/Archlinux/PKGBUILD
Normal file
@ -0,0 +1,21 @@
|
||||
# Maintainer: Yury Gubich <blue@macaw.me>
|
||||
pkgname=squawk
|
||||
pkgver=0.0.5
|
||||
pkgrel=1
|
||||
pkgdesc="An XMPP desktop messenger, written on qt"
|
||||
arch=('i686' 'x86_64')
|
||||
url="https://git.macaw.me/blue/squawk"
|
||||
license=('GPL3')
|
||||
depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.0.0')
|
||||
makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools')
|
||||
source=("$pkgname-$pkgver.tar.gz")
|
||||
sha256sums=('12bfc517574387257a82143d8970ec0d8d434ccd32f7ac400355ed5fa18192ab')
|
||||
build() {
|
||||
cd "$srcdir/squawk"
|
||||
cmake . -D CMAKE_INSTALL_PREFIX=/usr
|
||||
cmake --build . -j $nproc
|
||||
}
|
||||
package() {
|
||||
cd "$srcdir/squawk"
|
||||
DESTDIR="$pkgdir/" cmake --build . --target install
|
||||
}
|
16
packaging/squawk.desktop
Normal file
@ -0,0 +1,16 @@
|
||||
[Desktop Entry]
|
||||
|
||||
Type=Application
|
||||
Version=1.0
|
||||
Name=Squawk
|
||||
GenericName=Instant Messenger
|
||||
GenericName[ru]=Мгновенные сообщения
|
||||
Comment=XMPP (Jabber) instant messenger client
|
||||
Comment[ru]=XMPP (Jabber) клиент обмена мгновенными сообщениями
|
||||
Exec=squawk %u
|
||||
Icon=squawk
|
||||
StartupNotify=true
|
||||
StartupWMClass=Squawk
|
||||
Terminal=false
|
||||
Categories=Network;InstantMessaging;Qt;
|
||||
MimeType=x-scheme-handler/xmpp;
|
14
resources/images/fallback/dark/big/add.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 10 4 L 10 11 L 3 11 L 3 12 L 10 12 L 10 19 L 11 19 L 11 12 L 18 12 L 18 11 L 11 11 L 11 4 L 10 4 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 441 B |
14
resources/images/fallback/dark/big/copy.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="m4 3v1 13h1 2 1v1 1h6l4-4v-1-7-1h-2v-3h-1-10-1m1 1h10v2h-7v1 9h-1-2v-12m4 3h8v7h-3-1v1 3h-4v-11"
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 432 B |
14
resources/images/fallback/dark/big/edit-rename.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="m14.996094 3l-11.992188 11.992188h-.003906v4.00781h1 2 1.00781v-.003906l11.992188-11.992188-.001953-.001953.001953-.001953-4-4-.001953.001953-.001953-.001953m-1.998047 3.412109l2.589844 2.589844-7.587891 7.587891v-1.589844h-1-1v-1-.589844l6.998047-6.998047m-7.998047 7.998047v1.589844h1 1v1 .589844l-.410156.410156h-1.589844l-1-1v-1.589844l1-1"
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 681 B |
14
resources/images/fallback/dark/big/favorite.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 11,3 8.523,8.266 3,9.11 7,13.21 6.055,19 11,16.266 15.945,19 15,13.21 19,9.11 13.477,8.266 11,3 Z"
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 439 B |
14
resources/images/fallback/dark/big/group-new.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 14 3 C 12.338 3 11 4.338 11 6 C 11 7.662 12.338 9 14 9 C 15.662 9 17 7.662 17 6 C 17 4.338 15.662 3 14 3 z M 8 6 C 6.3380003 6 5 7.3380003 5 9 C 5 10.662 6.3380003 12 8 12 C 9.6619997 12 11 10.662 11 9 C 11 7.3380003 9.6619997 6 8 6 z M 14 10 C 13.353654 10 12.744006 10.134157 12.181641 10.361328 L 12.636719 11.275391 C 13.064535 11.114885 13.514485 11 14 11 C 15.482985 11 16.758385 11.807292 17.449219 13 L 18.580078 13 C 17.810617 11.232833 16.056835 10 14 10 z M 8 13 C 5.2299834 13 3 15.229983 3 18 L 3 19 L 13 19 L 13 18 C 13 15.229983 10.770017 13 8 13 z M 8 14 C 10.216017 14 12 15.783983 12 18 L 4 18 C 4 15.783983 5.7839834 14 8 14 z M 16 14 L 16 16 L 14 16 L 14 17 L 16 17 L 16 19 L 17 19 L 17 17 L 19 17 L 19 16 L 17 16 L 17 14 L 16 14 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
14
resources/images/fallback/dark/big/group.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 14 3 C 12.338 3 11 4.338 11 6 C 11 7.662 12.338 9 14 9 C 15.662 9 17 7.662 17 6 C 17 4.338 15.662 3 14 3 z M 8 6 C 6.338 6 5 7.338 5 9 C 5 10.662 6.338 12 8 12 C 9.662 12 11 10.662 11 9 C 11 7.338 9.662 6 8 6 z M 14 10 C 13.353654 10 12.744006 10.134157 12.181641 10.361328 L 12.636719 11.275391 C 13.064535 11.114885 13.514485 11 14 11 C 16.21602 11 18 12.78398 18 15 L 14.5 15 L 15 16 L 19 16 L 19 15 C 19 12.22998 16.77002 10 14 10 z M 8 13 C 5.229983 13 3 15.229983 3 18 L 3 19 L 13 19 L 13 18 C 13 15.229983 10.770017 13 8 13 z M 8 14 C 10.216017 14 12 15.783983 12 18 L 4 18 C 4 15.783983 5.783983 14 8 14 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 953 B |
14
resources/images/fallback/dark/big/unfavorite.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 12,4 9.5273438,9.2675781 4,10.111328 8,14.210938 7.0566406,20 12,17.267578 16.943359,20 16,14.210938 20,10.111328 14.472656,9.2675781 12,4 Z M 12,6 13.853516,9.9492188 18,10.583984 15,13.658203 15.708984,18 12,15.949219 8.2910156,18 9,13.658203 6,10.583984 10.146484,9.9492188 12,6 Z" transform="translate(-.99999-.99999)"
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 660 B |
14
resources/images/fallback/dark/big/user-properties.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 10 3 L 10 4 L 19 4 L 19 3 L 10 3 z M 8 6 C 6.338 6 5 7.338 5 9 C 5 10.662 6.338 12 8 12 C 9.662 12 11 10.662 11 9 C 11 7.338 9.662 6 8 6 z M 12 8 L 12 9 L 19 9 L 19 8 L 12 8 z M 8 13 C 5.229983 13 3 15.229983 3 18 L 3 19 L 13 19 L 13 18 C 13 15.229983 10.770017 13 8 13 z M 13 13 L 13 14 L 19 14 L 19 13 L 13 13 z M 8 14 C 10.216017 14 12 15.783983 12 18 L 4 18 C 4 15.783983 5.783983 14 8 14 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 734 B |
13
resources/images/fallback/dark/small/add.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 7 3.0058594 L 7 8 L 2 8 L 2 8.9980469 L 7 8.9980469 L 7 14.007812 L 8 14.007812 L 8 8.9980469 L 13 8.9980469 L 13 8 L 8 8 L 8 3.0058594 L 7 3.0058594 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 490 B |
13
resources/images/fallback/dark/small/copy.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 3 2 L 3 13 L 6 13 L 6 14 L 11 14 L 14 11 L 14 4 L 13 4 L 13 2 L 3.7851562 2 L 3 2 z M 4 3 L 12 3 L 12 4 L 6 4 L 6 12 L 4 12 L 4 3 z M 7 5 L 13 5 L 13 10 L 10 10 L 10 13 L 7 13 L 7 5 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 522 B |
13
resources/images/fallback/dark/small/edit-rename.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 10.398438 2 L 5.2871094 7.1113281 L 2 10.398438 L 2 14 L 5.6015625 14 L 14 5.6015625 L 10.398438 2 z M 8.3496094 5.4902344 L 10.509766 7.6503906 L 7.3359375 10.826172 L 7.3359375 10.150391 L 6.3222656 10.171875 L 5.2871094 10.171875 L 5.2871094 9.1367188 L 5.2871094 8.5507812 L 6.7285156 7.1113281 L 8.3496094 5.4902344 z M 4.2734375 9.5644531 L 4.2734375 11.185547 L 5.3085938 11.185547 L 6.3007812 11.185547 L 6.3222656 11.837891 L 5.2421875 12.919922 L 3.8007812 12.919922 L 3.0800781 12.199219 L 3.0800781 10.757812 L 4.2734375 9.5644531 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 883 B |
14
resources/images/fallback/dark/small/favorite.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 11,3 8.523,8.266 3,9.11 7,13.21 6.055,19 11,16.266 15.945,19 15,13.21 19,9.11 13.477,8.266 11,3 Z"
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 439 B |
13
resources/images/fallback/dark/small/group-new.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 11.5 2 A 2.5 2.5 0 0 0 9 4.5 A 2.5 2.5 0 0 0 11.5 7 A 2.5 2.5 0 0 0 14 4.5 A 2.5 2.5 0 0 0 11.5 2 z M 5.5 4 A 2.5 2.5 0 0 0 3 6.5 A 2.5 2.5 0 0 0 5.5 9 A 2.5 2.5 0 0 0 8 6.5 A 2.5 2.5 0 0 0 5.5 4 z M 11 8.0722656 A 3.4999979 4 0 0 0 8.4921875 10.273438 A 4.5 5 0 0 1 9.0917969 11 L 9.2929688 11 A 2.5 3 0 0 1 11 9.1074219 L 11 8.0722656 z M 12 9 L 12 11 L 10 11 L 10 12 L 12 12 L 12 14 L 13 14 L 13 12 L 15 12 L 15 11 L 13 11 L 13 9 L 12 9 z M 5.5 10 A 3.4999979 4 0 0 0 2 14 L 3 14 L 8 14 L 9 14 A 3.4999979 4 0 0 0 5.5 10 z M 5.5 11 A 2.5 3 0 0 1 7.8535156 13 L 3.1464844 13 A 2.5 3 0 0 1 5.5 11 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 938 B |
13
resources/images/fallback/dark/small/group.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 11.5 2 A 2.5 2.5 0 0 0 9 4.5 A 2.5 2.5 0 0 0 11.5 7 A 2.5 2.5 0 0 0 14 4.5 A 2.5 2.5 0 0 0 11.5 2 z M 5.5 4 A 2.5 2.5 0 0 0 3 6.5 A 2.5 2.5 0 0 0 5.5 9 A 2.5 2.5 0 0 0 8 6.5 A 2.5 2.5 0 0 0 5.5 4 z M 11.646484 8 A 3.4999979 4 0 0 0 8.4921875 10.273438 A 4.5 5 0 0 1 9.6171875 12 L 14.146484 12 L 15.146484 12 A 3.4999979 4 0 0 0 11.646484 8 z M 11.646484 9 A 2.5 3 0 0 1 14 11 L 9.2929688 11 A 2.5 3 0 0 1 11.646484 9 z M 5.5 10 A 3.4999979 4 0 0 0 2 14 L 3 14 L 8 14 L 9 14 A 3.4999979 4 0 0 0 5.5 10 z M 5.5 11 A 2.5 3 0 0 1 7.8535156 13 L 3.1464844 13 A 2.5 3 0 0 1 5.5 11 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 916 B |
14
resources/images/fallback/dark/small/unfavorite.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 12,4 9.5273438,9.2675781 4,10.111328 8,14.210938 7.0566406,20 12,17.267578 16.943359,20 16,14.210938 20,10.111328 14.472656,9.2675781 12,4 Z M 12,6 13.853516,9.9492188 18,10.583984 15,13.658203 15.708984,18 12,15.949219 8.2910156,18 9,13.658203 6,10.583984 10.146484,9.9492188 12,6 Z" transform="translate(-.99999-.99999)"
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 660 B |
13
resources/images/fallback/dark/small/user-properties.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 9 2 L 9 3 L 14 3 L 14 2 L 9 2 z M 5.5 4 A 2.5 2.5 0 0 0 3 6.5 A 2.5 2.5 0 0 0 5.5 9 A 2.5 2.5 0 0 0 8 6.5 A 2.5 2.5 0 0 0 5.5 4 z M 9 5 L 9 6 L 14 6 L 14 5 L 9 5 z M 9 8 L 9 9 L 14 9 L 14 8 L 9 8 z M 5.5 10 A 3.499998 4 0 0 0 2 14 L 9 14 A 3.499998 4 0 0 0 5.5 10 z M 5.5 11 A 2.5 3 0 0 1 7.8535156 13 L 3.1464844 13 A 2.5 3 0 0 1 5.5 11 z "
|
||||
class="ColorScheme-Text"/>
|
||||
</svg>
|
After Width: | Height: | Size: 678 B |
14
resources/images/fallback/light/big/add.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 10 4 L 10 11 L 3 11 L 3 12 L 10 12 L 10 19 L 11 19 L 11 12 L 18 12 L 18 11 L 11 11 L 11 4 L 10 4 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 441 B |
14
resources/images/fallback/light/big/copy.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="m4 3v1 13h1 2 1v1 1h6l4-4v-1-7-1h-2v-3h-1-10-1m1 1h10v2h-7v1 9h-1-2v-12m4 3h8v7h-3-1v1 3h-4v-11"
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 432 B |
14
resources/images/fallback/light/big/edit-rename.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="m14.996094 3l-11.992188 11.992188h-.003906v4.00781h1 2 1.00781v-.003906l11.992188-11.992188-.001953-.001953.001953-.001953-4-4-.001953.001953-.001953-.001953m-1.998047 3.412109l2.589844 2.589844-7.587891 7.587891v-1.589844h-1-1v-1-.589844l6.998047-6.998047m-7.998047 7.998047v1.589844h1 1v1 .589844l-.410156.410156h-1.589844l-1-1v-1.589844l1-1"
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 681 B |
14
resources/images/fallback/light/big/favorite.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 11,3 8.523,8.266 3,9.11 7,13.21 6.055,19 11,16.266 15.945,19 15,13.21 19,9.11 13.477,8.266 11,3 Z"
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 439 B |
14
resources/images/fallback/light/big/group-new.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 14 3 C 12.338 3 11 4.338 11 6 C 11 7.662 12.338 9 14 9 C 15.662 9 17 7.662 17 6 C 17 4.338 15.662 3 14 3 z M 8 6 C 6.3380003 6 5 7.3380003 5 9 C 5 10.662 6.3380003 12 8 12 C 9.6619997 12 11 10.662 11 9 C 11 7.3380003 9.6619997 6 8 6 z M 14 10 C 13.353654 10 12.744006 10.134157 12.181641 10.361328 L 12.636719 11.275391 C 13.064535 11.114885 13.514485 11 14 11 C 15.482985 11 16.758385 11.807292 17.449219 13 L 18.580078 13 C 17.810617 11.232833 16.056835 10 14 10 z M 8 13 C 5.2299834 13 3 15.229983 3 18 L 3 19 L 13 19 L 13 18 C 13 15.229983 10.770017 13 8 13 z M 8 14 C 10.216017 14 12 15.783983 12 18 L 4 18 C 4 15.783983 5.7839834 14 8 14 z M 16 14 L 16 16 L 14 16 L 14 17 L 16 17 L 16 19 L 17 19 L 17 17 L 19 17 L 19 16 L 17 16 L 17 14 L 16 14 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
14
resources/images/fallback/light/big/group.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 14 3 C 12.338 3 11 4.338 11 6 C 11 7.662 12.338 9 14 9 C 15.662 9 17 7.662 17 6 C 17 4.338 15.662 3 14 3 z M 8 6 C 6.338 6 5 7.338 5 9 C 5 10.662 6.338 12 8 12 C 9.662 12 11 10.662 11 9 C 11 7.338 9.662 6 8 6 z M 14 10 C 13.353654 10 12.744006 10.134157 12.181641 10.361328 L 12.636719 11.275391 C 13.064535 11.114885 13.514485 11 14 11 C 16.21602 11 18 12.78398 18 15 L 14.5 15 L 15 16 L 19 16 L 19 15 C 19 12.22998 16.77002 10 14 10 z M 8 13 C 5.229983 13 3 15.229983 3 18 L 3 19 L 13 19 L 13 18 C 13 15.229983 10.770017 13 8 13 z M 8 14 C 10.216017 14 12 15.783983 12 18 L 4 18 C 4 15.783983 5.783983 14 8 14 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 953 B |
14
resources/images/fallback/light/big/unfavorite.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 12,4 9.5273438,9.2675781 4,10.111328 8,14.210938 7.0566406,20 12,17.267578 16.943359,20 16,14.210938 20,10.111328 14.472656,9.2675781 12,4 Z M 12,6 13.853516,9.9492188 18,10.583984 15,13.658203 15.708984,18 12,15.949219 8.2910156,18 9,13.658203 6,10.583984 10.146484,9.9492188 12,6 Z" transform="translate(-.99999-.99999)"
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 660 B |
14
resources/images/fallback/light/big/user-properties.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 10 3 L 10 4 L 19 4 L 19 3 L 10 3 z M 8 6 C 6.338 6 5 7.338 5 9 C 5 10.662 6.338 12 8 12 C 9.662 12 11 10.662 11 9 C 11 7.338 9.662 6 8 6 z M 12 8 L 12 9 L 19 9 L 19 8 L 12 8 z M 8 13 C 5.229983 13 3 15.229983 3 18 L 3 19 L 13 19 L 13 18 C 13 15.229983 10.770017 13 8 13 z M 13 13 L 13 14 L 19 14 L 19 13 L 13 13 z M 8 14 C 10.216017 14 12 15.783983 12 18 L 4 18 C 4 15.783983 5.783983 14 8 14 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 734 B |
13
resources/images/fallback/light/small/add.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 7 3.0058594 L 7 8 L 2 8 L 2 8.9980469 L 7 8.9980469 L 7 14.007812 L 8 14.007812 L 8 8.9980469 L 13 8.9980469 L 13 8 L 8 8 L 8 3.0058594 L 7 3.0058594 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 490 B |
13
resources/images/fallback/light/small/copy.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 3 2 L 3 13 L 6 13 L 6 14 L 11 14 L 14 11 L 14 4 L 13 4 L 13 2 L 3.7851562 2 L 3 2 z M 4 3 L 12 3 L 12 4 L 6 4 L 6 12 L 4 12 L 4 3 z M 7 5 L 13 5 L 13 10 L 10 10 L 10 13 L 7 13 L 7 5 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 522 B |
13
resources/images/fallback/light/small/edit-rename.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 10.398438 2 L 5.2871094 7.1113281 L 2 10.398438 L 2 14 L 5.6015625 14 L 14 5.6015625 L 10.398438 2 z M 8.3496094 5.4902344 L 10.509766 7.6503906 L 7.3359375 10.826172 L 7.3359375 10.150391 L 6.3222656 10.171875 L 5.2871094 10.171875 L 5.2871094 9.1367188 L 5.2871094 8.5507812 L 6.7285156 7.1113281 L 8.3496094 5.4902344 z M 4.2734375 9.5644531 L 4.2734375 11.185547 L 5.3085938 11.185547 L 6.3007812 11.185547 L 6.3222656 11.837891 L 5.2421875 12.919922 L 3.8007812 12.919922 L 3.0800781 12.199219 L 3.0800781 10.757812 L 4.2734375 9.5644531 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 883 B |
14
resources/images/fallback/light/small/favorite.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 11,3 8.523,8.266 3,9.11 7,13.21 6.055,19 11,16.266 15.945,19 15,13.21 19,9.11 13.477,8.266 11,3 Z"
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 439 B |
13
resources/images/fallback/light/small/group-new.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 11.5 2 A 2.5 2.5 0 0 0 9 4.5 A 2.5 2.5 0 0 0 11.5 7 A 2.5 2.5 0 0 0 14 4.5 A 2.5 2.5 0 0 0 11.5 2 z M 5.5 4 A 2.5 2.5 0 0 0 3 6.5 A 2.5 2.5 0 0 0 5.5 9 A 2.5 2.5 0 0 0 8 6.5 A 2.5 2.5 0 0 0 5.5 4 z M 11 8.0722656 A 3.4999979 4 0 0 0 8.4921875 10.273438 A 4.5 5 0 0 1 9.0917969 11 L 9.2929688 11 A 2.5 3 0 0 1 11 9.1074219 L 11 8.0722656 z M 12 9 L 12 11 L 10 11 L 10 12 L 12 12 L 12 14 L 13 14 L 13 12 L 15 12 L 15 11 L 13 11 L 13 9 L 12 9 z M 5.5 10 A 3.4999979 4 0 0 0 2 14 L 3 14 L 8 14 L 9 14 A 3.4999979 4 0 0 0 5.5 10 z M 5.5 11 A 2.5 3 0 0 1 7.8535156 13 L 3.1464844 13 A 2.5 3 0 0 1 5.5 11 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 938 B |
13
resources/images/fallback/light/small/group.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 11.5 2 A 2.5 2.5 0 0 0 9 4.5 A 2.5 2.5 0 0 0 11.5 7 A 2.5 2.5 0 0 0 14 4.5 A 2.5 2.5 0 0 0 11.5 2 z M 5.5 4 A 2.5 2.5 0 0 0 3 6.5 A 2.5 2.5 0 0 0 5.5 9 A 2.5 2.5 0 0 0 8 6.5 A 2.5 2.5 0 0 0 5.5 4 z M 11.646484 8 A 3.4999979 4 0 0 0 8.4921875 10.273438 A 4.5 5 0 0 1 9.6171875 12 L 14.146484 12 L 15.146484 12 A 3.4999979 4 0 0 0 11.646484 8 z M 11.646484 9 A 2.5 3 0 0 1 14 11 L 9.2929688 11 A 2.5 3 0 0 1 11.646484 9 z M 5.5 10 A 3.4999979 4 0 0 0 2 14 L 3 14 L 8 14 L 9 14 A 3.4999979 4 0 0 0 5.5 10 z M 5.5 11 A 2.5 3 0 0 1 7.8535156 13 L 3.1464844 13 A 2.5 3 0 0 1 5.5 11 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 916 B |
14
resources/images/fallback/light/small/unfavorite.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 12,4 9.5273438,9.2675781 4,10.111328 8,14.210938 7.0566406,20 12,17.267578 16.943359,20 16,14.210938 20,10.111328 14.472656,9.2675781 12,4 Z M 12,6 13.853516,9.9492188 18,10.583984 15,13.658203 15.708984,18 12,15.949219 8.2910156,18 9,13.658203 6,10.583984 10.146484,9.9492188 12,6 Z" transform="translate(-.99999-.99999)"
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 660 B |
13
resources/images/fallback/light/small/user-properties.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#eff0f1;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 9 2 L 9 3 L 14 3 L 14 2 L 9 2 z M 5.5 4 A 2.5 2.5 0 0 0 3 6.5 A 2.5 2.5 0 0 0 5.5 9 A 2.5 2.5 0 0 0 8 6.5 A 2.5 2.5 0 0 0 5.5 4 z M 9 5 L 9 6 L 14 6 L 14 5 L 9 5 z M 9 8 L 9 9 L 14 9 L 14 8 L 9 8 z M 5.5 10 A 3.499998 4 0 0 0 2 14 L 9 14 A 3.499998 4 0 0 0 5.5 10 z M 5.5 11 A 2.5 3 0 0 1 7.8535156 13 L 3.1464844 13 A 2.5 3 0 0 1 5.5 11 z "
|
||||
class="ColorScheme-Text"/>
|
||||
</svg>
|
After Width: | Height: | Size: 678 B |
@ -32,6 +32,14 @@
|
||||
<file>images/fallback/dark/big/clean.svg</file>
|
||||
<file>images/fallback/dark/big/send.svg</file>
|
||||
<file>images/fallback/dark/big/mail-attachment.svg</file>
|
||||
<file>images/fallback/dark/big/group.svg</file>
|
||||
<file>images/fallback/dark/big/group-new.svg</file>
|
||||
<file>images/fallback/dark/big/edit-rename.svg</file>
|
||||
<file>images/fallback/dark/big/user-properties.svg</file>
|
||||
<file>images/fallback/dark/big/copy.svg</file>
|
||||
<file>images/fallback/dark/big/favorite.svg</file>
|
||||
<file>images/fallback/dark/big/unfavorite.svg</file>
|
||||
<file>images/fallback/dark/big/add.svg</file>
|
||||
|
||||
|
||||
<file>images/fallback/dark/small/absent.svg</file>
|
||||
@ -64,6 +72,14 @@
|
||||
<file>images/fallback/dark/small/clean.svg</file>
|
||||
<file>images/fallback/dark/small/send.svg</file>
|
||||
<file>images/fallback/dark/small/mail-attachment.svg</file>
|
||||
<file>images/fallback/dark/small/group.svg</file>
|
||||
<file>images/fallback/dark/small/group-new.svg</file>
|
||||
<file>images/fallback/dark/small/edit-rename.svg</file>
|
||||
<file>images/fallback/dark/small/user-properties.svg</file>
|
||||
<file>images/fallback/dark/small/copy.svg</file>
|
||||
<file>images/fallback/dark/small/favorite.svg</file>
|
||||
<file>images/fallback/dark/small/unfavorite.svg</file>
|
||||
<file>images/fallback/dark/small/add.svg</file>
|
||||
|
||||
|
||||
<file>images/fallback/light/big/absent.svg</file>
|
||||
@ -96,6 +112,14 @@
|
||||
<file>images/fallback/light/big/clean.svg</file>
|
||||
<file>images/fallback/light/big/send.svg</file>
|
||||
<file>images/fallback/light/big/mail-attachment.svg</file>
|
||||
<file>images/fallback/light/big/group.svg</file>
|
||||
<file>images/fallback/light/big/group-new.svg</file>
|
||||
<file>images/fallback/light/big/edit-rename.svg</file>
|
||||
<file>images/fallback/light/big/user-properties.svg</file>
|
||||
<file>images/fallback/light/big/copy.svg</file>
|
||||
<file>images/fallback/light/big/favorite.svg</file>
|
||||
<file>images/fallback/light/big/unfavorite.svg</file>
|
||||
<file>images/fallback/light/big/add.svg</file>
|
||||
|
||||
|
||||
<file>images/fallback/light/small/absent.svg</file>
|
||||
@ -128,5 +152,13 @@
|
||||
<file>images/fallback/light/small/clean.svg</file>
|
||||
<file>images/fallback/light/small/send.svg</file>
|
||||
<file>images/fallback/light/small/mail-attachment.svg</file>
|
||||
<file>images/fallback/light/small/group.svg</file>
|
||||
<file>images/fallback/light/small/group-new.svg</file>
|
||||
<file>images/fallback/light/small/edit-rename.svg</file>
|
||||
<file>images/fallback/light/small/user-properties.svg</file>
|
||||
<file>images/fallback/light/small/copy.svg</file>
|
||||
<file>images/fallback/light/small/favorite.svg</file>
|
||||
<file>images/fallback/light/small/unfavorite.svg</file>
|
||||
<file>images/fallback/light/small/add.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -38,7 +38,7 @@ SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent):
|
||||
}
|
||||
|
||||
snInt = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this);
|
||||
connect(snInt, SIGNAL(activated(int)), this, SLOT(handleSigInt()));
|
||||
connect(snInt, &QSocketNotifier::activated, this, &SignalCatcher::handleSigInt);
|
||||
}
|
||||
|
||||
SignalCatcher::~SignalCatcher()
|
||||
|
858
translations/squawk.ru.ts
Normal file
@ -0,0 +1,858 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="ru_RU">
|
||||
<context>
|
||||
<name>Account</name>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="14"/>
|
||||
<source>Account</source>
|
||||
<translatorcomment>Заголовок окна</translatorcomment>
|
||||
<translation>Учетная запись</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="40"/>
|
||||
<source>Your account login</source>
|
||||
<translation>Имя пользователя Вашей учетной записи</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="43"/>
|
||||
<source>john_smith1987</source>
|
||||
<translation>ivan_ivanov1987</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="50"/>
|
||||
<source>Server</source>
|
||||
<translation>Сервер</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="57"/>
|
||||
<source>A server address of your account. Like 404.city or macaw.me</source>
|
||||
<translation>Адресс сервера вашей учетной записи (выглядит как 404.city или macaw.me)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="60"/>
|
||||
<source>macaw.me</source>
|
||||
<translation>macaw.me</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="67"/>
|
||||
<source>Login</source>
|
||||
<translation>Имя учетной записи</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="74"/>
|
||||
<source>Password</source>
|
||||
<translation>Пароль</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="81"/>
|
||||
<source>Password of your account</source>
|
||||
<translation>Пароль вашей учетной записи</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="103"/>
|
||||
<source>Name</source>
|
||||
<translation>Имя</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="110"/>
|
||||
<source>Just a name how would you call this account, doesn't affect anything</source>
|
||||
<translation>Просто имя, то как Вы называете свою учетную запись, может быть любым</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="113"/>
|
||||
<source>John</source>
|
||||
<translation>Иван</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="120"/>
|
||||
<source>Resource</source>
|
||||
<translation>Ресурс</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="127"/>
|
||||
<source>A resource name like "Home" or "Work"</source>
|
||||
<translation>Имя этой программы для ваших контактов, может быть "Home" или "Phone"</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/account.ui" line="130"/>
|
||||
<source>QXmpp</source>
|
||||
<translatorcomment>Ресурс по умолчанию</translatorcomment>
|
||||
<translation>QXmpp</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Accounts</name>
|
||||
<message>
|
||||
<location filename="../ui/widgets/accounts.ui" line="14"/>
|
||||
<source>Accounts</source>
|
||||
<translation>Учетные записи</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/accounts.ui" line="45"/>
|
||||
<source>Delete</source>
|
||||
<translation>Удалить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/accounts.ui" line="86"/>
|
||||
<source>Add</source>
|
||||
<translation>Добавить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/accounts.ui" line="96"/>
|
||||
<source>Edit</source>
|
||||
<translation>Редактировать</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/accounts.ui" line="106"/>
|
||||
<source>Change password</source>
|
||||
<translation>Изменить пароль</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/accounts.ui" line="129"/>
|
||||
<location filename="../ui/widgets/accounts.cpp" line="125"/>
|
||||
<location filename="../ui/widgets/accounts.cpp" line="128"/>
|
||||
<source>Connect</source>
|
||||
<translation>Подключить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/accounts.cpp" line="122"/>
|
||||
<source>Disconnect</source>
|
||||
<translation>Отключить</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Conversation</name>
|
||||
<message>
|
||||
<location filename="../ui/widgets/conversation.ui" line="449"/>
|
||||
<source>Type your message here...</source>
|
||||
<translation>Введите сообщение...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/conversation.cpp" line="260"/>
|
||||
<source>Chose a file to send</source>
|
||||
<translation>Выберите файл для отправки</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Global</name>
|
||||
<message>
|
||||
<source>Disconnected</source>
|
||||
<translation>Отключен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting</source>
|
||||
<translation>Подключается</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connected</source>
|
||||
<translation>Подключен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error</source>
|
||||
<translation>Ошибка</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Online</source>
|
||||
<translation>В сети</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Away</source>
|
||||
<translation>Отошел</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Busy</source>
|
||||
<translation>Занят</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Absent</source>
|
||||
<translation>Недоступен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Chatty</source>
|
||||
<translation>Готов поболтать</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invisible</source>
|
||||
<translation>Невидимый</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Offline</source>
|
||||
<translation>Отключен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>None</source>
|
||||
<translation>Нет</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>From</source>
|
||||
<translation>Входящая</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>To</source>
|
||||
<translation>Исходящая</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Both</source>
|
||||
<translation>Взаимная</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unknown</source>
|
||||
<translation>Неизвестно</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unspecified</source>
|
||||
<translation>Не назначено</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Outcast</source>
|
||||
<translation>Изгой</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Nobody</source>
|
||||
<translation>Никто</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Member</source>
|
||||
<translation>Участник</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Admin</source>
|
||||
<translation>Администратор</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Owner</source>
|
||||
<translation>Владелец</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Visitor</source>
|
||||
<translation>Гость</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Participant</source>
|
||||
<translation>Участник</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Moderator</source>
|
||||
<translation>Модератор</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Not specified</source>
|
||||
<translation>Не указан</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Personal</source>
|
||||
<translation>Личный</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Business</source>
|
||||
<translation>Рабочий</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Fax</source>
|
||||
<translation>Факс</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pager</source>
|
||||
<translation>Пэйджер</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Voice</source>
|
||||
<translation>Стационарный</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cell</source>
|
||||
<translation>Мобильный</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Video</source>
|
||||
<translation>Видеофон</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Modem</source>
|
||||
<translation>Модем</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Other</source>
|
||||
<translation>Другой</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>JoinConference</name>
|
||||
<message>
|
||||
<location filename="../ui/widgets/joinconference.ui" line="14"/>
|
||||
<source>Join new conference</source>
|
||||
<translatorcomment>Заголовок окна</translatorcomment>
|
||||
<translation>Присоединиться к новой беседе</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/joinconference.ui" line="22"/>
|
||||
<source>JID</source>
|
||||
<translation>JID</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/joinconference.ui" line="29"/>
|
||||
<source>Room JID</source>
|
||||
<translation>Jabber-идентификатор беседы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/joinconference.ui" line="32"/>
|
||||
<source>identifier@conference.server.org</source>
|
||||
<translation>identifier@conference.server.org</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/joinconference.ui" line="39"/>
|
||||
<source>Account</source>
|
||||
<translation>Учетная запись</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/joinconference.ui" line="49"/>
|
||||
<source>Join on login</source>
|
||||
<translation>Автовход</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/joinconference.ui" line="56"/>
|
||||
<source>If checked Squawk will try to join this conference on login</source>
|
||||
<translation>Если стоит галочка Squawk автоматически присоединится к этой беседе при подключении</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/joinconference.ui" line="66"/>
|
||||
<source>Nick name</source>
|
||||
<translation>Псевдоним</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/joinconference.ui" line="73"/>
|
||||
<source>Your nick name for that conference. If you leave this field empty your account name will be used as a nick name</source>
|
||||
<translation>Ваш псевдоним в этой беседе, если оставите это поле пустым - будет использовано имя Вашей учетной записи</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/joinconference.ui" line="76"/>
|
||||
<source>John</source>
|
||||
<translation>Ivan</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Message</name>
|
||||
<message>
|
||||
<location filename="../ui/utils/message.cpp" line="119"/>
|
||||
<source>Download</source>
|
||||
<translation>Скачать</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/utils/message.cpp" line="123"/>
|
||||
<source>Error downloading file: %1
|
||||
You can try again</source>
|
||||
<translation>Ошибка загрузки файла: %1
|
||||
Вы можете попробовать снова</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/utils/message.cpp" line="125"/>
|
||||
<source>%1 is offering you to download a file</source>
|
||||
<translation>%1 предлагает Вам скачать файл</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/utils/message.cpp" line="190"/>
|
||||
<source>Open</source>
|
||||
<translation>Открыть</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Models::Accounts</name>
|
||||
<message>
|
||||
<source>Name</source>
|
||||
<translation>Имя</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Server</source>
|
||||
<translation>Сервер</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>State</source>
|
||||
<translation>Состояние</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error</source>
|
||||
<translation>Ошибка</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Models::Room</name>
|
||||
<message>
|
||||
<location filename="../ui/models/room.cpp" line="196"/>
|
||||
<source>Subscribed</source>
|
||||
<translation>Вы состоите в беседе</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/room.cpp" line="198"/>
|
||||
<source>Temporarily unsubscribed</source>
|
||||
<translation>Вы временно не состоите в беседе</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/room.cpp" line="202"/>
|
||||
<source>Temporarily subscribed</source>
|
||||
<translation>Вы временно состоите в беседе</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/room.cpp" line="204"/>
|
||||
<source>Unsubscribed</source>
|
||||
<translation>Вы не состоите в беседе</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Models::Roster</name>
|
||||
<message>
|
||||
<location filename="../ui/models/roster.cpp" line="80"/>
|
||||
<source>New messages</source>
|
||||
<translation>Есть непрочитанные сообщения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/roster.cpp" line="178"/>
|
||||
<location filename="../ui/models/roster.cpp" line="204"/>
|
||||
<location filename="../ui/models/roster.cpp" line="241"/>
|
||||
<location filename="../ui/models/roster.cpp" line="253"/>
|
||||
<source>New messages: </source>
|
||||
<translation>Новых сообщений: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/roster.cpp" line="180"/>
|
||||
<source>Jabber ID: </source>
|
||||
<translation>Идентификатор: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/roster.cpp" line="184"/>
|
||||
<location filename="../ui/models/roster.cpp" line="207"/>
|
||||
<location filename="../ui/models/roster.cpp" line="220"/>
|
||||
<source>Availability: </source>
|
||||
<translation>Доступность: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/roster.cpp" line="188"/>
|
||||
<location filename="../ui/models/roster.cpp" line="210"/>
|
||||
<location filename="../ui/models/roster.cpp" line="223"/>
|
||||
<source>Status: </source>
|
||||
<translation>Статус: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/roster.cpp" line="191"/>
|
||||
<location filename="../ui/models/roster.cpp" line="193"/>
|
||||
<location filename="../ui/models/roster.cpp" line="255"/>
|
||||
<source>Subscription: </source>
|
||||
<translation>Подписка: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/roster.cpp" line="226"/>
|
||||
<source>Affiliation: </source>
|
||||
<translatorcomment>Я правда не знаю, как это объяснить, не то что перевести</translatorcomment>
|
||||
<translation>Причастность: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/roster.cpp" line="229"/>
|
||||
<source>Role: </source>
|
||||
<translation>Роль: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/roster.cpp" line="243"/>
|
||||
<source>Online contacts: </source>
|
||||
<translation>Контакстов в сети: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/roster.cpp" line="244"/>
|
||||
<source>Total contacts: </source>
|
||||
<translation>Всего контактов: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/roster.cpp" line="257"/>
|
||||
<source>Members: </source>
|
||||
<translation>Участников: </translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>NewContact</name>
|
||||
<message>
|
||||
<location filename="../ui/widgets/newcontact.ui" line="14"/>
|
||||
<source>Add new contact</source>
|
||||
<translatorcomment>Заголовок окна</translatorcomment>
|
||||
<translation>Добавление нового контакта</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/newcontact.ui" line="22"/>
|
||||
<source>Account</source>
|
||||
<translation>Учетная запись</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/newcontact.ui" line="29"/>
|
||||
<source>An account that is going to have new contact</source>
|
||||
<translation>Учетная запись для которой будет добавлен контакт</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/newcontact.ui" line="36"/>
|
||||
<source>JID</source>
|
||||
<translation>JID</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/newcontact.ui" line="43"/>
|
||||
<source>Jabber id of your new contact</source>
|
||||
<translation>Jabber-идентификатор нового контакта</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/newcontact.ui" line="46"/>
|
||||
<source>name@server.dmn</source>
|
||||
<translatorcomment>Placeholder поля ввода JID</translatorcomment>
|
||||
<translation>name@server.dmn</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/newcontact.ui" line="53"/>
|
||||
<source>Name</source>
|
||||
<translation>Имя</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/newcontact.ui" line="60"/>
|
||||
<source>The way this new contact will be labeled in your roster (optional)</source>
|
||||
<translation>То, как будет подписан контакт в вашем списке контактов (не обязательно)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/newcontact.ui" line="63"/>
|
||||
<source>John Smith</source>
|
||||
<translation>Иван Иванов</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Squawk</name>
|
||||
<message>
|
||||
<location filename="../ui/squawk.ui" line="14"/>
|
||||
<source>squawk</source>
|
||||
<translation>Squawk</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.ui" line="78"/>
|
||||
<source>Settings</source>
|
||||
<translation>Настройки</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.ui" line="84"/>
|
||||
<source>Squawk</source>
|
||||
<translation>Squawk</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.ui" line="99"/>
|
||||
<source>Accounts</source>
|
||||
<translation>Учетные записи</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.ui" line="108"/>
|
||||
<source>Quit</source>
|
||||
<translation>Выйти</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.ui" line="120"/>
|
||||
<source>Add contact</source>
|
||||
<translation>Добавить контакт</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.ui" line="132"/>
|
||||
<source>Add conference</source>
|
||||
<translation>Присоединиться к беседе</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="60"/>
|
||||
<source>Contact list</source>
|
||||
<translation>Список контактов</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="549"/>
|
||||
<source>Disconnect</source>
|
||||
<translation>Отключить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="555"/>
|
||||
<source>Connect</source>
|
||||
<translation>Подключить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="561"/>
|
||||
<location filename="../ui/squawk.cpp" line="659"/>
|
||||
<source>VCard</source>
|
||||
<translation>Карточка</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="565"/>
|
||||
<location filename="../ui/squawk.cpp" line="663"/>
|
||||
<location filename="../ui/squawk.cpp" line="703"/>
|
||||
<source>Remove</source>
|
||||
<translation>Удалить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="577"/>
|
||||
<source>Open dialog</source>
|
||||
<translation>Открыть диалог</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="587"/>
|
||||
<location filename="../ui/squawk.cpp" line="684"/>
|
||||
<source>Unsubscribe</source>
|
||||
<translation>Отписаться</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="597"/>
|
||||
<location filename="../ui/squawk.cpp" line="693"/>
|
||||
<source>Subscribe</source>
|
||||
<translation>Подписаться</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="608"/>
|
||||
<source>Rename</source>
|
||||
<translation>Переименовать</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="621"/>
|
||||
<source>Input new name for %1
|
||||
or leave it empty for the contact
|
||||
to be displayed as %1</source>
|
||||
<translation>Введите имя для %1
|
||||
или оставьте пустым,
|
||||
тогда контакт будет отображаться как
|
||||
%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="622"/>
|
||||
<source>Renaming %1</source>
|
||||
<translation>Назначение имени контакту %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="628"/>
|
||||
<source>Groups</source>
|
||||
<translation>Группы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="643"/>
|
||||
<source>New group</source>
|
||||
<translation>Создать новую группу</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="653"/>
|
||||
<source>New group name</source>
|
||||
<translation>Имя группы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="654"/>
|
||||
<source>Add %1 to a new group</source>
|
||||
<translation>Добавление %1 в новую группу</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="675"/>
|
||||
<source>Open conversation</source>
|
||||
<translation>Открыть окно беседы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="780"/>
|
||||
<source>%1 account card</source>
|
||||
<translation>Карточка учетной записи %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="782"/>
|
||||
<source>%1 contact card</source>
|
||||
<translation>Карточка контакта %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/squawk.cpp" line="794"/>
|
||||
<source>Downloading vCard</source>
|
||||
<translation>Получение карточки</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>VCard</name>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="65"/>
|
||||
<source>Received 12.07.2007 at 17.35</source>
|
||||
<translation>Не обновлялось</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="100"/>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="425"/>
|
||||
<source>General</source>
|
||||
<translation>Общее</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="130"/>
|
||||
<source>Organization</source>
|
||||
<translation>Место работы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="180"/>
|
||||
<source>Middle name</source>
|
||||
<translation>Среднее имя</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="190"/>
|
||||
<source>First name</source>
|
||||
<translation>Имя</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="200"/>
|
||||
<source>Last name</source>
|
||||
<translation>Фамилия</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="226"/>
|
||||
<source>Nick name</source>
|
||||
<translation>Псевдоним</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="252"/>
|
||||
<source>Birthday</source>
|
||||
<translation>Дата рождения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="272"/>
|
||||
<source>Organization name</source>
|
||||
<translation>Название организации</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="298"/>
|
||||
<source>Unit / Department</source>
|
||||
<translation>Отдел</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="324"/>
|
||||
<source>Role / Profession</source>
|
||||
<translation>Профессия</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="350"/>
|
||||
<source>Job title</source>
|
||||
<translation>Наименование должности</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="390"/>
|
||||
<source>Full name</source>
|
||||
<translation>Полное имя</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="460"/>
|
||||
<source>Personal information</source>
|
||||
<translation>Личная информация</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="653"/>
|
||||
<source>Addresses</source>
|
||||
<translation>Адреса</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="682"/>
|
||||
<source>E-Mail addresses</source>
|
||||
<translation>Адреса электронной почты</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="767"/>
|
||||
<source>Phone numbers</source>
|
||||
<translation>Номера телефонов</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="522"/>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="546"/>
|
||||
<source>Contact</source>
|
||||
<translation>Контактная информация</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="713"/>
|
||||
<source>Jabber ID</source>
|
||||
<translation>Jabber ID</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="739"/>
|
||||
<source>Web site</source>
|
||||
<translation>Веб сайт</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="798"/>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="822"/>
|
||||
<source>Description</source>
|
||||
<translation>Описание</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="859"/>
|
||||
<source>Set avatar</source>
|
||||
<translation>Установить иконку</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.ui" line="868"/>
|
||||
<source>Clear avatar</source>
|
||||
<translation>Убрать иконку</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="93"/>
|
||||
<source>Account %1 card</source>
|
||||
<translation>Карточка учетной записи %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="108"/>
|
||||
<source>Contact %1 card</source>
|
||||
<translation>Карточка контакта %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="170"/>
|
||||
<source>Received %1 at %2</source>
|
||||
<translation>Получено %1 в %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="221"/>
|
||||
<source>Chose your new avatar</source>
|
||||
<translation>Выберите новую иконку</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="223"/>
|
||||
<source>Images (*.png *.jpg *.jpeg)</source>
|
||||
<translation>Изображения (*.png *.jpg *.jpeg)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="310"/>
|
||||
<source>Add email address</source>
|
||||
<translation>Добавить адрес электронной почты</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="320"/>
|
||||
<source>Unset this email as preferred</source>
|
||||
<translation>Убрать отметку "предпочтительный" с этого адреса</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="323"/>
|
||||
<source>Set this email as preferred</source>
|
||||
<translation>Отметить этот адрес как "предпочтительный"</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="328"/>
|
||||
<source>Remove selected email addresses</source>
|
||||
<translation>Удалить выбранные адреса</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="333"/>
|
||||
<source>Copy selected emails to clipboard</source>
|
||||
<translation>Скопировать выбранные адреса в буфер обмена</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="338"/>
|
||||
<source>Add phone number</source>
|
||||
<translation>Добавить номер телефона</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="348"/>
|
||||
<source>Unset this phone as preferred</source>
|
||||
<translation>Убрать отметку "предпочтительный" с этого номера</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="351"/>
|
||||
<source>Set this phone as preferred</source>
|
||||
<translation>Отметить этот номер как "предпочтительный"</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="356"/>
|
||||
<source>Remove selected phone numbers</source>
|
||||
<translation>Удалить выбранные телефонные номера</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/widgets/vcard/vcard.cpp" line="361"/>
|
||||
<source>Copy selected phones to clipboard</source>
|
||||
<translation>Скопировать выбранные телефонные номера в буфер обмена</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
@ -10,6 +10,8 @@ set(CMAKE_AUTOUIC ON)
|
||||
find_package(Qt5Widgets CONFIG REQUIRED)
|
||||
find_package(Qt5DBus CONFIG REQUIRED)
|
||||
|
||||
add_subdirectory(widgets)
|
||||
|
||||
set(squawkUI_SRC
|
||||
squawk.cpp
|
||||
models/accounts.cpp
|
||||
@ -22,27 +24,20 @@ set(squawkUI_SRC
|
||||
models/room.cpp
|
||||
models/abstractparticipant.cpp
|
||||
models/participant.cpp
|
||||
widgets/conversation.cpp
|
||||
widgets/chat.cpp
|
||||
widgets/room.cpp
|
||||
widgets/newcontact.cpp
|
||||
widgets/accounts.cpp
|
||||
widgets/account.cpp
|
||||
widgets/joinconference.cpp
|
||||
utils/messageline.cpp
|
||||
utils//message.cpp
|
||||
utils/resizer.cpp
|
||||
utils/image.cpp
|
||||
utils/flowlayout.cpp
|
||||
utils/badge.cpp
|
||||
utils/progress.cpp
|
||||
utils/comboboxdelegate.cpp
|
||||
)
|
||||
|
||||
# Tell CMake to create the helloworld executable
|
||||
add_library(squawkUI ${squawkUI_SRC})
|
||||
|
||||
# Use the Widgets module from Qt 5.
|
||||
target_link_libraries(squawkUI squawkWidgets)
|
||||
target_link_libraries(squawkUI Qt5::Widgets)
|
||||
target_link_libraries(squawkUI Qt5::DBus)
|
||||
|
||||
# Install the executable
|
||||
install(TARGETS squawkUI DESTINATION lib)
|
||||
|
@ -32,6 +32,15 @@ Models::AbstractParticipant::AbstractParticipant(Models::Item::Type p_type, cons
|
||||
}
|
||||
}
|
||||
|
||||
Models::AbstractParticipant::AbstractParticipant(const Models::AbstractParticipant& other):
|
||||
Item(other),
|
||||
availability(other.availability),
|
||||
lastActivity(other.lastActivity),
|
||||
status(other.status)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Models::AbstractParticipant::~AbstractParticipant()
|
||||
{
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ class AbstractParticipant : public Models::Item
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AbstractParticipant(Type p_type, const QMap<QString, QVariant> &data, Item *parentItem = 0);
|
||||
AbstractParticipant(const AbstractParticipant& other);
|
||||
~AbstractParticipant();
|
||||
|
||||
virtual int columnCount() const override;
|
||||
|
@ -26,6 +26,7 @@ Models::Account::Account(const QMap<QString, QVariant>& data, Models::Item* pare
|
||||
server(data.value("server").toString()),
|
||||
resource(data.value("resource").toString()),
|
||||
error(data.value("error").toString()),
|
||||
avatarPath(data.value("avatarPath").toString()),
|
||||
state(Shared::disconnected),
|
||||
availability(Shared::offline)
|
||||
{
|
||||
@ -151,7 +152,7 @@ QVariant Models::Account::data(int column) const
|
||||
case 1:
|
||||
return server;
|
||||
case 2:
|
||||
return Shared::connectionStateNames[state];
|
||||
return QCoreApplication::translate("Global", Shared::connectionStateNames[state].toLatin1());
|
||||
case 3:
|
||||
return error;
|
||||
case 4:
|
||||
@ -159,9 +160,11 @@ QVariant Models::Account::data(int column) const
|
||||
case 5:
|
||||
return password;
|
||||
case 6:
|
||||
return Shared::availabilityNames[availability];
|
||||
return QCoreApplication::translate("Global", Shared::availabilityNames[availability].toLatin1());
|
||||
case 7:
|
||||
return resource;
|
||||
case 8:
|
||||
return avatarPath;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
@ -169,7 +172,7 @@ QVariant Models::Account::data(int column) const
|
||||
|
||||
int Models::Account::columnCount() const
|
||||
{
|
||||
return 8;
|
||||
return 9;
|
||||
}
|
||||
|
||||
void Models::Account::update(const QString& field, const QVariant& value)
|
||||
@ -190,6 +193,8 @@ void Models::Account::update(const QString& field, const QVariant& value)
|
||||
setResource(value.toString());
|
||||
} else if (field == "error") {
|
||||
setError(value.toString());
|
||||
} else if (field == "avatarPath") {
|
||||
setAvatarPath(value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,3 +229,24 @@ void Models::Account::toOfflineState()
|
||||
setAvailability(Shared::offline);
|
||||
Item::toOfflineState();
|
||||
}
|
||||
|
||||
QString Models::Account::getAvatarPath()
|
||||
{
|
||||
return avatarPath;
|
||||
}
|
||||
|
||||
void Models::Account::setAvatarPath(const QString& path)
|
||||
{
|
||||
avatarPath = path;
|
||||
changed(8); //it's uncoditional because the path doesn't change when one avatar of the same type replaces another, sha1 sums checks are on the backend
|
||||
}
|
||||
|
||||
QString Models::Account::getBareJid() const
|
||||
{
|
||||
return login + "@" + server;
|
||||
}
|
||||
|
||||
QString Models::Account::getFullJid() const
|
||||
{
|
||||
return getBareJid() + "/" + resource;
|
||||
}
|
||||
|
@ -50,6 +50,9 @@ namespace Models {
|
||||
void setError(const QString& p_resource);
|
||||
QString getError() const;
|
||||
|
||||
void setAvatarPath(const QString& path);
|
||||
QString getAvatarPath();
|
||||
|
||||
void setAvailability(Shared::Availability p_avail);
|
||||
void setAvailability(unsigned int p_avail);
|
||||
Shared::Availability getAvailability() const;
|
||||
@ -61,12 +64,16 @@ namespace Models {
|
||||
|
||||
void update(const QString& field, const QVariant& value);
|
||||
|
||||
QString getBareJid() const;
|
||||
QString getFullJid() const;
|
||||
|
||||
private:
|
||||
QString login;
|
||||
QString password;
|
||||
QString server;
|
||||
QString resource;
|
||||
QString error;
|
||||
QString avatarPath;
|
||||
Shared::ConnectionState state;
|
||||
Shared::Availability availability;
|
||||
|
||||
|
@ -20,13 +20,9 @@
|
||||
#include "../../global.h"
|
||||
|
||||
#include <QIcon>
|
||||
#include <QDebug>
|
||||
|
||||
std::deque<QString> Models::Accounts::columns = {
|
||||
"name",
|
||||
"server",
|
||||
"state",
|
||||
"error"
|
||||
};
|
||||
std::deque<QString> Models::Accounts::columns = {"Name", "Server", "State", "Error"};
|
||||
|
||||
Models::Accounts::Accounts(QObject* parent):
|
||||
QAbstractTableModel(parent),
|
||||
@ -72,7 +68,7 @@ int Models::Accounts::rowCount ( const QModelIndex& parent ) const
|
||||
QVariant Models::Accounts::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
|
||||
return columns[section];
|
||||
return tr(columns[section].toLatin1());
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
@ -81,8 +77,19 @@ QVariant Models::Accounts::headerData(int section, Qt::Orientation orientation,
|
||||
void Models::Accounts::addAccount(Account* account)
|
||||
{
|
||||
beginInsertRows(QModelIndex(), accs.size(), accs.size());
|
||||
accs.push_back(account);
|
||||
connect(account, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onAccountChanged(Models::Item*, int, int)));
|
||||
int index = 0;
|
||||
std::deque<Account*>::const_iterator before = accs.begin();
|
||||
while (before != accs.end()) {
|
||||
Account* bfr = *before;
|
||||
if (bfr->getDisplayedName() > account->getDisplayedName()) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
before++;
|
||||
}
|
||||
|
||||
accs.insert(before, account);
|
||||
connect(account, &Account::childChanged, this, &Accounts::onAccountChanged);
|
||||
endInsertRows();
|
||||
|
||||
emit sizeChanged(accs.size());
|
||||
@ -96,8 +103,32 @@ void Models::Accounts::onAccountChanged(Item* item, int row, int col)
|
||||
return; //it means the signal is emitted by one of accounts' children, not exactly him, this model has no interest in that
|
||||
}
|
||||
|
||||
if (col == 0) {
|
||||
int newRow = 0;
|
||||
std::deque<Account*>::const_iterator before = accs.begin();
|
||||
while (before != accs.end()) {
|
||||
Item* bfr = *before;
|
||||
if (bfr->getDisplayedName() > item->getDisplayedName()) {
|
||||
break;
|
||||
}
|
||||
newRow++;
|
||||
before++;
|
||||
}
|
||||
|
||||
if (newRow != row || (before != accs.end() && *before != item)) {
|
||||
emit beginMoveRows(createIndex(row, 0), row, row, createIndex(newRow, 0), newRow);
|
||||
std::deque<Account*>::const_iterator old = accs.begin();
|
||||
old += row;
|
||||
accs.erase(old);
|
||||
accs.insert(before, acc);
|
||||
emit endMoveRows();
|
||||
|
||||
row = newRow;
|
||||
}
|
||||
}
|
||||
|
||||
if (col < columnCount(QModelIndex())) {
|
||||
emit dataChanged(createIndex(row, col, this), createIndex(row, col, this));
|
||||
emit dataChanged(createIndex(row, col), createIndex(row, col));
|
||||
}
|
||||
emit changed();
|
||||
}
|
||||
@ -112,7 +143,7 @@ void Models::Accounts::removeAccount(int index)
|
||||
{
|
||||
Account* account = accs[index];
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
disconnect(account, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onAccountChanged(Models::Item*, int, int)));
|
||||
disconnect(account, &Account::childChanged, this, &Accounts::onAccountChanged);
|
||||
accs.erase(accs.begin() + index);
|
||||
endRemoveRows();
|
||||
|
||||
|
@ -25,14 +25,26 @@ Models::Contact::Contact(const QString& p_jid ,const QMap<QString, QVariant> &da
|
||||
jid(p_jid),
|
||||
availability(Shared::offline),
|
||||
state(Shared::none),
|
||||
avatarState(Shared::Avatar::empty),
|
||||
presences(),
|
||||
messages(),
|
||||
childMessages(0)
|
||||
childMessages(0),
|
||||
status(),
|
||||
avatarPath()
|
||||
{
|
||||
QMap<QString, QVariant>::const_iterator itr = data.find("state");
|
||||
if (itr != data.end()) {
|
||||
setState(itr.value().toUInt());
|
||||
}
|
||||
|
||||
itr = data.find("avatarState");
|
||||
if (itr != data.end()) {
|
||||
setAvatarState(itr.value().toUInt());
|
||||
}
|
||||
itr = data.find("avatarPath");
|
||||
if (itr != data.end()) {
|
||||
setAvatarPath(itr.value().toString());
|
||||
}
|
||||
}
|
||||
|
||||
Models::Contact::~Contact()
|
||||
@ -100,7 +112,7 @@ void Models::Contact::setStatus(const QString& p_state)
|
||||
|
||||
int Models::Contact::columnCount() const
|
||||
{
|
||||
return 6;
|
||||
return 8;
|
||||
}
|
||||
|
||||
QVariant Models::Contact::data(int column) const
|
||||
@ -118,6 +130,10 @@ QVariant Models::Contact::data(int column) const
|
||||
return getMessagesCount();
|
||||
case 5:
|
||||
return getStatus();
|
||||
case 6:
|
||||
return static_cast<quint8>(getAvatarState());
|
||||
case 7:
|
||||
return getAvatarPath();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
@ -142,6 +158,10 @@ void Models::Contact::update(const QString& field, const QVariant& value)
|
||||
setAvailability(value.toUInt());
|
||||
} else if (field == "state") {
|
||||
setState(value.toUInt());
|
||||
} else if (field == "avatarState") {
|
||||
setAvatarState(value.toUInt());
|
||||
} else if (field == "avatarPath") {
|
||||
setAvatarPath(value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,7 +229,7 @@ void Models::Contact::refresh()
|
||||
void Models::Contact::_removeChild(int index)
|
||||
{
|
||||
Item* child = childItems[index];
|
||||
disconnect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh()));
|
||||
disconnect(child, &Item::childChanged, this, &Contact::refresh);
|
||||
Item::_removeChild(index);
|
||||
refresh();
|
||||
}
|
||||
@ -217,7 +237,7 @@ void Models::Contact::_removeChild(int index)
|
||||
void Models::Contact::appendChild(Models::Item* child)
|
||||
{
|
||||
Item::appendChild(child);
|
||||
connect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh()));
|
||||
connect(child, &Item::childChanged, this, &Contact::refresh);
|
||||
refresh();
|
||||
}
|
||||
|
||||
@ -304,7 +324,7 @@ void Models::Contact::toOfflineState()
|
||||
emit childIsAboutToBeRemoved(this, 0, childItems.size());
|
||||
for (int i = 0; i < childItems.size(); ++i) {
|
||||
Item* item = childItems[i];
|
||||
disconnect(item, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh()));
|
||||
disconnect(item, &Item::childChanged, this, &Contact::refresh);
|
||||
Item::_removeChild(i);
|
||||
item->deleteLater();
|
||||
}
|
||||
@ -313,3 +333,74 @@ void Models::Contact::toOfflineState()
|
||||
emit childRemoved();
|
||||
refresh();
|
||||
}
|
||||
|
||||
QString Models::Contact::getDisplayedName() const
|
||||
{
|
||||
return getContactName();
|
||||
}
|
||||
|
||||
bool Models::Contact::columnInvolvedInDisplay(int col)
|
||||
{
|
||||
return Item::columnInvolvedInDisplay(col) && col == 1;
|
||||
}
|
||||
|
||||
Models::Contact * Models::Contact::copy() const
|
||||
{
|
||||
Contact* cnt = new Contact(*this);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
Models::Contact::Contact(const Models::Contact& other):
|
||||
Item(other),
|
||||
jid(other.jid),
|
||||
availability(other.availability),
|
||||
state(other.state),
|
||||
presences(),
|
||||
messages(other.messages),
|
||||
childMessages(0)
|
||||
{
|
||||
for (const Presence* pres : other.presences) {
|
||||
Presence* pCopy = new Presence(*pres);
|
||||
presences.insert(pCopy->getName(), pCopy);
|
||||
Item::appendChild(pCopy);
|
||||
connect(pCopy, &Item::childChanged, this, &Contact::refresh);
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
QString Models::Contact::getAvatarPath() const
|
||||
{
|
||||
return avatarPath;
|
||||
}
|
||||
|
||||
Shared::Avatar Models::Contact::getAvatarState() const
|
||||
{
|
||||
return avatarState;
|
||||
}
|
||||
|
||||
void Models::Contact::setAvatarPath(const QString& path)
|
||||
{
|
||||
if (path != avatarPath) {
|
||||
avatarPath = path;
|
||||
changed(7);
|
||||
}
|
||||
}
|
||||
|
||||
void Models::Contact::setAvatarState(Shared::Avatar p_state)
|
||||
{
|
||||
if (avatarState != p_state) {
|
||||
avatarState = p_state;
|
||||
changed(6);
|
||||
}
|
||||
}
|
||||
|
||||
void Models::Contact::setAvatarState(unsigned int p_state)
|
||||
{
|
||||
if (p_state <= static_cast<quint8>(Shared::Avatar::valid)) {
|
||||
Shared::Avatar state = static_cast<Shared::Avatar>(p_state);
|
||||
setAvatarState(state);
|
||||
} else {
|
||||
qDebug() << "An attempt to set invalid avatar state" << p_state << "to the contact" << jid << ", skipping";
|
||||
}
|
||||
}
|
||||
|
@ -34,11 +34,14 @@ class Contact : public Item
|
||||
public:
|
||||
typedef std::deque<Shared::Message> Messages;
|
||||
Contact(const QString& p_jid, const QMap<QString, QVariant> &data, Item *parentItem = 0);
|
||||
Contact(const Contact& other);
|
||||
~Contact();
|
||||
|
||||
QString getJid() const;
|
||||
Shared::Availability getAvailability() const;
|
||||
Shared::SubscriptionState getState() const;
|
||||
Shared::Avatar getAvatarState() const;
|
||||
QString getAvatarPath() const;
|
||||
QIcon getStatusIcon(bool big = false) const;
|
||||
|
||||
int columnCount() const override;
|
||||
@ -57,9 +60,13 @@ public:
|
||||
unsigned int getMessagesCount() const;
|
||||
void dropMessages();
|
||||
void getMessages(Messages& container) const;
|
||||
QString getDisplayedName() const override;
|
||||
|
||||
Contact* copy() const;
|
||||
|
||||
protected:
|
||||
void _removeChild(int index) override;
|
||||
bool columnInvolvedInDisplay(int col) override;
|
||||
|
||||
protected slots:
|
||||
void refresh();
|
||||
@ -70,6 +77,9 @@ protected:
|
||||
void setAvailability(unsigned int p_state);
|
||||
void setState(Shared::SubscriptionState p_state);
|
||||
void setState(unsigned int p_state);
|
||||
void setAvatarState(Shared::Avatar p_state);
|
||||
void setAvatarState(unsigned int p_state);
|
||||
void setAvatarPath(const QString& path);
|
||||
void setJid(const QString p_jid);
|
||||
void setStatus(const QString& p_state);
|
||||
|
||||
@ -77,10 +87,12 @@ private:
|
||||
QString jid;
|
||||
Shared::Availability availability;
|
||||
Shared::SubscriptionState state;
|
||||
Shared::Avatar avatarState;
|
||||
QMap<QString, Presence*> presences;
|
||||
Messages messages;
|
||||
unsigned int childMessages;
|
||||
QString status;
|
||||
QString avatarPath;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ Models::Group::~Group()
|
||||
void Models::Group::appendChild(Models::Item* child)
|
||||
{
|
||||
Item::appendChild(child);
|
||||
connect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh()));
|
||||
connect(child, &Item::childChanged, this, &Group::refresh);
|
||||
changed(1);
|
||||
refresh();
|
||||
}
|
||||
@ -59,7 +59,7 @@ QVariant Models::Group::data(int column) const
|
||||
void Models::Group::_removeChild(int index)
|
||||
{
|
||||
Item* child = childItems[index];
|
||||
disconnect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh()));
|
||||
disconnect(child, &Item::childChanged, this, &Group::refresh);
|
||||
Item::_removeChild(index);
|
||||
changed(1);
|
||||
refresh();
|
||||
@ -103,3 +103,16 @@ unsigned int Models::Group::getOnlineContacts() const
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
bool Models::Group::hasContact(const QString& jid) const
|
||||
{
|
||||
for (Models::Item* item : childItems) {
|
||||
if (item->type == Item::contact) {
|
||||
const Contact* cnt = static_cast<const Contact*>(item);
|
||||
if (cnt->getJid() == jid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ public:
|
||||
unsigned int getUnreadMessages() const;
|
||||
unsigned int getOnlineContacts() const;
|
||||
|
||||
bool hasContact(const QString& jid) const;
|
||||
|
||||
protected:
|
||||
void _removeChild(int index) override;
|
||||
void setUnreadMessages(unsigned int amount);
|
||||
|
@ -34,6 +34,15 @@ Models::Item::Item(Type p_type, const QMap<QString, QVariant> &p_data, Item *p_p
|
||||
}
|
||||
}
|
||||
|
||||
Models::Item::Item(const Models::Item& other):
|
||||
QObject(),
|
||||
type(other.type),
|
||||
name(other.name),
|
||||
childItems(),
|
||||
parent(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Models::Item::~Item()
|
||||
{
|
||||
std::deque<Item*>::const_iterator itr = childItems.begin();
|
||||
@ -55,25 +64,37 @@ void Models::Item::setName(const QString& p_name)
|
||||
void Models::Item::appendChild(Models::Item* child)
|
||||
{
|
||||
bool moving = false;
|
||||
int oldRow = child->row();
|
||||
int newRow = this->childCount();
|
||||
int newRow = 0;
|
||||
std::deque<Item*>::const_iterator before = childItems.begin();
|
||||
while (before != childItems.end()) {
|
||||
Item* bfr = *before;
|
||||
if (bfr->type > child->type) {
|
||||
break;
|
||||
} else if (bfr->type == child->type && bfr->getDisplayedName() > child->getDisplayedName()) {
|
||||
break;
|
||||
}
|
||||
newRow++;
|
||||
before++;
|
||||
}
|
||||
|
||||
if (child->parent != 0) {
|
||||
int oldRow = child->row();
|
||||
moving = true;
|
||||
emit childIsAboutToBeMoved(child->parent, oldRow, oldRow, this, newRow);
|
||||
child->parent->_removeChild(oldRow);
|
||||
} else {
|
||||
emit childIsAboutToBeInserted(this, newRow, newRow);
|
||||
}
|
||||
childItems.push_back(child);
|
||||
childItems.insert(before, child);
|
||||
child->parent = this;
|
||||
|
||||
QObject::connect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SIGNAL(childChanged(Models::Item*, int, int)));
|
||||
QObject::connect(child, SIGNAL(childIsAboutToBeInserted(Item*, int, int)), this, SIGNAL(childIsAboutToBeInserted(Item*, int, int)));
|
||||
QObject::connect(child, SIGNAL(childInserted()), this, SIGNAL(childInserted()));
|
||||
QObject::connect(child, SIGNAL(childIsAboutToBeRemoved(Item*, int, int)), this, SIGNAL(childIsAboutToBeRemoved(Item*, int, int)));
|
||||
QObject::connect(child, SIGNAL(childRemoved()), this, SIGNAL(childRemoved()));
|
||||
QObject::connect(child, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int)), this, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int)));
|
||||
QObject::connect(child, SIGNAL(childMoved()), this, SIGNAL(childMoved()));
|
||||
QObject::connect(child, &Item::childChanged, this, &Item::childChanged);
|
||||
QObject::connect(child, &Item::childIsAboutToBeInserted, this, &Item::childIsAboutToBeInserted);
|
||||
QObject::connect(child, &Item::childInserted, this, &Item::childInserted);
|
||||
QObject::connect(child, &Item::childIsAboutToBeRemoved, this, &Item::childIsAboutToBeRemoved);
|
||||
QObject::connect(child, &Item::childRemoved, this, &Item::childRemoved);
|
||||
QObject::connect(child, &Item::childIsAboutToBeMoved, this, &Item::childIsAboutToBeMoved);
|
||||
QObject::connect(child, &Item::childMoved, this, &Item::childMoved);
|
||||
|
||||
if (moving) {
|
||||
emit childMoved();
|
||||
@ -120,7 +141,7 @@ const Models::Item * Models::Item::parentItemConst() const
|
||||
|
||||
int Models::Item::columnCount() const
|
||||
{
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
QString Models::Item::getName() const
|
||||
@ -147,13 +168,13 @@ void Models::Item::_removeChild(int index)
|
||||
{
|
||||
Item* child = childItems[index];
|
||||
|
||||
QObject::disconnect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SIGNAL(childChanged(Models::Item*, int, int)));
|
||||
QObject::disconnect(child, SIGNAL(childIsAboutToBeInserted(Item*, int, int)), this, SIGNAL(childIsAboutToBeInserted(Item*, int, int)));
|
||||
QObject::disconnect(child, SIGNAL(childInserted()), this, SIGNAL(childInserted()));
|
||||
QObject::disconnect(child, SIGNAL(childIsAboutToBeRemoved(Item*, int, int)), this, SIGNAL(childIsAboutToBeRemoved(Item*, int, int)));
|
||||
QObject::disconnect(child, SIGNAL(childRemoved()), this, SIGNAL(childRemoved()));
|
||||
QObject::disconnect(child, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int)), this, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int)));
|
||||
QObject::disconnect(child, SIGNAL(childMoved()), this, SIGNAL(childMoved()));
|
||||
QObject::disconnect(child, &Item::childChanged, this, &Item::childChanged);
|
||||
QObject::disconnect(child, &Item::childIsAboutToBeInserted, this, &Item::childIsAboutToBeInserted);
|
||||
QObject::disconnect(child, &Item::childInserted, this, &Item::childInserted);
|
||||
QObject::disconnect(child, &Item::childIsAboutToBeRemoved, this, &Item::childIsAboutToBeRemoved);
|
||||
QObject::disconnect(child, &Item::childRemoved, this, &Item::childRemoved);
|
||||
QObject::disconnect(child, &Item::childIsAboutToBeMoved, this, &Item::childIsAboutToBeMoved);
|
||||
QObject::disconnect(child, &Item::childMoved, this, &Item::childMoved);
|
||||
|
||||
childItems.erase(childItems.begin() + index);
|
||||
child->parent = 0;
|
||||
@ -212,3 +233,62 @@ QString Models::Item::getAccountName() const
|
||||
}
|
||||
return acc->getName();
|
||||
}
|
||||
|
||||
Shared::Availability Models::Item::getAccountAvailability() const
|
||||
{
|
||||
const Account* acc = static_cast<const Account*>(getParentAccount());
|
||||
if (acc == 0) {
|
||||
return Shared::offline;
|
||||
}
|
||||
return acc->getAvailability();
|
||||
}
|
||||
|
||||
Shared::ConnectionState Models::Item::getAccountConnectionState() const
|
||||
{
|
||||
const Account* acc = static_cast<const Account*>(getParentAccount());
|
||||
if (acc == 0) {
|
||||
return Shared::disconnected;
|
||||
}
|
||||
return acc->getState();
|
||||
}
|
||||
|
||||
QString Models::Item::getDisplayedName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
void Models::Item::onChildChanged(Models::Item* item, int row, int col)
|
||||
{
|
||||
Item* parent = item->parentItem();
|
||||
if (parent != 0 && parent == this) {
|
||||
if (item->columnInvolvedInDisplay(col)) {
|
||||
int newRow = 0;
|
||||
std::deque<Item*>::const_iterator before = childItems.begin();
|
||||
while (before != childItems.end()) {
|
||||
Item* bfr = *before;
|
||||
if (bfr->type > item->type) {
|
||||
break;
|
||||
} else if (bfr->type == item->type && bfr->getDisplayedName() > item->getDisplayedName()) {
|
||||
break;
|
||||
}
|
||||
newRow++;
|
||||
before++;
|
||||
}
|
||||
|
||||
if (newRow != row || (before != childItems.end() && *before != item)) {
|
||||
emit childIsAboutToBeMoved(this, row, row, this, newRow);
|
||||
std::deque<Item*>::const_iterator old = childItems.begin();
|
||||
old += row;
|
||||
childItems.erase(old);
|
||||
childItems.insert(before, item);
|
||||
emit childMoved();
|
||||
}
|
||||
}
|
||||
}
|
||||
emit childChanged(item, row, col);
|
||||
}
|
||||
|
||||
bool Models::Item::columnInvolvedInDisplay(int col)
|
||||
{
|
||||
return col == 0;
|
||||
}
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "../../global.h"
|
||||
|
||||
namespace Models {
|
||||
|
||||
class Item : public QObject{
|
||||
@ -41,6 +43,7 @@ class Item : public QObject{
|
||||
};
|
||||
|
||||
explicit Item(Type p_type, const QMap<QString, QVariant> &data, Item *parentItem = 0);
|
||||
Item(const Item& other);
|
||||
~Item();
|
||||
|
||||
signals:
|
||||
@ -55,6 +58,7 @@ class Item : public QObject{
|
||||
public:
|
||||
virtual void appendChild(Item *child);
|
||||
virtual void removeChild(int index);
|
||||
virtual QString getDisplayedName() const;
|
||||
QString getName() const;
|
||||
void setName(const QString& name);
|
||||
|
||||
@ -70,14 +74,20 @@ class Item : public QObject{
|
||||
QString getAccountName() const;
|
||||
QString getAccountJid() const;
|
||||
QString getAccountResource() const;
|
||||
Shared::ConnectionState getAccountConnectionState() const;
|
||||
Shared::Availability getAccountAvailability() const;
|
||||
|
||||
const Type type;
|
||||
|
||||
protected:
|
||||
virtual void changed(int col);
|
||||
virtual void _removeChild(int index);
|
||||
virtual bool columnInvolvedInDisplay(int col);
|
||||
const Item* getParentAccount() const;
|
||||
|
||||
protected slots:
|
||||
void onChildChanged(Models::Item* item, int row, int col);
|
||||
|
||||
protected:
|
||||
QString name;
|
||||
std::deque<Item*> childItems;
|
||||
|
@ -24,6 +24,13 @@ Models::Presence::Presence(const QMap<QString, QVariant>& data, Item* parentItem
|
||||
{
|
||||
}
|
||||
|
||||
Models::Presence::Presence(const Models::Presence& other):
|
||||
AbstractParticipant(other),
|
||||
messages(other.messages)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Models::Presence::~Presence()
|
||||
{
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ class Presence : public Models::AbstractParticipant
|
||||
public:
|
||||
typedef std::deque<Shared::Message> Messages;
|
||||
explicit Presence(const QMap<QString, QVariant> &data, Item *parentItem = 0);
|
||||
Presence(const Presence& other);
|
||||
~Presence();
|
||||
|
||||
int columnCount() const override;
|
||||
|
@ -193,15 +193,15 @@ QString Models::Room::getStatusText() const
|
||||
{
|
||||
if (autoJoin) {
|
||||
if (joined) {
|
||||
return "Subscribed";
|
||||
return tr("Subscribed");
|
||||
} else {
|
||||
return "Temporarily unsubscribed";
|
||||
return tr("Temporarily unsubscribed");
|
||||
}
|
||||
} else {
|
||||
if (joined) {
|
||||
return "Temporarily subscribed";
|
||||
return tr("Temporarily subscribed");
|
||||
} else {
|
||||
return "Unsubscribed";
|
||||
return tr("Unsubscribed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -238,7 +238,6 @@ void Models::Room::toOfflineState()
|
||||
emit childIsAboutToBeRemoved(this, 0, childItems.size());
|
||||
for (int i = 0; i < childItems.size(); ++i) {
|
||||
Item* item = childItems[i];
|
||||
disconnect(item, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh()));
|
||||
Item::_removeChild(i);
|
||||
item->deleteLater();
|
||||
}
|
||||
@ -309,3 +308,13 @@ void Models::Room::setSubject(const QString& sub)
|
||||
changed(6);
|
||||
}
|
||||
}
|
||||
|
||||
QString Models::Room::getDisplayedName() const
|
||||
{
|
||||
return getRoomName();
|
||||
}
|
||||
|
||||
bool Models::Room::columnInvolvedInDisplay(int col)
|
||||
{
|
||||
return Item::columnInvolvedInDisplay(col) && col == 1;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
#include "item.h"
|
||||
#include "participant.h"
|
||||
#include "../global.h"
|
||||
#include "../../global.h"
|
||||
|
||||
namespace Models {
|
||||
|
||||
@ -68,10 +68,14 @@ public:
|
||||
void removeParticipant(const QString& name);
|
||||
|
||||
void toOfflineState() override;
|
||||
QString getDisplayedName() const override;
|
||||
|
||||
private:
|
||||
void handleParticipantUpdate(std::map<QString, Participant*>::const_iterator itr, const QMap<QString, QVariant>& data);
|
||||
|
||||
protected:
|
||||
bool columnInvolvedInDisplay(int col) override;
|
||||
|
||||
private:
|
||||
bool autoJoin;
|
||||
bool joined;
|
||||
|
@ -21,8 +21,6 @@
|
||||
#include <QIcon>
|
||||
#include <QFont>
|
||||
|
||||
using namespace Models;
|
||||
|
||||
Models::Roster::Roster(QObject* parent):
|
||||
QAbstractItemModel(parent),
|
||||
accountsModel(new Accounts()),
|
||||
@ -31,17 +29,14 @@ Models::Roster::Roster(QObject* parent):
|
||||
groups(),
|
||||
contacts()
|
||||
{
|
||||
connect(accountsModel,
|
||||
SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)),
|
||||
this,
|
||||
SLOT(onAccountDataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
|
||||
connect(root, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onChildChanged(Models::Item*, int, int)));
|
||||
connect(root, SIGNAL(childIsAboutToBeInserted(Item*, int, int)), this, SLOT(onChildIsAboutToBeInserted(Item*, int, int)));
|
||||
connect(root, SIGNAL(childInserted()), this, SLOT(onChildInserted()));
|
||||
connect(root, SIGNAL(childIsAboutToBeRemoved(Item*, int, int)), this, SLOT(onChildIsAboutToBeRemoved(Item*, int, int)));
|
||||
connect(root, SIGNAL(childRemoved()), this, SLOT(onChildRemoved()));
|
||||
connect(root, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int)), this, SLOT(onChildIsAboutToBeMoved(Item*, int, int, Item*, int)));
|
||||
connect(root, SIGNAL(childMoved()), this, SLOT(onChildMoved()));
|
||||
connect(accountsModel, &Accounts::dataChanged, this, &Roster::onAccountDataChanged);
|
||||
connect(root, &Item::childChanged, this, &Roster::onChildChanged);
|
||||
connect(root, &Item::childIsAboutToBeInserted, this, &Roster::onChildIsAboutToBeInserted);
|
||||
connect(root, &Item::childInserted, this, &Roster::onChildInserted);
|
||||
connect(root, &Item::childIsAboutToBeRemoved, this, &Roster::onChildIsAboutToBeRemoved);
|
||||
connect(root, &Item::childRemoved, this, &Roster::onChildRemoved);
|
||||
connect(root, &Item::childIsAboutToBeMoved, this, &Roster::onChildIsAboutToBeMoved);
|
||||
connect(root, &Item::childMoved, this, &Roster::onChildMoved);
|
||||
}
|
||||
|
||||
Models::Roster::~Roster()
|
||||
@ -70,6 +65,10 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
if (index.column() != 0) {
|
||||
result = "";
|
||||
break;
|
||||
}
|
||||
switch (item->type) {
|
||||
case Item::group: {
|
||||
Group* gr = static_cast<Group*>(item);
|
||||
@ -78,7 +77,7 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
||||
str += gr->getName();
|
||||
unsigned int amount = gr->getUnreadMessages();
|
||||
if (amount > 0) {
|
||||
str += QString(" (") + "New messages" + ")";
|
||||
str += QString(" (") + tr("New messages") + ")";
|
||||
}
|
||||
|
||||
result = str;
|
||||
@ -93,26 +92,51 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
||||
case Qt::DecorationRole:
|
||||
switch (item->type) {
|
||||
case Item::account: {
|
||||
quint8 col = index.column();
|
||||
Account* acc = static_cast<Account*>(item);
|
||||
if (col == 0) {
|
||||
result = acc->getStatusIcon(false);
|
||||
} else if (col == 1) {
|
||||
QString path = acc->getAvatarPath();
|
||||
|
||||
if (path.size() > 0) {
|
||||
result = QIcon(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Item::contact: {
|
||||
Contact* contact = static_cast<Contact*>(item);
|
||||
quint8 col = index.column();
|
||||
if (col == 0) {
|
||||
result = contact->getStatusIcon(false);
|
||||
} else if (col == 1) {
|
||||
if (contact->getAvatarState() != Shared::Avatar::empty) {
|
||||
result = QIcon(contact->getAvatarPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Item::presence: {
|
||||
if (index.column() != 0) {
|
||||
break;
|
||||
}
|
||||
Presence* presence = static_cast<Presence*>(item);
|
||||
result = presence->getStatusIcon(false);
|
||||
}
|
||||
break;
|
||||
case Item::room: {
|
||||
if (index.column() != 0) {
|
||||
break;
|
||||
}
|
||||
Room* room = static_cast<Room*>(item);
|
||||
result = room->getStatusIcon(false);
|
||||
}
|
||||
break;
|
||||
case Item::participant: {
|
||||
if (index.column() != 0) {
|
||||
break;
|
||||
}
|
||||
Participant* p = static_cast<Participant*>(item);
|
||||
result = p->getStatusIcon(false);
|
||||
}
|
||||
@ -143,7 +167,7 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
||||
switch (item->type) {
|
||||
case Item::account: {
|
||||
Account* acc = static_cast<Account*>(item);
|
||||
result = QString(Shared::availabilityNames[acc->getAvailability()]);
|
||||
result = QCoreApplication::translate("Global", Shared::availabilityNames[acc->getAvailability()].toLatin1());
|
||||
}
|
||||
break;
|
||||
case Item::contact: {
|
||||
@ -151,22 +175,22 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
||||
QString str("");
|
||||
int mc = contact->getMessagesCount();
|
||||
if (mc > 0) {
|
||||
str += QString("New messages: ") + std::to_string(mc).c_str() + "\n";
|
||||
str += QString(tr("New messages: ")) + std::to_string(mc).c_str() + "\n";
|
||||
}
|
||||
str += "Jabber ID: " + contact->getJid() + "\n";
|
||||
str += tr("Jabber ID: ") + contact->getJid() + "\n";
|
||||
Shared::SubscriptionState ss = contact->getState();
|
||||
if (ss == Shared::both) {
|
||||
Shared::Availability av = contact->getAvailability();
|
||||
str += "Availability: " + Shared::availabilityNames[av];
|
||||
str += tr("Availability: ") + QCoreApplication::translate("Global", Shared::availabilityNames[av].toLatin1());
|
||||
if (av != Shared::offline) {
|
||||
QString s = contact->getStatus();
|
||||
if (s.size() > 0) {
|
||||
str += "\nStatus: " + s;
|
||||
str += "\n" + tr("Status: ") + s;
|
||||
}
|
||||
}
|
||||
str += "\nSubscription: " + Shared::subscriptionStateNames[ss];
|
||||
str += "\n" + tr("Subscription: ") + QCoreApplication::translate("Global", Shared::subscriptionStateNames[ss].toLatin1());
|
||||
} else {
|
||||
str += "Subscription: " + Shared::subscriptionStateNames[ss];
|
||||
str += tr("Subscription: ") + QCoreApplication::translate("Global", Shared::subscriptionStateNames[ss].toLatin1());
|
||||
}
|
||||
|
||||
result = str;
|
||||
@ -177,13 +201,13 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
||||
QString str("");
|
||||
int mc = contact->getMessagesCount();
|
||||
if (mc > 0) {
|
||||
str += QString("New messages: ") + std::to_string(mc).c_str() + "\n";
|
||||
str += tr("New messages: ") + std::to_string(mc).c_str() + "\n";
|
||||
}
|
||||
Shared::Availability av = contact->getAvailability();
|
||||
str += "Availability: " + Shared::availabilityNames[av];
|
||||
str += tr("Availability: ") + QCoreApplication::translate("Global", Shared::availabilityNames[av].toLatin1());
|
||||
QString s = contact->getStatus();
|
||||
if (s.size() > 0) {
|
||||
str += "\nStatus: " + s;
|
||||
str += "\n" + tr("Status: ") + s;
|
||||
}
|
||||
|
||||
result = str;
|
||||
@ -193,14 +217,18 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
||||
Participant* p = static_cast<Participant*>(item);
|
||||
QString str("");
|
||||
Shared::Availability av = p->getAvailability();
|
||||
str += "Availability: " + Shared::availabilityNames[av] + "\n";
|
||||
str += tr("Availability: ") + QCoreApplication::translate("Global", Shared::availabilityNames[av].toLatin1()) + "\n";
|
||||
QString s = p->getStatus();
|
||||
if (s.size() > 0) {
|
||||
str += "Status: " + s + "\n";
|
||||
str += tr("Status: ") + s + "\n";
|
||||
}
|
||||
|
||||
str += "Affiliation: " + Shared::affiliationNames[static_cast<unsigned int>(p->getAffiliation())] + "\n";
|
||||
str += "Role: " + Shared::roleNames[static_cast<unsigned int>(p->getRole())];
|
||||
str += tr("Affiliation: ") +
|
||||
QCoreApplication::translate("Global",
|
||||
Shared::affiliationNames[static_cast<unsigned int>(p->getAffiliation())].toLatin1()) + "\n";
|
||||
str += tr("Role: ") +
|
||||
QCoreApplication::translate("Global",
|
||||
Shared::roleNames[static_cast<unsigned int>(p->getRole())].toLatin1());
|
||||
|
||||
result = str;
|
||||
}
|
||||
@ -210,10 +238,10 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
||||
unsigned int count = gr->getUnreadMessages();
|
||||
QString str("");
|
||||
if (count > 0) {
|
||||
str += QString("New messages: ") + std::to_string(count).c_str() + "\n";
|
||||
str += tr("New messages: ") + std::to_string(count).c_str() + "\n";
|
||||
}
|
||||
str += QString("Online contacts: ") + std::to_string(gr->getOnlineContacts()).c_str() + "\n";
|
||||
str += QString("Total contacts: ") + std::to_string(gr->childCount()).c_str();
|
||||
str += tr("Online contacts: ") + std::to_string(gr->getOnlineContacts()).c_str() + "\n";
|
||||
str += tr("Total contacts: ") + std::to_string(gr->childCount()).c_str();
|
||||
result = str;
|
||||
}
|
||||
break;
|
||||
@ -222,11 +250,11 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
||||
unsigned int count = rm->getUnreadMessagesCount();
|
||||
QString str("");
|
||||
if (count > 0) {
|
||||
str += QString("New messages: ") + std::to_string(count).c_str() + "\n";
|
||||
str += tr("New messages: ") + std::to_string(count).c_str() + "\n";
|
||||
}
|
||||
str += QString("Subscription: ") + rm->getStatusText();
|
||||
str += tr("Subscription: ") + rm->getStatusText();
|
||||
if (rm->getJoined()) {
|
||||
str += QString("\nMembers: ") + std::to_string(rm->childCount()).c_str();
|
||||
str += QString("\n") + tr("Members: ") + std::to_string(rm->childCount()).c_str();
|
||||
}
|
||||
result = str;
|
||||
}
|
||||
@ -383,7 +411,7 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons
|
||||
{
|
||||
Item* parent;
|
||||
Account* acc;
|
||||
Contact* contact;
|
||||
Contact* sample = 0;
|
||||
ElId id(account, jid);
|
||||
|
||||
{
|
||||
@ -395,27 +423,29 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons
|
||||
acc = itr->second;
|
||||
}
|
||||
|
||||
if (group == "") {
|
||||
std::multimap<ElId, Contact*>::iterator itr = contacts.lower_bound(id);
|
||||
std::multimap<ElId, Contact*>::iterator eItr = contacts.upper_bound(id);
|
||||
while (itr != eItr) {
|
||||
if (itr->second->parentItem() == acc) {
|
||||
qDebug() << "An attempt to add a contact " << jid << " ungrouped to non the account " << account << " for the second time, skipping";
|
||||
for (std::multimap<ElId, Contact*>::iterator itr = contacts.lower_bound(id), eItr = contacts.upper_bound(id); itr != eItr; ++itr) {
|
||||
sample = itr->second; //need to find if this contact is already added somewhere
|
||||
break; //so one iteration is enough
|
||||
}
|
||||
|
||||
if (group == "") { //this means this contact is already added somewhere and there is no sense to add it ungrouped
|
||||
if (sample != 0) {
|
||||
qDebug() << "An attempt to add a contact" << jid << "to the ungrouped contact set of account" << account << "for the second time, skipping";
|
||||
return;
|
||||
}
|
||||
itr++;
|
||||
}
|
||||
} else {
|
||||
parent = acc;
|
||||
}
|
||||
} else {
|
||||
std::map<ElId, Group*>::iterator itr = groups.find({account, group});
|
||||
if (itr == groups.end()) {
|
||||
qDebug() << "An attempt to add a contact " << jid << " to non existing group " << group << ", skipping";
|
||||
return;
|
||||
qDebug() << "An attempt to add a contact" << jid << "to non existing group" << group << ", adding group";
|
||||
addGroup(account, group);
|
||||
itr = groups.find({account, group});
|
||||
}
|
||||
|
||||
parent = itr->second;
|
||||
|
||||
for (int i = 0; i < parent->childCount(); ++i) {
|
||||
for (int i = 0; i < parent->childCount(); ++i) { //checking if the contact is already added to that group
|
||||
Item* item = parent->child(i);
|
||||
if (item->type == Item::contact) {
|
||||
Contact* ca = static_cast<Contact*>(item);
|
||||
@ -426,7 +456,7 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < acc->childCount(); ++i) {
|
||||
for (int i = 0; i < acc->childCount(); ++i) { //checking if that contact is among ugrouped
|
||||
Item* item = acc->child(i);
|
||||
if (item->type == Item::contact) {
|
||||
Contact* ca = static_cast<Contact*>(item);
|
||||
@ -440,7 +470,12 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons
|
||||
}
|
||||
|
||||
}
|
||||
Contact* contact;
|
||||
if (sample == 0) {
|
||||
contact = new Contact(jid, data);
|
||||
} else {
|
||||
contact = sample->copy();
|
||||
}
|
||||
contacts.insert(std::make_pair(id, contact));
|
||||
parent->appendChild(contact);
|
||||
}
|
||||
@ -548,12 +583,41 @@ void Models::Roster::removeContact(const QString& account, const QString& jid, c
|
||||
qDebug() << "An attempt to remove contact " << jid << " from non existing group " << group << " of account " << account <<", skipping";
|
||||
return;
|
||||
}
|
||||
Account* acc = accounts.find(account)->second; //I assume the account is found, otherwise there will be no groups with that ElId;
|
||||
Group* gr = gItr->second;
|
||||
Contact* cont = 0;
|
||||
|
||||
std::multimap<ElId, Contact*>::iterator cBeg = contacts.lower_bound(contactId);
|
||||
std::multimap<ElId, Contact*>::iterator cEnd = contacts.upper_bound(contactId);
|
||||
for (;cBeg != cEnd; ++cBeg) {
|
||||
unsigned int entries(0);
|
||||
unsigned int ungroupped(0);
|
||||
for (std::multimap<ElId, Contact*>::iterator cBeg = contacts.lower_bound(contactId), cEnd = contacts.upper_bound(contactId); cBeg != cEnd; ++cBeg) {
|
||||
++entries;
|
||||
Contact* elem = cBeg->second;
|
||||
if (elem->parentItem() == acc) {
|
||||
++ungroupped;
|
||||
}
|
||||
}
|
||||
|
||||
if (ungroupped == 0 && entries == 1) {
|
||||
for (std::multimap<ElId, Contact*>::iterator cBeg = contacts.lower_bound(contactId), cEnd = contacts.upper_bound(contactId); cBeg != cEnd; ++cBeg) {
|
||||
if (cBeg->second->parentItem() == gr) {
|
||||
cont = cBeg->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cont == 0) {
|
||||
qDebug() << "An attempt to remove contact " << jid << " of account " << account << " from group " << group <<", but there is no such contact in that group, skipping";
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "An attempt to remove last instance of contact" << jid << "from the group" << group << ", contact will be moved to ungrouped contacts of" << account;
|
||||
acc->appendChild(cont);
|
||||
|
||||
if (gr->childCount() == 0) {
|
||||
removeGroup(account, group);
|
||||
}
|
||||
} else {
|
||||
for (std::multimap<ElId, Contact*>::iterator cBeg = contacts.lower_bound(contactId), cEnd = contacts.upper_bound(contactId); cBeg != cEnd; ++cBeg) {
|
||||
if (cBeg->second->parentItem() == gr) {
|
||||
cont = cBeg->second;
|
||||
contacts.erase(cBeg);
|
||||
@ -573,11 +637,13 @@ void Models::Roster::removeContact(const QString& account, const QString& jid, c
|
||||
removeGroup(account, group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Models::Roster::onChildChanged(Models::Item* item, int row, int col)
|
||||
{
|
||||
QModelIndex index = createIndex(row, 0, item);
|
||||
emit dataChanged(index, index);
|
||||
QModelIndex index2 = createIndex(row, 1, item);
|
||||
emit dataChanged(index, index2);
|
||||
}
|
||||
|
||||
void Models::Roster::onChildIsAboutToBeInserted(Models::Item* parent, int first, int last)
|
||||
@ -738,7 +804,7 @@ QString Models::Roster::getContactName(const QString& account, const QString& ji
|
||||
if (rItr == rooms.end()) {
|
||||
qDebug() << "An attempt to get a name of non existing contact/room " << account << ":" << jid << ", skipping";
|
||||
} else {
|
||||
name = rItr->second->getName();
|
||||
name = rItr->second->getRoomName();
|
||||
}
|
||||
} else {
|
||||
name = cItr->second->getContactName();
|
||||
@ -844,3 +910,47 @@ void Models::Roster::removeRoomParticipant(const QString& account, const QString
|
||||
itr->second->removeParticipant(name);
|
||||
}
|
||||
}
|
||||
|
||||
std::deque<QString> Models::Roster::groupList(const QString& account) const
|
||||
{
|
||||
std::deque<QString> answer;
|
||||
for (std::pair<ElId, Group*> pair : groups) {
|
||||
if (pair.first.account == account) {
|
||||
answer.push_back(pair.first.name);
|
||||
}
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
bool Models::Roster::groupHasContact(const QString& account, const QString& group, const QString& contact) const
|
||||
{
|
||||
ElId grId({account, group});
|
||||
std::map<ElId, Group*>::const_iterator gItr = groups.find(grId);
|
||||
if (gItr == groups.end()) {
|
||||
return false;
|
||||
} else {
|
||||
const Group* gr = gItr->second;
|
||||
return gr->hasContact(contact);
|
||||
}
|
||||
}
|
||||
|
||||
QString Models::Roster::getContactIconPath(const QString& account, const QString& jid)
|
||||
{
|
||||
ElId id(account, jid);
|
||||
std::multimap<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
||||
QString path = "";
|
||||
if (cItr == contacts.end()) {
|
||||
std::map<ElId, Room*>::const_iterator rItr = rooms.find(id);
|
||||
if (rItr == rooms.end()) {
|
||||
qDebug() << "An attempt to get an icon path of non existing contact" << account << ":" << jid << ", returning empty value";
|
||||
} else {
|
||||
//path = rItr->second->getRoomName();
|
||||
}
|
||||
} else {
|
||||
if (cItr->second->getAvatarState() != Shared::Avatar::empty) {
|
||||
path = cItr->second->getAvatarPath();
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
@ -71,6 +71,10 @@ public:
|
||||
QModelIndex parent ( const QModelIndex& child ) const override;
|
||||
QModelIndex index ( int row, int column, const QModelIndex& parent ) const override;
|
||||
|
||||
std::deque<QString> groupList(const QString& account) const;
|
||||
bool groupHasContact(const QString& account, const QString& group, const QString& contactJID) const;
|
||||
QString getContactIconPath(const QString& account, const QString& jid);
|
||||
|
||||
Accounts* accountsModel;
|
||||
|
||||
private:
|
||||
|
240
ui/squawk.cpp
@ -20,6 +20,7 @@
|
||||
#include "ui_squawk.h"
|
||||
#include <QDebug>
|
||||
#include <QIcon>
|
||||
#include <QInputDialog>
|
||||
|
||||
Squawk::Squawk(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
@ -28,27 +29,35 @@ Squawk::Squawk(QWidget *parent) :
|
||||
rosterModel(),
|
||||
conversations(),
|
||||
contextMenu(new QMenu()),
|
||||
dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus())
|
||||
dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()),
|
||||
requestedFiles(),
|
||||
vCards()
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
m_ui->roster->setModel(&rosterModel);
|
||||
m_ui->roster->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_ui->roster->setColumnWidth(1, 20);
|
||||
m_ui->roster->setIconSize(QSize(20, 20));
|
||||
m_ui->roster->header()->setStretchLastSection(false);
|
||||
m_ui->roster->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
|
||||
for (unsigned int i = Shared::availabilityLowest; i < Shared::availabilityHighest + 1; ++i) {
|
||||
Shared::Availability av = static_cast<Shared::Availability>(i);
|
||||
m_ui->comboBox->addItem(Shared::availabilityIcon(av), Shared::availabilityNames[av]);
|
||||
m_ui->comboBox->addItem(Shared::availabilityIcon(av), QCoreApplication::translate("Global", Shared::availabilityNames[av].toLatin1()));
|
||||
}
|
||||
m_ui->comboBox->setCurrentIndex(Shared::offline);
|
||||
|
||||
connect(m_ui->actionAccounts, SIGNAL(triggered()), this, SLOT(onAccounts()));
|
||||
connect(m_ui->actionAddContact, SIGNAL(triggered()), this, SLOT(onNewContact()));
|
||||
connect(m_ui->actionAddConference, SIGNAL(triggered()), this, SLOT(onNewConference()));
|
||||
connect(m_ui->comboBox, SIGNAL(activated(int)), this, SLOT(onComboboxActivated(int)));
|
||||
connect(m_ui->roster, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onRosterItemDoubleClicked(const QModelIndex&)));
|
||||
connect(m_ui->roster, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onRosterContextMenu(const QPoint&)));
|
||||
connect(m_ui->actionAccounts, &QAction::triggered, this, &Squawk::onAccounts);
|
||||
connect(m_ui->actionAddContact, &QAction::triggered, this, &Squawk::onNewContact);
|
||||
connect(m_ui->actionAddConference, &QAction::triggered, this, &Squawk::onNewConference);
|
||||
connect(m_ui->comboBox, qOverload<int>(&QComboBox::activated), this, &Squawk::onComboboxActivated);
|
||||
connect(m_ui->roster, &QTreeView::doubleClicked, this, &Squawk::onRosterItemDoubleClicked);
|
||||
connect(m_ui->roster, &QTreeView::customContextMenuRequested, this, &Squawk::onRosterContextMenu);
|
||||
|
||||
connect(rosterModel.accountsModel, SIGNAL(sizeChanged(unsigned int)), this, SLOT(onAccountsSizeChanged(unsigned int)));
|
||||
connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged);
|
||||
//m_ui->mainToolBar->addWidget(m_ui->comboBox);
|
||||
|
||||
setWindowTitle(tr("Contact list"));
|
||||
}
|
||||
|
||||
Squawk::~Squawk() {
|
||||
@ -60,12 +69,12 @@ void Squawk::onAccounts()
|
||||
if (accounts == 0) {
|
||||
accounts = new Accounts(rosterModel.accountsModel, this);
|
||||
accounts->setAttribute(Qt::WA_DeleteOnClose);
|
||||
connect(accounts, SIGNAL(destroyed(QObject*)), this, SLOT(onAccountsClosed(QObject*)));
|
||||
connect(accounts, SIGNAL(newAccount(const QMap<QString, QVariant>&)), this, SIGNAL(newAccountRequest(const QMap<QString, QVariant>&)));
|
||||
connect(accounts, SIGNAL(changeAccount(const QString&, const QMap<QString, QVariant>&)), this, SIGNAL(modifyAccountRequest(const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(accounts, SIGNAL(connectAccount(const QString&)), this, SIGNAL(connectAccount(const QString&)));
|
||||
connect(accounts, SIGNAL(disconnectAccount(const QString&)), this, SIGNAL(disconnectAccount(const QString&)));
|
||||
connect(accounts, SIGNAL(removeAccount(const QString&)), this, SIGNAL(removeAccountRequest(const QString&)));
|
||||
connect(accounts, &Accounts::destroyed, this, &Squawk::onAccountsClosed);
|
||||
connect(accounts, &Accounts::newAccount, this, &Squawk::newAccountRequest);
|
||||
connect(accounts, &Accounts::changeAccount, this, &Squawk::modifyAccountRequest);
|
||||
connect(accounts, &Accounts::connectAccount, this, &Squawk::connectAccount);
|
||||
connect(accounts, &Accounts::disconnectAccount, this, &Squawk::disconnectAccount);
|
||||
connect(accounts, &Accounts::removeAccount, this, &Squawk::removeAccountRequest);
|
||||
|
||||
accounts->show();
|
||||
} else {
|
||||
@ -90,8 +99,8 @@ void Squawk::onNewContact()
|
||||
{
|
||||
NewContact* nc = new NewContact(rosterModel.accountsModel, this);
|
||||
|
||||
connect(nc, SIGNAL(accepted()), this, SLOT(onNewContactAccepted()));
|
||||
connect(nc, SIGNAL(rejected()), nc, SLOT(deleteLater()));
|
||||
connect(nc, &NewContact::accepted, this, &Squawk::onNewContactAccepted);
|
||||
connect(nc, &NewContact::rejected, nc, &NewContact::deleteLater);
|
||||
|
||||
nc->exec();
|
||||
}
|
||||
@ -100,8 +109,8 @@ void Squawk::onNewConference()
|
||||
{
|
||||
JoinConference* jc = new JoinConference(rosterModel.accountsModel, this);
|
||||
|
||||
connect(jc, SIGNAL(accepted()), this, SLOT(onJoinConferenceAccepted()));
|
||||
connect(jc, SIGNAL(rejected()), jc, SLOT(deleteLater()));
|
||||
connect(jc, &JoinConference::accepted, this, &Squawk::onJoinConferenceAccepted);
|
||||
connect(jc, &JoinConference::rejected, jc, &JoinConference::deleteLater);
|
||||
|
||||
jc->exec();
|
||||
}
|
||||
@ -131,12 +140,19 @@ void Squawk::closeEvent(QCloseEvent* event)
|
||||
if (accounts != 0) {
|
||||
accounts->close();
|
||||
}
|
||||
|
||||
for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) {
|
||||
disconnect(itr->second, SIGNAL(destroyed(QObject*)), this, SLOT(onConversationClosed(QObject*)));
|
||||
disconnect(itr->second, &Conversation::destroyed, this, &Squawk::onConversationClosed);
|
||||
itr->second->close();
|
||||
}
|
||||
conversations.clear();
|
||||
|
||||
for (std::map<QString, VCard*>::const_iterator itr = vCards.begin(), end = vCards.end(); itr != end; ++itr) {
|
||||
disconnect(itr->second, &VCard::destroyed, this, &Squawk::onVCardClosed);
|
||||
itr->second->close();
|
||||
}
|
||||
vCards.clear();
|
||||
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
|
||||
@ -283,13 +299,14 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item)
|
||||
if (created) {
|
||||
conv->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
connect(conv, SIGNAL(destroyed(QObject*)), this, SLOT(onConversationClosed(QObject*)));
|
||||
connect(conv, SIGNAL(sendMessage(const Shared::Message&)), this, SLOT(onConversationMessage(const Shared::Message&)));
|
||||
connect(conv, SIGNAL(sendMessage(const Shared::Message&, const QString&)), this, SLOT(onConversationMessage(const Shared::Message&, const QString&)));
|
||||
connect(conv, SIGNAL(requestArchive(const QString&)), this, SLOT(onConversationRequestArchive(const QString&)));
|
||||
connect(conv, SIGNAL(requestLocalFile(const QString&, const QString&)), this, SLOT(onConversationRequestLocalFile(const QString&, const QString&)));
|
||||
connect(conv, SIGNAL(downloadFile(const QString&, const QString&)), this, SLOT(onConversationDownloadFile(const QString&, const QString&)));
|
||||
connect(conv, SIGNAL(shown()), this, SLOT(onConversationShown()));
|
||||
connect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed);
|
||||
connect(conv, qOverload<const Shared::Message&>(&Conversation::sendMessage), this, qOverload<const Shared::Message&>(&Squawk::onConversationMessage));
|
||||
connect(conv, qOverload<const Shared::Message&, const QString&>(&Conversation::sendMessage),
|
||||
this, qOverload<const Shared::Message&, const QString&>(&Squawk::onConversationMessage));
|
||||
connect(conv, &Conversation::requestArchive, this, &Squawk::onConversationRequestArchive);
|
||||
connect(conv, &Conversation::requestLocalFile, this, &Squawk::onConversationRequestLocalFile);
|
||||
connect(conv, &Conversation::downloadFile, this, &Squawk::onConversationDownloadFile);
|
||||
connect(conv, &Conversation::shown, this, &Squawk::onConversationShown);
|
||||
|
||||
conversations.insert(std::make_pair(*id, conv));
|
||||
|
||||
@ -449,11 +466,16 @@ void Squawk::accountMessage(const QString& account, const Shared::Message& data)
|
||||
|
||||
void Squawk::notify(const QString& account, const Shared::Message& msg)
|
||||
{
|
||||
QString name = QString(rosterModel.getContactName(account, msg.getPenPalJid()));;
|
||||
QString name = QString(rosterModel.getContactName(account, msg.getPenPalJid()));
|
||||
QString path = QString(rosterModel.getContactIconPath(account, msg.getPenPalJid()));
|
||||
QVariantList args;
|
||||
args << QString(QCoreApplication::applicationName());
|
||||
args << QVariant(QVariant::UInt); //TODO some normal id
|
||||
args << QString("mail-message"); //TODO icon
|
||||
if (path.size() > 0) {
|
||||
args << path;
|
||||
} else {
|
||||
args << QString("mail-message");
|
||||
}
|
||||
if (msg.getType() == Shared::Message::groupChat) {
|
||||
args << msg.getFromResource() + " from " + name;
|
||||
} else {
|
||||
@ -502,10 +524,9 @@ void Squawk::removeAccount(const QString& account)
|
||||
Conversations::const_iterator lItr = itr;
|
||||
++itr;
|
||||
Conversation* conv = lItr->second;
|
||||
disconnect(conv, SIGNAL(destroyed(QObject*)), this, SLOT(onConversationClosed(QObject*)));
|
||||
disconnect(conv, SIGNAL(sendMessage(const Shared::Message&)), this, SLOT(onConversationMessage(const Shared::Message&)));
|
||||
disconnect(conv, SIGNAL(requestArchive(const QString&)), this, SLOT(onConversationRequestArchive(const QString&)));
|
||||
disconnect(conv, SIGNAL(shown()), this, SLOT(onConversationShown()));
|
||||
disconnect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed);
|
||||
disconnect(conv, &Conversation::requestArchive, this, &Squawk::onConversationRequestArchive);
|
||||
disconnect(conv, &Conversation::shown, this, &Squawk::onConversationShown);
|
||||
conv->close();
|
||||
conversations.erase(lItr);
|
||||
} else {
|
||||
@ -523,6 +544,7 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
||||
|
||||
contextMenu->clear();
|
||||
bool hasMenu = false;
|
||||
bool active = item->getAccountConnectionState() == Shared::connected;
|
||||
switch (item->type) {
|
||||
case Models::Item::account: {
|
||||
Models::Account* acc = static_cast<Models::Account*>(item);
|
||||
@ -530,18 +552,24 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
||||
QString name = acc->getName();
|
||||
|
||||
if (acc->getState() != Shared::disconnected) {
|
||||
QAction* con = contextMenu->addAction(Shared::icon("network-disconnect"), "Disconnect");
|
||||
QAction* con = contextMenu->addAction(Shared::icon("network-disconnect"), tr("Disconnect"));
|
||||
con->setEnabled(active);
|
||||
connect(con, &QAction::triggered, [this, name]() {
|
||||
emit disconnectAccount(name);
|
||||
});
|
||||
} else {
|
||||
QAction* con = contextMenu->addAction(Shared::icon("network-connect"), "Connect");
|
||||
QAction* con = contextMenu->addAction(Shared::icon("network-connect"), tr("Connect"));
|
||||
connect(con, &QAction::triggered, [this, name]() {
|
||||
emit connectAccount(name);
|
||||
});
|
||||
}
|
||||
|
||||
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), "Remove");
|
||||
QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard"));
|
||||
card->setEnabled(active);
|
||||
connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, name, acc->getBareJid(), true));
|
||||
|
||||
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove"));
|
||||
remove->setEnabled(active);
|
||||
connect(remove, &QAction::triggered, [this, name]() {
|
||||
emit removeAccount(name);
|
||||
});
|
||||
@ -552,7 +580,8 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
||||
Models::Contact* cnt = static_cast<Models::Contact*>(item);
|
||||
hasMenu = true;
|
||||
|
||||
QAction* dialog = contextMenu->addAction(Shared::icon("mail-message"), "Open dialog");
|
||||
QAction* dialog = contextMenu->addAction(Shared::icon("mail-message"), tr("Open dialog"));
|
||||
dialog->setEnabled(active);
|
||||
connect(dialog, &QAction::triggered, [this, index]() {
|
||||
onRosterItemDoubleClicked(index);
|
||||
});
|
||||
@ -561,7 +590,8 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
||||
switch (state) {
|
||||
case Shared::both:
|
||||
case Shared::to: {
|
||||
QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), "Unsubscribe");
|
||||
QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), tr("Unsubscribe"));
|
||||
unsub->setEnabled(active);
|
||||
connect(unsub, &QAction::triggered, [this, cnt]() {
|
||||
emit unsubscribeContact(cnt->getAccountName(), cnt->getJid(), "");
|
||||
});
|
||||
@ -570,14 +600,74 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
||||
case Shared::from:
|
||||
case Shared::unknown:
|
||||
case Shared::none: {
|
||||
QAction* sub = contextMenu->addAction(Shared::icon("news-subscribe"), "Subscribe");
|
||||
QAction* sub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe"));
|
||||
sub->setEnabled(active);
|
||||
connect(sub, &QAction::triggered, [this, cnt]() {
|
||||
emit subscribeContact(cnt->getAccountName(), cnt->getJid(), "");
|
||||
});
|
||||
}
|
||||
}
|
||||
QString accName = cnt->getAccountName();
|
||||
QString cntJID = cnt->getJid();
|
||||
QString cntName = cnt->getName();
|
||||
|
||||
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), "Remove");
|
||||
QAction* rename = contextMenu->addAction(Shared::icon("edit-rename"), tr("Rename"));
|
||||
rename->setEnabled(active);
|
||||
connect(rename, &QAction::triggered, [this, cntName, accName, cntJID]() {
|
||||
QInputDialog* dialog = new QInputDialog(this);
|
||||
connect(dialog, &QDialog::accepted, [this, dialog, cntName, accName, cntJID]() {
|
||||
QString newName = dialog->textValue();
|
||||
if (newName != cntName) {
|
||||
emit renameContactRequest(accName, cntJID, newName);
|
||||
}
|
||||
dialog->deleteLater();
|
||||
});
|
||||
connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater);
|
||||
dialog->setInputMode(QInputDialog::TextInput);
|
||||
dialog->setLabelText(tr("Input new name for %1\nor leave it empty for the contact \nto be displayed as %1").arg(cntJID));
|
||||
dialog->setWindowTitle(tr("Renaming %1").arg(cntJID));
|
||||
dialog->setTextValue(cntName);
|
||||
dialog->exec();
|
||||
});
|
||||
|
||||
|
||||
QMenu* groupsMenu = contextMenu->addMenu(Shared::icon("group"), tr("Groups"));
|
||||
std::deque<QString> groupList = rosterModel.groupList(accName);
|
||||
for (QString groupName : groupList) {
|
||||
QAction* gr = groupsMenu->addAction(groupName);
|
||||
gr->setCheckable(true);
|
||||
gr->setChecked(rosterModel.groupHasContact(accName, groupName, cntJID));
|
||||
gr->setEnabled(active);
|
||||
connect(gr, &QAction::toggled, [this, accName, groupName, cntJID](bool checked) {
|
||||
if (checked) {
|
||||
emit addContactToGroupRequest(accName, cntJID, groupName);
|
||||
} else {
|
||||
emit removeContactFromGroupRequest(accName, cntJID, groupName);
|
||||
}
|
||||
});
|
||||
}
|
||||
QAction* newGroup = groupsMenu->addAction(Shared::icon("group-new"), tr("New group"));
|
||||
newGroup->setEnabled(active);
|
||||
connect(newGroup, &QAction::triggered, [this, accName, cntJID]() {
|
||||
QInputDialog* dialog = new QInputDialog(this);
|
||||
connect(dialog, &QDialog::accepted, [this, dialog, accName, cntJID]() {
|
||||
emit addContactToGroupRequest(accName, cntJID, dialog->textValue());
|
||||
dialog->deleteLater();
|
||||
});
|
||||
connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater);
|
||||
dialog->setInputMode(QInputDialog::TextInput);
|
||||
dialog->setLabelText(tr("New group name"));
|
||||
dialog->setWindowTitle(tr("Add %1 to a new group").arg(cntJID));
|
||||
dialog->exec();
|
||||
});
|
||||
|
||||
|
||||
QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard"));
|
||||
card->setEnabled(active);
|
||||
connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, accName, cnt->getJid(), false));
|
||||
|
||||
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove"));
|
||||
remove->setEnabled(active);
|
||||
connect(remove, &QAction::triggered, [this, cnt]() {
|
||||
emit removeContactRequest(cnt->getAccountName(), cnt->getJid());
|
||||
});
|
||||
@ -588,7 +678,8 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
||||
Models::Room* room = static_cast<Models::Room*>(item);
|
||||
hasMenu = true;
|
||||
|
||||
QAction* dialog = contextMenu->addAction(Shared::icon("mail-message"), "Open conversation");
|
||||
QAction* dialog = contextMenu->addAction(Shared::icon("mail-message"), tr("Open conversation"));
|
||||
dialog->setEnabled(active);
|
||||
connect(dialog, &QAction::triggered, [this, index]() {
|
||||
onRosterItemDoubleClicked(index);
|
||||
});
|
||||
@ -596,7 +687,8 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
||||
|
||||
Models::Roster::ElId id(room->getAccountName(), room->getJid());
|
||||
if (room->getAutoJoin()) {
|
||||
QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), "Unsubscribe");
|
||||
QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), tr("Unsubscribe"));
|
||||
unsub->setEnabled(active);
|
||||
connect(unsub, &QAction::triggered, [this, id]() {
|
||||
emit setRoomAutoJoin(id.account, id.name, false);
|
||||
if (conversations.find(id) == conversations.end()) { //to leave the room if it's not opened in a conversation window
|
||||
@ -604,7 +696,8 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
||||
}
|
||||
});
|
||||
} else {
|
||||
QAction* unsub = contextMenu->addAction(Shared::icon("news-subscribe"), "Subscribe");
|
||||
QAction* unsub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe"));
|
||||
unsub->setEnabled(active);
|
||||
connect(unsub, &QAction::triggered, [this, id]() {
|
||||
emit setRoomAutoJoin(id.account, id.name, true);
|
||||
if (conversations.find(id) == conversations.end()) { //to join the room if it's not already joined
|
||||
@ -613,7 +706,8 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
||||
});
|
||||
}
|
||||
|
||||
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), "Remove");
|
||||
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove"));
|
||||
remove->setEnabled(active);
|
||||
connect(remove, &QAction::triggered, [this, id]() {
|
||||
emit removeRoomRequest(id.account, id.name);
|
||||
});
|
||||
@ -657,3 +751,61 @@ void Squawk::removeRoomParticipant(const QString& account, const QString& jid, c
|
||||
{
|
||||
rosterModel.removeRoomParticipant(account, jid, name);
|
||||
}
|
||||
|
||||
void Squawk::responseVCard(const QString& jid, const Shared::VCard& card)
|
||||
{
|
||||
std::map<QString, VCard*>::const_iterator itr = vCards.find(jid);
|
||||
if (itr != vCards.end()) {
|
||||
itr->second->setVCard(card);
|
||||
itr->second->hideProgress();
|
||||
}
|
||||
}
|
||||
|
||||
void Squawk::onVCardClosed()
|
||||
{
|
||||
VCard* vCard = static_cast<VCard*>(sender());
|
||||
|
||||
std::map<QString, VCard*>::const_iterator itr = vCards.find(vCard->getJid());
|
||||
if (itr == vCards.end()) {
|
||||
qDebug() << "VCard has been closed but can not be found among other opened vCards, application is most probably going to crash";
|
||||
return;
|
||||
}
|
||||
vCards.erase(itr);
|
||||
}
|
||||
|
||||
void Squawk::onActivateVCard(const QString& account, const QString& jid, bool edition)
|
||||
{
|
||||
std::map<QString, VCard*>::const_iterator itr = vCards.find(jid);
|
||||
VCard* card;
|
||||
Models::Contact::Messages deque;
|
||||
if (itr != vCards.end()) {
|
||||
card = itr->second;
|
||||
} else {
|
||||
card = new VCard(jid, edition);
|
||||
if (edition) {
|
||||
card->setWindowTitle(tr("%1 account card").arg(account));
|
||||
} else {
|
||||
card->setWindowTitle(tr("%1 contact card").arg(jid));
|
||||
}
|
||||
card->setAttribute(Qt::WA_DeleteOnClose);
|
||||
vCards.insert(std::make_pair(jid, card));
|
||||
|
||||
connect(card, &VCard::destroyed, this, &Squawk::onVCardClosed);
|
||||
connect(card, &VCard::saveVCard, std::bind( &Squawk::onVCardSave, this, std::placeholders::_1, account));
|
||||
}
|
||||
|
||||
card->show();
|
||||
card->raise();
|
||||
card->activateWindow();
|
||||
card->showProgress(tr("Downloading vCard"));
|
||||
|
||||
emit requestVCard(account, jid);
|
||||
}
|
||||
|
||||
void Squawk::onVCardSave(const Shared::VCard& card, const QString& account)
|
||||
{
|
||||
VCard* widget = static_cast<VCard*>(sender());
|
||||
emit uploadVCard(account, card);
|
||||
|
||||
widget->deleteLater();
|
||||
}
|
||||
|
13
ui/squawk.h
@ -34,8 +34,9 @@
|
||||
#include "widgets/newcontact.h"
|
||||
#include "widgets/joinconference.h"
|
||||
#include "models/roster.h"
|
||||
#include "widgets/vcard/vcard.h"
|
||||
|
||||
#include "../global.h"
|
||||
#include "global.h"
|
||||
|
||||
namespace Ui {
|
||||
class Squawk;
|
||||
@ -63,12 +64,17 @@ signals:
|
||||
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||
void removeContactRequest(const QString& account, const QString& jid);
|
||||
void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups);
|
||||
void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
||||
void removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
||||
void renameContactRequest(const QString& account, const QString& jid, const QString& newName);
|
||||
void setRoomJoined(const QString& account, const QString& jid, bool joined);
|
||||
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
|
||||
void addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
||||
void removeRoomRequest(const QString& account, const QString& jid);
|
||||
void fileLocalPathRequest(const QString& messageId, const QString& url);
|
||||
void downloadFileRequest(const QString& messageId, const QString& url);
|
||||
void requestVCard(const QString& account, const QString& jid);
|
||||
void uploadVCard(const QString& account, const Shared::VCard& card);
|
||||
|
||||
public slots:
|
||||
void newAccount(const QMap<QString, QVariant>& account);
|
||||
@ -94,6 +100,7 @@ public slots:
|
||||
void fileLocalPathResponse(const QString& messageId, const QString& path);
|
||||
void downloadFileError(const QString& messageId, const QString& error);
|
||||
void downloadFileProgress(const QString& messageId, qreal value);
|
||||
void responseVCard(const QString& jid, const Shared::VCard& card);
|
||||
|
||||
private:
|
||||
typedef std::map<Models::Roster::ElId, Conversation*> Conversations;
|
||||
@ -105,6 +112,7 @@ private:
|
||||
QMenu* contextMenu;
|
||||
QDBusInterface dbus;
|
||||
std::map<QString, std::set<Models::Roster::ElId>> requestedFiles;
|
||||
std::map<QString, VCard*> vCards;
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent * event) override;
|
||||
@ -119,6 +127,9 @@ private slots:
|
||||
void onAccountsSizeChanged(unsigned int size);
|
||||
void onAccountsClosed(QObject* parent = 0);
|
||||
void onConversationClosed(QObject* parent = 0);
|
||||
void onVCardClosed();
|
||||
void onVCardSave(const Shared::VCard& card, const QString& account);
|
||||
void onActivateVCard(const QString& account, const QString& jid, bool edition = false);
|
||||
void onComboboxActivated(int index);
|
||||
void onRosterItemDoubleClicked(const QModelIndex& item);
|
||||
void onConversationMessage(const Shared::Message& msg);
|
||||
|
@ -51,6 +51,9 @@
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="expandsOnDoubleClick">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
@ -122,7 +125,8 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="resource-group-new"/>
|
||||
<iconset theme="resource-group-new">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add conference</string>
|
||||
|
@ -42,7 +42,7 @@ Badge::Badge(const QString& p_id, const QString& p_text, const QIcon& icon, QWid
|
||||
|
||||
layout->setContentsMargins(2, 2, 2, 2);
|
||||
|
||||
connect(closeButton, SIGNAL(clicked()), this, SIGNAL(close()));
|
||||
connect(closeButton, &QPushButton::clicked, this, &Badge::close);
|
||||
}
|
||||
|
||||
Badge::~Badge()
|
||||
|
85
ui/utils/comboboxdelegate.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Squawk messenger.
|
||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "QTimer"
|
||||
|
||||
#include "comboboxdelegate.h"
|
||||
|
||||
ComboboxDelegate::ComboboxDelegate(QObject *parent):
|
||||
QStyledItemDelegate(parent),
|
||||
entries(),
|
||||
ff(new FocusFilter())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ComboboxDelegate::~ComboboxDelegate()
|
||||
{
|
||||
delete ff;
|
||||
}
|
||||
|
||||
|
||||
QWidget* ComboboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QComboBox *cb = new QComboBox(parent);
|
||||
|
||||
for (const std::pair<QString, QIcon> pair : entries) {
|
||||
cb->addItem(pair.second, pair.first);
|
||||
}
|
||||
|
||||
return cb;
|
||||
}
|
||||
|
||||
|
||||
void ComboboxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
QComboBox *cb = static_cast<QComboBox*>(editor);
|
||||
int currentIndex = index.data(Qt::EditRole).toInt();
|
||||
if (currentIndex >= 0) {
|
||||
cb->setCurrentIndex(currentIndex);
|
||||
cb->installEventFilter(ff);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ComboboxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
|
||||
{
|
||||
QComboBox *cb = static_cast<QComboBox *>(editor);
|
||||
model->setData(index, cb->currentIndex(), Qt::EditRole);
|
||||
}
|
||||
|
||||
void ComboboxDelegate::addEntry(const QString& title, const QIcon& icon)
|
||||
{
|
||||
entries.emplace_back(title, icon);
|
||||
}
|
||||
|
||||
bool ComboboxDelegate::FocusFilter::eventFilter(QObject* src, QEvent* evt)
|
||||
{
|
||||
if (evt->type() == QEvent::FocusIn) {
|
||||
QComboBox* cb = static_cast<QComboBox*>(src);
|
||||
cb->removeEventFilter(this);
|
||||
QTimer* timer = new QTimer; //TODO that is ridiculous! I refuse to believe there is no better way than that one!
|
||||
QObject::connect(timer, &QTimer::timeout, [timer, cb]() {
|
||||
cb->showPopup();
|
||||
timer->deleteLater();
|
||||
});
|
||||
|
||||
timer->setSingleShot(true);
|
||||
timer->start(100);
|
||||
}
|
||||
return QObject::eventFilter(src, evt);
|
||||
}
|
57
ui/utils/comboboxdelegate.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Squawk messenger.
|
||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef COMBOBOXDELEGATE_H
|
||||
#define COMBOBOXDELEGATE_H
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QComboBox>
|
||||
#include <QFocusEvent>
|
||||
|
||||
#include <deque>
|
||||
|
||||
/**
|
||||
* @todo write docs
|
||||
*/
|
||||
class ComboboxDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class FocusFilter : public QObject {
|
||||
public:
|
||||
bool eventFilter(QObject *src, QEvent *evt) override;
|
||||
};
|
||||
|
||||
public:
|
||||
ComboboxDelegate(QObject *parent = nullptr);
|
||||
~ComboboxDelegate();
|
||||
|
||||
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
|
||||
|
||||
void addEntry(const QString& title, const QIcon& icon = QIcon());
|
||||
|
||||
private:
|
||||
std::deque<std::pair<QString, QIcon>> entries;
|
||||
FocusFilter* ff;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // COMBOBOXDELEGATE_H
|
@ -19,19 +19,14 @@
|
||||
#include <QDebug>
|
||||
#include "image.h"
|
||||
|
||||
Image::Image(const QString& path, QWidget* parent):
|
||||
Image::Image(const QString& path, quint16 p_minWidth, QWidget* parent):
|
||||
QLabel(parent),
|
||||
pixmap(path),
|
||||
aspectRatio(0)
|
||||
aspectRatio(0),
|
||||
minWidth(p_minWidth)
|
||||
{
|
||||
|
||||
qreal height = pixmap.height();
|
||||
qreal width = pixmap.width();
|
||||
aspectRatio = width / height;
|
||||
setPixmap(pixmap);
|
||||
setScaledContents(true);
|
||||
setMinimumHeight(50 / aspectRatio);
|
||||
setMinimumWidth(50);
|
||||
recalculateAspectRatio();
|
||||
}
|
||||
|
||||
Image::~Image()
|
||||
@ -42,11 +37,39 @@ Image::~Image()
|
||||
int Image::heightForWidth(int width) const
|
||||
{
|
||||
int height = width / aspectRatio;
|
||||
//qDebug() << height << width << aspectRatio;
|
||||
return height;
|
||||
}
|
||||
|
||||
int Image::widthForHeight(int height) const
|
||||
{
|
||||
return height * aspectRatio;
|
||||
}
|
||||
|
||||
bool Image::hasHeightForWidth() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void Image::recalculateAspectRatio()
|
||||
{
|
||||
qreal height = pixmap.height();
|
||||
qreal width = pixmap.width();
|
||||
aspectRatio = width / height;
|
||||
setPixmap(pixmap);
|
||||
setMinimumHeight(minWidth / aspectRatio);
|
||||
setMinimumWidth(minWidth);
|
||||
}
|
||||
|
||||
void Image::setMinWidth(quint16 p_minWidth)
|
||||
{
|
||||
if (minWidth != p_minWidth) {
|
||||
minWidth = p_minWidth;
|
||||
recalculateAspectRatio();
|
||||
}
|
||||
}
|
||||
|
||||
void Image::setPath(const QString& path)
|
||||
{
|
||||
pixmap = QPixmap(path);
|
||||
recalculateAspectRatio();
|
||||
}
|
||||
|
@ -28,34 +28,23 @@
|
||||
class Image : public QLabel
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
Image(const QString& path, QWidget* parent = nullptr);
|
||||
Image(const QString& path, quint16 minWidth = 50, QWidget* parent = nullptr);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Image();
|
||||
|
||||
/**
|
||||
* @todo write docs
|
||||
*
|
||||
* @param TODO
|
||||
* @return TODO
|
||||
*/
|
||||
int heightForWidth(int width) const override;
|
||||
|
||||
/**
|
||||
* @todo write docs
|
||||
*
|
||||
* @return TODO
|
||||
*/
|
||||
virtual bool hasHeightForWidth() const;
|
||||
int widthForHeight(int height) const;
|
||||
bool hasHeightForWidth() const override;
|
||||
void setPath(const QString& path);
|
||||
void setMinWidth(quint16 minWidth);
|
||||
|
||||
private:
|
||||
QPixmap pixmap;
|
||||
qreal aspectRatio;
|
||||
quint16 minWidth;
|
||||
|
||||
private:
|
||||
void recalculateAspectRatio();
|
||||
};
|
||||
|
||||
#endif // IMAGE_H
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include <QFileInfo>
|
||||
#include "message.h"
|
||||
|
||||
const QRegExp urlReg("^(?!<img\\ssrc=\")((?:https?|ftp)://\\S+)");
|
||||
const QRegExp urlReg("(?!<img\\ssrc=\")((?:https?|ftp)://\\S+)");
|
||||
const QRegExp imgReg("((?:https?|ftp)://\\S+\\.(?:jpg|jpeg|png|svg|gif))");
|
||||
|
||||
Message::Message(const Shared::Message& source, bool outgoing, const QString& p_sender, QWidget* parent):
|
||||
@ -63,7 +63,6 @@ Message::Message(const Shared::Message& source, bool outgoing, const QString& p_
|
||||
dFont.setItalic(true);
|
||||
dFont.setPointSize(dFont.pointSize() - 2);
|
||||
date->setFont(dFont);
|
||||
date->setForegroundRole(QPalette::ToolTipText);
|
||||
|
||||
QFont f;
|
||||
f.setBold(true);
|
||||
@ -117,16 +116,16 @@ void Message::addDownloadDialog()
|
||||
text->setText("");
|
||||
text->hide();
|
||||
}
|
||||
downloadButton = new QPushButton(QIcon::fromTheme("download"), "Download");
|
||||
downloadButton = new QPushButton(QIcon::fromTheme("download"), tr("Download"));
|
||||
downloadButton->setToolTip("<a href=\"" + msg.getOutOfBandUrl() + "\">" + msg.getOutOfBandUrl() + "</a>");
|
||||
if (errorDownloadingFile) {
|
||||
fileComment->setWordWrap(true);
|
||||
fileComment->setText("Error downloading file: " + errorText + "\nYou can try again");
|
||||
fileComment->setText(tr("Error downloading file: %1\nYou can try again").arg(QCoreApplication::translate("NetworkErrors", errorText.toLatin1())));
|
||||
} else {
|
||||
fileComment->setText(sender->text() + " is offering you to download a file");
|
||||
fileComment->setText(tr("%1 is offering you to download a file").arg(sender->text()));
|
||||
}
|
||||
fileComment->show();
|
||||
connect(downloadButton, SIGNAL(clicked()), this, SLOT(onDownload()));
|
||||
connect(downloadButton, &QPushButton::clicked, this, &Message::onDownload);
|
||||
bodyLayout->insertWidget(2, fileComment);
|
||||
bodyLayout->insertWidget(3, downloadButton);
|
||||
hasDownloadButton = true;
|
||||
@ -188,7 +187,7 @@ void Message::showFile(const QString& path)
|
||||
fileComment->show();
|
||||
}
|
||||
file->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||
QAction* openAction = new QAction(QIcon::fromTheme("document-new-from-template"), "Open", file);
|
||||
QAction* openAction = new QAction(QIcon::fromTheme("document-new-from-template"), tr("Open"), file);
|
||||
connect(openAction, &QAction::triggered, [path]() { //TODO need to get rid of this shame
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
});
|
||||
|
@ -31,35 +31,11 @@ MessageLine::MessageLine(bool p_room, QWidget* parent):
|
||||
palNames(),
|
||||
views(),
|
||||
room(p_room),
|
||||
busyPixmap(new QGraphicsPixmapItem(Shared::icon("view-refresh", true).pixmap(70))),
|
||||
busyScene(),
|
||||
busyLabel(&busyScene),
|
||||
busyLayout(),
|
||||
busyShown(false),
|
||||
rotation()
|
||||
progress()
|
||||
{
|
||||
setBackgroundRole(QPalette::Base);
|
||||
layout->addStretch();
|
||||
|
||||
busyScene.addItem(busyPixmap);
|
||||
busyLayout.addStretch();
|
||||
busyLayout.addWidget(&busyLabel);
|
||||
busyLayout.addStretch();
|
||||
busyLabel.setMaximumSize(70, 70);
|
||||
busyLabel.setMinimumSize(70, 70);
|
||||
busyLabel.setSceneRect(0, 0, 70, 70);
|
||||
busyLabel.setFrameStyle(0);
|
||||
busyLabel.setContentsMargins(0, 0, 0, 0);
|
||||
busyLabel.setInteractive(false);
|
||||
busyPixmap->setTransformOriginPoint(35, 35);
|
||||
busyPixmap->setTransformationMode(Qt::SmoothTransformation);
|
||||
busyPixmap->setOffset(0, 0);;
|
||||
|
||||
rotation.setDuration(500);
|
||||
rotation.setStartValue(0.0f);
|
||||
rotation.setEndValue(180.0f);
|
||||
rotation.setLoopCount(-1);
|
||||
connect(&rotation, SIGNAL(valueChanged(const QVariant&)), this, SLOT(onAnimationValueChanged(const QVariant&)));
|
||||
}
|
||||
|
||||
MessageLine::~MessageLine()
|
||||
@ -151,7 +127,7 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg)
|
||||
|
||||
if (msg.hasOutOfBandUrl()) {\
|
||||
emit requestLocalFile(msg.getId(), msg.getOutOfBandUrl());
|
||||
connect(message, SIGNAL(downloadFile(const QString&, const QString&)), this, SIGNAL(downloadFile(const QString&, const QString&)));
|
||||
connect(message, &Message::downloadFile, this, &MessageLine::downloadFile);
|
||||
}
|
||||
|
||||
return res;
|
||||
@ -201,28 +177,21 @@ QString MessageLine::firstMessageId() const
|
||||
void MessageLine::showBusyIndicator()
|
||||
{
|
||||
if (!busyShown) {
|
||||
layout->insertLayout(0, &busyLayout);
|
||||
layout->insertWidget(0, &progress);
|
||||
progress.start();
|
||||
busyShown = true;
|
||||
rotation.start();
|
||||
busyLabel.show();
|
||||
}
|
||||
}
|
||||
|
||||
void MessageLine::hideBusyIndicator()
|
||||
{
|
||||
if (busyShown) {
|
||||
busyLabel.hide();
|
||||
rotation.stop();
|
||||
layout->removeItem(&busyLayout);
|
||||
progress.stop();
|
||||
layout->removeWidget(&progress);
|
||||
busyShown = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MessageLine::onAnimationValueChanged(const QVariant& value)
|
||||
{
|
||||
busyPixmap->setRotation(value.toReal());
|
||||
}
|
||||
|
||||
void MessageLine::responseDownloadProgress(const QString& messageId, qreal progress)
|
||||
{
|
||||
Index::const_iterator itr = messageIndex.find(messageId);
|
||||
|
@ -25,13 +25,10 @@
|
||||
#include <QLabel>
|
||||
#include <QResizeEvent>
|
||||
#include <QIcon>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsView>
|
||||
#include <QGraphicsPixmapItem>
|
||||
#include <QVariantAnimation>
|
||||
|
||||
#include "../global.h"
|
||||
#include "../../global.h"
|
||||
#include "message.h"
|
||||
#include "progress.h"
|
||||
|
||||
class MessageLine : public QWidget
|
||||
{
|
||||
@ -85,15 +82,8 @@ private:
|
||||
std::map<QString, QString> palNames;
|
||||
std::deque<QHBoxLayout*> views;
|
||||
bool room;
|
||||
QGraphicsPixmapItem* busyPixmap;
|
||||
QGraphicsScene busyScene;
|
||||
QGraphicsView busyLabel;
|
||||
QHBoxLayout busyLayout;
|
||||
bool busyShown;
|
||||
QVariantAnimation rotation;
|
||||
|
||||
private slots:
|
||||
void onAnimationValueChanged(const QVariant& value);
|
||||
Progress progress;
|
||||
};
|
||||
|
||||
#endif // MESSAGELINE_H
|
||||
|
85
ui/utils/progress.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Squawk messenger.
|
||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "progress.h"
|
||||
|
||||
Progress::Progress(quint16 p_size, QWidget* parent):
|
||||
QWidget(parent),
|
||||
pixmap(new QGraphicsPixmapItem(Shared::icon("view-refresh", true).pixmap(p_size))),
|
||||
scene(),
|
||||
label(&scene),
|
||||
progress(false),
|
||||
animation(),
|
||||
size(p_size)
|
||||
{
|
||||
scene.addItem(pixmap);
|
||||
label.setMaximumSize(size, size);
|
||||
label.setMinimumSize(size, size);
|
||||
label.setSceneRect(0, 0, size, size);
|
||||
label.setFrameStyle(0);
|
||||
label.setContentsMargins(0, 0, 0, 0);
|
||||
label.setInteractive(false);
|
||||
label.setStyleSheet("background: transparent");
|
||||
pixmap->setTransformOriginPoint(size / 2, size / 2);
|
||||
pixmap->setTransformationMode(Qt::SmoothTransformation);
|
||||
pixmap->setOffset(0, 0);
|
||||
|
||||
animation.setDuration(500);
|
||||
animation.setStartValue(0.0f);
|
||||
animation.setEndValue(180.0f);
|
||||
animation.setLoopCount(-1);
|
||||
connect(&animation, &QVariantAnimation::valueChanged, this, &Progress::onValueChanged);
|
||||
|
||||
QGridLayout* layout = new QGridLayout();
|
||||
setLayout(layout);
|
||||
layout->setMargin(0);
|
||||
layout->setVerticalSpacing(0);
|
||||
layout->setHorizontalSpacing(0);
|
||||
|
||||
setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
layout->addWidget(&label, 0, 0, 1, 1);
|
||||
label.hide();
|
||||
}
|
||||
|
||||
Progress::~Progress()
|
||||
{
|
||||
}
|
||||
|
||||
void Progress::onValueChanged(const QVariant& value)
|
||||
{
|
||||
pixmap->setRotation(value.toReal());
|
||||
}
|
||||
|
||||
void Progress::start()
|
||||
{
|
||||
if (!progress) {
|
||||
label.show();
|
||||
animation.start();
|
||||
progress = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Progress::stop()
|
||||
{
|
||||
if (progress) {
|
||||
label.hide();
|
||||
animation.stop();
|
||||
progress = false;
|
||||
}
|
||||
}
|
57
ui/utils/progress.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Squawk messenger.
|
||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PROGRESS_H
|
||||
#define PROGRESS_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QGraphicsScene>
|
||||
#include <QIcon>
|
||||
#include <QGraphicsView>
|
||||
#include <QGraphicsPixmapItem>
|
||||
#include <QVariantAnimation>
|
||||
#include <QGridLayout>
|
||||
|
||||
#include "../../global.h"
|
||||
|
||||
/**
|
||||
* @todo write docs
|
||||
*/
|
||||
class Progress : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Progress(quint16 p_size = 70, QWidget* parent = nullptr);
|
||||
~Progress();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private slots:
|
||||
void onValueChanged(const QVariant& value);
|
||||
|
||||
private:
|
||||
QGraphicsPixmapItem* pixmap;
|
||||
QGraphicsScene scene;
|
||||
QGraphicsView label;
|
||||
bool progress;
|
||||
QVariantAnimation animation;
|
||||
quint16 size;
|
||||
};
|
||||
|
||||
#endif // PROGRESS_H
|
29
ui/widgets/CMakeLists.txt
Normal file
@ -0,0 +1,29 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(squawkWidgets)
|
||||
|
||||
# Instruct CMake to run moc automatically when needed.
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
# Instruct CMake to create code from Qt designer ui files
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
# Find the QtWidgets library
|
||||
find_package(Qt5Widgets CONFIG REQUIRED)
|
||||
|
||||
add_subdirectory(vcard)
|
||||
|
||||
set(squawkWidgets_SRC
|
||||
conversation.cpp
|
||||
chat.cpp
|
||||
room.cpp
|
||||
newcontact.cpp
|
||||
accounts.cpp
|
||||
account.cpp
|
||||
joinconference.cpp
|
||||
)
|
||||
|
||||
# Tell CMake to create the helloworld executable
|
||||
add_library(squawkWidgets ${squawkWidgets_SRC})
|
||||
|
||||
# Use the Widgets module from Qt 5.
|
||||
target_link_libraries(squawkWidgets vCardUI)
|
||||
target_link_libraries(squawkWidgets Qt5::Widgets)
|
@ -39,6 +39,9 @@
|
||||
<property name="toolTip">
|
||||
<string>Your account login</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>john_smith1987</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
@ -53,6 +56,9 @@
|
||||
<property name="toolTip">
|
||||
<string>A server address of your account. Like 404.city or macaw.me</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>macaw.me</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
@ -83,6 +89,9 @@
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
@ -100,6 +109,9 @@
|
||||
<property name="toolTip">
|
||||
<string>Just a name how would you call this account, doesn't affect anything</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>John</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
|
@ -29,14 +29,13 @@ Accounts::Accounts(Models::Accounts* p_model, QWidget *parent) :
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
connect(m_ui->addButton, SIGNAL(clicked(bool)), this, SLOT(onAddButton(bool)));
|
||||
connect(m_ui->editButton, SIGNAL(clicked(bool)), this, SLOT(onEditButton(bool)));
|
||||
connect(m_ui->connectButton, SIGNAL(clicked(bool)), this, SLOT(onConnectButton(bool)));
|
||||
connect(m_ui->deleteButton, SIGNAL(clicked(bool)), this, SLOT(onDeleteButton(bool)));
|
||||
connect(m_ui->addButton, &QPushButton::clicked, this, &Accounts::onAddButton);
|
||||
connect(m_ui->editButton, &QPushButton::clicked, this, &Accounts::onEditButton);
|
||||
connect(m_ui->connectButton, &QPushButton::clicked, this, &Accounts::onConnectButton);
|
||||
connect(m_ui->deleteButton, &QPushButton::clicked, this, &Accounts::onDeleteButton);
|
||||
m_ui->tableView->setModel(model);
|
||||
connect(m_ui->tableView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
||||
this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&)));
|
||||
connect(p_model, SIGNAL(changed()), this, SLOT(updateConnectButton()));
|
||||
connect(m_ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Accounts::onSelectionChanged);
|
||||
connect(p_model, &Models::Accounts::changed, this, &Accounts::updateConnectButton);
|
||||
}
|
||||
|
||||
Accounts::~Accounts() = default;
|
||||
@ -44,8 +43,8 @@ Accounts::~Accounts() = default;
|
||||
void Accounts::onAddButton(bool clicked)
|
||||
{
|
||||
Account* acc = new Account();
|
||||
connect(acc, SIGNAL(accepted()), this, SLOT(onAccountAccepted()));
|
||||
connect(acc, SIGNAL(rejected()), this, SLOT(onAccountRejected()));
|
||||
connect(acc, &Account::accepted, this, &Accounts::onAccountAccepted);
|
||||
connect(acc, &Account::rejected, this, &Accounts::onAccountRejected);
|
||||
acc->exec();
|
||||
}
|
||||
|
||||
@ -84,8 +83,8 @@ void Accounts::onEditButton(bool clicked)
|
||||
{"resource", mAcc->getResource()}
|
||||
});
|
||||
acc->lockId();
|
||||
connect(acc, SIGNAL(accepted()), this, SLOT(onAccountAccepted()));
|
||||
connect(acc, SIGNAL(rejected()), this, SLOT(onAccountRejected()));
|
||||
connect(acc, &Account::accepted, this, &Accounts::onAccountAccepted);
|
||||
connect(acc, &Account::rejected, this, &Accounts::onAccountRejected);
|
||||
editing = true;
|
||||
acc->exec();
|
||||
}
|
||||
@ -120,13 +119,13 @@ void Accounts::updateConnectButton()
|
||||
}
|
||||
if (allConnected) {
|
||||
toDisconnect = true;
|
||||
m_ui->connectButton->setText("Disconnect");
|
||||
m_ui->connectButton->setText(tr("Disconnect"));
|
||||
} else {
|
||||
toDisconnect = false;
|
||||
m_ui->connectButton->setText("Connect");
|
||||
m_ui->connectButton->setText(tr("Connect"));
|
||||
}
|
||||
} else {
|
||||
m_ui->connectButton->setText("Connect");
|
||||
m_ui->connectButton->setText(tr("Connect"));
|
||||
toDisconnect = false;
|
||||
m_ui->connectButton->setEnabled(false);
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ Chat::Chat(Models::Contact* p_contact, QWidget* parent):
|
||||
updateState();
|
||||
setStatus(p_contact->getStatus());
|
||||
|
||||
connect(contact, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onContactChanged(Models::Item*, int, int)));
|
||||
connect(contact, &Models::Contact::childChanged, this, &Chat::onContactChanged);
|
||||
|
||||
line->setMyName(p_contact->getAccountName());
|
||||
}
|
||||
@ -56,7 +56,7 @@ void Chat::updateState()
|
||||
{
|
||||
Shared::Availability av = contact->getAvailability();
|
||||
statusIcon->setPixmap(Shared::availabilityIcon(av, true).pixmap(40));
|
||||
statusIcon->setToolTip(Shared::availabilityNames[av]);
|
||||
statusIcon->setToolTip(QCoreApplication::translate("Global", Shared::availabilityNames[av].toLatin1()));
|
||||
}
|
||||
|
||||
void Chat::handleSendMessage(const QString& text)
|
||||
|
@ -59,15 +59,15 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co
|
||||
statusIcon = m_ui->statusIcon;
|
||||
statusLabel = m_ui->statusLabel;
|
||||
|
||||
connect(&ker, SIGNAL(enterPressed()), this, SLOT(onEnterPressed()));
|
||||
connect(&res, SIGNAL(resized()), this, SLOT(onScrollResize()));
|
||||
connect(&vis, SIGNAL(shown()), this, SLOT(onScrollResize()));
|
||||
connect(&vis, SIGNAL(hidden()), this, SLOT(onScrollResize()));
|
||||
connect(m_ui->sendButton, SIGNAL(clicked(bool)), this, SLOT(onEnterPressed()));
|
||||
connect(line, SIGNAL(resize(int)), this, SLOT(onMessagesResize(int)));
|
||||
connect(line, SIGNAL(downloadFile(const QString&, const QString&)), this, SIGNAL(downloadFile(const QString&, const QString&)));
|
||||
connect(line, SIGNAL(requestLocalFile(const QString&, const QString&)), this, SIGNAL(requestLocalFile(const QString&, const QString&)));
|
||||
connect(m_ui->attachButton, SIGNAL(clicked(bool)), this, SLOT(onAttach()));
|
||||
connect(&ker, &KeyEnterReceiver::enterPressed, this, &Conversation::onEnterPressed);
|
||||
connect(&res, &Resizer::resized, this, &Conversation::onScrollResize);
|
||||
connect(&vis, &VisibilityCatcher::shown, this, &Conversation::onScrollResize);
|
||||
connect(&vis, &VisibilityCatcher::hidden, this, &Conversation::onScrollResize);
|
||||
connect(m_ui->sendButton, &QPushButton::clicked, this, &Conversation::onEnterPressed);
|
||||
connect(line, &MessageLine::resize, this, &Conversation::onMessagesResize);
|
||||
connect(line, &MessageLine::downloadFile, this, &Conversation::downloadFile);
|
||||
connect(line, &MessageLine::requestLocalFile, this, &Conversation::requestLocalFile);
|
||||
connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach);
|
||||
|
||||
m_ui->messageEditor->installEventFilter(&ker);
|
||||
|
||||
@ -76,7 +76,7 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co
|
||||
vs->installEventFilter(&vis);
|
||||
vs->setBackgroundRole(QPalette::Base);
|
||||
vs->setAutoFillBackground(true);
|
||||
connect(vs, SIGNAL(valueChanged(int)), this, SLOT(onSliderValueChanged(int)));
|
||||
connect(vs, &QScrollBar::valueChanged, this, &Conversation::onSliderValueChanged);
|
||||
m_ui->scrollArea->installEventFilter(&res);
|
||||
|
||||
applyVisualEffects();
|
||||
@ -257,11 +257,11 @@ void Conversation::showEvent(QShowEvent* event)
|
||||
|
||||
void Conversation::onAttach()
|
||||
{
|
||||
QFileDialog* d = new QFileDialog(this, "Chose a file to send");
|
||||
QFileDialog* d = new QFileDialog(this, tr("Chose a file to send"));
|
||||
d->setFileMode(QFileDialog::ExistingFile);
|
||||
|
||||
connect(d, SIGNAL(accepted()), this, SLOT(onFileSelected()));
|
||||
connect(d, SIGNAL(rejected()), d, SLOT(deleteLater()));
|
||||
connect(d, &QFileDialog::accepted, this, &Conversation::onFileSelected);
|
||||
connect(d, &QFileDialog::rejected, d, &QFileDialog::deleteLater);
|
||||
|
||||
d->show();
|
||||
}
|
||||
@ -317,7 +317,7 @@ void Conversation::addAttachedFile(const QString& path)
|
||||
|
||||
Badge* badge = new Badge(path, info.fileName(), QIcon::fromTheme(type.iconName()));
|
||||
|
||||
connect(badge, SIGNAL(close()), this, SLOT(onBadgeClose()));
|
||||
connect(badge, &Badge::close, this, &Conversation::onBadgeClose);
|
||||
filesToAttach.push_back(badge); //TODO neet to check if there are any duplicated ids
|
||||
filesLayout->addWidget(badge);
|
||||
if (filesLayout->count() == 1) {
|
||||
|
@ -445,6 +445,9 @@
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Type your message here...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -26,7 +26,7 @@ Room::Room(Models::Room* p_room, QWidget* parent):
|
||||
line->setMyName(room->getNick());
|
||||
setStatus(room->getSubject());
|
||||
|
||||
connect(room, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onRoomChanged(Models::Item*, int, int)));
|
||||
connect(room, &Models::Room::childChanged, this, &Room::onRoomChanged);
|
||||
}
|
||||
|
||||
Room::~Room()
|
||||
|
22
ui/widgets/vcard/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(vCardUI)
|
||||
|
||||
# Instruct CMake to run moc automatically when needed.
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
# Instruct CMake to create code from Qt designer ui files
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
# Find the QtWidgets library
|
||||
find_package(Qt5Widgets CONFIG REQUIRED)
|
||||
|
||||
set(vCardUI_SRC
|
||||
vcard.cpp
|
||||
emailsmodel.cpp
|
||||
phonesmodel.cpp
|
||||
)
|
||||
|
||||
# Tell CMake to create the helloworld executable
|
||||
add_library(vCardUI ${vCardUI_SRC})
|
||||
|
||||
# Use the Widgets module from Qt 5.
|
||||
target_link_libraries(vCardUI Qt5::Widgets)
|
205
ui/widgets/vcard/emailsmodel.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Squawk messenger.
|
||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "emailsmodel.h"
|
||||
|
||||
UI::VCard::EMailsModel::EMailsModel(bool p_edit, QObject* parent):
|
||||
QAbstractTableModel(parent),
|
||||
edit(p_edit),
|
||||
deque()
|
||||
{
|
||||
}
|
||||
|
||||
int UI::VCard::EMailsModel::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
int UI::VCard::EMailsModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
return deque.size();
|
||||
}
|
||||
|
||||
QVariant UI::VCard::EMailsModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (index.isValid()) {
|
||||
int col = index.column();
|
||||
switch (col) {
|
||||
case 0:
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
return deque[index.row()].address;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[deque[index.row()].role].toStdString().c_str());
|
||||
case Qt::EditRole:
|
||||
return deque[index.row()].role;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return QVariant();
|
||||
case Qt::DecorationRole:
|
||||
if (deque[index.row()].prefered) {
|
||||
return Shared::icon("favorite", false);
|
||||
}
|
||||
return QVariant();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags UI::VCard::EMailsModel::flags(const QModelIndex& index) const
|
||||
{
|
||||
Qt::ItemFlags f = QAbstractTableModel::flags(index);
|
||||
if (edit && index.column() != 2) {
|
||||
f = Qt::ItemIsEditable | f;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
bool UI::VCard::EMailsModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||
{
|
||||
if (role == Qt::EditRole && checkIndex(index)) {
|
||||
Shared::VCard::Email& item = deque[index.row()];
|
||||
switch (index.column()) {
|
||||
case 0:
|
||||
item.address = value.toString();
|
||||
return true;
|
||||
case 1: {
|
||||
quint8 newRole = value.toUInt();
|
||||
if (newRole > Shared::VCard::Email::work) {
|
||||
return false;
|
||||
}
|
||||
item.role = static_cast<Shared::VCard::Email::Role>(newRole);
|
||||
return true;
|
||||
}
|
||||
case 2: {
|
||||
bool newDef = value.toBool();
|
||||
if (newDef != item.prefered) {
|
||||
if (newDef) {
|
||||
//dropPrefered();
|
||||
}
|
||||
item.prefered = newDef;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool UI::VCard::EMailsModel::dropPrefered()
|
||||
{
|
||||
bool dropped = false;
|
||||
int i = 0;
|
||||
for (Shared::VCard::Email& email : deque) {
|
||||
if (email.prefered) {
|
||||
email.prefered = false;
|
||||
QModelIndex ci = createIndex(i, 2, &email);
|
||||
emit dataChanged(ci, ci);
|
||||
dropped = true;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return dropped;
|
||||
}
|
||||
|
||||
QModelIndex UI::VCard::EMailsModel::addNewEmptyLine()
|
||||
{
|
||||
beginInsertRows(QModelIndex(), deque.size(), deque.size());
|
||||
deque.emplace_back("");
|
||||
endInsertRows();
|
||||
return createIndex(deque.size() - 1, 0, &(deque.back()));
|
||||
}
|
||||
|
||||
bool UI::VCard::EMailsModel::isPreferred(int row) const
|
||||
{
|
||||
if (row < deque.size()) {
|
||||
return deque[row].prefered;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void UI::VCard::EMailsModel::removeLines(int index, int count)
|
||||
{
|
||||
if (index < deque.size()) {
|
||||
int maxCount = deque.size() - index;
|
||||
if (count > maxCount) {
|
||||
count = maxCount;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
beginRemoveRows(QModelIndex(), index, index + count - 1);
|
||||
std::deque<Shared::VCard::Email>::const_iterator itr = deque.begin() + index;
|
||||
std::deque<Shared::VCard::Email>::const_iterator end = itr + count;
|
||||
deque.erase(itr, end);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UI::VCard::EMailsModel::getEmails(std::deque<Shared::VCard::Email>& emails) const
|
||||
{
|
||||
for (const Shared::VCard::Email& my : deque) {
|
||||
emails.emplace_back(my);
|
||||
}
|
||||
}
|
||||
|
||||
void UI::VCard::EMailsModel::setEmails(const std::deque<Shared::VCard::Email>& emails)
|
||||
{
|
||||
if (deque.size() > 0) {
|
||||
removeLines(0, deque.size());
|
||||
}
|
||||
|
||||
if (emails.size() > 0) {
|
||||
beginInsertRows(QModelIndex(), 0, emails.size() - 1);
|
||||
for (const Shared::VCard::Email& comming : emails) {
|
||||
deque.emplace_back(comming);
|
||||
}
|
||||
endInsertRows();
|
||||
}
|
||||
}
|
||||
|
||||
void UI::VCard::EMailsModel::revertPreferred(int row)
|
||||
{
|
||||
setData(createIndex(row, 2), !isPreferred(row));
|
||||
}
|
||||
|
||||
QString UI::VCard::EMailsModel::getEmail(int row) const
|
||||
{
|
||||
return deque[row].address;
|
||||
}
|
64
ui/widgets/vcard/emailsmodel.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Squawk messenger.
|
||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UI_VCARD_EMAILSMODEL_H
|
||||
#define UI_VCARD_EMAILSMODEL_H
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QIcon>
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "global.h"
|
||||
|
||||
namespace UI {
|
||||
namespace VCard {
|
||||
|
||||
class EMailsModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
EMailsModel(bool edit = false, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
bool isPreferred(int row) const;
|
||||
|
||||
void removeLines(int index, int count);
|
||||
void setEmails(const std::deque<Shared::VCard::Email>& emails);
|
||||
void getEmails(std::deque<Shared::VCard::Email>& emails) const;
|
||||
QString getEmail(int row) const;
|
||||
|
||||
public slots:
|
||||
QModelIndex addNewEmptyLine();
|
||||
void revertPreferred(int row);
|
||||
|
||||
private:
|
||||
bool edit;
|
||||
std::deque<Shared::VCard::Email> deque;
|
||||
|
||||
private:
|
||||
bool dropPrefered();
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif // UI_VCARD_EMAILSMODEL_H
|
222
ui/widgets/vcard/phonesmodel.cpp
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Squawk messenger.
|
||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "phonesmodel.h"
|
||||
|
||||
UI::VCard::PhonesModel::PhonesModel(bool p_edit, QObject* parent):
|
||||
QAbstractTableModel(parent),
|
||||
edit(p_edit),
|
||||
deque()
|
||||
{
|
||||
}
|
||||
|
||||
int UI::VCard::PhonesModel::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
int UI::VCard::PhonesModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
return deque.size();
|
||||
}
|
||||
|
||||
QVariant UI::VCard::PhonesModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (index.isValid()) {
|
||||
int col = index.column();
|
||||
switch (col) {
|
||||
case 0:
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
return deque[index.row()].number;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return QCoreApplication::translate("Global", Shared::VCard::Phone::roleNames[deque[index.row()].role].toStdString().c_str());
|
||||
case Qt::EditRole:
|
||||
return deque[index.row()].role;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[deque[index.row()].type].toStdString().c_str());
|
||||
case Qt::EditRole:
|
||||
return deque[index.row()].type;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return QVariant();
|
||||
case Qt::DecorationRole:
|
||||
if (deque[index.row()].prefered) {
|
||||
return Shared::icon("favorite", false);
|
||||
}
|
||||
return QVariant();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QModelIndex UI::VCard::PhonesModel::addNewEmptyLine()
|
||||
{
|
||||
beginInsertRows(QModelIndex(), deque.size(), deque.size());
|
||||
deque.emplace_back("", Shared::VCard::Phone::other);
|
||||
endInsertRows();
|
||||
return createIndex(deque.size() - 1, 0, &(deque.back()));
|
||||
}
|
||||
|
||||
Qt::ItemFlags UI::VCard::PhonesModel::flags(const QModelIndex& index) const
|
||||
{
|
||||
Qt::ItemFlags f = QAbstractTableModel::flags(index);
|
||||
if (edit && index.column() != 3) {
|
||||
f = Qt::ItemIsEditable | f;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
bool UI::VCard::PhonesModel::dropPrefered()
|
||||
{
|
||||
bool dropped = false;
|
||||
int i = 0;
|
||||
for (Shared::VCard::Phone& phone : deque) {
|
||||
if (phone.prefered) {
|
||||
phone.prefered = false;
|
||||
QModelIndex ci = createIndex(i, 2, &phone);
|
||||
emit dataChanged(ci, ci);
|
||||
dropped = true;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return dropped;
|
||||
}
|
||||
|
||||
void UI::VCard::PhonesModel::getPhones(std::deque<Shared::VCard::Phone>& phones) const
|
||||
{
|
||||
for (const Shared::VCard::Phone& my : deque) {
|
||||
phones.emplace_back(my);
|
||||
}
|
||||
}
|
||||
|
||||
bool UI::VCard::PhonesModel::isPreferred(int row) const
|
||||
{
|
||||
if (row < deque.size()) {
|
||||
return deque[row].prefered;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void UI::VCard::PhonesModel::removeLines(int index, int count)
|
||||
{
|
||||
if (index < deque.size()) {
|
||||
int maxCount = deque.size() - index;
|
||||
if (count > maxCount) {
|
||||
count = maxCount;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
beginRemoveRows(QModelIndex(), index, index + count - 1);
|
||||
std::deque<Shared::VCard::Phone>::const_iterator itr = deque.begin() + index;
|
||||
std::deque<Shared::VCard::Phone>::const_iterator end = itr + count;
|
||||
deque.erase(itr, end);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UI::VCard::PhonesModel::revertPreferred(int row)
|
||||
{
|
||||
setData(createIndex(row, 3), !isPreferred(row));
|
||||
}
|
||||
|
||||
bool UI::VCard::PhonesModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||
{
|
||||
if (role == Qt::EditRole && checkIndex(index)) {
|
||||
Shared::VCard::Phone& item = deque[index.row()];
|
||||
switch (index.column()) {
|
||||
case 0:
|
||||
item.number = value.toString();
|
||||
return true;
|
||||
case 1: {
|
||||
quint8 newRole = value.toUInt();
|
||||
if (newRole > Shared::VCard::Phone::work) {
|
||||
return false;
|
||||
}
|
||||
item.role = static_cast<Shared::VCard::Phone::Role>(newRole);
|
||||
return true;
|
||||
}
|
||||
case 2: {
|
||||
quint8 newType = value.toUInt();
|
||||
if (newType > Shared::VCard::Phone::other) {
|
||||
return false;
|
||||
}
|
||||
item.type = static_cast<Shared::VCard::Phone::Type>(newType);
|
||||
return true;
|
||||
}
|
||||
case 3: {
|
||||
bool newDef = value.toBool();
|
||||
if (newDef != item.prefered) {
|
||||
if (newDef) {
|
||||
//dropPrefered();
|
||||
}
|
||||
item.prefered = newDef;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void UI::VCard::PhonesModel::setPhones(const std::deque<Shared::VCard::Phone>& phones)
|
||||
{
|
||||
if (deque.size() > 0) {
|
||||
removeLines(0, deque.size());
|
||||
}
|
||||
|
||||
if (phones.size() > 0) {
|
||||
beginInsertRows(QModelIndex(), 0, phones.size() - 1);
|
||||
for (const Shared::VCard::Phone& comming : phones) {
|
||||
deque.emplace_back(comming);
|
||||
}
|
||||
endInsertRows();
|
||||
}
|
||||
}
|
||||
|
||||
QString UI::VCard::PhonesModel::getPhone(int row) const
|
||||
{
|
||||
return deque[row].number;
|
||||
}
|
65
ui/widgets/vcard/phonesmodel.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Squawk messenger.
|
||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UI_VCARD_PHONESMODEL_H
|
||||
#define UI_VCARD_PHONESMODEL_H
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QIcon>
|
||||
|
||||
#include "global.h"
|
||||
|
||||
namespace UI {
|
||||
namespace VCard {
|
||||
|
||||
/**
|
||||
* @todo write docs
|
||||
*/
|
||||
class PhonesModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PhonesModel(bool edit = false, QObject *parent = nullptr);
|
||||
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
int columnCount(const QModelIndex& parent) const override;
|
||||
int rowCount(const QModelIndex& parent) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
bool isPreferred(int row) const;
|
||||
|
||||
void removeLines(int index, int count);
|
||||
void setPhones(const std::deque<Shared::VCard::Phone>& phones);
|
||||
void getPhones(std::deque<Shared::VCard::Phone>& phones) const;
|
||||
QString getPhone(int row) const;
|
||||
|
||||
public slots:
|
||||
QModelIndex addNewEmptyLine();
|
||||
void revertPreferred(int row);
|
||||
|
||||
private:
|
||||
bool edit;
|
||||
std::deque<Shared::VCard::Phone> deque;
|
||||
|
||||
private:
|
||||
bool dropPrefered();
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif // UI_VCARD_PHONESMODEL_H
|
460
ui/widgets/vcard/vcard.cpp
Normal file
@ -0,0 +1,460 @@
|
||||
/*
|
||||
* Squawk messenger.
|
||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "vcard.h"
|
||||
#include "ui_vcard.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
const std::set<QString> VCard::supportedTypes = {"image/jpeg", "image/png"};
|
||||
|
||||
VCard::VCard(const QString& jid, bool edit, QWidget* parent):
|
||||
QWidget(parent),
|
||||
m_ui(new Ui::VCard()),
|
||||
avatarButtonMargins(),
|
||||
avatarMenu(nullptr),
|
||||
editable(edit),
|
||||
currentAvatarType(Shared::Avatar::empty),
|
||||
currentAvatarPath(""),
|
||||
progress(new Progress(100)),
|
||||
progressLabel(new QLabel()),
|
||||
overlay(new QWidget()),
|
||||
contextMenu(new QMenu()),
|
||||
emails(edit),
|
||||
phones(edit),
|
||||
roleDelegate(new ComboboxDelegate()),
|
||||
phoneTypeDelegate(new ComboboxDelegate())
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
m_ui->jabberID->setText(jid);
|
||||
m_ui->jabberID->setReadOnly(true);
|
||||
|
||||
QAction* setAvatar = m_ui->actionSetAvatar;
|
||||
QAction* clearAvatar = m_ui->actionClearAvatar;
|
||||
|
||||
connect(setAvatar, &QAction::triggered, this, &VCard::onSetAvatar);
|
||||
connect(clearAvatar, &QAction::triggered, this, &VCard::onClearAvatar);
|
||||
|
||||
setAvatar->setEnabled(true);
|
||||
clearAvatar->setEnabled(false);
|
||||
|
||||
roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[0].toStdString().c_str()));
|
||||
roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[1].toStdString().c_str()));
|
||||
roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[2].toStdString().c_str()));
|
||||
|
||||
phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[0].toStdString().c_str()));
|
||||
phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[1].toStdString().c_str()));
|
||||
phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[2].toStdString().c_str()));
|
||||
phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[3].toStdString().c_str()));
|
||||
phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[4].toStdString().c_str()));
|
||||
phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[5].toStdString().c_str()));
|
||||
phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[6].toStdString().c_str()));
|
||||
|
||||
m_ui->emailsView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_ui->emailsView->setModel(&emails);
|
||||
m_ui->emailsView->setItemDelegateForColumn(1, roleDelegate);
|
||||
m_ui->emailsView->setColumnWidth(2, 25);
|
||||
m_ui->emailsView->horizontalHeader()->setStretchLastSection(false);
|
||||
m_ui->emailsView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
|
||||
m_ui->phonesView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_ui->phonesView->setModel(&phones);
|
||||
m_ui->phonesView->setItemDelegateForColumn(1, roleDelegate);
|
||||
m_ui->phonesView->setItemDelegateForColumn(2, phoneTypeDelegate);
|
||||
m_ui->phonesView->setColumnWidth(3, 25);
|
||||
m_ui->phonesView->horizontalHeader()->setStretchLastSection(false);
|
||||
m_ui->phonesView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
|
||||
connect(m_ui->phonesView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu);
|
||||
connect(m_ui->emailsView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu);
|
||||
|
||||
if (edit) {
|
||||
avatarMenu = new QMenu();
|
||||
m_ui->avatarButton->setMenu(avatarMenu);
|
||||
avatarMenu->addAction(setAvatar);
|
||||
avatarMenu->addAction(clearAvatar);
|
||||
m_ui->title->setText(tr("Account %1 card").arg(jid));
|
||||
} else {
|
||||
m_ui->buttonBox->hide();
|
||||
m_ui->fullName->setReadOnly(true);
|
||||
m_ui->firstName->setReadOnly(true);
|
||||
m_ui->middleName->setReadOnly(true);
|
||||
m_ui->lastName->setReadOnly(true);
|
||||
m_ui->nickName->setReadOnly(true);
|
||||
m_ui->birthday->setReadOnly(true);
|
||||
m_ui->organizationName->setReadOnly(true);
|
||||
m_ui->organizationDepartment->setReadOnly(true);
|
||||
m_ui->organizationTitle->setReadOnly(true);
|
||||
m_ui->organizationRole->setReadOnly(true);
|
||||
m_ui->description->setReadOnly(true);
|
||||
m_ui->url->setReadOnly(true);
|
||||
m_ui->title->setText(tr("Contact %1 card").arg(jid));
|
||||
}
|
||||
|
||||
connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &VCard::onButtonBoxAccepted);
|
||||
connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &VCard::close);
|
||||
|
||||
avatarButtonMargins = m_ui->avatarButton->size();
|
||||
|
||||
int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height();
|
||||
m_ui->avatarButton->setIconSize(QSize(height, height));
|
||||
|
||||
QGridLayout* gr = static_cast<QGridLayout*>(layout());
|
||||
gr->addWidget(overlay, 0, 0, 4, 1);
|
||||
QVBoxLayout* nl = new QVBoxLayout();
|
||||
QGraphicsOpacityEffect* opacity = new QGraphicsOpacityEffect();
|
||||
opacity->setOpacity(0.8);
|
||||
overlay->setLayout(nl);
|
||||
overlay->setBackgroundRole(QPalette::Base);
|
||||
overlay->setAutoFillBackground(true);
|
||||
overlay->setGraphicsEffect(opacity);
|
||||
progressLabel->setAlignment(Qt::AlignCenter);
|
||||
progressLabel->setStyleSheet("font: 16pt");
|
||||
nl->addStretch();
|
||||
nl->addWidget(progress);
|
||||
nl->addWidget(progressLabel);
|
||||
nl->addStretch();
|
||||
overlay->hide();
|
||||
}
|
||||
|
||||
VCard::~VCard()
|
||||
{
|
||||
if (editable) {
|
||||
avatarMenu->deleteLater();
|
||||
}
|
||||
|
||||
phoneTypeDelegate->deleteLater();
|
||||
roleDelegate->deleteLater();
|
||||
contextMenu->deleteLater();
|
||||
}
|
||||
|
||||
void VCard::setVCard(const QString& jid, const Shared::VCard& card)
|
||||
{
|
||||
m_ui->jabberID->setText(jid);
|
||||
setVCard(card);
|
||||
}
|
||||
|
||||
void VCard::setVCard(const Shared::VCard& card)
|
||||
{
|
||||
m_ui->fullName->setText(card.getFullName());
|
||||
m_ui->firstName->setText(card.getFirstName());
|
||||
m_ui->middleName->setText(card.getMiddleName());
|
||||
m_ui->lastName->setText(card.getLastName());
|
||||
m_ui->nickName->setText(card.getNickName());
|
||||
m_ui->birthday->setDate(card.getBirthday());
|
||||
m_ui->organizationName->setText(card.getOrgName());
|
||||
m_ui->organizationDepartment->setText(card.getOrgUnit());
|
||||
m_ui->organizationTitle->setText(card.getOrgTitle());
|
||||
m_ui->organizationRole->setText(card.getOrgRole());
|
||||
m_ui->description->setText(card.getDescription());
|
||||
m_ui->url->setText(card.getUrl());
|
||||
|
||||
QDateTime receivingTime = card.getReceivingTime();
|
||||
m_ui->receivingTimeLabel->setText(tr("Received %1 at %2").arg(receivingTime.date().toString()).arg(receivingTime.time().toString()));
|
||||
currentAvatarType = card.getAvatarType();
|
||||
currentAvatarPath = card.getAvatarPath();
|
||||
|
||||
updateAvatar();
|
||||
|
||||
const std::deque<Shared::VCard::Email>& ems = card.getEmails();
|
||||
const std::deque<Shared::VCard::Phone>& phs = card.getPhones();
|
||||
emails.setEmails(ems);
|
||||
phones.setPhones(phs);
|
||||
}
|
||||
|
||||
QString VCard::getJid() const
|
||||
{
|
||||
return m_ui->jabberID->text();
|
||||
}
|
||||
|
||||
void VCard::onButtonBoxAccepted()
|
||||
{
|
||||
Shared::VCard card;
|
||||
card.setFullName(m_ui->fullName->text());
|
||||
card.setFirstName(m_ui->firstName->text());
|
||||
card.setMiddleName(m_ui->middleName->text());
|
||||
card.setLastName(m_ui->lastName->text());
|
||||
card.setNickName(m_ui->nickName->text());
|
||||
card.setBirthday(m_ui->birthday->date());
|
||||
card.setDescription(m_ui->description->toPlainText());
|
||||
card.setUrl(m_ui->url->text());
|
||||
card.setOrgName(m_ui->organizationName->text());
|
||||
card.setOrgUnit(m_ui->organizationDepartment->text());
|
||||
card.setOrgRole(m_ui->organizationRole->text());
|
||||
card.setOrgTitle(m_ui->organizationTitle->text());
|
||||
card.setAvatarPath(currentAvatarPath);
|
||||
card.setAvatarType(currentAvatarType);
|
||||
|
||||
emails.getEmails(card.getEmails());
|
||||
phones.getPhones(card.getPhones());
|
||||
|
||||
emit saveVCard(card);
|
||||
}
|
||||
|
||||
void VCard::onClearAvatar()
|
||||
{
|
||||
currentAvatarType = Shared::Avatar::empty;
|
||||
currentAvatarPath = "";
|
||||
|
||||
updateAvatar();
|
||||
}
|
||||
|
||||
void VCard::onSetAvatar()
|
||||
{
|
||||
QFileDialog* d = new QFileDialog(this, tr("Chose your new avatar"));
|
||||
d->setFileMode(QFileDialog::ExistingFile);
|
||||
d->setNameFilter(tr("Images (*.png *.jpg *.jpeg)"));
|
||||
|
||||
connect(d, &QFileDialog::accepted, this, &VCard::onAvatarSelected);
|
||||
connect(d, &QFileDialog::rejected, d, &QFileDialog::deleteLater);
|
||||
|
||||
d->show();
|
||||
}
|
||||
|
||||
void VCard::updateAvatar()
|
||||
{
|
||||
int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height();
|
||||
switch (currentAvatarType) {
|
||||
case Shared::Avatar::empty:
|
||||
m_ui->avatarButton->setIcon(Shared::icon("user", true));
|
||||
m_ui->avatarButton->setIconSize(QSize(height, height));
|
||||
m_ui->actionClearAvatar->setEnabled(false);
|
||||
break;
|
||||
case Shared::Avatar::autocreated:
|
||||
case Shared::Avatar::valid:
|
||||
QPixmap pixmap(currentAvatarPath);
|
||||
qreal h = pixmap.height();
|
||||
qreal w = pixmap.width();
|
||||
qreal aspectRatio = w / h;
|
||||
m_ui->avatarButton->setIconSize(QSize(height * aspectRatio, height));
|
||||
m_ui->avatarButton->setIcon(QIcon(currentAvatarPath));
|
||||
m_ui->actionClearAvatar->setEnabled(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VCard::onAvatarSelected()
|
||||
{
|
||||
QFileDialog* d = static_cast<QFileDialog*>(sender());
|
||||
QMimeDatabase db;
|
||||
QString path = d->selectedFiles().front();
|
||||
QMimeType type = db.mimeTypeForFile(path);
|
||||
d->deleteLater();
|
||||
|
||||
if (supportedTypes.find(type.name()) == supportedTypes.end()) {
|
||||
qDebug() << "Selected for avatar file is not supported, skipping";
|
||||
} else {
|
||||
QImage src(path);
|
||||
QImage dst;
|
||||
if (src.width() > 160 || src.height() > 160) {
|
||||
dst = src.scaled(160, 160, Qt::KeepAspectRatio);
|
||||
}
|
||||
QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + m_ui->jabberID->text() + ".temp." + type.preferredSuffix();
|
||||
QFile oldTemp(path);
|
||||
if (oldTemp.exists()) {
|
||||
if (!oldTemp.remove()) {
|
||||
qDebug() << "Error removing old temp avatar" << path;
|
||||
return;
|
||||
}
|
||||
}
|
||||
bool success = dst.save(path);
|
||||
if (success) {
|
||||
currentAvatarPath = path;
|
||||
currentAvatarType = Shared::Avatar::valid;
|
||||
|
||||
updateAvatar();
|
||||
} else {
|
||||
qDebug() << "couldn't save avatar" << path << ", skipping";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VCard::showProgress(const QString& line)
|
||||
{
|
||||
progressLabel->setText(line);
|
||||
overlay->show();
|
||||
progress->start();
|
||||
}
|
||||
|
||||
void VCard::hideProgress()
|
||||
{
|
||||
overlay->hide();
|
||||
progress->stop();
|
||||
}
|
||||
|
||||
void VCard::onContextMenu(const QPoint& point)
|
||||
{
|
||||
contextMenu->clear();
|
||||
bool hasMenu = false;
|
||||
QAbstractItemView* snd = static_cast<QAbstractItemView*>(sender());
|
||||
if (snd == m_ui->emailsView) {
|
||||
hasMenu = true;
|
||||
if (editable) {
|
||||
QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add email address"));
|
||||
connect(add, &QAction::triggered, this, &VCard::onAddEmail);
|
||||
|
||||
QItemSelectionModel* sm = m_ui->emailsView->selectionModel();
|
||||
int selectionSize = sm->selectedRows().size();
|
||||
|
||||
if (selectionSize > 0) {
|
||||
if (selectionSize == 1) {
|
||||
int row = sm->selectedRows().at(0).row();
|
||||
if (emails.isPreferred(row)) {
|
||||
QAction* rev = contextMenu->addAction(Shared::icon("unfavorite"), tr("Unset this email as preferred"));
|
||||
connect(rev, &QAction::triggered, std::bind(&UI::VCard::EMailsModel::revertPreferred, &emails, row));
|
||||
} else {
|
||||
QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this email as preferred"));
|
||||
connect(rev, &QAction::triggered, std::bind(&UI::VCard::EMailsModel::revertPreferred, &emails, row));
|
||||
}
|
||||
}
|
||||
|
||||
QAction* del = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove selected email addresses"));
|
||||
connect(del, &QAction::triggered, this, &VCard::onRemoveEmail);
|
||||
}
|
||||
}
|
||||
|
||||
QAction* cp = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected emails to clipboard"));
|
||||
connect(cp, &QAction::triggered, this, &VCard::onCopyEmail);
|
||||
} else if (snd == m_ui->phonesView) {
|
||||
hasMenu = true;
|
||||
if (editable) {
|
||||
QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add phone number"));
|
||||
connect(add, &QAction::triggered, this, &VCard::onAddPhone);
|
||||
|
||||
QItemSelectionModel* sm = m_ui->phonesView->selectionModel();
|
||||
int selectionSize = sm->selectedRows().size();
|
||||
|
||||
if (selectionSize > 0) {
|
||||
if (selectionSize == 1) {
|
||||
int row = sm->selectedRows().at(0).row();
|
||||
if (phones.isPreferred(row)) {
|
||||
QAction* rev = contextMenu->addAction(Shared::icon("view-media-favorite"), tr("Unset this phone as preferred"));
|
||||
connect(rev, &QAction::triggered, std::bind(&UI::VCard::PhonesModel::revertPreferred, &phones, row));
|
||||
} else {
|
||||
QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this phone as preferred"));
|
||||
connect(rev, &QAction::triggered, std::bind(&UI::VCard::PhonesModel::revertPreferred, &phones, row));
|
||||
}
|
||||
}
|
||||
|
||||
QAction* del = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove selected phone numbers"));
|
||||
connect(del, &QAction::triggered, this, &VCard::onRemovePhone);
|
||||
}
|
||||
}
|
||||
|
||||
QAction* cp = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected phones to clipboard"));
|
||||
connect(cp, &QAction::triggered, this, &VCard::onCopyPhone);
|
||||
}
|
||||
|
||||
if (hasMenu) {
|
||||
contextMenu->popup(snd->viewport()->mapToGlobal(point));
|
||||
}
|
||||
}
|
||||
|
||||
void VCard::onAddEmail()
|
||||
{
|
||||
QModelIndex index = emails.addNewEmptyLine();
|
||||
m_ui->emailsView->setCurrentIndex(index);
|
||||
m_ui->emailsView->edit(index);
|
||||
}
|
||||
|
||||
void VCard::onAddAddress()
|
||||
{
|
||||
|
||||
}
|
||||
void VCard::onAddPhone()
|
||||
{
|
||||
QModelIndex index = phones.addNewEmptyLine();
|
||||
m_ui->phonesView->setCurrentIndex(index);
|
||||
m_ui->phonesView->edit(index);
|
||||
}
|
||||
void VCard::onRemoveAddress()
|
||||
{
|
||||
}
|
||||
void VCard::onRemoveEmail()
|
||||
{
|
||||
QItemSelection selection(m_ui->emailsView->selectionModel()->selection());
|
||||
|
||||
QList<int> rows;
|
||||
for (const QModelIndex& index : selection.indexes()) {
|
||||
rows.append(index.row());
|
||||
}
|
||||
|
||||
std::sort(rows.begin(), rows.end());
|
||||
|
||||
int prev = -1;
|
||||
for (int i = rows.count() - 1; i >= 0; i -= 1) {
|
||||
int current = rows[i];
|
||||
if (current != prev) {
|
||||
emails.removeLines(current, 1);
|
||||
prev = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VCard::onRemovePhone()
|
||||
{
|
||||
QItemSelection selection(m_ui->phonesView->selectionModel()->selection());
|
||||
|
||||
QList<int> rows;
|
||||
for (const QModelIndex& index : selection.indexes()) {
|
||||
rows.append(index.row());
|
||||
}
|
||||
|
||||
std::sort(rows.begin(), rows.end());
|
||||
|
||||
int prev = -1;
|
||||
for (int i = rows.count() - 1; i >= 0; i -= 1) {
|
||||
int current = rows[i];
|
||||
if (current != prev) {
|
||||
phones.removeLines(current, 1);
|
||||
prev = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VCard::onCopyEmail()
|
||||
{
|
||||
QList<QModelIndex> selection(m_ui->emailsView->selectionModel()->selectedRows());
|
||||
|
||||
QList<QString> addrs;
|
||||
for (const QModelIndex& index : selection) {
|
||||
addrs.push_back(emails.getEmail(index.row()));
|
||||
}
|
||||
|
||||
QString list = addrs.join("\n");
|
||||
|
||||
QClipboard* cb = QApplication::clipboard();
|
||||
cb->setText(list);
|
||||
}
|
||||
|
||||
void VCard::onCopyPhone()
|
||||
{
|
||||
QList<QModelIndex> selection(m_ui->phonesView->selectionModel()->selectedRows());
|
||||
|
||||
QList<QString> phs;
|
||||
for (const QModelIndex& index : selection) {
|
||||
phs.push_back(phones.getPhone(index.row()));
|
||||
}
|
||||
|
||||
QString list = phs.join("\n");
|
||||
|
||||
QClipboard* cb = QApplication::clipboard();
|
||||
cb->setText(list);
|
||||
}
|
106
ui/widgets/vcard/vcard.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Squawk messenger.
|
||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef VCARD_H
|
||||
#define VCARD_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QScopedPointer>
|
||||
#include <QPixmap>
|
||||
#include <QMenu>
|
||||
#include <QFileDialog>
|
||||
#include <QMimeDatabase>
|
||||
#include <QImage>
|
||||
#include <QStandardPaths>
|
||||
#include <QLabel>
|
||||
#include <QGraphicsOpacityEffect>
|
||||
#include <QVBoxLayout>
|
||||
#include <QMenu>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "global.h"
|
||||
#include "emailsmodel.h"
|
||||
#include "phonesmodel.h"
|
||||
#include "ui/utils/progress.h"
|
||||
#include "ui/utils/comboboxdelegate.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class VCard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo write docs
|
||||
*/
|
||||
class VCard : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VCard(const QString& jid, bool edit = false, QWidget* parent = nullptr);
|
||||
~VCard();
|
||||
|
||||
void setVCard(const Shared::VCard& card);
|
||||
void setVCard(const QString& jid, const Shared::VCard& card);
|
||||
QString getJid() const;
|
||||
void showProgress(const QString& = "");
|
||||
void hideProgress();
|
||||
|
||||
signals:
|
||||
void saveVCard(const Shared::VCard& card);
|
||||
|
||||
private slots:
|
||||
void onButtonBoxAccepted();
|
||||
void onClearAvatar();
|
||||
void onSetAvatar();
|
||||
void onAvatarSelected();
|
||||
void onAddAddress();
|
||||
void onRemoveAddress();
|
||||
void onAddEmail();
|
||||
void onCopyEmail();
|
||||
void onRemoveEmail();
|
||||
void onAddPhone();
|
||||
void onCopyPhone();
|
||||
void onRemovePhone();
|
||||
void onContextMenu(const QPoint& point);
|
||||
|
||||
private:
|
||||
QScopedPointer<Ui::VCard> m_ui;
|
||||
QSize avatarButtonMargins;
|
||||
QMenu* avatarMenu;
|
||||
bool editable;
|
||||
Shared::Avatar currentAvatarType;
|
||||
QString currentAvatarPath;
|
||||
Progress* progress;
|
||||
QLabel* progressLabel;
|
||||
QWidget* overlay;
|
||||
QMenu* contextMenu;
|
||||
UI::VCard::EMailsModel emails;
|
||||
UI::VCard::PhonesModel phones;
|
||||
ComboboxDelegate* roleDelegate;
|
||||
ComboboxDelegate* phoneTypeDelegate;
|
||||
|
||||
static const std::set<QString> supportedTypes;
|
||||
|
||||
private:
|
||||
void updateAvatar();
|
||||
};
|
||||
|
||||
#endif // VCARD_H
|
892
ui/widgets/vcard/vcard.ui
Normal file
@ -0,0 +1,892 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>VCard</class>
|
||||
<widget class="QWidget" name="VCard">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>578</width>
|
||||
<height>671</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="title">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 16pt </string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">Contact john@dow.org card</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="receivingTimeLabel">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: italic 8pt;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Received 12.07.2007 at 17.35</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
<property name="tabPosition">
|
||||
<enum>QTabWidget::North</enum>
|
||||
</property>
|
||||
<property name="tabShape">
|
||||
<enum>QTabWidget::Rounded</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="elideMode">
|
||||
<enum>Qt::ElideNone</enum>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="tabBarAutoHide">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="General">
|
||||
<attribute name="title">
|
||||
<string>General</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_2" columnstretch="1,2,2,1">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="horizontalSpacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="6" column="1" colspan="2">
|
||||
<widget class="QLabel" name="organizationHeading">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 600 16pt;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Organization</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<layout class="QFormLayout" name="personalForm">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="formAlignment">
|
||||
<set>Qt::AlignHCenter|Qt::AlignTop</set>
|
||||
</property>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="middleName">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>350</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="firstName">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>350</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="middleNameLabel">
|
||||
<property name="text">
|
||||
<string>Middle name</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>middleName</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="firstNameLabel">
|
||||
<property name="text">
|
||||
<string>First name</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>firstName</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="lastNameLabel">
|
||||
<property name="text">
|
||||
<string>Last name</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>lastName</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="lastName">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>350</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="nickNameLabel">
|
||||
<property name="text">
|
||||
<string>Nick name</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>nickName</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="nickName">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>350</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="birthdayLabel">
|
||||
<property name="text">
|
||||
<string>Birthday</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>birthday</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QDateEdit" name="birthday"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="7" column="1" colspan="2">
|
||||
<layout class="QFormLayout" name="organizationForm">
|
||||
<property name="formAlignment">
|
||||
<set>Qt::AlignHCenter|Qt::AlignTop</set>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="organizationNameLabel">
|
||||
<property name="text">
|
||||
<string>Organization name</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>organizationName</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="organizationName">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>350</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="organizationDepartmentLabel">
|
||||
<property name="text">
|
||||
<string>Unit / Department</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>organizationDepartment</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="organizationDepartment">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>350</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="roleLabel">
|
||||
<property name="text">
|
||||
<string>Role / Profession</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>organizationRole</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="organizationRole">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>350</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="organizationTitleLabel">
|
||||
<property name="text">
|
||||
<string>Job title</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>organizationTitle</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="organizationTitle">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>350</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="8" column="1" colspan="2">
|
||||
<widget class="Line" name="organizationLine">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="2">
|
||||
<layout class="QFormLayout" name="commonForm">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="fullName"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="fullNameLabel">
|
||||
<property name="text">
|
||||
<string>Full name</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>fullName</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0" rowspan="7">
|
||||
<spacer name="generalLeftHSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="1" colspan="2">
|
||||
<widget class="Line" name="personalLine">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="4">
|
||||
<widget class="QLabel" name="generalHeading">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 600 24pt ;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>General</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="3" rowspan="7">
|
||||
<spacer name="generalRightHSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QLabel" name="personalHeading">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 600 16pt;</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Personal information</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QToolButton" name="avatarButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="user">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::InstantPopup</enum>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonIconOnly</enum>
|
||||
</property>
|
||||
<property name="arrowType">
|
||||
<enum>Qt::NoArrow</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1" colspan="2">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="Contact">
|
||||
<attribute name="title">
|
||||
<string>Contact</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="contactHeading">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 600 24pt ;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Contact</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="contactScrollArea">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>566</width>
|
||||
<height>498</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3" columnstretch="1,3,1">
|
||||
<item row="7" column="1">
|
||||
<widget class="Line" name="phonesLine">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QTableView" name="emailsView">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<widget class="Line" name="addressesLine">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" rowspan="12">
|
||||
<spacer name="contactLeftSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Line" name="contactFormLine">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="Line" name="emailsLine">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<spacer name="contactBottomSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QLabel" name="addressesHeading">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 600 16pt;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Addresses</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QTableView" name="phonesView">
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="emailsHeading">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 600 16pt;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>E-Mail addresses</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QFormLayout" name="contactForm">
|
||||
<property name="formAlignment">
|
||||
<set>Qt::AlignHCenter|Qt::AlignTop</set>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="jabberID">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="jabberIDLabel">
|
||||
<property name="text">
|
||||
<string>Jabber ID</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>jabberID</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="url">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="urlLabel">
|
||||
<property name="text">
|
||||
<string>Web site</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>url</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="2" rowspan="12">
|
||||
<spacer name="contactRightSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="phenesHeading">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 600 16pt;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Phone numbers</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QTableView" name="addressesView">
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="Description">
|
||||
<attribute name="title">
|
||||
<string>Description</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="horizontalSpacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="descriptionHeading">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 600 24pt ;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Description</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QTextEdit" name="description">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close|QDialogButtonBox::Save</set>
|
||||
</property>
|
||||
<property name="centerButtons">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
<action name="actionSetAvatar">
|
||||
<property name="icon">
|
||||
<iconset theme="photo">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set avatar</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionClearAvatar">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-clear-all">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Clear avatar</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>fullName</tabstop>
|
||||
<tabstop>firstName</tabstop>
|
||||
<tabstop>middleName</tabstop>
|
||||
<tabstop>lastName</tabstop>
|
||||
<tabstop>nickName</tabstop>
|
||||
<tabstop>birthday</tabstop>
|
||||
<tabstop>avatarButton</tabstop>
|
||||
<tabstop>organizationName</tabstop>
|
||||
<tabstop>organizationDepartment</tabstop>
|
||||
<tabstop>organizationRole</tabstop>
|
||||
<tabstop>organizationTitle</tabstop>
|
||||
<tabstop>jabberID</tabstop>
|
||||
<tabstop>url</tabstop>
|
||||
<tabstop>description</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../resources/resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|