Merge remote-tracking branch 'origin/stable' into dev

Conflicts:
	src/corelib/tools/qstring.cpp

Change-Id: Ifc6cd3a0f1cf14cc0fe6cf30afb0c7f40cfdbc3e
This commit is contained in:
Frederik Gladhorn 2013-09-16 13:49:26 +02:00
commit 8c6755aeec
22 changed files with 205 additions and 272 deletions

View File

@ -49,7 +49,6 @@ static const int ConnectTimeout = 60 * 1000;
static const int KeepAliveInterval = 30 * 1000;
static const int RateControlTimerDelay = 2000;
static const int MinimalHeaderSize = 48;
static const int FullHeaderSize = 68;
static const char ProtocolId[] = "BitTorrent protocol";
static const char ProtocolIdSize = 19;

View File

@ -64,7 +64,6 @@ static const int RateControlTimerDelay = 1000;
static const int MinimumTimeBeforeRevisit = 30;
static const int MaxUploads = 4;
static const int UploadScheduleInterval = 10000;
static const int EndGamePieces = 5;
class TorrentPiece {
public:

View File

@ -44,8 +44,6 @@
#include <QtGui/QImage>
#include <QtCore/QPropertyAnimation>
static const qreal FACE_SIZE = 0.4;
static const qreal speeds[] = { 3.8f, 4.4f, 5.6f };
static const qreal amplitudes[] = { 2.0f, 2.5f, 3.0f };

View File

@ -49,10 +49,7 @@
static const qreal tee_height = 0.311126;
static const qreal cross_width = 0.25;
static const qreal bar_thickness = 0.113137;
static const qreal inside_diam = 0.20;
static const qreal outside_diam = 0.30;
static const qreal logo_depth = 0.10;
static const int num_divisions = 32;
//! [0]
struct Geometry

View File

@ -142,7 +142,7 @@ void MainWindow::showAlbumDetails(QModelIndex index)
break;
}
}
if (!trackList->count() == 0)
if (trackList->count() != 0)
trackList->show();
}

View File

@ -44,10 +44,7 @@
* demonstrates use of QThread, says hello in another thread and terminates
*/
//! [1]
// hellothread/hellothread.cpp
void HelloThread::run()
{
qDebug() << "hello from worker thread " << thread()->currentThreadId();
}
//! [1]

View File

@ -42,13 +42,12 @@
#define HELLOTHREAD_H
#include <QThread>
//! [1]
// hellothread/hellothread.h
class HelloThread : public QThread
{
Q_OBJECT
private:
void run();
};
//! [1]
#endif // HELLOTHREAD_H

View File

@ -41,7 +41,6 @@
#include <QtCore>
#include "hellothread.h"
//! [1]
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
@ -51,4 +50,3 @@ int main(int argc, char *argv[])
thread.wait(); // do not exit before the thread is completed!
return 0;
}
//! [1]

View File

@ -84,7 +84,7 @@ protected:
virtual QString escapeFilePath(const QString &path) const;
ProString escapeFilePath(const ProString &path) const { return MakefileGenerator::escapeFilePath(path); }
bool doPrecompiledHeaders() const { return false; }
virtual bool doDepends() const { return false; } //never necesary
virtual bool doDepends() const { return writingUnixMakefileGenerator && UnixMakefileGenerator::doDepends(); }
};
inline ProjectBuilderMakefileGenerator::~ProjectBuilderMakefileGenerator()

View File

@ -42,16 +42,6 @@
#include <QMutex>
#include <QThreadStorage>
#include "threads.h"
//! [0]
void MyThread::run()
//! [0] //! [1]
{
//! [1] //! [2]
}
//! [2]
#define Counter ReentrantCounter
//! [3]

View File

@ -1,51 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
** of its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QThread>
//! [0]
class MyThread : public QThread
{
Q_OBJECT
protected:
void run();
};
//! [0]

View File

