Source file: src/query_listview.cpp
/* Copyright (C) 2004-2010 Daniel Verite
This file is part of Manitou-Mail (see http://www.manitou-mail.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#include "main.h"
#include "query_listview.h"
#include "tags.h"
#include "user_queries.h"
#include "selectmail.h"
#include "message_port.h"
#include "msg_status_cache.h"
#include "db.h"
#include "sqlstream.h"
#include <QMenu>
#include <QCursor>
#include <QMessageBox>
#include <QMouseEvent>
#include <QTreeWidgetItemIterator>
int
query_lvitem::id_generator;
query_listview::query_listview(QWidget* parent): QTreeWidget(parent)
{
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
this, SLOT(context_menu(const QPoint&)));
// connect to any change to tags
message_port::connect_receiver(SIGNAL(tags_restructured()),
this, SLOT(tags_restructured()));
// subscribe to new messages notifications
message_port::connect_receiver(SIGNAL(new_mail_imported(mail_id_t)),
this, SLOT(got_new_mail(mail_id_t)));
}
query_listview::~query_listview()
{
free_mail_maps();
}
void
query_listview::free_mail_maps()
{
qs_tag_map::iterator it;
for (it=m_tagged.begin(); it!=m_tagged.end(); ++it) {
// free each qs_mail_map
delete it->second;
}
m_tagged.clear();
}
/* Return the number of unread messages inside the map (mail_id=>status) */
// static
int
query_listview::count_unread_messages(const qs_mail_map* m)
{
int cnt=0;
for (qs_mail_map::const_iterator iter = m->begin();
iter != m->end();
++iter)
{
if (((iter->second) & (mail_msg::statusRead|mail_msg::statusArchived|mail_msg::statusTrashed)) == 0)
cnt++;
}
return cnt;
}
/* Return true if there is at least one unread message inside the map
(mail_id=>status) */
// static
bool
query_listview::has_unread_messages(const qs_mail_map* m)
{
for (qs_mail_map::const_iterator iter = m->begin();
iter != m->end();
++iter)
{
if (((iter->second) & (mail_msg::statusRead|mail_msg::statusArchived|mail_msg::statusTrashed)) == 0)
return true;
}
return false;
}
/* Return the internal_id of the current item, or 0 if there's none */
int
query_listview::current_id()
{
QTreeWidgetItem* selected = currentItem();
if (!selected)
return 0;
QTreeWidgetItemIterator it(this);
while (*it) {
if (*it == selected) {
query_lvitem* item = static_cast<query_lvitem*>(*it);
return item->m_unique_id;
}
++it;
}
return 0;
}
/*
Set the focus and highlight on a particular item within the tree.
That might be expanded in the future to a vector of items
if the listview gets multi-selectable
*/
void
query_listview::set_focus_on_id(int id)
{
DBG_PRINTF(5, "set_focus_on_id(%d)", id);
QTreeWidgetItemIterator it(this);
while (id>0 && *it) {
query_lvitem* item = static_cast<query_lvitem*>(*it);
if (item->m_unique_id == id) {
/* We block the signals here to avoid setCurrentItem() emit
a selectionChanged() signal that would launch a new query
while we just want the item to be displayed as current (reverse video
and focus set on it). */
set_item_no_signal(item);
return;
}
++it;
}
set_item_no_signal(NULL);
}
void
query_listview::set_item_no_signal(query_lvitem* item)
{
blockSignals(true);
setCurrentItem(item);
blockSignals(false);
}
/*
Clear the selection status of whatever item is selected. To be
called when a new set of messages is retrieved outside of the query
listview
*/
void query_listview::clear_selection()
{
set_item_no_signal(NULL);
}
/*
Select an entry in the list and return its internal ID. Used when the selection
comes from outside the list (a command key, a menu entry...)
*/
int
query_listview::highlight_entry(query_lvitem::item_type type, uint tag_id)
{
query_lvitem* item;
switch(type) {
case query_lvitem::new_all:
item = m_item_new_all;
break;
case query_lvitem::virtfold_sent:
item = m_item_virtfold_sent;
break;
case query_lvitem::virtfold_trashcan:
item = m_item_virtfold_trashcan;
break;
case query_lvitem::current_tagged:
item = find_tag(m_item_current_tags, tag_id);
break;
case query_lvitem::archived_tagged:
item = find_tag(m_item_tags, tag_id);
break;
default:
item = NULL;
break;
}
if (item != NULL) {
if (!item->isSelected()) {
blockSignals(true);
item->setSelected(true);
blockSignals(false);
}
return item->m_unique_id;
}
return 0;
}
/* Set up the [Current Messages]->Tagged subtree */
void
query_listview::make_item_current_tags(const tag_node* root)
{
m_item_current_tags = new query_lvitem(m_item_current, query_lvitem::tree_node, tr("Tagged"));
qs_tag_map::const_iterator qsi;
for (qsi=m_tagged.begin(); qsi!=m_tagged.end() ; ++qsi) {
if (qsi->first==0) continue; // ignore non-tagged
const tag_node* node = root->find(qsi->first);
if (node) {
query_tag_lvitem* q = new query_tag_lvitem(m_item_current_tags, query_lvitem::current_tagged, QString::null /*title*/, qsi->first);
q->set_title(node->hierarchy(), qsi->second);
}
}
m_item_current_tags->setExpanded(true);
m_item_current_tags->sortChildren(0, Qt::AscendingOrder);
}
/*
Returns the number of messages within m_tagged[tag_id] whose status has the
bits from mask_not_set CLEARED
(that is, (status & mask_not_set) = 0)
*/
int
query_listview::map_count(int mask_not_set, uint tag_id)
{
int cnt=0;
qs_tag_map::iterator qsi;
for (qsi=m_tagged.begin(); qsi!=m_tagged.end() ; ++qsi) {
if (qsi->first==tag_id) {
qs_mail_map* m = qsi->second;
std::map<uint,int>::iterator it = m->begin();
for (; it!=m->end(); ++it) {
if ((it->second & mask_not_set)==0) {
cnt++;
}
}
}
}
return cnt;
}
void
query_listview::insert_child_tags(tag_node* n, query_tag_lvitem* parent_item,
int type, QSet<uint>* expanded_set)
{
std::list<tag_node*>::iterator iter;
for (iter=n->m_childs.begin(); iter!=n->m_childs.end(); ++iter) {
query_tag_lvitem* q = new query_tag_lvitem(parent_item, type, (*iter)->name(), (*iter)->id());
if (!(*iter)->m_childs.empty()) {
insert_child_tags(*iter, q, type, expanded_set);
if (expanded_set!=NULL && expanded_set->contains((*iter)->id())) {
q->setExpanded(true);
}
q->sortChildren(0, Qt::AscendingOrder);
}
}
}
query_lvitem*
query_listview::create_branch_current(const tag_node* root)
{
// Current messages (not archived)
m_item_current = new query_lvitem(tr("Current messages"));
insertTopLevelItem(index_branch_current, m_item_current);
m_item_current_all = new query_lvitem(m_item_current, query_lvitem::nonproc_all, tr("All"));
make_item_current_tags(root);
m_item_current_prio = new query_lvitem(m_item_current, query_lvitem::current_prio, tr("Prioritized"));
m_item_current_untagged = new query_lvitem(m_item_current, query_lvitem::nonproc_not_tagged, QString::null);
update_tag_current_counter(0); // update counts of Current->[Not tagged] branch
m_item_current->setExpanded(true);
return m_item_current;
}
void
query_listview::init()
{
setHeaderLabel(tr("Quick selection"));
setRootIsDecorated(true);
fetch_tag_map();
tags_definition_list tag_list;
tag_list.fetch();
tag_node root;
root.get_child_tags(tag_list);
// New messages
query_lvitem* item_new = new query_lvitem(this, tr("Unread messages"));
m_item_new_all = new query_lvitem(item_new, query_lvitem::new_all, tr("All"));
m_item_new_untagged = new query_lvitem(item_new, query_lvitem::new_not_tagged, tr("Not tagged"));
item_new->setExpanded(true);
create_branch_current(&root);
// Tagged messages
// The root is a pseudo-tag with an id=0
m_item_tags = new query_tag_lvitem(this, query_lvitem::archived_tagged, tr("Archived tagged mail"));
insert_child_tags(&root, m_item_tags, query_lvitem::archived_tagged, NULL);
m_item_tags->sortChildren(0, Qt::AscendingOrder);
m_item_virtfold_sent = new query_lvitem(this, tr("Sent mail"));
m_item_virtfold_sent->set_type(query_lvitem::virtfold_sent);
m_item_virtfold_trashcan = new query_lvitem(this, tr("Trashcan"));
m_item_virtfold_trashcan->set_type(query_lvitem::virtfold_trashcan);
// User queries
m_item_user_queries = new query_lvitem(this, tr("User queries"));
reload_user_queries();
display_counter(query_lvitem::new_all);
display_counter(query_lvitem::nonproc_all);
display_counter(query_lvitem::new_not_tagged);
}
void
query_listview::reload_user_queries()
{
DBG_PRINTF(5, "reload_user_queries");
user_queries_repository uq;
std::map<QString,QString>::const_iterator iterq;
// delete existing elements
QTreeWidgetItem* lv;
while ((lv=m_item_user_queries->child(0))) {
m_item_user_queries->removeChild(lv);
delete lv;
}
if (uq.fetch()) {
// query_lvitem* prev=NULL;
for (iterq=uq.m_map.begin(); iterq!=uq.m_map.end(); ++iterq) {
query_lvitem* q = new query_lvitem(m_item_user_queries, query_lvitem::user_defined, iterq->first);
q->m_sql = iterq->second;
}
}
}
/*
Store in 'set' the tag IDs of the items that are in expanded state
and are descendants of 'parent'
*/
void
query_listview::store_expanded_state(QTreeWidgetItem* parent,
QSet<uint>* set)
{
for (int i=0; i<parent->childCount(); ++i) {
QTreeWidgetItem* item = parent->child(i);
if (item->isExpanded()) {
query_tag_lvitem* t = static_cast<query_tag_lvitem*>(item);
set->insert(t->m_tag_id);
}
if (item->childCount()>0)
store_expanded_state(item, set);
}
}
/*
Slot. Called on new mail notifications. The global status map should already know about
the new mail so we just update some counters
*/
void
query_listview::got_new_mail(mail_id_t id)
{
DBG_PRINTF(5, "got_new_mail(%d)", id);
mail_msg msg;
msg.set_mail_id(id);
mail_status_changed(&msg, 0);
}
/*
Slot. Called when changes occur in tags definitions
*/
void
query_listview::tags_restructured()
{
DBG_PRINTF(5, "tags_restructured");
tags_definition_list tag_list;
tag_list.fetch();
tag_node root;
root.get_child_tags(tag_list);
/* Update the m_item_tags tree (all tags) */
// memorize which items are opened
QSet<uint> opened;
store_expanded_state(m_item_tags, &opened);
// destroy the tree
m_item_tags->remove_children();
// recreate the tree
insert_child_tags(&root, m_item_tags, query_lvitem::archived_tagged, &opened);
m_item_tags->sortChildren(0, Qt::AscendingOrder);
/* Update m_item_current_tags tree (tagged, non-archived messages) */
QSet<query_tag_lvitem*> remove_set;
for (int idx=0; idx<m_item_current_tags->childCount(); ++idx) {
query_tag_lvitem* t = static_cast<query_tag_lvitem*>(m_item_current_tags->child(idx));
const tag_node* n = root.find(t->m_tag_id);
if (n) {
update_tag_current_counter(t->m_tag_id);
}
else {
remove_set.insert(t);
}
}
// remove items
for (QSet<query_tag_lvitem*>::iterator it=remove_set.begin();
it!=remove_set.end();
++it)
{
delete(*it);
}
// sort
m_item_current_tags->sortChildren(0, Qt::AscendingOrder);
}
/* Also fill a pri_map for priorities */
bool
query_listview::fetch_tag_map()
{
db_cnx db;
try {
int mask = mail_msg::statusTrashed + mail_msg::statusArchived + mail_msg::statusSent;
sql_stream s(QString("SELECT ms.mail_id,mt.tag,ms.status,m.priority FROM (mail m JOIN mail_status ms USING (mail_id)) LEFT OUTER JOIN mail_tags mt ON mt.mail_id=ms.mail_id WHERE ms.status&%1=0").arg(mask), db); // status is not (sent OR archived OR trashed)
unsigned int mail_id, tag, status;
int pri;
qs_tag_map::iterator it;
m_all_unread_count = 0;
// m_unprocessed_untagged_count = 0;
// m_unread_untagged_count = 0;
m_unprocessed_prioritized_count = 0;
m_all_unprocessed_count = 0;
while (!s.eos()) {
s >> mail_id >> tag >> status >> pri;
msg_status_cache::update(mail_id, status);
m_prio_map[mail_id] = pri;
if (pri>0)
m_unprocessed_prioritized_count++;
if ((status & mail_msg::statusRead) == 0)
m_all_unread_count++;
it = m_tagged.find(tag); // tag is 0 if no tag is assigned to mail_id
qs_mail_map* m;
if (it != m_tagged.end()) {
m = it->second;
}
else {
m = new qs_mail_map();
m_tagged[tag] = m;
}
(*m)[mail_id] = status;
}
m_all_unprocessed_count = m_prio_map.size();
}
catch(db_excpt& p) {
db.handle_exception(p);
return false;
}
return true;
}
/*
Iterate through the tree items of the Current mail (=unprocessed) tags
to find the one matching 'tag_id'.
If found, change its counters.
*/
void
query_listview::update_tag_current_counter(uint tag_id)
{
if (tag_id!=0) {
if (!m_item_current_tags)
return;
int child_index=0;
query_tag_lvitem* q = static_cast<query_tag_lvitem*>(m_item_current_tags->child(child_index));
while (q) {
if (q->m_tag_id==tag_id) {
DBG_PRINTF(5, "update_tag_current_counter(%d)", tag_id);
qs_tag_map::const_iterator it = m_tagged.find(tag_id);
q->set_title(tags_repository::hierarchy(tag_id),
it != m_tagged.end() ? it->second : NULL);
break; // tag found, stop iterating
}
q = static_cast<query_tag_lvitem*>(m_item_current_tags->child(++child_index));
}
}
else {
if (m_item_current_untagged) {
DBG_PRINTF(5, "update_tag_current_counter(%d)", tag_id);
qs_tag_map::const_iterator it = m_tagged.find(0); // 0=>not tagged
m_item_current_untagged->set_title(tr("Not tagged"),
it != m_tagged.end() ? it->second : NULL);
}
}
}
void
query_listview::display_counter(query_lvitem::item_type type)
{
unsigned int count;
QFont f;
switch(type) {
case query_lvitem::new_all:
count = msg_status_cache::unread_count();
m_item_new_all->setText(0, tr("All (%1)").arg(count));
f = m_item_new_all->font(0);
f.setBold(count>0);
m_item_new_all->setFont(0, f);
break;
case query_lvitem::nonproc_all:
{
int unread_count = msg_status_cache::unread_count();
int unprocessed_count = msg_status_cache::unprocessed_count();
if (unread_count>0) {
m_item_current_all->setText(0, tr("All (%1<%2)").arg(unprocessed_count).arg(unread_count));
}
else {
m_item_current_all->setText(0, tr("All (%1)").arg(unprocessed_count));
}
f = m_item_current_all->font(0);
f.setBold(unread_count>0);
m_item_current_all->setFont(0, f);
m_item_current_all->setToolTip(0, query_lvitem::nonproc_tooltip_text(unprocessed_count, unread_count));
}
break;
case query_lvitem::new_not_tagged:
{
int unread_untagged_count = 0;
qs_tag_map::const_iterator it = m_tagged.find(0);
if (it != m_tagged.end())
unread_untagged_count = count_unread_messages(it->second);
DBG_PRINTF(5, "update unread_untag_count=%d", unread_untagged_count);
m_item_new_untagged->setText(0, tr("Not tagged (%1)").arg(unread_untagged_count));
f = m_item_new_untagged->font(0);
f.setBold(unread_untagged_count>0);
m_item_new_untagged->setFont(0, f);
}
break;
default:
break;
}
}
query_tag_lvitem*
query_listview::find_tag(query_lvitem* tree, uint tag_id)
{
QTreeWidgetItemIterator it(tree);
while (*it) {
query_tag_lvitem* item = static_cast<query_tag_lvitem*>(*it);
if (item->m_tag_id == tag_id)
return item;
++it;
}
return NULL;
}
void
query_listview::add_current_tag(uint tag_id)
{
if (!m_item_current_tags)
return;
tags_definition_list tag_list;
tag_list.fetch();
tag_node root;
root.get_child_tags(tag_list);
const tag_node* node = root.find(tag_id);
query_tag_lvitem* q = new query_tag_lvitem(m_item_current_tags, query_lvitem::current_tagged, QString::null, tag_id);
q->set_title(node->hierarchy());
m_item_current_tags->sortChildren(0, Qt::AscendingOrder);
qs_mail_map* m = new qs_mail_map();
m_tagged[tag_id] = m;
}
/*
Called by an msg_list_window to update counters when a mail tag
has changed. 'added' is true if the tag has been added, false if removed
*/
void
query_listview::mail_tag_changed(const mail_msg& msg, uint tag_id, bool added)
{
DBG_PRINTF(8, "mail_tag_changed mail_id=%u, tag=%u %s\n", msg.get_id(),
tag_id, added?"added":"removed");
qs_tag_map::iterator itt = m_tagged.find(tag_id);
qs_tag_map::iterator it_no_tag = m_tagged.find(0);
if (itt!=m_tagged.end()) {
// the leaf for the tag already exists
qs_mail_map* m = itt->second;
if (added) {
(*m)[msg.get_id()] = msg.status();
}
else {
m->erase(msg.get_id());
// if the message no longer has any tag, update the 'Not Tagged' branches
if (msg.get_cached_tags().empty() && it_no_tag!=m_tagged.end()) {
qs_mail_map* m_no_tag = it_no_tag->second;
(*m_no_tag)[msg.get_id()] = msg.status();
update_tag_current_counter(0);
display_counter(query_lvitem::new_not_tagged);
}
}
update_tag_current_counter(tag_id);
// update the 'Not Tagged' branch counter if the mail was counted in it
if (added) {
if (it_no_tag!=m_tagged.end()) {
m = it_no_tag->second;
qs_mail_map::iterator qi;
qi = m->find(msg.get_id());
if (qi!=m->end()) {
m->erase(msg.get_id());
update_tag_current_counter(0);
}
}
}
}
else {
DBG_PRINTF(8, "create a new tag branch for '%u' in query_listview\n", tag_id);
add_current_tag(tag_id); // adds tag_id to m_tagged
itt = m_tagged.find(tag_id);
if (itt!=m_tagged.end()) {
qs_mail_map* m = itt->second;
(*m)[msg.get_id()] = msg.status();
update_tag_current_counter(tag_id);
}
else
DBG_PRINTF(1, "ERR: couldn't find tag map for tag_id=%u just added\n", tag_id);
}
}
/*
Update the global counters due to mail status transitions or mail deletion
*/
void
query_listview::update_status_counters()
{
display_counter(query_lvitem::new_all);
display_counter(query_lvitem::nonproc_all);
display_counter(query_lvitem::new_not_tagged);
}
/*
Called by an msg_list_window to update counters when a mail status
has changed. 'nstatus' is -1 if the mail has been deleted.
*/
void
query_listview::mail_status_changed(mail_msg* msg, int nstatus)
{
uint mail_id = msg->get_id();
DBG_PRINTF(8, "mail_status_changed mail_id=%u, status=%d", mail_id, nstatus);
msg_status_cache::update(mail_id, nstatus);
qs_tag_map::iterator itt;
qs_mail_map::iterator itm;
const int archd = mail_msg::statusArchived;
const int trshd = mail_msg::statusTrashed;
bool status_counter_updated = false;
if (nstatus>=0 && !(nstatus&archd) && !(nstatus&trshd)) {
// if the msg is not archived and not trashed,
// then we try to get its tags and update the corresponding counters
DBG_PRINTF(8, "mail_id=%u is candidate for query_listview", mail_id);
std::list<uint>& tags = msg->get_tags();
std::list<uint>::iterator it;
for (it=tags.begin(); it!=tags.end(); ++it) {
itt = m_tagged.find(*it);
if (itt!=m_tagged.end()) {
// the leaf for tag_id=(*it) already exists
qs_mail_map* m = itt->second;
if (!status_counter_updated) {
// update global counters once per message, not once per tag
update_status_counters();
status_counter_updated = true;
}
(*m)[mail_id] = nstatus;
update_tag_current_counter(*it);
}
else {
// the message has tags that arent't in the treeview yet
DBG_PRINTF(8, "create a new tag branch for '%u' in query_listview!\n", *it);
add_current_tag(*it);
itt = m_tagged.find(*it);
if (itt!=m_tagged.end()) {
qs_mail_map* m = itt->second;
if (!status_counter_updated) {
// update global counters once per message, not once per tag
update_status_counters();
status_counter_updated = true;
}
(*m)[mail_id] = nstatus;
update_tag_current_counter(*it);
}
else
DBG_PRINTF(1, "ERR: couldn't find tag map for tag_id=%u just added\n", *it);
}
}
if (tags.empty()) {
itt = m_tagged.find(0); // 0=>not tagged.
if (itt!=m_tagged.end()) {
qs_mail_map* m = itt->second;
(*m)[mail_id] = nstatus;
// update the Current->Untagged counter
update_tag_current_counter(0);
update_status_counters();
}
}
return; // EXIT
}
// The message is getting trashed or archived or deleted.
// Check if a tag has been removed or if the message has been removed and
// had tags before, and update counters accordingly.
for (itt=m_tagged.begin(); itt!=m_tagged.end(); ++itt) {
qs_mail_map* m = itt->second;
itm = m->find(mail_id);
if (itm!=m->end()) {
int ostatus = itm->second;
if (nstatus<0
|| (nstatus & archd) != (ostatus & archd)
|| (nstatus & trshd) != (ostatus & trshd))
{
if (nstatus<0 || nstatus & (archd|trshd)) {
// msg was current and now is archived, or trashed, or deleted
DBG_PRINTF(8, "mail_id=%u is being arch/trshd/del\n", mail_id);
m->erase(mail_id);
if (!status_counter_updated) {
update_status_counters();
status_counter_updated = true;
}
update_tag_current_counter(itt->first);
}
else {
// msg wasn't current and now is.
// actually that shouldn't happen since the msg wouldn't be
// in the 'm' map in the first place
DBG_PRINTF(8, "mail_id=%u is being unarchived\n", mail_id);
if (!status_counter_updated) {
update_status_counters();
status_counter_updated = true;
}
(*m)[mail_id] = nstatus;
update_tag_current_counter(itt->first);
}
}
}
}
}
void
query_listview::context_menu(const QPoint& pos)
{
query_lvitem* item = dynamic_cast<query_lvitem*>(itemAt(pos));
if (item && item != currentItem())
setCurrentItem(item);
if (item && item->m_type==query_lvitem::user_defined) {
// contextual menu
QMenu qmenu(this);
qmenu.setTitle(item->text(0));
QAction* action_run = qmenu.addAction(tr("Run query"));
QAction* action_edit = qmenu.addAction(tr("Edit"));
QAction* action_remove = qmenu.addAction(tr("Remove"));
QAction* selected=qmenu.exec(QCursor::pos());
// user action
if (selected==action_remove) {
// remove this user query
int r=QMessageBox::warning(this, tr("Please confirm"), tr("Delete user query?"), tr("OK"), tr("Cancel"), QString::null);
if (r==0) {
if (user_queries_repository::remove_query(item->text(0))) {
delete item;
}
}
}
else if (selected==action_edit) {
// edit the user query
extern void save_filter_query(msgs_filter*, int, const QString); // FIXME
msgs_filter f;
f.set_user_query(item->m_sql);
save_filter_query(&f, 1, item->text(0));
reload_user_queries();
}
else if (selected==action_run) {
// run the query
msgs_filter f;
f.m_sql_stmt = item->m_sql;
emit run_selection_filter(f);
}
return;
}
}
void
query_listview::mousePressEvent(QMouseEvent* event)
{
// suppress itemPressed signal on right click
if (event->button() != Qt::RightButton)
QTreeWidget::mousePressEvent(event);
}
void
query_listview::refresh()
{
msg_status_cache::reset();
free_mail_maps();
fetch_tag_map();
if (m_item_current)
delete m_item_current;
tags_definition_list tag_list;
tag_list.fetch();
tag_node root;
root.get_child_tags(tag_list);
create_branch_current(&root);
display_counter(query_lvitem::new_all);
display_counter(query_lvitem::nonproc_all);
display_counter(query_lvitem::new_not_tagged);
}
query_lvitem::query_lvitem(QTreeWidgetItem* parent, int item_type, const QString name) :
QTreeWidgetItem(parent, QStringList(name))
{
m_type=item_type;
m_unique_id = ++id_generator;
}
query_lvitem::query_lvitem(QTreeWidget* parent, const QString name) :
QTreeWidgetItem(parent, QStringList(name))
{
m_type=tree_node; // 1st level node (=directory)
m_unique_id = ++id_generator;
}
query_lvitem::query_lvitem(const QString name) : QTreeWidgetItem(QStringList(name))
{
}
query_lvitem::~query_lvitem()
{
}
/* Text for the tooltips for leafs inside the 'Current messages' branch */
//static
QString
query_lvitem::nonproc_tooltip_text(int nb, int unread)
{
QString s;
if (unread==0) {
switch(nb) {
case 0:
s=QObject::tr("No message to process");
break;
case 1:
s=QObject::tr("1 message to process");
break;
default:
s=QObject::tr("%1 messages to process").arg(nb);
break;
}
}
else if (unread==nb) {
switch(nb) {
case 1:
s=QObject::tr("1 unread message to process");
break;
default:
s=QObject::tr("%1 unread messages to process").arg(nb);
break;
}
}
else {
switch(nb) {
case 1:
s=QObject::tr("1 unread message to process, including 1 unread").arg(nb).arg(unread);
break;
default:
if (unread>0) {
//: more than 1 unread messages
s=QObject::tr("%1 messages to process, including %2 unread", "several unread messages").arg(nb).arg(unread);
}
else {
s=QObject::tr("%1 messages to process, including 1 unread").arg(nb).arg(unread);
}
break;
}
}
return s;
}
/* status_map (if set) must be a map of unprocessed messages,
otherwise the tooltips would be wrong */
void
query_lvitem::set_title(const QString t, const qs_mail_map* status_map/*=NULL*/)
{
if (status_map) {
int unread = query_listview::count_unread_messages(status_map);
QString title;
int nb=status_map->size();
if (unread==0) {
// no unread
title = QString("%1 (%2)").arg(t).arg(nb);
setToolTip(0, nonproc_tooltip_text(nb, unread));
}
else {
title = QString("%1 (%2<%3)").arg(t).arg(nb).arg(unread);
setToolTip(0, nonproc_tooltip_text(nb, unread));
}
setText(0, title);
QFont f = font(0);
f.setBold(unread>0);
setFont(0, f);
}
else
setText(0, t);
}
void
query_lvitem::remove_children()
{
QTreeWidgetItem* item;
while ((item=this->child(0))!=NULL) {
delete item;
}
}
HTML source code generated by GNU Source-Highlight plus some custom post-processing
List of all available source files