Manitou-Mail logo title

Source file: src/tags.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 "db.h"
#include "sqlstream.h"
#include "tags.h"
#include <QStringList>

// separator between parent and child tag
// unicode right arrow
#define TAG_SEPARATOR QChar(0x2192)
//#define TAG_SEPARATOR QChar(0x2799)

//static
std::map<int,message_tag> tags_repository::m_tags_map;

int tags_repository::m_tags_map_fetched=0;

/* Fill in the cache */
void
tags_repository::fetch()
{
  db_cnx db;
  try {
    sql_stream s("SELECT tag_id,name,coalesce(parent_id,0) FROM tags", db);
    while (!s.eof()) {
      int id, parent_id;
      QString name;
      s >> id >> name >> parent_id;
      message_tag tag(id, name);
      if (parent_id)
	tag.set_parent_id(parent_id);
      m_tags_map[id] = tag;
    }
    m_tags_map_fetched=true;
  }
  catch(db_excpt& p) {
    m_tags_map.clear();
    m_tags_map_fetched=false;
    DBEXCPT(p);
  }
}


//static
/*
void
tags_repository::get_sorted_list(QStringList* taglist)
{
  if (!m_tags_map_fetched) {
    fetch();
  }
  std::map<int,QString>::const_iterator iter;
  for (iter=tags_repository::m_tags_map.begin();
       iter!=tags_repository::m_tags_map.end();
       ++iter) {
    taglist->push_front(iter->second);
  }
  taglist->sort();
}
*/

/* Empty the cache */
void
tags_repository::reset()
{
  m_tags_map.clear();
  m_tags_map_fetched = 0;
}

/*
  Return a list of tag names from a list of tag id's (<id_list>),
  sorted case-insensitively
*/
QStringList
tags_repository::names_list(std::list<uint>& id_list)
{
  if (!m_tags_map_fetched) {
    fetch();
  }
  QStringList result;
  std::map<int,message_tag>::const_iterator iterm;
  std::list<uint>::const_iterator iterl = id_list.begin();
  QMap<QString, QString> m;
  for (; iterl != id_list.end(); ++iterl) {
    iterm = m_tags_map.find((int)*iterl);
    if (iterm != m_tags_map.end()) {
      QString tag = tags_repository::hierarchy(iterm->second.id());
      m.insert(tag.toLower(), tag);
    }
  }
  // this returns the strings in the order of the map according to
  // Qt's documentation
  return m.values();
}

QString
tags_repository::name(int id)
{
  if (!m_tags_map_fetched) {
    fetch();
  }
  std::map<int,message_tag>::const_iterator i;
  i = m_tags_map.find(id);
  if (i!=m_tags_map.end())
    return i->second.name();
  else
    return "";
}

/*
  Return the name of the tag including its parent hierarchy
*/
QString
tags_repository::hierarchy(int id)
{
  if (!m_tags_map_fetched) {
    fetch();
  }
  std::map<int,message_tag>::const_iterator i;
  i = m_tags_map.find(id);
  if (i == m_tags_map.end())
    return "";

  QString s;
  int parent_id = i->second.parent_id();
  if (parent_id) {
    s = hierarchy(parent_id);
    s.append(TAG_SEPARATOR); //"->");
    s.append(i->second.name());
  }
  else
    s=i->second.name();
  return s;
}

//static
QString
message_tag::convert_separator_to_ascii(const QString& orig)
{
  QString s=orig;
  s.replace(TAG_SEPARATOR, "->");
  return s;
}

//static
QString
message_tag::convert_separator_from_ascii(const QString& orig)
{
  QString s=orig;
  s.replace("->", TAG_SEPARATOR);
  return s;
}

//static
bool
message_tag::is_valid_name(const QString name, QString* errmsg/*=NULL*/)
{
  if (name.indexOf("->")>=0) {
    if (errmsg) {
      *errmsg = QObject::tr("A tag's name cannot contain '->' since it is used for expressing tag hierarchies");
    }
    return false;
  }
  if (name=="/") {
    if (errmsg)
      *errmsg = QObject::tr("/ is not allowed as a name for a tag");
    return false;
  }
  return true;
}

bool
message_tag::store()
{
  bool result=true;
  try {
    db_cnx db;
    if (m_tag_id<=0) {
      db.next_seq_val("seq_tag_id", &m_tag_id);
      sql_stream s("INSERT INTO tags(tag_id,name,parent_id) VALUES (:p1,:p2,:p3)", db);
      s << m_tag_id << m_name;
      if (m_parent_id)
	s << m_parent_id;
      else
	s << sql_null();
    }
    else {
      sql_stream s("UPDATE tags SET name=':p1',parent_id=:p3 where tag_id=:p2", db);
      s << m_name;
      if (m_parent_id)
	s << m_parent_id;
      else
	s << sql_null();
      s << m_tag_id;
    }
  }
  catch(db_excpt& p) {
    DBEXCPT(p);
    result=false;
  }
  return result;
}