@ -172,109 +172,16 @@
\section2 Which Qt Thread Technology Should You Use?
Sometimes you want to do more than just running a method in the context of
another thread. You may want to have an object which lives in another
thread that provides a service to the GUI thread. Maybe you want another
thread to stay alive forever to poll hardware ports and send a signal to
the GUI thread when something noteworthy has happened. Qt provides
different solutions for developing threaded applications. The right
solution depends on the purpose of the new thread as well as on the
thread's lifetime.
\table
\header
\li Lifetime of thread
\li Development task
\li Solution
\row
\li One call
\li Run one method within another thread and quit the thread when the
method is finished.
\li Qt provides different solutions:
\list
\li Write a function and run it with QtConcurrent::run()
\li Derive a class from QRunnable and run it in the global thread
pool with QThreadPool::globalInstance()->start()
\li Derive a class from QThread, reimplement the QThread::run()
method and use QThread::start() to run it.
\endlist
\row
\li One call
\li Operations are to be performed on all items of a container.
Processing should be performed using all available cores. A common
example is to produce thumbnails from a list of images.
\li QtConcurrent provides the \l{QtConcurrent::}{map()} function for
applying operations on every container element,
\l{QtConcurrent::}{filter()} for selecting container elements, and
the option of specifying a reduce function for combining the
remaining elements.
\row
\li One call
\li A long running operation has to be put in another thread. During the
course of processing, status information should be sent to the GUI
thread.
\li Use QThread, reimplement run and emit signals as needed. Connect the
signals to the GUI thread's slots using queued signal/slot
connections.
\row
\li Permanent
\li Have an object living in another thread and let it perform different
tasks upon request.
This means communication to and from the worker thread is required.
\li Derive a class from QObject and implement the necessary slots and
signals, move the object to a thread with a running event loop and
communicate with the object over queued signal/slot connections.
\row
\li Permanent
\li Have an object living in another thread, let the object perform
repeated tasks such as polling a port and enable communication with
the GUI thread.
\li Same as above but also use a timer in the worker thread to implement
polling. However, the best solution for polling is to avoid it
completely. Sometimes using QSocketNotifier is an alternative.
\endtable
See the \l{Multithreading Technologies in Qt} page for an introduction to the
different approaches to multithreading to Qt, and for guidelines on how to
choose among them.
\section1 Qt Thread Basics
QThread is a very convenient cross platform abstraction of native platform
threads. Starting a thread is very simple. Let us look at a short piece of
code that generates another thread which says hello in that thread and then
exits.
\snippet ../widgets/tutorials/threads/hellothread/hellothread.h 1
We derive a class from QThread and reimplement the \l{QThread::}{run()}
method.
\snippet ../widgets/tutorials/threads/hellothread/hellothread.cpp 1
The run method contains the code that will be run in a separate thread. In
this example, a message containing the thread ID will be printed.
QThread::start() will call the method in another thread.
\snippet ../widgets/tutorials/threads/hellothread/main.cpp 1
To start the thread, our thread object needs to be instantiated. The
\l{QThread::}{start()} method creates a new thread and calls the
reimplemented \l{QThread::}{run()} method in this new thread. Right after
\l{QThread::}{start()} is called, two program counters walk through the
program code. The main function starts with only the GUI thread running and
it should terminate with only the GUI thread running. Exiting the program
when another thread is still busy is a programming error, and therefore,
wait is called which blocks the calling thread until the
\l{QThread::}{run()} method has completed.
This is the result of running the code:
\code
//bad code
hello from GUI thread 3079423696
hello from worker thread 3076111216
\endcode
The following sections describe how QObjects interact with threads, how
programs can safely access data from multiple threads, and how asynchronous
execution produces results without blocking a thread.
\section2 QObject and Threads

View File

