Skip to content

Commit a138235

Browse files
committed
gui: Refresh var list incrementally, receive vars even if type changed since refresh
1 parent 59a2442 commit a138235

File tree

5 files changed

+209
-36
lines changed

5 files changed

+209
-36
lines changed

core/vat.c

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -426,19 +426,63 @@ bool vat_search_next(calc_var_t *var) {
426426
}
427427

428428
bool vat_search_find(const calc_var_t *target, calc_var_t *result) {
429-
/* Ignore linked formula when comparing list names */
430-
bool isList = calc_var_is_list(target);
431429
vat_search_init(result);
432430
while (vat_search_next(result)) {
433-
if (result->type == target->type &&
434-
result->namelen == target->namelen &&
435-
!memcmp(result->name, target->name, target->namelen - isList)) {
431+
if (0 == calc_var_compare_names(target, result)) {
436432
return true;
437433
}
438434
}
439435
return false;
440436
}
441437

438+
int calc_var_compare_names(const calc_var_t *left, const calc_var_t *right) {
439+
bool leftIsAns = !left->named & (left->name[0] == 0x72);
440+
bool rightIsAns = !right->named & (right->name[0] == 0x72);
441+
if (leftIsAns || rightIsAns) {
442+
return leftIsAns - rightIsAns;
443+
}
444+
/* Ignore linked formula when comparing list names */
445+
uint8_t leftNameLen = left->namelen - calc_var_is_list(left);
446+
uint8_t rightNameLen = right->namelen - calc_var_is_list(right);
447+
int cmp = memcmp(left->name, right->name, leftNameLen < rightNameLen ? leftNameLen : rightNameLen);
448+
if (cmp != 0) {
449+
return cmp;
450+
}
451+
cmp = leftNameLen - rightNameLen;
452+
if (cmp != 0) {
453+
return cmp;
454+
}
455+
if (likely(left->type == right->type)) {
456+
return 0;
457+
}
458+
return calc_var_normalized_type(left->type) - calc_var_normalized_type(right->type);
459+
}
460+
461+
calc_var_type_t calc_var_normalized_type(calc_var_type_t type) {
462+
switch (type) {
463+
case CALC_VAR_TYPE_REAL:
464+
case CALC_VAR_TYPE_REAL_FRAC:
465+
case CALC_VAR_TYPE_MIXED_FRAC:
466+
case CALC_VAR_TYPE_REAL_RADICAL:
467+
case CALC_VAR_TYPE_REAL_PI:
468+
case CALC_VAR_TYPE_REAL_PI_FRAC:
469+
case CALC_VAR_TYPE_CPLX:
470+
case CALC_VAR_TYPE_CPLX_FRAC:
471+
case CALC_VAR_TYPE_CPLX_RADICAL:
472+
case CALC_VAR_TYPE_CPLX_PI:
473+
case CALC_VAR_TYPE_CPLX_PI_FRAC:
474+
return CALC_VAR_TYPE_REAL;
475+
case CALC_VAR_TYPE_REAL_LIST:
476+
case CALC_VAR_TYPE_CPLX_LIST:
477+
return CALC_VAR_TYPE_REAL_LIST;
478+
case CALC_VAR_TYPE_PROG:
479+
case CALC_VAR_TYPE_PROT_PROG:
480+
return CALC_VAR_TYPE_PROG;
481+
default:
482+
return type;
483+
}
484+
}
485+
442486
bool calc_var_is_list(const calc_var_t *var) {
443487
return var && (var->type == CALC_VAR_TYPE_REAL_LIST || var->type == CALC_VAR_TYPE_CPLX_LIST);
444488
}

core/vat.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ void vat_search_init(calc_var_t *);
6868
bool vat_search_next(calc_var_t *);
6969
bool vat_search_find(const calc_var_t *, calc_var_t *);
7070

71+
int calc_var_compare_names(const calc_var_t *, const calc_var_t *);
72+
calc_var_type_t calc_var_normalized_type(calc_var_type_t);
7173
bool calc_var_is_list(const calc_var_t *);
7274
bool calc_var_is_prog(const calc_var_t *);
7375
bool calc_var_is_asmprog(const calc_var_t *);

gui/qt/mainwindow.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U
141141
m_varTableModel = new VarTableModel(ui->emuVarView);
142142
QSortFilterProxyModel *varTableSortModel = new QSortFilterProxyModel(m_varTableModel);
143143
varTableSortModel->setSourceModel(m_varTableModel);
144+
varTableSortModel->setSortCaseSensitivity(Qt::CaseInsensitive);
145+
varTableSortModel->setSortLocaleAware(true);
144146
ui->emuVarView->setModel(varTableSortModel);
145147
ui->emuVarView->sortByColumn(VarTableModel::VAR_NAME_COL, Qt::AscendingOrder);
146148

@@ -2063,22 +2065,32 @@ void MainWindow::varSaveSelectedFiles() {
20632065
varReceive([this, dir = dialog.directory().absolutePath()](bool isBlocked) {
20642066
QString name;
20652067
QString filename;
2066-
bool good = true;
2068+
QString errMsg;
20672069

20682070
for (int currRow = 0; currRow < ui->emuVarView->model()->rowCount(); currRow++) {
20692071
QModelIndex nameIndex = ui->emuVarView->model()->index(currRow, VarTableModel::VAR_NAME_COL);
20702072
if (nameIndex.data(Qt::CheckStateRole) == Qt::Checked) {
2071-
calc_var_t var = nameIndex.data(Qt::UserRole).value<calc_var_t>();
2073+
calc_var_t var, varName = nameIndex.data(Qt::UserRole).value<calc_var_t>();
2074+
// Find the variable first to ensure the current type is used for the file extension
2075+
if (!vat_search_find(&varName, &var)) {
2076+
if (calc_var_is_list(&varName)) {
2077+
// Remove any linked formula before generating filename
2078+
varName.name[varName.namelen - 1] = 0;
2079+
}
2080+
name = QString(calc_var_name_to_utf8(varName.name, varName.namelen, varName.named));
2081+
errMsg = tr("Transfer error, variable no longer exists: ") + name;
2082+
break;
2083+
}
20722084
if (calc_var_is_list(&var)) {
20732085
// Remove any linked formula before generating filename
20742086
var.name[var.namelen - 1] = 0;
20752087
}
20762088

20772089
name = QString(calc_var_name_to_utf8(var.name, var.namelen, var.named));
2078-
filename = dir + "/" + name + "." + m_varExtensions[var.type1];
2090+
filename = dir + "/" + name + "." + m_varExtensions[var.type];
20792091

20802092
if (emu_receive_variable(filename.toStdString().c_str(), &var, 1) != LINK_GOOD) {
2081-
good = false;
2093+
errMsg = tr("Transfer error, see console for information:\nFile: ") + filename;
20822094
break;
20832095
}
20842096
}
@@ -2087,10 +2099,10 @@ void MainWindow::varSaveSelectedFiles() {
20872099
emu.unblock();
20882100
}
20892101

2090-
if (good) {
2102+
if (errMsg.isEmpty()) {
20912103
QMessageBox::information(this, MSG_INFORMATION, tr("Transfer completed successfully."));
20922104
} else {
2093-
QMessageBox::critical(this, MSG_ERROR, tr("Transfer error, see console for information:\nFile: ") + filename);
2105+
QMessageBox::critical(this, MSG_ERROR, errMsg);
20942106
}
20952107
});
20962108
}

gui/qt/vartablemodel.cpp

Lines changed: 124 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,75 @@
44
#include "utils.h"
55

66
#include <QtGui/QBrush>
7+
#include <algorithm>
8+
#include <cassert>
79

810
Q_DECLARE_METATYPE(calc_var_t)
911

10-
VarTableModel::VarData::VarData(const calc_var_t &var) : info(var), data(reinterpret_cast<char*>(var.data), var.size), checked(Qt::Unchecked) {
11-
info.data = reinterpret_cast<uint8_t*>(data.data());
12-
updatePreview();
12+
VarTableModel::VarData::VarData(const calc_var_t &var) : info(var), previewState(PreviewState::Outdated), checked(false) {
13+
info.data = new uint8_t[var.size];
14+
memcpy(info.data, var.data, var.size);
15+
}
16+
17+
VarTableModel::VarData::~VarData() {
18+
delete[] info.data;
19+
}
20+
21+
VarTableModel::VarData::VarData(VarData &&other) noexcept {
22+
info.data = nullptr;
23+
*this = std::move(other);
24+
}
25+
26+
VarTableModel::VarData &VarTableModel::VarData::operator=(VarData &&other) noexcept {
27+
uint8_t *data = info.data;
28+
info = other.info;
29+
other.info.data = data;
30+
preview = std::move(other.preview);
31+
previewState = other.previewState;
32+
checked = other.checked;
33+
return *this;
34+
}
35+
36+
uint8_t VarTableModel::VarData::updateInfo(const calc_var_t &var) {
37+
uint8_t changed = 0;
38+
if (var.namelen != info.namelen ||
39+
0 != memcmp(var.name, info.name, var.namelen)) {
40+
changed |= (1 << VAR_NAME_COL);
41+
}
42+
if (var.archived != info.archived) {
43+
changed |= (1 << VAR_LOCATION_COL);
44+
}
45+
if (var.type != info.type) {
46+
changed |= (1 << VAR_TYPE_COL);
47+
}
48+
uint8_t *data = info.data;
49+
if (var.size != info.size) {
50+
changed |= (1 << VAR_SIZE_COL) | (1 << VAR_PREVIEW_COL);
51+
data = new uint8_t[var.size];
52+
delete[] info.data;
53+
} else if (0 != memcmp(var.data, data, var.size)) {
54+
changed |= (1 << VAR_PREVIEW_COL);
55+
}
56+
info = var;
57+
info.data = data;
58+
if (changed & (1 << VAR_PREVIEW_COL)) {
59+
memcpy(info.data, var.data, var.size);
60+
previewState = PreviewState::Outdated;
61+
}
62+
return changed;
1363
}
1464

1565
void VarTableModel::VarData::updatePreview() {
16-
previewValid = true;
66+
previewState = PreviewState::Valid;
1767
if (info.size <= 2) {
1868
preview = tr("Empty");
19-
previewValid = false;
69+
previewState = PreviewState::Invalid;
2070
} else if (calc_var_is_asmprog(&info)) {
2171
preview = tr("Can't preview this");
22-
previewValid = false;
72+
previewState = PreviewState::Invalid;
2373
} else if (calc_var_is_internal(&info) && info.name[0] != '#') { // # is previewable
2474
preview = tr("Can't preview this OS variable");
25-
previewValid = false;
75+
previewState = PreviewState::Invalid;
2676
} else {
2777
try {
2878
preview = QString::fromStdString(calc_var_content_string(info)).trimmed().replace("\n", " \\ ");
@@ -32,7 +82,7 @@ void VarTableModel::VarData::updatePreview() {
3282
}
3383
} catch (...) {
3484
preview = tr("Can't preview this");
35-
previewValid = false;
85+
previewState = PreviewState::Invalid;
3686
}
3787
}
3888
}
@@ -48,37 +98,91 @@ void VarTableModel::clear() {
4898
endResetModel();
4999
}
50100

101+
static bool varLess(const calc_var_t &var, const calc_var_t &other) {
102+
return calc_var_compare_names(&var, &other) < 0;
103+
}
104+
51105
void VarTableModel::refresh() {
52106
calc_var_t var;
53107

54-
clear();
108+
std::vector<calc_var_t> newVars;
55109

56110
vat_search_init(&var);
57111
while (vat_search_next(&var)) {
58112
if (var.named || var.size > 2) {
59-
int row = vars.size();
60-
beginInsertRows(QModelIndex(), row, row);
61-
vars.push_back(VarData(var));
113+
newVars.push_back(var);
114+
}
115+
}
116+
117+
std::sort(newVars.begin(), newVars.end(), varLess);
118+
119+
auto oldIter = vars.begin();
120+
auto newIter = newVars.begin();
121+
while (newIter != newVars.end()) {
122+
size_t row = oldIter - vars.begin();
123+
// Remove old vars smaller than the next new var
124+
auto oldRemoveEnd = std::find_if_not(oldIter, vars.end(), [=](const VarData &var) { return varLess(var.info, *newIter); });
125+
if (oldIter != oldRemoveEnd) {
126+
beginRemoveRows(QModelIndex(), row, row + (oldRemoveEnd - oldIter - 1));
127+
oldIter = vars.erase(oldIter, oldRemoveEnd);
128+
endRemoveRows();
129+
}
130+
// Insert new vars smaller than the next old var, or insert all if no more old vars
131+
auto newInsertEnd = newVars.end();
132+
if (oldIter != vars.end()) {
133+
newInsertEnd = std::find_if_not(newIter, newVars.end(), [=](const calc_var_t &var) { return varLess(var, oldIter->info); });
134+
}
135+
if (newIter != newInsertEnd) {
136+
beginInsertRows(QModelIndex(), row, row + (newInsertEnd - newIter - 1));
137+
oldIter = vars.insert(oldIter, newIter, newInsertEnd);
62138
endInsertRows();
139+
oldIter += (newInsertEnd - newIter);
140+
newIter = newInsertEnd;
141+
} else {
142+
// No new vars were smaller, and the old var cannot be smaller because those were already removed, so they're equal
143+
assert(oldIter != vars.end());
144+
// Update the old variable with the new variable data
145+
uint8_t changed = oldIter->updateInfo(*newIter);
146+
// Inform the view of changed columns
147+
for (uint8_t col = 0; col < VAR_NUM_COLS; col++) {
148+
if (changed & (1 << col)) {
149+
QModelIndex cell = index(row, col);
150+
emit dataChanged(cell, cell);
151+
}
152+
}
153+
oldIter++;
154+
newIter++;
63155
}
64156
}
157+
// Remove any remaining old vars
158+
if (oldIter != vars.end()) {
159+
beginRemoveRows(QModelIndex(), oldIter - vars.begin(), vars.size() - 1);
160+
vars.erase(oldIter, vars.end());
161+
endRemoveRows();
162+
}
65163
}
66164

67165
void VarTableModel::retranslate() {
68166
if (!vars.empty()) {
69167
for (VarData &var : vars) {
70-
var.updatePreview();
168+
// Only invalid previews are translated
169+
if (var.previewState == PreviewState::Invalid) {
170+
var.previewState = PreviewState::Outdated;
171+
}
71172
}
72-
emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
173+
emit dataChanged(index(0, VAR_LOCATION_COL), index(vars.size() - 1, VAR_NUM_COLS - 1));
73174
}
74-
emit headerDataChanged(Qt::Horizontal, 0, columnCount() - 1);
175+
emit headerDataChanged(Qt::Horizontal, 0, VAR_NUM_COLS - 1);
75176
}
76177

77178
QVariant VarTableModel::data(const QModelIndex &index, int role) const {
78179
if (!index.isValid()) {
79180
return QVariant();
80181
}
81-
const VarData &var = vars[index.row()];
182+
VarData &var = vars[index.row()];
183+
if (index.column() == VAR_PREVIEW_COL && var.previewState == PreviewState::Outdated) {
184+
var.updatePreview();
185+
}
82186
switch (role) {
83187
case Qt::DisplayRole:
84188
switch (index.column()) {
@@ -104,17 +208,17 @@ QVariant VarTableModel::data(const QModelIndex &index, int role) const {
104208
};
105209
case Qt::FontRole:
106210
if (index.column() == VAR_PREVIEW_COL) {
107-
return var.previewValid ? varPreviewCEFont : varPreviewItalicFont;
211+
return var.previewState == PreviewState::Invalid ? varPreviewItalicFont : varPreviewCEFont;
108212
}
109213
return QVariant();
110214
case Qt::ForegroundRole:
111-
if (index.column() == VAR_PREVIEW_COL && !var.previewValid) {
215+
if (index.column() == VAR_PREVIEW_COL && var.previewState == PreviewState::Invalid) {
112216
return QBrush(Qt::gray);
113217
}
114218
return QVariant();
115219
case Qt::CheckStateRole:
116220
if (index.column() == VAR_NAME_COL) {
117-
return var.checked;
221+
return var.checked ? Qt::Checked : Qt::Unchecked;
118222
}
119223
return QVariant();
120224
case Qt::UserRole:
@@ -126,7 +230,7 @@ QVariant VarTableModel::data(const QModelIndex &index, int role) const {
126230

127231
bool VarTableModel::setData(const QModelIndex &index, const QVariant &value, int role) {
128232
if (index.isValid() && index.column() == VAR_NAME_COL && role == Qt::CheckStateRole) {
129-
vars[index.row()].checked = qvariant_cast<Qt::CheckState>(value);
233+
vars[index.row()].checked = qvariant_cast<Qt::CheckState>(value) == Qt::Checked;
130234
emit dataChanged(index, index, { Qt::CheckStateRole });
131235
return true;
132236
}

0 commit comments

Comments
 (0)