Manitou-Mail logo title

Source file: src/composer_widgets.cpp

/* Copyright (C) 2004-2014 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.
*/

/* Various widgets used by the main composer class (new_mail_widget) */

#include "main.h"
#include "app_config.h"
#include "composer_widgets.h"
#include "icons.h"

#include <QComboBox>
#include <QPushButton>
#include <QGridLayout>
#include <QBoxLayout>
#include <QLabel>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QHeaderView>
#include <QTimer>
#include <QDebug>
#include <QMimeData>
#include <QDropEvent>
#include <QDragEnterEvent>
#include <QDragMoveEvent>

/***********************/
/* header_field_editor */
/***********************/
header_field_editor::header_field_editor(QWidget* parent)
{
  static const char* defaults[] = {
    // the contents must be aligned with enum header_index
    QT_TR_NOOP("To"),
    QT_TR_NOOP("Cc"),
    QT_TR_NOOP("Bcc"),
    QT_TR_NOOP("ReplyTo"),
    QT_TR_NOOP("Add..."),
    QT_TR_NOOP("Remove...")
  };

  m_cb = new QComboBox(parent);
  for (uint i=0; i<sizeof(defaults)/sizeof(defaults[0]); i++) {
    m_cb->addItem(tr(defaults[i]));
  }
  m_old_index=0;
  connect(m_cb, SIGNAL(currentIndexChanged(int)), this, SLOT(cb_changed(int)));
  m_lineedit = new edit_address_widget(parent);
  m_more_button = new QPushButton(FT_MAKE_ICON(FT_ICON16_ADDRESSBOOK), "", parent);
  connect(m_more_button, SIGNAL(clicked()), this, SLOT(more_addresses()));
}

header_field_editor::~header_field_editor()
{
  /* The widgets have to be deleted explictly since our parent won't
     do it when this happens by the 'remove' combobox action */
  delete m_lineedit;
  delete m_more_button;
  delete m_cb;
}

/*
  Slot. Called on combobox index change
 */
void
header_field_editor::cb_changed(int index)
{
  if (index == index_header_remove) {
    emit remove();    
  }
  else if (index == index_header_add) {
    // When "Add..." gets selected, execute the action but put back
    // the previous index
    m_cb->blockSignals(true);
    m_cb->setCurrentIndex(m_old_index);
    m_cb->blockSignals(false);    
    emit add();
  }
  else
    m_old_index=index;
}

void
header_field_editor::set_type(header_index type)
{
  m_cb->blockSignals(true);
  m_cb->setCurrentIndex((int)type);
  m_cb->blockSignals(false);
}

QString
header_field_editor::get_value() const
{
  return m_lineedit->text();
}

QString
header_field_editor::get_field_name() const
{
  if (!m_lineedit->text().isEmpty()) {
    switch(m_cb->currentIndex()) {
    case index_header_to:
      return "To";
    case index_header_cc:
      return "Cc";
    case index_header_bcc:
      return "Bcc";
    case index_header_replyto:
      return "ReplyTo";
    }
  }
  return QString::null;
}

void
header_field_editor::set_value(const QString val)
{
  m_lineedit->setText(val);
}

void
header_field_editor::grid_insert(QGridLayout* layout, int row, int column)
{
  layout->addWidget(m_cb, row, column++);
  layout->addWidget(m_lineedit, row, column++);
  layout->addWidget(m_more_button, row, column++);
}

void
header_field_editor::more_addresses()
{
  input_addresses_widget* w = new input_addresses_widget(get_value());
  connect(w, SIGNAL(addresses_available(QString)),
	   this, SLOT(addresses_offered(QString)));
  // try to open the window at the current mouse position
  w->move(QCursor::pos());
  w->show();  
}

/* Slot. Typically called when the user has OK'd the
   input_addresses_widget window. At this point we receive the new
   addresses, possibly reformat them and put them into our lineedit
   control */
void
header_field_editor::addresses_offered(QString addresses)
{
  mail_address::join_address_lines(addresses);
  m_lineedit->setText(addresses);
}

