3

I need my QT application to create a table and copy this table into the clipboard, so that it can be pasted as table into libreoffice Writer or MS Word later.

My first approach was to create html code for the table and insert it into the clipboard with

QClipboard *clipboard = QApplication::clipboard();
QMimeData *mimeData = new QMimeData();
mimeData->setData("text/html", html.toUtf8());
clipboard->setMimeData(mimeData, QClipboard::Clipboard);

This approach didn't work. When pasting, the table cells where just appended to each other and inserted without formatting.

My second approach using RTF:

QTextDocument rtfDocument;
rtfDocument.setHtml(html);

But I found no way to copy this QTextDocument into the clipboard. Is there any? If I could get the RTF code out of the QTextDocument, I could use a way like

QClipboard *clipboard = QApplication::clipboard();
QMimeData *mimeData = new QMimeData();
mimeData->setData("text/rtf", rtfDocument.getCode());
clipboard->setMimeData(mimeData, QClipboard::Clipboard);

But I also didn't find a function returning the rtf code.

edit:

With the last code box above I have a working way to copy rtf code into the clipboard. So any solution that can create RTF code representing a table would solve my problem.

Heinzi
  • 5,793
  • 4
  • 40
  • 69
  • According to an answer on [this SO question](http://stackoverflow.com/questions/294343/read-and-write-rtf-files-with-c-qt), you can do RTF handling with [librtf](http://sourceforge.net/projects/librtf/). I'd check into that. – Attila Mar 26 '12 at 15:44
  • Unfortunatelly librtf only allows parsing, not creating rtf files. – Heinzi Mar 29 '12 at 12:22

3 Answers3

3

I'm not sure what the source of your data is, but here is code we used to subclass the normal QTableView to make it copy-able. Some of the code has been cut out, but you can get the basic idea. RTF/HTML is overkill--all the spreadsheets accept good ol' CSV.

Of course, this answer won't help at all if you require formatting. I wasn't clear from your question if that was a requirement or not.

// Escapes a string according to RFC-4180 specification.
static QString csvEscape(const QString &value) {
  if (value.contains(QRegExp(QLatin1String("[\"\\n\\r,]")))) {
    QString escaped(value);
    escaped.replace(QLatin1String("\""), QLatin1String("\"\""));
    return QString::fromLatin1("\"%1\"").arg(escaped);
  } else {
    return value;
  }
}

void ClipboardAwareTableView::Copy() const {
  QModelIndexList indexes = selectedIndexes();

  Q_ASSERT(!indexes.isEmpty());
  if(indexes.isEmpty()) {
    return;
  }

  // The default sort is by rows then columns. This is what we want.
  qSort(indexes);

  // Remember the mapping between model columns and visible columns. This is
  // local instead of an instance member because it would need to be invalidated
  // any time a column is added, removed, or moved. The minor performance hit
  // is worth the simplicity.
  QHash<int, int> map_cache;

  // Before we start exporting text, we have to know the index of the left-
  // most column in our selection range so we can add the appropriate number
  // of column separators.
  int minimum_column = GetViewColumnIndex(indexes.first().column(), &map_cache);
  for (int i = 1; i < indexes.size(); ++i) {
    minimum_column =
        qMin(minimum_column,
             GetViewColumnIndex(indexes.at(i).column(), &map_cache));
  }

  // Keep track of the previous index so that we know if we need a new line and
  // how many column separators to insert. We start with an invalid index.
  QModelIndex previous;

  QString text;

  for (int i = 0; i < indexes.size(); ++i) {
    QModelIndex current = indexes.at(i);

    // Do we need to add a new line character?
    if (previous.isValid() && current.row() != previous.row()) {
      text.append(QLatin1String("\n"));
    }

    // Are we on a new line?
    if (!previous.isValid() || current.row() != previous.row()) {
      // Add enough separators to get from the minimum to the current column.
      text.append(QString::fromLatin1(",")
                  .repeated(GetViewColumnIndex(current.column(), &map_cache) -
                            minimum_column));
    } else {
      // Add enough separators to get from the previous to the current column.
      text.append(QString::fromLatin1(",")
                  .repeated(GetViewColumnIndex(current.column(), &map_cache) -
                            GetViewColumnIndex(previous.column(), &map_cache)));
    }

    // Append the text. If the column delegate is a QStyledItemDelegate, we use
    // the display text.
    QStyledItemDelegate *delegate =
        qobject_cast<QStyledItemDelegate*>(
          itemDelegateForColumn(current.column()));
    if (delegate) {
      text.append(csvEscape(delegate->displayText(current.data(), QLocale())));
    } else {
      text.append(csvEscape(current.data().toString()));
    }

    previous = current;
  }

  qApp->clipboard()->setText(text);
}

int ClipboardAwareTableView::GetViewColumnIndex(
    int model_column_index,
    QHash<int, int> *cached_mappings) const {
  if (cached_mappings->contains(model_column_index)) {
    return cached_mappings->value(model_column_index);
  }

  int view_index = 0;
  for (int i = 0; i < model()->columnCount(); ++i) {
    if (model_column_index == i) {
      cached_mappings->insert(model_column_index, view_index);
      return view_index;
    } else if (!isColumnHidden(i)) {
      ++view_index;
    }
  }

  throw std::invalid_argument("model_column_index was out of range.");
}

void ClipboardAwareTableView::keyPressEvent(QKeyEvent *event) {
  if (event->matches(QKeySequence::Copy) && !selectedIndexes().isEmpty()) {
    Copy();
    event->accept();
    return;  // The base class implementation will overwrite the clipboard.
  }

  event->ignore();
  QTableView::keyPressEvent(event);
}
Dave Mateer
  • 17,608
  • 15
  • 96
  • 149
  • I need the table in MS Word/LO Writer, not in Excel/Calc. So I think I do need a format like rtf. Sorry for the unclear question - I changed it in a way making this more clear. – Heinzi Mar 29 '12 at 12:23
2

You could try using QTextDocument::toHtml() and set the mime type to text/html

Attila
  • 28,265
  • 3
  • 46
  • 55
  • This was my first approach, it didn't work (see question for details). I wrote the html code by myself, but that shouldn't be the problem. I get the same wrong results, when I copy a table out of firefox into LO Writer. I think that LO Writer just ignores the html tags. – Heinzi Mar 29 '12 at 12:48
  • I got it working. Libreoffice ignored the html tags because I had an error in there after all. – Heinzi Mar 29 '12 at 13:48
0

I wrote in gedit 1[tab space]2[tab space]3\n4[tab space]5[tab space]6 and copied it to spreadsheet and it worked. So, I think if you use "\t" for separating cells in rows and "\n" for separating rows, it will work.

godlark
  • 157
  • 7
  • I need the table in MS Word/LO Writer, not in Excel/Calc. So I think I do need a format like rtf. Sorry for the unclear question - I changed it in a way making this more clear. – Heinzi Mar 29 '12 at 12:24