diff --git a/CommonData/column.cpp b/CommonData/column.cpp index b12f9afaf3..a62f22e1bb 100644 --- a/CommonData/column.cpp +++ b/CommonData/column.cpp @@ -59,7 +59,7 @@ void Column::dbLoad(int id, bool getValues) Json::Value emptyVals; db().columnGetBasicInfo( _id, _name, _title, _description, _type, _revision, emptyVals, _autoSortByValue); - db().columnGetComputedInfo( _id, _analysisId, _invalidated, _codeType, _rCode, _error, _constructorJson); + db().columnGetComputedInfo( _id, _analysisId, _invalidated, _codeType, _rCode, _error, _constructorJson, _computeFilter); _emptyValues->fromJson(emptyVals); @@ -159,6 +159,18 @@ void Column::setDescription(const std::string &description) incRevision(); } +void Column::setComputeFilter(const std::string &filter) +{ + JASPTIMER_SCOPE(Column::setComputeFilter); + + if(_computeFilter == filter) + return; + + _computeFilter = filter; + db().columnSetComputeFilter(_id, _computeFilter); + incRevision(); +} + void Column::setType(columnType colType) { JASPTIMER_SCOPE(Column::setType); @@ -206,7 +218,7 @@ bool Column::setCustomEmptyValues(const stringset& customEmptyValues) void Column::dbUpdateComputedColumnStuff() { - db().columnSetComputedInfo(_id, _analysisId, _invalidated, _codeType, _rCode, _error, constructorJsonStr()); + db().columnSetComputedInfo(_id, _analysisId, _invalidated, _codeType, _rCode, _error, constructorJsonStr(), _computeFilter); incRevision(); } @@ -2307,7 +2319,7 @@ void Column::deserialize(const Json::Value &json) _constructorJson = json["constructorJson"]; _autoSortByValue = json["autoSortByValue"].asBool(); - db().columnSetComputedInfo(_id, _analysisId, _invalidated, _codeType, _rCode, _error, constructorJsonStr()); + db().columnSetComputedInfo(_id, _analysisId, _invalidated, _codeType, _rCode, _error, constructorJsonStr(), _computeFilter); deserializeLabelsForCopy(json["labels"]); diff --git a/CommonData/column.h b/CommonData/column.h index 42dc16acb2..c4b60ebf32 100644 --- a/CommonData/column.h +++ b/CommonData/column.h @@ -65,6 +65,7 @@ class Column : public DataSetBaseNode columnTypeChangeResult changeType( columnType colType ); void setCodeType( computedColumnType codeType ); void setDescription( const std::string & description ); + void setComputeFilter( const std::string & filter = "" ); bool setConstructorJson( const Json::Value & constructorJson ); bool setConstructorJson( const std::string & constructorJson ); void setAutoSortByValue( bool sort ); @@ -99,6 +100,7 @@ class Column : public DataSetBaseNode const std::string & error() const { return _error; } const std::string & rCode() const { return _rCode; } const std::string & description() const { return _description; } + const std::string & computeFilter() const { return _computeFilter; } std::string rCodeStripped() const { return stringUtils::stripRComments(_rCode); } std::string constructorJsonStr() const { return _constructorJson.toStyledString(); } const Json::Value & constructorJson() const { return _constructorJson; } @@ -228,8 +230,8 @@ class Column : public DataSetBaseNode bool isEmptyValue( const std::string & val) const; bool isEmptyValue( const double val) const; - size_t getMaximumWidthInCharactersIncludingShadow(); - size_t getMaximumWidthInCharacters(bool shortenAndFancyEmptyValue, bool valuesPlease, size_t extraPad = 4); ///< Tries to take into consideration that utf-8 can have more characters than codepoints and compensates for it + size_t getMaximumWidthInCharactersIncludingShadow(); + size_t getMaximumWidthInCharacters(bool shortenAndFancyEmptyValue, bool valuesPlease, size_t extraPad = 4); ///< Tries to take into consideration that utf-8 can have more characters than codepoints and compensates for it columnType resetValues(int thresholdScale); ///< "Reimport" the values it already has with a possibly different threshold of values stringset mergeOldMissingDataMap(const Json::Value & missingData); ///< <0.19 JASP collected the removed empty values values in a map in a json object... We need to be able to read at least 0.18.3 so here this function that absorbs such a map and adds any required labels. It does not add the empty values itself though! @@ -273,7 +275,8 @@ class Column : public DataSetBaseNode _title, _description, _error, - _rCode; + _rCode, + _computeFilter; Json::Value _constructorJson = Json::objectValue; doublevec _dbls; intvec _ints; diff --git a/CommonData/databaseinterface.cpp b/CommonData/databaseinterface.cpp index a953140bc5..2f578689e4 100644 --- a/CommonData/databaseinterface.cpp +++ b/CommonData/databaseinterface.cpp @@ -1063,11 +1063,21 @@ void DatabaseInterface::columnSetDescription(int columnId, const std::string & d }); } -void DatabaseInterface::columnSetComputedInfo(int columnId, int analysisId, bool invalidated, computedColumnType codeType, const std::string & rCode, const std::string & error, const std::string & constructorJsonStr) +void DatabaseInterface::columnSetComputeFilter(int columnId, const std::string &filter) +{ + JASPTIMER_SCOPE(DatabaseInterface::columnSetComputeFilter); + runStatements("UPDATE Columns SET computeFilter=? WHERE id=?;", [&](sqlite3_stmt * stmt) + { + sqlite3_bind_text(stmt, 1, filter.c_str(), filter.length(), SQLITE_TRANSIENT); + sqlite3_bind_int(stmt, 2, columnId); + }); +} + +void DatabaseInterface::columnSetComputedInfo(int columnId, int analysisId, bool invalidated, computedColumnType codeType, const std::string & rCode, const std::string & error, const std::string & constructorJsonStr, const std::string & computeFilter) { JASPTIMER_SCOPE(DatabaseInterface::columnSetComputedInfo); - runStatements("UPDATE Columns SET invalidated=?, codeType=?, rCode=?, error=?, constructorJson=?, analysisId=? WHERE id=?;", [&](sqlite3_stmt * stmt) + runStatements("UPDATE Columns SET invalidated=?, codeType=?, rCode=?, error=?, constructorJson=?, analysisId=?, computeFilter=? WHERE id=?;", [&](sqlite3_stmt * stmt) { std::string codeT = computedColumnTypeToString(codeType); @@ -1077,7 +1087,8 @@ void DatabaseInterface::columnSetComputedInfo(int columnId, int analysisId, bool sqlite3_bind_text(stmt, 4, error.c_str(), error.length(), SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 5, constructorJsonStr.c_str(), constructorJsonStr.length(), SQLITE_TRANSIENT); sqlite3_bind_int(stmt, 6, analysisId); - sqlite3_bind_int(stmt, 7, columnId); + sqlite3_bind_text(stmt, 7, computeFilter.c_str(), computeFilter.length(), SQLITE_TRANSIENT); + sqlite3_bind_int(stmt, 8, columnId); }); } @@ -1122,7 +1133,7 @@ std::string DatabaseInterface::_wrap_sqlite3_column_text(sqlite3_stmt * stmt, in return !col ? "" : std::string(reinterpret_cast(col)); } -void DatabaseInterface::columnGetComputedInfo(int columnId, int &analysisId, bool &invalidated, computedColumnType &codeType, std::string &rCode, std::string &error, Json::Value &constructorJson) +void DatabaseInterface::columnGetComputedInfo(int columnId, int &analysisId, bool &invalidated, computedColumnType &codeType, std::string &rCode, std::string &error, Json::Value &constructorJson, std::string & computeFilter) { JASPTIMER_SCOPE(DatabaseInterface::columnGetComputedInfo); @@ -1135,7 +1146,7 @@ void DatabaseInterface::columnGetComputedInfo(int columnId, int &analysisId, boo { int colCount = sqlite3_column_count(stmt); - assert(colCount == 6); + assert(colCount == 7); invalidated = sqlite3_column_int( stmt, 0); std::string codeTypeStr = _wrap_sqlite3_column_text(stmt, 1); @@ -1143,6 +1154,7 @@ void DatabaseInterface::columnGetComputedInfo(int columnId, int &analysisId, boo error = _wrap_sqlite3_column_text(stmt, 3); std::string constructorJsonStr = _wrap_sqlite3_column_text(stmt, 4); analysisId = sqlite3_column_int( stmt, 5); + computeFilter = _wrap_sqlite3_column_text(stmt, 6); codeType = computedColumnType::notComputed; if (!codeTypeStr.empty()) @@ -1155,7 +1167,7 @@ void DatabaseInterface::columnGetComputedInfo(int columnId, int &analysisId, boo Json::Reader().parse(constructorJsonStr, constructorJson); }; - runStatements("SELECT invalidated, codeType, rCode, error, constructorJson, analysisId FROM Columns WHERE id = ?;", prepare, processRow); + runStatements("SELECT invalidated, codeType, rCode, error, constructorJson, analysisId, computeFilter FROM Columns WHERE id = ?;", prepare, processRow); } void DatabaseInterface::labelsClear(int columnId) diff --git a/CommonData/databaseinterface.h b/CommonData/databaseinterface.h index b8e5f28185..86de529309 100644 --- a/CommonData/databaseinterface.h +++ b/CommonData/databaseinterface.h @@ -133,9 +133,10 @@ class DatabaseInterface void columnSetTitle( int columnId, const std::string & title); void columnSetEmptyVals( int columnId, const std::string & emptyValsJson); void columnSetDescription( int columnId, const std::string & description); + void columnSetComputeFilter( int columnId, const std::string & description); void columnGetBasicInfo( int columnId, std::string & name, std::string & title, std::string & description, columnType & colType, int & revision, Json::Value & emptyValuesJson, bool & autoSort); - void columnSetComputedInfo( int columnId, int analysisId, bool invalidated, computedColumnType codeType, const std::string & rCode, const std::string & error, const std::string & constructorJson); - void columnGetComputedInfo( int columnId, int &analysisId, bool & invalidated, computedColumnType & codeType, std::string & rCode, std::string & error, Json::Value & constructorJson); + void columnSetComputedInfo( int columnId, int analysisId, bool invalidated, computedColumnType codeType, const std::string & rCode, const std::string & error, const std::string & constructorJson, const std::string & computeFilter); + void columnGetComputedInfo( int columnId, int &analysisId, bool & invalidated, computedColumnType & codeType, std::string & rCode, std::string & error, Json::Value & constructorJson, std::string & computeFilter); void columnSetValues( int columnId, const intvec & ints, const doublevec & dbls); void columnSetValue( int columnId, size_t row, int valueInt, double valueDbl); intvec columnGetLabelIds( int columnId); diff --git a/Desktop/components/JASP/Widgets/LabelEditorWindow.qml b/Desktop/components/JASP/Widgets/LabelEditorWindow.qml index e5d2ea67c5..e27512381a 100644 --- a/Desktop/components/JASP/Widgets/LabelEditorWindow.qml +++ b/Desktop/components/JASP/Widgets/LabelEditorWindow.qml @@ -490,6 +490,8 @@ FocusScope RowLayout { id: newLabelContainer + spacing: 0 + anchors { left: tableBackground.left @@ -505,17 +507,18 @@ FocusScope QTC.TextField { - id: newLevelValueInput + id: newLevelValueInput - font: jaspTheme.font - color: jaspTheme.textEnabled - selectedTextColor: jaspTheme.white - selectionColor: jaspTheme.itemSelectedColor - selectByMouse: true + font: jaspTheme.font + color: jaspTheme.textEnabled + selectedTextColor: jaspTheme.white + selectionColor: jaspTheme.itemSelectedColor + selectByMouse: true + Layout.minimumWidth: levelsTableView.valueColWidth - text: "" - placeholderText: qsTr("Value") - height: buttonColumnVariablesWindow.buttonHeight + text: "" + placeholderText: qsTr("Value") + height: buttonColumnVariablesWindow.buttonHeight background: Rectangle { @@ -586,7 +589,7 @@ FocusScope toolTip: qsTr("Add a level that's missing from the data") height: implicitHeight - implicitHeight: buttonColumnVariablesWindow.buttonHeight + implicitHeight: buttonColumnVariablesWindow.buttonHeight - newLevelValueInput.padding width: height } }