1 #ifndef QT_HELPERS_HPP_
2 #define QT_HELPERS_HPP_
3
4 #include <stdexcept>
5 #include <ostream>
6
7 #include <QString>
8 #include <QChar>
9 #include <QMetaObject>
10 #include <QHostAddress>
11 #include <QDataStream>
12 #include <QMetaType>
13 #include <QMetaEnum>
14
15 #define ENUM_QDATASTREAM_OPS_DECL(CLASS, ENUM) \
16 QDataStream& operator << (QDataStream&, CLASS::ENUM const&); \
17 QDataStream& operator >> (QDataStream&, CLASS::ENUM&);
18
19 #define ENUM_QDATASTREAM_OPS_IMPL(CLASS, ENUM) \
20 QDataStream& operator << (QDataStream& os, CLASS::ENUM const& v) \
21 { \
22 auto const& mo = CLASS::staticMetaObject; \
23 return os << mo.enumerator (mo.indexOfEnumerator (#ENUM)).valueToKey (static_cast<int> (v)); \
24 } \
25 \
26 QDataStream& operator >> (QDataStream& is, CLASS::ENUM& v) \
27 { \
28 char * buffer; \
29 is >> buffer; \
30 bool ok {false}; \
31 auto const& mo = CLASS::staticMetaObject; \
32 auto const& me = mo.enumerator (mo.indexOfEnumerator (#ENUM)); \
33 if (buffer) \
34 { \
35 v = static_cast<CLASS::ENUM> (me.keyToValue (buffer, &ok)); \
36 delete [] buffer; \
37 } \
38 if (!ok) \
39 { \
40 v = static_cast<CLASS::ENUM> (me.value (0)); \
41 } \
42 return is; \
43 }
44
45 #define ENUM_CONVERSION_OPS_DECL(CLASS, ENUM) \
46 QString enum_to_qstring (CLASS::ENUM const&);
47
48 #define ENUM_CONVERSION_OPS_IMPL(CLASS, ENUM) \
49 QString enum_to_qstring (CLASS::ENUM const& m) \
50 { \
51 auto const& mo = CLASS::staticMetaObject; \
52 return QString {mo.enumerator (mo.indexOfEnumerator (#ENUM)).valueToKey (static_cast<int> (m))}; \
53 }
54
55 #if QT_VERSION >= QT_VERSION_CHECK (5, 15, 0)
56 Qt::SplitBehaviorFlags const SkipEmptyParts = Qt::SkipEmptyParts;
57 #else
58 QString::SplitBehavior const SkipEmptyParts = QString::SkipEmptyParts;
59 #endif
60
61 inline
operator <<(std::ostream & os,QByteArray const & b)62 std::ostream& operator << (std::ostream& os, QByteArray const& b)
63 {
64 return os << b.constData ();
65 }
66
67 inline
operator <<(std::ostream & os,QString const & s)68 std::ostream& operator << (std::ostream& os, QString const& s)
69 {
70 return os << s.toStdString ();
71 }
72
73 inline
throw_qstring(QString const & qs)74 void throw_qstring (QString const& qs)
75 {
76 throw std::runtime_error {qs.toLocal8Bit ().constData ()};
77 }
78
79 QString font_as_stylesheet (QFont const&);
80
81 // do what is necessary to change a dynamic property and trigger any
82 // conditional style sheet updates
83 void update_dynamic_property (QWidget *, char const * property, QVariant const& value);
84
85 // round a QDateTime instance to an integral interval of milliseconds
86 QDateTime qt_round_date_time_to (QDateTime dt, int milliseconds);
87
88 // truncate a QDateTime to an integral interval of milliseconds
89 QDateTime qt_truncate_date_time_to (QDateTime dt, int milliseconds);
90
91 template <class T>
92 class VPtr
93 {
94 public:
asPtr(QVariant v)95 static T * asPtr (QVariant v)
96 {
97 return reinterpret_cast<T *> (v.value<void *> ());
98 }
99
asQVariant(T * ptr)100 static QVariant asQVariant(T * ptr)
101 {
102 return QVariant::fromValue (reinterpret_cast<void *> (ptr));
103 }
104 };
105
106 #if QT_VERSION < QT_VERSION_CHECK (5, 14, 0)
107 // The Qt devs "fixed" this in 5.14 to specialize to use their own
108 // qHash(), it doesn't fix the problem we were addressing as qHash()
109 // returns a uint so is still a poorly distributed 32-bit value on
110 // 64-bit platforms, but we can't specialize ourselves as Qt already
111 // has - sigh.
112 namespace std
113 {
114 // std::hash<> specialization for QString based on the dbj2
115 // algorithm http://www.cse.yorku.ca/~oz/hash.html because qHash()
116 // is poor on 64-bit platforms due to being a 32-bit hash value
117 template<>
118 struct hash<QString>
119 {
operator ()std::hash120 std::size_t operator () (QString const& s) const noexcept
121 {
122 std::size_t hash {5381};
123 for (int i = 0; i < s.size (); ++i)
124 {
125 hash = ((hash << 5) + hash) + ((s.at (i).row () << 8) | s.at (i).cell ());
126 }
127 return hash;
128 }
129 };
130 }
131 #endif
132
133 inline
is_broadcast_address(QHostAddress const & host_addr)134 bool is_broadcast_address (QHostAddress const& host_addr)
135 {
136 #if QT_VERSION >= QT_VERSION_CHECK (5, 11, 0)
137 return host_addr.isBroadcast ();
138 #else
139 bool ok;
140 return host_addr.toIPv4Address (&ok) == 0xffffffffu && ok;
141 #endif
142 }
143
144 inline
is_multicast_address(QHostAddress const & host_addr)145 bool is_multicast_address (QHostAddress const& host_addr)
146 {
147 #if QT_VERSION >= QT_VERSION_CHECK (5, 6, 0)
148 return host_addr.isMulticast ();
149 #else
150 bool ok;
151 return (((host_addr.toIPv4Address (&ok) & 0xf0000000u) == 0xe0000000u) && ok)
152 || host_addr.toIPv6Address ()[0] == 0xff;
153 #endif
154 }
155
156 inline
is_MAC_ambiguous_multicast_address(QHostAddress const & host_addr)157 bool is_MAC_ambiguous_multicast_address (QHostAddress const& host_addr)
158 {
159 // sub-ranges 224.128.0.0/24, 225.0.0.0/24, 225.128.0.0/24,
160 // 226.0.0.0/24, 226.128.0.0/24, ..., 239.0.0.0/24, 239.128.0.0/24
161 // are not supported as they are inefficient due to ambiguous
162 // mappings to Ethernet MAC addresses. 224.0.0.0/24 alone is allowed
163 // from these ranges
164 bool ok;
165 auto ipv4 = host_addr.toIPv4Address (&ok);
166 return ok && !((ipv4 & 0xffffff00u) == 0xe0000000) && (ipv4 & 0xf07fff00) == 0xe0000000;
167 }
168
169 // Register some useful Qt types with QMetaType
170 Q_DECLARE_METATYPE (QHostAddress);
171
172 #endif
173