aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTed Gould <ted@canonical.com>2009-08-04 16:34:08 +0100
committerTed Gould <ted@canonical.com>2009-08-04 16:34:08 +0100
commitf5c8526e3bb73345242ea37c337562182351020b (patch)
tree432cb5de765f8484daa87646c0a60d3bac6b3f4f
parentec5c382624da19a1a83d1e75d21f74778df01f38 (diff)
parent92f690257a45e81b86b39fc77ce1fe10c7b4908a (diff)
downloadlibdbusmenu-f5c8526e3bb73345242ea37c337562182351020b.tar.gz
libdbusmenu-f5c8526e3bb73345242ea37c337562182351020b.tar.bz2
libdbusmenu-f5c8526e3bb73345242ea37c337562182351020b.zip
Merging in the development branches
-rw-r--r--.bzrignore8
-rw-r--r--COPYING827
-rw-r--r--COPYING.2.1510
-rw-r--r--configure.ac26
-rw-r--r--libdbusmenu-glib/client.c204
-rw-r--r--libdbusmenu-glib/client.h4
-rw-r--r--libdbusmenu-glib/menuitem-marshal.list2
-rw-r--r--libdbusmenu-glib/menuitem.c138
-rw-r--r--libdbusmenu-glib/menuitem.h10
-rw-r--r--libdbusmenu-glib/server.c5
-rw-r--r--libdbusmenu-gtk/Makefile.am13
-rw-r--r--libdbusmenu-gtk/client.c260
-rw-r--r--libdbusmenu-gtk/client.h109
-rw-r--r--libdbusmenu-gtk/menu.c281
-rw-r--r--libdbusmenu-gtk/menu.h102
-rw-r--r--libdbusmenu-gtk/test.c4
-rw-r--r--libdbusmenu-gtk/test.h2
-rw-r--r--tests/Makefile.am59
-rw-r--r--tests/dbusmenu-gtk/Makefile.am43
-rw-r--r--tests/dbusmenu-gtk/data/blank_label.json26
-rw-r--r--tests/dbusmenu-gtk/data/blank_label_2levels.json82
-rw-r--r--tests/dbusmenu-gtk/data/blank_submenus.json48
-rw-r--r--tests/dbusmenu-gtk/data/dynamic.json158
-rw-r--r--tests/dbusmenu-gtk/data/long_label.json82
-rw-r--r--tests/dbusmenu-gtk/data/no_id.json24
-rw-r--r--tests/dbusmenu-gtk/data/no_label.json24
-rw-r--r--tests/dbusmenu-gtk/data/sameid_submenus.json82
-rw-r--r--tests/dbusmenu-gtk/data/sameid_submenus_diff_sizes.json64
-rw-r--r--tests/dbusmenu-gtk/data/sameid_top_and_submenus.json82
-rw-r--r--tests/dbusmenu-gtk/data/sameid_topmenu.json158
-rw-r--r--tests/dbusmenu-gtk/data/several_submenus.json51
-rw-r--r--tests/dbusmenu-gtk/data/several_submenus_recursive.json49
-rw-r--r--tests/dbusmenu-gtk/data/several_submenus_utf8.json51
-rw-r--r--tests/dbusmenu-gtk/data/static.json26
-rw-r--r--tests/dbusmenu-gtk/data/test-gtk-label.json158
-rwxr-xr-xtests/dbusmenu-gtk/dbusMenuTest8
-rw-r--r--tests/dbusmenu-gtk/dbusmenu.py.in71
-rw-r--r--tests/dbusmenu-gtk/dbusmenu.xml.in89
-rw-r--r--tests/test-glib-layout-client.c4
-rw-r--r--tests/test-glib-layout-server.c2
-rw-r--r--tests/test-glib-properties-server.c28
-rw-r--r--tests/test-glib-properties.h21
-rw-r--r--tests/test-glib-simple-items.c45
-rw-r--r--tests/test-gtk-label-client.c181
-rw-r--r--tests/test-gtk-label-server.c168
-rw-r--r--tests/test-gtk-label.json158
46 files changed, 3844 insertions, 703 deletions
diff --git a/.bzrignore b/.bzrignore
index 8c4e180..1bac747 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -33,3 +33,11 @@ menuitem-marshal.h
libdbusmenu_glib_la-menuitem-marshal.lo
test-glib-properties-client
test-glib-properties-server
+libdbusmenu_gtk_la-menu.lo
+test-gtk-label-client
+test-gtk-label-server
+libdbusmenu_gtk_la-client.lo
+dbusmenu.xml
+dbusmenu.py
+mago.results
+test-glib-simple-items
diff --git a/COPYING b/COPYING
index 94a9ed0..fc8a5de 100644
--- a/COPYING
+++ b/COPYING
@@ -1,674 +1,165 @@
- GNU GENERAL PUBLIC LICENSE
+ GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, 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
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If 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 convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU 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
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- 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 3 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, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- <program> Copyright (C) <year> <name of author>
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-<http://www.gnu.org/licenses/>.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ 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 that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU 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 as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/COPYING.2.1 b/COPYING.2.1
new file mode 100644
index 0000000..2d2d780
--- /dev/null
+++ b/COPYING.2.1
@@ -0,0 +1,510 @@
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin St, 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
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James
+ Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/configure.ac b/configure.ac
index 96b23fc..6a5ed9a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -35,6 +35,31 @@ AC_SUBST(DBUSMENUGLIB_CFLAGS)
AC_SUBST(DBUSMENUGLIB_LIBS)
###########################
+# Dependencies - GTK
+###########################
+
+GTK_REQUIRED_VERSION=2.16
+
+PKG_CHECK_MODULES(DBUSMENUGTK, gtk+-2.0 >= $GTK_REQUIRED_VERSION
+ glib-2.0 >= $GLIB_REQUIRED_VERSION
+ dbus-glib-1 >= $DBUS_REQUIRED_VERSION
+ libxml-2.0 >= $XML_REQUIRED_VERSION)
+
+AC_SUBST(DBUSMENUGTK_CFLAGS)
+AC_SUBST(DBUSMENUGTK_LIBS)
+
+###########################
+# Dependencies - Testing
+###########################
+
+JSON_GLIB_REQUIRED_VERSION=0.6.0
+
+PKG_CHECK_MODULES(DBUSMENUTESTS, json-glib-1.0 >= $JSON_GLIB_REQUIRED_VERSION)
+
+AC_SUBST(DBUSMENUTESTS_CFLAGS)
+AC_SUBST(DBUSMENUTESTS_LIBS)
+
+###########################
# Lib versioning
###########################
@@ -70,6 +95,7 @@ libdbusmenu-gtk/dbusmenu-gtk.pc
libdbusmenu-qt/Makefile
libdbusmenu-qt/dbusmenu-qt.pc
tests/Makefile
+tests/dbusmenu-gtk/Makefile
])
###########################
diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c
index 6094eca..212071b 100644
--- a/libdbusmenu-glib/client.c
+++ b/libdbusmenu-glib/client.c
@@ -47,6 +47,8 @@ enum {
/* Signals */
enum {
LAYOUT_UPDATED,
+ ROOT_CHANGED,
+ NEW_MENUITEM,
LAST_SIGNAL
};
@@ -64,6 +66,8 @@ struct _DbusmenuClientPrivate
DBusGProxy * menuproxy;
DBusGProxy * propproxy;
DBusGProxyCall * layoutcall;
+
+ DBusGProxy * dbusproxy;
};
#define DBUSMENU_CLIENT_GET_PRIVATE(o) \
@@ -82,7 +86,7 @@ static void id_prop_update (DBusGProxy * proxy, guint id, gchar * property, gcha
static void id_update (DBusGProxy * proxy, guint id, DbusmenuClient * client);
static void build_proxies (DbusmenuClient * client);
static guint parse_node_get_id (xmlNodePtr node);
-static DbusmenuMenuitem * parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, DBusGProxy * proxy);
+static DbusmenuMenuitem * parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, DBusGProxy * proxy);
static void parse_layout (DbusmenuClient * client, const gchar * layout);
static void update_layout_cb (DBusGProxy * proxy, DBusGProxyCall * call, void * data);
static void update_layout (DbusmenuClient * client);
@@ -118,6 +122,39 @@ dbusmenu_client_class_init (DbusmenuClientClass *klass)
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0, G_TYPE_NONE);
+ /**
+ DbusmenuClient::root-changed:
+ @arg0: The #DbusmenuClient object
+ @arg1: The new root #DbusmenuMenuitem
+
+ The layout has changed in a way that can not be
+ represented by the individual items changing as the
+ root of this client has changed.
+ */
+ signals[ROOT_CHANGED] = g_signal_new(DBUSMENU_CLIENT_SIGNAL_ROOT_CHANGED,
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (DbusmenuClientClass, root_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+ /**
+ DbusmenuClient::new-menuitem:
+ @arg0: The #DbusmenuClient object
+ @arg1: The new #DbusmenuMenuitem created
+
+ Signaled when the client creates a new menuitem. This
+ doesn't mean that it's placed anywhere. The parent that
+ it's applied to will signal #DbusmenuMenuitem::child-added
+ when it gets parented.
+ */
+ signals[NEW_MENUITEM] = g_signal_new(DBUSMENU_CLIENT_SIGNAL_NEW_MENUITEM,
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (DbusmenuClientClass, new_menuitem),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
g_object_class_install_property (object_class, PROP_DBUSOBJECT,
g_param_spec_string(DBUSMENU_CLIENT_PROP_DBUS_OBJECT, "DBus Object we represent",
@@ -148,6 +185,8 @@ dbusmenu_client_init (DbusmenuClient *self)
priv->propproxy = NULL;
priv->layoutcall = NULL;
+ priv->dbusproxy = NULL;
+
return;
}
@@ -168,8 +207,17 @@ dbusmenu_client_dispose (GObject *object)
g_object_unref(G_OBJECT(priv->propproxy));
priv->propproxy = NULL;
}
+ if (priv->dbusproxy != NULL) {
+ g_object_unref(G_OBJECT(priv->dbusproxy));
+ priv->dbusproxy = NULL;
+ }
priv->session_bus = NULL;
+ if (priv->root != NULL) {
+ g_object_unref(G_OBJECT(priv->root));
+ priv->root = NULL;
+ }
+
G_OBJECT_CLASS (dbusmenu_client_parent_class)->dispose (object);
return;
}
@@ -272,6 +320,85 @@ id_update (DBusGProxy * proxy, guint id, DbusmenuClient * client)
return;
}
+/* Watches to see if our DBus savior comes onto the bus */
+static void
+dbus_owner_change (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, DbusmenuClient * client)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+
+ if (!(new != NULL && prev == NULL)) {
+ /* If it's not someone new getting on the bus, sorry we
+ simply just don't care. It's not that your service isn't
+ important to someone, just not us. You'll find the right
+ process someday, there's lots of processes out there. */
+ return;
+ }
+
+ if (g_strcmp0(new, priv->dbus_name)) {
+ /* Again, someone else's service. */
+ return;
+ }
+
+ /* Woot! A service for us to love and to hold for ever
+ and ever and ever! */
+ return build_proxies(client);
+}
+
+/* This function builds the DBus proxy which will look out for
+ the service coming up. */
+static void
+build_dbus_proxy (DbusmenuClient * client)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ GError * error = NULL;
+
+ if (priv->dbusproxy != NULL) {
+ return;
+ }
+
+ priv->dbusproxy = dbus_g_proxy_new_for_name_owner (priv->session_bus,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ &error);
+ if (error != NULL) {
+ g_debug("Oh, that's bad. That's really bad. We can't get a proxy to DBus itself? Seriously? Here's all I know: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ dbus_g_proxy_add_signal(priv->dbusproxy, "NameOwnerChanged",
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(priv->dbusproxy, "NameOwnerChanged",
+ G_CALLBACK(dbus_owner_change), client, NULL);
+
+ return;
+}
+
+/* A signal handler that gets called when a proxy is destoryed a
+ so it needs to clean up a little. Make sure we don't think we
+ have a layout and setup the dbus watcher. */
+static void
+proxy_destroyed (GObject * gobj_proxy, gpointer userdata)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(userdata);
+
+ if (priv->root != NULL) {
+ g_object_unref(G_OBJECT(priv->root));
+ priv->root = NULL;
+ g_signal_emit(G_OBJECT(userdata), signals[ROOT_CHANGED], 0, NULL, TRUE);
+ g_signal_emit(G_OBJECT(userdata), signals[LAYOUT_UPDATED], 0, TRUE);
+ }
+
+ if ((gpointer)priv->menuproxy == (gpointer)gobj_proxy) {
+ priv->layoutcall = NULL;
+ }
+
+ build_dbus_proxy(DBUSMENU_CLIENT(userdata));
+ return;
+}
+
/* When we have a name and an object, build the two proxies and get the
first version of the layout */
static void
@@ -287,6 +414,7 @@ build_proxies (DbusmenuClient * client)
if (error != NULL) {
g_error("Unable to get session bus: %s", error->message);
g_error_free(error);
+ build_dbus_proxy(client);
return;
}
@@ -296,10 +424,12 @@ build_proxies (DbusmenuClient * client)
DBUS_INTERFACE_PROPERTIES,
&error);
if (error != NULL) {
- g_error("Unable to get property proxy for %s on %s: %s", priv->dbus_name, priv->dbus_object, error->message);
+ g_warning("Unable to get property proxy for %s on %s: %s", priv->dbus_name, priv->dbus_object, error->message);
g_error_free(error);
return;
}
+ g_object_add_weak_pointer(G_OBJECT(priv->propproxy), (gpointer *)&priv->propproxy);
+ g_signal_connect(G_OBJECT(priv->propproxy), "destroy", G_CALLBACK(proxy_destroyed), client);
priv->menuproxy = dbus_g_proxy_new_for_name_owner(priv->session_bus,
priv->dbus_name,
@@ -307,10 +437,18 @@ build_proxies (DbusmenuClient * client)
"org.freedesktop.dbusmenu",
&error);
if (error != NULL) {
- g_error("Unable to get dbusmenu proxy for %s on %s: %s", priv->dbus_name, priv->dbus_object, error->message);
+ g_warning("Unable to get dbusmenu proxy for %s on %s: %s", priv->dbus_name, priv->dbus_object, error->message);
g_error_free(error);
return;
}
+ g_object_add_weak_pointer(G_OBJECT(priv->menuproxy), (gpointer *)&priv->menuproxy);
+ g_signal_connect(G_OBJECT(priv->menuproxy), "destroy", G_CALLBACK(proxy_destroyed), client);
+
+ /* If we get here, we don't need the DBus proxy */
+ if (priv->dbusproxy != NULL) {
+ g_object_unref(G_OBJECT(priv->dbusproxy));
+ priv->dbusproxy = NULL;
+ }
dbus_g_proxy_add_signal(priv->menuproxy, "LayoutUpdate", G_TYPE_INVALID);
dbus_g_proxy_connect_signal(priv->menuproxy, "LayoutUpdate", G_CALLBACK(layout_update), client, NULL);
@@ -322,6 +460,8 @@ build_proxies (DbusmenuClient * client)
dbus_g_proxy_add_signal(priv->menuproxy, "IdUpdate", G_TYPE_UINT, G_TYPE_INVALID);
dbus_g_proxy_connect_signal(priv->menuproxy, "IdUpdate", G_CALLBACK(id_update), client, NULL);
+ update_layout(client);
+
return;
}
@@ -375,10 +515,30 @@ menuitem_get_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError
return;
}
+static void
+menuitem_call_cb (DBusGProxy * proxy, GError * error, gpointer userdata)
+{
+ DbusmenuMenuitem * mi = (DbusmenuMenuitem *)userdata;
+
+ if (error != NULL) {
+ g_warning("Unable to call menu item %d: %s", dbusmenu_menuitem_get_id(mi), error->message);
+ }
+
+ return;
+}
+
+static void
+menuitem_activate (DbusmenuMenuitem * mi, DbusmenuClient * client)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ org_freedesktop_dbusmenu_call_async (priv->menuproxy, dbusmenu_menuitem_get_id(mi), menuitem_call_cb, mi);
+ return;
+}
+
/* Parse recursively through the XML and make it into
objects as need be */
static DbusmenuMenuitem *
-parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, DBusGProxy * proxy)
+parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, DBusGProxy * proxy)
{
guint id = parse_node_get_id(node);
/* g_debug("Looking at node with id: %d", id); */
@@ -388,6 +548,7 @@ parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * pa
dbusmenu_menuitem_child_delete(parent, item);
}
g_object_unref(G_OBJECT(item));
+ item = NULL;
}
if (id == 0) {
@@ -397,6 +558,11 @@ parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * pa
/* Build a new item */
item = dbusmenu_menuitem_new_with_id(id);
+ if (parent == NULL) {
+ dbusmenu_menuitem_set_root(item, TRUE);
+ }
+ g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(menuitem_activate), client);
+ g_signal_emit(G_OBJECT(client), signals[NEW_MENUITEM], 0, item, TRUE);
/* Get the properties queued up for this item */
org_freedesktop_dbusmenu_get_properties_async(proxy, id, menuitem_get_properties_cb, item);
}
@@ -404,6 +570,7 @@ parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * pa
xmlNodePtr children;
guint position;
GList * oldchildren = dbusmenu_menuitem_take_children(item);
+ /* g_debug("Starting old children: %d", g_list_length(oldchildren)); */
for (children = node->children, position = 0; children != NULL; children = children->next, position++) {
/* g_debug("Looking at child: %d", position); */
@@ -420,13 +587,15 @@ parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * pa
}
}
- childmi = parse_layout_xml(children, childmi, item, proxy);
+ childmi = parse_layout_xml(client, children, childmi, item, proxy);
dbusmenu_menuitem_child_add_position(item, childmi, position);
}
+ /* g_debug("Stopping old children: %d", g_list_length(oldchildren)); */
GList * oldchildleft = NULL;
for (oldchildleft = oldchildren; oldchildleft != NULL; oldchildleft = g_list_next(oldchildleft)) {
DbusmenuMenuitem * oldmi = DBUSMENU_MENUITEM(oldchildleft->data);
+ g_debug("Unref'ing menu item with layout update. ID: %d", dbusmenu_menuitem_get_id(oldmi));
g_object_unref(G_OBJECT(oldmi));
}
g_list_free(oldchildren);
@@ -447,12 +616,18 @@ parse_layout (DbusmenuClient * client, const gchar * layout)
xmlNodePtr root = xmlDocGetRootElement(xmldoc);
- priv->root = parse_layout_xml(root, priv->root, NULL, priv->menuproxy);
+ DbusmenuMenuitem * oldroot = priv->root;
+ priv->root = parse_layout_xml(client, root, priv->root, NULL, priv->menuproxy);
+ xmlFreeDoc(xmldoc);
+
if (priv->root == NULL) {
g_warning("Unable to parse layout on client %s object %s: %s", priv->dbus_name, priv->dbus_object, layout);
}
- xmlFreeDoc(xmldoc);
+ if (priv->root != oldroot) {
+ g_signal_emit(G_OBJECT(client), signals[ROOT_CHANGED], 0, priv->root, TRUE);
+ }
+
return;
}
@@ -466,9 +641,9 @@ update_layout_cb (DBusGProxy * proxy, DBusGProxyCall * call, void * data)
GError * error = NULL;
GValue value = {0};
+ priv->layoutcall = NULL;
if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_VALUE, &value, G_TYPE_INVALID)) {
g_warning("Getting layout failed on client %s object %s: %s", priv->dbus_name, priv->dbus_object, error->message);
- priv->layoutcall = NULL;
g_error_free(error);
return;
}
@@ -477,7 +652,6 @@ update_layout_cb (DBusGProxy * proxy, DBusGProxyCall * call, void * data)
/* g_debug("Got layout string: %s", xml); */
parse_layout(client, xml);
- priv->layoutcall = NULL;
/* g_debug("Root is now: 0x%X", (unsigned int)priv->root); */
g_signal_emit(G_OBJECT(client), signals[LAYOUT_UPDATED], 0, TRUE);
@@ -491,6 +665,10 @@ update_layout (DbusmenuClient * client)
{
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ if (priv->propproxy == NULL) {
+ return;
+ }
+
if (priv->layoutcall != NULL) {
return;
}
@@ -528,7 +706,6 @@ dbusmenu_client_new (const gchar * name, const gchar * object)
DBUSMENU_CLIENT_PROP_DBUS_NAME, name,
DBUSMENU_CLIENT_PROP_DBUS_OBJECT, object,
NULL);
- update_layout(self);
return self;
}
@@ -546,7 +723,8 @@ dbusmenu_client_new (const gchar * name, const gchar * object)
it could block longer.
Return value: A #DbusmenuMenuitem representing the root of
- menu on the server.
+ menu on the server. If there is no server or there is
+ an error receiving its layout it'll return #NULL.
*/
DbusmenuMenuitem *
dbusmenu_client_get_root (DbusmenuClient * client)
@@ -555,6 +733,10 @@ dbusmenu_client_get_root (DbusmenuClient * client)
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ if (priv->propproxy == NULL) {
+ return NULL;
+ }
+
if (priv->layoutcall != NULL) {
/* Will end the current call and block on it's completion */
update_layout_cb(priv->propproxy, priv->layoutcall, client);
diff --git a/libdbusmenu-glib/client.h b/libdbusmenu-glib/client.h
index d591ebb..35f7122 100644
--- a/libdbusmenu-glib/client.h
+++ b/libdbusmenu-glib/client.h
@@ -44,6 +44,8 @@ G_BEGIN_DECLS
#define DBUSMENU_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_CLIENT, DbusmenuClientClass))
#define DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED "layout-updated"
+#define DBUSMENU_CLIENT_SIGNAL_ROOT_CHANGED "root-changed"
+#define DBUSMENU_CLIENT_SIGNAL_NEW_MENUITEM "new-menuitem"
#define DBUSMENU_CLIENT_PROP_DBUS_NAME "dbus-name"
#define DBUSMENU_CLIENT_PROP_DBUS_OBJECT "dbus-object"
@@ -66,6 +68,8 @@ struct _DbusmenuClientClass {
GObjectClass parent_class;
void (*layout_updated)(void);
+ void (*root_changed) (DbusmenuMenuitem * newroot);
+ void (*new_menuitem) (DbusmenuMenuitem * newitem);
/* Reserved for future use */
void (*reserved1) (void);
diff --git a/libdbusmenu-glib/menuitem-marshal.list b/libdbusmenu-glib/menuitem-marshal.list
index fc0318f..a32e7e3 100644
--- a/libdbusmenu-glib/menuitem-marshal.list
+++ b/libdbusmenu-glib/menuitem-marshal.list
@@ -1,3 +1,5 @@
VOID: STRING, STRING
+VOID: OBJECT, UINT, UINT
+VOID: OBJECT, UINT
VOID: OBJECT
VOID: VOID
diff --git a/libdbusmenu-glib/menuitem.c b/libdbusmenu-glib/menuitem.c
index 9506cad..aba1f64 100644
--- a/libdbusmenu-glib/menuitem.c
+++ b/libdbusmenu-glib/menuitem.c
@@ -39,6 +39,7 @@ License version 3 and version 2.1 along with this program. If not, see
@children: A list of #DbusmenuMenuitem objects that are
children to this one.
@properties: All of the properties on this menu item.
+ @root: Whether this node is the root node
These are the little secrets that we don't want getting
out of data that we have. They can still be gotten using
@@ -50,6 +51,7 @@ struct _DbusmenuMenuitemPrivate
guint id;
GList * children;
GHashTable * properties;
+ gboolean root;
};
/* Signals */
@@ -58,6 +60,7 @@ enum {
ITEM_ACTIVATED,
CHILD_ADDED,
CHILD_REMOVED,
+ CHILD_MOVED,
LAST_SIGNAL
};
@@ -129,6 +132,7 @@ dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass)
DbusmenuMenuitem::child-added:
@arg0: The #DbusmenuMenuitem which is the parent.
@arg1: The #DbusmenuMenuitem which is the child.
+ @arg2: The position that the child is being added in.
Signaled when the child menuitem has been added to
the parent.
@@ -138,8 +142,8 @@ dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(DbusmenuMenuitemClass, child_added),
NULL, NULL,
- _dbusmenu_menuitem_marshal_VOID__OBJECT,
- G_TYPE_NONE, 2, G_TYPE_OBJECT);
+ _dbusmenu_menuitem_marshal_VOID__OBJECT_UINT,
+ G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT);
/**
DbusmenuMenuitem::child-removed:
@arg0: The #DbusmenuMenuitem which was the parent.
@@ -156,7 +160,24 @@ dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass)
G_STRUCT_OFFSET(DbusmenuMenuitemClass, child_removed),
NULL, NULL,
_dbusmenu_menuitem_marshal_VOID__OBJECT,
- G_TYPE_NONE, 2, G_TYPE_OBJECT);
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+ /**
+ DbusmenuMenuitem::child-moved:
+ @arg0: The #DbusmenuMenuitem which is the parent.
+ @arg1: The #DbusmenuMenuitem which is the child.
+ @arg2: The position that the child is being moved to.
+ @arg3: The position that the child is was in.
+
+ Signaled when the child menuitem has had it's location
+ in the list change.
+ */
+ signals[CHILD_MOVED] = g_signal_new(DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED,
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(DbusmenuMenuitemClass, child_moved),
+ NULL, NULL,
+ _dbusmenu_menuitem_marshal_VOID__OBJECT_UINT_UINT,
+ G_TYPE_NONE, 3, G_TYPE_OBJECT, G_TYPE_UINT, G_TYPE_UINT);
g_object_class_install_property (object_class, PROP_ID,
g_param_spec_uint("id", "ID for the menu item",
@@ -178,6 +199,8 @@ dbusmenu_menuitem_init (DbusmenuMenuitem *self)
priv->children = NULL;
priv->properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+ priv->root = FALSE;
return;
}
@@ -185,6 +208,14 @@ dbusmenu_menuitem_init (DbusmenuMenuitem *self)
static void
dbusmenu_menuitem_dispose (GObject *object)
{
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(object);
+
+ GList * child = NULL;
+ for (child = priv->children; child != NULL; child = g_list_next(child)) {
+ g_object_unref(G_OBJECT(child->data));
+ }
+ g_list_free(priv->children);
+ priv->children = NULL;
G_OBJECT_CLASS (dbusmenu_menuitem_parent_class)->dispose (object);
return;
@@ -193,6 +224,7 @@ dbusmenu_menuitem_dispose (GObject *object)
static void
dbusmenu_menuitem_finalize (GObject *object)
{
+ /* g_debug("Menuitem dying"); */
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(object);
if (priv->properties != NULL) {
@@ -389,7 +421,29 @@ dbusmenu_menuitem_child_append (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
priv->children = g_list_append(priv->children, child);
- g_signal_emit(G_OBJECT(mi), signals[CHILD_ADDED], 0, child, TRUE);
+ g_signal_emit(G_OBJECT(mi), signals[CHILD_ADDED], 0, child, g_list_length(priv->children) - 1, TRUE);
+ return TRUE;
+}
+
+/**
+ dbusmenu_menuitem_child_prepend:
+ @mi: The #DbusmenuMenuitem which will become a new parent
+ @child: The #DbusmenMenuitem that will be a child
+
+ This function adds @child to the list of children on @mi at
+ the beginning of that list.
+
+ Return value: Whether the child has been added successfully.
+*/
+gboolean
+dbusmenu_menuitem_child_prepend (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(child), FALSE);
+
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ priv->children = g_list_prepend(priv->children, child);
+ g_signal_emit(G_OBJECT(mi), signals[CHILD_ADDED], 0, child, 0, TRUE);
return TRUE;
}
@@ -436,7 +490,44 @@ dbusmenu_menuitem_child_add_position (DbusmenuMenuitem * mi, DbusmenuMenuitem *
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
priv->children = g_list_insert(priv->children, child, position);
- g_signal_emit(G_OBJECT(mi), signals[CHILD_ADDED], 0, child, TRUE);
+ g_signal_emit(G_OBJECT(mi), signals[CHILD_ADDED], 0, child, position, TRUE);
+ return TRUE;
+}
+
+/**
+ dbusmenu_menuitem_child_reorder:
+ @base: The #DbusmenuMenuitem that has children needing realignment
+ @child: The #DbusmenuMenuitem that is a child needing to be moved
+ @position: The position in the list to place it in
+
+ This function moves a child on the list of children. It is
+ for a child that is already in the list, but simply needs a
+ new location.
+
+ Return value: Whether the move was successful.
+*/
+gboolean
+dbusmenu_menuitem_child_reorder(DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(child), FALSE);
+
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ gint oldpos = g_list_index(priv->children, child);
+
+ if (oldpos == -1) {
+ g_warning("Can not reorder child that isn't actually a child.");
+ return FALSE;
+ }
+ if (oldpos == position) {
+ return TRUE;
+ }
+
+ priv->children = g_list_remove(priv->children, child);
+ priv->children = g_list_insert(priv->children, child, position);
+
+ g_signal_emit(G_OBJECT(mi), signals[CHILD_MOVED], 0, child, position, oldpos, TRUE);
+
return TRUE;
}
@@ -658,6 +749,43 @@ dbusmenu_menuitem_properties_copy (DbusmenuMenuitem * mi)
}
/**
+ dbusmenu_menuitem_set_root:
+ @mi: #DbusmenuMenuitem to set whether it's root
+ @root: Whether @mi is a root node or not
+
+ This function sets the internal value of whether this is a
+ root node or not.
+
+ Return value: None
+*/
+void
+dbusmenu_menuitem_set_root (DbusmenuMenuitem * mi, gboolean root)
+{
+ g_return_if_fail(DBUSMENU_IS_MENUITEM(mi));
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ priv->root = root;
+ return;
+}
+
+/**
+ dbusmenu_menuitem_get_root:
+ @mi: #DbusmenuMenuitem to see whether it's root
+
+ This function returns the internal value of whether this is a
+ root node or not.
+
+ Return value: #TRUE if this is a root node
+*/
+gboolean
+dbusmenu_menuitem_get_root (DbusmenuMenuitem * mi)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ return priv->root;
+}
+
+
+/**
dbusmenu_menuitem_buildxml:
@mi: #DbusmenuMenuitem to represent in XML
@array: A list of string that will be turned into an XML file
diff --git a/libdbusmenu-glib/menuitem.h b/libdbusmenu-glib/menuitem.h
index 3f3e97c..29865c7 100644
--- a/libdbusmenu-glib/menuitem.h
+++ b/libdbusmenu-glib/menuitem.h
@@ -46,6 +46,7 @@ G_BEGIN_DECLS
#define DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED "item-activated"
#define DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED "child-added"
#define DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED "child-removed"
+#define DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED "child-moved"
/**
DbusmenuMenuitem:
@@ -69,6 +70,7 @@ struct _DbusmenuMenuitem
@item_activated: Slot for #DbusmenuMenuitem::item-activated.
@child_added: Slot for #DbusmenuMenuitem::child-added.
@child_removed: Slot for #DbusmenuMenuitem::child-removed.
+ @child_moved: Slot for #DbusmenuMenuitem::child-moved.
@buildxml: Virtual function that appends the strings required
to represent this menu item in the menu XML file.
@reserved1: Reserved for future use.
@@ -84,8 +86,9 @@ struct _DbusmenuMenuitemClass
/* Signals */
void (*property_changed) (gchar * property, gchar * value);
void (*item_activated) (void);
- void (*child_added) (DbusmenuMenuitem * child);
+ void (*child_added) (DbusmenuMenuitem * child, guint position);
void (*child_removed) (DbusmenuMenuitem * child);
+ void (*child_moved) (DbusmenuMenuitem * child, guint newpos, guint oldpos);
/* Virtual functions */
void (*buildxml) (GPtrArray * stringarray);
@@ -107,8 +110,10 @@ GList * dbusmenu_menuitem_take_children (DbusmenuMenuitem * mi) G_GNUC_WARN_UNUS
guint dbusmenu_menuitem_get_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent);
gboolean dbusmenu_menuitem_child_append (DbusmenuMenuitem * mi, DbusmenuMenuitem * child);
+gboolean dbusmenu_menuitem_child_prepend (DbusmenuMenuitem * mi, DbusmenuMenuitem * child);
gboolean dbusmenu_menuitem_child_delete (DbusmenuMenuitem * mi, DbusmenuMenuitem * child);
gboolean dbusmenu_menuitem_child_add_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position);
+gboolean dbusmenu_menuitem_child_reorder (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position);
DbusmenuMenuitem * dbusmenu_menuitem_child_find (DbusmenuMenuitem * mi, guint id);
DbusmenuMenuitem * dbusmenu_menuitem_find_id (DbusmenuMenuitem * mi, guint id);
@@ -118,6 +123,9 @@ gboolean dbusmenu_menuitem_property_exist (DbusmenuMenuitem * mi, const gchar *
GList * dbusmenu_menuitem_properties_list (DbusmenuMenuitem * mi) G_GNUC_WARN_UNUSED_RESULT;
GHashTable * dbusmenu_menuitem_properties_copy (DbusmenuMenuitem * mi);
+void dbusmenu_menuitem_set_root (DbusmenuMenuitem * mi, gboolean root);
+gboolean dbusmenu_menuitem_get_root (DbusmenuMenuitem * mi);
+
void dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array);
void dbusmenu_menuitem_foreach (DbusmenuMenuitem * mi, void (*func) (DbusmenuMenuitem * mi, gpointer data), gpointer data);
void dbusmenu_menuitem_activate (DbusmenuMenuitem * mi);
diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c
index 562b2d7..ab5951f 100644
--- a/libdbusmenu-glib/server.c
+++ b/libdbusmenu-glib/server.c
@@ -86,7 +86,7 @@ static void dbusmenu_server_finalize (GObject *object);
static void set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec);
static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec);
static void menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, gchar * value, DbusmenuServer * server);
-static void menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, DbusmenuServer * server);
+static void menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint pos, DbusmenuServer * server);
static void menuitem_child_removed (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, DbusmenuServer * server);
static void menuitem_signals_create (DbusmenuMenuitem * mi, gpointer data);
static void menuitem_signals_remove (DbusmenuMenuitem * mi, gpointer data);
@@ -306,7 +306,7 @@ menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, gchar * valu
}
static void
-menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, DbusmenuServer * server)
+menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint pos, DbusmenuServer * server)
{
menuitem_signals_create(child, server);
/* TODO: We probably need to group the layout update signals to make the number more reasonble. */
@@ -490,6 +490,7 @@ dbusmenu_server_set_root (DbusmenuServer * self, DbusmenuMenuitem * root)
g_value_init(&rootvalue, G_TYPE_OBJECT);
g_value_set_object(&rootvalue, root);
g_object_set_property(G_OBJECT(self), DBUSMENU_SERVER_PROP_ROOT_NODE, &rootvalue);
+ g_object_unref(G_OBJECT(root));
return;
}
diff --git a/libdbusmenu-gtk/Makefile.am b/libdbusmenu-gtk/Makefile.am
index 1e36228..c375428 100644
--- a/libdbusmenu-gtk/Makefile.am
+++ b/libdbusmenu-gtk/Makefile.am
@@ -8,10 +8,14 @@ lib_LTLIBRARIES = \
libdbusmenu_gtkincludedir=$(includedir)/libdbusmenu-0.1/libdbusmenu-gtk/
libdbusmenu_gtkinclude_HEADERS = \
- test.h
+ client.h \
+ menu.h
libdbusmenu_gtk_la_SOURCES = \
- test.c
+ client.h \
+ client.c \
+ menu.h \
+ menu.c
libdbusmenu_gtk_la_LDFLAGS = \
-version-info $(LIBDBUSMENU_CURRENT):$(LIBDBUSMENU_REVISION):$(LIBDBUSMENU_AGE) \
@@ -19,10 +23,11 @@ libdbusmenu_gtk_la_LDFLAGS = \
-export-symbols-regex "^[^_].*"
libdbusmenu_gtk_la_CFLAGS = \
- $(LIBDBUSMENU_GTK_CFLAGS)
+ $(DBUSMENUGTK_CFLAGS) -I$(srcdir)/.. -Wall -Werror
libdbusmenu_gtk_la_LIBADD = \
- $(LIBDBUSMENU_GTK_LIBS)
+ ../libdbusmenu-glib/libdbusmenu-glib.la \
+ $(DBUSMENUGTK_LIBS)
pkgconfig_DATA = dbusmenu-gtk.pc
pkgconfigdir = $(libdir)/pkgconfig
diff --git a/libdbusmenu-gtk/client.c b/libdbusmenu-gtk/client.c
new file mode 100644
index 0000000..a236123
--- /dev/null
+++ b/libdbusmenu-gtk/client.c
@@ -0,0 +1,260 @@
+/*
+A library to take the object model made consistent by libdbusmenu-glib
+and visualize it in GTK.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include "client.h"
+
+/* Prototypes */
+static void dbusmenu_gtkclient_class_init (DbusmenuGtkClientClass *klass);
+static void dbusmenu_gtkclient_init (DbusmenuGtkClient *self);
+static void dbusmenu_gtkclient_dispose (GObject *object);
+static void dbusmenu_gtkclient_finalize (GObject *object);
+static void new_menuitem (DbusmenuClient * client, DbusmenuMenuitem * mi, gpointer userdata);
+static void new_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position, DbusmenuGtkClient * gtkclient);
+static void delete_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, DbusmenuGtkClient * gtkclient);
+static void move_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint new, guint old, DbusmenuGtkClient * gtkclient);
+
+/* GObject Stuff */
+G_DEFINE_TYPE (DbusmenuGtkClient, dbusmenu_gtkclient, DBUSMENU_TYPE_CLIENT);
+
+static void
+dbusmenu_gtkclient_class_init (DbusmenuGtkClientClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = dbusmenu_gtkclient_dispose;
+ object_class->finalize = dbusmenu_gtkclient_finalize;
+
+ return;
+}
+
+static void
+dbusmenu_gtkclient_init (DbusmenuGtkClient *self)
+{
+ g_signal_connect(G_OBJECT(self), DBUSMENU_CLIENT_SIGNAL_NEW_MENUITEM, G_CALLBACK(new_menuitem), NULL);
+ return;
+}
+
+static void
+dbusmenu_gtkclient_dispose (GObject *object)
+{
+
+ G_OBJECT_CLASS (dbusmenu_gtkclient_parent_class)->dispose (object);
+ return;
+}
+
+static void
+dbusmenu_gtkclient_finalize (GObject *object)
+{
+
+ G_OBJECT_CLASS (dbusmenu_gtkclient_parent_class)->finalize (object);
+ return;
+}
+
+/* Internal Functions */
+
+static const gchar * data_menuitem = "dbusmenugtk-data-gtkmenuitem";
+static const gchar * data_menu = "dbusmenugtk-data-gtkmenu";
+
+/* This is the call back for the GTK widget for when it gets
+ clicked on by the user to send it back across the bus. */
+static gboolean
+menu_pressed_cb (GtkMenuItem * gmi, DbusmenuMenuitem * mi)
+{
+ dbusmenu_menuitem_activate(mi);
+ return TRUE;
+}
+
+/* Whenever we have a property change on a DbusmenuMenuitem
+ we need to be responsive to that. */
+static void
+menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, gchar * value, GtkMenuItem * gmi)
+{
+ if (!g_strcmp0(prop, "label")) {
+ gtk_menu_item_set_label(gmi, value);
+ gtk_widget_show(GTK_WIDGET(gmi));
+ }
+
+ return;
+}
+
+/* Call back that happens when the DbusmenuMenuitem
+ is destroyed. We're making sure to clean up everything
+ else down the pipe. */
+static void
+destoryed_dbusmenuitem_cb (gpointer udata, GObject * dbusmenuitem)
+{
+ /* g_debug("DbusmenuMenuitem was destroyed"); */
+ gtk_widget_destroy(GTK_WIDGET(udata));
+ return;
+}
+
+/* This takes a new DbusmenuMenuitem and attaches the
+ various things that we need to make it work in a
+ GTK World. */
+static void
+new_menuitem (DbusmenuClient * client, DbusmenuMenuitem * mi, gpointer userdata)
+{
+ gpointer ann_mi = g_object_get_data(G_OBJECT(mi), data_menuitem);
+ GtkMenuItem * gmi = GTK_MENU_ITEM(ann_mi);
+
+ if (gmi != NULL) {
+ /* It's possible we've already been looked at, that's
+ okay, but we can just ignore this signal then. */
+ return;
+ }
+
+ gmi = GTK_MENU_ITEM(gtk_menu_item_new());
+
+ /* Attach these two */
+ g_object_set_data(G_OBJECT(mi), data_menuitem, gmi);
+
+ /* DbusmenuMenuitem signals */
+ g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menu_prop_change_cb), gmi);
+ g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED, G_CALLBACK(new_child), client);
+ g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(delete_child), client);
+ g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(move_child), client);
+
+ /* GtkMenuitem signals */
+ g_signal_connect(G_OBJECT(gmi), "activate", G_CALLBACK(menu_pressed_cb), mi);
+
+ /* Life insurance */
+ g_object_weak_ref(G_OBJECT(mi), destoryed_dbusmenuitem_cb, gmi);
+
+ return;
+}
+
+static void
+new_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position, DbusmenuGtkClient * gtkclient)
+{
+ if (dbusmenu_menuitem_get_root(mi)) { return; }
+
+ gpointer ann_menu = g_object_get_data(G_OBJECT(mi), data_menu);
+ GtkMenu * menu = GTK_MENU(ann_menu);
+ if (menu == NULL) {
+ /* Oh, we don't have a submenu, build one! */
+ menu = GTK_MENU(gtk_menu_new());
+ g_object_set_data(G_OBJECT(mi), data_menu, menu);
+
+ GtkMenuItem * parent = dbusmenu_gtkclient_menuitem_get(gtkclient, mi);
+ gtk_menu_item_set_submenu(parent, GTK_WIDGET(menu));
+ }
+
+ GtkMenuItem * childmi = dbusmenu_gtkclient_menuitem_get(gtkclient, child);
+ gtk_menu_shell_insert(GTK_MENU_SHELL(menu), GTK_WIDGET(childmi), position);
+ gtk_widget_show(GTK_WIDGET(menu));
+
+ return;
+}
+
+static void
+delete_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, DbusmenuGtkClient * gtkclient)
+{
+ if (dbusmenu_menuitem_get_root(mi)) { return; }
+
+ if (g_list_length(dbusmenu_menuitem_get_children(mi)) == 0) {
+ gpointer ann_menu = g_object_get_data(G_OBJECT(mi), data_menu);
+ GtkMenu * menu = GTK_MENU(ann_menu);
+
+ if (menu != NULL) {
+ gtk_widget_destroy(GTK_WIDGET(menu));
+ g_object_set_data(G_OBJECT(mi), data_menu, NULL);
+ }
+ }
+
+ return;
+}
+
+static void
+move_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint new, guint old, DbusmenuGtkClient * gtkclient)
+{
+ if (dbusmenu_menuitem_get_root(mi)) { return; }
+
+ gpointer ann_menu = g_object_get_data(G_OBJECT(mi), data_menu);
+ if (ann_menu == NULL) {
+ g_warning("Moving a child when we don't have a submenu!");
+ return;
+ }
+
+ GtkMenuItem * childmi = dbusmenu_gtkclient_menuitem_get(gtkclient, child);
+ gtk_menu_reorder_child(GTK_MENU(ann_menu), GTK_WIDGET(childmi), new);
+
+ return;
+}
+
+/* Public API */
+
+/**
+ dbusmenu_gtkclient_new:
+ @dbus_name: Name of the #DbusmenuServer on DBus
+ @dbus_name: Name of the object on the #DbusmenuServer
+
+ Creates a new #DbusmenuGtkClient object and creates a #DbusmenuClient
+ that connects across DBus to a #DbusmenuServer.
+
+ Return value: A new #DbusmenuGtkClient sync'd with a server
+*/
+DbusmenuGtkClient *
+dbusmenu_gtkclient_new (gchar * dbus_name, gchar * dbus_object)
+{
+ return g_object_new(DBUSMENU_GTKCLIENT_TYPE,
+ DBUSMENU_CLIENT_PROP_DBUS_OBJECT, dbus_object,
+ DBUSMENU_CLIENT_PROP_DBUS_NAME, dbus_name,
+ NULL);
+}
+
+/**
+ dbusmenu_gtkclient_menuitem_get:
+ @client: A #DbusmenuGtkClient with the item in it.
+ @item: #DbusmenuMenuitem to get associated #GtkMenuItem on.
+
+ This grabs the #GtkMenuItem that is associated with the
+ #DbusmenuMenuitem.
+
+ Return value: The #GtkMenuItem that can be played with.
+*/
+GtkMenuItem *
+dbusmenu_gtkclient_menuitem_get (DbusmenuGtkClient * client, DbusmenuMenuitem * item)
+{
+ g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), NULL);
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(item), NULL);
+
+ GtkMenuItem * mi = GTK_MENU_ITEM(g_object_get_data(G_OBJECT(item), data_menuitem));
+ if (mi == NULL) {
+ new_menuitem(DBUSMENU_CLIENT(client), item, NULL);
+ mi = GTK_MENU_ITEM(g_object_get_data(G_OBJECT(item), data_menuitem));
+ }
+
+ return mi;
+}
+
diff --git a/libdbusmenu-gtk/client.h b/libdbusmenu-gtk/client.h
new file mode 100644
index 0000000..a549fe0
--- /dev/null
+++ b/libdbusmenu-gtk/client.h
@@ -0,0 +1,109 @@
+/*
+A library to take the object model made consistent by libdbusmenu-glib
+and visualize it in GTK.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+*/
+
+#ifndef __DBUSMENU_GTKCLIENT_H__
+#define __DBUSMENU_GTKCLIENT_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libdbusmenu-glib/client.h>
+
+G_BEGIN_DECLS
+
+#define DBUSMENU_GTKCLIENT_TYPE (dbusmenu_gtkclient_get_type ())
+#define DBUSMENU_GTKCLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_GTKCLIENT_TYPE, DbusmenuGtkClient))
+#define DBUSMENU_GTKCLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_GTKCLIENT_TYPE, DbusmenuGtkClientClass))
+#define DBUSMENU_IS_GTKCLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_GTKCLIENT_TYPE))
+#define DBUSMENU_IS_GTKCLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_GTKCLIENT_TYPE))
+#define DBUSMENU_GTKCLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_GTKCLIENT_TYPE, DbusmenuGtkClientClass))
+
+#define DBUSMENU_GTKCLIENT_SIGNAL_ROOT_CHANGED DBUSMENU_CLIENT_SIGNAL_ROOT_CHANGED
+
+/**
+ DbusmenuGtkClientClass:
+ @parent_class: #GtkMenuClass
+ @reserved1: Reserved for future use.
+ @reserved2: Reserved for future use.
+ @reserved3: Reserved for future use.
+ @reserved4: Reserved for future use.
+*/
+typedef struct _DbusmenuGtkClientClass DbusmenuGtkClientClass;
+struct _DbusmenuGtkClientClass {
+ DbusmenuClientClass parent_class;
+
+ /* Signals */
+ void (*root_changed) (DbusmenuMenuitem * newroot);
+
+ /* Reserved */
+ void (*reserved1) (void);
+ void (*reserved2) (void);
+ void (*reserved3) (void);
+ void (*reserved4) (void);
+};
+
+/**
+ DbusmenuGtkClient:
+ @parent: #GtkMenu
+*/
+typedef struct _DbusmenuGtkClient DbusmenuGtkClient;
+struct _DbusmenuGtkClient {
+ DbusmenuClient parent;
+};
+
+GType dbusmenu_gtkclient_get_type (void);
+DbusmenuGtkClient * dbusmenu_gtkclient_new (gchar * dbus_name, gchar * dbus_object);
+GtkMenuItem * dbusmenu_gtkclient_menuitem_get (DbusmenuGtkClient * client, DbusmenuMenuitem * item);
+
+/**
+ SECTION:gtkmenu
+ @short_description: A GTK Menu Object that syncronizes over DBus
+ @stability: Unstable
+ @include: libdbusmenu-gtk/menu.h
+
+ In general, this is just a #GtkMenu, why else would you care? Oh,
+ because this menu is created by someone else on a server that exists
+ on the other side of DBus. You need a #DbusmenuServer to be able
+ push the data into this menu.
+
+ The first thing you need to know is how to find that #DbusmenuServer
+ on DBus. This involves both the DBus name and the DBus object that
+ the menu interface can be found on. Those two value should be set
+ when creating the object using dbusmenu_gtkmenu_new(). They are then
+ stored on two properties #DbusmenuGtkClient:dbus-name and #DbusmenuGtkClient:dbus-object.
+
+ After creation the #DbusmenuGtkClient it will continue to keep in
+ synchronization with the #DbusmenuServer object across Dbus. If the
+ number of entries change, the menus change, if they change thier
+ properties change, they update in the items. All of this should
+ be handled transparently to the user of this object.
+
+ TODO: Document properties.
+*/
+G_END_DECLS
+
+#endif
diff --git a/libdbusmenu-gtk/menu.c b/libdbusmenu-gtk/menu.c
new file mode 100644
index 0000000..bc7458c
--- /dev/null
+++ b/libdbusmenu-gtk/menu.c
@@ -0,0 +1,281 @@
+/*
+A library to take the object model made consistent by libdbusmenu-glib
+and visualize it in GTK.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include "menu.h"
+#include "libdbusmenu-glib/client.h"
+#include "client.h"
+
+/* Properties */
+enum {
+ PROP_0,
+ PROP_DBUSOBJECT,
+ PROP_DBUSNAME
+};
+
+/* Private */
+typedef struct _DbusmenuGtkMenuPrivate DbusmenuGtkMenuPrivate;
+struct _DbusmenuGtkMenuPrivate {
+ DbusmenuGtkClient * client;
+
+ gchar * dbus_object;
+ gchar * dbus_name;
+};
+
+#define DBUSMENU_GTKMENU_GET_PRIVATE(o) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_GTKMENU_TYPE, DbusmenuGtkMenuPrivate))
+
+/* Prototypes */
+static void dbusmenu_gtkmenu_class_init (DbusmenuGtkMenuClass *klass);
+static void dbusmenu_gtkmenu_init (DbusmenuGtkMenu *self);
+static void dbusmenu_gtkmenu_dispose (GObject *object);
+static void dbusmenu_gtkmenu_finalize (GObject *object);
+static void set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec);
+static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec);
+/* Internal */
+static void build_client (DbusmenuGtkMenu * self);
+
+/* GObject Stuff */
+G_DEFINE_TYPE (DbusmenuGtkMenu, dbusmenu_gtkmenu, GTK_TYPE_MENU);
+
+static void
+dbusmenu_gtkmenu_class_init (DbusmenuGtkMenuClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (DbusmenuGtkMenuPrivate));
+
+ object_class->dispose = dbusmenu_gtkmenu_dispose;
+ object_class->finalize = dbusmenu_gtkmenu_finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ g_object_class_install_property (object_class, PROP_DBUSOBJECT,
+ g_param_spec_string(DBUSMENU_CLIENT_PROP_DBUS_OBJECT, "DBus Object we represent",
+ "The Object on the client that we're getting our data from.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_DBUSNAME,
+ g_param_spec_string(DBUSMENU_CLIENT_PROP_DBUS_NAME, "DBus Client we connect to",
+ "Name of the DBus client we're connecting to.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ return;
+}
+
+static void
+dbusmenu_gtkmenu_init (DbusmenuGtkMenu *self)
+{
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(self);
+
+ priv->client = NULL;
+
+ priv->dbus_object = NULL;
+ priv->dbus_name = NULL;
+
+ return;
+}
+
+static void
+dbusmenu_gtkmenu_dispose (GObject *object)
+{
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(object);
+
+ if (priv->client != NULL) {
+ g_object_unref(G_OBJECT(priv->client));
+ priv->client = NULL;
+ }
+
+ G_OBJECT_CLASS (dbusmenu_gtkmenu_parent_class)->dispose (object);
+ return;
+}
+
+static void
+dbusmenu_gtkmenu_finalize (GObject *object)
+{
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(object);
+
+ g_free(priv->dbus_object);
+ priv->dbus_object = NULL;
+
+ g_free(priv->dbus_name);
+ priv->dbus_name = NULL;
+
+ G_OBJECT_CLASS (dbusmenu_gtkmenu_parent_class)->finalize (object);
+ return;
+}
+
+static void
+set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
+{
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(obj);
+
+ switch (id) {
+ case PROP_DBUSNAME:
+ priv->dbus_name = g_value_dup_string(value);
+ if (priv->dbus_name != NULL && priv->dbus_object != NULL) {
+ build_client(DBUSMENU_GTKMENU(obj));
+ }
+ break;
+ case PROP_DBUSOBJECT:
+ priv->dbus_object = g_value_dup_string(value);
+ if (priv->dbus_name != NULL && priv->dbus_object != NULL) {
+ build_client(DBUSMENU_GTKMENU(obj));
+ }
+ break;
+ default:
+ g_warning("Unknown property %d.", id);
+ return;
+ }
+
+ return;
+}
+
+static void
+get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
+{
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(obj);
+
+ switch (id) {
+ case PROP_DBUSNAME:
+ g_value_set_string(value, priv->dbus_name);
+ break;
+ case PROP_DBUSOBJECT:
+ g_value_set_string(value, priv->dbus_object);
+ break;
+ default:
+ g_warning("Unknown property %d.", id);
+ return;
+ }
+
+ return;
+}
+
+/* Internal Functions */
+
+static void
+root_child_added (DbusmenuMenuitem * root, DbusmenuMenuitem * child, guint position, DbusmenuGtkMenu * menu)
+{
+ g_debug("Root new child");
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(menu);
+ gtk_menu_shell_insert(GTK_MENU_SHELL(menu), GTK_WIDGET(dbusmenu_gtkclient_menuitem_get(priv->client, child)), position);
+ gtk_widget_show(GTK_WIDGET(menu));
+ return;
+}
+
+static void
+root_child_moved (DbusmenuMenuitem * root, DbusmenuMenuitem * child, guint newposition, guint oldposition, DbusmenuGtkMenu * menu)
+{
+ g_debug("Root child moved");
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(menu);
+ gtk_menu_reorder_child(GTK_MENU(menu), GTK_WIDGET(dbusmenu_gtkclient_menuitem_get(priv->client, child)), newposition);
+ return;
+}
+
+static void
+root_child_delete (DbusmenuMenuitem * root, DbusmenuMenuitem * child, DbusmenuGtkMenu * menu)
+{
+ g_debug("Root child deleted");
+ if (g_list_length(dbusmenu_menuitem_get_children(root)) == 0) {
+ gtk_widget_hide(GTK_WIDGET(menu));
+ }
+ return;
+}
+
+static void
+root_changed (DbusmenuGtkClient * client, DbusmenuMenuitem * newroot, DbusmenuGtkMenu * menu) {
+ if (newroot == NULL) {
+ gtk_widget_hide(GTK_WIDGET(menu));
+ return;
+ }
+
+ g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED, G_CALLBACK(root_child_added), menu);
+ g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(root_child_moved), menu);
+ g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(root_child_delete), menu);
+
+ GList * child = NULL;
+ guint count = 0;
+ for (child = dbusmenu_menuitem_get_children(newroot); child != NULL; child = g_list_next(child)) {
+ gtk_menu_append(menu, GTK_WIDGET(dbusmenu_gtkclient_menuitem_get(client, child->data)));
+ count++;
+ }
+
+ if (count > 0) {
+ gtk_widget_show(GTK_WIDGET(menu));
+ } else {
+ gtk_widget_hide(GTK_WIDGET(menu));
+ }
+
+ return;
+}
+
+/* Builds the client and connects all of the signals
+ up for it so that it's happy-happy */
+static void
+build_client (DbusmenuGtkMenu * self)
+{
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(self);
+
+ if (priv->client == NULL) {
+ priv->client = dbusmenu_gtkclient_new(priv->dbus_name, priv->dbus_object);
+
+ /* Register for layout changes, this should come after the
+ creation of the client pulls it from DBus */
+ g_signal_connect(G_OBJECT(priv->client), DBUSMENU_GTKCLIENT_SIGNAL_ROOT_CHANGED, G_CALLBACK(root_changed), self);
+ }
+
+ return;
+}
+
+/* Public API */
+
+/**
+ dbusmenu_gtkmenu_new:
+ @dbus_name: Name of the #DbusmenuServer on DBus
+ @dbus_name: Name of the object on the #DbusmenuServer
+
+ Creates a new #DbusmenuGtkMenu object and creates a #DbusmenuClient
+ that connects across DBus to a #DbusmenuServer.
+
+ Return value: A new #DbusmenuGtkMenu sync'd with a server
+*/
+DbusmenuGtkMenu *
+dbusmenu_gtkmenu_new (gchar * dbus_name, gchar * dbus_object)
+{
+ return g_object_new(DBUSMENU_GTKMENU_TYPE,
+ DBUSMENU_CLIENT_PROP_DBUS_OBJECT, dbus_object,
+ DBUSMENU_CLIENT_PROP_DBUS_NAME, dbus_name,
+ NULL);
+}
+
diff --git a/libdbusmenu-gtk/menu.h b/libdbusmenu-gtk/menu.h
new file mode 100644
index 0000000..73804c5
--- /dev/null
+++ b/libdbusmenu-gtk/menu.h
@@ -0,0 +1,102 @@
+/*
+A library to take the object model made consistent by libdbusmenu-glib
+and visualize it in GTK.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+*/
+
+#ifndef __DBUSMENU_GTKMENU_H__
+#define __DBUSMENU_GTKMENU_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define DBUSMENU_GTKMENU_TYPE (dbusmenu_gtkmenu_get_type ())
+#define DBUSMENU_GTKMENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_GTKMENU_TYPE, DbusmenuGtkMenu))
+#define DBUSMENU_GTKMENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_GTKMENU_TYPE, DbusmenuGtkMenuClass))
+#define DBUSMENU_IS_GTKMENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_GTKMENU_TYPE))
+#define DBUSMENU_IS_GTKMENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_GTKMENU_TYPE))
+#define DBUSMENU_GTKMENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_GTKMENU_TYPE, DbusmenuGtkMenuClass))
+
+/**
+ DbusmenuGtkMenuClass:
+ @parent_class: #GtkMenuClass
+ @reserved1: Reserved for future use.
+ @reserved2: Reserved for future use.
+ @reserved3: Reserved for future use.
+ @reserved4: Reserved for future use.
+*/
+typedef struct _DbusmenuGtkMenuClass DbusmenuGtkMenuClass;
+struct _DbusmenuGtkMenuClass {
+ GtkMenuClass parent_class;
+
+ /* Reserved */
+ void (*reserved1) (void);
+ void (*reserved2) (void);
+ void (*reserved3) (void);
+ void (*reserved4) (void);
+};
+
+/**
+ DbusmenuGtkMenu:
+ @parent: #GtkMenu
+*/
+typedef struct _DbusmenuGtkMenu DbusmenuGtkMenu;
+struct _DbusmenuGtkMenu {
+ GtkMenu parent;
+};
+
+GType dbusmenu_gtkmenu_get_type (void);
+DbusmenuGtkMenu * dbusmenu_gtkmenu_new (gchar * dbus_name, gchar * dbus_object);
+
+/**
+ SECTION:gtkmenu
+ @short_description: A GTK Menu Object that syncronizes over DBus
+ @stability: Unstable
+ @include: libdbusmenu-gtk/menu.h
+
+ In general, this is just a #GtkMenu, why else would you care? Oh,
+ because this menu is created by someone else on a server that exists
+ on the other side of DBus. You need a #DbusmenuServer to be able
+ push the data into this menu.
+
+ The first thing you need to know is how to find that #DbusmenuServer
+ on DBus. This involves both the DBus name and the DBus object that
+ the menu interface can be found on. Those two value should be set
+ when creating the object using dbusmenu_gtkmenu_new(). They are then
+ stored on two properties #DbusmenuGtkMenu:dbus-name and #DbusmenuGtkMenu:dbus-object.
+
+ After creation the #DbusmenuGtkMenu it will continue to keep in
+ synchronization with the #DbusmenuServer object across Dbus. If the
+ number of entries change, the menus change, if they change thier
+ properties change, they update in the items. All of this should
+ be handled transparently to the user of this object.
+
+ TODO: Document properties.
+*/
+G_END_DECLS
+
+#endif
diff --git a/libdbusmenu-gtk/test.c b/libdbusmenu-gtk/test.c
deleted file mode 100644
index 8ebb3f7..0000000
--- a/libdbusmenu-gtk/test.c
+++ /dev/null
@@ -1,4 +0,0 @@
-
-void mysymbol (void) {
- return;
-}
diff --git a/libdbusmenu-gtk/test.h b/libdbusmenu-gtk/test.h
deleted file mode 100644
index ad000af..0000000
--- a/libdbusmenu-gtk/test.h
+++ /dev/null
@@ -1,2 +0,0 @@
-
-void mysymbol (void);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 1f21141..8c47a93 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,15 +1,20 @@
+SUBDIRS = dbusmenu-gtk
+
check: tests
DBUS_RUNNER=dbus-test-runner --dbus-config /usr/share/dbus-test-runner/session.conf
-tests: test-glib-layout test-glib-properties
+tests: test-glib-layout test-glib-properties test-gtk-label test-glib-simple-items
libexec_PROGRAMS = \
glib-server-nomenu \
test-glib-layout-client \
test-glib-layout-server \
test-glib-properties-client \
- test-glib-properties-server
+ test-glib-properties-server \
+ test-gtk-label-client \
+ test-gtk-label-server \
+ test-glib-simple-items
glib_server_nomenu_SOURCES = \
glib-server-nomenu.c
@@ -81,10 +86,58 @@ test_glib_properties_client_LDADD = \
$(DBUSMENUGLIB_LIBS)
+test_glib_simple_items_SOURCES = \
+ test-glib-simple-items.c
+
+test_glib_simple_items_CFLAGS = \
+ -I $(srcdir)/.. \
+ $(DBUSMENUGLIB_CFLAGS) -Wall -Werror
+
+test_glib_simple_items_LDADD = \
+ ../libdbusmenu-glib/libdbusmenu-glib.la \
+ $(DBUSMENUGLIB_LIBS)
+
+
+test-gtk-label: test-gtk-label-client test-gtk-label-server test-gtk-label.json
+ $(DBUS_RUNNER) --task ./test-gtk-label-client --task-name Client --task ./test-gtk-label-server --parameter $(srcdir)/test-gtk-label.json --task-name Server --ignore-return
+
+test_gtk_label_server_SOURCES = \
+ test-gtk-label-server.c
+
+test_gtk_label_server_CFLAGS = \
+ -I $(srcdir)/.. \
+ $(DBUSMENUGTK_CFLAGS) \
+ $(DBUSMENUTESTS_CFLAGS) \
+ $(DBUSMENUGLIB_CFLAGS) -Wall -Werror
+
+test_gtk_label_server_LDADD = \
+ ../libdbusmenu-glib/libdbusmenu-glib.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ $(DBUSMENUGTK_LIBS) \
+ $(DBUSMENUTESTS_LIBS)
+
+test_gtk_label_client_SOURCES = \
+ test-gtk-label-client.c
+
+test_gtk_label_client_CFLAGS = \
+ -I $(srcdir)/.. \
+ $(DBUSMENUGTK_CFLAGS) \
+ $(DBUSMENUTESTS_CFLAGS) \
+ $(DBUSMENUGLIB_CFLAGS) -Wall -Werror
+
+test_gtk_label_client_LDADD = \
+ ../libdbusmenu-glib/libdbusmenu-glib.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ $(DBUSMENUGTK_LIBS) \
+ $(DBUSMENUTESTS_LIBS)
+
+
examplesdir = $(docdir)/examples/
examples_DATA = \
$(glib_server_nomenu_SOURCES)
-EXTRA_DIST = $(examples_DATA)
+EXTRA_DIST = \
+ $(examples_DATA) \
+ test-gtk-label.json
diff --git a/tests/dbusmenu-gtk/Makefile.am b/tests/dbusmenu-gtk/Makefile.am
new file mode 100644
index 0000000..6acbbe0
--- /dev/null
+++ b/tests/dbusmenu-gtk/Makefile.am
@@ -0,0 +1,43 @@
+
+check: tests
+
+tests: mago
+
+mago: dbusmenu.xml dbusmenu.py
+ PYTHONPATH=$(builddir) mago -f $(builddir)/dbusmenu.xml -t $(builddir)/mago.results
+
+dbusmenu.xml: dbusmenu.xml.in
+ sed -e "s|\@srcdir\@|$(srcdir)|" $< > $@
+
+dbusmenu.py: dbusmenu.py.in
+ sed -e "s|\@srcdir\@|$(srcdir)|" $< > $@
+
+EXTRA_DIST = \
+ dbusmenu.xml.in \
+ dbusmenu.py.in \
+ dbusMenuTest \
+ data/blank_label_2levels.json \
+ data/blank_label.json \
+ data/blank_submenus.json \
+ data/dynamic.json \
+ data/long_label.json \
+ data/no_id.json \
+ data/no_label.json \
+ data/sameid_submenus_diff_sizes.json \
+ data/sameid_submenus.json \
+ data/sameid_top_and_submenus.json \
+ data/sameid_topmenu.json \
+ data/several_submenus.json \
+ data/several_submenus_recursive.json \
+ data/several_submenus_utf8.json \
+ data/static.json \
+ data/test-gtk-label.json
+
+CLEANFILES = \
+ dbusmenu.xml \
+ dbusmenu.pyc \
+ dbusmenu.py
+
+distclean-local:
+ -rm -rf $(builddir)/mago.results
+
diff --git a/tests/dbusmenu-gtk/data/blank_label.json b/tests/dbusmenu-gtk/data/blank_label.json
new file mode 100644
index 0000000..d62d49b
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/blank_label.json
@@ -0,0 +1,26 @@
+[
+ {"id": 1,
+ "label": "",
+ "submenu": [
+ {"id": 30,
+ "label": ""},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ }]
diff --git a/tests/dbusmenu-gtk/data/blank_label_2levels.json b/tests/dbusmenu-gtk/data/blank_label_2levels.json
new file mode 100644
index 0000000..e3335a5
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/blank_label_2levels.json
@@ -0,0 +1,82 @@
+[
+ {"id": 4,
+ "label": "",
+ "submenu": [
+ {"id": 5,
+ "label": "",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"},
+ {"id": 13,
+ "label": "value13"},
+ {"id": 14,
+ "label": "value14"},
+ {"id": 15,
+ "label": "value15"},
+ {"id": 16,
+ "label": "value16"},
+ {"id": 17,
+ "label": "value17"},
+ {"id": 18,
+ "label": "value18"},
+ {"id": 19,
+ "label": "value19"}
+ ]
+ },
+ {"id": 6,
+ "label": "",
+ "submenu": [
+ {"id": 20,
+ "label": "value20"},
+ {"id": 21,
+ "label": "value21"},
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 7,
+ "label": "",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ ]
+ }
+]
diff --git a/tests/dbusmenu-gtk/data/blank_submenus.json b/tests/dbusmenu-gtk/data/blank_submenus.json
new file mode 100644
index 0000000..2eaff27
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/blank_submenus.json
@@ -0,0 +1,48 @@
+[
+ {"id": 4,
+ "label": "value2",
+ "submenu": [
+ {"id": 5,
+ "label": "value5",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"}
+ ]
+ },
+ {"id": 6,
+ "label": "value6",
+ "submenu": [
+ ]
+ },
+ {"id": 7,
+ "label": "value7",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ ]
+ }
+]
diff --git a/tests/dbusmenu-gtk/data/dynamic.json b/tests/dbusmenu-gtk/data/dynamic.json
new file mode 100644
index 0000000..efa7a75
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/dynamic.json
@@ -0,0 +1,158 @@
+[
+ {"id": 1,
+ "label": "value1",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ {"id": 1,
+ "label": "value2",
+ "submenu": [
+ {"id": 20,
+ "label": "value20"},
+ {"id": 21,
+ "label": "value21"},
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 1,
+ "label": "a super long label that is really of unreasonable length but we should make sure it makes it across the bus",
+ "not.a.value": "A useless value",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"},
+ {"id": 13,
+ "label": "value13"},
+ {"id": 14,
+ "label": "value14"},
+ {"id": 15,
+ "label": "value15"},
+ {"id": 16,
+ "label": "value16"},
+ {"id": 17,
+ "label": "value17"},
+ {"id": 18,
+ "label": "value18"},
+ {"id": 19,
+ "label": "value19"}
+ ]
+ },
+ {"id": 1,
+ "label": "value2",
+ "submenu": [
+ {"id": 5,
+ "label": "value5",
+ "submenu": [
+ {"id": 10,
+ "label": "value100000"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"},
+ {"id": 13,
+ "label": "value13"},
+ {"id": 14,
+ "label": "value14"},
+ {"id": 15,
+ "label": "value15"},
+ {"id": 16,
+ "label": "value16"},
+ {"id": 17,
+ "label": "value17"},
+ {"id": 18,
+ "label": "value18"},
+ {"id": 19,
+ "label": "value19"}
+ ]
+ },
+ {"id": 6,
+ "label": "value6",
+ "submenu": [
+ {"id": 20,
+ "label": "value20"},
+ {"id": 21,
+ "label": "value21"},
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 7,
+ "label": "value7",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ ]
+ }
+]
diff --git a/tests/dbusmenu-gtk/data/long_label.json b/tests/dbusmenu-gtk/data/long_label.json
new file mode 100644
index 0000000..7cacb7f
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/long_label.json
@@ -0,0 +1,82 @@
+[
+ {"id": 4,
+ "label": "",
+ "submenu": [
+ {"id": 5,
+ "label": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"},
+ {"id": 13,
+ "label": "value13"},
+ {"id": 14,
+ "label": "value14"},
+ {"id": 15,
+ "label": "value15"},
+ {"id": 16,
+ "label": "value16"},
+ {"id": 17,
+ "label": "value17"},
+ {"id": 18,
+ "label": "value18"},
+ {"id": 19,
+ "label": "value19"}
+ ]
+ },
+ {"id": 6,
+ "label": "",
+ "submenu": [
+ {"id": 20,
+ "label": "value20"},
+ {"id": 21,
+ "label": "value21"},
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 7,
+ "label": "",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ ]
+ }
+]
diff --git a/tests/dbusmenu-gtk/data/no_id.json b/tests/dbusmenu-gtk/data/no_id.json
new file mode 100644
index 0000000..201408a
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/no_id.json
@@ -0,0 +1,24 @@
+[
+ { "label": "I dont have an id",
+ "submenu": [
+ {"label": "30", "submenu": [{"label": "submenu_from_no_id"}]},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ }]
diff --git a/tests/dbusmenu-gtk/data/no_label.json b/tests/dbusmenu-gtk/data/no_label.json
new file mode 100644
index 0000000..c651cbc
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/no_label.json
@@ -0,0 +1,24 @@
+[
+ {"id": 1, "label": "hey",
+ "submenu": [
+ {"id": 30, "submenu": [{"id":500, "label": "submenu_from_no_label"}]},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ }]
diff --git a/tests/dbusmenu-gtk/data/sameid_submenus.json b/tests/dbusmenu-gtk/data/sameid_submenus.json
new file mode 100644
index 0000000..a61c92b
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/sameid_submenus.json
@@ -0,0 +1,82 @@
+[
+ {"id": 4,
+ "label": "value2",
+ "submenu": [
+ {"id": 6,
+ "label": "value5",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"},
+ {"id": 13,
+ "label": "value13"},
+ {"id": 14,
+ "label": "value14"},
+ {"id": 15,
+ "label": "value15"},
+ {"id": 16,
+ "label": "value16"},
+ {"id": 17,
+ "label": "value17"},
+ {"id": 18,
+ "label": "value18"},
+ {"id": 19,
+ "label": "value19"}
+ ]
+ },
+ {"id": 6,
+ "label": "value6",
+ "submenu": [
+ {"id": 20,
+ "label": "value20"},
+ {"id": 21,
+ "label": "value21"},
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 6,
+ "label": "value7",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ ]
+ }
+]
diff --git a/tests/dbusmenu-gtk/data/sameid_submenus_diff_sizes.json b/tests/dbusmenu-gtk/data/sameid_submenus_diff_sizes.json
new file mode 100644
index 0000000..d8f36c6
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/sameid_submenus_diff_sizes.json
@@ -0,0 +1,64 @@
+[
+ {"id": 4,
+ "label": "value2",
+ "submenu": [
+ {"id": 6,
+ "label": "value5",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"}
+ ]
+ },
+ {"id": 6,
+ "label": "value6",
+ "submenu": [
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 6,
+ "label": "value7",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ ]
+ }
+]
diff --git a/tests/dbusmenu-gtk/data/sameid_top_and_submenus.json b/tests/dbusmenu-gtk/data/sameid_top_and_submenus.json
new file mode 100644
index 0000000..102720f
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/sameid_top_and_submenus.json
@@ -0,0 +1,82 @@
+[
+ {"id": 4,
+ "label": "value2",
+ "submenu": [
+ {"id": 4,
+ "label": "value5",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"},
+ {"id": 13,
+ "label": "value13"},
+ {"id": 14,
+ "label": "value14"},
+ {"id": 15,
+ "label": "value15"},
+ {"id": 16,
+ "label": "value16"},
+ {"id": 17,
+ "label": "value17"},
+ {"id": 18,
+ "label": "value18"},
+ {"id": 19,
+ "label": "value19"}
+ ]
+ },
+ {"id": 4,
+ "label": "value6",
+ "submenu": [
+ {"id": 20,
+ "label": "value20"},
+ {"id": 21,
+ "label": "value21"},
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 4,
+ "label": "value7",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ ]
+ }
+]
diff --git a/tests/dbusmenu-gtk/data/sameid_topmenu.json b/tests/dbusmenu-gtk/data/sameid_topmenu.json
new file mode 100644
index 0000000..ee9d97f
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/sameid_topmenu.json
@@ -0,0 +1,158 @@
+[
+ {"id": 1,
+ "label": "value1",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ {"id": 1,
+ "label": "value2",
+ "submenu": [
+ {"id": 20,
+ "label": "value20"},
+ {"id": 21,
+ "label": "value21"},
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 1,
+ "label": "a super long label that is really of unreasonable length but we should make sure it makes it across the bus",
+ "not.a.value": "A useless value",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"},
+ {"id": 13,
+ "label": "value13"},
+ {"id": 14,
+ "label": "value14"},
+ {"id": 15,
+ "label": "value15"},
+ {"id": 16,
+ "label": "value16"},
+ {"id": 17,
+ "label": "value17"},
+ {"id": 18,
+ "label": "value18"},
+ {"id": 19,
+ "label": "value19"}
+ ]
+ },
+ {"id": 1,
+ "label": "value2",
+ "submenu": [
+ {"id": 5,
+ "label": "value5",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"},
+ {"id": 13,
+ "label": "value13"},
+ {"id": 14,
+ "label": "value14"},
+ {"id": 15,
+ "label": "value15"},
+ {"id": 16,
+ "label": "value16"},
+ {"id": 17,
+ "label": "value17"},
+ {"id": 18,
+ "label": "value18"},
+ {"id": 19,
+ "label": "value19"}
+ ]
+ },
+ {"id": 6,
+ "label": "value6",
+ "submenu": [
+ {"id": 20,
+ "label": "value20"},
+ {"id": 21,
+ "label": "value21"},
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 7,
+ "label": "value7",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ ]
+ }
+]
diff --git a/tests/dbusmenu-gtk/data/several_submenus.json b/tests/dbusmenu-gtk/data/several_submenus.json
new file mode 100644
index 0000000..3b9bb98
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/several_submenus.json
@@ -0,0 +1,51 @@
+[
+ {"id": 4,
+ "label": "value2",
+ "submenu": [
+ {"id": 5,
+ "label": "value5",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"}
+ ]
+ },
+ {"id": 6,
+ "label": "value6",
+ "submenu": [
+ ]
+ },
+ {"id": 7,
+ "label": "value7",
+ "submenu": [
+ {"id": 30,
+ "label": "value30",
+ "submenu": [
+ {"id": 4, "label": "value99", "nonsenses": "useless"}
+ ]},
+ {"id": 31,
+ "label": "alue31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ ]
+ }
+]
diff --git a/tests/dbusmenu-gtk/data/several_submenus_recursive.json b/tests/dbusmenu-gtk/data/several_submenus_recursive.json
new file mode 100644
index 0000000..1320ef4
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/several_submenus_recursive.json
@@ -0,0 +1,49 @@
+[
+ {"id": 4,
+ "label": "value2",
+ "submenu": [
+ {"id": 5,
+ "label": "value5",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"}
+ ]
+ },
+ {"id": 6,
+ "label": "value6",
+ "submenu": [
+ ]
+ },
+ {"id": 7,
+ "label": "value7",
+ "submenu": [
+ {"id": 31,
+ "label": "value30",
+ "submenu": [{"id": 301, "label": "value301", "submenu": [{"id": 3001, "label": "value3001", "submenu": [{"id": 5001, "label": "value5001", "submenu": [{"id": 7001, "label": "value7001"}]}]}]}, {"id": 88, "label": "value88"}]},
+ {"id": 30,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ ]
+ }
+]
diff --git a/tests/dbusmenu-gtk/data/several_submenus_utf8.json b/tests/dbusmenu-gtk/data/several_submenus_utf8.json
new file mode 100644
index 0000000..b7b07f9
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/several_submenus_utf8.json
@@ -0,0 +1,51 @@
+[
+ {"id": 4,
+ "label": "value2ñ",
+ "submenu": [
+ {"id": 5,
+ "label": "value5ス",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"}
+ ]
+ },
+ {"id": 6,
+ "label": "value6ñ",
+ "submenu": [
+ ]
+ },
+ {"id": 7,
+ "label": "value7",
+ "submenu": [
+ {"id": 30,
+ "label": "value30",
+ "submenu": [
+ {"id": 50, "label": "value9", "nonsenses": "useless"}
+ ]},
+ {"id": 31,
+ "label": "alue31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ ]
+ }
+]
diff --git a/tests/dbusmenu-gtk/data/static.json b/tests/dbusmenu-gtk/data/static.json
new file mode 100644
index 0000000..dec591a
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/static.json
@@ -0,0 +1,26 @@
+[
+ {"id": 1,
+ "label": "value1",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ }]
diff --git a/tests/dbusmenu-gtk/data/test-gtk-label.json b/tests/dbusmenu-gtk/data/test-gtk-label.json
new file mode 100644
index 0000000..64c1386
--- /dev/null
+++ b/tests/dbusmenu-gtk/data/test-gtk-label.json
@@ -0,0 +1,158 @@
+[
+ {"id": 1,
+ "label": "value1",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ {"id": 2,
+ "label": "value2",
+ "submenu": [
+ {"id": 20,
+ "label": "value20"},
+ {"id": 21,
+ "label": "value21"},
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 3,
+ "label": "a super long label that is really of unreasonable length but we should make sure it makes it across the bus",
+ "not.a.value": "A useless value",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"},
+ {"id": 13,
+ "label": "value13"},
+ {"id": 14,
+ "label": "value14"},
+ {"id": 15,
+ "label": "value15"},
+ {"id": 16,
+ "label": "value16"},
+ {"id": 17,
+ "label": "value17"},
+ {"id": 18,
+ "label": "value18"},
+ {"id": 19,
+ "label": "value19"}
+ ]
+ },
+ {"id": 4,
+ "label": "value2",
+ "submenu": [
+ {"id": 5,
+ "label": "value5",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"},
+ {"id": 13,
+ "label": "value13"},
+ {"id": 14,
+ "label": "value14"},
+ {"id": 15,
+ "label": "value15"},
+ {"id": 16,
+ "label": "value16"},
+ {"id": 17,
+ "label": "value17"},
+ {"id": 18,
+ "label": "value18"},
+ {"id": 19,
+ "label": "value19"}
+ ]
+ },
+ {"id": 6,
+ "label": "value6",
+ "submenu": [
+ {"id": 20,
+ "label": "value20"},
+ {"id": 21,
+ "label": "value21"},
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 7,
+ "label": "value7",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ ]
+ }
+]
diff --git a/tests/dbusmenu-gtk/dbusMenuTest b/tests/dbusmenu-gtk/dbusMenuTest
new file mode 100755
index 0000000..46c5f67
--- /dev/null
+++ b/tests/dbusmenu-gtk/dbusMenuTest
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+export NO_GAIL=0
+export NO_AT_BRIDGE=0
+
+dbus-test-runner --dbus-config /usr/share/dbus-test-runner/session.conf --task ../test-gtk-label-client --task-name Client --task ../test-gtk-label-server --parameter ./$1 --task-name Server --ignore-return
+
+
diff --git a/tests/dbusmenu-gtk/dbusmenu.py.in b/tests/dbusmenu-gtk/dbusmenu.py.in
new file mode 100644
index 0000000..ce159e1
--- /dev/null
+++ b/tests/dbusmenu-gtk/dbusmenu.py.in
@@ -0,0 +1,71 @@
+from mago.test_suite.main import SingleApplicationTestSuite
+from mago.application.main import Application
+
+import ldtp, ooldtp, ldtputils
+
+class DbusMenuGtkApp():
+ LAUNCHER = "@srcdir@/dbusMenuTest"
+ WINDOW = "frmlibdbusmenu-gtktest"
+
+ def open(self, menu_schema=''):
+ ldtp.launchapp(self.LAUNCHER, [menu_schema])
+
+ def menu_exists(self, menu=''):
+ app = ooldtp.context(self.WINDOW)
+
+ if menu == '':
+ menu = "mnu1"
+
+ try:
+ component = app.getchild(menu)
+ except ldtp.LdtpExecutionError:
+ return False
+
+ return True
+
+ def get_submenus(self, menu=''):
+ app = ooldtp.context(self.WINDOW)
+
+ if menu == '':
+ menu = "mnu1"
+
+ component = app.getchild(menu)
+
+ try:
+ submenus = component.listsubmenus()
+ except ldtp.LdtpExecutionError:
+ return ""
+
+ return submenus
+
+class DbusMenuGtkTest(SingleApplicationTestSuite):
+ APPLICATION_FACTORY = DbusMenuGtkApp
+
+ def cleanup(self):
+ ldtp.waittillguinotexist(self.application.WINDOW, guiTimeOut=70)
+
+ def teardown(self):
+ ldtp.waittillguinotexist(self.application.WINDOW, guiTimeOut=70)
+
+ def testStaticMenu(self, menu_schema, menu_item='', notexists=''):
+ self.application.open(menu_schema)
+ ldtp.waittillguiexist(self.application.WINDOW)
+
+ if notexists == "True":
+ if self.application.menu_exists(menu_item):
+ raise AssertionError("The menu item exists")
+ else:
+ if not self.application.menu_exists(menu_item):
+ raise AssertionError("The menu item does not exists")
+
+
+ def testSubmenus(self, menu_schema, menu_item='', submenus=''):
+ self.application.open(menu_schema)
+ ldtp.waittillguiexist(self.application.WINDOW)
+
+ if submenus != self.application.get_submenus(menu_item):
+ raise AssertionError("The submenus are different")
+
+
+
+
diff --git a/tests/dbusmenu-gtk/dbusmenu.xml.in b/tests/dbusmenu-gtk/dbusmenu.xml.in
new file mode 100644
index 0000000..b49ee5e
--- /dev/null
+++ b/tests/dbusmenu-gtk/dbusmenu.xml.in
@@ -0,0 +1,89 @@
+<?xml version="1.0"?>
+<suite name="dbusmenu gtk test suite">
+ <class>dbusmenu.DbusMenuGtkTest</class>
+ <description>
+ Test libdbusmenu-gtk.
+ </description>
+ <case name="Static Test">
+ <method>testStaticMenu</method>
+ <description>Simple check for a menu </description>
+ <args>
+ <menu_schema>@srcdir@/data/static.json</menu_schema>
+ <menu_item>value39</menu_item>
+ </args>
+ </case>
+ <case name="Blank label">
+ <method>testStaticMenu</method>
+ <description>Blank Label</description>
+ <args>
+ <menu_schema>@srcdir@/data/blank_label.json</menu_schema>
+ <menu_item></menu_item>
+ </args>
+ </case>
+ <case name="Blank Submenus">
+ <method>testSubmenus</method>
+ <description>Blank Submenus</description>
+ <args>
+ <menu_schema>@srcdir@/data/blank_submenus.json</menu_schema>
+ <menu_item>value6</menu_item>
+ <submenus></submenus>
+ </args>
+ </case>
+ <case name="Long Label">
+ <method>testStaticMenu</method>
+ <description>Really Long Label (1000 chars)</description>
+ <args>
+ <menu_schema>@srcdir@/data/long_label.json</menu_schema>
+ <menu_item>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</menu_item>
+ </args>
+ </case>
+ <case name="No Id">
+ <method>testStaticMenu</method>
+ <description>Search for a submenu that comes from a menu without ID</description>
+ <args>
+ <menu_schema>@srcdir@/data/no_id.json</menu_schema>
+ <menu_item>submenu_from_no_id</menu_item>
+ </args>
+ </case>
+ <case name="Blank Label 2 levels">
+ <method>testStaticMenu</method>
+ <description>Search for a submenu that comes from a menu with a blank label</description>
+ <args>
+ <menu_schema>@srcdir@/data/blank_label_2levels.json</menu_schema>
+ <menu_item>value10</menu_item>
+ </args>
+ </case>
+ <case name="No Label">
+ <method>testStaticMenu</method>
+ <description>Be sure that a submenu from a menu without label does not exist</description>
+ <args>
+ <menu_schema>@srcdir@/data/no_label.json</menu_schema>
+ <menu_item>submenu_from_no_label</menu_item>
+ <notexists>True</notexists>
+ </args>
+ </case>
+ <case name="Several Submenus">
+ <method>testStaticMenu</method>
+ <description>Check that a submenu is shown</description>
+ <args>
+ <menu_schema>@srcdir@/data/several_submenus.json</menu_schema>
+ <menu_item>value10</menu_item>
+ </args>
+ </case>
+ <case name="Several Submenus Recursive">
+ <method>testStaticMenu</method>
+ <description>Be sure that a submenu from a 4th level depth, is shown</description>
+ <args>
+ <menu_schema>@srcdir@/data/several_submenus_recursive.json</menu_schema>
+ <menu_item>value7001</menu_item>
+ </args>
+ </case>
+ <case name="Several Submenus UTF8">
+ <method>testStaticMenu</method>
+ <description>Be sure that a submenu, with a UTF-8 label, is shown</description>
+ <args>
+ <menu_schema>@srcdir@/data/several_submenus_utf8.json</menu_schema>
+ <menu_item>value5ス</menu_item>
+ </args>
+ </case>
+</suite>
diff --git a/tests/test-glib-layout-client.c b/tests/test-glib-layout-client.c
index 7a53443..1b74544 100644
--- a/tests/test-glib-layout-client.c
+++ b/tests/test-glib-layout-client.c
@@ -88,6 +88,10 @@ layout_updated (DbusmenuClient * client, gpointer data)
layouton++;
+ if (layouts[layouton].id == 0) {
+ g_main_loop_quit(mainloop);
+ }
+
return;
}
diff --git a/tests/test-glib-layout-server.c b/tests/test-glib-layout-server.c
index e69c6b2..cc9b8e7 100644
--- a/tests/test-glib-layout-server.c
+++ b/tests/test-glib-layout-server.c
@@ -48,7 +48,7 @@ layout2menuitem (layout_t * layout)
}
}
- g_debug("Layout to menu return: 0x%X", (unsigned int)local);
+ /* g_debug("Layout to menu return: 0x%X", (unsigned int)local); */
return local;
}
diff --git a/tests/test-glib-properties-server.c b/tests/test-glib-properties-server.c
index 477f951..8dad37f 100644
--- a/tests/test-glib-properties-server.c
+++ b/tests/test-glib-properties-server.c
@@ -1,3 +1,24 @@
+/*
+A test for libdbusmenu to ensure its quality.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 3, as published
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.
+*/
+
#include <glib.h>
#include <dbus/dbus.h>
@@ -40,7 +61,7 @@ layout2menuitem (proplayout_t * layout)
}
}
- g_debug("Layout to menu return: 0x%X", (unsigned int)local);
+ /* g_debug("Layout to menu return: 0x%X", (unsigned int)local); */
return local;
}
@@ -57,7 +78,9 @@ timer_func (gpointer data)
}
g_debug("Updating to Layout %d", layouton);
- dbusmenu_server_set_root(server, layout2menuitem(&layouts[layouton]));
+ DbusmenuMenuitem * mi = layout2menuitem(&layouts[layouton]);
+ dbusmenu_server_set_root(server, mi);
+ g_object_unref(G_OBJECT(mi));
layouton++;
return TRUE;
@@ -78,6 +101,7 @@ main (int argc, char ** argv)
mainloop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(mainloop);
+ g_object_unref(G_OBJECT(server));
g_debug("Quiting");
return 0;
diff --git a/tests/test-glib-properties.h b/tests/test-glib-properties.h
index 3ab7ee7..5e83f5c 100644
--- a/tests/test-glib-properties.h
+++ b/tests/test-glib-properties.h
@@ -1,3 +1,24 @@
+/*
+A test for libdbusmenu to ensure its quality.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 3, as published
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.
+*/
+
#include <glib.h>
diff --git a/tests/test-glib-simple-items.c b/tests/test-glib-simple-items.c
new file mode 100644
index 0000000..56536e9
--- /dev/null
+++ b/tests/test-glib-simple-items.c
@@ -0,0 +1,45 @@
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-bindings.h>
+
+#include <libdbusmenu-glib/server.h>
+#include <libdbusmenu-glib/menuitem.h>
+
+static DbusmenuMenuitem * root_menuitem = NULL;
+static GMainLoop * mainloop = NULL;
+
+gchar * dummies[] = {
+ "Bob", "Jim", "Alvin", "Mary", NULL
+};
+
+static void
+dummy_users (DbusmenuMenuitem * root) {
+ int count;
+ for (count = 0; dummies[count] != NULL; count++) {
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_new();
+ g_debug("Creating item: %d %s", dbusmenu_menuitem_get_id(mi), dummies[count]);
+ g_debug("\tRoot ID: %d", dbusmenu_menuitem_get_id(root));
+ dbusmenu_menuitem_property_set(mi, "label", dummies[count]);
+ dbusmenu_menuitem_child_add_position(root, mi, count);
+ }
+
+ return;
+}
+
+int
+main (int argc, char ** argv)
+{
+ g_type_init();
+
+ DbusmenuServer * server = dbusmenu_server_new("/test/object");
+ root_menuitem = dbusmenu_menuitem_new();
+ dbusmenu_server_set_root(server, root_menuitem);
+ g_debug("Root ID: %d", dbusmenu_menuitem_get_id(root_menuitem));
+
+ dummy_users(root_menuitem);
+
+ mainloop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(mainloop);
+
+ return 0;
+}
+
diff --git a/tests/test-gtk-label-client.c b/tests/test-gtk-label-client.c
new file mode 100644
index 0000000..b691f84
--- /dev/null
+++ b/tests/test-gtk-label-client.c
@@ -0,0 +1,181 @@
+/*
+A test for libdbusmenu to ensure its quality.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 3, as published
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <gtk/gtk.h>
+#include <libdbusmenu-gtk/menu.h>
+
+static guint layouton = 0;
+static GMainLoop * mainloop = NULL;
+static gboolean passed = TRUE;
+static guint death_timer = 0;
+
+#if 0
+static gboolean
+verify_props (DbusmenuMenuitem * mi, gchar ** properties)
+{
+ if (properties == NULL) {
+ return TRUE;
+ }
+
+ /* Verify they're all there and correct */
+ guint i;
+ for (i = 0; properties[i] != NULL; i += 2) {
+ const gchar * value = dbusmenu_menuitem_property_get(mi, properties[i]);
+ if (g_strcmp0(value, properties[i + 1])) {
+ g_debug("\tFailed as property '%s' should be '%s' and is '%s'", properties[i], properties[i+1], value);
+ return FALSE;
+ }
+ }
+
+ /* Verify that we don't have any extras */
+ // GList * props = dbusmenu_menuitem_properties_list(mi);
+
+ return TRUE;
+}
+
+static gboolean
+verify_root_to_layout(DbusmenuMenuitem * mi, proplayout_t * layout)
+{
+ g_debug("Verifying ID: %d", layout->id);
+
+ if (layout->id != dbusmenu_menuitem_get_id(mi)) {
+ g_debug("\tFailed as ID %d is not equal to %d", layout->id, dbusmenu_menuitem_get_id(mi));
+ return FALSE;
+ }
+
+ if (!verify_props(mi, layout->properties)) {
+ g_debug("\tFailed as unable to verify properties.");
+ return FALSE;
+ }
+
+ GList * children = dbusmenu_menuitem_get_children(mi);
+
+ if (children == NULL && layout->submenu == NULL) {
+ g_debug("\tPassed: %d", layout->id);
+ return TRUE;
+ }
+ if (children == NULL || layout->submenu == NULL) {
+ if (children == NULL) {
+ g_debug("\tFailed as there are no children but we have submenus");
+ } else {
+ g_debug("\tFailed as we have children but no submenu");
+ }
+ return FALSE;
+ }
+
+ guint i = 0;
+ for (i = 0; children != NULL && layout->submenu[i].id != 0; children = g_list_next(children), i++) {
+ if (!verify_root_to_layout(DBUSMENU_MENUITEM(children->data), &layout->submenu[i])) {
+ return FALSE;
+ }
+ }
+
+ if (children == NULL && layout->submenu[i].id == 0) {
+ g_debug("\tPassed: %d", layout->id);
+ return TRUE;
+ }
+
+ if (children != NULL) {
+ g_debug("\tFailed as there are still children but no submenus. (ID: %d)", layout->id);
+ } else {
+ g_debug("\tFailed as there are still submenus but no children. (ID: %d)", layout->id);
+ }
+ return FALSE;
+}
+#endif
+
+static gboolean
+timer_func (gpointer data)
+{
+ g_debug("Death timer. Oops. Got to: %d", layouton);
+ passed = FALSE;
+ g_main_loop_quit(mainloop);
+ return FALSE;
+}
+
+#if 0
+static gboolean layout_verify_timer (gpointer data);
+
+static void
+layout_updated (DbusmenuClient * client, gpointer data)
+{
+ g_debug("Layout Updated");
+ g_timeout_add (250, layout_verify_timer, client);
+ return;
+}
+
+static gboolean
+layout_verify_timer (gpointer data)
+{
+ DbusmenuMenuitem * menuroot = dbusmenu_client_get_root(DBUSMENU_CLIENT(data));
+ proplayout_t * layout = &layouts[layouton];
+
+ if (!verify_root_to_layout(menuroot, layout)) {
+ g_debug("FAILED LAYOUT: %d", layouton);
+ passed = FALSE;
+ } else {
+ /* Extend our death */
+ g_source_remove(death_timer);
+ death_timer = g_timeout_add_seconds(10, timer_func, data);
+ }
+
+ layouton++;
+
+ if (layouts[layouton].id == 0) {
+ g_main_loop_quit(mainloop);
+ }
+
+ return FALSE;
+}
+#endif
+
+int
+main (int argc, char ** argv)
+{
+ gtk_init(&argc, &argv);
+
+ /* Make sure the server starts up and all that */
+ g_usleep(500000);
+
+ GtkWidget * window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ GtkWidget * menubar = gtk_menu_bar_new();
+ GtkWidget * menuitem = gtk_menu_item_new_with_label("Test");
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(dbusmenu_gtkmenu_new ("glib.label.test", "/org/test")));
+ gtk_widget_show(menuitem);
+ gtk_menu_bar_append(menubar, menuitem);
+ gtk_widget_show(menubar);
+ gtk_container_add(GTK_CONTAINER(window), menubar);
+ gtk_window_set_title(GTK_WINDOW(window), "libdbusmenu-gtk test");
+ gtk_widget_show(window);
+
+ death_timer = g_timeout_add_seconds(60, timer_func, window);
+
+ mainloop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(mainloop);
+
+ if (passed) {
+ g_debug("Quiting");
+ return 0;
+ } else {
+ g_debug("Quiting as we're a failure");
+ return 0;
+ }
+}
diff --git a/tests/test-gtk-label-server.c b/tests/test-gtk-label-server.c
new file mode 100644
index 0000000..7730fe2
--- /dev/null
+++ b/tests/test-gtk-label-server.c
@@ -0,0 +1,168 @@
+/*
+A test for libdbusmenu to ensure its quality.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 3, as published
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus-glib-bindings.h>
+
+#include <libdbusmenu-glib/menuitem.h>
+#include <libdbusmenu-glib/server.h>
+
+#include <json-glib/json-glib.h>
+
+static void
+menuitem_click(DbusmenuMenuitem * mi, gpointer user_data)
+{
+ g_debug("Clicked on: %d", dbusmenu_menuitem_get_id(mi));
+ return;
+}
+
+static void
+set_props (DbusmenuMenuitem * mi, JsonObject * node)
+{
+ if (node == NULL) return;
+
+ GList * members = NULL;
+ for (members = json_object_get_members(node); members != NULL; members = g_list_next(members)) {
+ const gchar * member = members->data;
+
+ if (!g_strcmp0(member, "id")) { continue; }
+ if (!g_strcmp0(member, "submenu")) { continue; }
+
+ JsonNode * lnode = json_object_get_member(node, member);
+ if (JSON_NODE_TYPE(lnode) != JSON_NODE_VALUE) { continue; }
+
+ dbusmenu_menuitem_property_set(mi, member, json_node_get_string(lnode));
+ }
+
+ return;
+}
+
+static DbusmenuMenuitem *
+layout2menuitem (JsonNode * inlayout)
+{
+ if (inlayout == NULL) return NULL;
+ if (JSON_NODE_TYPE(inlayout) != JSON_NODE_OBJECT) return NULL;
+
+ JsonObject * layout = json_node_get_object(inlayout);
+
+ DbusmenuMenuitem * local = NULL;
+ if (json_object_has_member(layout, "id")) {
+ JsonNode * node = json_object_get_member(layout, "id");
+ g_return_val_if_fail(JSON_NODE_TYPE(node) == JSON_NODE_VALUE, NULL);
+ local = dbusmenu_menuitem_new_with_id(json_node_get_int(node));
+ } else {
+ local = dbusmenu_menuitem_new();
+ }
+ g_signal_connect(G_OBJECT(local), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(menuitem_click), NULL);
+
+ set_props(local, layout);
+
+ if (json_object_has_member(layout, "submenu")) {
+ JsonNode * node = json_object_get_member(layout, "submenu");
+ g_return_val_if_fail(JSON_NODE_TYPE(node) == JSON_NODE_ARRAY, local);
+ JsonArray * array = json_node_get_array(node);
+ guint count;
+ for (count = 0; count < json_array_get_length(array); count++) {
+ DbusmenuMenuitem * child = layout2menuitem(json_array_get_element(array, count));
+ if (child != NULL) {
+ dbusmenu_menuitem_child_append(local, child);
+ }
+ }
+ }
+
+ /* g_debug("Layout to menu return: 0x%X", (unsigned int)local); */
+ return local;
+}
+
+static JsonArray * root_array = NULL;
+static guint layouton = 0;
+static DbusmenuServer * server = NULL;
+static GMainLoop * mainloop = NULL;
+
+static gboolean
+timer_func (gpointer data)
+{
+ if (layouton == json_array_get_length(root_array)) {
+ g_debug("Completed %d layouts", layouton);
+ g_main_loop_quit(mainloop);
+ return FALSE;
+ }
+ g_debug("Updating to Layout %d", layouton);
+
+ dbusmenu_server_set_root(server, layout2menuitem(json_array_get_element(root_array, layouton)));
+ layouton++;
+
+ return TRUE;
+}
+
+int
+main (int argc, char ** argv)
+{
+ g_type_init();
+
+ JsonParser * parser = json_parser_new();
+ GError * error = NULL;
+ if (!json_parser_load_from_file(parser, argv[1], &error)) {
+ g_debug("Failed parsing file %s because: %s", argv[1], error->message);
+ return 1;
+ }
+ JsonNode * root_node = json_parser_get_root(parser);
+ if (JSON_NODE_TYPE(root_node) != JSON_NODE_ARRAY) {
+ g_debug("Root node is not an array, fail. It's an: %s", json_node_type_name(root_node));
+ return 1;
+ }
+
+ root_array = json_node_get_array(root_node);
+ g_debug("%d layouts in test description '%s'", json_array_get_length(root_array), argv[1]);
+
+ DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
+ g_debug("DBus ID: %s", dbus_connection_get_server_id(dbus_g_connection_get_connection(dbus_g_bus_get(DBUS_BUS_SESSION, NULL))));
+
+ DBusGProxy * bus_proxy = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
+ guint nameret = 0;
+
+ if (!org_freedesktop_DBus_request_name(bus_proxy, "glib.label.test", 0, &nameret, &error)) {
+ g_error("Unable to call to request name");
+ return 1;
+ }
+
+ if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+ g_error("Unable to get name");
+ return 1;
+ }
+
+ server = dbusmenu_server_new("/org/test");
+
+ timer_func(NULL);
+ g_timeout_add_seconds(15, timer_func, NULL);
+
+ mainloop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(mainloop);
+
+ g_debug("Quiting");
+
+ return 0;
+}
+
diff --git a/tests/test-gtk-label.json b/tests/test-gtk-label.json
new file mode 100644
index 0000000..64c1386
--- /dev/null
+++ b/tests/test-gtk-label.json
@@ -0,0 +1,158 @@
+[
+ {"id": 1,
+ "label": "value1",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ {"id": 2,
+ "label": "value2",
+ "submenu": [
+ {"id": 20,
+ "label": "value20"},
+ {"id": 21,
+ "label": "value21"},
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 3,
+ "label": "a super long label that is really of unreasonable length but we should make sure it makes it across the bus",
+ "not.a.value": "A useless value",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"},
+ {"id": 13,
+ "label": "value13"},
+ {"id": 14,
+ "label": "value14"},
+ {"id": 15,
+ "label": "value15"},
+ {"id": 16,
+ "label": "value16"},
+ {"id": 17,
+ "label": "value17"},
+ {"id": 18,
+ "label": "value18"},
+ {"id": 19,
+ "label": "value19"}
+ ]
+ },
+ {"id": 4,
+ "label": "value2",
+ "submenu": [
+ {"id": 5,
+ "label": "value5",
+ "submenu": [
+ {"id": 10,
+ "label": "value10"},
+ {"id": 11,
+ "label": "value11"},
+ {"id": 12,
+ "label": "value12"},
+ {"id": 13,
+ "label": "value13"},
+ {"id": 14,
+ "label": "value14"},
+ {"id": 15,
+ "label": "value15"},
+ {"id": 16,
+ "label": "value16"},
+ {"id": 17,
+ "label": "value17"},
+ {"id": 18,
+ "label": "value18"},
+ {"id": 19,
+ "label": "value19"}
+ ]
+ },
+ {"id": 6,
+ "label": "value6",
+ "submenu": [
+ {"id": 20,
+ "label": "value20"},
+ {"id": 21,
+ "label": "value21"},
+ {"id": 22,
+ "label": "value22"},
+ {"id": 23,
+ "label": "value23"},
+ {"id": 24,
+ "label": "value24"},
+ {"id": 25,
+ "label": "value25"},
+ {"id": 26,
+ "label": "value26"},
+ {"id": 27,
+ "label": "value27"},
+ {"id": 28,
+ "label": "value28"},
+ {"id": 29,
+ "label": "value29"}
+ ]
+ },
+ {"id": 7,
+ "label": "value7",
+ "submenu": [
+ {"id": 30,
+ "label": "value30"},
+ {"id": 31,
+ "label": "value31"},
+ {"id": 32,
+ "label": "value32"},
+ {"id": 33,
+ "label": "value33"},
+ {"id": 34,
+ "label": "value34"},
+ {"id": 35,
+ "label": "value35"},
+ {"id": 36,
+ "label": "value36"},
+ {"id": 37,
+ "label": "value37"},
+ {"id": 38,
+ "label": "value38"},
+ {"id": 39,
+ "label": "value39"}
+ ]
+ },
+ ]
+ }
+]