input_addresses_widget::input_addresses_widget(const QString& addresses)
{
  m_accel_type=0;
  QVBoxLayout *topLayout = new QVBoxLayout(this);
  topLayout->setSpacing(5);
  topLayout->setMargin(5);

  // search
  QHBoxLayout* hb = new QHBoxLayout();
  hb->setSpacing(3);
  topLayout->addLayout(hb);
  QLabel* lb1 = new QLabel(tr("Search for:"));
  hb->addWidget(lb1);
  m_wfind = new QLineEdit();
  hb->addWidget(m_wfind);
  connect(m_wfind, SIGNAL(returnPressed()), SLOT(find_contacts()));
  QPushButton* w_find_btn = new QPushButton(tr("Find"));
  hb->addWidget(w_find_btn);
  connect(w_find_btn, SIGNAL(clicked()), SLOT(find_contacts()));

  QLabel* lb = new QLabel(tr("Enter the email addresses (separated by comma or newline) :"), this);
  topLayout->addWidget(lb);

  m_wEdit = new QTextEdit();
  m_wEdit->setText(format_multi_lines(addresses));
  topLayout->addWidget(m_wEdit);

  QHBoxLayout* h1 = new QHBoxLayout();
  QPushButton* w_recent_to= new QPushButton(tr("Recent To:"));
  h1->addWidget(w_recent_to);
  QPushButton* w_recent_from= new QPushButton(tr("Recent From:"));
  h1->addWidget(w_recent_from);
  QPushButton* w_prio = new QPushButton(tr("Prioritized:"));
  h1->addWidget(w_prio);
  connect(w_recent_to, SIGNAL(clicked()), SLOT(show_recent_to()));
  connect(w_recent_from, SIGNAL(clicked()), SLOT(show_recent_from()));
  connect(w_prio, SIGNAL(clicked()), SLOT(show_prio_contacts()));
  topLayout->addLayout(h1);

  m_addr_list = new QTreeWidget();
  m_addr_list->hide();
  m_addr_list->setColumnCount(2);
  QStringList labels;
  labels << tr("Name and email") << tr("Last");
  m_addr_list->setHeaderLabels(labels);
  //m_addr_list->setAllColumnsShowFocus(true);
  m_addr_list->setRootIsDecorated(false);
/*
  // TODO: fix this code to resize section 0 the available width
  QFontMetrics font_metrics(m_addr_list->font());
  int date_width=font_metrics.width("0000/00/00 00:00");
  m_addr_list->header()->resizeSection(1, date_width);
  m_addr_list->header()->resizeSection(0, m_addr_list->header()->length()-date_width);
*/
  m_addr_list->header()->setResizeMode(QHeaderView::ResizeToContents);
  connect(m_addr_list, SIGNAL(itemActivated(QTreeWidgetItem*,int)),
	  this, SLOT(addr_selected(QTreeWidgetItem*,int)));

  topLayout->addWidget(m_addr_list);

  QHBoxLayout* h = new QHBoxLayout();
  h->setSpacing(10);
  h->setMargin(10);
  QPushButton* wOk = new QPushButton(tr("OK"));
  h->addWidget(wOk);
  h->addStretch(3);
  QPushButton* wCancel = new QPushButton(tr("Cancel"));
  h->addWidget(wCancel);
  topLayout->addLayout(h);
  setWindowTitle("Mail addresses");
  connect(wOk, SIGNAL(clicked()), SLOT(ok()));
  connect(wCancel, SIGNAL(clicked()), SLOT(cancel()));
}

void
input_addresses_widget::find_contacts()
{
  QString s = m_wfind->text();
  if (s.isEmpty())
    return;
  
  m_addr_list->clear();

  mail_address_list result_list;
  if (result_list.fetch_from_substring(s)) {
    mail_address_list::const_iterator iter;
    for (iter = result_list.begin(); iter != result_list.end(); iter++) {
      QString name_and_email = iter->name() + " <" + iter->get() + ">";
      date date1 = iter->last_sent();
      date date2 = iter->last_recv();
      QString d;
      d = (date1.FullOutput() > date2.FullOutput()) ? date1.OutputHM(2):
	date2.OutputHM(2);
      QStringList string_items;
      string_items << name_and_email << d;
      (void)new QTreeWidgetItem(m_addr_list, string_items);
    }
  }
  m_addr_list->setMinimumHeight(130); // arbitrary
  //  m_addr_list->setSorting(1, false); // descending order
  //  m_addr_list->setShowSortIndicator(true);
  m_addr_list->show();
}

