Initial Commit

This commit is contained in:
Justin Hammond 2014-10-04 00:09:38 +08:00
commit 5b48528a3e
21 changed files with 6366 additions and 0 deletions

0
AUTHORS Normal file
View file

458
COPYING Normal file
View file

@ -0,0 +1,458 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS

0
ChangeLog Normal file
View file

370
INSTALL Normal file
View file

@ -0,0 +1,370 @@
Installation Instructions
*************************
Copyright (C) 1994-1996, 1999-2002, 2004-2012 Free Software Foundation,
Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without warranty of any kind.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package. Some packages provide this
`INSTALL' file but do not implement all of the features documented
below. The lack of an optional feature in a given package is not
necessarily a bug. More recommendations for GNU packages can be found
in *note Makefile Conventions: (standards)Makefile Conventions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package, generally using the just-built uninstalled binaries.
4. Type `make install' to install the programs and any data files and
documentation. When installing into a prefix owned by root, it is
recommended that the package be configured and built as a regular
user, and only the `make install' phase executed with root
privileges.
5. Optionally, type `make installcheck' to repeat any self-tests, but
this time using the binaries in their final installed location.
This target does not install anything. Running this target as a
regular user, particularly if the prior `make install' required
root privileges, verifies that the installation completed
correctly.
6. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
7. Often, you can also type `make uninstall' to remove the installed
files again. In practice, not all packages have tested that
uninstallation works correctly, even though it is required by the
GNU Coding Standards.
8. Some packages, particularly those that use Automake, provide `make
distcheck', which can by used by developers to test that all other
targets like `make install' and `make uninstall' work correctly.
This target is generally not run by end users.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. Run `./configure --help'
for details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'. This
is known as a "VPATH" build.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
On MacOS X 10.5 and later systems, you can create libraries and
executables that work on multiple system types--known as "fat" or
"universal" binaries--by specifying multiple `-arch' options to the
compiler but only a single `-arch' option to the preprocessor. Like
this:
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CPP="gcc -E" CXXCPP="g++ -E"
This is not guaranteed to produce working output in all cases, you
may have to build one architecture at a time and combine the results
using the `lipo' tool if you have problems.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX', where PREFIX must be an
absolute file name.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them. In general, the
default for these options is expressed in terms of `${prefix}', so that
specifying just `--prefix' will affect all of the other directory
specifications that were not explicitly provided.
The most portable way to affect installation locations is to pass the
correct locations to `configure'; however, many packages provide one or
both of the following shortcuts of passing variable assignments to the
`make install' command line to change installation locations without
having to reconfigure or recompile.
The first method involves providing an override variable for each
affected directory. For example, `make install
prefix=/alternate/directory' will choose an alternate location for all
directory configuration variables that were expressed in terms of
`${prefix}'. Any directories that were specified during `configure',
but not in terms of `${prefix}', must each be overridden at install
time for the entire installation to be relocated. The approach of
makefile variable overrides for each directory variable is required by
the GNU Coding Standards, and ideally causes no recompilation.
However, some platforms have known limitations with the semantics of
shared libraries that end up requiring recompilation when using this
method, particularly noticeable in packages that use GNU Libtool.
The second method involves providing the `DESTDIR' variable. For
example, `make install DESTDIR=/alternate/directory' will prepend
`/alternate/directory' before all installation names. The approach of
`DESTDIR' overrides is not required by the GNU Coding Standards, and
does not work on platforms that have drive letters. On the other hand,
it does better at avoiding recompilation issues, and works well even
when some directory options were not specified in terms of `${prefix}'
at `configure' time.
Optional Features
=================
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Some packages offer the ability to configure how verbose the
execution of `make' will be. For these packages, running `./configure
--enable-silent-rules' sets the default to minimal output, which can be
overridden with `make V=1'; while running `./configure
--disable-silent-rules' sets the default to verbose, which can be
overridden with `make V=0'.
Particular systems
==================
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
CC is not installed, it is recommended to use the following options in
order to use an ANSI C compiler:
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
HP-UX `make' updates targets which have the same time stamps as
their prerequisites, which makes it generally unusable when shipped
generated files such as `configure' are involved. Use GNU `make'
instead.
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
a workaround. If GNU CC is not installed, it is therefore recommended
to try
./configure CC="cc"
and if that doesn't work, try
./configure CC="cc -nodtk"
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
directory contains several dysfunctional programs; working variants of
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
in your `PATH', put it _after_ `/usr/bin'.
On Haiku, software installed for all users goes in `/boot/common',
not `/usr/local'. It is recommended to use the following options:
./configure --prefix=/boot/common
Specifying the System Type
==========================
There may be some features `configure' cannot figure out
automatically, but needs to determine by the type of machine the package
will run on. Usually, assuming the package is built to be run on the
_same_ architectures, `configure' can figure that out, but if it prints
a message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS
KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf limitation. Until the limitation is lifted, you can use
this workaround:
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it
operates.
`--help'
`-h'
Print a summary of all of the options to `configure', and exit.
`--help=short'
`--help=recursive'
Print a summary of the options unique to this package's
`configure', and exit. The `short' variant lists options used
only in the top level, while the `recursive' variant lists options
also present in any nested packages.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`--prefix=DIR'
Use DIR as the installation prefix. *note Installation Names::
for more details, including other options available for fine-tuning
the installation locations.
`--no-create'
`-n'
Run the configure checks, but stop before creating any output
files.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

0
NEWS Normal file
View file

1
README Normal file
View file

@ -0,0 +1 @@
This is the libiHanClient Library.

View file

@ -0,0 +1,396 @@
/****************************************************************************
**
**
****************************************************************************/
#ifndef DEVICEMODEL_H
#define DEVICEMODEL_H
#define QT_SHAREDPOINTER_TRACK_POINTERS 1
#include <QtCore/QAbstractListModel>
#include <QtCore/QStringList>
#include <QtCore/QDebug>
#include "iHanClient/varcontainer.hpp"
#include "iHanClient/MessageBus.hpp"
#include "iHanClient/MsgTypes.hpp"
class DeviceItem
{
public:
DeviceItem(VarStorage data, DeviceItem *parent = 0);
~DeviceItem();
void setData(VarStorage data);
void appendChild(DeviceItem *child);
DeviceItem *child(int row);
int childCount() const;
int columnCount() const;
VarStorage data() const;
int row() const;
DeviceItem *parent();
bool removeChild(DeviceItem *item);
bool remove();
private:
QList<DeviceItem *> childItems;
VarStorage itemData;
DeviceItem *parentItem;
};
class DeviceModel_t : public QAbstractItemModel {
Q_OBJECT
public:
enum DeviceRoles {
NameRole = Qt::UserRole + 1,
SerialRole,
TypeRole,
ParentSerialRole,
ConfigRole,
ConfigDescRole,
VariablesRole,
VariablesDescRole,
VariableHelper,
ConfigHelper
};
enum DeviceColumns {
DeviceName = 0,
DeviceSerial,
DeviceType,
DeviceParentSerial,
DeviceConfig,
DeviceConfigDescriptor,
DeviceVariable,
DeviceVariableDescriptor,
DeviceVarHelper,
DeviceConfigHelper,
DEVICEMODEL_T_COLUMNS_MAX = DeviceConfigHelper + 1
};
DeviceModel_t(QObject *parent = 0);
~DeviceModel_t();
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
int columnCount(const QModelIndex & /*parent*/) const;
void addDevice(const VarStorage &device);
void delDevice(const std::string Device);
QModelIndex index(int, int, const QModelIndex&) const;
QModelIndex parent(const QModelIndex&) const;
QModelIndex FindDevice(QString serial);
Q_INVOKABLE int rowCount(const QModelIndex & parent = QModelIndex()) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
bool setData (const QModelIndex & index, const QVariant &value, int role = Qt::EditRole);
void setData (QString serial, QString name, QVariant value, bool sync = true);
void updateDevice(const VarStorage &device);
void updateDeviceConfig(const VarStorage &device);
signals:
void sendMsg(MessageBus);
private:
QMap <std::string, DeviceItem*> m_devices;
DeviceItem *rootItem;
};
typedef boost::shared_ptr<DeviceModel_t> DeviceModel;
extern QMap<std::string, VarStorage> GlobalDevices;
typedef QMap<int, QString> VarList_t;
Q_DECLARE_METATYPE(DeviceModel_t*)
Q_DECLARE_METATYPE(VarList_t);
class VarStorageElement: public QObject {
Q_OBJECT
public:
VarStorageElement() {};
VarStorageElement(VarStorage item) { this->var = item; };
void SetVarStorageElement(VarStorage item) { this->var = item; };
VarStorage GetItem() { return this->var;};
Q_INVOKABLE QStringList getFields(VarStorage Var) {
if (!Var) {
qDebug() << "Empty Var for getFields";
return QStringList();
}
std::vector<std::string> *fields;
QStringList fieldnames;
fields = Var->getFields();
std::vector<std::string>::iterator it;
for (it = fields->begin(); it != fields->end(); ++it) {
fieldnames.append(static_cast<std::string>((*it)).c_str());
}
return fieldnames;
}
Q_INVOKABLE QString getString(VarStorage Var, QString name, int pos = 0) {
if (!Var) {
qDebug() << "Empty Var for " << name;
return QString();
}
if ((unsigned int)pos > Var->getSize(name.toStdString())) {
qWarning() << "pos is greater than size for " << name;
return QString();
}
std::string result;
if (Var->getStringValue(name.toStdString(), result, pos) == true)
return result.c_str();
else {
qDebug() << "No Variable Named " << name;
return QString();
}
}
Q_INVOKABLE int getInt(VarStorage Var, QString name, int pos = 0) {
if (!Var) return 0;
int result;
if ((unsigned int)pos > Var->getSize(name.toStdString())) {
qWarning() << "pos is greater than size for " << name;
return 0;
}
if (Var->getIntValue(name.toStdString(), result, pos) == true)
return result;
else
return 0;
}
Q_INVOKABLE qlonglong getLong(VarStorage Var, QString name, int pos = 0) {
if (!Var) return 0;
long result;
if ((unsigned int)pos > Var->getSize(name.toStdString())) {
qWarning() << "pos is greater than size for " << name;
return 0;
}
if (Var->getLongValue(name.toStdString(), result, pos) == true)
return result;
else
return 0;
}
Q_INVOKABLE qlonglong getLongLong(VarStorage Var, QString name, int pos = 0) {
if (!Var) return 0;
long long result;
if ((unsigned int)pos > Var->getSize(name.toStdString())) {
qWarning() << "pos is greater than size for " << name;
return 0;
}
if (Var->getLongLongValue(name.toStdString(), result, pos) == true)
return result;
else
return 0;
}
Q_INVOKABLE float getFloat(VarStorage Var, QString name, int pos = 0) {
if (!Var) return 0;
if ((unsigned int)pos > Var->getSize(name.toStdString())) {
qWarning() << "pos is greater than size for " << name;
return 0;
}
float result;
if (Var->getFloatValue(name.toStdString(), result, pos) == true)
return result;
else
return 0;
}
Q_INVOKABLE bool getBool(VarStorage Var, QString name, int pos = 0) {
if (!Var) return false;
if ((unsigned int)pos > Var->getSize(name.toStdString())) {
qWarning() << "pos is greater than size for " << name;
return false;
}
bool result;
if (Var->getBoolValue(name.toStdString(), result, pos) == true)
return result;
else
return false;
}
Q_INVOKABLE QString getTime(VarStorage Var, QString name, int pos = 0) {
if (!Var) return QString();
if ((unsigned int)pos > Var->getSize(name.toStdString())) {
qWarning() << "pos is greater than size for " << name;
return QString();
}
boost::posix_time::ptime result;
if (Var->getTimeValue(name.toStdString(), result, pos) == true)
return boost::posix_time::to_iso_extended_string(result).c_str();
else
return boost::posix_time::to_iso_extended_string(boost::posix_time::ptime(boost::date_time::not_a_date_time)).c_str();
}
Q_INVOKABLE QVariant getHash(VarStorage Var, QString name, QString element, int pos = 0) {
if (!Var) return QVariant();
if ((unsigned int)pos > Var->getSize(name.toStdString())) {
qWarning() << "pos is greater than size for " << name;
return QVariant();
}
HashVals result;
if (Var->getHashValue(name.toStdString(), result, pos) == true) {
if (result[element.toStdString()].type() == typeid(std::string)) {
return QString(boost::get<std::string>(result[element.toStdString()]).c_str());
} else if (result[element.toStdString()].type() == typeid(int)) {
return (int)boost::get<int>(result[element.toStdString()]);
} else if (result[element.toStdString()].type() == typeid(long)) {
return (qlonglong)boost::get<long>(result[element.toStdString()]);
} else if (result[element.toStdString()].type() == typeid(long long)) {
return (qlonglong)boost::get<long long>(result[element.toStdString()]);
} else if (result[element.toStdString()].type() == typeid(float)) {
return (float)boost::get<float>(result[element.toStdString()]);
} else if (result[element.toStdString()].type() == typeid(boost::posix_time::ptime)) {
return QString(boost::posix_time::to_iso_string(boost::get<boost::posix_time::ptime>(result[element.toStdString()])).c_str());
} else if (result[element.toStdString()].type() == typeid(ListOptions)) {
ListOptions lv = boost::get<ListOptions>(result[element.toStdString()]);
int i = 0;
VarList_t map;
while (strlen(lv[i].desc) > 0) {
map.insert(lv[i].index, lv[i].desc);
i++;
}
return QVariant::fromValue<VarList_t>(map);
} else {
qWarning() << "Can't Handle Hash Type ";
return QVariant();
}
} else {
return QVariant();
}
}
Q_INVOKABLE QVariant getHash(VarStorage Var, QString name, int pos = 0) {
if (!Var) return QVariant();
if ((unsigned int)pos > Var->getSize(name.toStdString())) {
qWarning() << "pos is greater than size for " << name;
return QVariant();
}
HashVals result;
if (Var->getHashValue(name.toStdString(), result, pos) == true) {
return QVariant::fromValue<HashVals>(result);
} else {
return QVariant();
}
}
Q_INVOKABLE QVariant getVarStorage(VarStorage Var, QString name, int pos = 0) {
if (!Var) return QVariant();
if ((unsigned int)pos > Var->getSize(name.toStdString())) {
qWarning() << "pos is greater than size for " << name;
return QVariant();
}
VarStorage result;
if (Var->getVarStorageValue(name.toStdString(), result, pos) == true) {
/* we should make a copy, not just pass the shared_ptr out. This way
* any updates to the VarStorage will not be automatically reflected here
*/
VarContainerCopy(returnval, result);
return QVariant::fromValue<VarStorage>(returnval);
} else {
return QVariant();
}
}
Q_INVOKABLE QVariant getList(VarStorage Var, QString name, int pos = 0) {
if (!Var) return QVariant();
if ((unsigned int)pos > Var->getSize(name.toStdString())) {
qWarning() << "pos is greater than size for " << name;
return QVariant();
}
list_const_iterator iter;
VarList_t map;
std::cout << Var << std::endl;
std::cout << name.toStdString() << std::endl;
for (iter = Var->getListIterBegin(name.toStdString(), pos); iter != Var->getListIterEnd(name.toStdString(), pos); ++iter) {
std::cout << (*iter).first << " " << (*iter).second.c_str() << std::endl;
map.insert((*iter).first, (*iter).second.c_str());
std::cout << "done" << std::endl;
}
return QVariant::fromValue<VarList_t>(map);
}
Q_INVOKABLE QVariant getListSelection(VarStorage Var, QString name, int pos = 0) {
if (!Var) return QVariant();
if ((unsigned int)pos > Var->getSize(name.toStdString())) {
qWarning() << "pos is greater than size for " << name;
return QVariant();
}
unsigned int result;
if (Var->getListSelectedValue(name.toStdString(), result, pos)) {
return result;
} else {
return QVariant();
}
}
Q_INVOKABLE void setData(QObject *model, QString serial, QString name, QVariant value, int pos = 0) {
if (pos > 0)
qWarning() << "setData for Arrays not implemented yet";
return;
dynamic_cast<DeviceModel_t*>(model)->setData(serial, name, value);
}
Q_INVOKABLE bool getReadOnly(VarStorage Var, QString name) {
if (!Var) {
qWarning() << "Var Doesn't exist in getReadOnly?!?!? (looking for " << name << ")";
return true;
}
HashVals fieldconfig;
qWarning() << "Looking For " << name << " as Read Only\n";
if (Var->getHashValue(name.toStdString(), fieldconfig)) {
if ((int)boost::get<int>(fieldconfig["flags"]) && TCF_READONLY) {
return true;
} else {
return false;
}
}
qWarning() << "Doesn exist\n";
return false;
}
Q_INVOKABLE QString getString(QString name, int pos = 0) { return this->getString(this->var, name, pos);}
Q_INVOKABLE int getInt(QString name, int pos = 0) { return this->getInt(this->var, name, pos);}
Q_INVOKABLE qlonglong getLong(QString name, int pos = 0) { return this->getLong(this->var, name, pos);}
Q_INVOKABLE qlonglong getLongLong(QString name, int pos = 0) { return this->getLongLong(this->var, name, pos); }
Q_INVOKABLE float getFloat(QString name, int pos = 0) { return this->getFloat(this->var, name, pos);}
Q_INVOKABLE bool getBool(QString name, int pos = 0) { return this->getBool(this->var, name, pos);}
Q_INVOKABLE QString getTime(QString name, int pos = 0) { return this->getTime(this->var, name, pos);}
Q_INVOKABLE QVariant getHash(QString name, QString element, int pos = 0) { return this->getHash(this->var, name, element, pos);}
Q_INVOKABLE QVariant getList(QString name, int pos = 0) { return this->getList(this->var, name, pos);}
Q_INVOKABLE QVariant getListSelection(QString name, int pos = 0) { return this->getListSelection(this->var, name, pos);}
Q_INVOKABLE QStringList getFields() { return this->getFields(this->var); }
private:
VarStorage var;
};
class VarStorageHelper_t {
public:
VarStorageHelper_t(VarStorage value, VarStorage descriptor, QString Serial);
~VarStorageHelper_t();
QVariant getValue(QString name, int pos = 0);
VarStorage setValue(VarStorage, QString, QVariant, int pos = 0);
QVariant getListOptions(QString name, int pos = 0);
int getRealType(QString name);
int getType(QString name);
int getType(QString name, QString element);
QString getName(QString name);
QString getDescription(QString name);
qlonglong getMin(QString name);
qlonglong getMax(QString name);
QVariant getDefault(QString name);
int getFlags(QString name);
bool getReadOnly(QString name);
int getGroup(QString name);
QStringList getItems();
int getArrayMaxSize(QString name);
int getSize(QString name);
VarStorage delValue(VarStorage UpdateVals, QString name, int pos = 0);
QString getSerial();
int getVarType(QString name);
private:
VarStorage values;
VarStorage descriptor;
VarStorageElement VSE;
QString Serial;
};
typedef QSharedPointer<VarStorageHelper_t> VarStorageHelper;
Q_DECLARE_METATYPE(VarStorageHelper)
#endif