@ -41,7 +41,7 @@
\ingroup frameworks-technologies
\nextpage Starting Threads with QThread
\nextpage Multithreading Technologies in Qt
Qt provides thread support in the form of platform-independent
threading classes, a thread-safe way of posting events, and
@ -59,7 +59,7 @@
\list
\li \l{Recommended Reading}
\li \l{The Threading Classes}
\li \l{Starting Threads with QThread}
\li \l{Multithreading Technologies in Qt}
\li \l{Synchronizing Threads}
\li \l{Reentrancy and Thread-Safety}
\li \l{Threads and QObjects}
@ -112,59 +112,182 @@
*/
/*!
\page threads-starting.html
\title Starting Threads with QThread
\page threads-technologies.html
\title Multithreading Technologies in Qt
\ingroup qt-basic-concepts
\brief An overview and comparison of different ways to use threads in Qt.
\ingroup frameworks-technologies
\contentspage Thread Support in Qt
\previouspage Thread Support in Qt
\nextpage Synchronizing Threads
A QThread instance represents a thread and provides the means to
\l{QThread::start()}{start()} a thread, which will then execute the
reimplementation of QThread::run(). The \c run() implementation is for a
thread what the \c main() entry point is for the application. All code
executed in a call stack that starts in the \c run() function is executed
by the new thread, and the thread finishes when the function returns.
QThread emits signals to indicate that the thread started or finished
executing.
Qt offers many classes and functions for working with threads. Below are
three different approaches that Qt programmers can use to implement
multithreaded applications.
\section1 Creating a Thread
To create a thread, subclass QThread and reimplement its
\l{QThread::run()}{run()} function. For example:
\section1 QThread: Low-Level API with Optional Event Loops
\snippet threads/threads.h 0
\codeline
\snippet threads/threads.cpp 0
\snippet threads/threads.cpp 1
\dots
\snippet threads/threads.cpp 2
QThread is the foundation of all thread control in Qt. Each QThread
instance represents and controls one thread.
\section1 Starting a Thread
QThread can either be instantiated directly or subclassed. Instantiating a
QThread provides a parallel event loop, allowing QObject slots to be invoked
in a secondary thread. Subclassing a QThread allows the application to initialize
the new thread before starting its event loop, or to run parallel code
without an event loop.
Then, create an instance of the thread object and call
QThread::start(). Note that you must create the QApplication (or
QCoreApplication) object before you can create a QThread.
See the \l{QThread}{QThread class reference} and the \l{Threading and
Concurrent Programming Examples}{threading examples} for demonstrations on
how to use QThread.
The function will return immediately and the
main thread will continue. The code that appears in the
\l{QThread::run()}{run()} reimplementation will then be executed
in a separate thread.
Creating threads is explained in more detail in the QThread
documentation.
\section1 QThreadPool and QRunnable: Reusing Threads
Note that QCoreApplication::exec() must always be called from the
main thread (the thread that executes \c{main()}), not from a
QThread. In GUI applications, the main thread is also called the
GUI thread because it's the only thread that is allowed to
perform GUI-related operations.
Creating and destroying threads frequently can be expensive. To reduce this
overhead, existing threads can be reused for new tasks. QThreadPool is a
collection of reuseable QThreads.
To run code in one of a QThreadPool's threads, reimplement QRunnable::run()
and instantiate the subclassed QRunnable. Use QThreadPool::start() to put
the QRunnable in the QThreadPool's run queue. When a thread becomes available,
the code within QRunnable::run() will execute in that thread.
Each Qt application has a global thread pool, which is accessible through
QThreadPool::globalInstance(). This global thread pool automatically maintains
an optimal number of threads based on the number of cores in the CPU. However,
a separate QThreadPool can be created and managed explicitly.
\section1 Qt Concurrent: Using a High-level API
The \l{Qt Concurrent} module provides high-level functions that deal with some
common parallel computation patterns: map, filter, and reduce. Unlike QThread
and QRunnable, these functions do not require the use of low-level threading
primitives such as mutexes or semaphores. \l {Qt Concurrent} will automatically
adjust the number of threads used according to the number of processor cores
available, so applications written today will continue to scale when deployed
later on a system with more cores.
This module also provides the QtConcurrent::run() function, which can run
any function in a thread managed by the global QThreadPool.
See the \l{Qt Concurrent} module documentation for details on the individual functions.
\section1 Choosing an Appropriate Approach
As demonstrated above, Qt provides different solutions for developing threaded
applications. The right solution for a given application depends on the purpose
of the new thread and the thread's lifetime. Below is a comparison of Qt's
threading technologies, followed by recommended solutions for some example use cases.
\section2 Comparison of Solutions
\table
\header
\li Feature/Characteristic
\li QThread
\li QRunnable
\li Qt Concurrent\sup{*}
\row
\li Supports different thread priorities
\li Yes
\li
\li
\row
\li Supports an event loop
\li Yes
\li
\li
\row
\li Supports transferring data to the thread using signals
\li Yes (received by a worker QObject)
\li
\li
\row
\li Supports controlling the thread using signals
\li Yes (received by QThread)
\li
\li Yes (received by QFutureWatcher)
\row
\li Supports thread reuse
\li
\li Yes
\li Yes
\row
\li Task-oriented
\li
\li Yes
\li Yes
\row
\li High level API
\li
\li
\li Yes
\row
\li Supports pausing/resuming/canceling
\li
\li
\li Yes
\endtable
\sup{\e{*Except QtConcurrent::run(), which is like QRunnable}}
\section2 Example Use Cases
\table
\header
\li Lifetime of thread
\li Operation
\li Solution
\row
\li One call
\li Run a linear function within another thread, optionally with progress
updates during the run.
\li Qt provides different solutions:
\list
\li Place the function in a reimplementation of QThread::run() and
start the QThread. Emit signals to update progress. OR
\li Place the function in a reimplementation of QRunnable::run() and
add the QRunnable to a QThreadPool. Write to a \l{Synchronizing
Threads}{thread-safe variable} to update progress. OR
\li Run the function using QtConcurrent::run(). Write to a \l{Synchronizing
Threads}{thread-safe variable} to update progress.
\endlist
\row
\li One call
\li Perform an operation on all items of a container, using all available
cores. For example, producing thumbnails from a list of images.
\li Use Qt Concurrent's \l{QtConcurrent::}{filter()} function to select
container elements, and the \l{QtConcurrent::}{map()} function to apply
an operation to each element. To fold the output into a single result,
use \l{QtConcurrent::}{filterReduced()} and \l{QtConcurrent::}{mapReduced()}
instead.
\row
\li Permanent
\li Have an object living in another thread that can perform different
tasks upon request and/or can receive new data to work with.
\li Subclass a QObject to create a worker. Instantiate this worker object
and a QThread. Move the worker to the new thread. Send commands or
data to the worker object over queued signal-slot connections.
\row
\li Permanent
\li Repeatedly perform an expensive operation in another thread, where the
thread does not need to receive any signals or events.
\li Write the infinite loop directly within a reimplementation of QThread::run().
Start the thread without an event loop. Let the thread emit signals to
send data back to the GUI thread.
\endtable
*/
/*!
\page threads-synchronizing.html
\title Synchronizing Threads
\previouspage Starting Threads with QThread
\previouspage Multithreading Technologies in Qt
\contentspage Thread Support in Qt
\nextpage Reentrancy and Thread-Safety

View File

@ -805,8 +805,7 @@ const QString::Null QString::null = { };
/*! \typedef QString::const_iterator
The QString::const_iterator typedef provides an STL-style const
iterator for QString.
This typedef provides an STL-style const iterator for QString.
\sa QString::iterator
*/
@ -834,13 +833,12 @@ const QString::Null QString::null = { };
/*!
\typedef QString::const_reference
The QString::const_reference typedef provides an STL-style
const reference for a QString element (QChar).
This typedef provides an STL-style const reference for a QString element (QChar).
*/
/*!
\typedef QString::reference
The QString::reference typedef provides an STL-style
This typedef provides an STL-style
reference for a QString element (QChar).
*/
@ -860,8 +858,7 @@ const QString::Null QString::null = { };
/*!
\typedef QString::value_type
The QString::const_reference typedef provides an STL-style
value type for QString.
This typedef provides an STL-style value type for QString.
*/
/*! \fn QString::iterator QString::begin()

View File

@ -104,6 +104,10 @@ public: // typedefs
{ return QLatin1String(interface) < other; }
inline bool operator<(const QByteArray &other) const
{ return interface < other; }
#if defined(Q_CC_MSVC) && _MSC_VER < 1600
friend inline bool operator<(const QString &str, const AdaptorData &obj)
{ return str < QLatin1String(obj.interface); }
#endif
};
typedef QVector<AdaptorData> AdaptorMap;

View File

@ -804,7 +804,7 @@ bool QDBusConnection::registerObject(const QString &path, QObject *object, Regis
return false;
if (options & QDBusConnectionPrivate::VirtualObject) {
if (options & SubPath && node->activeChildren)
if (options & SubPath && !node->children.isEmpty())
return false;
} else {
if ((options & ExportChildObjects && !node->children.isEmpty()))
@ -842,7 +842,6 @@ bool QDBusConnection::registerObject(const QString &path, QObject *object, Regis
}
} else {
// add entry
++node->activeChildren;
node = node->children.insert(it, pathComponents.at(i));
}

View File

@ -140,16 +140,24 @@ public:
{
typedef QVector<ObjectTreeNode> DataList;
inline ObjectTreeNode() : obj(0), flags(0), activeChildren(0) { }
inline ObjectTreeNode() : obj(0), flags(0) { }
inline ObjectTreeNode(const QString &n) // intentionally implicit
: name(n), obj(0), flags(0), activeChildren(0) { }
: name(n), obj(0), flags(0) { }
inline ~ObjectTreeNode() { }
inline bool operator<(const QString &other) const
{ return name < other; }
inline bool operator<(const QStringRef &other) const
{ return QStringRef(&name) < other; }
#if defined(Q_CC_MSVC) && _MSC_VER < 1600
inline bool operator<(const ObjectTreeNode &other) const
{ return name < other.name; }
friend inline bool operator<(const QString &str, const ObjectTreeNode &obj)
{ return str < obj.name; }
friend inline bool operator<(const QStringRef &str, const ObjectTreeNode &obj)
{ return str < QStringRef(&obj.name); }
#endif
inline bool isActive() const
{ return obj || activeChildren; }
{ return obj || !children.isEmpty(); }
QString name;
union {
@ -157,7 +165,6 @@ public:
QDBusVirtualObject *treeNode;
};
int flags;
int activeChildren;
DataList children;
};

View File

@ -587,46 +587,22 @@ bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg)
return false;
}
static void garbageCollectChildren(QDBusConnectionPrivate::ObjectTreeNode &node)
{
int size = node.children.count();
if (node.activeChildren == 0) {
// easy case
node.children.clear();
} else if (size > node.activeChildren * 3 || (size > 20 && size * 2 > node.activeChildren * 3)) {
// rewrite the vector, keeping only the active children
// if the vector is large (> 20 items) and has one third of inactives
// or if the vector is small and has two thirds of inactives.
QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator end = node.children.end();
QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = node.children.begin();
QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator tgt = it;
for ( ; it != end; ++it) {
if (it->isActive())
*tgt++ = qMove(*it);
}
++tgt;
node.children.erase(tgt, end);
}
}
static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode &haystack)
{
QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = haystack.children.begin();
QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator end = haystack.children.end();
for ( ; it != end; ++it) {
if (!it->isActive())
continue;
while (it != haystack.children.end()) {
huntAndDestroy(needle, *it);
if (!it->isActive())
--haystack.activeChildren;
it = haystack.children.erase(it);
else
it++;
}
if (needle == haystack.obj) {
haystack.obj = 0;
haystack.flags = 0;
}
garbageCollectChildren(haystack);
}
static void huntAndUnregister(const QStringList &pathComponents, int i, QDBusConnection::UnregisterMode mode,
@ -639,7 +615,6 @@ static void huntAndUnregister(const QStringList &pathComponents, int i, QDBusCon
if (mode == QDBusConnection::UnregisterTree) {
// clear the sub-tree as well
node->activeChildren = 0;
node->children.clear(); // can't disconnect the objects because we really don't know if they can
// be found somewhere else in the path too
}
@ -648,14 +623,12 @@ static void huntAndUnregister(const QStringList &pathComponents, int i, QDBusCon
QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator end = node->children.end();
QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it =
std::lower_bound(node->children.begin(), end, pathComponents.at(i));
if (it == end || it->name != pathComponents.at(i) || !it->isActive())
if (it == end || it->name != pathComponents.at(i))
return; // node not found
huntAndUnregister(pathComponents, i + 1, mode, it);
if (!it->isActive())
--node->activeChildren;
garbageCollectChildren(*node);
node->children.erase(it);
}
}

View File

@ -70,7 +70,6 @@ QT_END_NAMESPACE
Qt::KeyboardModifiers currentWheelModifiers;
bool m_subscribesForGlobalFrameNotifications;
QCocoaGLContext *m_glContext;
bool m_drawRectHasBeenCalled;
bool m_shouldSetGLContextinDrawRect;
}

View File

@ -87,7 +87,6 @@ static QTouchDevice *touchDevice = 0;
m_sendKeyEvent = false;
m_subscribesForGlobalFrameNotifications = false;
m_glContext = 0;
m_drawRectHasBeenCalled = false;
m_shouldSetGLContextinDrawRect = false;
currentCustomDragTypes = 0;
m_sendUpAsRightButton = false;
@ -154,9 +153,9 @@ static QTouchDevice *touchDevice = 0;
- (void) setQCocoaGLContext:(QCocoaGLContext *)context
{
m_glContext = context;
if (m_drawRectHasBeenCalled) {
[m_glContext->nsOpenGLContext() setView:self];
} else {
[m_glContext->nsOpenGLContext() setView:self];
if (![m_glContext->nsOpenGLContext() view]) {
//was unable to set view
m_shouldSetGLContextinDrawRect = true;
}
@ -393,8 +392,6 @@ static QTouchDevice *touchDevice = 0;
m_shouldSetGLContextinDrawRect = false;
}
m_drawRectHasBeenCalled = true;
if (!m_backingStore)
return;

View File

@ -2467,7 +2467,7 @@ void tst_QSettings::testEscapes()
QString s = QSettingsPrivate::variantToString(v); \
QCOMPARE(s, escStr); \
QCOMPARE(QVariant(QSettingsPrivate::stringToVariant(escStr)), v); \
QVERIFY(val == v.func()); \
QVERIFY((val) == v.func()); \
}
#define testBadEscape(escStr, vStr) \

View File

@ -56,10 +56,11 @@
#define STRINGPREP_BIDI_LEADTRAIL_NOT_RAL 5
struct ushortarray {
ushortarray(unsigned short *array = 0)
ushortarray() {}
template <size_t N>
ushortarray(unsigned short (&array)[N])
{
if (array)
memcpy(points, array, sizeof(points));
memcpy(points, array, N*sizeof(unsigned short));
}
unsigned short points[100];