bool
message_tag::remove()
{
  bool result=true;
  db_cnx db;
  try {
    DBG_PRINTF(5,"message_tag::remove(%u)\n", getId());
    db.begin_transaction();
    sql_stream s1("DELETE FROM mail_tags WHERE tag=:p1", db);
    s1 << getId();
    sql_stream s2("DELETE FROM tags WHERE tag_id=:p1", db);
    s2 << getId();
    db.commit_transaction();
  }
  catch(db_excpt& p) {
    db.rollback_transaction();
    DBEXCPT(p);
    result=false;
  }
  return result;
}

bool
message_tag::reload()
{
  bool result=true;
  db_cnx db;
  try {
    sql_stream s("SELECT name,parent_id from tags WHERE tag_id=:p1", db);
    s << m_tag_id;
    if (!s.eos()) {
      s >> m_name >> m_parent_id;
    }
    else {
      m_name=QString::null;
      m_parent_id=0;
    }
  }
  catch(db_excpt& p) {
    DBEXCPT(p);
    result=false;
  }
  return result;
}

bool
tags_definition_list::fetch(bool force /*=false*/)
{
  bool result=true;
  if (m_bFetched && !force)
    return true;

  try {
    db_cnx db;
    sql_stream s ("SELECT tag_id,name,parent_id FROM tags ORDER BY name", db);
    while (!s.eos()) {
      int id, parent_id;
      QString name;
      s >> id >> name >> parent_id;
      message_tag tag(id, name);
      tag.set_parent_id(parent_id);
      push_back(tag);
    }
    m_bFetched=true;
  }
  catch (db_excpt& p) {
    DBEXCPT(p);
    result=false;
  }
  return result;
}


tag_node::~tag_node()
{
  clear();
}

void
tag_node::clear()
{
  std::list<tag_node*>::iterator iter;
  for (iter=m_childs.begin(); iter!=m_childs.end(); ++iter) {
    (*iter)->clear();
    delete *iter;
  }
  m_childs.clear();
}

tag_qitem::tag_qitem(QListWidget* parent, tag_node* n, const QString txt_entry) :
  QListWidgetItem(parent), m_node(n)
{
  setText(txt_entry);
  m_id=n->id();
}

tag_qitem::~tag_qitem()
{
}

tag_selector::tag_selector(QWidget* parent) : QComboBox(parent)
{
}

tag_selector::~tag_selector()
{
}

void
tag_node::get_child_tags(tags_definition_list& l)
{
  tags_definition_list::iterator iter;
  for (iter=l.begin(); iter!=l.end(); ++iter) {
    if (iter->parent_id()==(int)m_id) {
      tag_node* t = new tag_node(this);
      t->m_id = iter->getId();
      t->m_name = iter->getName();
      t->m_parent_id = this->m_id;
      this->m_childs.push_back(t);
      t->get_child_tags(l);
    }
  }
}

void
tag_selector::insert_childs(tag_node* n, int level)
{
  std::list<tag_node*>::iterator iter;
  for (iter=n->m_childs.begin(); iter!=n->m_childs.end(); ++iter) {
    QString s=(*iter)->hierarchy();
    addItem(s, QVariant((*iter)->id()));
    if (!(*iter)->m_childs.empty()) {
      insert_childs(*iter, level+1);
    }
  }
}

bool
tag_selector::init(bool first_is_empty)
{
  setEditable(false);
  tags_definition_list l;
  if (!l.fetch())
    return false;

  clear();
  m_root.clear();
  if (first_is_empty) {
    // An empty entry at first position meaning "no selection made"
    addItem(QString(""), QVariant(0));
  }

  m_root.get_child_tags(l);
  insert_childs(&m_root, 0);

  return true;
}

const tag_node*
tag_selector::selected_node() const
{
  int idx=currentIndex();
  if (idx<0) return NULL;
  QVariant v=itemData(idx);
  return m_root.find(v.toInt());
}

uint
tag_selector::current_tag_id() const
{
  const tag_node* n = selected_node();
  return n ? n->id(): 0;
}

void
tag_selector::set_current_tag_id(uint id)
{
  int idx=findData(QVariant(id));
  if (idx>=0) {
    setCurrentIndex(idx);
  }
}

/*
  Return the name of the tag including its parent hierarchy
*/
QString
tag_node::hierarchy() const
{
  QString s=m_name;
  tag_node* p=m_parent_node;
  while (p) {
    if (p->id()!=0) {		// skip the root node, which has an id=0 and no name
      s.prepend(TAG_SEPARATOR); //"->");
      s.prepend(p->name());
    }
    p=p->parent_node();
  }
  return s;
}

const tag_node*
tag_node::find(uint tag_id) const
{
  if (tag_id==0) return NULL;	// special case, we never want to find the root
  if (tag_id==m_id) return this;
  std::list<tag_node*>::const_iterator iter;
  for (iter=m_childs.begin(); iter!=m_childs.end(); ++iter) {
    if ((*iter)->id()==tag_id)
      return *iter;
    const tag_node* n = (*iter)->find(tag_id);
    if (n) return n;
  }
  return NULL;
}

HTML source code generated by GNU Source-Highlight plus some custom post-processing

List of all available source files