View file

@ -0,0 +1,126 @@
/*
* MessageHandler.h
*
* Created on: Sep 1, 2009
* Author: fish
*/
#ifndef MESSAGEHANDLER_H_
#define MESSAGEHANDLER_H_
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QtNetwork>
#include "support/MuscleSupport.h"
#include "message/Message.h"
#include "iogateway/MessageIOGateway.h"
#include "iogateway/AbstractMessageIOGateway.h"
#include "util/RefCount.h"
#include "util/Hashtable.h"
//#include "qtsupport/QSocketDataIO.h"
#include "iHanClient/varcontainer.hpp"
#include "iHanClient/MessageBus.hpp"
#include "iHanClient/MsgTypes.hpp"
class QTcpDataIO;
typedef enum State_e {S_DISCONNECTED, S_CONNECTED, S_READY} State_e;
class MessageHandler : public QObject, public muscle::AbstractGatewayMessageReceiver
{
Q_OBJECT
public:
MessageHandler(QObject *parent = 0);
virtual ~MessageHandler();
Q_PROPERTY(int type READ getType WRITE setType);
Q_PROPERTY(QString username READ getUserName WRITE setUserName);
Q_PROPERTY(QString password WRITE setPassword);
Q_PROPERTY(QString hostname READ getHostName WRITE setHostName);
Q_PROPERTY(quint32 port READ getPort WRITE setPort);
Q_PROPERTY(quint32 flags READ getFlags WRITE setFlags);
void setType(int Type);
int getType();
void setUserName(QString name);
QString getUserName();
void setPassword(QString password);
QString getPassword();
void setHostName(QString hostname);
QString getHostName();
void setPort(quint32 port);
quint32 getPort();
quint32 getFlags() { return this->flags; };
void setFlags(quint32 newflags) { this->flags = newflags; };
int getHostID() {
return this->hostid;
}
void setHostID(int val) {
this->hostid = val;
}
bool connect();
State_e getState();
Q_SIGNALS:
void connected();
void disconnected();
void error(QString, QAbstractSocket::SocketError);
void StateChange(State_e);
void newEndPt(MessageBus item);
void delEndPt(MessageBus item);
void updateValues(MessageBus vals);
void updateConfig(MessageBus vals);
void gotTermTypeMapping(MessageBus vals);
void gotMyInfo(MessageBus vals);
public Q_SLOTS:
void sendMessage(MessageBus);
private slots:
void HandleSockPackets();
void HandleSockError(QAbstractSocket::SocketError);
void HandleSockUpdate(QAbstractSocket::SocketState);
void HandleConnected();
private:
void MessageReceivedFromGateway(const muscle::MessageRef & msg, void * /*userData*/);
void processServerCaps(MessageBus msg);
void processNewEndPt(MessageBus msg);
void processDelEndPt(MessageBus msg);
void processSensorUpdate(MessageBus msg);
void processConfigUpdate(MessageBus msg);
void processSetup(MessageBus msg);
int type;
QString username;
QString password;
QString hostname;
quint32 port;
muscle::MessageIOGateway gw;
QTcpSocket *tcpSocket;
QTcpDataIO *tcpDataIO;
int hostid;
State_e CurState;
quint32 flags;
};
class QTcpDataIO : public muscle::DataIO, public muscle::MessageIOGateway
{
public:
QTcpDataIO(QTcpSocket *thetcpsocket);
virtual ~QTcpDataIO () { }
int32 Write(const void * buffer, uint32 size);
int32 Read(void * buffer, uint32 size);
virtual status_t Seek(int64 /*offset*/, int /*whence*/) {return B_ERROR;};
virtual int64 GetPosition() const {return B_ERROR;};
virtual void FlushOutput() {return;};
virtual void Shutdown() {if (tcpsocket->isOpen()) tcpsocket->close();};
virtual const muscle::ConstSocketRef & GetSelectSocket() const { return muscle::GetNullSocket(); };
virtual const muscle::ConstSocketRef & GetReadSelectSocket() const { return muscle::GetNullSocket(); };
virtual const muscle::ConstSocketRef & GetWriteSelectSocket() const { return muscle::GetNullSocket(); };
void ReleaseSocket() {return;};
void setParent(QTcpSocket *myparent) { this->tcpsocket = myparent;}
void RealRead();
private:
QTcpSocket *tcpsocket;
QByteArray indata;
};
#endif /* MESSAGEHANDLER_H_ */

View file

@ -0,0 +1,124 @@
/* controlpanel - QtiHanClient.h
** Copyright (c) 2010 Justin Hammond
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** controlpanel SVN Identification:
** $Rev$
*/
/** @file QtiHanClient.h
* @brief
*/
#ifndef QTIHANCLIENT_H_
#define QTIHANCLIENT_H_
#include <QtCore/QtCore>
#include <QtiHanClient/MessageHandler.h>
#include <QtiHanClient/DeviceModel.h>
#include <QtiHanClient/kdescendantsproxymodel.h>
#include <QtiHanClient/modeltest.h>
class QtiHanClient : public QObject {
Q_OBJECT
public:
/* VarType TermName VarID VarValue */
typedef QMap< QString, QMap < QString, QMap < QString, QVariant > > > Term_Map_t;
static QtiHanClient* Create(QObject *parent=0);
static QtiHanClient* Get(){ return s_instance; }
static void Destroy();
Q_PROPERTY(int type READ getType WRITE setType);
Q_PROPERTY(QString username READ getUserName WRITE setUserName);
Q_PROPERTY(QString password READ getPassword WRITE setPassword);
Q_PROPERTY(QString hostname READ getHostName WRITE setHostName);
Q_PROPERTY(quint32 port READ getPort WRITE setPort);
Q_PROPERTY(int hostid READ getHostID WRITE setHostID);
void setType(int Type);
int getType();
void setUserName(QString name);
QString getUserName();
void setPassword(QString password);
QString getPassword();
void setHostName(QString hostname);
QString getHostName();
void setPort(quint32 port);
quint32 getPort();
int getHostID();
void setHostID(int);
bool connect();
DeviceModel_t *getDeviceModel() { return this->tdm; };
KDescendantsProxyModel *getFlatDeviceModel() { return this->fdm; };
Term_Map_t getTermTypeMappings() { return this->TermTypeMappings; };
QString getMyDeviceID();
Q_SIGNALS:
void connected();
void disconnected();
void error(QString, QAbstractSocket::SocketError);
void newEndPt(VarStorage item);
void delEndPt(std::string item);
void updateValues(QString, QVector<QString>);
void updateConfig(QString, QVector<QString>);
void StateChange(State_e);
public Q_SLOTS:
void sendMessage(MessageBus);
private Q_SLOTS:
void HandleConnected();
void HandleDisconnected();
void HandleError(QString, QAbstractSocket::SocketError);
void HandleNewDevice(MessageBus item);
void HandleDelDevice(MessageBus item);
void HandleDeviceUpdate(MessageBus item);
void HandleDeviceConfigUpdate(MessageBus item);
void HandleStateChange(State_e state);
void HandleTermTypeMappings(MessageBus vals);
void HandleClientInform(MessageBus vals);
private:
QtiHanClient(QObject *parent = 0);
virtual ~QtiHanClient();
int type;
MessageHandler *mh;
DeviceModel_t *tdm;
KDescendantsProxyModel *fdm;
ModelTest *mt;
Term_Map_t TermTypeMappings;
static QtiHanClient* s_instance;
VarStorage myinfo;
};
#endif /* QTIHANCLIENT_H_ */

View file

