Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Copy/Paste with header should work #5320

Merged
merged 1 commit into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions Desktop/components/JASP/Widgets/DataTableView.qml
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,6 @@ FocusScope
dataTableView.showPopupMenu(parent, mapToGlobal(mouseEvent.x, mouseEvent.y), rowIndex, -1);
}
}

}

columnHeaderDelegate: Rectangle
Expand All @@ -516,6 +515,30 @@ FocusScope
property real iconTextPadding: 10
readonly property int __iconDim: baseBlockDim * preferencesModel.uiScale

Keys.onPressed: (event) =>
{
var controlPressed = Boolean(event.modifiers & Qt.ControlModifier)

if (controlPressed)
{
switch(event.key)
{
case Qt.Key_C:
theView.copy(Qt.point(columnIndex, -1));
event.accepted = true;
break;
case Qt.Key_X:
theView.cut(Qt.point(columnIndex, -1));
event.accepted = true;
break;
case Qt.Key_V:
theView.paste(Qt.point(columnIndex, -1));
event.accepted = true;
break;
}
}
}

Image
{
id: colIcon
Expand All @@ -525,7 +548,7 @@ FocusScope


source: String(dataSetModel.getColumnTypesWithIcons()[columnType]) === "" ? "" : jaspTheme.iconPath + dataSetModel.getColumnTypesWithIcons()[columnType]
width: headerRoot.__iconDim
width: source == "" ? 0 : headerRoot.__iconDim
height: headerRoot.__iconDim

sourceSize { width: width * 2
Expand Down Expand Up @@ -696,6 +719,8 @@ FocusScope
{
if(columnIndex >= 0)
{
headerRoot.forceActiveFocus()

if(mouseEvent.button === Qt.LeftButton || mouseEvent.button === Qt.RightButton)
dataTableView.view.columnSelect(columnIndex, mouseEvent.modifiers & Qt.ShiftModifier, mouseEvent.button === Qt.RightButton)

Expand Down
6 changes: 2 additions & 4 deletions Desktop/data/datasetpackage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2083,7 +2083,7 @@ void DataSetPackage::setWorkspaceEmptyValues(const stringset &emptyValues, bool
emit workspaceEmptyValuesChanged();
}

void DataSetPackage::pasteSpreadsheet(size_t row, size_t col, const std::vector<std::vector<QString>> & cells, intvec coltypes)
void DataSetPackage::pasteSpreadsheet(size_t row, size_t col, const std::vector<std::vector<QString>> & cells, const intvec & coltypes, const QStringList & colNames)
{
JASPTIMER_SCOPE(DataSetPackage::pasteSpreadsheet);

Expand All @@ -2096,8 +2096,6 @@ void DataSetPackage::pasteSpreadsheet(size_t row, size_t col, const std::vector<

if(colCountChanged || rowCountChanged)
setDataSetSize(std::max(size_t(dataColumnCount()), colMax + col), std::max(size_t(dataRowCount()), rowMax + row));

stringvec colNames = getColumnNames();

stringvec changed;

Expand All @@ -2106,6 +2104,7 @@ void DataSetPackage::pasteSpreadsheet(size_t row, size_t col, const std::vector<
int dataCol = c + col;
stringvec colVals = getColumnDataStrs(dataCol);
columnType desiredType = coltypes.size() > c ? columnType(coltypes[c]) : columnType::unknown;
std::string colName = (colNames.size() > c && !colNames[c].isEmpty()) ? fq(colNames[c]) : getColumnName(dataCol);

for(int r=0; r<rowMax; r++)
{
Expand All @@ -2119,7 +2118,6 @@ void DataSetPackage::pasteSpreadsheet(size_t row, size_t col, const std::vector<
colVals[r + row] = cellVal == ColumnUtils::emptyValue ? "" : cellVal;
}

std::string colName = getColumnName(dataCol);
initColumnWithStrings(dataCol, colName, colVals, "", desiredType);

changed.push_back(colName);
Expand Down
2 changes: 1 addition & 1 deletion Desktop/data/datasetpackage.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ class DataSetPackage : public QAbstractItemModel //Not QAbstractTableModel becau
void initColumnWithStrings( QVariant colId, const std::string & newName, const stringvec & values, const std::string & title = "", columnType desiredType = columnType::unknown);
void initializeComputedColumns();

void pasteSpreadsheet(size_t row, size_t column, const std::vector<std::vector<QString>> & cells, intvec colTypes = intvec());
void pasteSpreadsheet(size_t row, size_t column, const std::vector<std::vector<QString>> & cells, const intvec & colTypes = intvec(), const QStringList & colNames = {});

void storeMissingData( const std::string & columnName, const intstrmap & missingData);

Expand Down
4 changes: 2 additions & 2 deletions Desktop/data/datasettablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ bool DataSetTableModel::columnUsedInEasyFilter(int column) const
).toBool();
}

void DataSetTableModel::pasteSpreadsheet(size_t row, size_t col, const std::vector<std::vector<QString> > & cells, std::vector<int> colTypes)
void DataSetTableModel::pasteSpreadsheet(size_t row, size_t col, const std::vector<std::vector<QString> > & cells, const std::vector<int> & colTypes, const QStringList & colNames)
{
QModelIndex idx = mapToSource(index(row, col));
DataSetPackage::pkg()->pasteSpreadsheet(idx.row(), idx.column(), cells, colTypes);
DataSetPackage::pkg()->pasteSpreadsheet(idx.row(), idx.column(), cells, colTypes, colNames);
}

QString DataSetTableModel::insertColumnSpecial(int column, const QMap<QString, QVariant>& props)
Expand Down
2 changes: 1 addition & 1 deletion Desktop/data/datasettablemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class DataSetTableModel : public DataSetTableProxy

int getColumnIndex(const std::string& col) const { return DataSetPackage::pkg()->getColumnIndex(col); }
bool synchingData() const { return DataSetPackage::pkg()->synchingData(); }
void pasteSpreadsheet(size_t row, size_t col, const std::vector<std::vector<QString>> & cells, std::vector<int> colTypes = std::vector<int>());
void pasteSpreadsheet(size_t row, size_t col, const std::vector<std::vector<QString>> & cells, const std::vector<int> & colTypes = std::vector<int>(), const QStringList & colNames = {});
bool showInactive() const { return _showInactive; }

QString insertColumnSpecial(int column, const QMap<QString, QVariant>& props);
Expand Down
4 changes: 2 additions & 2 deletions Desktop/data/expanddataproxymodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,13 +230,13 @@ void ExpandDataProxyModel::setData(int row, int col, const QVariant &value, int
_undoStack->endMacro(new SetDataCommand(_sourceModel, row, col, value, role));
}

void ExpandDataProxyModel::pasteSpreadsheet(int row, int col, const std::vector<std::vector<QString>> & cells)
void ExpandDataProxyModel::pasteSpreadsheet(int row, int col, const std::vector<std::vector<QString>> & cells, const QStringList& colNames)
{
if (!_sourceModel || row < 0 || col < 0 || cells.size() == 0 || cells[0].size() == 0)
return;

_expandIfNecessary(row + cells[0].size() - 1, col + cells.size() - 1);
_undoStack->endMacro(new PasteSpreadsheetCommand(_sourceModel, row, col, cells));
_undoStack->endMacro(new PasteSpreadsheetCommand(_sourceModel, row, col, cells, colNames));
}

int ExpandDataProxyModel::setColumnType(int columnIndex, int columnType)
Expand Down
2 changes: 1 addition & 1 deletion Desktop/data/expanddataproxymodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class ExpandDataProxyModel : public QObject
void removeColumns(int start, int count);
void insertRow(int row);
void insertColumn(int col, bool computed, bool R);
void pasteSpreadsheet(int row, int col, const std::vector<std::vector<QString>> & cells);
void pasteSpreadsheet(int row, int col, const std::vector<std::vector<QString>> & cells, const QStringList& colNames = {});
int setColumnType(int columnIndex, int columnType);
void copyColumns(int startCol, const std::vector<Json::Value>& copiedColumns);
Json::Value serializedColumn(int col);
Expand Down
9 changes: 5 additions & 4 deletions Desktop/data/undostack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ void RemoveRowsCommand::redo()
_model->removeRows(_start, _count);
}

PasteSpreadsheetCommand::PasteSpreadsheetCommand(QAbstractItemModel *model, int row, int col, const std::vector<std::vector<QString> > &cells)
: UndoModelCommand(model), _row{row}, _col{col}, _newCells{cells}
PasteSpreadsheetCommand::PasteSpreadsheetCommand(QAbstractItemModel *model, int row, int col, const std::vector<std::vector<QString> > &cells, const QStringList& colNames)
: UndoModelCommand(model), _row{row}, _col{col}, _newCells{cells}, _newColNames{colNames}
{
setText(QObject::tr("Paste values at row %1 column '%2'").arg(rowName(_row)).arg(columnName(_col)));
}
Expand All @@ -190,7 +190,7 @@ void PasteSpreadsheetCommand::undo()
DataSetTableModel* dataSetTable = qobject_cast<DataSetTableModel*>(_model);

if (dataSetTable)
dataSetTable->pasteSpreadsheet(_row, _col, _oldCells);
dataSetTable->pasteSpreadsheet(_row, _col, _oldCells, {}, _oldColNames);
}

void PasteSpreadsheetCommand::redo()
Expand All @@ -199,14 +199,15 @@ void PasteSpreadsheetCommand::redo()
for (int c = 0; c < _newCells.size(); c++)
{
_oldCells.push_back(std::vector<QString>());
_oldColNames.push_back(_model->headerData(_col + c, Qt::Horizontal).toString());
for (int r = 0; r < _newCells[c].size(); r++)
_oldCells[c].push_back(_model->data(_model->index(_row + r, _col + c)).toString());
}

DataSetTableModel* dataSetTable = qobject_cast<DataSetTableModel*>(_model);

if (dataSetTable)
dataSetTable->pasteSpreadsheet(_row, _col, _newCells);
dataSetTable->pasteSpreadsheet(_row, _col, _newCells, {}, _newColNames);
}


Expand Down
4 changes: 3 additions & 1 deletion Desktop/data/undostack.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,16 @@ class SetDataCommand : public UndoModelCommand
class PasteSpreadsheetCommand : public UndoModelCommand
{
public:
PasteSpreadsheetCommand(QAbstractItemModel *model, int row, int col, const std::vector<std::vector<QString>>& cells);
PasteSpreadsheetCommand(QAbstractItemModel *model, int row, int col, const std::vector<std::vector<QString>>& cells, const QStringList & colNames);

void undo() override;
void redo() override;

private:
std::vector<std::vector<QString>> _newCells,
_oldCells;
QStringList _newColNames,
_oldColNames;
int _row = -1,
_col = -1;
};
Expand Down
42 changes: 27 additions & 15 deletions Desktop/qquick/datasetview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1150,9 +1150,13 @@ void DataSetView::_copy(QPoint where, bool clear)
previousRow = selectee.row();
}

int rowsSelected = rows.size();
if(rows.size() == 0)
return; //Nothing to copy

_copiedColumns.clear();

int rowsSelected = rows.size();

if(isColumnHeader(where))
{
rows.insert(rows.begin(), tq(std::vector<std::string>(maxCol - minCol + 1, "")));
Expand All @@ -1163,17 +1167,13 @@ void DataSetView::_copy(QPoint where, bool clear)
}
}

if(rows.size() == 0)
return; //Nothing to copy

QStringList all;
for(const auto & row : rows)
all.append(row.join("\t"));
QString copyThis = all.join("\n");

//Log::log() << "copying:\n" << copyThis << "\nThats it" << std::endl;

QGuiApplication::clipboard()->setText(copyThis);
_lastJaspCopyIntoClipboard = copyThis;

if(clear)
{
Expand Down Expand Up @@ -1203,22 +1203,36 @@ void DataSetView::_copy(QPoint where, bool clear)

void DataSetView::paste(QPoint where)
{
if (where.isNull())
where = selectionTopLeft();

QString clipboardStr = QGuiApplication::clipboard()->text();

if (_lastJaspCopyIntoClipboard != clipboardStr)
// The text in the clipboard does not come from a JASP copy. So clear the reference to the copied columns, so that they are not used for the paste action.
_copiedColumns.clear();

if (isColumnHeader(where) && where.x() >= 0 && _copiedColumns.size() > 0)
_model->copyColumns(where.x(), _copiedColumns);
else
{
QPoint topLeft = isCell(where) ? where : selectionTopLeft();
std::vector<std::vector<QString>> newData;
QStringList colNames,
rows = clipboardStr.contains("\r\n") ? clipboardStr.split("\r\n") : clipboardStr.split("\n");

QClipboard * clipboard = QGuiApplication::clipboard();
//Log::log() << "Clipboard: " << clipboard->text(); //We should not log clipboard for privacy/data anonimity reasons
if (rows.isEmpty()) return;

std::vector<std::vector<QString>> newData;
if (isColumnHeader(where))
{
colNames = rows[0].split("\t");
rows.removeFirst();
}

size_t row = 0, col = 0;
for(const QString & rowStr : clipboard->text().split("\n"))
for(const QString & rowStr : rows)
{
col = 0;
if (rowStr.isEmpty()) continue; // Some editors might throw an empty line onto the end of the clipboard. Should at least have one value on the row
if (rowStr.isEmpty() && row == rows.length() - 1) continue; // Some editors might throw an empty line onto the end of the clipboard. Should at least have one value on the row
for(const QString & cellStr : rowStr.split("\t"))
{
if(newData.size() <= col) newData. resize(col+1);
Expand All @@ -1232,9 +1246,7 @@ void DataSetView::paste(QPoint where)
for(auto& column : newData)
column.resize(row); // Make sure that all columns have the same number of rows

Log::log() << "DataSetView about to paste data (" << col << " columns and " << row << " rows) at row: " << topLeft.y() << " and col: " << topLeft.x() << std::endl;

_model->pasteSpreadsheet(topLeft.y(), topLeft.x(), newData);
_model->pasteSpreadsheet(isColumnHeader(where) ? 0 : where.y(), where.x(), newData, colNames);
}
}

Expand Down
3 changes: 2 additions & 1 deletion Desktop/qquick/datasetview.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,8 @@ public slots:
long _selectScrollMs = 0;
QPoint _selectionStart = QPoint(-1, -1),
_selectionEnd = QPoint(-1, -1);
std::vector<Json::Value> _copiedColumns;
std::vector<Json::Value> _copiedColumns;
QString _lastJaspCopyIntoClipboard;


};
Expand Down
Loading