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