@ -0,0 +1,624 @@
/*
Copyright (C) 2010 Klarälvdalens Datakonsult AB,
a KDAB Group company, info@kdab.net,
author Stephen Kelly <stephen@kdab.com>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#ifndef KBIHASH_P_H
#define KBIHASH_P_H
#include <QtCore/QHash>
#include <QtCore/QMap>
#include <QtCore/QDebug>
template<typename LeftContainer, typename RightContainer>
class KBiAssociativeContainer;
template<typename LeftContainer, typename RightContainer>
QDebug operator<<(QDebug out, const KBiAssociativeContainer<LeftContainer, RightContainer> &container);
template<typename LeftContainer, typename RightContainer>
QDataStream &operator<<(QDataStream &out, const KBiAssociativeContainer<LeftContainer, RightContainer> &container);
template<typename LeftContainer, typename RightContainer>
QDataStream &operator>>(QDataStream &in, KBiAssociativeContainer<LeftContainer, RightContainer> &container);
template<typename LeftContainer, typename RightContainer>
class KBiAssociativeContainer
{
// We need to convert from a QHash::iterator or QMap::iterator
// to a KBiAssociativeContainer::iterator (left or right)
// Do do that we use this implicit ctor. We partially specialize
// it for QHash and QMap types.
// Our iterator inherits from this struct to get the implicit ctor,
// and this struct must inherit from the QHash or QMap iterator.
template<typename Container, typename T, typename U>
struct _iterator_impl_ctor : public Container::iterator {
_iterator_impl_ctor(typename Container::iterator it);
};
template<typename T, typename U>
struct _iterator_impl_ctor<QHash<T, U>, T, U> : public QHash<T, U>::iterator {
/* implicit */ _iterator_impl_ctor(const typename QHash<T, U>::iterator it)
: QHash<T, U>::iterator(it)
{
}
};
template<typename T, typename U>
struct _iterator_impl_ctor<QMap<T, U>, T, U> : public QMap<T, U>::iterator {
/* implicit */ _iterator_impl_ctor(const typename QMap<T, U>::iterator it)
: QMap<T, U>::iterator(it)
{
}
};
public:
typedef typename RightContainer::mapped_type left_type;
typedef typename LeftContainer::mapped_type right_type;
template <typename Container>
class _iterator : public _iterator_impl_ctor<Container, typename Container::key_type, typename Container::mapped_type>
{
public:
explicit inline _iterator(void *data) : Container::iterator(data) {}
/* implicit */ _iterator(const typename Container::iterator it)
: _iterator_impl_ctor<Container, typename Container::key_type, typename Container::mapped_type>(it)
{
}
inline const typename Container::mapped_type &value() const
{
return Container::iterator::value();
}
inline const typename Container::mapped_type &operator*() const
{
return Container::iterator::operator*();
}
inline const typename Container::mapped_type *operator->() const
{
return Container::iterator::operator->();
}
private:
#ifndef Q_CC_MSVC
using Container::iterator::operator*;
using Container::iterator::operator->;
using Container::iterator::value;
#endif
};
typedef _iterator<LeftContainer> left_iterator;
typedef typename LeftContainer::const_iterator left_const_iterator;
typedef _iterator<RightContainer> right_iterator;
typedef typename RightContainer::const_iterator right_const_iterator;
inline KBiAssociativeContainer() {}
inline KBiAssociativeContainer(const KBiAssociativeContainer<LeftContainer, RightContainer> &other)
{
*this = other;
}
const KBiAssociativeContainer<LeftContainer, RightContainer> &operator=(const KBiAssociativeContainer<LeftContainer, RightContainer> &other)
{
_leftToRight = other._leftToRight; _rightToLeft = other._rightToLeft; return *this;
}
inline bool removeLeft(left_type t)
{
const right_type u = _leftToRight.take(t);
return _rightToLeft.remove(u) != 0;
}
inline bool removeRight(right_type u)
{
const left_type t = _rightToLeft.take(u);
return _leftToRight.remove(t) != 0;
}
inline right_type takeLeft(left_type t)
{
const right_type u = _leftToRight.take(t);
_rightToLeft.remove(u);
return u;
}
inline left_type takeRight(right_type u)
{
const left_type t = _rightToLeft.take(u);
_leftToRight.remove(t);
return t;
}
inline left_type rightToLeft(right_type u) const
{
return _rightToLeft.value(u);
}
inline right_type leftToRight(left_type t) const
{
return _leftToRight.value(t);
}
inline bool leftContains(left_type t) const
{
return _leftToRight.contains(t);
}
inline bool rightContains(right_type u) const
{
return _rightToLeft.contains(u);
}
inline int size() const
{
return _leftToRight.size();
}
inline int count() const
{
return _leftToRight.count();
}
inline int capacity() const
{
return _leftToRight.capacity();
}
void reserve(int size)
{
_leftToRight.reserve(size); _rightToLeft.reserve(size);
}
inline void squeeze()
{
_leftToRight.squeeze(); _rightToLeft.squeeze();
}
inline void detach()
{
_leftToRight.detach(); _rightToLeft.detach();
}
inline bool isDetached() const
{
return _leftToRight.isDetached();
}
inline void setSharable(bool sharable)
{
_leftToRight.setSharable(sharable); _rightToLeft.setSharable(sharable);
}
inline bool isSharedWith(const KBiAssociativeContainer<RightContainer, LeftContainer> &other) const
{
return _leftToRight.isSharedWith(other._leftToRight) && _rightToLeft.isSharedWith(other._leftToRight);
}
void clear()
{
_leftToRight.clear(); _rightToLeft.clear();
}
QList<left_type> leftValues() const
{
return _leftToRight.keys();
}
QList<right_type> rightValues() const
{
return _rightToLeft.keys();
}
right_iterator eraseRight(right_iterator it)
{
Q_ASSERT(it != rightEnd());
_leftToRight.remove(it.value());
return _rightToLeft.erase(it);
}
left_iterator eraseLeft(left_iterator it)
{
Q_ASSERT(it != leftEnd());
_rightToLeft.remove(it.value());
return _leftToRight.erase(it);
}
left_iterator findLeft(left_type t)
{
return _leftToRight.find(t);
}
left_const_iterator findLeft(left_type t) const
{
return _leftToRight.find(t);
}
left_const_iterator constFindLeft(left_type t) const
{
return _leftToRight.constFind(t);
}
right_iterator findRight(right_type u)
{
return _rightToLeft.find(u);
}
right_const_iterator findRight(right_type u) const
{
return _rightToLeft.find(u);
}
right_const_iterator constFindRight(right_type u) const
{
return _rightToLeft.find(u);
}
left_iterator insert(left_type t, right_type u)
{
// biHash.insert(5, 7); // creates 5->7 in _leftToRight and 7->5 in _rightToLeft
// biHash.insert(5, 9); // replaces 5->7 with 5->9 in _leftToRight and inserts 9->5 in _rightToLeft.
// The 7->5 in _rightToLeft would be dangling, so we remove it before insertion.
// This means we need to hash u and t up to twice each. Could probably be done better using QHashNode.
if (_leftToRight.contains(t)) {
_rightToLeft.remove(_leftToRight.take(t));
}
if (_rightToLeft.contains(u)) {
_leftToRight.remove(_rightToLeft.take(u));
}
_rightToLeft.insert(u, t);
return _leftToRight.insert(t, u);
}
KBiAssociativeContainer<LeftContainer, RightContainer> &intersect(const KBiAssociativeContainer<LeftContainer, RightContainer> &other)
{
typename KBiAssociativeContainer<RightContainer, LeftContainer>::left_iterator it = leftBegin();
while (it != leftEnd()) {
if (!other.leftContains(it.key())) {
it = eraseLeft(it);
} else {
++it;
}
}
return *this;
}
KBiAssociativeContainer<LeftContainer, RightContainer> &subtract(const KBiAssociativeContainer<LeftContainer, RightContainer> &other)
{
typename KBiAssociativeContainer<RightContainer, LeftContainer>::left_iterator it = leftBegin();
while (it != leftEnd()) {
if (other._leftToRight.contains(it.key())) {
it = eraseLeft(it);
} else {
++it;
}
}
return *this;
}
KBiAssociativeContainer<LeftContainer, RightContainer> &unite(const KBiAssociativeContainer<LeftContainer, RightContainer> &other)
{
typename LeftContainer::const_iterator it = other._leftToRight.constBegin();
const typename LeftContainer::const_iterator end = other._leftToRight.constEnd();
while (it != end) {
const left_type key = it.key();
if (!_leftToRight.contains(key)) {
insert(key, it.value());
}
++it;
}
return *this;
}
void updateRight(left_iterator it, right_type u)
{
Q_ASSERT(it != leftEnd());
const left_type key = it.key();
_rightToLeft.remove(_leftToRight.value(key));
_leftToRight[key] = u;
_rightToLeft[u] = key;
}
void updateLeft(right_iterator it, left_type t)
{
Q_ASSERT(it != rightEnd());
const right_type key = it.key();
_leftToRight.remove(_rightToLeft.value(key));
_rightToLeft[key] = t;
_leftToRight[t] = key;
}
inline bool isEmpty() const
{
return _leftToRight.isEmpty();
}
const right_type operator[](const left_type &t) const
{
return _leftToRight.operator[](t);
}
bool operator==(const KBiAssociativeContainer<LeftContainer, RightContainer> &other)
{
return _leftToRight.operator == (other._leftToRight);
}
bool operator!=(const KBiAssociativeContainer<LeftContainer, RightContainer> &other)
{
return _leftToRight.operator != (other._leftToRight);
}
left_iterator toLeftIterator(right_iterator it) const
{
Q_ASSERT(it != rightEnd());
return _leftToRight.find(it.value());
}
right_iterator toRightIterator(left_iterator it) const
{
Q_ASSERT(it != leftEnd());
return _rightToLeft.find(it.value());
}
inline left_iterator leftBegin()
{
return _leftToRight.begin();
}
inline left_iterator leftEnd()
{
return _leftToRight.end();
}
inline left_const_iterator leftBegin() const
{
return _leftToRight.begin();
}
inline left_const_iterator leftEnd() const
{
return _leftToRight.end();
}
inline left_const_iterator leftConstBegin() const
{
return _leftToRight.constBegin();
}
inline left_const_iterator leftConstEnd() const
{
return _leftToRight.constEnd();
}
inline right_iterator rightBegin()
{
return _rightToLeft.begin();
}
inline right_iterator rightEnd()
{
return _rightToLeft.end();
}
inline right_const_iterator rightBegin() const
{
return _rightToLeft.begin();
}
inline right_const_iterator rightEnd() const
{
return _rightToLeft.end();
}
inline right_const_iterator rightConstBegin() const
{
return _rightToLeft.constBegin();
}
inline right_const_iterator rightConstEnd() const
{
return _rightToLeft.constEnd();
}
static KBiAssociativeContainer<LeftContainer, RightContainer> fromHash(const QHash<left_type, right_type> &hash)
{
KBiAssociativeContainer<LeftContainer, RightContainer> container;
typename QHash<left_type, right_type>::const_iterator it = hash.constBegin();
const typename QHash<left_type, right_type>::const_iterator end = hash.constEnd();
for (; it != end; ++it) {
container.insert(it.key(), it.value());
}
return container;
}
static KBiAssociativeContainer<LeftContainer, RightContainer> fromMap(const QMap<left_type, right_type> &hash)
{
KBiAssociativeContainer<LeftContainer, RightContainer> container;
typename QMap<left_type, right_type>::const_iterator it = hash.constBegin();
const typename QMap<left_type, right_type>::const_iterator end = hash.constEnd();
for (; it != end; ++it) {
container.insert(it.key(), it.value());
}
return container;
}
friend QDataStream &operator<< <LeftContainer, RightContainer>(QDataStream &out, const KBiAssociativeContainer<LeftContainer, RightContainer> &bihash);
friend QDataStream &operator>> <LeftContainer, RightContainer>(QDataStream &in, KBiAssociativeContainer<LeftContainer, RightContainer> &biHash);
friend QDebug operator<< <LeftContainer, RightContainer>(QDebug out, const KBiAssociativeContainer<LeftContainer, RightContainer> &biHash);
protected:
LeftContainer _leftToRight;
RightContainer _rightToLeft;
};
template<typename LeftContainer, typename RightContainer>
QDataStream &operator<<(QDataStream &out, const KBiAssociativeContainer<LeftContainer, RightContainer> &container)
{
return out << container._leftToRight;
}
template<typename LeftContainer, typename RightContainer>
QDataStream &operator>>(QDataStream &in, KBiAssociativeContainer<LeftContainer, RightContainer> &container)
{
LeftContainer leftToRight;
in >> leftToRight;
typename LeftContainer::const_iterator it = leftToRight.constBegin();
const typename LeftContainer::const_iterator end = leftToRight.constEnd();
for (; it != end; ++it) {
container.insert(it.key(), it.value());
}
return in;
}
template<typename Container, typename T, typename U>
struct _containerType {
operator const char *();
};
template<typename T, typename U>
struct _containerType<QHash<T, U>, T, U> {
operator const char *()
{
return "QHash";
}
};
template<typename T, typename U>
struct _containerType<QMap<T, U>, T, U> {
operator const char *()
{
return "QMap";
}
};
template<typename Container>
static const char *containerType()
{
return _containerType<Container, typename Container::key_type, typename Container::mapped_type>();
}
template<typename LeftContainer, typename RightContainer>
QDebug operator<<(QDebug out, const KBiAssociativeContainer<LeftContainer, RightContainer> &container)
{
typename KBiAssociativeContainer<LeftContainer, RightContainer>::left_const_iterator it = container.leftConstBegin();
const typename KBiAssociativeContainer<LeftContainer, RightContainer>::left_const_iterator end = container.leftConstEnd();
out.nospace() << "KBiAssociativeContainer<" << containerType<LeftContainer>() << ", " << containerType<RightContainer>() << ">" << "(";
for (; it != end; ++it) {
out << "(" << it.key() << " <=> " << it.value() << ") ";
}
out << ")";
return out;
}
/**
* @brief KBiHash provides a bi-directional hash container
*
* @note This class is designed to make mapping easier in proxy model implementations.
*
* @todo Figure out whether to discard this and use boost::bimap instead, submit it Qt or keep it here and make more direct use of QHashNode.
*/
template <typename T, typename U>
struct KBiHash : public KBiAssociativeContainer<QHash<T, U>, QHash<U, T> > {
KBiHash()
: KBiAssociativeContainer<QHash<T, U>, QHash<U, T> > ()
{
}
KBiHash(const KBiAssociativeContainer<QHash<T, U>, QHash<U, T> > &container)
: KBiAssociativeContainer<QHash<T, U>, QHash<U, T> > (container)
{
}
};
template<typename T, typename U>
QDebug operator<<(QDebug out, const KBiHash<T, U> &biHash)
{
typename KBiHash<T, U>::left_const_iterator it = biHash.leftConstBegin();
const typename KBiHash<T, U>::left_const_iterator end = biHash.leftConstEnd();
out.nospace() << "KBiHash(";
for (; it != end; ++it) {
out << "(" << it.key() << " <=> " << it.value() << ") ";
}
out << ")";
return out;
}
template <typename T, typename U>
struct KHash2Map : public KBiAssociativeContainer<QHash<T, U>, QMap<U, T> > {
KHash2Map()
: KBiAssociativeContainer<QHash<T, U>, QMap<U, T> > ()
{
}
KHash2Map(const KBiAssociativeContainer<QHash<T, U>, QMap<U, T> > &container)
: KBiAssociativeContainer<QHash<T, U>, QMap<U, T> > (container)
{
}
typename KBiAssociativeContainer<QHash<T, U>, QMap<U, T> >::right_iterator rightLowerBound(const U &key)
{
return this->_rightToLeft.lowerBound(key);
}
typename KBiAssociativeContainer<QHash<T, U>, QMap<U, T> >::right_const_iterator rightLowerBound(const U &key) const
{
return this->_rightToLeft.lowerBound(key);
}
typename KBiAssociativeContainer<QHash<T, U>, QMap<U, T> >::right_iterator rightUpperBound(const U &key)
{
return this->_rightToLeft.upperBound(key);
}
typename KBiAssociativeContainer<QHash<T, U>, QMap<U, T> >::right_const_iterator rightUpperBound(const U &key) const
{
return this->_rightToLeft.upperBound(key);
}
};
template<typename T, typename U>
QDebug operator<<(QDebug out, const KHash2Map<T, U> &container)
{
typename KHash2Map<T, U>::left_const_iterator it = container.leftConstBegin();
const typename KHash2Map<T, U>::left_const_iterator end = container.leftConstEnd();
out.nospace() << "KHash2Map(";
for (; it != end; ++it) {
out << "(" << it.key() << " <=> " << it.value() << ") ";
}
out << ")";
return out;
}
#endif

View file

@ -0,0 +1,206 @@
/*
Copyright (c) 2009 Stephen Kelly <steveire@gmail.com>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#ifndef KDESCENDANTSPROXYMODEL_P_H
#define KDESCENDANTSPROXYMODEL_P_H
#include <QAbstractProxyModel>
class KDescendantsProxyModelPrivate;
#include "kitemmodels_export.h"
/**
@brief Proxy Model for restructuring a Tree into a list.
A KDescendantsProxyModel may be used to alter how the items in the tree are presented.
Given a model which is represented as a tree:
\image html entitytreemodel.png "A plain EntityTreeModel in a view"
The KDescendantsProxyModel restructures the sourceModel to represent it as a flat list.
@code
// ... Create an entityTreeModel
KDescendantsProxyModel *descProxy = new KDescendantsProxyModel(this);
descProxy->setSourceModel(entityTree);
view->setModel(descProxy);
@endcode
\image html descendantentitiesproxymodel.png "A KDescendantsProxyModel."
KDescendantEntitiesProxyModel can also display the ancestors of the index in the source model as part of its display.
@code
// ... Create an entityTreeModel
KDescendantsProxyModel *descProxy = new KDescendantsProxyModel(this);
descProxy->setSourceModel(entityTree);
// #### This is new
descProxy->setDisplayAncestorData(true);
descProxy->setDisplayAncestorSeparator(QString(" / "));
view->setModel(descProxy);
@endcode
\image html descendantentitiesproxymodel-withansecnames.png "A KDescendantsProxyModel with ancestor names."
@since 4.6
@author Stephen Kelly <steveire@gmail.com>
*/
class KITEMMODELS_EXPORT KDescendantsProxyModel : public QAbstractProxyModel
{
Q_OBJECT
public:
/**
* Creates a new descendant entities proxy model.
*
* @param parent The parent object.
*/
explicit KDescendantsProxyModel(QObject *parent = 0);
/**
* Destroys the descendant entities proxy model.
*/
virtual ~KDescendantsProxyModel();
/**
* Sets the source @p model of the proxy.
*/
virtual void setSourceModel(QAbstractItemModel *model);
/**
* @deprecated
*
* This method does nothing.
*/
void setRootIndex(const QModelIndex &index);
/**
* Set whether to show ancestor data in the model. If @p display is true, then
* a source model which is displayed as
*
* @code
* -> "Item 0-0" (this is row-depth)
* -> -> "Item 0-1"
* -> -> "Item 1-1"
* -> -> -> "Item 0-2"
* -> -> -> "Item 1-2"
* -> "Item 1-0"
* @endcode
*
* will be displayed as
*
* @code
* -> *Item 0-0"
* -> "Item 0-0 / Item 0-1"
* -> "Item 0-0 / Item 1-1"
* -> "Item 0-0 / Item 1-1 / Item 0-2"
* -> "Item 0-0 / Item 1-1 / Item 1-2"
* -> "Item 1-0"
* @endcode
*
* If @p display is false, the proxy will show
*
* @code
* -> *Item 0-0"
* -> "Item 0-1"
* -> "Item 1-1"
* -> "Item 0-2"
* -> "Item 1-2"
* -> "Item 1-0"
* @endcode
*
* Default is false.
*/
void setDisplayAncestorData(bool display);
/**
* Whether ancestor data will be displayed.
*/
bool displayAncestorData() const;
/**
* Sets the ancestor @p separator used between data of ancestors.
*/
void setAncestorSeparator(const QString &separator);
/**
* Separator used between data of ancestors.
*/
QString ancestorSeparator() const;
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
virtual QMimeData *mimeData(const QModelIndexList &indexes) const;
virtual QStringList mimeTypes() const;
virtual bool hasChildren(const QModelIndex &parent = QModelIndex()) const;
virtual QModelIndex index(int, int, const QModelIndex &parent = QModelIndex()) const;
virtual QModelIndex parent(const QModelIndex &) const;
virtual int columnCount(const QModelIndex &index = QModelIndex()) const;
virtual Qt::DropActions supportedDropActions() const;
/**
Reimplemented to match all descendants.
*/
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value,
int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const;
private:
Q_DECLARE_PRIVATE(KDescendantsProxyModel)
//@cond PRIVATE
KDescendantsProxyModelPrivate *d_ptr;
Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeInserted(const QModelIndex &, int, int))
Q_PRIVATE_SLOT(d_func(), void sourceRowsInserted(const QModelIndex &, int, int))
Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeRemoved(const QModelIndex &, int, int))
Q_PRIVATE_SLOT(d_func(), void sourceRowsRemoved(const QModelIndex &, int, int))
Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int))
Q_PRIVATE_SLOT(d_func(), void sourceRowsMoved(const QModelIndex &, int, int, const QModelIndex &, int))
Q_PRIVATE_SLOT(d_func(), void sourceModelAboutToBeReset())
Q_PRIVATE_SLOT(d_func(), void sourceModelReset())
Q_PRIVATE_SLOT(d_func(), void sourceLayoutAboutToBeChanged())
Q_PRIVATE_SLOT(d_func(), void sourceLayoutChanged())
Q_PRIVATE_SLOT(d_func(), void sourceDataChanged(const QModelIndex &, const QModelIndex &))
Q_PRIVATE_SLOT(d_func(), void sourceModelDestroyed())
Q_PRIVATE_SLOT(d_func(), void processPendingParents())
// Make these private, they shouldn't be called by applications
// virtual bool insertRows(int , int, const QModelIndex & = QModelIndex());
// virtual bool insertColumns(int, int, const QModelIndex & = QModelIndex());
// virtual bool removeRows(int, int, const QModelIndex & = QModelIndex());
// virtual bool removeColumns(int, int, const QModelIndex & = QModelIndex());
//@endcond
};
#endif

View file

@ -0,0 +1,41 @@
#ifndef KITEMMODELS_EXPORT_H
#define KITEMMODELS_EXPORT_H
#ifdef KITEMMODELS_STATIC_DEFINE
# define KITEMMODELS_EXPORT
# define KITEMMODELS_NO_EXPORT
#else
# ifndef KITEMMODELS_EXPORT
# ifdef KF5ItemModels_EXPORTS
/* We are building this library */
# define KITEMMODELS_EXPORT __attribute__((visibility("default")))
# else
/* We are using this library */
# define KITEMMODELS_EXPORT __attribute__((visibility("default")))
# endif
# endif
# ifndef KITEMMODELS_NO_EXPORT
# define KITEMMODELS_NO_EXPORT __attribute__((visibility("hidden")))
# endif
#endif
#ifndef KITEMMODELS_DEPRECATED
# define KITEMMODELS_DEPRECATED __attribute__ ((__deprecated__))
#endif
#ifndef KITEMMODELS_DEPRECATED_EXPORT
# define KITEMMODELS_DEPRECATED_EXPORT KITEMMODELS_EXPORT KITEMMODELS_DEPRECATED
#endif
#ifndef KITEMMODELS_DEPRECATED_NO_EXPORT
# define KITEMMODELS_DEPRECATED_NO_EXPORT KITEMMODELS_NO_EXPORT KITEMMODELS_DEPRECATED
#endif
#define DEFINE_NO_DEPRECATED 0
#if DEFINE_NO_DEPRECATED
# define KITEMMODELS_NO_DEPRECATED
#endif
#endif

View file

@ -0,0 +1,95 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef MODELTEST_H
#define MODELTEST_H
#include <QtCore/QObject>
#include <QtCore/QAbstractItemModel>
#include <QtCore/QStack>
class ModelTest : public QObject
{
Q_OBJECT
public:
ModelTest( QAbstractItemModel *model, QObject *parent = 0 );
private Q_SLOTS:
void nonDestructiveBasicTest();
void rowCount();
void columnCount();
void hasIndex();
void index();
void parent();
void data();
protected Q_SLOTS:
void runAllTests();
void layoutAboutToBeChanged();
void layoutChanged();
void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end );
void rowsInserted( const QModelIndex & parent, int start, int end );
void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end );
void rowsRemoved( const QModelIndex & parent, int start, int end );
void runDataTests(const QModelIndex &, const QModelIndex & );
private:
void checkChildren( const QModelIndex &parent, int currentDepth = 0 );
QAbstractItemModel *model;
struct Changing {
QModelIndex parent;
int oldSize;
QVariant last;
QVariant next;
};
QStack<Changing> insert;
QStack<Changing> remove;
bool fetchingMore;
QList<QPersistentModelIndex> changing;
};
#endif

85
libihanclient.spec.in Normal file
View file

