Source file: src/newmailwidget.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 "newmailwidget.h"
#include "app_config.h"
#include "edit_address_widget.h"
#include "notepad.h"
#include "attachment_listview.h"
#include "main.h"
#include "dragdrop.h"
#include "icons.h"
#include "users.h"
#include "notewidget.h"
#include "tagsbox.h"
#include "sqlstream.h"
#include "html_editor.h"
#include <set>
#include <QCloseEvent>
#include <QComboBox>
#include <QCursor>
#include <QTimer>
#include <QDropEvent>
#include <QFileDialog>
#include <QFontDialog>
#include <QGridLayout>
#include <QHeaderView>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLabel>
#include <QList>
#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
#include <QPrintDialog>
#include <QPrinter>
#include <QPushButton>
#include <QToolButton>
#include <QSplitter>
#include <QTextDocument>
#include <QTreeWidgetItem>
#include <QToolBar>
#include <QVBoxLayout>
#include <QStackedWidget>
#include <QRegExp>
#include <QDebug>
/***********************/
/* 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("Remove...")
};
m_cb = new QComboBox(parent);
for (uint i=0; i<sizeof(defaults)/sizeof(defaults[0]); i++) {
m_cb->addItem(tr(defaults[i]));
}
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 changes */
void
header_field_editor::cb_changed(int index)
{
if (index == index_header_remove) {
emit remove();
}
}
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);
}
/***********************/
/* new_mail_widget */
/***********************/
QString
new_mail_widget::m_last_attch_dir;
void
new_mail_widget::create_actions()
{
m_action_send_msg = new QAction(FT_MAKE_ICON(FT_ICON16_SEND),
tr("Send mail"), this);
connect(m_action_send_msg, SIGNAL(activated()), this, SLOT(send()));
m_action_attach_file = new QAction(FT_MAKE_ICON(FT_ICON16_ATTACH),
tr("Attach file"), this);
connect(m_action_attach_file, SIGNAL(activated()), this, SLOT(attach_files()));
m_action_insert_file = new QAction(FT_MAKE_ICON(FT_ICON16_OPENFILE),
tr("Insert text file"), this);
connect(m_action_insert_file, SIGNAL(activated()), this, SLOT(insert_file()));
m_action_edit_note = new QAction(FT_MAKE_ICON(FT_ICON16_EDIT_NOTE),
tr("Edit note"), this);
connect(m_action_edit_note, SIGNAL(activated()), this, SLOT(edit_note()));
m_action_add_header = new QAction(FT_MAKE_ICON(ICON16_HEADER),
tr("Add header field"), this);
connect(m_action_add_header, SIGNAL(activated()), this, SLOT(add_field_editor()));
m_action_open_notepad = new QAction(FT_MAKE_ICON(ICON16_NOTEPAD),
tr("Open global notepad"), this);
connect(m_action_open_notepad, SIGNAL(activated()), this, SLOT(open_global_notepad()));
}
void
new_mail_widget::make_toolbars()
{
QToolBar* toolbar = addToolBar(tr("Message Operations"));
toolbar->addAction(m_action_send_msg);
toolbar->addAction(m_action_attach_file);
toolbar->addAction(m_action_edit_note);
toolbar->addAction(m_action_add_header);
toolbar->addAction(m_action_open_notepad);
m_toolbar_html1 = m_toolbar_html2 = NULL;
if (m_html_edit) {
QList<QToolBar*> toolbars = m_html_edit->create_toolbars();
for (int i=0; i<toolbars.size(); i++) {
if (i==0) m_toolbar_html1 = toolbars[i];
if (i==1) {
addToolBarBreak();
m_toolbar_html2 = toolbars[i];
}
addToolBar(toolbars[i]);
}
// Add our own toggle button to the second HTML toolbar
if (m_toolbar_html2 != NULL) {
QToolButton* source = new QToolButton();
source->setIcon(HTML_ICON("stock_view-html-source.png"));
source->setToolTip(tr("Toggle between HTML source edit and visual edit"));
source->setCheckable(true);
connect(source, SIGNAL(toggled(bool)), this, SLOT(toggle_edit_source(bool)));
m_toolbar_html2->addWidget(source);
}
}
}
void
new_mail_widget::toggle_edit_source(bool checked)
{
if (!m_html_edit) {
DBG_PRINTF(1, "ERR: no html editor is instantiated");
return;
}
if (checked) {
m_html_source_editor = new html_source_editor();
m_html_source_editor->setPlainText(m_html_edit->html_text());
m_edit_stack->addWidget(m_html_source_editor);
m_edit_stack->setCurrentWidget(m_html_source_editor);
m_html_edit->enable_html_controls(false);
// disable some actions while we're editing html source
m_action_send_msg->setEnabled(false);
m_action_insert_file->setEnabled(false);
}
else {
m_html_edit->set_html_text(m_html_source_editor->toPlainText());
m_html_edit->enable_html_controls(true);
delete m_html_source_editor;
m_html_source_editor = NULL;
m_action_send_msg->setEnabled(true);
m_action_insert_file->setEnabled(true);
m_edit_stack->setCurrentWidget(m_html_edit);
}
}
new_mail_widget::new_mail_widget(mail_msg* msg, QWidget* parent)
: QMainWindow(parent)
{
lSubject = NULL;
m_wSubject = NULL;
setWindowTitle(tr("New message"));
m_wrap_lines = true;
m_close_confirm = true;
m_msg = *msg;
QWidget* main_hb = new QWidget(this); // container
setCentralWidget(main_hb);
QHBoxLayout *topLayout = new QHBoxLayout(main_hb);
topLayout->setSpacing(4);
topLayout->setMargin(4);
m_wtags=new tags_box_widget(main_hb);
topLayout->addWidget(m_wtags);
topLayout->setStretchFactor(m_wtags, 0);
QSplitter* split=new QSplitter(Qt::Vertical, main_hb);
m_edit_stack = new QStackedWidget(split);
m_bodyw = new body_edit_widget();
m_html_edit = new html_editor();
m_html_edit->set_html_text("<html><body></body></html>");
m_edit_stack->addWidget(m_bodyw);
m_edit_stack->addWidget(m_html_edit);
m_edit_mode = html_mode;
m_edit_stack->setCurrentWidget(m_html_edit);
QVBoxLayout *hLayout = new QVBoxLayout();
topLayout->addLayout(hLayout);
topLayout->setStretchFactor(hLayout, 1);
gridLayout = new QGridLayout();
hLayout->addLayout(gridLayout);
hLayout->addWidget(split);
// QWidget* wcont=main_hb;
int nRow=0;
header_field_editor* ed = new header_field_editor(this);
ed->set_type(header_field_editor::index_header_to);
ed->grid_insert(gridLayout, nRow, 0);
ed->set_value(m_msg.header().m_to);
connect(ed, SIGNAL(remove()), this, SLOT(remove_field_editor()));
// connect(ed, SIGNAL(add()), this, SLOT(add_field_editor()));
m_header_editors.append(ed);
nRow++;
QString initial_cc = m_msg.header().m_cc;
if (!initial_cc.isEmpty()) {
header_field_editor* ed1 = new header_field_editor(this);
ed1->set_type(header_field_editor::index_header_cc);
ed1->grid_insert(gridLayout, nRow++, 0);
ed1->set_value(initial_cc);
connect(ed1, SIGNAL(remove()), this, SLOT(remove_field_editor()));
// connect(ed1, SIGNAL(add()), this, SLOT(add_field_editor()));
m_header_editors.append(ed1);
}
lSubject=new QLabel(tr("Subject:"), this);
gridLayout->addWidget(lSubject, nRow, 0);
m_wSubject=new QLineEdit(this);
m_wSubject->setText(m_msg.header().m_subject);
gridLayout->addWidget(m_wSubject, nRow, 1);
nRow++;
m_qAttch=new attch_listview(split);
m_qAttch->allow_delete(true);
m_qAttch->hide();
m_qAttch->set_attch_list(&m_msg.attachments());
resize(900,600);
QList<int> lSizes;
lSizes.append(400);
lSizes.append(20);
split->setSizes(lSizes);
set_wrap_mode();
create_actions();
QMenu* pMsg=new QMenu(tr("&Message"), this);
CHECK_PTR(pMsg);
QIcon ico_print(FT_MAKE_ICON(FT_ICON16_PRINT));
QIcon ico_note(FT_MAKE_ICON(FT_ICON16_EDIT_NOTE));
QIcon ico_close(FT_MAKE_ICON(FT_ICON16_CLOSE_WINDOW));
pMsg->addAction(m_action_send_msg);
pMsg->addAction(m_action_attach_file);
pMsg->addAction(m_action_insert_file);
// pMsg->addAction(tr("Keep for later"), this, SLOT(keep()));
pMsg->addAction(ico_note, tr("Edit private note"), this, SLOT(edit_note()));
pMsg->addAction(ico_print, tr("Print"), this, SLOT(print()));
pMsg->addAction(ico_close, tr("&Cancel"), this, SLOT(cancel()));
QIcon ico_cut(FT_MAKE_ICON(FT_ICON16_EDIT_CUT));
QIcon ico_copy(FT_MAKE_ICON(FT_ICON16_EDIT_COPY));
QIcon ico_paste(FT_MAKE_ICON(FT_ICON16_EDIT_PASTE));
QIcon ico_undo(FT_MAKE_ICON(FT_ICON16_UNDO));
QIcon ico_redo(FT_MAKE_ICON(FT_ICON16_REDO));
QMenu* pEdit = new QMenu(tr("Edit"), this);
pEdit->addAction(ico_cut, tr("Cut"), m_bodyw, SLOT(cut()));
pEdit->addAction(ico_copy, tr("Copy"), m_bodyw, SLOT(copy()));
pEdit->addAction(ico_paste, tr("Paste"), m_bodyw, SLOT(paste()));
pEdit->addAction(ico_undo, tr("Undo"), m_bodyw, SLOT(undo()));
pEdit->addAction(ico_redo, tr("Redo"), m_bodyw, SLOT(redo()));
m_ident_menu = new QMenu(tr("Identity"), this);
load_identities(m_ident_menu);
if (m_ids.empty()) {
m_errmsg=tr("No sender identity is defined.\nUse the File/Preferences menu and fill in an e-mail identity to be able to send messages.");
QMessageBox::warning(NULL, tr("Error"), m_errmsg, QMessageBox::Ok, Qt::NoButton);
}
m_pMenuFormat = new QMenu(tr("&Format"), this);
QAction* action_wrap_lines = m_pMenuFormat->addAction(tr("&Wrap lines"), this, SLOT(toggle_wrap(bool)));
action_wrap_lines->setCheckable(true);
action_wrap_lines->setChecked(m_wrap_lines);
QIcon ico_font(FT_MAKE_ICON(FT_ICON16_FONT));
m_pMenuFormat->addAction(ico_font, tr("Font"), this, SLOT(change_font()));
QActionGroup* msg_format_group = new QActionGroup(this);
msg_format_group->setExclusive(true);
m_action_plain_text = m_pMenuFormat->addAction(tr("Plain text"), this, SLOT(to_format_plain_text()));
m_action_plain_text->setCheckable(true);
m_action_html_text = m_pMenuFormat->addAction(tr("HTML"), this, SLOT(to_format_html_text()));
m_action_html_text->setCheckable(true);
m_action_plain_text->setChecked(m_edit_mode == plain_mode);
m_action_html_text->setChecked(m_edit_mode == html_mode);
msg_format_group->addAction(m_action_plain_text);
msg_format_group->addAction(m_action_html_text);
m_pMenuFormat->addAction(tr("Store settings"), this, SLOT(store_settings()));
QMenu* menu_display = new QMenu(tr("Display"), this);
QAction* action_display_tags = menu_display->addAction(tr("Tags panel"), this, SLOT(toggle_tags_panel(bool)));
action_display_tags->setCheckable(true);
action_display_tags->setChecked(true);
QMenuBar* menu=menuBar();
menu->addMenu(pMsg);
menu->addMenu(pEdit);
menu->addMenu(m_ident_menu);
menu->addMenu(m_pMenuFormat);
menu->addMenu(menu_display);
// topLayout->setMenuBar(menu);
make_toolbars();
QString body_html = m_msg.get_body_html();
if (!body_html.isEmpty()) {
m_html_edit->set_html_text(body_html);
m_html_edit->finish_load();
m_html_edit->setFocus();
format_html_text();
}
else {
set_body_text(m_msg.get_body_text());
format_plain_text();
}
}
/* Switch to plain text format initiated by the user */
void
new_mail_widget::to_format_plain_text()
{
if (m_html_edit->isModified()) {
int res = QMessageBox::Ok;
res = QMessageBox::warning(this, APP_NAME, tr("The Conversion to plain text format has the effect of loosing the rich text formating (bold, italic, colors, fonts, ...). Please confirm your choice."), QMessageBox::Ok, QMessageBox::Cancel|QMessageBox::Default, Qt::NoButton);
if (res == QMessageBox::Ok) {
set_body_text(m_html_edit->to_plain_text());
format_plain_text();
}
else {
m_action_html_text->setChecked(true);
}
}
else {
set_body_text(m_html_edit->to_plain_text());
format_plain_text();
}
}
/* Switch to html format initiated by the user */
void
new_mail_widget::to_format_html_text()
{
QString text = mail_displayer::htmlize(m_bodyw->document()->toPlainText());
text.replace("\n", "<br>\n");
m_html_edit->set_html_text(text);
format_html_text();
}
void
new_mail_widget::format_plain_text()
{
m_edit_stack->setCurrentWidget(m_bodyw);
m_edit_mode = plain_mode;
if (m_toolbar_html1)
removeToolBar(m_toolbar_html1);
if (m_toolbar_html2) {
removeToolBarBreak(m_toolbar_html2);
removeToolBar(m_toolbar_html2);
}
m_action_plain_text->setChecked(true);
}
void
new_mail_widget::format_html_text()
{
m_edit_stack->setCurrentWidget(m_html_edit);
m_edit_mode = html_mode;
if (m_toolbar_html1 && m_toolbar_html1->isHidden()) {
addToolBar(m_toolbar_html1);
m_toolbar_html1->show();
}
if (m_toolbar_html2 && m_toolbar_html2->isHidden()) {
addToolBarBreak();
addToolBar(m_toolbar_html2);
m_toolbar_html2->show();
}
m_action_html_text->setChecked(true);
}
/* Slot. Called when the user chooses to remove a header field editor */
void
new_mail_widget::remove_field_editor()
{
header_field_editor* ed = dynamic_cast<header_field_editor*>(QObject::sender());
if (ed != NULL) {
m_header_editors.removeOne(ed);
ed->deleteLater();
}
}
/* Slot. Called when the user chooses to add a header field editor */
void
new_mail_widget::add_field_editor()
{
// keep the subject at the end of the grid by removing/re-adding
if (lSubject)
gridLayout->removeWidget(lSubject);
if (m_wSubject)
gridLayout->removeWidget(m_wSubject);
header_field_editor* ed = new header_field_editor(this);
ed->set_type(header_field_editor::index_header_to);
// insert before the last row of the grid, which is always occupied by the subject line
ed->grid_insert(gridLayout, gridLayout->rowCount(), 0);
ed->set_value(m_msg.header().m_to);
connect(ed, SIGNAL(remove()), this, SLOT(remove_field_editor()));
// connect(ed, SIGNAL(add()), this, SLOT(add_field_editor()));
m_header_editors.append(ed);
int r=gridLayout->rowCount();
if (lSubject)
gridLayout->addWidget(lSubject, r , 0);
if (m_wSubject)
gridLayout->addWidget(m_wSubject, r, 1);
}
void
new_mail_widget::set_wrap_mode()
{
if (m_wrap_lines) {
m_bodyw->setWordWrapMode(QTextOption::WordWrap);
// m_bodyw->setWrapColumnOrWidth(80);
m_bodyw->setLineWrapMode(QPlainTextEdit::WidgetWidth);
}
else {
m_bodyw->setLineWrapMode(QPlainTextEdit::NoWrap);
}
}
void
new_mail_widget::load_identities(QMenu* m)
{
m_identities_group = new QActionGroup(this);
m_identities_group->setExclusive(true);
/* Auto-select our identity as a sender.
First, if we're replying to a message, try the identity to which this
message was sent (envelope_from would have been set up by our caller).
Otherwise get the default identity from the configuration */
QString default_email = m_msg.header().m_envelope_from;
if (default_email.isEmpty())
default_email = get_config().get_string("default_identity");
if (m_ids.fetch()) {
identities::iterator iter;
for (iter = m_ids.begin(); iter != m_ids.end(); ++iter) {
mail_identity* p = &iter->second;
QAction* action = m->addAction(p->m_email_addr, this, SLOT(change_identity()));
m_identities_group->addAction(action);
action->setCheckable(true);
if (!default_email.isEmpty() && p->m_email_addr==default_email) {
action->setChecked(true);
m_from = default_email;
}
m_identities_actions.insert(action, p);
}
// if no identity is still defined, use the first one from the list
if (default_email.isEmpty() && !m_ids.empty() && !m_identities_group->actions().isEmpty()) {
iter=m_ids.begin();
QAction* action = m_identities_group->actions().first();
action->setChecked(true);
m_from = iter->first;
}
// Add "Other" that lets the user enter an identity that is not defined
// within the preferences
m_action_identity_other = m->addAction(tr("Other..."), this, SLOT(other_identity()));
m_identities_group->addAction(m_action_identity_other);
m_action_identity_other->setCheckable(true);
m_identities_actions.insert(m_action_identity_other, NULL);
m_action_edit_other = m->addAction(tr("Edit other..."), this, SLOT(other_identity()));
// m_action_edit_other is enabled only when the "Other" identity is selected
m_action_edit_other->setEnabled(false);
}
setWindowTitle(tr("New mail from ")+m_from);
}
// User-input identity to be used only for this mail
void
new_mail_widget::other_identity()
{
DBG_PRINTF(3, "other_identity()");
identity_widget* w = new identity_widget(this);
if (!m_other_identity_email.isEmpty())
w->set_email_address(m_other_identity_email);
if (!m_other_identity_name.isEmpty())
w->set_name(m_other_identity_name);
if (w->exec() == QDialog::Accepted) {
m_other_identity_email = w->email_address();
m_other_identity_name = w->name();
m_action_identity_other->setChecked(true);
m_action_edit_other->setEnabled(true);
change_identity(); // will update 'm_from'
}
else {
// if the dialog has been cancelled we need to restore the previous identity in
// the menu through the action group
QMap<QAction*,mail_identity*>::const_iterator it;
for (it=m_identities_actions.begin(); it != m_identities_actions.end(); ++it) {
if (it.value()!= NULL && it.value()->m_email_addr==m_from) {
// it.key()->blockSignals(true);
it.key()->setChecked(true);
// it.key()->blockSignals(false);
m_action_edit_other->setEnabled(false);
break;
}
}
}
w->close();
}
void
new_mail_widget::change_identity()
{
DBG_PRINTF(3, "change_identity()");
QString old_from=m_from;
QString old_sig;
QString new_sig;
identities::iterator iter;
for (iter = m_ids.begin(); iter != m_ids.end(); ++iter) {
mail_identity* p = &iter->second;
if (old_from == p->m_email_addr) {
old_sig = expand_signature(p->m_signature, *p);
}
}
QAction* action = m_identities_group->checkedAction();
// if the identity is the "other one" (user input) which
// doesn't belong to m_ids
if (action==m_action_identity_other) {
new_sig="";
m_from = m_other_identity_email;
}
else {
// get at the identity by the action associated to it
QMap<QAction*, mail_identity*>::const_iterator i = m_identities_actions.find(action);
if (i!=m_identities_actions.constEnd()) {
new_sig = (*i)->m_signature;
new_sig = expand_signature(new_sig, **i);
m_from = (*i)->m_email_addr;
}
m_action_edit_other->setEnabled(false);
}
if (old_sig != new_sig && !(old_sig.isEmpty() && new_sig.isEmpty())) {
// try to locate the old signature at the end of the body
QString body=m_bodyw->toPlainText();
int idxb=body.lastIndexOf(old_sig);
if (idxb>=0) {
// if located, replace it with the signature of the new identity
body.replace(idxb, old_sig.length(), new_sig);
m_bodyw->setPlainText(body);
}
}
DBG_PRINTF(3, "Changing from to %s", m_from.toLatin1().constData());
setWindowTitle(tr("New mail from ")+m_from);
}
// Edit the current message's private note
void
new_mail_widget::edit_note()
{
note_widget* w=new note_widget(this);
w->set_note_text(m_note);
int ret=w->exec();
if (ret) {
m_note=w->get_note_text();
display_note();
}
w->close();
}
void
new_mail_widget::open_global_notepad()
{
notepad* n = notepad::open_unique();
if (n) {
n->show();
n->activateWindow();
n->raise();
}
}
void
new_mail_widget::display_note()
{
attch_lvitem* lvpItem = dynamic_cast<attch_lvitem*>(m_qAttch->topLevelItem(0));
uint index=0;
while (lvpItem) {
if (!lvpItem->get_attachment()) {
break; // note found
}
index++;
lvpItem = dynamic_cast<attch_lvitem*>(m_qAttch->topLevelItem(index));
}
if (m_note.isEmpty()) {
if (lvpItem) { // case 2
delete lvpItem; // remove the note from the listview
lvpItem=NULL;
}
}
else {
if (!lvpItem) { // case 3
lvpItem = new attch_lvitem(m_qAttch, NULL);
}
// case 3 & 4
lvpItem->set_note(m_note);
lvpItem->fill_columns();
}
// don't show the attachments & note's listview when it's empty
if (m_qAttch->topLevelItem(0))
m_qAttch->show();
else
m_qAttch->hide();
}
void
new_mail_widget::toggle_wrap(bool wrap)
{
m_wrap_lines = wrap;
set_wrap_mode();
}
void
new_mail_widget::toggle_tags_panel(bool show_panel)
{
if (show_panel)
m_wtags->show();
else
m_wtags->hide();
}
void
new_mail_widget::show_tags()
{
m_wtags->set_tags(m_msg.get_tags());
}
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();
}
void
new_mail_widget::closeEvent(QCloseEvent* e)
{
int res=QMessageBox::Ok;
if (m_close_confirm && (m_bodyw->document()->isModified() || m_html_edit->isModified())) {
res=QMessageBox::warning(this, APP_NAME, tr("The message you are composing in this window will be lost if you confirm."), QMessageBox::Ok, QMessageBox::Cancel|QMessageBox::Default, Qt::NoButton);
}
if (res==QMessageBox::Ok)
e->accept();
else
e->ignore();
}
void
new_mail_widget::cancel()
{
close();
}
void
new_mail_widget::keep()
{
// TODO
}
void
new_mail_widget::send()
{
if (m_wtags)
m_wtags->get_selected(m_msg.get_tags());
// collect the addresses from the header field editors
QMap<QString,QString> addresses;
QListIterator<header_field_editor*> iter(m_header_editors);
while (iter.hasNext()) {
header_field_editor* ed = iter.next();
QString field_name = ed->get_field_name();
QString value = expand_aliases(ed->get_value());
if (!field_name.isEmpty() && !value.isEmpty()) {
if (addresses.contains(field_name)) {
// append the value
addresses[field_name].append(QString(", %1").arg(value));
}
else {
addresses.insert(field_name, value);
}
}
}
if (!addresses.contains("To") && !addresses.contains("Bcc")) {
QMessageBox::critical(NULL, tr("Error"), tr("The To: and Bcc: field cannot be both empty"), QMessageBox::Ok, Qt::NoButton);
return;
}
if (m_ids.empty()) {
QMessageBox::critical(NULL, tr("Error"), tr("The message has no sender (create an e-mail identity in the Preferences)"), QMessageBox::Ok, Qt::NoButton);
return;
}
if (m_wSubject->text().isEmpty()) {
int res=QMessageBox::warning(this, APP_NAME, tr("The message has no subject.\nSend nonetheless?"), QMessageBox::Ok, QMessageBox::Cancel|QMessageBox::Default, Qt::NoButton);
if (res!=QMessageBox::Ok)
return;
}
if (m_edit_mode == html_mode) {
// Collect the attachments refered to by the HTML contents, and generated
// MIME content-id's for them.
QStringList local_names = m_html_edit->collect_local_references();
QMap<QString,QString> map_local_cid;
attachments_list& alist = m_msg.attachments();
QStringListIterator it(local_names);
// For each reference to a distinct local file, we create a new attachment
while (it.hasNext()) {
QString filename = it.next();
// Unicity test, because we don't want to create different attachments
// for several references to the same file. That may happen for
// smiley pictures, for example
if (!map_local_cid.contains(filename)) {
attachment attch;
QString external_filename = filename;
if (external_filename.startsWith("file://", Qt::CaseInsensitive))
external_filename = external_filename.mid(strlen("file://")); // remove the scheme
// TODO: what if we have file:///home/file and /home/file in local_names?
#ifdef Q_OS_WIN
if (external_filename.startsWith("/")) {
external_filename = external_filename.mid(1);
}
#endif
attch.set_filename(external_filename);
attch.get_size_from_file();
attch.set_mime_type(attachment::guess_mime_type(filename));
attch.create_mime_content_id();
alist.push_back(attch);
map_local_cid[filename] = attch.mime_content_id();
}
}
// Finally, translate the local names to the CIDs references in
// the HTML document
if (!map_local_cid.empty()) {
m_html_edit->replace_local_references(map_local_cid);
}
}
try {
make_message(addresses);
}
catch(QString error_msg) {
QMessageBox::critical(NULL, tr("Error"), error_msg);
return;
}
if (m_msg.store()) {
/* If this message was a reply, then tell to the originator mailitem
to update it's status */
if (m_msg.inReplyTo() != 0) {
DBG_PRINTF(5, "refresh_request for %d", m_msg.inReplyTo());
emit refresh_request(m_msg.inReplyTo());
}
else if (m_msg.forwardOf().size() != 0) {
const std::vector<mail_id_t>& v = m_msg.forwardOf();
for (uint i=0; i < v.size(); i++) {
emit refresh_request(v[i]);
}
}
m_close_confirm=false;
close();
}
else {
QMessageBox::critical(NULL, tr("Error"), "Error while saving the message");
}
}
void
new_mail_widget::attach_files()
{
QStringList l=QFileDialog::getOpenFileNames(this, tr("Select files to attach"), m_last_attch_dir);
QStringList::Iterator it;
for (it=l.begin(); it!=l.end(); it++) {
attachment attch;
attch.set_filename((*it));
attch.get_size_from_file();
attch.set_mime_type(attachment::guess_mime_type(*it));
m_msg.attachments().push_back(attch);
attachment& attch1 = m_msg.attachments().back();
// Insert the attachment's listviewitem at the end of the listview
attch_lvitem* pItem=new attch_lvitem(m_qAttch, &attch1);
pItem->fill_columns();
}
m_qAttch->show();
}
void
new_mail_widget::insert_file()
{
QString file_contents;
QString name = QFileDialog::getOpenFileName(this);
if (name.isEmpty())
return;
QFile f(name);
bool opened=f.open(QIODevice::ReadOnly | QIODevice::Text);
if (opened) {
while (!f.atEnd() && !f.error()) {
char buf[8192+1];
qint64 n_read;
while ((n_read=f.read(buf, sizeof(buf)-1)) >0) {
buf[(uint)n_read] = '\0';
file_contents += buf;
}
}
}
if (f.error() || !opened) {
file_contents=QString::null;
QString errstr;
if (f.error()==QFile::OpenError || !opened) {
errstr=tr("Unable to open file '%1'").arg(name);
}
else {
errstr=tr("Unable to read file '%1': error #%2").arg(name).arg(f.error());
}
QMessageBox::information(this, APP_NAME, errstr);
}
if (!file_contents.isEmpty()) {
m_bodyw->insertPlainText(file_contents);
}
}
void
new_mail_widget::make_message(const QMap<QString,QString>& user_headers)
{
mail_header& h=m_msg.header();
m_msg.set_note(m_note);
QTextDocument* doc = m_bodyw->document();
if (m_edit_mode == plain_mode) {
m_msg.set_body_text(doc->toPlainText());
m_msg.set_body_html("");
}
else {
m_msg.set_body_html(m_html_edit->html_text());
m_msg.set_body_text(m_html_edit->to_plain_text());
}
mail_address addr;
identities::iterator iter;
for (iter = m_ids.begin(); iter != m_ids.end(); ++iter) {
mail_identity* p = &iter->second;
if (m_from == p->m_email_addr) {
addr.set_name(p->m_name);
addr.set_email(p->m_email_addr);
h.m_xface = p->m_xface;
break;
}
}
if (iter==m_ids.end()) {
// if no identity has been found with 'm_ids', it has to mean
// that the "other" identity has been selected
if (!m_other_identity_email.isEmpty()) {
addr.set_email(m_other_identity_email);
addr.set_name(m_other_identity_name);
}
else {
throw(tr("Error: no sender can be assigned to the message"));
}
}
h.m_from = addr.email_and_name();
h.m_sender = addr.email();
h.m_sender_fullname = addr.name();
h.m_to = user_headers.value("To");
/* Remove problematic characters from the subject. Despite being a
single-line edit, it is apparently possible to paste newlines
into the text. */
h.m_subject = m_wSubject->text().trimmed();
h.m_subject.replace("\n\r", " ");
h.m_subject.replace("\n", " ");
h.m_subject.replace("\r", " ");
h.m_subject.replace("\t", " ");
h.m_cc = user_headers.value("Cc");
h.m_replyTo = user_headers.value("ReplyTo");
h.m_bcc = user_headers.value("Bcc");
m_msg.setStatus(mail_msg::statusOutgoing + mail_msg::statusRead);
}
QString
new_mail_widget::expand_aliases(const QString addresses)
{
if (addresses.isEmpty())
return QString("");
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;
bool found;
if (!(iter1->indexOf('@')==-1 &&
addr.fetch_by_nickname(*iter1, &found) && found)) {
addr.set_email(*iter1);
addr.set_name(*iter2);
}
// else fetch_by_nickname has set up addr contents
result.append(addr.email_and_name());
result.append(",");
}
// remove the trailing ',' if necessary
if (!result.isEmpty() && result.at(result.length()-1) == ',')
result.truncate(result.length()-1);
return result;
}
void
new_mail_widget::start_edit()
{
m_bodyw->setFocus();
m_bodyw->document()->setModified(false);
}
const mail_identity*
new_mail_widget::get_current_identity()
{
identities::const_iterator iter = m_ids.find(m_from);
return (iter!=m_ids.end() ? &iter->second : NULL);
}
void
new_mail_widget::insert_signature()
{
const mail_identity* id = get_current_identity();
if (id) {
QString sig = expand_signature(id->m_signature, *id);
if (sig.at(0)!='\n')
sig.prepend("\n");
if (m_edit_mode == plain_mode) {
// insert the signature and leave the cursor just above
QTextCursor cursor = m_bodyw->textCursor();
cursor.movePosition(QTextCursor::End);
int pos = cursor.position();
m_bodyw->appendPlainText(sig);
cursor.setPosition(pos);
m_bodyw->setTextCursor(cursor);
}
else if (m_edit_mode == html_mode) {
QString html_sig = mail_displayer::htmlize(sig);
html_sig = "<p><div class=\"manitou-sig\">" + html_sig + "</div>";
m_html_edit->append_paragraph(html_sig);
}
}
}
QString
new_mail_widget::expand_signature(const QString sig, const mail_identity& identity)
{
QString esig=sig;
bool user_fetched = false;
user u;
int pos=0;
QRegExp rx("\\{(\\w+)\\}");
while ((pos=rx.indexIn(esig, pos))>=0) {
const QString field = rx.cap(1);
if (!user_fetched && field.startsWith("operator")) {
int user_id = user::current_user_id();
if (user_id>0)
u.fetch(user_id);
}
QString field_val;
bool use_field = true;
if (field=="operator_login") {
field_val = u.m_login;
}
else if (field=="operator_firstname") {
int bpos = u.m_fullname.indexOf(' ');
if (bpos>=1) {
field_val = u.m_fullname.left(bpos);
}
}
else if (field=="operator_fullname") {
field_val = u.m_fullname;
}
else if (field=="operator_email") {
field_val = u.m_email;
}
else if (field=="operator_custom_field1") {
field_val = u.m_custom_field1;
}
else if (field=="operator_custom_field2") {
field_val = u.m_custom_field2;
}
else if (field=="operator_custom_field3") {
field_val = u.m_custom_field3;
}
else if (field=="sender_email") {
field_val = identity.m_email_addr;
}
else if (field=="sender_name") {
field_val = identity.m_name;
}
else
use_field=false;
if (use_field) {
esig.replace(pos, rx.matchedLength(), field_val);
pos += field_val.length();
}
else
pos += rx.matchedLength();
}
return esig;
}
void
new_mail_widget::change_font()
{
bool ok;
QFont f=QFontDialog::getFont(&ok, m_bodyw->font());
if (ok) {
get_config().set_string("newmail/font", f.toString());
m_bodyw->setFont(f);
}
}
void
new_mail_widget::store_settings()
{
// Save the font
app_config& conf=get_config();
if (conf.store("newmail/font")) {
QString user_msg;
if (conf.name().isEmpty())
user_msg = QString(tr("The display settings have been saved in the default configuration."));
else
user_msg = QString(tr("The display settings have been saved in the '%1' configuration.")).arg(conf.name());
QMessageBox::information(NULL, tr("Confirmation"), user_msg);
}
}
body_edit_widget::body_edit_widget(QWidget* p)
: QPlainTextEdit(p)
{
// setTextFormat(Qt::PlainText);
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));
}
}
void
new_mail_widget::print()
{
QTextDocument* doc = m_bodyw->document();
QPrinter printer;
QPrintDialog *dialog = new QPrintDialog(&printer, this);
dialog->setWindowTitle(tr("Print Document"));
if (dialog->exec() != QDialog::Accepted)
return;
doc->print(&printer);
}
///
/// 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