Manitou-Mail logo title

Source file: src/attachment_listview.cpp

/* Copyright (C) 2004-2012 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 "attachment_listview.h"
#include "app_config.h"
#include "main.h"
#include <QAction>
#include <QFile>
#include <QFileInfo>
#include <QHeaderView>
#include <QMenu>
#include <QPlainTextEdit>
#include <QUrl>
#include <QDialog>
#include <QDialogButtonBox>
#include <QBoxLayout>
#include <QLabel>
#include <QRadioButton>
#include <QPushButton>
#include <QFileDialog>
#include <QMessageBox>

// *************** //
//  attch_lvitem
// *************** //

void
attch_lvitem::fill_columns()
{
  QString sSize;
  if (m_pAttachment && m_type==0) {
    setText(0, m_pAttachment->mime_type());
    setText(1, m_pAttachment->filename());
    sSize.sprintf("%d", m_pAttachment->size());
  }
  else if (m_type==1) {
    setText(0, "(private note)");
    setText(1, m_note);
    sSize.sprintf("%d", m_note.length());
    QBrush brush(QColor("#f1db20"));
    setBackground(0, brush);
    setBackground(1, brush);
    setBackground(2, brush);
  }
  setText(2, sSize);
  setTextAlignment(0, Qt::AlignTop);
  setTextAlignment(1, Qt::AlignTop);
  setTextAlignment(2, Qt::AlignTop);
}


void
attch_lvitem::set_note(const QString& note)
{
  m_note=note;
  m_type=1;
}

/* download the attachment into 'destfilename'
   stop when finished or when *abort becomes true, which may
   happen in the same thread by way of progress_report()
   calling processEvents() */
bool
attch_lvitem::download(const QString destfilename, bool *abort)
{
  attachment* pa = m_pAttachment;
  DBG_PRINTF(5, "saving attachment into %s", destfilename.toLocal8Bit().constData());
  struct attachment::lo_ctxt lo;
  if (pa->open_lo(&lo)) {
    QByteArray qba = destfilename.toLocal8Bit();
    std::ofstream of(qba.constData(), std::ios::out|std::ios::binary);
    lview()->progress_report(-(int)((lo.size+lo.chunk_size-1)/lo.chunk_size));
    int steps=0;
    int r;
    do {
      r = pa->streamout_chunk(&lo, of);
      lview()->progress_report(++steps);
    } while (r && (!abort || *abort==false));
    pa->close_lo(&lo);
    of.close();
    if (*abort) {
      QFile::remove(destfilename);
    }
    return true;
  }
  return false;
}

QString
attch_lvitem::save_to_disk(const QString fname, bool* abort)
{
  download(fname, abort);
  return QFileInfo(fname).canonicalPath();
}

attch_listview::attch_listview(QWidget* parent):
  QTreeWidget(parent), m_pAttchList(NULL)
{
  QString fontname=get_config().get_string("display/font/msglist");
  if (!fontname.isEmpty() && fontname!="xft") {
    QFont f;
    if (fontname.at(0)=='-')
      f.setRawName(fontname);
    else
      f.fromString(fontname);
    setFont(f);
  }

  setColumnCount(3);
  QStringList labels;
  labels << tr("Type") << tr("Filename/Note") << tr("Size");
  setHeaderLabels(labels);
  static const int header_lengths[] = { 25, 50, 6 };
  QTreeWidgetItem* header = headerItem();
  QFontMetrics fm(header->font(0));
  QHeaderView* hv = this->header();
  for (uint col=0; col<sizeof(header_lengths)/sizeof(header_lengths[0]); col++) {
    hv->resizeSection(col, header_lengths[col]*fm.averageCharWidth());
  }
  setRootIsDecorated(false);
  setSelectionMode(QAbstractItemView::ExtendedSelection);

  setContextMenuPolicy(Qt::CustomContextMenu);
  connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
	  this, SLOT(popup_ctxt_menu(const QPoint&)));

}