@ -0,0 +1,85 @@
%if %{defined mdkversion}
%define __libtoolize /bin/true
%endif
Name: libihanclient
Group: Productivity/Networking/Other
Summary: Intelegent Home Automation Network Client Library
URL: http://wiki.my-ho.st/confluence/display/HTC/
License: LGPL2+
Version: @VERSION@
Release: 0
BuildRequires: gcc-c++ make boost-devel pkgconfig automake libtool
Source0: libihanclient-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-root
%description
Intelegent Home Automation Network (iHan) Client Library is for application
developers to write iHan applications
%package -n %{name}-devel
Summary: Intelegent Home Automation Network Client Library
Group: Development/Libraries/C and C++
Requires: %{name} = %{version}-%{release}
Requires: boost-devel
#BuildRequires: %{name}
%description -n %{name}-devel
header files needed when you want to compile your own application using
libihanclient.
Intelegent Home Automation Network (iHan) Client Library is for application
developers to write iHan applications
%prep
%setup -q
if [ ! -f configure ]; then
./bootstrap.sh
fi
%build
touch .hush
%configure --disable-static
make %{?_smp_mflags}
%check
%ifnarch %{arm}
make check
%else
GTEST_FILTER=*-*Serialize make check
%endif
%install
make install DESTDIR=${RPM_BUILD_ROOT}
rm %{buildroot}/usr/bin/demo
%files
%defattr(-,root,root,-)
#%dir %{_libdir}
%{_libdir}/libihanclient.so.*
%doc %{_prefix}/share/doc/libihanclient-@VERSION@/
%doc README
%files -n %{name}-devel
%defattr(-,root,root,-)
%dir %{_includedir}/iHanClient/
%dir %{_includedir}/muscle/
#%dir %{_libdir}
#%dir %{_libdir}/pkgconfig/
%{_includedir}/iHanClient/*
%{_includedir}/muscle/*
%{_libdir}/libihanclient.la
%{_libdir}/libihanclient.so
%{_libdir}/pkgconfig/*.pc
%post
/sbin/ldconfig
%postun
/sbin/ldconfig
%changelog

18
libqtihanclient.pri Normal file
View file

@ -0,0 +1,18 @@
INCLUDEPATH += include/
DEPENDPATH += $$PWD
DEFINES += MUSCLE_SINGLE_THREAD_ONLY
SOURCES += $$PWD/src/DeviceModel.cpp \
$$PWD/src/MessageHandler.cpp \
$$PWD/src/QtiHanClient.cpp \
$$PWD/src/modeltest.cpp \
$$PWD/src/kdescendantsproxymodel.cpp
HEADERS += $$PWD/include/QtiHanClient/DeviceModel.h \
$$PWD/include/QtiHanClient/QtiHanClient.h \
$$PWD/include/QtiHanClient/MessageHandler.h \
$$PWD/include/QtiHanClient/modeltest.h \
$$PWD/include/QtiHanClient/kdescendantsproxymodel.h \
$$PWD/include/QtiHanClient/kbihash_p.h \
$$PWD/include/QtiHanClient/kitemmodels_export.h

38
libqtihanclient.pro Normal file
View file

@ -0,0 +1,38 @@
#-------------------------------------------------
#
# Project created by QtCreator 2014-03-15T21:59:46
#
#-------------------------------------------------
QT += core gui widgets
VERSION = 0.1.0
TARGET = qtihanclient
TEMPLATE = lib
CONFIG += lib_bundle c++11 shared link_pkgconfig create_pc create_prl no_install_prl
!isEmpty(IHANCLIENTPATH) {
#message($$IHANCLIENTPATH/include/iHanClient/MsgTypes.hpp)
exists($$IHANCLIENTPATH/include/iHanClient/MsgTypes.hpp) {
message("Using Custom Path to iHanClient")
INCLUDEPATH += $$IHANCLIENTPATH/include/ $$IHANCLIENTPATH/muscle/
LIBS += -L$$IHANCLIENTPATH -lihanclient
CONFIG += staticlib
CONFIG -= create_pc create_prl no_install_prl
}
} else {
PKGCONFIG += libihanclient
}
include (libqtihanclient.pri)
QMAKE_PKGCONFIG_NAME = libQTiHanClient
QMAKE_PKGCONFIG_DESCRIPTION = Qt bindings for the iHanClient Library
QMAKE_PKGCONFIG_PREFIX = $$INSTALLBASE
QMAKE_PKGCONFIG_LIBDIR = $$target.path
QMAKE_PKGCONFIG_INCDIR = $$headers.path
QMAKE_PKGCONFIG_VERSION = $$VERSION

1037
src/DeviceModel.cpp Normal file

File diff suppressed because it is too large Load diff

317
src/MessageHandler.cpp Normal file
View file

@ -0,0 +1,317 @@
/*
* MessageHandler.cpp
*
* Created on: Sep 1, 2009
* Author: fish
*/
#include <QtGui/QScreen>
#include <QtNetwork/QAbstractSocket>
#include "QtiHanClient/MessageHandler.h"
#include "QtiHanClient/DeviceModel.h"
#include "iHanClient/MsgTypes.hpp"
#include "iHanClient/VariableTypes.hpp"
#include "iHanClient/MessageBus.hpp"
MessageHandler::MessageHandler(QObject *parent) : CurState(S_DISCONNECTED), flags(0) {
setParent(parent);
tcpSocket = new QTcpSocket(this);
tcpDataIO = new QTcpDataIO(this->tcpSocket);
QObject::connect(tcpSocket, SIGNAL(readyRead()),
this, SLOT(HandleSockPackets()));
QObject::connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(HandleSockError(QAbstractSocket::SocketError)));
QObject::connect(tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this, SLOT(HandleSockUpdate(QAbstractSocket::SocketState)));
QObject::connect(tcpSocket, SIGNAL(connected()),
this, SLOT(HandleConnected()));
gw.SetDataIO(muscle::DataIORef(this->tcpDataIO));
this->hostid = qrand();
this->port = 1234;
this->type = 0;
}
MessageHandler::~MessageHandler() {
this->gw.Shutdown();
iHanClient::VarTypeHelper::Destroy();
}
bool MessageHandler::connect() {
qDebug() << this->hostname;
if (!this->hostname.length()) {
emit error("Hostname Not Set", QAbstractSocket::UnknownSocketError);
return false;
}
if (this->port == 0) {
emit error("Port Not Set", QAbstractSocket::UnknownSocketError);
return false;
}
if (!this->username.length()) {
emit error("Username Not Set", QAbstractSocket::UnknownSocketError);
return false;
}
if (!this->password.length()) {
emit error("Password Not Set", QAbstractSocket::UnknownSocketError);
return false;
}
if (this->type == 0) {
emit error("Client Type Not Set", QAbstractSocket::UnknownSocketError);
return false;
}
qDebug() << "Trying to connect to " << this->hostname << ":" << this->port;
tcpSocket->connectToHost(this->hostname, this->port);
return true;
}
void MessageHandler::HandleSockPackets()
{
this->tcpDataIO->RealRead();
this->gw.DoInput(*this);
}
void MessageHandler::HandleSockError(QAbstractSocket::SocketError errorcode) {
qDebug() << "SockError " << qPrintable(tcpSocket->errorString());
emit error(tcpSocket->errorString(), errorcode);
emit disconnected();
}
void MessageHandler::HandleSockUpdate(QAbstractSocket::SocketState state) {
if (state == QAbstractSocket::ClosingState) {
qDebug() << "Socket Closed";
emit disconnected();
}
}
void MessageHandler::HandleConnected() {
VarContainerFactory(clncap);
clncap->addStringValue(MSGB_CLNCAP_AUTHUSER, this->username.toStdString());
clncap->addStringValue(MSGB_CLNCAP_AUTHKEY, this->password.toStdString());
clncap->addLongValue(MSGB_CLNCAP_CAPDEVICE, this->type);
clncap->addLongValue(MSGB_CLNCAP_HOSTID, this->hostid);
clncap->addLongLongValue(MSGB_CLNCAP_FLAGS, this->flags);
MessageBusFactory(mb);
if (!mb->createClientCap(clncap, "")) {
qWarning() << "Failed to create ClientCap Message";
return;
}
this->sendMessage(mb);
emit connected();
this->CurState = S_CONNECTED;
emit StateChange(this->CurState);
}
void MessageHandler::MessageReceivedFromGateway(const muscle::MessageRef & msg, void * /*userData*/)
{
VarContainerFactory(rawmsg);
rawmsg->importMuscleMsg(msg);
MessageBusFactory(mb);
if (!mb->importTransportVarStorage(rawmsg)) {
qWarning() << "Failed to importTransportVarStorage Message";
msg()->PrintToStream();
return;
}
switch (mb->getType()) {
case MSB_SERVER_CAP:
/* w00p, we are logged in. Send our CAPS */
processServerCaps(mb);
qDebug() << "Connected!";
this->CurState = S_READY;
emit StateChange(this->CurState);
break;
case MSB_NEW_DEVICE:
processNewEndPt(mb);
break;
case MSB_DEL_DEVICE:
processDelEndPt(mb);
break;
case MSB_REPORT_VAR:
processSensorUpdate(mb);
break;
case MSB_REPORT_CONFIG:
processConfigUpdate(mb);
break;
case MSB_SETUP:
processSetup(mb);
break;
default:
qWarning() << "Got Unknown What Message: " << mb->getTypeAsString().c_str();
//qWarning() << mb;
break;
}
}
void MessageHandler::processNewEndPt(MessageBus msg) {
if (msg->getType() != MSB_NEW_DEVICE) {
qWarning() << "Invalid MessageBus Type recieved in processNewEndPt" << msg->getTypeAsString().c_str();
return;
}
VarStorage newdev = msg->getNewDevice();
if (newdev->getSize() == 0) {
qWarning() << "Empty newDev Client from MessageBus";
return;
}
std::string DeviceSerial;
newdev->getStringValue(SRVCAP_ENDPT_SERIAL, DeviceSerial);
std::string DeviceName;
newdev->getStringValue(SRVCAP_ENDPT_NAME, DeviceName);
qDebug() << "Adding New Endpoint: " << DeviceName.c_str() << " (" << DeviceSerial.c_str() << ")";
emit newEndPt(msg);
}
void MessageHandler::processDelEndPt(MessageBus msg) {
if (msg->getType() != MSB_DEL_DEVICE) {
qWarning() << "Invalid MessageBus Type recieved in processDelEndPt" << msg->getTypeAsString().c_str();
return;
}
VarStorage newdev = msg->getNewDevice();
if (newdev->getSize() == 0) {
qWarning() << "Empty newDev Client from MessageBus";
return;
}
std::string DeviceSerial;
newdev->getStringValue(SRVCAP_ENDPT_SERIAL, DeviceSerial);
std::string DeviceName;
newdev->getStringValue(SRVCAP_ENDPT_NAME, DeviceName);
qDebug() << "Deleting Endpoint: " << DeviceName.c_str() << " (" << DeviceSerial.c_str() << ")";
emit delEndPt(msg);
}
void MessageHandler::processServerCaps(MessageBus msg) {
Q_UNUSED(msg);
// msg()->PrintToStream();
}
void MessageHandler::sendMessage(MessageBus msg) {
VarStorage rawmsg = msg->getTransportVarStorage();
if (rawmsg) {
muscle::MessageRef MMsg = rawmsg->toMuscle();
MMsg()->what = rawmsg->getWhat();
this->gw.AddOutgoingMessage(MMsg);
this->gw.DoOutput();
} else {
qWarning() << "Failed to get TransportVarStorage Message";
return;
}
}
void MessageHandler::processSensorUpdate(MessageBus msg) {
if (msg->getType() != MSB_REPORT_VAR) {
qWarning() << "Invalid MessageBus Type recieved in processSensorUpdate" << msg->getTypeAsString().c_str();
return;
}
VarStorage var = msg->getReportVar();
if (var->getSize() == 0) {
qWarning() << "Empty var message from MessageBus";
std::cout << msg << std::endl;
return;
}
std::cout << msg << std::endl;
emit updateValues(msg);
}
void MessageHandler::processConfigUpdate(MessageBus msg) {
if (msg->getType() != MSB_REPORT_CONFIG) {
qWarning() << "Invalid MessageBus Type recieved in processConfigUpdate" << msg->getTypeAsString().c_str();
return;
}
VarStorage config = msg->getReportConfig();
if (config->getSize() == 0) {
qWarning() << "Empty config message from MessageBus";
std::cout << msg << std::endl;
return;
}
emit updateConfig(msg);
}
void MessageHandler::setType(int Type) {
this->type = Type;
}
int MessageHandler::getType() {
return this->type;
}
void MessageHandler::setUserName(QString name) {
this->username = name;
}
QString MessageHandler::getUserName() {
return this->username;
}
void MessageHandler::setPassword(QString password) {
this->password = password;
}
QString MessageHandler::getPassword() {
return this->password;
}
void MessageHandler::setHostName(QString hostname) {
this->hostname = hostname;
}
QString MessageHandler::getHostName() {
return this->hostname;
}
void MessageHandler::setPort(quint32 port) {
this->port = port;
}
quint32 MessageHandler::getPort() {
return this->port;
}
State_e MessageHandler::getState() {
return this->CurState;
}
void MessageHandler::processSetup(MessageBus msg) {
if (msg->getType() != MSB_SETUP) {
qWarning() << "Invalid MessageBus Type recieved in processSetup" << msg->getTypeAsString().c_str();
return;
}
VarStorage setup = msg->getSetup();
if (setup->getSize() == 0) {
qWarning() << "Empty Setup message from MessageBus";
return;
}
if (setup->getSize(MSGB_SETUP_CLIENTINFORM) > 0) {
qDebug() << "Got Client Inform Message";
emit gotMyInfo(msg);
}
if ((this->flags & CLNTCAP_FLAG_VARTYPE) & setup->getSize("VarTypes")) {
qDebug() << "Got VarTypes Setup Message";
HashVals VarTypes;
setup->getHashValue("VarTypes", VarTypes);
if (VarTypes.size() == 0) {
qWarning() << "VarTypes Setup Message is Empty";
return;
}
iHanClient::VarTypeHelper::Create(VarTypes);
}
if ((this->flags & CLNTCAP_FLAG_TERMS) & setup->getSize("TermTypes")) {
qDebug() << "Got TermTypeMappings Setup Message";
emit gotTermTypeMapping(msg);
}
}
QTcpDataIO::QTcpDataIO(QTcpSocket *thetcpsocket) {
this->tcpsocket = thetcpsocket;
}
int32 QTcpDataIO::Write(const void * buffer, uint32 size) {
return this->tcpsocket->write((const char *)buffer,size);
}
int32 QTcpDataIO::Read(void * buffer, uint32 size) {
int32 actualsize;
if ((int)size > this->indata.size())
actualsize = this->indata.size();
else
actualsize = size;
memcpy(buffer, this->indata.data(), actualsize);
this->indata.remove(0, actualsize);
return actualsize;
}
void QTcpDataIO::RealRead() {
this->indata.append(this->tcpsocket->readAll());
}

720
src/QtiHanClient.cpp Normal file
View file