/*
  Add to the list an address which the user has selected
*/
void
input_addresses_widget::addr_selected(QTreeWidgetItem* item, int column)
{
  Q_UNUSED(column);
  if (!item) return;
  QString addresses = m_wEdit->toPlainText();
  if (!addresses.isEmpty() && addresses.right(1) != "\n") {
    addresses.append("\n");
  }
  QString new_addr=format_multi_lines(item->text(0));
  addresses.append(new_addr);
  m_wEdit->setText(addresses);
}

/*
  Display email addresses recently used in from or to recipients
  what: 1=to, 2=from, 3=contacts with a positive priority
*/
void
input_addresses_widget::show_recent(int what)
{
  const int fetch_step=10;
  struct accel* accel;
  mail_address_list result_list;
  switch(what) {
  case 1:
    accel=&m_recent_to;
    break;
  case 2:
    accel=&m_recent_from;
    break;
  case 3:
    accel=&m_prioritized;
    break;
  default:
    DBG_PRINTF(5,"ERR: impossible choice");
    return;
  }
  if (what!=m_accel_type) {
    accel->rows_displayed=0;
    m_addr_list->clear();
    //    m_addr_list->setSorting(1, false); // descending order
    //    m_addr_list->setShowSortIndicator(true);
    m_addr_list->setMinimumHeight(130); // arbitrary
  }
  if (result_list.fetch_recent(what, fetch_step, accel->rows_displayed)) {
    mail_address_list::const_iterator iter;
    for (iter = result_list.begin(); iter != result_list.end(); iter++) {
      QString name_and_email = iter->name() + " <" + iter->get() + ">";
      date d = (what==1)?iter->last_sent():iter->last_recv();
      QString sdate = d.OutputHM(2); // US format
      QString col1 = (what==3) ? QString("%1").arg(iter->recv_pri()) : sdate;
      QStringList string_items;
      string_items << name_and_email << col1;
      (void)new QTreeWidgetItem(m_addr_list, string_items);
      accel->rows_displayed++;
    }
    m_addr_list->show();
    m_accel_type=what;
  }
}
void
input_addresses_widget::set_header_col1(const QString& text)
{
  QTreeWidgetItem* item=m_addr_list->headerItem();
  if (item!=NULL) {
    item->setText(1, text);
  }
}


void
input_addresses_widget::show_recent_to()
{
  set_header_col1(tr("When"));
  show_recent(1);
}

void
input_addresses_widget::show_recent_from()
{
  set_header_col1(tr("When"));
  show_recent(2);
}

void
input_addresses_widget::show_prio_contacts()
{
  set_header_col1(tr("Priority"));
  show_recent(3);
}


QString
input_addresses_widget::format_multi_lines(const QString addresses)
{
  std::list<QString> emails_list;
  std::list<QString> names_list;
  QByteArray ba = addresses.toLatin1(); // TODO: replace this when ExtractAddresses takes a QString instead of char*
  mail_address::ExtractAddresses(ba.constData(), emails_list, names_list);
  QString result;

  std::list<QString>::const_iterator iter1,iter2;
  for (iter1 = emails_list.begin(), iter2 = names_list.begin();
       iter1!=emails_list.end() && iter2!=names_list.end();
       ++iter1, ++iter2)
    {
      mail_address addr;
      addr.set_email(*iter1);
      addr.set_name(*iter2);
      result.append(addr.email_and_name());
      result.append("\n");
    }
  return result;
}

void
input_addresses_widget::ok()
{
  emit addresses_available(m_wEdit->toPlainText());
  hide();
}

void
input_addresses_widget::cancel()
{
  hide();
}

body_edit_widget::body_edit_widget(QWidget* p)
  : QTextEdit(p)
{
  setAcceptRichText(false);
  QString fontname=get_config().get_string("newmail/font");
  if (!fontname.isEmpty() && fontname!="xft") {
    QFont f;
    if (fontname.at(0)=='-')
      f.setRawName(fontname);		// for pre-0.9.5 entries
    else
      f.fromString(fontname);
    setFont(f);
  }
  else {
//    setFont(QFont("courier", 12));
  }

  setAcceptDrops(true);
}