attch_listview::~attch_listview()
{
}

QString attch_listview::m_last_attch_dir;

void
attch_listview::popup_ctxt_menu(const QPoint& pos)
{
  attch_lvitem* item = dynamic_cast<attch_lvitem*>(itemAt(pos));
  if (!item)
    return;
  attachment* pa = item->get_attachment();
  if (!pa)
    return; // it may be a note

  QString app_name = pa->default_os_application();
  QMenu menu(this);
  QAction* action_open_app = NULL;
  if (!app_name.isEmpty())
    action_open_app = menu.addAction(tr("Open with: %1").arg(app_name));
  QAction* action_save = menu.addAction(tr("Save to disk"));
  QAction* action_view_text = menu.addAction(tr("Display as text"));

  QAction* action = menu.exec(mapToGlobal(pos));
  if (action==NULL) {
    return;
  }
  else if (action==action_view_text) {
    const char* contents = pa->get_contents();
    if (contents) {
      QPlainTextEdit* v = new QPlainTextEdit(NULL);
      v->resize(640,480);
      v->setPlainText(contents);
      v->setWindowTitle(tr("Attachment as text"));
      v->show();
    }
  }
  else if (action==action_open_app) {
    QString tmpname = pa->get_temp_location();
    emit init_progress(tr("Downloading attached file: %1").arg(tmpname));
    m_abort=false;
    item->download(tmpname, &m_abort);
    emit finish_progress();
    if (!m_abort)
      pa->launch_os_viewer(tmpname);
  }
  else if (action==action_save) {
    save_attachments();
  }
}

void
attch_listview::download_aborted()
{
  m_abort=true;
}

void
attch_listview::allow_delete(bool b)
{
  if (b) {
    QAction* m_delete_action = new QAction(this);
    m_delete_action->setShortcut(Qt::Key_Delete);
    this->addAction(m_delete_action);
    connect(m_delete_action, SIGNAL(triggered()), this, SLOT(remove_current_attachment()));
  }
}

void
attch_listview::remove_current_attachment()
{
  DBG_PRINTF(3, "remove attachment\n");
  attch_lvitem* pItem=dynamic_cast<attch_lvitem*>(currentItem());
  if (!pItem) return;
  QModelIndex midx = indexFromItem(pItem);
  int pos=midx.row();
  std::list<attachment>::iterator it=m_pAttchList->begin();
  for (int i=0; it!=m_pAttchList->end(); ++it,++i) {
    if (i==pos) {
      m_pAttchList->erase(it);
      delete pItem;
      break;
    }
  }
}

void
attch_listview::progress_report(int count)
{
  emit progress(count);
}


bool
attch_listview::dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action)
{
  Q_UNUSED(parent);
  Q_UNUSED(index);
  Q_UNUSED(action);
  foreach (QUrl url, data->urls())  {
    emit attach_file_request(url);
  }
  return false;
}

Qt::DropActions
attch_listview::supportedDropActions() const
{
  return Qt::CopyAction | Qt::MoveAction;
}

QStringList
attch_listview::mimeTypes() const
{
  return QStringList("text/uri-list");
}

bool
attch_listview::confirm_write(const QString fname)
{
  if (QFile::exists(fname)) {
    int res = QMessageBox::question(this, QObject::tr("Please confirm"), QObject::tr("A file '%1' already exists. Overwrite?").arg(fname), QObject::tr("&Yes"), QObject::tr("&No"), QString::null, 0, 1);
    if (res==1)
      return false;
  }
  return true;
}


