mirror of
https://github.com/Fishwaldo/libqtihandclient.git
synced 2025-03-15 19:31:30 +00:00
Initial Commit
This commit is contained in:
commit
5b48528a3e
21 changed files with 6366 additions and 0 deletions
0
AUTHORS
Normal file
0
AUTHORS
Normal file
458
COPYING
Normal file
458
COPYING
Normal 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
0
ChangeLog
Normal file
370
INSTALL
Normal file
370
INSTALL
Normal 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
0
NEWS
Normal file
1
README
Normal file
1
README
Normal file
|
@ -0,0 +1 @@
|
|||
This is the libiHanClient Library.
|
396
include/QtiHanClient/DeviceModel.h
Normal file
396
include/QtiHanClient/DeviceModel.h
Normal 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
|
126
include/QtiHanClient/MessageHandler.h
Normal file
126
include/QtiHanClient/MessageHandler.h
Normal 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_ */
|
124
include/QtiHanClient/QtiHanClient.h
Normal file
124
include/QtiHanClient/QtiHanClient.h
Normal 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_ */
|
624
include/QtiHanClient/kbihash_p.h
Normal file
624
include/QtiHanClient/kbihash_p.h
Normal 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
|
206
include/QtiHanClient/kdescendantsproxymodel.h
Normal file
206
include/QtiHanClient/kdescendantsproxymodel.h
Normal 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
|
41
include/QtiHanClient/kitemmodels_export.h
Normal file
41
include/QtiHanClient/kitemmodels_export.h
Normal 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
|
95
include/QtiHanClient/modeltest.h
Normal file
95
include/QtiHanClient/modeltest.h
Normal 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
85
libihanclient.spec.in
Normal 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
18
libqtihanclient.pri
Normal 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
38
libqtihanclient.pro
Normal 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
1037
src/DeviceModel.cpp
Normal file
File diff suppressed because it is too large
Load diff
317
src/MessageHandler.cpp
Normal file
317
src/MessageHandler.cpp
Normal 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
720
src/QtiHanClient.cpp
Normal 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());
|
||||
}
|
997
src/kdescendantsproxymodel.cpp
Normal file
997
src/kdescendantsproxymodel.cpp
Normal 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
713
src/modeltest.cpp
Normal 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 ) ) );
|
||||
}
|
||||
|
||||
|
Loading…
Add table
Reference in a new issue