void
body_edit_widget::dragEnterEvent(QDragEnterEvent* event)
{
  /* it's necessary on Windows to explicitly accept the event since
     the default implementation doesn't seem to accept it for dragged
     files */
  if (event->mimeData()->hasUrls())
    event->acceptProposedAction();
  else
    QTextEdit::dragEnterEvent(event);
}

void
body_edit_widget::dragMoveEvent(QDragMoveEvent* event)
{
  
  /* We don't let the base dragMoveEvent handle URLs to avoid it
     taking control over the caret and not releasing it after the drop */
  if (!event->mimeData()->hasUrls())
    QTextEdit::dragMoveEvent(event);
}

void
body_edit_widget::dropEvent(QDropEvent* event)
{
  if (event->mimeData()->hasUrls()) {
    event->acceptProposedAction();
    foreach (QUrl url, event->mimeData()->urls())  {
      emit attach_file_request(url);
    }    
  }
  else
    QTextEdit::dropEvent(event);
}

///
/// identity_widget
///
identity_widget::identity_widget(QWidget* parent): QDialog(parent)
{
  setWindowTitle(tr("Other identity"));
  QVBoxLayout *layout = new QVBoxLayout(this);
  QGridLayout* grid = new QGridLayout();
  layout->addLayout(grid);
  w_email = new QLineEdit();
  w_email->setMinimumWidth(200);
  w_name = new QLineEdit();
  int row=0;
  grid->addWidget(new QLabel(tr("Email address")), row, 0);
  grid->addWidget(w_email, row, 1);
  row++;
  grid->addWidget(new QLabel(tr("Name (optional)")), row, 0);
  grid->addWidget(w_name, row, 1);
  
  QHBoxLayout* buttons_layout = new QHBoxLayout();
  layout->addLayout(buttons_layout);
  buttons_layout->addStretch(3);
  QPushButton* wok = new QPushButton(tr("OK"));
  buttons_layout->addStretch(2);
  QPushButton* wcancel = new QPushButton(tr("Cancel"));
  buttons_layout->addStretch(3);
  connect(wok, SIGNAL(clicked()), SLOT(ok()));
  connect(wcancel, SIGNAL(clicked()), SLOT(cancel()));
  buttons_layout->addWidget(wok);
  buttons_layout->addWidget(wcancel);
}

identity_widget::~identity_widget()
{
}

void
identity_widget::set_email_address(const QString email)
{
  w_email->setText(email);
}

void
identity_widget::set_name(const QString name)
{
  w_name->setText(name);
}

QString
identity_widget::name()
{
  return w_name->text();
}

QString
identity_widget::email_address()
{
  return w_email->text();
}

void
identity_widget::cancel()
{
  this->reject();
}

void
identity_widget::ok()
{
  this->accept();
}

//
// html_source_editor
//

html_source_editor::html_source_editor(QWidget* parent) : QPlainTextEdit(parent)
{
  m_sticker = new QLabel(this);
  m_sticker->setTextFormat(Qt::RichText);
  m_sticker->setText(QString(" <font size=+2>%1</font> ").arg(tr("Source editor")));
  m_sticker->setToolTip(tr("Use the toggle button of the toolbar to go back to visual editor"));
  m_sticker->setAutoFillBackground(true);
  QPalette pal = m_sticker->palette();
  pal.setColor(QPalette::WindowText, QColor(255, 10, 20, 128));
  pal.setColor(QPalette::Background, QColor(255, 239, 52, 100));
  m_sticker->setPalette(pal);
  m_sticker->setFrameStyle(QFrame::StyledPanel);
  QTimer::singleShot(0, this, SLOT(position_label()));
}

void
html_source_editor::position_label()
{
  QRect r = viewport()->contentsRect();
  m_sticker->move(r.x()+r.width()-3-m_sticker->width(),
		  r.y()+5);
}

void
html_source_editor::resizeEvent(QResizeEvent* e)
{
  QPlainTextEdit::resizeEvent(e);
  position_label();
}

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

List of all available source files