void
attch_listview::save_attachments()
{
  // list of attachments with a filename
  std::list<attch_lvitem*> list_name;
  // list of attachments without a filename
  std::list<attch_lvitem*> list_noname;

  QList<QTreeWidgetItem*> list = this->selectedItems();
  QList<QTreeWidgetItem*>::const_iterator itw = list.begin();
  for (; itw!=list.end(); ++itw) {
    attch_lvitem* item = static_cast<attch_lvitem*>(*itw);
    if (item->is_note())
      continue;
    if (!item->get_attachment()->filename().isEmpty())
      list_name.push_back(item);
    else
      list_noname.push_back(item);
  }
  if (list_name.empty() && list_noname.empty()) {
    QMessageBox::warning(this, APP_NAME, tr("Please select one or several attachments"));
    return;
  }

  QString fname;
  QString dir=attch_listview::m_last_attch_dir;
  if (dir.isEmpty()) {
    dir = get_config().get_string("attachments_directory");
  }
  std::list<attch_lvitem*>::iterator it;
  if (list_name.size()>1) {
    // several named attachments: ask only for the directory
    dir = QFileDialog::getExistingDirectory(this, tr("Save to directory..."), dir);
    if (!dir.isEmpty()) { // if accepted
      for (it=list_name.begin(); it!=list_name.end(); ++it) {
	attachment* pa = (*it)->get_attachment();
	fname = dir + "/" + pa->filename();
	if (!confirm_write(fname))
	  continue;
	emit init_progress(tr("Downloading attachment into: %1").arg(fname));
	m_abort=false;
	dir=(*it)->save_to_disk(fname, &m_abort);
	emit finish_progress();
	if (m_abort)
	  break;
      }
    }
    else
      return;			// cancel
  }
  if (list_name.size()==1) {
    it=list_name.begin();
    attachment* pa = (*it)->get_attachment();
    fname = pa->filename();
    if (!dir.isEmpty()) {
      fname = dir + "/" + fname;
    }
    fname = QFileDialog::getSaveFileName(this, tr("File"), fname);
    DBG_PRINTF(5, "fname=%s", fname.toLocal8Bit().constData());
    if (!fname.isEmpty()) {
      emit init_progress(tr("Downloading attached file: %1").arg(fname));
      m_abort=false;
      dir=(*it)->save_to_disk(fname, &m_abort);
      emit finish_progress();
    }
  }
  if (!list_noname.empty()) {
    for (it=list_noname.begin(); it!=list_noname.end(); ++it) {
      fname = QFileDialog::getSaveFileName(this, tr("File"), dir);
      if (!fname.isEmpty()) {
	emit init_progress(tr("Downloading attachment into: %1").arg(fname));
	m_abort=false;
	dir=(*it)->save_to_disk(fname, &m_abort);
	emit finish_progress();
      }
      else
	break;
    }
  }
  if (!dir.isEmpty())
    attch_listview::m_last_attch_dir=dir;
}

attch_dialog_save::attch_dialog_save()
{
  QVBoxLayout* layout = new QVBoxLayout(this);
  layout->setMargin(5);
  setWindowTitle(tr("Open or save"));
  m_label_file = new QLabel;
  m_label_file->setTextFormat(Qt::PlainText);
  layout->addWidget(m_label_file);
  m_rb_launch = new QRadioButton(tr("Open with: %1").arg(""));
  QRadioButton* b2 = new QRadioButton(tr("Save to disk"));
  b2->setChecked(true);
  layout->addWidget(m_rb_launch);
  layout->addWidget(b2);
  QDialogButtonBox* box = new QDialogButtonBox;
  layout->addWidget(box);
  box->addButton(tr("OK"), QDialogButtonBox::AcceptRole);
  box->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
  connect(box, SIGNAL(accepted()), this, SLOT(accept()));
  connect(box, SIGNAL(rejected()), this, SLOT(reject()));
}

void
attch_dialog_save::set_app_name(const QString name)
{
  m_rb_launch->setText(tr("Open with: %1").arg(name));
}

void
attch_dialog_save::set_file_name(const QString name)
{
  m_label_file->setText(tr("File: %1").arg(name));
}

int
attch_dialog_save::choice()
{
  return (m_rb_launch->isChecked()) ? 2 : 1;
}

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

List of all available source files