@ -0,0 +1,720 @@
/* controlpanel - QtiHanClient.cpp
** Copyright (c) 2010 Justin Hammond
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** controlpanel SVN Identification:
** $Rev$
*/
/** @file QtiHanClient.cpp
* @brief
*/
#include "QtiHanClient/QtiHanClient.h"
#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
#include <vector>
#include <sstream>
#include <cstdio>
#include <QTableView>
QMap<std::string, VarStorage> GlobalDevices;
QtiHanClient* QtiHanClient::s_instance = NULL;
QtiHanClient* QtiHanClient::Create(QObject *parent)
{
if( NULL == s_instance )
{
s_instance = new QtiHanClient(parent);
}
return s_instance;
}
void QtiHanClient::Destroy()
{
delete s_instance;
s_instance = NULL;
}
QtiHanClient::QtiHanClient(QObject *parent) {
setParent(parent);
this->mh = new MessageHandler(this);
/* XXX Do this Dynamically when reqested */
this->tdm = new DeviceModel_t(this);
this->fdm = new KDescendantsProxyModel(this);
this->fdm->setSourceModel(this->tdm);
this->mt = new ModelTest(this->tdm, this);
QObject::connect(this->mh, SIGNAL(connected()),
this, SLOT(HandleConnected()));
QObject::connect(this->mh, SIGNAL(disconnected()),
this, SLOT(HandleDisconnected()));
QObject::connect(this->mh, SIGNAL(error(QString, QAbstractSocket::SocketError)),
this, SLOT(HandleError(QString, QAbstractSocket::SocketError)));
QObject::connect(this->mh, SIGNAL(newEndPt(MessageBus)),
this, SLOT(HandleNewDevice(MessageBus)));
QObject::connect(this->mh, SIGNAL(delEndPt(MessageBus)),
this, SLOT(HandleDelDevice(MessageBus)));
QObject::connect(this->mh, SIGNAL(updateValues(MessageBus)),
this, SLOT(HandleDeviceUpdate(MessageBus)));
QObject::connect(this->mh, SIGNAL(updateConfig(MessageBus)),
this, SLOT(HandleDeviceConfigUpdate(MessageBus)));
QObject::connect(this->mh, SIGNAL(StateChange(State_e)),
this, SLOT(HandleStateChange(State_e)));
QObject::connect(this->mh, SIGNAL(gotTermTypeMapping(MessageBus)),
this, SLOT(HandleTermTypeMappings(MessageBus)));
QObject::connect(this->tdm, SIGNAL(sendMsg(MessageBus)),
this->mh, SLOT(sendMessage(MessageBus)));
QObject::connect(this->mh, SIGNAL(gotMyInfo(MessageBus)),
this, SLOT(HandleClientInform(MessageBus)));
this->type = 0;
this->mh->setFlags(CLNTCAP_FLAG_VARTYPE | CLNTCAP_FLAG_TERMS);
QTableView *tv = new QTableView();
tv->setModel(this->fdm);
tv->show();
}
QtiHanClient::~QtiHanClient() {
delete this->mh;
}
void QtiHanClient::setType(int Type) {
this->type = Type;
this->mh->setType(this->type);
}
int QtiHanClient::getType() {
return this->type;
}
void QtiHanClient::setUserName(QString name) {
this->mh->setUserName(name);
}
QString QtiHanClient::getUserName() {
return this->mh->getUserName();
}
void QtiHanClient::setPassword(QString password) {
this->mh->setPassword(password);
}
QString QtiHanClient::getPassword() {
return this->mh->getPassword();
}
void QtiHanClient::setHostName(QString hostname) {
this->mh->setHostName(hostname);
}
QString QtiHanClient::getHostName() {
return this->mh->getHostName();
}
void QtiHanClient::setPort(quint32 port) {
this->mh->setPort(port);
}
quint32 QtiHanClient::getPort() {
return this->mh->getPort();
}
int QtiHanClient::getHostID() {
return this->mh->getHostID();
}
void QtiHanClient::setHostID(int val) {
this->mh->setHostID(val);
}
bool QtiHanClient::connect() {
return this->mh->connect();
}
void QtiHanClient::HandleConnected() {
emit connected();
}
void QtiHanClient::HandleDisconnected() {
emit disconnected();
}
void QtiHanClient::HandleError(QString errstr, QAbstractSocket::SocketError errtype) {
emit error(errstr, errtype);
}
void QtiHanClient::HandleStateChange(State_e state) {
emit StateChange(state);
}
void QtiHanClient::HandleNewDevice(MessageBus msg) {
VarStorage item = msg->getNewDevice();
std::string deviceID;
if (!item->getStringValue(SRVCAP_ENDPT_SERIAL, deviceID)) {
qWarning() << "Can't get End Point Serial in newEndPt";
return;
}
/* Regardless of whats going on, add this new End Point to the Global DeviceMap */
if (GlobalDevices.contains(deviceID)) {
qWarning() << "Device Already Exists in Global DeviceMap in newEndPt";
return;
}
GlobalDevices[deviceID] = item;
/* Create a New DeviceModel based on this device */
this->tdm->addDevice(item);
emit newEndPt(item);
}
void QtiHanClient::HandleDelDevice(MessageBus msg) {
std::string deviceID = msg->getDelDevice();
#if 0
if (!item->getStringValue(SRVCAP_ENDPT_SERIAL, deviceID)) {
qWarning() << "Can't get End Point Serial in delEndPt";
return;
}
#endif
this->tdm->delDevice(deviceID);
/* Regardless of whats going on, del this End Point in the Global DeviceMap */
if (GlobalDevices.contains(deviceID)) {
GlobalDevices.remove(deviceID);
return;
} else {
qWarning() << "Device Does Not Exists in Global DeviceMap";
}
emit delEndPt(deviceID);
}
void QtiHanClient::HandleDeviceUpdate(MessageBus msg) {
VarStorage newvals = msg->getReportVar();
std::string deviceID;
QVector<QString> updatedfields;
VarStorage vals;
deviceID = msg->getSource();
#if 0
if (!item->getStringValue(SRVCAP_ENDPT_SERIAL, deviceID)) {
qWarning() << "Can't get End Point Serial in HandleDeviceUpdate";
return;
}
VarStorage newvals;
if (item->getVarStorageValue(SRVCAP_ENDPT_VARS, newvals) == false) {
qWarning("Can't get End Point Vars from Packet");
std::cout << *item << std::endl;
return;
}
#endif
if (!GlobalDevices.contains(deviceID)) {
qWarning() << "Can't find Device in DeviceMap for HandleDeviceUpdate";
return;
}
qDebug() << "Got Updated Values from Device";
if (GlobalDevices[deviceID]->getVarStorageValue(SRVCAP_ENDPT_VARS, vals) == false) {
qWarning("Can't get End Point Vars from GloablDevices");
return;
}
//cout << "New: " << std::endl;
//newvals->printToStream();
//cout << "Old: " << std::endl;
//vals->printToStream();
std::vector<std::string> *newfields = newvals->getFields();
//std::vector<std::string> *oldfields = vals->getFields();
//qDebug() << "New Fields:";
for (std::size_t i = 0; i < newfields->size(); i++) {
//std::cout << newfields->at(i) << std::endl;
/* confirm exists in oldfields */
if (vals->getSize(newfields->at(i)) <= 0) {
qDebug() << "New Field in Update Message";
/* do something */
}
StoredType_t newtype = newvals->getType(newfields->at(i));
if (newtype != vals->getType(newfields->at(i))) {
/* values do not match type */
cout << newtype << vals->getType(newfields->at(i)) << std::endl;
qWarning() << "Updated Field is not the same type as existing field";
continue;
}
for (unsigned int j = 0; j < newvals->getSize(newfields->at(i)); j++) {
switch (newtype) {
case ST_STRING: {
std::string newval;
std::string oldval;
if (!newvals->getStringValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated String Value";
continue;
}
if (!vals->getStringValue(newfields->at(i), oldval, j)) {
qWarning() << "Couldn't get Old String Value";
continue;
}
if (newval != oldval) {
vals->replaceStringValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
}
break;
case ST_INT: {
int newval;
int oldval;
if (!newvals->getIntValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated Int Value";
continue;
}
if (!vals->getIntValue(newfields->at(i), oldval, j)) {
qWarning() << "Couldn't get Old Int Value";
continue;
}
if (newval != oldval) {
vals->replaceIntValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
}
break;
case ST_LONG: {
long newval;
long oldval;
if (!newvals->getLongValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated Long Value";
continue;
}
if (!vals->getLongValue(newfields->at(i), oldval, j)) {
qWarning() << "Couldn't get Old Long Value";
continue;
}
if (newval != oldval) {
vals->replaceLongValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
}
break;
case ST_LONGLONG:{
long long newval;
long long oldval;
if (!newvals->getLongLongValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated Long Long Value";
continue;
}
if (!vals->getLongLongValue(newfields->at(i), oldval, j)) {
qWarning() << "Couldn't get Old Long Long Value";
continue;
}
if (newval != oldval) {
vals->replaceLongLongValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
}
break;
case ST_FLOAT:{
float newval;
float oldval;
if (!newvals->getFloatValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated Float Value";
continue;
}
if (!vals->getFloatValue(newfields->at(i), oldval, j)) {
qWarning() << "Couldn't get Old Float Value";
continue;
}
if (newval != oldval) {
vals->replaceFloatValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
}
break;
case ST_BOOL:{
bool newval;
bool oldval;
if (!newvals->getBoolValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated Bool Value";
continue;
}
if (!vals->getBoolValue(newfields->at(i), oldval, j)) {
qWarning() << "Couldn't get Old Bool Value";
continue;
}
if (newval != oldval) {
vals->replaceBoolValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
}
break;
case ST_DATETIME:{
boost::posix_time::ptime newval;
boost::posix_time::ptime oldval;
if (!newvals->getTimeValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated Bool Value";
continue;
}
if (!vals->getTimeValue(newfields->at(i), oldval, j)) {
qWarning() << "Couldn't get Old Bool Value";
continue;
}
if (newval != oldval) {
vals->replaceTimeValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
}
break;
case ST_VARSTORAGE: {
/* For now, just replace the entire thing */
VarStorage newval;
if (!newvals->getVarStorageValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated VarStorage Value";
continue;
}
vals->replaceVarStorageValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
break;
case ST_HASH: {
/* For now, just replace the entire thing */
HashVals newval;
if (!newvals->getHashValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated Hash Value";
continue;
}
vals->replaceHashValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
break;
case ST_LIST: {
ListVals value;
if (!newvals->getListValue_p(newfields->at(i), value, j)) {
qWarning() << "Couldn't get Updated List Value";
continue;
}
vals->replaceListValue_p(newfields->at(i), value, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
break;
case ST_INVALID: {
qWarning() << "Recieved a Field with Type 'Invalid'";
continue;
}
break;
};
}
}
qDebug() << "Updated Fields:";
for (int i = 0; i < updatedfields.size(); i++) {
std::cout << qPrintable(updatedfields.at(i)) << std::endl;
}
//newvals->addStringValue(SRVCAP_ENDPT_SERIAL, deviceID);
//GlobalDevices[deviceID]->replaceVarStorageValue(SRVCAP_ENDPT_VARS, newvals);
this->tdm->updateDevice(newvals);
emit updateValues(deviceID.c_str(), updatedfields);
}
void QtiHanClient::HandleDeviceConfigUpdate(MessageBus msg) {
VarStorage newvals = msg->getReportConfig();
std::string deviceID = msg->getSource();
QVector<QString> updatedfields;
VarStorage vals;
if (!GlobalDevices.contains(deviceID)) {
qWarning() << "Can't find Device in DeviceMap for HandleDeviceConfigUpdate";
return;
}
qDebug() << "Got Updated Config from Device";
#if 0
if (!item->getStringValue(SRVCAP_ENDPT_SERIAL, deviceID)) {
qWarning() << "Can't get End Point Serial in HandleDeviceConfigUpdate";
return;
}
VarStorage newvals;
if (item->getVarStorageValue(SRVCAP_ENDPT_CONFIG, newvals) == false) {
qWarning("Can't get End Point Config from Packet");
return;
}
#endif
if (GlobalDevices[deviceID]->getVarStorageValue(SRVCAP_ENDPT_CONFIG, vals) == false) {
qWarning("Can't get End Point Config from GloablDevices");
return;
}
//cout << "New: " << std::endl;
//newvals->printToStream();
//cout << "Old: " << std::endl;
//vals->printToStream();
std::vector<std::string> *newfields = newvals->getFields();
//std::vector<std::string> *oldfields = vals->getFields();
//qDebug() << "New Fields:";
for (std::size_t i = 0; i < newfields->size(); i++) {
//std::cout << newfields->at(i) << std::endl;
/* confirm exists in oldfields */
if (vals->getSize(newfields->at(i)) <= 0) {
qDebug() << "New Field in HandleDeviceConfigUpdate";
/* do something */
}
StoredType_t newtype = newvals->getType(newfields->at(i));
if (newtype != vals->getType(newfields->at(i))) {
/* values do not match type */
//cout << newtype << vals->getType(newfields->at(i)) << std::endl;
qWarning() << "Updated Field is not the same type as existing field";
continue;
}
for (unsigned int j = 0 ; j < newvals->getSize(newfields->at(i)); j++ ) {
switch (newtype) {
case ST_STRING: {
std::string newval;
std::string oldval;
if (!newvals->getStringValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated String Value";
continue;
}
if (!vals->getStringValue(newfields->at(i), oldval, j)) {
qWarning() << "Couldn't get Old String Value";
continue;
}
if (newval != oldval) {
vals->replaceStringValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
}
break;
case ST_INT: {
int newval;
int oldval;
if (!newvals->getIntValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated Int Value";
continue;
}
if (!vals->getIntValue(newfields->at(i), oldval, j)) {
qWarning() << "Couldn't get Old Int Value";
continue;
}
if (newval != oldval) {
vals->replaceIntValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
}
break;
case ST_LONG: {
long newval;
long oldval;
if (!newvals->getLongValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated Long Value";
continue;
}
if (!vals->getLongValue(newfields->at(i), oldval, j)) {
qWarning() << "Couldn't get Old Long Value";
continue;
}
if (newval != oldval) {
vals->replaceLongValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
}
break;
case ST_LONGLONG:{
long long newval;
long long oldval;
if (!newvals->getLongLongValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated Long Long Value";
continue;
}
if (!vals->getLongLongValue(newfields->at(i), oldval, j)) {
qWarning() << "Couldn't get Old Long Long Value";
continue;
}
if (newval != oldval) {
vals->replaceLongLongValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
}
break;
case ST_FLOAT:{
float newval;
float oldval;
if (!newvals->getFloatValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated Float Value";
continue;
}
if (!vals->getFloatValue(newfields->at(i), oldval, j)) {
qWarning() << "Couldn't get Old Float Value";
continue;
}
if (newval != oldval) {
vals->replaceFloatValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
}
break;
case ST_BOOL:{
bool newval;
bool oldval;
if (!newvals->getBoolValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated Bool Value";
continue;
}
if (!vals->getBoolValue(newfields->at(i), oldval, j)) {
qWarning() << "Couldn't get Old Bool Value";
continue;
}
if (newval != oldval) {
vals->replaceBoolValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
}
break;
case ST_DATETIME:{
boost::posix_time::ptime newval;
boost::posix_time::ptime oldval;
if (!newvals->getTimeValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated Bool Value";
continue;
}
if (!vals->getTimeValue(newfields->at(i), oldval, j)) {
qWarning() << "Couldn't get Old Bool Value";
continue;
}
if (newval != oldval) {
vals->replaceTimeValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
}
break;
case ST_VARSTORAGE: {
/* For now, just replace the entire thing */
VarStorage newval;
if (!newvals->getVarStorageValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated VarStorage Value";
continue;
}
vals->replaceVarStorageValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
break;
case ST_HASH: {
/* For now, just replace the entire thing */
HashVals newval;
if (!newvals->getHashValue(newfields->at(i), newval, j)) {
qWarning() << "Couldn't get updated Hash Value";
continue;
}
vals->replaceHashValue(newfields->at(i), newval, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
break;
case ST_LIST: {
ListVals value;
if (!newvals->getListValue_p(newfields->at(i), value, j)) {
qWarning() << "Couldn't get Updated List Value";
continue;
}
vals->replaceListValue_p(newfields->at(i), value, j);
if (!updatedfields.contains(newfields->at(i).c_str()))
updatedfields.push_back(newfields->at(i).c_str());
}
break;
case ST_INVALID: {
qWarning() << "Recieved a Field with Type 'Invalid'";
continue;
}
break;
};
}
}
qDebug() << "Updated Config Fields:";
for (int i = 0; i < updatedfields.size(); i++) {
std::cout << qPrintable(updatedfields.at(i)) << std::endl;
}
//newvals->addStringValue(SRVCAP_ENDPT_SERIAL, deviceID);
//GlobalDevices[deviceID]->replaceVarStorageValue(SRVCAP_ENDPT_VARS, newvals);
//this->tdm->updateDevice(item);
this->tdm->updateDeviceConfig(newvals);
emit updateConfig(deviceID.c_str(), updatedfields);
}
void QtiHanClient::sendMessage(MessageBus msg) {
this->mh->sendMessage(msg);
}
void QtiHanClient::HandleTermTypeMappings(MessageBus msg) {
VarStorage val1 = msg->getSetup();
VarStorage vals;
if (!val1->getVarStorageValue("TermTypes", vals)) {
qWarning() << "Can't get TermTypes from Setup Message";
return;
}
/* Convert to QMap */
/* VarType TermName VarID VarValue */
/* QMap< QString, QMap < QString, QMap < QString, QVariant > > > Term_Map_t; */
std::vector<std::string> *VarTypes = vals->getFields();
for (std::vector<std::string>::iterator iter = VarTypes->begin(); iter != VarTypes->end(); ++iter) {
/* (*iter) = VarType */
QString VarType((*iter).c_str());
VarContainerFactory(Vars);
if (!vals->getVarStorageValue((*iter), Vars)) {
qWarning() << "Could not Get TermName Variable: " << (*iter).c_str();
//qWarning() << vals;
continue;
}
QMap<QString, QMap < QString, QVariant > > QTermVals;
std::vector<std::string> *TermNames = Vars->getFields();
for (std::vector<std::string>::iterator TermIter = TermNames->begin(); TermIter != TermNames->end(); TermIter++) {
HashVals VarValues;
//std::cout << (*TermIter) << std::endl;
if (!Vars->getHashValue((*TermIter), VarValues)) {
qWarning() << "Could not get Term Values for Term " << (*TermIter).c_str();
//qWarning() << Vars;
continue;
}
QMap<QString, QVariant> QTermVars;
std::map<std::string, HashValsVariant_t>::const_iterator hvit;
for (hvit = VarValues.begin(); hvit != VarValues.end(); hvit++) {
QTermVars.insert((*hvit).first.c_str(), QVariant(boost::get<std::string>((*hvit).second).c_str()));
cout << "\t\t" << (*hvit).first.c_str() << std::endl;
}
QTermVals.insert((*TermIter).c_str(), QTermVars);
}
this->TermTypeMappings.insert((*iter).c_str(), QTermVals);
}
}
void QtiHanClient::HandleClientInform(MessageBus msg) {
VarStorage clninfo;
msg->getSetup()->getVarStorageValue(MSGB_SETUP_CLIENTINFORM, clninfo);
if (clninfo->getSize() == 0) {
qWarning() << "ClientInfo Structure is empty";
exit(-1);
}
this->myinfo = clninfo;
}
QString QtiHanClient::getMyDeviceID() {
if (this->myinfo->getSize(SRVCAP_ENDPT_SERIAL) == 0) {
qDebug() << "Missing Serial Numer";
return QString();
}
std::string id;
this->myinfo->getStringValue(SRVCAP_ENDPT_SERIAL, id);
return QString(id.c_str());
}

View file

@ -0,0 +1,997 @@
/*
Copyright (c) 2009 Stephen Kelly <steveire@gmail.com>
Copyright (C) 2010 Klarälvdalens Datakonsult AB,
a KDAB Group company, info@kdab.net,
author Stephen Kelly <stephen@kdab.com>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "QtiHanClient/kdescendantsproxymodel.h"
#include <QtCore/QStringList>
#include <QtCore/QTimer>
#include <QtCore/QDebug>
#define KDO(object) qDebug() << #object << object
#include "QtiHanClient/kbihash_p.h"
typedef KHash2Map<QPersistentModelIndex, int> Mapping;
class KDescendantsProxyModelPrivate
{
KDescendantsProxyModelPrivate(KDescendantsProxyModel *qq)
: q_ptr(qq),
m_rowCount(0),
m_ignoreNextLayoutAboutToBeChanged(false),
m_ignoreNextLayoutChanged(false),
m_relayouting(false),
m_displayAncestorData(false),
m_ancestorSeparator(QLatin1String(" / "))
{
}
Q_DECLARE_PUBLIC(KDescendantsProxyModel)
KDescendantsProxyModel *const q_ptr;
mutable QVector<QPersistentModelIndex> m_pendingParents;
void scheduleProcessPendingParents() const;
void processPendingParents();
void synchronousMappingRefresh();
void updateInternalIndexes(int start, int offset);
void resetInternalData();
void sourceRowsAboutToBeInserted(const QModelIndex &, int, int);
void sourceRowsInserted(const QModelIndex &, int, int);
void sourceRowsAboutToBeRemoved(const QModelIndex &, int, int);
void sourceRowsRemoved(const QModelIndex &, int, int);
void sourceRowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int);
void sourceRowsMoved(const QModelIndex &, int, int, const QModelIndex &, int);
void sourceModelAboutToBeReset();
void sourceModelReset();
void sourceLayoutAboutToBeChanged();
void sourceLayoutChanged();
void sourceDataChanged(const QModelIndex &, const QModelIndex &);
void sourceModelDestroyed();
Mapping m_mapping;
int m_rowCount;
QPair<int, int> m_removePair;
QPair<int, int> m_insertPair;
bool m_ignoreNextLayoutAboutToBeChanged;
bool m_ignoreNextLayoutChanged;
bool m_relayouting;
bool m_displayAncestorData;
QString m_ancestorSeparator;
QList<QPersistentModelIndex> m_layoutChangePersistentIndexes;
QModelIndexList m_proxyIndexes;
};
void KDescendantsProxyModelPrivate::resetInternalData()
{
m_rowCount = 0;
m_mapping.clear();
m_layoutChangePersistentIndexes.clear();
m_proxyIndexes.clear();
}
void KDescendantsProxyModelPrivate::synchronousMappingRefresh()
{
m_rowCount = 0;
m_mapping.clear();
m_pendingParents.clear();
m_pendingParents.append(QModelIndex());
m_relayouting = true;
while (!m_pendingParents.isEmpty()) {
processPendingParents();
}
m_relayouting = false;
}
void KDescendantsProxyModelPrivate::scheduleProcessPendingParents() const
{
const_cast<KDescendantsProxyModelPrivate *>(this)->processPendingParents();
}
void KDescendantsProxyModelPrivate::processPendingParents()
{
Q_Q(KDescendantsProxyModel);
const QVector<QPersistentModelIndex>::iterator begin = m_pendingParents.begin();
QVector<QPersistentModelIndex>::iterator it = begin;
const QVector<QPersistentModelIndex>::iterator end = m_pendingParents.end();
QVector<QPersistentModelIndex> newPendingParents;
while (it != end && it != m_pendingParents.end()) {
const QModelIndex sourceParent = *it;
if (!sourceParent.isValid() && m_rowCount > 0) {
// It was removed from the source model before it was inserted.
it = m_pendingParents.erase(it);
continue;
}
const int rowCount = q->sourceModel()->rowCount(sourceParent);
Q_ASSERT(rowCount > 0);
const QPersistentModelIndex sourceIndex = q->sourceModel()->index(rowCount - 1, 0, sourceParent);
Q_ASSERT(sourceIndex.isValid());
const QModelIndex proxyParent = q->mapFromSource(sourceParent);
Q_ASSERT(sourceParent.isValid() == proxyParent.isValid());
const int proxyEndRow = proxyParent.row() + rowCount;
const int proxyStartRow = proxyEndRow - rowCount + 1;
if (!m_relayouting) {
q->beginInsertRows(QModelIndex(), proxyStartRow, proxyEndRow);
}
updateInternalIndexes(proxyStartRow, rowCount);
m_mapping.insert(sourceIndex, proxyEndRow);
it = m_pendingParents.erase(it);
m_rowCount += rowCount;
if (!m_relayouting) {
q->endInsertRows();
}
for (int sourceRow = 0; sourceRow < rowCount; ++sourceRow) {
static const int column = 0;
const QModelIndex child = q->sourceModel()->index(sourceRow, column, sourceParent);
Q_ASSERT(child.isValid());
if (q->sourceModel()->hasChildren(child)) {
Q_ASSERT(q->sourceModel()->rowCount(child) > 0);
newPendingParents.append(child);
}
}
}
m_pendingParents += newPendingParents;
if (!m_pendingParents.isEmpty()) {
processPendingParents();
}
// scheduleProcessPendingParents();
}
void KDescendantsProxyModelPrivate::updateInternalIndexes(int start, int offset)
{
// TODO: Make KHash2Map support key updates and do this backwards.
QHash<int, QPersistentModelIndex> updates;
{
Mapping::right_iterator it = m_mapping.rightLowerBound(start);
const Mapping::right_iterator end = m_mapping.rightEnd();
while (it != end) {
updates.insert(it.key() + offset, *it);
++it;
}
}
{
QHash<int, QPersistentModelIndex>::const_iterator it = updates.constBegin();
const QHash<int, QPersistentModelIndex>::const_iterator end = updates.constEnd();
for (; it != end; ++it) {
m_mapping.insert(it.value(), it.key());
}
}
}
KDescendantsProxyModel::KDescendantsProxyModel(QObject *parent)
: QAbstractProxyModel(parent), d_ptr(new KDescendantsProxyModelPrivate(this))
{
}
KDescendantsProxyModel::~KDescendantsProxyModel()
{
delete d_ptr;
}
void KDescendantsProxyModel::setRootIndex(const QModelIndex &index)
{
Q_UNUSED(index)
}
QModelIndexList KDescendantsProxyModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
{
return QAbstractProxyModel::match(start, role, value, hits, flags);
}
void KDescendantsProxyModel::setDisplayAncestorData(bool display)
{
Q_D(KDescendantsProxyModel);
d->m_displayAncestorData = display;
}
bool KDescendantsProxyModel::displayAncestorData() const
{
Q_D(const KDescendantsProxyModel);
return d->m_displayAncestorData;
}
void KDescendantsProxyModel::setAncestorSeparator(const QString &separator)
{
Q_D(KDescendantsProxyModel);
d->m_ancestorSeparator = separator;
}
QString KDescendantsProxyModel::ancestorSeparator() const
{
Q_D(const KDescendantsProxyModel);
return d->m_ancestorSeparator;
}
void KDescendantsProxyModel::setSourceModel(QAbstractItemModel *_sourceModel)
{
beginResetModel();
if (_sourceModel) {
disconnect(_sourceModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(sourceRowsAboutToBeInserted(QModelIndex,int,int)));
disconnect(_sourceModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(sourceRowsInserted(QModelIndex,int,int)));
disconnect(_sourceModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
disconnect(_sourceModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(sourceRowsRemoved(QModelIndex,int,int)));
// disconnect(_sourceModel, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
// this, SLOT(sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
// disconnect(_sourceModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
// this, SLOT(sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)));
disconnect(_sourceModel, SIGNAL(modelAboutToBeReset()),
this, SLOT(sourceModelAboutToBeReset()));
disconnect(_sourceModel, SIGNAL(modelReset()),
this, SLOT(sourceModelReset()));
disconnect(_sourceModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(sourceDataChanged(QModelIndex,QModelIndex)));
disconnect(_sourceModel, SIGNAL(layoutAboutToBeChanged()),
this, SLOT(sourceLayoutAboutToBeChanged()));
disconnect(_sourceModel, SIGNAL(layoutChanged()),
this, SLOT(sourceLayoutChanged()));
disconnect(_sourceModel, SIGNAL(destroyed()),
this, SLOT(sourceModelDestroyed()));
}
QAbstractProxyModel::setSourceModel(_sourceModel);
if (_sourceModel) {
connect(_sourceModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
SLOT(sourceRowsAboutToBeInserted(QModelIndex,int,int)));
connect(_sourceModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
SLOT(sourceRowsInserted(QModelIndex,int,int)));
connect(_sourceModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
SLOT(sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
connect(_sourceModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
SLOT(sourceRowsRemoved(QModelIndex,int,int)));
// connect(_sourceModel, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
// SLOT(sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
// connect(_sourceModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
// SLOT(sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)));
connect(_sourceModel, SIGNAL(modelAboutToBeReset()),
SLOT(sourceModelAboutToBeReset()));
connect(_sourceModel, SIGNAL(modelReset()),
SLOT(sourceModelReset()));
connect(_sourceModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
SLOT(sourceDataChanged(QModelIndex,QModelIndex)));
connect(_sourceModel, SIGNAL(layoutAboutToBeChanged()),
SLOT(sourceLayoutAboutToBeChanged()));
connect(_sourceModel, SIGNAL(layoutChanged()),
SLOT(sourceLayoutChanged()));
connect(_sourceModel, SIGNAL(destroyed()),
SLOT(sourceModelDestroyed()));
}
endResetModel();
}
QModelIndex KDescendantsProxyModel::parent(const QModelIndex &index) const
{
Q_UNUSED(index)
return QModelIndex();
}
bool KDescendantsProxyModel::hasChildren(const QModelIndex &parent) const
{
Q_D(const KDescendantsProxyModel);
return !(d->m_mapping.isEmpty() || parent.isValid());
}
int KDescendantsProxyModel::rowCount(const QModelIndex &parent) const
{
Q_D(const KDescendantsProxyModel);
if (d->m_pendingParents.contains(parent) || parent.isValid() || !sourceModel()) {
return 0;
}
if (d->m_mapping.isEmpty() && sourceModel()->hasChildren()) {
Q_ASSERT(sourceModel()->rowCount() > 0);
const_cast<KDescendantsProxyModelPrivate *>(d)->synchronousMappingRefresh();
}
return d->m_rowCount;
}
QModelIndex KDescendantsProxyModel::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid()) {
return QModelIndex();
}
if (!hasIndex(row, column, parent)) {
return QModelIndex();
}
return createIndex(row, column);
}
QModelIndex KDescendantsProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
Q_D(const KDescendantsProxyModel);
if (d->m_mapping.isEmpty() || !proxyIndex.isValid() || !sourceModel()) {
return QModelIndex();
}
const Mapping::right_const_iterator result = d->m_mapping.rightLowerBound(proxyIndex.row());
Q_ASSERT(result != d->m_mapping.rightEnd());
const int proxyLastRow = result.key();
const QModelIndex sourceLastChild = result.value();
Q_ASSERT(sourceLastChild.isValid());
// proxyLastRow is greater than proxyIndex.row().
// sourceLastChild is vertically below the result we're looking for
// and not necessarily in the correct parent.
// We travel up through its parent hierarchy until we are in the
// right parent, then return the correct sibling.
// Source: Proxy: Row
// - A - A - 0
// - B - B - 1
// - C - C - 2
// - D - D - 3
// - - E - E - 4
// - - F - F - 5
// - - G - G - 6
// - - H - H - 7
// - - I - I - 8
// - - - J - J - 9
// - - - K - K - 10
// - - - L - L - 11
// - - M - M - 12
// - - N - N - 13
// - O - O - 14
// Note that L, N and O are lastChildIndexes, and therefore have a mapping. If we
// are trying to map G from the proxy to the source, We at this point have an iterator
// pointing to (L -> 11). The proxy row of G is 6. (proxyIndex.row() == 6). We seek the
// sourceIndex which is vertically above L by the distance proxyLastRow - proxyIndex.row().
// In this case the verticalDistance is 5.
int verticalDistance = proxyLastRow - proxyIndex.row();
// We traverse the ancestors of L, until we can index the desired row in the source.
QModelIndex ancestor = sourceLastChild;
while (ancestor.isValid()) {
const int ancestorRow = ancestor.row();
if (verticalDistance <= ancestorRow) {
return ancestor.sibling(ancestorRow - verticalDistance, proxyIndex.column());
}
verticalDistance -= (ancestorRow + 1);
ancestor = ancestor.parent();
}
Q_ASSERT(!"Didn't find target row.");
return QModelIndex();
}
QModelIndex KDescendantsProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
Q_D(const KDescendantsProxyModel);
if (!sourceModel()) {
return QModelIndex();
}
if (d->m_mapping.isEmpty()) {
return QModelIndex();
}
{
// TODO: Consider a parent Mapping to speed this up.
Mapping::right_const_iterator it = d->m_mapping.rightConstBegin();
const Mapping::right_const_iterator end = d->m_mapping.rightConstEnd();
const QModelIndex sourceParent = sourceIndex.parent();
Mapping::right_const_iterator result = end;
for (; it != end; ++it) {
QModelIndex index = it.value();
bool found_block = false;
while (index.isValid()) {
const QModelIndex ancestor = index.parent();
if (ancestor == sourceParent && index.row() >= sourceIndex.row()) {
found_block = true;
if (result == end || it.key() < result.key()) {
result = it;
break; // Leave the while loop. index is still valid.
}
}
index = ancestor;
}
if (found_block && !index.isValid())
// Looked through the ascendants of it.key() without finding sourceParent.
// That means we've already got the result we need.
{
break;
}
}
Q_ASSERT(result != end);
const QModelIndex sourceLastChild = result.value();
int proxyRow = result.key();
QModelIndex index = sourceLastChild;
while (index.isValid()) {
const QModelIndex ancestor = index.parent();
if (ancestor == sourceParent) {
return createIndex(proxyRow - (index.row() - sourceIndex.row()), sourceIndex.column());
}
proxyRow -= (index.row() + 1);
index = ancestor;
}
Q_ASSERT(!"Didn't find valid proxy mapping.");
return QModelIndex();
}
}
int KDescendantsProxyModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid() /* || rowCount(parent) == 0 */ || !sourceModel()) {
return 0;
}
return sourceModel()->columnCount();
}
QVariant KDescendantsProxyModel::data(const QModelIndex &index, int role) const
{
Q_D(const KDescendantsProxyModel);
if (!sourceModel()) {
return QVariant();
}
if (!index.isValid()) {
return sourceModel()->data(index, role);
}
QModelIndex sourceIndex = mapToSource(index);
if ((d->m_displayAncestorData) && (role == Qt::DisplayRole)) {
if (!sourceIndex.isValid()) {
return QVariant();
}
QString displayData = sourceIndex.data().toString();
sourceIndex = sourceIndex.parent();
while (sourceIndex.isValid()) {
displayData.prepend(d->m_ancestorSeparator);
displayData.prepend(sourceIndex.data().toString());
sourceIndex = sourceIndex.parent();
}
return displayData;
} else {
return sourceIndex.data(role);
}
}
QVariant KDescendantsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (!sourceModel() || columnCount() <= section) {
return QVariant();
}
return QAbstractProxyModel::headerData(section, orientation, role);
}
Qt::ItemFlags KDescendantsProxyModel::flags(const QModelIndex &index) const
{
if (!index.isValid() || !sourceModel()) {
return QAbstractProxyModel::flags(index);
}
const QModelIndex srcIndex = mapToSource(index);
Q_ASSERT(srcIndex.isValid());
return sourceModel()->flags(srcIndex);
}
void KDescendantsProxyModelPrivate::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
{
Q_Q(KDescendantsProxyModel);
if (!q->sourceModel()->hasChildren(parent)) {
Q_ASSERT(q->sourceModel()->rowCount(parent) == 0);
// parent was not a parent before.
return;
}
int proxyStart = -1;
const int rowCount = q->sourceModel()->rowCount(parent);
if (rowCount > start) {
const QModelIndex belowStart = q->sourceModel()->index(start, 0, parent);
proxyStart = q->mapFromSource(belowStart).row();
} else if (rowCount == 0) {
proxyStart = q->mapFromSource(parent).row() + 1;
} else {
Q_ASSERT(rowCount == start);
static const int column = 0;
QModelIndex idx = q->sourceModel()->index(rowCount - 1, column, parent);
while (q->sourceModel()->hasChildren(idx)) {
Q_ASSERT(q->sourceModel()->rowCount(idx) > 0);
idx = q->sourceModel()->index(q->sourceModel()->rowCount(idx) - 1, column, idx);
}
// The last item in the list is getting a sibling below it.
proxyStart = q->mapFromSource(idx).row() + 1;
}
const int proxyEnd = proxyStart + (end - start);
m_insertPair = qMakePair(proxyStart, proxyEnd);
q->beginInsertRows(QModelIndex(), proxyStart, proxyEnd);
}
void KDescendantsProxyModelPrivate::sourceRowsInserted(const QModelIndex &parent, int start, int end)
{
Q_Q(KDescendantsProxyModel);
Q_ASSERT(q->sourceModel()->index(start, 0, parent).isValid());
const int rowCount = q->sourceModel()->rowCount(parent);
Q_ASSERT(rowCount > 0);
const int difference = end - start + 1;
if (rowCount == difference) {
// @p parent was not a parent before.
m_pendingParents.append(parent);
scheduleProcessPendingParents();
return;
}
const int proxyStart = m_insertPair.first;
Q_ASSERT(proxyStart >= 0);
updateInternalIndexes(proxyStart, difference);
if (rowCount - 1 == end) {
// The previously last row (the mapped one) is no longer the last.
// For example,
// - A - A 0
// - - B - B 1
// - - C - C 2
// - - - D - D 3
// - - - E -> - E 4
// - - F - F 5
// - - G -> - G 6
// - H - H 7
// - I -> - I 8
// As last children, E, F and G have mappings.
// Consider that 'J' is appended to the children of 'C', below 'E'.
// - A - A 0
// - - B - B 1
// - - C - C 2
// - - - D - D 3
// - - - E -> - E 4
// - - - J - ??? 5
// - - F - F 6
// - - G -> - G 7
// - H - H 8
// - I -> - I 9
// The updateInternalIndexes call above will have updated the F and G mappings correctly because proxyStart is 5.
// That means that E -> 4 was not affected by the updateInternalIndexes call.
// Now the mapping for E -> 4 needs to be updated so that it's a mapping for J -> 5.
Q_ASSERT(!m_mapping.isEmpty());
static const int column = 0;
const QModelIndex oldIndex = q->sourceModel()->index(rowCount - 1 - difference, column, parent);
Q_ASSERT(m_mapping.leftContains(oldIndex));
const QModelIndex newIndex = q->sourceModel()->index(rowCount - 1, column, parent);
QModelIndex indexAbove = oldIndex;
if (start > 0) {
// If we have something like this:
//
// - A
// - - B
// - - C
//
// and we then insert D as a sibling of A below it, we need to remove the mapping for A,
// and the row number used for D must take into account the descendants of A.
while (q->sourceModel()->hasChildren(indexAbove)) {
Q_ASSERT(q->sourceModel()->rowCount(indexAbove) > 0);
indexAbove = q->sourceModel()->index(q->sourceModel()->rowCount(indexAbove) - 1, column, indexAbove);
}
Q_ASSERT(q->sourceModel()->rowCount(indexAbove) == 0);
}
Q_ASSERT(m_mapping.leftContains(indexAbove));
const int newProxyRow = m_mapping.leftToRight(indexAbove) + difference;
// oldIndex is E in the source. proxyRow is 4.
m_mapping.removeLeft(oldIndex);
// newIndex is J. (proxyRow + difference) is 5.
m_mapping.insert(newIndex, newProxyRow);
}
for (int row = start; row <= end; ++row) {
static const int column = 0;
const QModelIndex idx = q->sourceModel()->index(row, column, parent);
Q_ASSERT(idx.isValid());
if (q->sourceModel()->hasChildren(idx)) {
Q_ASSERT(q->sourceModel()->rowCount(idx) > 0);
m_pendingParents.append(idx);
}
}
m_rowCount += difference;
q->endInsertRows();
scheduleProcessPendingParents();
}
void KDescendantsProxyModelPrivate::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
{
Q_Q(KDescendantsProxyModel);
const int proxyStart = q->mapFromSource(q->sourceModel()->index(start, 0, parent)).row();
static const int column = 0;
QModelIndex idx = q->sourceModel()->index(end, column, parent);
while (q->sourceModel()->hasChildren(idx)) {
Q_ASSERT(q->sourceModel()->rowCount(idx) > 0);
idx = q->sourceModel()->index(q->sourceModel()->rowCount(idx) - 1, column, idx);
}
const int proxyEnd = q->mapFromSource(idx).row();
m_removePair = qMakePair(proxyStart, proxyEnd);
q->beginRemoveRows(QModelIndex(), proxyStart, proxyEnd);
}
static QModelIndex getFirstDeepest(QAbstractItemModel *model, const QModelIndex &parent, int *count)
{
static const int column = 0;
Q_ASSERT(model->hasChildren(parent));
Q_ASSERT(model->rowCount(parent) > 0);
for (int row = 0; row < model->rowCount(parent); ++row) {
(*count)++;
const QModelIndex child = model->index(row, column, parent);
Q_ASSERT(child.isValid());
if (model->hasChildren(child)) {
return getFirstDeepest(model, child, count);
}
}
return model->index(model->rowCount(parent) - 1, column, parent);
}
void KDescendantsProxyModelPrivate::sourceRowsRemoved(const QModelIndex &parent, int start, int end)
{
Q_Q(KDescendantsProxyModel);
Q_UNUSED(end)
const int rowCount = q->sourceModel()->rowCount(parent);
const int proxyStart = m_removePair.first;
const int proxyEnd = m_removePair.second;
const int difference = proxyEnd - proxyStart + 1;
{
Mapping::right_iterator it = m_mapping.rightLowerBound(proxyStart);
const Mapping::right_iterator endIt = m_mapping.rightUpperBound(proxyEnd);
if (endIt != m_mapping.rightEnd())
while (it != endIt) {
it = m_mapping.eraseRight(it);
}
else
while (it != m_mapping.rightUpperBound(proxyEnd)) {
it = m_mapping.eraseRight(it);
}
}
m_removePair = qMakePair(-1, -1);
m_rowCount -= difference;
Q_ASSERT(m_rowCount >= 0);
updateInternalIndexes(proxyStart, -1 * difference);
if (rowCount != start || rowCount == 0) {
q->endRemoveRows();
return;
}
static const int column = 0;
const QModelIndex newEnd = q->sourceModel()->index(rowCount - 1, column, parent);
Q_ASSERT(newEnd.isValid());
if (m_mapping.isEmpty()) {
m_mapping.insert(newEnd, newEnd.row());
q->endRemoveRows();
return;
}
if (q->sourceModel()->hasChildren(newEnd)) {
int count = 0;
const QModelIndex firstDeepest = getFirstDeepest(q->sourceModel(), newEnd, &count);
Q_ASSERT(firstDeepest.isValid());
const int firstDeepestProxy = m_mapping.leftToRight(firstDeepest);
m_mapping.insert(newEnd, firstDeepestProxy - count);
q->endRemoveRows();
return;
}
Mapping::right_iterator lowerBound = m_mapping.rightLowerBound(proxyStart);
if (lowerBound == m_mapping.rightEnd()) {
int proxyRow = (lowerBound - 1).key();
for (int row = newEnd.row(); row >= 0; --row) {
const QModelIndex newEndSibling = q->sourceModel()->index(row, column, parent);
if (!q->sourceModel()->hasChildren(newEndSibling)) {
++proxyRow;
} else {
break;
}
}
m_mapping.insert(newEnd, proxyRow);
q->endRemoveRows();
return;
} else if (lowerBound == m_mapping.rightBegin()) {
int proxyRow = rowCount - 1;
QModelIndex trackedParent = parent;
while (trackedParent.isValid()) {
proxyRow += (trackedParent.row() + 1);
trackedParent = trackedParent.parent();
}
m_mapping.insert(newEnd, proxyRow);
q->endRemoveRows();
return;
}
const Mapping::right_iterator boundAbove = lowerBound - 1;
QVector<QModelIndex> targetParents;
targetParents.push_back(parent);
{
QModelIndex target = parent;
int count = 0;
while (target.isValid()) {
if (target == boundAbove.value()) {
m_mapping.insert(newEnd, count + boundAbove.key() + newEnd.row() + 1);
q->endRemoveRows();
return;
}
count += (target.row() + 1);
target = target.parent();
if (target.isValid()) {
targetParents.push_back(target);
}
}
}
QModelIndex boundParent = boundAbove.value().parent();
QModelIndex prevParent = boundParent;
Q_ASSERT(boundParent.isValid());
while (boundParent.isValid()) {
prevParent = boundParent;
boundParent = boundParent.parent();
if (targetParents.contains(prevParent)) {
break;
}
if (!m_mapping.leftContains(prevParent)) {
break;
}
if (m_mapping.leftToRight(prevParent) > boundAbove.key()) {
break;
}
}
QModelIndex trackedParent = parent;
int proxyRow = boundAbove.key();
Q_ASSERT(prevParent.isValid());
proxyRow -= prevParent.row();
while (trackedParent != boundParent) {
proxyRow += (trackedParent.row() + 1);
trackedParent = trackedParent.parent();
}
m_mapping.insert(newEnd, proxyRow + newEnd.row());
q->endRemoveRows();
}
void KDescendantsProxyModelPrivate::sourceRowsAboutToBeMoved(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destStart)
{
Q_UNUSED(srcParent)
Q_UNUSED(srcStart)
Q_UNUSED(srcEnd)
Q_UNUSED(destParent)
Q_UNUSED(destStart)
Q_Q(KDescendantsProxyModel);
q->beginResetModel();
}
void KDescendantsProxyModelPrivate::sourceRowsMoved(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destStart)
{
Q_UNUSED(srcParent)
Q_UNUSED(srcStart)
Q_UNUSED(srcEnd)
Q_UNUSED(destParent)
Q_UNUSED(destStart)
Q_Q(KDescendantsProxyModel);
resetInternalData();
q->endResetModel();
}
void KDescendantsProxyModelPrivate::sourceModelAboutToBeReset()
{
Q_Q(KDescendantsProxyModel);
q->beginResetModel();
}
void KDescendantsProxyModelPrivate::sourceModelReset()
{
Q_Q(KDescendantsProxyModel);
resetInternalData();
if (q->sourceModel()->hasChildren()) {
Q_ASSERT(q->sourceModel()->rowCount() > 0);
m_pendingParents.append(QModelIndex());
scheduleProcessPendingParents();
}
q->endResetModel();
}
void KDescendantsProxyModelPrivate::sourceLayoutAboutToBeChanged()
{
Q_Q(KDescendantsProxyModel);
if (m_ignoreNextLayoutChanged) {
m_ignoreNextLayoutChanged = false;
return;
}
if (m_mapping.isEmpty()) {
return;
}
QPersistentModelIndex srcPersistentIndex;
Q_FOREACH (const QPersistentModelIndex &proxyPersistentIndex, q->persistentIndexList()) {
m_proxyIndexes << proxyPersistentIndex;
Q_ASSERT(proxyPersistentIndex.isValid());
srcPersistentIndex = q->mapToSource(proxyPersistentIndex);
Q_ASSERT(srcPersistentIndex.isValid());
m_layoutChangePersistentIndexes << srcPersistentIndex;
}
q->layoutAboutToBeChanged();
}
void KDescendantsProxyModelPrivate::sourceLayoutChanged()
{
Q_Q(KDescendantsProxyModel);
if (m_ignoreNextLayoutAboutToBeChanged) {
m_ignoreNextLayoutAboutToBeChanged = false;
return;
}
if (m_mapping.isEmpty()) {
return;
}
m_rowCount = 0;
synchronousMappingRefresh();
for (int i = 0; i < m_proxyIndexes.size(); ++i) {
q->changePersistentIndex(m_proxyIndexes.at(i), q->mapFromSource(m_layoutChangePersistentIndexes.at(i)));
}
m_layoutChangePersistentIndexes.clear();
m_proxyIndexes.clear();
q->layoutChanged();
}
void KDescendantsProxyModelPrivate::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
Q_Q(KDescendantsProxyModel);
Q_ASSERT(topLeft.model() == q->sourceModel());
Q_ASSERT(bottomRight.model() == q->sourceModel());
const int topRow = topLeft.row();
const int bottomRow = bottomRight.row();
for (int i = topRow; i <= bottomRow; ++i) {
const QModelIndex sourceTopLeft = q->sourceModel()->index(i, topLeft.column(), topLeft.parent());
Q_ASSERT(sourceTopLeft.isValid());
const QModelIndex proxyTopLeft = q->mapFromSource(sourceTopLeft);
// TODO. If an index does not have any descendants, then we can emit in blocks of rows.
// As it is we emit once for each row.
const QModelIndex sourceBottomRight = q->sourceModel()->index(i, bottomRight.column(), bottomRight.parent());
const QModelIndex proxyBottomRight = q->mapFromSource(sourceBottomRight);
Q_ASSERT(proxyTopLeft.isValid());
Q_ASSERT(proxyBottomRight.isValid());
emit q->dataChanged(proxyTopLeft, proxyBottomRight);
}
}
void KDescendantsProxyModelPrivate::sourceModelDestroyed()
{
resetInternalData();
}
QMimeData *KDescendantsProxyModel::mimeData(const QModelIndexList &indexes) const
{
if (!sourceModel()) {
return QAbstractProxyModel::mimeData(indexes);
}
Q_ASSERT(sourceModel());
QModelIndexList sourceIndexes;
Q_FOREACH (const QModelIndex &index, indexes) {
sourceIndexes << mapToSource(index);
}
return sourceModel()->mimeData(sourceIndexes);
}
QStringList KDescendantsProxyModel::mimeTypes() const
{
if (!sourceModel()) {
return QAbstractProxyModel::mimeTypes();
}
Q_ASSERT(sourceModel());
return sourceModel()->mimeTypes();
}
Qt::DropActions KDescendantsProxyModel::supportedDropActions() const
{
if (!sourceModel()) {
return QAbstractProxyModel::supportedDropActions();
}
return sourceModel()->supportedDropActions();
}
#include "moc_kdescendantsproxymodel.cpp"

713
src/modeltest.cpp Normal file
View file

@ -0,0 +1,713 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtGui/QtGui>
#include <QVariant>
#include "QtiHanClient/modeltest.h"
#define QUOTE(str) #str
#define EAQ(str) QUOTE(str)
#define QVERIFY(x) if (!(x)) qWarning() << EAQ(__FILE__) << ":" << EAQ(__LINE__) << "(" << EAQ(__FUNCTION__) << ") Verify Failed: " << EAQ(x);
#define QCOMPARE(x,y) if (x != y) qWarning() << EAQ(__FILE__) << ":" << EAQ(__LINE__) << "(" << EAQ(__FUNCTION__) << ") Compare Failed: " << EAQ(x);
Q_DECLARE_METATYPE ( QModelIndex )
/*!
Connect to all of the models signals. Whenever anything happens recheck everything.
*/
ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false )
{
if (!model)
qFatal("%s: model must not be null", Q_FUNC_INFO);
connect ( model, SIGNAL ( columnsAboutToBeInserted ( const QModelIndex &, int, int ) ),
this, SLOT ( runAllTests() ) );
connect ( model, SIGNAL ( columnsAboutToBeRemoved ( const QModelIndex &, int, int ) ),
this, SLOT ( runAllTests() ) );
connect ( model, SIGNAL ( columnsInserted ( const QModelIndex &, int, int ) ),
this, SLOT ( runAllTests() ) );
connect ( model, SIGNAL ( columnsRemoved ( const QModelIndex &, int, int ) ),
this, SLOT ( runAllTests() ) );
connect ( model, SIGNAL ( dataChanged ( const QModelIndex &, const QModelIndex & ) ),
this, SLOT ( runAllTests() ) );
connect ( model, SIGNAL ( dataChanged ( const QModelIndex &, const QModelIndex & ) ),
this, SLOT ( runDataTests(const QModelIndex &, const QModelIndex & ) ) );
connect ( model, SIGNAL ( headerDataChanged ( Qt::Orientation, int, int ) ),
this, SLOT ( runAllTests() ) );
connect ( model, SIGNAL ( layoutAboutToBeChanged () ), this, SLOT ( runAllTests() ) );
connect ( model, SIGNAL ( layoutChanged () ), this, SLOT ( runAllTests() ) );
connect ( model, SIGNAL ( modelReset () ), this, SLOT ( runAllTests() ) );
connect ( model, SIGNAL ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ),
this, SLOT ( runAllTests() ) );
connect ( model, SIGNAL ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ),
this, SLOT ( runAllTests() ) );
connect ( model, SIGNAL ( rowsInserted ( const QModelIndex &, int, int ) ),
this, SLOT ( runAllTests() ) );
connect ( model, SIGNAL ( rowsRemoved ( const QModelIndex &, int, int ) ),
this, SLOT ( runAllTests() ) );
// Special checks for inserting/removing
connect ( model, SIGNAL ( layoutAboutToBeChanged() ),
this, SLOT ( layoutAboutToBeChanged() ) );
connect ( model, SIGNAL ( layoutChanged() ),
this, SLOT ( layoutChanged() ) );
connect ( model, SIGNAL ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ),
this, SLOT ( rowsAboutToBeInserted ( const QModelIndex &, int, int ) ) );
connect ( model, SIGNAL ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ),
this, SLOT ( rowsAboutToBeRemoved ( const QModelIndex &, int, int ) ) );
connect ( model, SIGNAL ( rowsInserted ( const QModelIndex &, int, int ) ),
this, SLOT ( rowsInserted ( const QModelIndex &, int, int ) ) );
connect ( model, SIGNAL ( rowsRemoved ( const QModelIndex &, int, int ) ),
this, SLOT ( rowsRemoved ( const QModelIndex &, int, int ) ) );
runAllTests();
}
void ModelTest::runAllTests()
{
if ( fetchingMore )
return;
nonDestructiveBasicTest();
rowCount();
columnCount();
hasIndex();
index();
parent();
data();
}
/*!
nonDestructiveBasicTest tries to call a number of the basic functions (not all)
to make sure the model doesn't outright segfault, testing the functions that makes sense.
*/
void ModelTest::nonDestructiveBasicTest()
{
QVERIFY( model->buddy ( QModelIndex() ) == QModelIndex() );
model->canFetchMore ( QModelIndex() );
QVERIFY( model->columnCount ( QModelIndex() ) >= 0 );
QVERIFY( model->data ( QModelIndex() ) == QVariant() );
fetchingMore = true;
model->fetchMore ( QModelIndex() );
fetchingMore = false;
Qt::ItemFlags flags = model->flags ( QModelIndex() );
QVERIFY( flags == Qt::ItemIsDropEnabled || flags == 0 );
model->hasChildren ( QModelIndex() );
model->hasIndex ( 0, 0 );
model->headerData ( 0, Qt::Horizontal );
model->index ( 0, 0 );
model->itemData ( QModelIndex() );
QVariant cache;
model->match ( QModelIndex(), -1, cache );
model->mimeTypes();
QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() );
QVERIFY( model->rowCount() >= 0 );
QVariant variant;
model->setData ( QModelIndex(), variant, -1 );
model->setHeaderData ( -1, Qt::Horizontal, QVariant() );
model->setHeaderData ( 999999, Qt::Horizontal, QVariant() );
QMap<int, QVariant> roles;
model->sibling ( 0, 0, QModelIndex() );
model->span ( QModelIndex() );
model->supportedDropActions();
}
/*!
Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren()
Models that are dynamically populated are not as fully tested here.
*/
void ModelTest::rowCount()
{
// qDebug() << "rc";
// check top row
QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
int rows = model->rowCount ( topIndex );
QVERIFY( rows >= 0 );
if ( rows > 0 )
QVERIFY( model->hasChildren ( topIndex ) );
QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex );
if ( secondLevelIndex.isValid() ) { // not the top level
// check a row count where parent is valid
rows = model->rowCount ( secondLevelIndex );
QVERIFY( rows >= 0 );
if ( rows > 0 )
QVERIFY( model->hasChildren ( secondLevelIndex ) );
}
// The models rowCount() is tested more extensively in checkChildren(),
// but this catches the big mistakes
}
/*!
Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren()
*/
void ModelTest::columnCount()
{
// check top row
QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
QVERIFY( model->columnCount ( topIndex ) >= 0 );
// check a column count where parent is valid
QModelIndex childIndex = model->index ( 0, 0, topIndex );
if ( childIndex.isValid() )
QVERIFY( model->columnCount ( childIndex ) >= 0 );
// columnCount() is tested more extensively in checkChildren(),
// but this catches the big mistakes
}
/*!
Tests model's implementation of QAbstractItemModel::hasIndex()
*/
void ModelTest::hasIndex()
{
// qDebug() << "hi";
// Make sure that invalid values returns an invalid index
QVERIFY( !model->hasIndex ( -2, -2 ) );
QVERIFY( !model->hasIndex ( -2, 0 ) );
QVERIFY( !model->hasIndex ( 0, -2 ) );
int rows = model->rowCount();
int columns = model->columnCount();
// check out of bounds
QVERIFY( !model->hasIndex ( rows, columns ) );
QVERIFY( !model->hasIndex ( rows + 1, columns + 1 ) );
if ( rows > 0 )
QVERIFY( model->hasIndex ( 0, 0 ) );
// hasIndex() is tested more extensively in checkChildren(),
// but this catches the big mistakes
}
/*!
Tests model's implementation of QAbstractItemModel::index()
*/
void ModelTest::index()
{
// qDebug() << "i";
// Make sure that invalid values returns an invalid index
QVERIFY( model->index ( -2, -2 ) == QModelIndex() );
QVERIFY( model->index ( -2, 0 ) == QModelIndex() );
QVERIFY( model->index ( 0, -2 ) == QModelIndex() );
int rows = model->rowCount();
int columns = model->columnCount();
if ( rows == 0 )
return;
// Catch off by one errors
QVERIFY( model->index ( rows, columns ) == QModelIndex() );
QVERIFY( model->index ( 0, 0 ).isValid() );
// Make sure that the same index is *always* returned
QModelIndex a = model->index ( 0, 0 );
QModelIndex b = model->index ( 0, 0 );
QVERIFY( a == b );
// index() is tested more extensively in checkChildren(),
// but this catches the big mistakes
}
/*!
Tests model's implementation of QAbstractItemModel::parent()
*/
void ModelTest::parent()
{
// qDebug() << "p";
// Make sure the model wont crash and will return an invalid QModelIndex
// when asked for the parent of an invalid index.
QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() );
if ( model->rowCount() == 0 )
return;
// Column 0 | Column 1 |
// QModelIndex() | |
// \- topIndex | topIndex1 |
// \- childIndex | childIndex1 |
// Common error test #1, make sure that a top level index has a parent
// that is a invalid QModelIndex.
QModelIndex topIndex = model->index ( 0, 0, QModelIndex() );
QVERIFY( model->parent ( topIndex ) == QModelIndex() );
// Common error test #2, make sure that a second level index has a parent
// that is the first level index.
if ( model->rowCount ( topIndex ) > 0 ) {
QModelIndex childIndex = model->index ( 0, 0, topIndex );
QVERIFY( model->parent ( childIndex ) == topIndex );
}
// Common error test #3, the second column should NOT have the same children
// as the first column in a row.
// Usually the second column shouldn't have children.
QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() );
if ( model->rowCount ( topIndex1 ) > 0 ) {
QModelIndex childIndex = model->index ( 0, 0, topIndex );
QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 );
QVERIFY( childIndex != childIndex1 );
}
// Full test, walk n levels deep through the model making sure that all
// parent's children correctly specify their parent.
checkChildren ( QModelIndex() );
}
/*!
Called from the parent() test.
A model that returns an index of parent X should also return X when asking
for the parent of the index.
This recursive function does pretty extensive testing on the whole model in an
effort to catch edge cases.
This function assumes that rowCount(), columnCount() and index() already work.
If they have a bug it will point it out, but the above tests should have already
found the basic bugs because it is easier to figure out the problem in
those tests then this one.
*/
void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth )
{
// First just try walking back up the tree.
QModelIndex p = parent;
while ( p.isValid() )
p = p.parent();
// For models that are dynamically populated
if ( model->canFetchMore ( parent ) ) {
fetchingMore = true;
model->fetchMore ( parent );
fetchingMore = false;
}
int rows = model->rowCount ( parent );
int columns = model->columnCount ( parent );
if ( rows > 0 )
QVERIFY( model->hasChildren ( parent ) );
// Some further testing against rows(), columns(), and hasChildren()
QVERIFY( rows >= 0 );
QVERIFY( columns >= 0 );
if ( rows > 0 )
QVERIFY( model->hasChildren ( parent ) );
//qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows
// << "columns:" << columns << "parent column:" << parent.column();
QVERIFY( !model->hasIndex ( rows + 1, 0, parent ) );
for ( int r = 0; r < rows; ++r ) {
if ( model->canFetchMore ( parent ) ) {
fetchingMore = true;
model->fetchMore ( parent );
fetchingMore = false;
}
QVERIFY( !model->hasIndex ( r, columns + 1, parent ) );
for ( int c = 0; c < columns; ++c ) {
QVERIFY( model->hasIndex ( r, c, parent ) );
QModelIndex index = model->index ( r, c, parent );
// rowCount() and columnCount() said that it existed...
QVERIFY( index.isValid() );
// index() should always return the same index when called twice in a row
QModelIndex modifiedIndex = model->index ( r, c, parent );
QVERIFY( index == modifiedIndex );
// Make sure we get the same index if we request it twice in a row
QModelIndex a = model->index ( r, c, parent );
QModelIndex b = model->index ( r, c, parent );
QVERIFY( a == b );
// Some basic checking on the index that is returned
QVERIFY( index.model() == model );
QCOMPARE( index.row(), r );
QCOMPARE( index.column(), c );
// While you can technically return a QVariant usually this is a sign
// of a bug in data(). Disable if this really is ok in your model.
// QVERIFY( model->data ( index, Qt::DisplayRole ).isValid() );
// If the next test fails here is some somewhat useful debug you play with.
if (model->parent(index) != parent) {
qDebug() << r << c << currentDepth << model->data(index).toString()
<< model->data(parent).toString();
qDebug() << index << parent << model->parent(index);
// And a view that you can even use to show the model.
// QTreeView view;
// view.setModel(model);
// view.show();
}
// Check that we can get back our real parent.
QCOMPARE( model->parent ( index ), parent );
// recursively go down the children
if ( model->hasChildren ( index ) && currentDepth < 10 ) {
//qDebug() << r << c << "has children" << model->rowCount(index);
checkChildren ( index, ++currentDepth );
}/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/
// make sure that after testing the children that the index doesn't change.
QModelIndex newerIndex = model->index ( r, c, parent );
QVERIFY( index == newerIndex );
}
}
}
void ModelTest::runDataTests(const QModelIndex &start, const QModelIndex &end ) {
Q_UNUSED(end);
// Invalid index should return an invalid qvariant
QVERIFY( !model->data ( QModelIndex() ).isValid() );
if ( model->rowCount() == 0 )
return;
// A valid index should have a valid QVariant data
QVERIFY( model->index ( 0, 0 ).isValid() );
// shouldn't be able to set data on an invalid index
QVERIFY( !model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) );
qDebug() << "DataChanged:" << model->data(start, Qt::DisplayRole);
// General Purpose roles that should return a QString
QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole );
if ( variant.isValid() ) {
#if QT_VERSION < 0x050000
QVERIFY( qVariantCanConvert<QString> ( variant ) );
#else
QVERIFY( variant.canConvert(QMetaType::QString) );
#endif
}
variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole );
if ( variant.isValid() ) {
#if QT_VERSION < 0x050000
QVERIFY( qVariantCanConvert<QString> ( variant ) );
#else
QVERIFY( variant.canConvert(QMetaType::QString) );
#endif
}
variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole );
if ( variant.isValid() ) {
#if QT_VERSION < 0x050000
QVERIFY( qVariantCanConvert<QString> ( variant ) );
#else
QVERIFY( variant.canConvert(QMetaType::QString) );
#endif
}
// General Purpose roles that should return a QSize
variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole );
if ( variant.isValid() ) {
#if QT_VERSION < 0x050000
QVERIFY( qVariantCanConvert<QSize> ( variant ) );
#else
QVERIFY( variant.canConvert(QMetaType::QSize) );
#endif
}
// General Purpose roles that should return a QFont
QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole );
if ( fontVariant.isValid() ) {
#if QT_VERSION < 0x050000
QVERIFY( qVariantCanConvert<QFont> ( fontVariant ) );
#else
QVERIFY( fontVariant.canConvert(QMetaType::QFont) );
#endif
}
// Check that the alignment is one we know about
QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole );
if ( textAlignmentVariant.isValid() ) {
int alignment = textAlignmentVariant.toInt();
QCOMPARE( alignment, ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) );
}
// General Purpose roles that should return a QColor
QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole );
if ( colorVariant.isValid() ) {
#if QT_VERSION < 0x050000
QVERIFY( qVariantCanConvert<QColor> ( colorVariant ) );
#else
QVERIFY( colorVariant.canConvert(QMetaType::QColor) );
#endif
}
colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole );
if ( colorVariant.isValid() ) {
#if QT_VERSION < 0x050000
QVERIFY( qVariantCanConvert<QColor> ( colorVariant ) );
#else
QVERIFY( colorVariant.canConvert(QMetaType::QColor) );
#endif
}
// Check that the "check state" is one we know about.
QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole );
if ( checkStateVariant.isValid() ) {
int state = checkStateVariant.toInt();
QVERIFY( state == Qt::Unchecked ||
state == Qt::PartiallyChecked ||
state == Qt::Checked );
}
}
/*!
Tests model's implementation of QAbstractItemModel::data()
*/
void ModelTest::data()
{
// Invalid index should return an invalid qvariant
QVERIFY( !model->data ( QModelIndex() ).isValid() );
if ( model->rowCount() == 0 )
return;
// A valid index should have a valid QVariant data
QVERIFY( model->index ( 0, 0 ).isValid() );
// shouldn't be able to set data on an invalid index
QVERIFY( !model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) );
// General Purpose roles that should return a QString
QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole );
if ( variant.isValid() ) {
#if QT_VERSION < 0x050000
QVERIFY( qVariantCanConvert<QString> ( variant ) );
#else
QVERIFY( variant.canConvert(QMetaType::QString) );
#endif
}
variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole );
if ( variant.isValid() ) {
#if QT_VERSION < 0x050000
QVERIFY( qVariantCanConvert<QString> ( variant ) );
#else
QVERIFY( variant.canConvert(QMetaType::QString) );
#endif
}
variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole );
if ( variant.isValid() ) {
#if QT_VERSION < 0x050000
QVERIFY( qVariantCanConvert<QString> ( variant ) );
#else
QVERIFY( variant.canConvert(QMetaType::QString) );
#endif
}
// General Purpose roles that should return a QSize
variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole );
if ( variant.isValid() ) {
#if QT_VERSION < 0x050000
QVERIFY( qVariantCanConvert<QSize> ( variant ) );
#else
QVERIFY( variant.canConvert(QMetaType::QSize) );
#endif
}
// General Purpose roles that should return a QFont
QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole );
if ( fontVariant.isValid() ) {
#if QT_VERSION < 0x050000
QVERIFY( qVariantCanConvert<QFont> ( fontVariant ) );
#else
QVERIFY( fontVariant.canConvert(QMetaType::QFont) );
#endif
}
// Check that the alignment is one we know about
QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole );
if ( textAlignmentVariant.isValid() ) {
int alignment = textAlignmentVariant.toInt();
QCOMPARE( alignment, ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) );
}
// General Purpose roles that should return a QColor
QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole );
if ( colorVariant.isValid() ) {
#if QT_VERSION < 0x050000
QVERIFY( qVariantCanConvert<QColor> ( colorVariant ) );
#else
QVERIFY( colorVariant.canConvert(QMetaType::QColor) );
#endif
}
colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole );
if ( colorVariant.isValid() ) {
#if QT_VERSION < 0x050000
QVERIFY( qVariantCanConvert<QColor> ( colorVariant ) );
#else
QVERIFY( colorVariant.canConvert(QMetaType::QColor) );
#endif
}
// Check that the "check state" is one we know about.
QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole );
if ( checkStateVariant.isValid() ) {
int state = checkStateVariant.toInt();
QVERIFY( state == Qt::Unchecked ||
state == Qt::PartiallyChecked ||
state == Qt::Checked );
}
}
/*!
Store what is about to be inserted to make sure it actually happens
\sa rowsInserted()
*/
void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int end )
{
Q_UNUSED(end);
// qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString()
// << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) );
// qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) );
Changing c;
c.parent = parent;
c.oldSize = model->rowCount ( parent );
c.last = model->data ( model->index ( start - 1, 0, parent ) );
c.next = model->data ( model->index ( start, 0, parent ) );
insert.push ( c );
}
/*!
Confirm that what was said was going to happen actually did
\sa rowsAboutToBeInserted()
*/
void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end )
{
Changing c = insert.pop();
QVERIFY( c.parent == parent );
//qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize
//<< "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent );
// for (int ii=start; ii <= end; ii++)
//{
// qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent )).toString();
//}
//qDebug() << "oldsize: " << c.oldSize << " end: " << end << " start: " << start << " rowcount: " << model->rowCount(parent);
QVERIFY( c.oldSize + ( end - start + 1 ) == model->rowCount ( parent ) );
QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) );
//qDebug() << "Last: " << c.last.toString() << " New: " << model->data ( model->index ( start - 1, 0, c.parent ) ).toString();
// if (c.next != model->data(model->index(end + 1, 0, c.parent))) {
// qDebug() << start << end;
// for (int i=0; i < model->rowCount(); ++i)
// qDebug() << model->index(i, 0).data().toString();
// qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent));
// }
QVERIFY( c.next == model->data ( model->index ( end + 1, 0, c.parent ) ) );
}
void ModelTest::layoutAboutToBeChanged()
{
for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i )
changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) );
}
void ModelTest::layoutChanged()
{
for ( int i = 0; i < changing.count(); ++i ) {
QPersistentModelIndex p = changing[i];
QVERIFY( p == model->index ( p.row(), p.column(), p.parent() ) );
}
changing.clear();
}
/*!
Store what is about to be inserted to make sure it actually happens
\sa rowsRemoved()
*/
void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end )
{
//qDebug() << "ratbr" << parent << start << end;
Changing c;
c.parent = parent;
c.oldSize = model->rowCount ( parent );
c.last = model->data ( model->index ( start - 1, 0, parent ) );
c.next = model->data ( model->index ( end + 1, 0, parent ) );
remove.push ( c );
//qDebug() << c.oldSize << c.last << c.next;
//qDebug() << model->data(parent);
}
/*!
Confirm that what was said was going to happen actually did
\sa rowsAboutToBeRemoved()
*/
void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end )
{
Changing c = remove.pop();
//qDebug() << "rr" << parent << start << end;
// qDebug() << (end - start + 1) << model->rowCount(parent);
//qDebug() << model->data ( model->index ( start - 1, 0, c.parent ) );
//qDebug() << model->data ( model->index ( start, 0, c.parent ) );
//qDebug() << model->data ( parent);
QVERIFY( c.parent == parent );
QVERIFY( c.oldSize - ( end - start + 1 ) == model->rowCount ( parent ) );
QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) );
QVERIFY( c.next == model->data ( model->index ( start, 0, c.parent ) ) );
}