#include "marshal.h" #include #include #include #include #include #include static void warning_msg(const char* message, cl_object l_args) { STATIC_SYMBOL (s_error_output, "*ERROR-OUTPUT*") cl_format(4, cl_symbol_value(s_error_output), STRING("~%[LQML:warning] ~A ~S~%"), STRING(message), l_args); } // *** Lisp to Qt *** template T toInt(cl_object l_num) { T i = 0; if (cl_integerp(l_num) == ECL_T) { i = fixint(l_num); } return i; } int toInt(cl_object l_num) { return toInt(l_num); } template T toUInt(cl_object l_num) { T i = 0; if (cl_integerp(l_num) == ECL_T) { i = fixnnint(l_num); } return i; } uint toUInt(cl_object l_num) { return toUInt(l_num); } template T toFloat(cl_object l_num) { T f = 0; if (ECL_SINGLE_FLOAT_P(l_num)) { f = sf(l_num); } else if (ECL_DOUBLE_FLOAT_P(l_num)) { f = df(l_num); } #ifdef ECL_LONG_FLOAT else if (ECL_LONG_FLOAT_P(l_num)) { f = ecl_long_float(l_num); } #endif else if (cl_integerp(l_num) == ECL_T) { f = fixint(l_num); } else { cl_object l_f = cl_float(1, l_num); if (ECL_DOUBLE_FLOAT_P(l_f)) { f = df(l_f); } else if (ECL_SINGLE_FLOAT_P(l_f)) { f = sf(l_f); } #ifdef ECL_LONG_FLOAT else if (ECL_LONG_FLOAT_P(l_f)) { f = ecl_long_float(l_f); } #endif } return f; } float toFloat(cl_object l_num) { return toFloat(l_num); } qreal toReal(cl_object l_num) { return toFloat(l_num); } QByteArray toCString(cl_object l_str) { QByteArray ba; if (ECL_STRINGP(l_str)) { if (ECL_BASE_STRING_P(l_str)) { ba = QByteArray(reinterpret_cast(l_str->base_string.self), l_str->base_string.fillp); } else { uint l = l_str->string.fillp; ba.resize(l); ecl_character* l_s = l_str->string.self; for (uint i = 0; i < l; i++) { ba[i] = l_s[i]; } } } return ba; } QByteArray toQByteArray(cl_object l_vec) { QByteArray ba; if (ECL_VECTORP(l_vec)) { int len = LEN(l_vec); ba.resize(len); for (int i = 0; i < len; i++) { ba[i] = toInt(ecl_aref(l_vec, i)); } } return ba; } QString toQString(cl_object l_str) { QString s; if (ECL_STRINGP(l_str)) { if (ECL_BASE_STRING_P(l_str)) { s = QString::fromLatin1(reinterpret_cast(l_str->base_string.self), l_str->base_string.fillp); } else { uint l = l_str->string.fillp; s.resize(l); ecl_character* l_s = l_str->string.self; for (uint i = 0; i < l; i++) { s[i] = QChar(l_s[i]); } } } return s; } TO_QT_2 (QPoint, toInt) TO_QT_2 (QSize, toInt) TO_QT_4 (QRect, toInt) TO_QT_2 (QPointF, toFloat) TO_QT_2 (QSizeF, toFloat) TO_QT_4 (QRectF, toFloat) QVariant toQVariant(cl_object l_arg, int type) { QVariant var; switch (type) { case QMetaType::QByteArray: var = toQByteArray(l_arg); break; case QMetaType::QColor: var = QColor(toQString(l_arg)); break; case QMetaType::QPoint: var = toQPoint(l_arg); break; case QMetaType::QPointF: var = toQPointF(l_arg); break; case QMetaType::QRect: var = toQRect(l_arg); break; case QMetaType::QRectF: var = toQRectF(l_arg); break; case QMetaType::QSize: var = toQSize(l_arg); break; case QMetaType::QSizeF: var = toQSizeF(l_arg); break; case QMetaType::QUrl: var = QUrl(toQString(l_arg)); break; case QMetaType::QObjectStar: { QObject* o = toQObjectPointer(l_arg); var = QVariant::fromValue(o); // QObject* } break; default: if (cl_integerp(l_arg) == ECL_T) { // int var = QVariant(toInt(l_arg)); } else if (cl_floatp(l_arg) == ECL_T) { // double var = QVariant(toFloat(l_arg)); } else if (cl_stringp(l_arg) == ECL_T) { // string var = QVariant(toQString(l_arg)); } else if (cl_characterp(l_arg) == ECL_T) { // char var = QChar(toInt(cl_char_code(l_arg))); } else if (l_arg == ECL_T) { // true var = QVariant(true); } else if (l_arg == ECL_NIL) { // false var = QVariant(false); } else if (cl_listp(l_arg) == ECL_T) { // list var = (cl_keywordp(cl_first(l_arg)) == ECL_T) ? toQVariantMap(l_arg) : toQVariantList(l_arg); } else if (cl_vectorp(l_arg) == ECL_T) { // vector (of octets) var = QVariant(toQByteArray(l_arg)); } else if (cl_symbolp(l_arg) == ECL_T) { // symbol var = QVariant(toQString(cl_string_downcase(1, cl_symbol_name(l_arg)))); } else { QObject* o = toQObjectPointer(l_arg); if (o != nullptr) { var = QVariant::fromValue(o); // e.g. QQuickItem* } else { var = QVariant(); // default: undefined } } } return var; } QVariantList toQVariantList(cl_object l_list) { QVariantList l; if (ECL_LISTP(l_list)) { for (cl_object l_do_list = l_list; l_do_list != ECL_NIL; l_do_list = cl_cdr(l_do_list)) { cl_object l_el = cl_car(l_do_list); l << toQVariant(l_el); } } return l; } QString toCamelCase(const QString& name) { // convert Lisp name to Qt name QString qtName(name); int j = 0; for (int i = 0; i < name.length(); i++, j++) { qtName[j] = (name.at(i) == QChar('-')) ? name.at(++i).toUpper() : name.at(i); } qtName.truncate(j); return qtName; } QVariant toQVariantMap(cl_object l_list) { // special case for JS dictionary, for e.g. populating a Model in QML STATIC_SYMBOL_PKG (s_hex, "HEX", "QML") QVariantMap map; cl_object l_do_args = l_list; while (l_do_args != ECL_NIL) { cl_object l_keyword = cl_first(l_do_args); if (cl_keywordp(l_keyword) == ECL_T) { cl_object l_value = cl_second(l_do_args); // convert INTEGER to hex string, since we only have floats in JS; // will be converted back automatically if passed with 'Lisp.call()' if (cl_integerp(l_value) == ECL_T) { l_value = cl_funcall(2, s_hex, l_value); } map.insert(toCamelCase(toQString(cl_symbol_name(l_keyword)).toLower()), toQVariant(l_value)); l_do_args = cl_cddr(l_do_args); } else { // might not be an error, e.g. unintended return value of Lisp.call() warning_msg("conversion to QVariantMap failed", l_list); break; } } return map; } QObject* toQObjectPointer(cl_object l_obj) { STATIC_SYMBOL_PKG (s_qt_object_p, "QT-OBJECT-P", "QML") // see 'ini.lisp' STATIC_SYMBOL_PKG (s_qt_object_address, "QT-OBJECT-ADDRESS", "QML") if (cl_funcall(2, s_qt_object_p, l_obj) != ECL_NIL) { return reinterpret_cast(toUInt(cl_funcall(2, s_qt_object_address, l_obj))); } return nullptr; } // *** Qt to Lisp *** cl_object from_cstring(const QByteArray& s) { cl_object l_s = ecl_alloc_simple_base_string(s.length()); memcpy(l_s->base_string.self, s.constData(), s.length()); return l_s; } cl_object from_qbytearray(const QByteArray& ba) { STATIC_SYMBOL_PKG (s_make_byte_vector, "%MAKE-BYTE-VECTOR", "QML") // see 'ini.lisp' cl_object l_list = ECL_NIL; for (int i = 0; i < ba.size(); i++) { l_list = CONS(ecl_make_fixnum(static_cast(ba.at(i))), l_list); } cl_object l_vec = cl_funcall(2, s_make_byte_vector, cl_nreverse(l_list)); return l_vec; } cl_object from_qchar(const QChar& ch) { cl_object l_char = cl_code_char(ecl_make_fixnum(ch.unicode())); return l_char; } cl_object from_qstring(const QString& s) { cl_object l_s = ecl_alloc_simple_extended_string(s.length()); ecl_character* l_p = l_s->string.self; for (int i = 0; i < s.length(); i++) { l_p[i] = s.at(i).unicode(); } return l_s; } TO_CL_2 (QPoint, qpoint, ecl_make_fixnum, x, y) TO_CL_2 (QSize, qsize, ecl_make_fixnum, width, height) TO_CL_4 (QRect, qrect, ecl_make_fixnum, x, y, width, height) TO_CL_2 (QPointF, qpointf, ecl_make_doublefloat, x, y) TO_CL_2 (QSizeF, qsizef, ecl_make_doublefloat, width, height) TO_CL_4 (QRectF, qrectf, ecl_make_doublefloat, x, y, width, height) cl_object from_qvariant(const QVariant& var) { cl_object l_obj = ECL_NIL; #if QT_VERSION < 0x060000 const int type = var.type(); #else const int type = var.typeId(); #endif if ((type == QMetaType::QObjectStar) || // QObject* (type >= QMetaType::User)) { // e.g. QQuickItem* QObject* o = var.value(); if (o == nullptr) { // must be a QJSValue (e.g. return value from 'qjs') l_obj = from_qvariant(var.value().toVariant()); } else { l_obj = from_qobject_pointer(o); } } else { switch (type) { case QMetaType::Bool: l_obj = var.toBool() ? ECL_T : ECL_NIL; break; case QMetaType::Double: l_obj = ecl_make_doublefloat(var.toDouble()); break; case QMetaType::Int: l_obj = ecl_make_integer(var.toInt()); break; case QMetaType::UInt: l_obj = ecl_make_unsigned_integer(var.toUInt()); break; case QMetaType::LongLong: l_obj = ecl_make_integer(var.toLongLong()); break; case QMetaType::ULongLong: l_obj = ecl_make_unsigned_integer(var.toULongLong()); break; case QMetaType::QByteArray: l_obj = from_qbytearray(var.toByteArray()); break; case QMetaType::QChar: l_obj = from_qchar(var.toChar()); break; case QMetaType::QColor: l_obj = from_qstring(var.value().name()); break; case QMetaType::QPoint: l_obj = from_qpoint(var.toPoint()); break; case QMetaType::QPointF: l_obj = from_qpointf(var.toPointF()); break; case QMetaType::QRect: l_obj = from_qrect(var.toRect()); break; case QMetaType::QRectF: l_obj = from_qrectf(var.toRectF()); break; case QMetaType::QSize: l_obj = from_qsize(var.toSize()); break; case QMetaType::QSizeF: l_obj = from_qsizef(var.toSizeF()); break; case QMetaType::QString: case QMetaType::QUrl: l_obj = from_qstring(var.toString()); break; // special case (can be nested) case QMetaType::QVariantList: { const QVariantList list(var.value()); for (QVariant v : list) { l_obj = CONS(from_qvariant(v), l_obj); } l_obj = cl_nreverse(l_obj); } break; } } return l_obj; } cl_object from_qobject_pointer(QObject* qobject) { STATIC_SYMBOL_PKG (s_qt_object, "QT-OBJECT", "QML") // see 'ini.lisp' return cl_funcall(2, s_qt_object, ecl_make_unsigned_integer(reinterpret_cast(qobject))); }