Source file: src/newmailwidget.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.
*/
#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 "mail_template.h"
#include <QCloseEvent>
#include <QComboBox>
#include <QCursor>
#include <QDropEvent>
#include <QFileDialog>
#include <QFontDialog>
#include <QGridLayout>
#include <QInputDialog>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLabel>
#include <QList>
#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
#include <QPrintDialog>
#include <QPrinter>
#include <QToolButton>
#include <QSplitter>
#include <QTextDocument>
#include <QTreeWidgetItem>
#include <QToolBar>
#include <QVBoxLayout>
#include <QStackedWidget>
#include <QRegExp>
#include <QStatusBar>
#include <QProgressBar>
/***********************/
/* 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(triggered()), 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(triggered()), 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(triggered()), 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(triggered()), 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(triggered()), 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(triggered()), 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;
m_progress_bar = 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();
connect(m_bodyw, SIGNAL(attach_file_request(const QUrl)),
this, SLOT(attach_file(const QUrl)));
m_html_edit = new html_editor();
connect(m_html_edit, SIGNAL(attach_file_request(const QUrl)),
this, SLOT(attach_file(const QUrl)));
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->setAcceptDrops(true);
m_qAttch->hide();
m_qAttch->set_attch_list(&m_msg.attachments());
connect(m_qAttch, SIGNAL(attach_file_request(const QUrl)),
this, SLOT(attach_file(const QUrl)));
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(UI_ICON(ICON16_LOAD_TEMPLATE), tr("Load template"),
this, SLOT(load_template()));
pMsg->addAction(UI_ICON(ICON16_SAVE_TEMPLATE), tr("Save as template"),
this, SLOT(save_template()));
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(this, 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->setLineWrapColumnOrWidth(78);
m_bodyw->setLineWrapMode(QTextEdit::FixedColumnWidth);
}
else {
m_bodyw->setLineWrapMode(QTextEdit::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());
}
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);
QStringList bad_addresses;
while (iter.hasNext()) {
header_field_editor* ed = iter.next();
QString field_name = ed->get_field_name();
bool errparse;
QString value = check_addresses(ed->get_value(), bad_addresses, &errparse);
if (errparse) {
QMessageBox::critical(NULL, tr("Syntax error"), tr("Invalid syntax in email addresses:")+"<br><i>"+ed->get_value()+"</i>", QMessageBox::Ok, Qt::NoButton);
return;
}
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 (!bad_addresses.isEmpty()) {
QString msg;
if (bad_addresses.count()>1)
msg = tr("The following email addresses are invalid:")+"<br><i>"+bad_addresses.join("<br>")+"</i>";
else
msg = tr("Invalid email address:")+"<br><i>"+bad_addresses.at(0)+"</i>";
QMessageBox::critical(this, tr("Address error"), msg,
QMessageBox::Ok, Qt::NoButton);
return;
}
if (!addresses.contains("To") && !addresses.contains("Bcc")) {
QMessageBox::critical(this, tr("Error"), tr("The To: and Bcc: field cannot be both empty"), QMessageBox::Ok, Qt::NoButton);
return;
}
if (m_ids.empty()) {
QMessageBox::critical(this, 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, tr("Warning"), 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(this, tr("Error"), error_msg);
return;
}
ui_feedback* ui = new ui_feedback(this);
statusBar()->showMessage(tr("Saving message"));
if (!m_progress_bar) {
m_progress_bar = new QProgressBar(this);
statusBar()->addPermanentWidget(m_progress_bar);
}
connect(ui, SIGNAL(set_max(int)), m_progress_bar, SLOT(setMaximum(int)));
connect(ui, SIGNAL(set_val(int)), m_progress_bar, SLOT(setValue(int)));
connect(ui, SIGNAL(set_text(const QString&)), statusBar(), SLOT(showMessage(const QString&)));
if (m_msg.store(ui)) {
/* 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(this, tr("Error"), "Error while saving the message");
statusBar()->showMessage(tr("Send failed."), 3000);
if (m_progress_bar) {
delete m_progress_bar;
m_progress_bar=NULL;
}
}
}
void
new_mail_widget::attach_files()
{
if (m_last_attch_dir.isEmpty())
m_last_attch_dir = get_config().get_string("attachments_directory");
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));
m_last_attch_dir = QFileInfo(*it).canonicalPath();
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::attach_file(const QUrl url)
{
QFileInfo info(url.toLocalFile());
if (info.isReadable()) {
attachment attch;
attch.set_filename(info.absoluteFilePath());
attch.get_size_from_file();
attch.set_mime_type(attachment::guess_mime_type(info.absoluteFilePath()));
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;
m_msg.set_identity_id(p->m_identity_id);
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);
m_msg.set_identity_id(0);
}
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::check_addresses(const QString addresses,
QStringList& bad_addresses,
bool* unparsable /*=NULL*/)
{
if (addresses.isEmpty())
return QString("");
if (unparsable)
*unparsable=false;
std::list<QString> emails_list;
std::list<QString> names_list;
int err = mail_address::ExtractAddresses(addresses, emails_list, names_list);
QString result;
if (err && unparsable) {
*unparsable=true;
return result;
}
bool do_basic_check=(get_config().get_string("composer/address_check")=="basic");
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;
if (do_basic_check) {
if (!mail_address::basic_syntax_check(*iter1)) {
bad_addresses.append(*iter1);
}
}
addr.set_email(*iter1);
addr.set_name(*iter2);
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.length()>0 && 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->append(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(this, tr("Confirmation"), user_msg);
}
}
void
new_mail_widget::load_template()
{
template_dialog dialog;
int r = dialog.exec();
if (r==QDialog::Accepted) {
int id = dialog.selected_template_id();
if (id>0) {
m_template.load(id);
// TODO: ask confirmation before loosing the current text if any
if (m_template.html_body().isEmpty()) {
set_body_text(m_template.text_body());
format_plain_text();
}
else {
m_html_edit->set_html_text(m_template.html_body());
m_html_edit->finish_load();
m_html_edit->setFocus();
format_html_text();
}
}
}
}
void
new_mail_widget::save_template()
{
QString title;
bool ok;
if (m_template.m_template_id>0) {
// a template has been loaded
title = m_template.m_title;
}
bool keep_asking=false;
do {
title = QInputDialog::getText(this, tr("Save template"), tr("Title of the template:"),
QLineEdit::Normal, title, &ok);
if (title.isEmpty()) {
// TODO: display an error message
keep_asking=true;
}
// TODO: check if the title is already used in the database
} while (ok && keep_asking);
if (ok) {
try {
QMap<QString,QString> addresses;
make_message(addresses);
}
catch(QString error_msg) {
QMessageBox::critical(this, tr("Error"), error_msg);
return;
}
m_template.m_title = title;
QString header;
// TODO: add other header fields?
header = "Subject: " + m_msg.header().m_subject;
m_template.set_header(header);
if (m_edit_mode == plain_mode) {
QTextDocument* doc = m_bodyw->document();
m_template.set_text_body(doc->toPlainText());
m_template.set_html_body(QString::null);
}
else {
m_template.set_html_body(m_html_edit->html_text());
QString text=m_html_edit->to_plain_text();
/* is translated to 0xA0 but we prefer the normal space character
for our usage in mail text parts */
text.replace(QChar(0xa0), QChar(0x20));
m_template.set_text_body(text);
}
if (m_template.m_template_id>0)
m_template.update();
else
m_template.insert();
}
}
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);
}
HTML source code generated by GNU Source-Highlight plus some custom post-processing
List of all available source files