aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYING.GPLv3674
-rw-r--r--Makefile.am1
-rwxr-xr-xautogen.sh14
-rw-r--r--common/Makefile.am32
-rw-r--r--common/com.canonical.indicator.messages.application.xml39
-rw-r--r--configure.ac43
-rw-r--r--data/Makefile.am5
-rw-r--r--data/com.canonical.indicator.messages10
-rw-r--r--debian/changelog97
-rw-r--r--debian/control14
-rw-r--r--debian/indicator-messages.install2
-rw-r--r--debian/libmessaging-menu-dev.install1
-rw-r--r--debian/libmessaging-menu0.symbols15
-rw-r--r--debian/source/format1
-rw-r--r--doc/reference/Makefile.am3
-rw-r--r--doc/reference/messaging-menu-docs.xml.in3
-rw-r--r--libmessaging-menu/Makefile.am55
-rw-r--r--libmessaging-menu/gtupleaction.c354
-rw-r--r--libmessaging-menu/gtupleaction.h40
-rw-r--r--libmessaging-menu/messaging-menu-app.h163
-rw-r--r--libmessaging-menu/messaging-menu-message.c547
-rw-r--r--libmessaging-menu/messaging-menu-message.h70
-rw-r--r--libmessaging-menu/messaging-menu.c (renamed from libmessaging-menu/messaging-menu-app.c)745
-rw-r--r--libmessaging-menu/messaging-menu.h125
-rw-r--r--po/POTFILES.in14
-rw-r--r--po/indicator-messages.pot46
-rw-r--r--src/Makefile.am71
-rw-r--r--src/app-section.c434
-rw-r--r--src/dbus-data.h17
-rw-r--r--src/gactionmuxer.c8
-rw-r--r--src/gactionmuxer.h3
-rw-r--r--src/gmenuutils.c2
-rw-r--r--src/ido-detail-label.c401
-rw-r--r--src/ido-detail-label.h59
-rw-r--r--src/ido-menu-item.c439
-rw-r--r--src/ido-menu-item.h54
-rw-r--r--src/im-app-menu-item.c354
-rw-r--r--src/im-app-menu-item.h54
-rw-r--r--src/im-application-list.c1139
-rw-r--r--src/im-application-list.h62
-rw-r--r--src/im-desktop-menu.c294
-rw-r--r--src/im-desktop-menu.h35
-rw-r--r--src/im-menu.c191
-rw-r--r--src/im-menu.h64
-rw-r--r--src/im-phone-menu.c317
-rw-r--r--src/im-phone-menu.h68
-rw-r--r--src/im-source-menu-item.c407
-rw-r--r--src/im-source-menu-item.h54
-rw-r--r--src/indicator-messages.c382
-rw-r--r--src/messages-service.c617
-rw-r--r--src/messages-service.xml (renamed from common/com.canonical.indicator.messages.service.xml)0
-rw-r--r--test/Makefile.am5
-rw-r--r--test/indicator-messages-service-activate.c4
-rw-r--r--test/test-gactionmuxer.cpp34
54 files changed, 3857 insertions, 4825 deletions
diff --git a/COPYING.GPLv3 b/COPYING.GPLv3
deleted file mode 100644
index 94a9ed0..0000000
--- a/COPYING.GPLv3
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU 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>.
diff --git a/Makefile.am b/Makefile.am
index f8141a8..daeb2b7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,5 @@
SUBDIRS = \
- common \
src \
libmessaging-menu \
data \
diff --git a/autogen.sh b/autogen.sh
index 1abf3a7..ab2f513 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,11 +1,5 @@
-#!/bin/sh
+#!/bin/sh -e
-PKG_NAME="indicator-messages"
-
-which gnome-autogen.sh || {
- echo "You need gnome-common from GNOME SVN"
- exit 1
-}
-
-USE_GNOME2_MACROS=1 \
-. gnome-autogen.sh
+gtkdocize
+autoreconf -i -f
+intltoolize
diff --git a/common/Makefile.am b/common/Makefile.am
deleted file mode 100644
index 0b8bad4..0000000
--- a/common/Makefile.am
+++ /dev/null
@@ -1,32 +0,0 @@
-
-noinst_LTLIBRARIES = libmessaging-common.la
-
-indicator-messages-service.c: com.canonical.indicator.messages.service.xml
- $(AM_V_GEN) gdbus-codegen \
- --interface-prefix com.canonical.indicator.messages. \
- --generate-c-code indicator-messages-service \
- --c-namespace IndicatorMessages \
- $^
-indicator-messages-service.h: indicator-messages-service.c
-
-indicator-messages-application.c: com.canonical.indicator.messages.application.xml
- $(AM_V_GEN) gdbus-codegen \
- --interface-prefix com.canonical.indicator.messages. \
- --generate-c-code indicator-messages-application \
- --c-namespace IndicatorMessages \
- $^
-indicator-messages-application.h: indicator-messages-application.c
-
-BUILT_SOURCES = \
- indicator-messages-service.c \
- indicator-messages-service.h \
- indicator-messages-application.c \
- indicator-messages-application.h
-
-libmessaging_common_la_SOURCES = \
- $(BUILT_SOURCES)
-
-libmessaging_common_la_CFLAGS = $(GIO_CFLAGS)
-libmessaging_common_la_LIBADD = $(GIO_LIBS)
-
-CLEANFILES = $(BUILT_SOURCES)
diff --git a/common/com.canonical.indicator.messages.application.xml b/common/com.canonical.indicator.messages.application.xml
deleted file mode 100644
index 6f038e6..0000000
--- a/common/com.canonical.indicator.messages.application.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<node name="/">
- <interface name="com.canonical.indicator.messages.application">
- <method name="ListSources">
- <arg type="a(sssuxsb)" name="sources" direction="out" />
- </method>
- <method name="ListMessages">
- <arg type="a(sssssxaa{sv}b)" name="message" direction="out" />
- </method>
- <method name="ActivateSource">
- <arg type="s" name="source_id" direction="in" />
- </method>
- <method name="ActivateMessage">
- <arg type="s" name="message_id" direction="in" />
- <arg type="s" name="action_id" direction="in" />
- <arg type="av" name="parameter" direction="in" />
- </method>
- <method name="Dismiss">
- <arg type="as" name="sources" direction="in" />
- <arg type="as" name="messages" direction="in" />
- </method>
- <signal name="SourceAdded">
- <arg type="u" name="position" direction="in" />
- <arg type="(sssuxsb)" name="source" direction="in" />
- </signal>
- <signal name="SourceChanged">
- <arg type="(sssuxsb)" name="source" direction="in" />
- </signal>
- <signal name="SourceRemoved">
- <arg type="s" name="source_id" direction="in" />
- </signal>
- <signal name="MessageAdded">
- <arg type="(sssssxaa{sv}b)" name="message" direction="in" />
- </signal>
- <signal name="MessageRemoved">
- <arg type="s" name="message_id" direction="in" />
- </signal>
- </interface>
-</node>
diff --git a/configure.ac b/configure.ac
index e67b66a..08ac680 100644
--- a/configure.ac
+++ b/configure.ac
@@ -61,12 +61,6 @@ GTK_DOC_CHECK([1.18], [--flavour no-tmpl])
GOBJECT_INTROSPECTION_CHECK([$INTROSPECTION_REQUIRED_VERSION])
-AC_ARG_WITH([indicator-dir],
- [AS_HELP_STRING([--with-indicator-dir=DIR], [Indicator directory [default=$datadir/unity/indicators]])],
- [],
- [with_indicator_dir=$datadir/unity/indicators])
-AC_SUBST([INDICATOR_DIR], [$with_indicator_dir])
-
###########################
# gcov coverage reporting
###########################
@@ -106,20 +100,33 @@ fi
AM_CONDITIONAL([BUILD_TESTS],[test "x$enable_tests" = "xyes"])
###########################
-# Vala API Generation
+# Check to see if we're local
###########################
-AC_ARG_ENABLE([vala],
- AC_HELP_STRING([--disable-vala], [Disable vala]),
- [enable_vala=$enableval], [enable_vala=yes])
+with_localinstall="no"
+AC_ARG_ENABLE(localinstall, AS_HELP_STRING([--enable-localinstall], [install all of the files localy instead of system directories (for distcheck)]), with_localinstall=$enableval, with_localinstall=no)
-AS_IF([test "x$enable_vala" != "xno"],[
- AM_COND_IF([HAVE_INTROSPECTION],,[
- AC_MSG_ERROR([Vala bindings require introspection support, please --enable-introspection])
- ])
-AC_PATH_PROG([VALA_API_GEN], [vapigen])
-])
-AM_CONDITIONAL([HAVE_VALA], [test -n "$VALA_API_GEN"])
+###########################
+# Indicator Info
+###########################
+
+if test "x$with_localinstall" = "xyes"; then
+ INDICATORDIR="${libdir}/indicators/2/"
+else
+ INDICATORDIR=`$PKG_CONFIG --variable=indicatordir indicator3-0.4`
+fi
+AC_SUBST(INDICATORDIR)
+
+###########################
+# DBus Service Info
+###########################
+
+if test "x$with_localinstall" = "xyes"; then
+ DBUSSERVICEDIR="${datadir}/dbus-1/services/"
+else
+ DBUSSERVICEDIR=`$PKG_CONFIG --variable=session_bus_services_dir dbus-1`
+fi
+AC_SUBST(DBUSSERVICEDIR)
##############################
# Custom Junk
@@ -155,7 +162,6 @@ AM_GLIB_GNU_GETTEXT
AC_OUTPUT([
Makefile
src/Makefile
-common/Makefile
data/Makefile
data/icons/Makefile
data/icons/16x16/Makefile
@@ -196,6 +202,5 @@ Messaging Indicator Configuration:
tests: $enable_tests
gcov: $use_gcov
introspecion: $enable_introspection
- Vala bindings: $enable_vala
documentation: $enable_gtk_doc
])
diff --git a/data/Makefile.am b/data/Makefile.am
index 45ff12f..488ce05 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,6 +1,6 @@
SUBDIRS = icons
-dbus_servicesdir = $(datadir)/dbus-1/services
+dbus_servicesdir = $(DBUSSERVICEDIR)
dbus_services_DATA = indicator-messages.service
%.service: %.service.in
@@ -9,9 +9,6 @@ dbus_services_DATA = indicator-messages.service
gsettings_SCHEMAS = com.canonical.indicator.messages.gschema.xml
@GSETTINGS_RULES@
-indicatordir = $(INDICATOR_DIR)
-dist_indicator_DATA = com.canonical.indicator.messages
-
EXTRA_DIST = \
$(gsettings_SCHEMAS) \
indicator-messages.service.in
diff --git a/data/com.canonical.indicator.messages b/data/com.canonical.indicator.messages
deleted file mode 100644
index fa0ac50..0000000
--- a/data/com.canonical.indicator.messages
+++ /dev/null
@@ -1,10 +0,0 @@
-[Indicator Service]
-Name=indicator-messages
-ObjectPath=/com/canonical/indicator/messages
-Position=50
-
-[desktop]
-ObjectPath=/com/canonical/indicator/messages/desktop
-
-[phone]
-ObjectPath=/com/canonical/indicator/messages/phone
diff --git a/debian/changelog b/debian/changelog
index a6fe645..3df5f41 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -9,12 +9,6 @@ indicator-messages (13.10.1+13.10.20130820-0ubuntu1) saucy; urgency=low
-- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 20 Aug 2013 02:06:40 +0000
-indicator-messages (13.10.0phablet1) raring; urgency=low
-
- * Version bump to not pull from archives
-
- -- Sergio Schvezov <sergio.schvezov@canonical.com> Fri, 26 Apr 2013 13:53:53 -0300
-
indicator-messages (12.10.6+13.10.20130702-0ubuntu1) saucy; urgency=low
[ Mathias Dietrich ]
@@ -25,12 +19,6 @@ indicator-messages (12.10.6+13.10.20130702-0ubuntu1) saucy; urgency=low
-- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 02 Jul 2013 02:02:36 +0000
-indicator-messages (12.10.6phablet1) quantal; urgency=low
-
- * Adding guards for g_type_init
-
- -- Sergio Schvezov <sergio.schvezov@canonical.com> Fri, 22 Mar 2013 17:23:45 -0300
-
indicator-messages (12.10.6daily13.06.19-0ubuntu1) saucy; urgency=low
[ Sebastien Bacher ]
@@ -144,91 +132,6 @@ indicator-messages (12.10.6daily12.11.21.1-0ubuntu1) raring; urgency=low
-- Automatic PS uploader <ps-jenkins@lists.canonical.com> Wed, 21 Nov 2012 10:41:37 +0000
-indicator-messages (12.10.6-0ubuntu1phablet9) quantal; urgency=low
-
- * add "remove-all" signal to imapplicationlist (temporarily)
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Thu, 20 Dec 2012 18:49:50 +0100
-
-indicator-messages (12.10.6-0ubuntu1phablet8) quantal; urgency=low
-
- * Make messaging_menu_app_remove_message() work for messages with a ref count of 1
- * Make handling of multiple processes with the same desktop id more robust
- * ImPhoneMenu: sort messages by time (fixes LP #1090266)
- * Don't show message sources
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Mon, 17 Dec 2012 14:42:03 +0100
-
-indicator-messages (12.10.6-0ubuntu1phablet7) quantal; urgency=low
-
- * Remove variant wrapper from 'parameter' argument of the "activate" signal
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Tue, 11 Dec 2012 14:28:15 +0100
-
-indicator-messages (12.10.6-0ubuntu1phablet6) quantal; urgency=low
-
- * Don't show sources
- * Always use the "messageitem" widget type for messages
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Mon, 10 Dec 2012 14:38:16 +0100
-
-indicator-messages (12.10.6-0ubuntu1phablet5) quantal; urgency=low
-
- * Don't shorten the app id to seven characters (fixes LP #1086729)
- * Add messaging_menu_app_get_message
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Thu, 06 Dec 2012 15:06:34 +0000
-
-indicator-messages (12.10.6-0ubuntu1phablet4) quantal; urgency=low
-
- [Lars Uebernickel]
- * Add the concept of actions to messages
- * Stop using IndicatorService
- * Reverse order of messages
- * Expose symbolic application icon
- * Change icon when there are any messages in the menu
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Tue, 04 Dec 2012 21:10:26 +0000
-
-indicator-messages (12.10.6-0ubuntu1phablet3) quantal; urgency=low
-
- [Lars Uebernickel]
- * expose root menu item of which the indicator menu is a submenu
- * fix crash in im-application-list on arm
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Thu, 29 Nov 2012 21:44:19 +0100
-
-indicator-messages (12.10.6-0ubuntu1phablet2) quantal; urgency=low
-
- [Lars Uebernickel]
- * refactor messages-service.c
- * notify applications about message and source activations
- * add "Clear All" menu item
- * allow dismissing of messages
- * include application icons on message items
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Mon, 26 Nov 2012 22:19:51 +0100
-
-indicator-messages (12.10.6-0ubuntu1phablet1) quantal; urgency=low
-
- [ Mathieu Trudel-Lapierre ]
- * debian/rules:
- - Use autogen.sh for dh_autoreconf.
- - Drop the override for dh_makeshlibs.
- - Add DPKG_GENSYMBOLS_CHECK_LEVEL=4.
- * debian/control:
- - Fix styling: add trailing commas at the end of lists.
- - Update Vcs-Bzr, Vcs-Browser fields and add a notice for developers.
- - Add libgtest-dev to Build-Depends.
- * Automatic snapshot from revision 329 (bootstrap):
- - Clear the detail (count or time) of a source when another type of detail
- is set. (LP: #1071640)
-
- [ Ricardo Mendoza ]
- * Releasing upstream for phablet
-
- -- Ricardo Mendoza <ricardo.mendoza@canonical.com> Tue, 20 Nov 2012 10:08:59 -0430
-
indicator-messages (12.10.5-0ubuntu2) raring; urgency=low
* Upload to raring
diff --git a/debian/control b/debian/control
index 47d4c0e..7773476 100644
--- a/debian/control
+++ b/debian/control
@@ -7,19 +7,19 @@ Uploaders: Evgeni Golov <evgeni@debian.org>
Build-Depends: debhelper (>= 9),
dh-autoreconf,
dh-translations,
- gobject-introspection (>= 0.9.12-4~),
- gtk-doc-tools,
- intltool,
+ libglib2.0-dev (>= 2.35.4),
+ libgtk-3-dev (>= 3.5.12),
libdbus-glib-1-dev,
+ intltool,
+ libindicator3-dev,
libdbusmenu-glib-dev (>= 0.5.90),
libdbusmenu-gtk3-dev (>= 0.5.90),
+ libtelepathy-glib-dev (>= 0.9.0),
+ gobject-introspection (>= 0.9.12-4~),
libgirepository1.0-dev (>= 0.9.12),
+ gtk-doc-tools,
libgtest-dev,
- libgtk-3-dev (>= 3.5.18),
- libindicator3-dev,
- libtelepathy-glib-dev (>= 0.9.0),
python3-dbusmock,
- valac,
Standards-Version: 3.9.3
Homepage: https://launchpad.net/indicator-messages
# If you aren't a member of ~indicator-applet-developers but need to upload
diff --git a/debian/indicator-messages.install b/debian/indicator-messages.install
index 39c40f5..c0f6e98 100644
--- a/debian/indicator-messages.install
+++ b/debian/indicator-messages.install
@@ -1,5 +1,5 @@
+usr/lib/indicators3
usr/lib/indicator-messages/indicator-messages-service
usr/share/dbus-1
usr/share/glib-2.0
usr/share/icons
-usr/share/unity/indicators
diff --git a/debian/libmessaging-menu-dev.install b/debian/libmessaging-menu-dev.install
index 53e0312..75ef089 100644
--- a/debian/libmessaging-menu-dev.install
+++ b/debian/libmessaging-menu-dev.install
@@ -2,5 +2,4 @@ usr/lib/libmessaging-menu.so
usr/lib/pkgconfig
usr/include /usr/
usr/share/gir-1.0/
-usr/share/vala/
usr/share/gtk-doc
diff --git a/debian/libmessaging-menu0.symbols b/debian/libmessaging-menu0.symbols
index 6a54911..6261a2a 100644
--- a/debian/libmessaging-menu0.symbols
+++ b/debian/libmessaging-menu0.symbols
@@ -1,11 +1,9 @@
libmessaging-menu.so.0 libmessaging-menu0 #MINVER#
- messaging_menu_app_append_message@Base 12.10.6-0ubuntu1phablet1
messaging_menu_app_append_source@Base 12.10.0
messaging_menu_app_append_source_with_count@Base 12.10.0
messaging_menu_app_append_source_with_string@Base 12.10.0
messaging_menu_app_append_source_with_time@Base 12.10.0
messaging_menu_app_draw_attention@Base 12.10.0
- messaging_menu_app_get_message@Base 13.10.1+13.10.20130820
messaging_menu_app_get_type@Base 12.10.0
messaging_menu_app_has_source@Base 12.10.0
messaging_menu_app_insert_source@Base 12.10.0
@@ -15,8 +13,6 @@ libmessaging-menu.so.0 libmessaging-menu0 #MINVER#
messaging_menu_app_new@Base 12.10.0
messaging_menu_app_register@Base 12.10.0
messaging_menu_app_remove_attention@Base 12.10.0
- messaging_menu_app_remove_message@Base 12.10.6-0ubuntu1phablet1
- messaging_menu_app_remove_message_by_id@Base 12.10.6-0ubuntu1phablet1
messaging_menu_app_remove_source@Base 12.10.0
messaging_menu_app_set_source_count@Base 12.10.0
messaging_menu_app_set_source_icon@Base 12.10.2
@@ -25,14 +21,3 @@ libmessaging-menu.so.0 libmessaging-menu0 #MINVER#
messaging_menu_app_set_source_time@Base 12.10.0
messaging_menu_app_set_status@Base 12.10.0
messaging_menu_app_unregister@Base 12.10.0
- messaging_menu_message_add_action@Base 12.10.6-0ubuntu1phablet3
- messaging_menu_message_get_body@Base 12.10.6-0ubuntu1phablet1
- messaging_menu_message_get_draws_attention@Base 12.10.6-0ubuntu1phablet1
- messaging_menu_message_get_icon@Base 12.10.6-0ubuntu1phablet1
- messaging_menu_message_get_id@Base 12.10.6-0ubuntu1phablet1
- messaging_menu_message_get_subtitle@Base 12.10.6-0ubuntu1phablet1
- messaging_menu_message_get_time@Base 12.10.6-0ubuntu1phablet1
- messaging_menu_message_get_title@Base 12.10.6-0ubuntu1phablet1
- messaging_menu_message_get_type@Base 12.10.6-0ubuntu1phablet1
- messaging_menu_message_new@Base 12.10.6-0ubuntu1phablet1
- messaging_menu_message_set_draws_attention@Base 12.10.6-0ubuntu1phablet1
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644
index d3827e7..0000000
--- a/debian/source/format
+++ /dev/null
@@ -1 +0,0 @@
-1.0
diff --git a/doc/reference/Makefile.am b/doc/reference/Makefile.am
index 3993b41..814a939 100644
--- a/doc/reference/Makefile.am
+++ b/doc/reference/Makefile.am
@@ -11,7 +11,8 @@ HFILE_GLOB = $(top_srcdir)/libmessaging-menu/*.h
CFILE_GLOB = $(top_srcdir)/libmessaging-menu/*.c
IGNORE_HFILES= \
- indicator-messages-service.h
+ indicator-messages-service.h \
+ gtupleaction.h
INCLUDES=-I$(top_srcdir)/libmessaging-menu $(GIO_CFLAGS)
GTKDOC_LIBS=$(top_builddir)/libmessaging-menu/libmessaging-menu.la
diff --git a/doc/reference/messaging-menu-docs.xml.in b/doc/reference/messaging-menu-docs.xml.in
index 4c1cec1..742d37b 100644
--- a/doc/reference/messaging-menu-docs.xml.in
+++ b/doc/reference/messaging-menu-docs.xml.in
@@ -18,8 +18,7 @@
<chapter>
<title>API Reference</title>
- <xi:include href="xml/messaging-menu-app.xml" />
- <xi:include href="xml/messaging-menu-message.xml" />
+ <xi:include href="xml/messaging-menu.xml"/>
</chapter>
<chapter id="object-tree">
diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am
index 1042d9f..7a6ee31 100644
--- a/libmessaging-menu/Makefile.am
+++ b/libmessaging-menu/Makefile.am
@@ -4,25 +4,36 @@ lib_LTLIBRARIES = libmessaging-menu.la
libmessaging_menu_ladir = $(includedir)/messaging-menu
libmessaging_menu_la_SOURCES = \
- messaging-menu-app.c \
- messaging-menu-message.c
+ messaging-menu.c \
+ gtupleaction.c \
+ gtupleaction.h \
+ $(BUILT_SOURCES)
libmessaging_menu_la_HEADERS = \
- messaging-menu-app.h \
- messaging-menu.h \
- messaging-menu-message.h
+ messaging-menu.h
-libmessaging_menu_la_LIBADD = \
- $(GIO_LIBS) \
- $(top_builddir)/common/libmessaging-common.la
+libmessaging_menu_la_LIBADD = $(GIO_LIBS)
libmessaging_menu_la_CFLAGS = \
- -I$(top_builddir)/common \
$(GIO_CFLAGS) \
-Wall
libmessaging_menu_la_LDFLAGS = -export-symbols-regex "^messaging_menu_.*"
+BUILT_SOURCES = \
+ indicator-messages-service.c \
+ indicator-messages-service.h
+
+CLEANFILES = $(BUILT_SOURCES)
+
+indicator-messages-service.c: $(top_srcdir)/src/messages-service.xml
+ $(AM_V_GEN) gdbus-codegen \
+ --interface-prefix com.canonical.indicator.messages. \
+ --generate-c-code indicator-messages-service \
+ --c-namespace IndicatorMessages \
+ $^
+indicator-messages-service.h: indicator-messages-service.c
+
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = messaging-menu.pc
@@ -41,11 +52,7 @@ MessagingMenu_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0
MessagingMenu_1_0_gir_CFLAGS = $(INCLUDES) $(GIO_CFLAGS)
MessagingMenu_1_0_gir_SCANNERFLAGS = --c-include="messaging-menu.h"
MessagingMenu_1_0_gir_LIBS = libmessaging-menu.la
-MessagingMenu_1_0_gir_FILES = \
- messaging-menu-app.c \
- messaging-menu-app.h \
- messaging-menu-message.c \
- messaging-menu-message.h
+MessagingMenu_1_0_gir_FILES = messaging-menu.c messaging-menu.h
MessagingMenu_1_0_gir_EXPORT_PACKAGES = messaging-menu
INTROSPECTION_GIRS += MessagingMenu-1.0.gir
@@ -55,23 +62,5 @@ gir_DATA = $(INTROSPECTION_GIRS)
typelibdir = $(libdir)/girepository-1.0
typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
-CLEANFILES = $(gir_DATA) $(typelib_DATA)
-
-
-#########################
-# VAPI Files
-#########################
-
-if HAVE_VALA
-
-vapidir = $(datadir)/vala/vapi
-vapi_DATA = MessagingMenu-1.0.vapi
-
-MessagingMenu-1.0.vapi: MessagingMenu-1.0.gir
- $(VALA_API_GEN) --pkg gio-2.0 --library=MessagingMenu-1.0 $<
-
-CLEANFILES += $(vapi_DATA)
-
-endif
-
+CLEANFILES +=$(gir_DATA) $(typelib_DATA)
endif
diff --git a/libmessaging-menu/gtupleaction.c b/libmessaging-menu/gtupleaction.c
new file mode 100644
index 0000000..21bc003
--- /dev/null
+++ b/libmessaging-menu/gtupleaction.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * 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/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#include "gtupleaction.h"
+
+typedef GObjectClass GTupleActionClass;
+
+struct _GTupleAction
+{
+ GObject parent;
+
+ gchar *name;
+ GVariantType *type;
+ gboolean enabled;
+
+ gsize n_children;
+ GVariant **children;
+};
+
+static void action_interface_init (GActionInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTupleAction, g_tuple_action, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION, action_interface_init));
+
+enum
+{
+ PROP_0,
+ PROP_NAME,
+ PROP_PARAMETER_TYPE,
+ PROP_ENABLED,
+ PROP_STATE_TYPE,
+ PROP_STATE,
+ N_PROPERTIES
+};
+
+enum
+{
+ SIGNAL_ACTIVATE,
+ N_SIGNALS
+};
+
+static GParamSpec *properties[N_PROPERTIES];
+static guint signal_ids[N_SIGNALS];
+
+static const gchar *
+g_tuple_action_get_name (GAction *action)
+{
+ GTupleAction *tuple = G_TUPLE_ACTION (action);
+
+ return tuple->name;
+}
+
+static const GVariantType *
+g_tuple_action_get_parameter_type (GAction *action)
+{
+ return NULL;
+}
+
+static const GVariantType *
+g_tuple_action_get_state_type (GAction *action)
+{
+ GTupleAction *tuple = G_TUPLE_ACTION (action);
+
+ return tuple->type;
+}
+
+static GVariant *
+g_tuple_action_get_state_hint (GAction *action)
+{
+ return NULL;
+}
+
+static gboolean
+g_tuple_action_get_enabled (GAction *action)
+{
+ GTupleAction *tuple = G_TUPLE_ACTION (action);
+
+ return tuple->enabled;
+}
+
+static GVariant *
+g_tuple_action_get_state (GAction *action)
+{
+ GTupleAction *tuple = G_TUPLE_ACTION (action);
+ GVariant *result;
+
+ result = g_variant_new_tuple (tuple->children, tuple->n_children);
+ return g_variant_ref_sink (result);
+}
+
+static void
+g_tuple_action_set_state (GTupleAction *tuple,
+ GVariant *state)
+{
+ int i;
+
+ g_return_if_fail (g_variant_type_is_tuple (g_variant_get_type (state)));
+
+ if (tuple->type == NULL)
+ {
+ tuple->type = g_variant_type_copy (g_variant_get_type (state));
+ tuple->n_children = g_variant_n_children (state);
+ tuple->children = g_new0 (GVariant *, tuple->n_children);
+ }
+
+ for (i = 0; i < tuple->n_children; i++)
+ {
+ if (tuple->children[i])
+ g_variant_unref (tuple->children[i]);
+ tuple->children[i] = g_variant_get_child_value (state, i);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (tuple), properties[PROP_STATE]);
+}
+
+static void
+g_tuple_action_change_state (GAction *action,
+ GVariant *value)
+{
+ GTupleAction *tuple = G_TUPLE_ACTION (action);
+
+ g_return_if_fail (value != NULL);
+ g_return_if_fail (g_variant_is_of_type (value, tuple->type));
+
+ g_variant_ref_sink (value);
+
+ /* TODO add a change-state signal similar to GSimpleAction */
+ g_tuple_action_set_state (tuple, value);
+
+ g_variant_unref (value);
+}
+
+static void
+g_tuple_action_activate (GAction *action,
+ GVariant *parameter)
+{
+ GTupleAction *tuple = G_TUPLE_ACTION (action);
+
+ g_return_if_fail (parameter == NULL);
+
+ if (tuple->enabled)
+ g_signal_emit (tuple, signal_ids[SIGNAL_ACTIVATE], 0, NULL);
+}
+
+static void
+g_tuple_action_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GAction *action = G_ACTION (object);
+
+ switch (prop_id)
+ {
+ case PROP_NAME:
+ g_value_set_string (value, g_tuple_action_get_name (action));
+ break;
+
+ case PROP_PARAMETER_TYPE:
+ g_value_set_boxed (value, g_tuple_action_get_parameter_type (action));
+ break;
+
+ case PROP_ENABLED:
+ g_value_set_boolean (value, g_tuple_action_get_enabled (action));
+ break;
+
+ case PROP_STATE_TYPE:
+ g_value_set_boxed (value, g_tuple_action_get_state_type (action));
+ break;
+
+ case PROP_STATE:
+ g_value_take_variant (value, g_tuple_action_get_state (action));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tuple_action_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GTupleAction *tuple = G_TUPLE_ACTION (object);
+
+ switch (prop_id)
+ {
+ case PROP_NAME:
+ tuple->name = g_value_dup_string (value);
+ g_object_notify_by_pspec (object, properties[PROP_NAME]);
+ break;
+
+ case PROP_ENABLED:
+ tuple->enabled = g_value_get_boolean (value);
+ g_object_notify_by_pspec (object, properties[PROP_ENABLED]);
+ break;
+
+ case PROP_STATE:
+ g_tuple_action_set_state (tuple, g_value_get_variant (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tuple_action_finalize (GObject *object)
+{
+ GTupleAction *tuple = G_TUPLE_ACTION (object);
+ int i;
+
+ g_free (tuple->name);
+ g_variant_type_free (tuple->type);
+
+ for (i = 0; i < tuple->n_children; i++)
+ g_variant_unref (tuple->children[i]);
+
+ g_free (tuple->children);
+
+ G_OBJECT_CLASS (g_tuple_action_parent_class)->finalize (object);
+}
+
+static void
+action_interface_init (GActionInterface *iface)
+{
+ iface->get_name = g_tuple_action_get_name;
+ iface->get_parameter_type = g_tuple_action_get_parameter_type;
+ iface->get_state_type = g_tuple_action_get_state_type;
+ iface->get_state_hint = g_tuple_action_get_state_hint;
+ iface->get_enabled = g_tuple_action_get_enabled;
+ iface->get_state = g_tuple_action_get_state;
+ iface->change_state = g_tuple_action_change_state;
+ iface->activate = g_tuple_action_activate;
+}
+
+static void
+g_tuple_action_class_init (GTupleActionClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->get_property = g_tuple_action_get_property;
+ object_class->set_property = g_tuple_action_set_property;
+ object_class->finalize = g_tuple_action_finalize;
+
+ properties[PROP_NAME] = g_param_spec_string ("name",
+ "Name",
+ "The name of the action",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_PARAMETER_TYPE] = g_param_spec_boxed ("parameter-type",
+ "Parameter Type",
+ "The variant type passed to activate",
+ G_TYPE_VARIANT_TYPE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_ENABLED] = g_param_spec_boolean ("enabled",
+ "Enabled",
+ "Whether the action can be activated",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_STATE_TYPE] = g_param_spec_boxed ("state-type",
+ "State Type",
+ "The variant type of the state, must be a tuple",
+ G_TYPE_VARIANT_TYPE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_STATE] = g_param_spec_variant ("state",
+ "State",
+ "The state of the action",
+ G_VARIANT_TYPE_TUPLE,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+
+ signal_ids[SIGNAL_ACTIVATE] = g_signal_new ("activate",
+ G_TYPE_TUPLE_ACTION,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_MUST_COLLECT,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VARIANT,
+ G_TYPE_NONE, 1,
+ G_TYPE_VARIANT);
+}
+
+static void
+g_tuple_action_init (GTupleAction *action)
+{
+ action->enabled = TRUE;
+}
+
+GTupleAction *
+g_tuple_action_new (const gchar *name,
+ GVariant *initial_state)
+{
+ const GVariantType *type;
+
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (initial_state != NULL, NULL);
+
+ type = g_variant_get_type (initial_state);
+ g_return_val_if_fail (g_variant_type_is_tuple (type), NULL);
+
+ return g_object_new (G_TYPE_TUPLE_ACTION,
+ "name", name,
+ "state", initial_state,
+ NULL);
+}
+
+void
+g_tuple_action_set_child (GTupleAction *action,
+ gsize index,
+ GVariant *value)
+{
+ const GVariantType *type;
+
+ g_return_if_fail (G_IS_TUPLE_ACTION (action));
+ g_return_if_fail (index < action->n_children);
+ g_return_if_fail (value != NULL);
+
+ type = g_variant_get_type (value);
+ g_return_if_fail (g_variant_is_of_type (value, type));
+
+ g_variant_unref (action->children[index]);
+ action->children[index] = g_variant_ref_sink (value);
+
+ g_object_notify_by_pspec (G_OBJECT (action), properties[PROP_STATE]);
+}
diff --git a/libmessaging-menu/gtupleaction.h b/libmessaging-menu/gtupleaction.h
new file mode 100644
index 0000000..c447d71
--- /dev/null
+++ b/libmessaging-menu/gtupleaction.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * 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/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#ifndef __g_tuple_action_h__
+#define __g_tuple_action_h__
+
+#include <gio/gio.h>
+
+#define G_TYPE_TUPLE_ACTION (g_tuple_action_get_type ())
+#define G_TUPLE_ACTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_TUPLE_ACTION, GTupleAction))
+#define G_IS_TUPLE_ACTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_TUPLE_ACTION))
+
+typedef struct _GTupleAction GTupleAction;
+
+GType g_tuple_action_get_type (void) G_GNUC_CONST;
+
+GTupleAction * g_tuple_action_new (const gchar *name,
+ GVariant *initial_state);
+
+void g_tuple_action_set_child (GTupleAction *action,
+ gsize index,
+ GVariant *value);
+
+#endif
diff --git a/libmessaging-menu/messaging-menu-app.h b/libmessaging-menu/messaging-menu-app.h
deleted file mode 100644
index c8097e1..0000000
--- a/libmessaging-menu/messaging-menu-app.h
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright 2012 Canonical Ltd.
- *
- * 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/>.
- *
- * Authors:
- * Lars Uebernickel <lars.uebernickel@canonical.com>
- */
-
-#ifndef __messaging_menu_app_h__
-#define __messaging_menu_app_h__
-
-#include <gio/gio.h>
-#include "messaging-menu-message.h"
-
-G_BEGIN_DECLS
-
-#define MESSAGING_MENU_TYPE_APP messaging_menu_app_get_type()
-#define MESSAGING_MENU_APP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MESSAGING_MENU_TYPE_APP, MessagingMenuApp))
-#define MESSAGING_MENU_APP_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), MESSAGING_MENU_TYPE_APP, MessagingMenuAppClass))
-#define MESSAGING_MENU_IS_APP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MESSAGING_MENU_TYPE_APP))
-
-/**
- * MessagingMenuStatus:
- * @MESSAGING_MENU_STATUS_AVAILABLE: available
- * @MESSAGING_MENU_STATUS_AWAY: away
- * @MESSAGING_MENU_STATUS_BUSY: busy
- * @MESSAGING_MENU_STATUS_INVISIBLE: invisible
- * @MESSAGING_MENU_STATUS_OFFLINE: offline
- *
- * An enumeration for the possible chat statuses the messaging menu can be in.
- */
-typedef enum {
- MESSAGING_MENU_STATUS_AVAILABLE,
- MESSAGING_MENU_STATUS_AWAY,
- MESSAGING_MENU_STATUS_BUSY,
- MESSAGING_MENU_STATUS_INVISIBLE,
- MESSAGING_MENU_STATUS_OFFLINE
-} MessagingMenuStatus;
-
-
-typedef GObjectClass MessagingMenuAppClass;
-typedef struct _MessagingMenuApp MessagingMenuApp;
-
-GType messaging_menu_app_get_type (void) G_GNUC_CONST;
-
-MessagingMenuApp * messaging_menu_app_new (const gchar *desktop_id);
-
-void messaging_menu_app_register (MessagingMenuApp *app);
-void messaging_menu_app_unregister (MessagingMenuApp *app);
-
-void messaging_menu_app_set_status (MessagingMenuApp *app,
- MessagingMenuStatus status);
-
-void messaging_menu_app_insert_source (MessagingMenuApp *app,
- gint position,
- const gchar *id,
- GIcon *icon,
- const gchar *label);
-
-void messaging_menu_app_append_source (MessagingMenuApp *app,
- const gchar *id,
- GIcon *icon,
- const gchar *label);
-
-void messaging_menu_app_insert_source_with_count (MessagingMenuApp *app,
- gint position,
- const gchar *id,
- GIcon *icon,
- const gchar *label,
- guint count);
-
-void messaging_menu_app_append_source_with_count (MessagingMenuApp *app,
- const gchar *id,
- GIcon *icon,
- const gchar *label,
- guint count);
-
-void messaging_menu_app_insert_source_with_time (MessagingMenuApp *app,
- gint position,
- const gchar *id,
- GIcon *icon,
- const gchar *label,
- gint64 time);
-
-void messaging_menu_app_append_source_with_time (MessagingMenuApp *app,
- const gchar *id,
- GIcon *icon,
- const gchar *label,
- gint64 time);
-
-void messaging_menu_app_append_source_with_string (MessagingMenuApp *app,
- const gchar *id,
- GIcon *icon,
- const gchar *label,
- const gchar *str);
-
-void messaging_menu_app_insert_source_with_string (MessagingMenuApp *app,
- gint position,
- const gchar *id,
- GIcon *icon,
- const gchar *label,
- const gchar *str);
-
-void messaging_menu_app_remove_source (MessagingMenuApp *app,
- const gchar *source_id);
-
-gboolean messaging_menu_app_has_source (MessagingMenuApp *app,
- const gchar *source_id);
-
-void messaging_menu_app_set_source_label (MessagingMenuApp *app,
- const gchar *source_id,
- const gchar *label);
-
-void messaging_menu_app_set_source_icon (MessagingMenuApp *app,
- const gchar *source_id,
- GIcon *icon);
-
-void messaging_menu_app_set_source_count (MessagingMenuApp *app,
- const gchar *source_id,
- guint count);
-
-void messaging_menu_app_set_source_time (MessagingMenuApp *app,
- const gchar *source_id,
- gint64 time);
-
-void messaging_menu_app_set_source_string (MessagingMenuApp *app,
- const gchar *source_id,
- const gchar *str);
-
-void messaging_menu_app_draw_attention (MessagingMenuApp *app,
- const gchar *source_id);
-
-void messaging_menu_app_remove_attention (MessagingMenuApp *app,
- const gchar *source_id);
-
-void messaging_menu_app_append_message (MessagingMenuApp *app,
- MessagingMenuMessage *msg,
- const gchar *source_id,
- gboolean notify);
-
-MessagingMenuMessage * messaging_menu_app_get_message (MessagingMenuApp *app,
- const gchar *id);
-
-void messaging_menu_app_remove_message (MessagingMenuApp *app,
- MessagingMenuMessage *msg);
-
-void messaging_menu_app_remove_message_by_id (MessagingMenuApp *app,
- const gchar *id);
-
-G_END_DECLS
-
-#endif
diff --git a/libmessaging-menu/messaging-menu-message.c b/libmessaging-menu/messaging-menu-message.c
deleted file mode 100644
index b81cbea..0000000
--- a/libmessaging-menu/messaging-menu-message.c
+++ /dev/null
@@ -1,547 +0,0 @@
-/*
- * Copyright 2012 Canonical Ltd.
- *
- * 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/>.
- *
- * Authors:
- * Lars Uebernickel <lars.uebernickel@canonical.com>
- */
-
-#include "messaging-menu-message.h"
-
-typedef GObjectClass MessagingMenuMessageClass;
-
-struct _MessagingMenuMessage
-{
- GObject parent;
-
- gchar *id;
- GIcon *icon;
- gchar *title;
- gchar *subtitle;
- gchar *body;
- gint64 time;
- gboolean draws_attention;
-
- GSList *actions;
-};
-
-G_DEFINE_TYPE (MessagingMenuMessage, messaging_menu_message, G_TYPE_OBJECT);
-
-enum
-{
- PROP_0,
- PROP_ID,
- PROP_ICON,
- PROP_TITLE,
- PROP_SUBTITLE,
- PROP_BODY,
- PROP_TIME,
- PROP_DRAWS_ATTENTION,
- NUM_PROPERTIES
-};
-
-static GParamSpec *properties[NUM_PROPERTIES];
-
-typedef struct
-{
- gchar *id;
- gchar *label;
- GVariantType *parameter_type;
- GVariant *parameter_hint;
-} Action;
-
-static void
-action_free (gpointer data)
-{
- Action *action = data;
-
- g_free (action->id);
- g_free (action->label);
-
- if (action->parameter_type)
- g_variant_type_free (action->parameter_type);
-
- if (action->parameter_hint)
- g_variant_unref (action->parameter_hint);
-
- g_slice_free (Action, action);
-}
-
-static void
-messaging_menu_message_dispose (GObject *object)
-{
- MessagingMenuMessage *msg = MESSAGING_MENU_MESSAGE (object);
-
- g_clear_object (&msg->icon);
-
- G_OBJECT_CLASS (messaging_menu_message_parent_class)->dispose (object);
-}
-
-static void
-messaging_menu_message_finalize (GObject *object)
-{
- MessagingMenuMessage *msg = MESSAGING_MENU_MESSAGE (object);
-
- g_free (msg->id);
- g_free (msg->title);
- g_free (msg->subtitle);
- g_free (msg->body);
-
- g_slist_free_full (msg->actions, action_free);
- msg->actions = NULL;
-
- G_OBJECT_CLASS (messaging_menu_message_parent_class)->finalize (object);
-}
-
-static void
-messaging_menu_message_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- MessagingMenuMessage *msg = MESSAGING_MENU_MESSAGE (object);
-
- switch (property_id)
- {
- case PROP_ID:
- g_value_set_string (value, msg->id);
- break;
-
- case PROP_ICON:
- g_value_set_object (value, msg->icon);
- break;
-
- case PROP_TITLE:
- g_value_set_string (value, msg->title);
- break;
-
- case PROP_SUBTITLE:
- g_value_set_string (value, msg->subtitle);
- break;
-
- case PROP_BODY:
- g_value_set_string (value, msg->body);
-
- case PROP_TIME:
- g_value_set_int64 (value, msg->time);
- break;
-
- case PROP_DRAWS_ATTENTION:
- g_value_set_boolean (value, msg->draws_attention);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- }
-}
-
-static void
-messaging_menu_message_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- MessagingMenuMessage *msg = MESSAGING_MENU_MESSAGE (object);
-
- switch (property_id)
- {
- case PROP_ID:
- msg->id = g_value_dup_string (value);
- break;
-
- case PROP_ICON:
- msg->icon = g_value_dup_object (value);
- break;
-
- case PROP_TITLE:
- msg->title = g_value_dup_string (value);
- break;
-
- case PROP_SUBTITLE:
- msg->subtitle = g_value_dup_string (value);
- break;
-
- case PROP_BODY:
- msg->body = g_value_dup_string (value);
-
- case PROP_TIME:
- msg->time = g_value_get_int64 (value);
- break;
-
- case PROP_DRAWS_ATTENTION:
- messaging_menu_message_set_draws_attention (msg, g_value_get_boolean (value));
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- }
-}
-
-static void
-messaging_menu_message_class_init (MessagingMenuMessageClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->dispose = messaging_menu_message_dispose;
- object_class->finalize = messaging_menu_message_finalize;
- object_class->get_property = messaging_menu_message_get_property;
- object_class->set_property = messaging_menu_message_set_property;
-
- properties[PROP_ID] = g_param_spec_string ("id", "Id",
- "Unique id of the message",
- NULL,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
-
- properties[PROP_ICON] = g_param_spec_object ("icon", "Icon",
- "Icon of the message",
- G_TYPE_ICON,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
-
- properties[PROP_TITLE] = g_param_spec_string ("title", "Title",
- "Title of the message",
- NULL,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
-
- properties[PROP_SUBTITLE] = g_param_spec_string ("subtitle", "Subtitle",
- "Subtitle of the message",
- NULL,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
-
- properties[PROP_BODY] = g_param_spec_string ("body", "Body",
- "First lines of the body of the message",
- NULL,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
-
- properties[PROP_TIME] = g_param_spec_int64 ("time", "Time",
- "Time the message was sent, in microseconds", 0, G_MAXINT64, 0,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
-
- properties[PROP_DRAWS_ATTENTION] = g_param_spec_boolean ("draws-attention", "Draws attention",
- "Whether the message should draw attention",
- FALSE,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-
- g_object_class_install_properties (klass, NUM_PROPERTIES, properties);
-
- /**
- * MessagingMenuMessage::activate:
- * @msg: the #MessagingMenuMessage
- * @action: (allow-none): the id of activated action, or %NULL
- * @parameter: (allow-none): activation parameter, or %NULL
- *
- * Emitted when the user has activated the message. The message is
- * immediately removed from the application's menu, handlers of this
- * signal do not need to call messaging_menu_app_remove_message().
- */
- g_signal_new ("activate",
- MESSAGING_MENU_TYPE_MESSAGE,
- G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
- 0,
- NULL, NULL,
- g_cclosure_marshal_generic,
- G_TYPE_NONE, 2,
- G_TYPE_STRING,
- G_TYPE_VARIANT);
-}
-
-static void
-messaging_menu_message_init (MessagingMenuMessage *self)
-{
-}
-
-/**
- * messaging_menu_message_new:
- * @id: unique id of the message
- * @icon: (transfer full) (allow-none): a #GIcon representing the message
- * @title: the title of the message
- * @subtitle: (allow-none): the subtitle of the message
- * @body: (allow-none): the message body
- * @time: the time the message was received
- *
- * Creates a new #MessagingMenuMessage.
- *
- * Returns: (transfer full): a new #MessagingMenuMessage
- */
-MessagingMenuMessage *
-messaging_menu_message_new (const gchar *id,
- GIcon *icon,
- const gchar *title,
- const gchar *subtitle,
- const gchar *body,
- gint64 time)
-{
- g_return_val_if_fail (id != NULL, NULL);
- g_return_val_if_fail (title != NULL, NULL);
-
- return g_object_new (MESSAGING_MENU_TYPE_MESSAGE,
- "id", id,
- "icon", icon,
- "title", title,
- "subtitle", subtitle,
- "body", body,
- "time", time,
- NULL);
-}
-
-/**
- * messaging_menu_message_get_id:
- * @msg: a #MessagingMenuMessage
- *
- * Returns: the unique id of @msg
- */
-const gchar *
-messaging_menu_message_get_id (MessagingMenuMessage *msg)
-{
- g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL);
-
- return msg->id;
-}
-
-/**
- * messaging_menu_message_get_icon:
- * @msg: a #MessagingMenuMessage
- *
- * Returns: (transfer none): the icon of @msg
- */
-GIcon *
-messaging_menu_message_get_icon (MessagingMenuMessage *msg)
-{
- g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL);
-
- return msg->icon;
-}
-
-/**
- * messaging_menu_message_get_title:
- * @msg: a #MessagingMenuMessage
- *
- * Returns: the title of @msg
- */
-const gchar *
-messaging_menu_message_get_title (MessagingMenuMessage *msg)
-{
- g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL);
-
- return msg->title;
-}
-
-/**
- * messaging_menu_message_get_subtitle:
- * @msg: a #MessagingMenuMessage
- *
- * Returns: the subtitle of @msg
- */
-const gchar *
-messaging_menu_message_get_subtitle (MessagingMenuMessage *msg)
-{
- g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL);
-
- return msg->subtitle;
-}
-
-/**
- * messaging_menu_message_get_body:
- * @msg: a #MessagingMenuMessage
- *
- * Returns: the body of @msg
- */
-const gchar *
-messaging_menu_message_get_body (MessagingMenuMessage *msg)
-{
- g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL);
-
- return msg->body;
-}
-
-/**
- * messaging_menu_message_get_time:
- * @msg: a #MessagingMenuMessage
- *
- * Returns: the time at which @msg was received
- */
-gint64
-messaging_menu_message_get_time (MessagingMenuMessage *msg)
-{
- g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), 0);
-
- return msg->time;
-}
-
-/**
- * messaging_menu_message_get_draws_attention:
- * @msg: a #MessagingMenuMessage
- *
- * Returns: whether @msg is drawing attention
- */
-gboolean
-messaging_menu_message_get_draws_attention (MessagingMenuMessage *msg)
-{
- g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), FALSE);
-
- return msg->draws_attention;
-}
-
-/**
- * messaging_menu_message_set_draws_attention:
- * @msg: a #MessagingMenuMessage
- * @draws_attention: whether @msg should draw attention
- *
- * Sets whether @msg is drawing attention.
- */
-void
-messaging_menu_message_set_draws_attention (MessagingMenuMessage *msg,
- gboolean draws_attention)
-{
- g_return_if_fail (MESSAGING_MENU_IS_MESSAGE (msg));
-
- msg->draws_attention = draws_attention;
- g_object_notify_by_pspec (G_OBJECT (msg), properties[PROP_DRAWS_ATTENTION]);
-}
-
-/**
- * messaging_menu_message_add_action:
- * @msg: a #MessagingMenuMessage
- * @id: unique id of the action
- * @label: (allow-none): label of the action
- * @parameter_type: (allow-none): a #GVariantType
- * @parameter_hint: (allow-none): a #GVariant suggesting a valid range
- * for parameters
- *
- * Adds an action with @id and @label to @message. Actions are an
- * alternative way for users to activate a message. Note that messages
- * can still be activated without an action.
- *
- * If @parameter_type is non-%NULL, the action is able to receive user
- * input in addition to simply activating the action. Currently, only
- * string parameters are supported.
- *
- * A list of predefined parameters can be supplied as a #GVariant array
- * of @parameter_type in @parameter_hint. If @parameter_hint is
- * floating, it will be consumed.
- *
- * It is recommended to add at most two actions to a message.
- */
-void
-messaging_menu_message_add_action (MessagingMenuMessage *msg,
- const gchar *id,
- const gchar *label,
- const GVariantType *parameter_type,
- GVariant *parameter_hint)
-{
- Action *action;
-
- g_return_if_fail (MESSAGING_MENU_IS_MESSAGE (msg));
- g_return_if_fail (id != NULL);
-
- action = g_slice_new (Action);
- action->id = g_strdup (id);
- action->label = g_strdup (label);
- action->parameter_type = parameter_type ? g_variant_type_copy (parameter_type) : NULL;
- action->parameter_hint = parameter_hint ? g_variant_ref_sink (parameter_hint) : NULL;
-
- msg->actions = g_slist_append (msg->actions, action);
-}
-
-static GVariant *
-action_to_variant (Action *action)
-{
- GVariantBuilder builder;
-
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
-
- g_variant_builder_add (&builder, "{sv}", "name", g_variant_new_string (action->id));
-
- if (action->label)
- g_variant_builder_add (&builder, "{sv}", "label", g_variant_new_string (action->label));
-
- if (action->parameter_type)
- {
- gchar *type = g_variant_type_dup_string (action->parameter_type);
- g_variant_builder_add (&builder, "{sv}", "parameter-type", g_variant_new_signature (type));
- g_free (type);
- }
-
- if (action->parameter_hint)
- g_variant_builder_add (&builder, "{sv}", "parameter-hint", action->parameter_hint);
-
- return g_variant_builder_end (&builder);
-}
-
-/*<internal>
- * _messaging_menu_message_to_variant:
- * @msg: a #MessagingMenuMessage
- *
- * Serializes @msg to a #GVariant of the form (sssssxaa{sv}b):
- *
- * id
- * icon
- * title
- * subtitle
- * body
- * time
- * array of action dictionaries
- * draws_attention
- *
- * Returns: a new floating #GVariant instance
- */
-GVariant *
-_messaging_menu_message_to_variant (MessagingMenuMessage *msg)
-{
- GVariantBuilder builder;
- GSList *it;
-
- g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL);
-
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("(sssssxaa{sv}b)"));
-
- g_variant_builder_add (&builder, "s", msg->id);
-
- if (msg->icon)
- {
- gchar *iconstr;
-
- iconstr = g_icon_to_string (msg->icon);
- g_variant_builder_add (&builder, "s", iconstr);
-
- g_free (iconstr);
- }
- else
- g_variant_builder_add (&builder, "s", "");
-
- g_variant_builder_add (&builder, "s", msg->title ? msg->title : "");
- g_variant_builder_add (&builder, "s", msg->subtitle ? msg->subtitle : "");
- g_variant_builder_add (&builder, "s", msg->body ? msg->body : "");
- g_variant_builder_add (&builder, "x", msg->time);
-
- g_variant_builder_open (&builder, G_VARIANT_TYPE ("aa{sv}"));
- for (it = msg->actions; it; it = it->next)
- g_variant_builder_add_value (&builder, action_to_variant (it->data));
- g_variant_builder_close (&builder);
-
- g_variant_builder_add (&builder, "b", msg->draws_attention);
-
- return g_variant_builder_end (&builder);
-}
diff --git a/libmessaging-menu/messaging-menu-message.h b/libmessaging-menu/messaging-menu-message.h
deleted file mode 100644
index 4708246..0000000
--- a/libmessaging-menu/messaging-menu-message.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2012 Canonical Ltd.
- *
- * 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/>.
- *
- * Authors:
- * Lars Uebernickel <lars.uebernickel@canonical.com>
- */
-
-#ifndef __messaging_menu_message_h__
-#define __messaging_menu_message_h__
-
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-#define MESSAGING_MENU_TYPE_MESSAGE (messaging_menu_message_get_type ())
-#define MESSAGING_MENU_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MESSAGING_MENU_TYPE_MESSAGE, MessagingMenuMessage))
-#define MESSAGING_MENU_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MESSAGING_MENU_TYPE_MESSAGE, MessagingMenuMessageClass))
-#define MESSAGING_MENU_IS_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MESSAGING_MENU_TYPE_MESSAGE))
-#define MESSAGING_MENU_IS_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MESSAGING_MENU_TYPE_MESSAGE))
-#define MESSAGING_MENU_MESSAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MESSAGING_MENU_TYPE_MESSAGE, MessagingMenuMessageClass))
-
-typedef struct _MessagingMenuMessage MessagingMenuMessage;
-
-GType messaging_menu_message_get_type (void) G_GNUC_CONST;
-
-MessagingMenuMessage * messaging_menu_message_new (const gchar *id,
- GIcon *icon,
- const gchar *title,
- const gchar *subtitle,
- const gchar *body,
- gint64 time);
-
-const gchar * messaging_menu_message_get_id (MessagingMenuMessage *msg);
-
-GIcon * messaging_menu_message_get_icon (MessagingMenuMessage *msg);
-
-const gchar * messaging_menu_message_get_title (MessagingMenuMessage *msg);
-
-const gchar * messaging_menu_message_get_subtitle (MessagingMenuMessage *msg);
-
-const gchar * messaging_menu_message_get_body (MessagingMenuMessage *msg);
-
-gint64 messaging_menu_message_get_time (MessagingMenuMessage *msg);
-
-gboolean messaging_menu_message_get_draws_attention (MessagingMenuMessage *msg);
-
-void messaging_menu_message_set_draws_attention (MessagingMenuMessage *msg,
- gboolean draws_attention);
-
-void messaging_menu_message_add_action (MessagingMenuMessage *msg,
- const gchar *id,
- const gchar *label,
- const GVariantType *parameter_type,
- GVariant *parameter_hint);
-
-G_END_DECLS
-
-#endif
diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu.c
index 421a09f..467a67a 100644
--- a/libmessaging-menu/messaging-menu-app.c
+++ b/libmessaging-menu/messaging-menu.c
@@ -17,12 +17,10 @@
* Lars Uebernickel <lars.uebernickel@canonical.com>
*/
-#include "messaging-menu-app.h"
+#include "messaging-menu.h"
#include "indicator-messages-service.h"
-#include "indicator-messages-application.h"
#include <gio/gdesktopappinfo.h>
-#include <string.h>
/**
* SECTION:messaging-menu
@@ -104,14 +102,14 @@ struct _MessagingMenuApp
int registered; /* -1 for unknown */
MessagingMenuStatus status;
gboolean status_set;
+ GSimpleActionGroup *source_actions;
+ GMenu *menu;
GDBusConnection *bus;
- GHashTable *messages;
- GList *sources;
- IndicatorMessagesApplication *app_interface;
-
IndicatorMessagesService *messages_service;
guint watch_id;
+ guint action_export_id;
+ guint menu_export_id;
GCancellable *cancellable;
};
@@ -135,61 +133,10 @@ static guint signals[N_SIGNALS];
static const gchar *status_ids[] = { "available", "away", "busy", "invisible", "offline" };
-typedef struct
-{
- gchar *id;
- GIcon *icon;
- gchar *label;
-
- guint32 count;
- gint64 time;
- gchar *string;
- gboolean draws_attention;
-} Source;
-
static void global_status_changed (IndicatorMessagesService *service,
const gchar *status_str,
gpointer user_data);
-/* in messaging-menu-message.c */
-GVariant * _messaging_menu_message_to_variant (MessagingMenuMessage *msg);
-
-static void
-source_free (gpointer data)
-{
- Source *source = data;
-
- if (source)
- {
- g_free (source->id);
- g_clear_object (&source->icon);
- g_free (source->label);
- g_free (source->string);
- g_slice_free (Source, source);
- }
-}
-
-static GVariant *
-source_to_variant (Source *source)
-{
- GVariant *v;
- gchar *iconstr;
-
- iconstr = source->icon ? g_icon_to_string (source->icon) : NULL;
-
- v = g_variant_new ("(sssuxsb)", source->id,
- source->label,
- iconstr ? iconstr : "",
- source->count,
- source->time,
- source->string ? source->string : "",
- source->draws_attention);
-
- g_free (iconstr);
-
- return v;
-}
-
static gchar *
messaging_menu_app_get_dbus_object_path (MessagingMenuApp *app)
{
@@ -208,14 +155,18 @@ messaging_menu_app_get_dbus_object_path (MessagingMenuApp *app)
}
static void
-messaging_menu_app_got_bus (GObject *source,
- GAsyncResult *res,
- gpointer user_data)
+export_menus_and_actions (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
{
MessagingMenuApp *app = user_data;
GError *error = NULL;
gchar *object_path;
+ object_path = messaging_menu_app_get_dbus_object_path (app);
+ if (!object_path)
+ return;
+
app->bus = g_bus_get_finish (res, &error);
if (app->bus == NULL)
{
@@ -224,13 +175,23 @@ messaging_menu_app_got_bus (GObject *source,
return;
}
- object_path = messaging_menu_app_get_dbus_object_path (app);
+ app->action_export_id = g_dbus_connection_export_action_group (app->bus,
+ object_path,
+ G_ACTION_GROUP (app->source_actions),
+ &error);
+ if (!app->action_export_id)
+ {
+ g_warning ("unable to export action group: %s", error->message);
+ g_clear_error (&error);
+ }
- if (object_path &&
- !g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (app->app_interface),
- app->bus, object_path, &error))
+ app->menu_export_id = g_dbus_connection_export_menu_model (app->bus,
+ object_path,
+ G_MENU_MODEL (app->menu),
+ &error);
+ if (!app->menu_export_id)
{
- g_warning ("unable to export application interface: %s", error->message);
+ g_warning ("unable to export menu: %s", error->message);
g_clear_error (&error);
}
@@ -253,7 +214,7 @@ messaging_menu_app_set_desktop_id (MessagingMenuApp *app,
g_bus_get (G_BUS_TYPE_SESSION,
app->cancellable,
- messaging_menu_app_got_bus,
+ export_menus_and_actions,
app);
}
@@ -287,6 +248,20 @@ messaging_menu_app_dispose (GObject *object)
{
MessagingMenuApp *app = MESSAGING_MENU_APP (object);
+ if (app->bus)
+ {
+ if (app->action_export_id > 0)
+ g_dbus_connection_unexport_action_group (app->bus, app->action_export_id);
+
+ if (app->menu_export_id > 0)
+ g_dbus_connection_unexport_menu_model (app->bus, app->menu_export_id);
+
+ app->action_export_id = 0;
+ app->menu_export_id = 0;
+ g_object_unref (app->bus);
+ app->bus = NULL;
+ }
+
if (app->watch_id > 0)
{
g_bus_unwatch_name (app->watch_id);
@@ -312,14 +287,9 @@ messaging_menu_app_dispose (GObject *object)
g_clear_object (&app->messages_service);
}
- g_clear_pointer (&app->messages, g_hash_table_unref);
-
- g_list_free_full (app->sources, source_free);
- app->sources = NULL;
-
- g_clear_object (&app->app_interface);
g_clear_object (&app->appinfo);
- g_clear_object (&app->bus);
+ g_clear_object (&app->source_actions);
+ g_clear_object (&app->menu);
G_OBJECT_CLASS (messaging_menu_app_parent_class)->dispose (object);
}
@@ -450,168 +420,6 @@ indicator_messages_vanished (GDBusConnection *bus,
}
}
-static gboolean
-messaging_menu_app_list_sources (IndicatorMessagesApplication *app_interface,
- GDBusMethodInvocation *invocation,
- gpointer user_data)
-{
- MessagingMenuApp *app = user_data;
- GVariantBuilder builder;
- GList *it;
-
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sssuxsb)"));
-
- for (it = app->sources; it; it = it->next)
- g_variant_builder_add_value (&builder, source_to_variant (it->data));
-
- indicator_messages_application_complete_list_sources (app_interface,
- invocation,
- g_variant_builder_end (&builder));
-
- return TRUE;
-}
-
-static gint
-compare_source_id (gconstpointer a,
- gconstpointer b)
-{
- const Source *source = a;
- const gchar *id = b;
-
- return strcmp (source->id, id);
-}
-
-static gboolean
-messaging_menu_app_remove_source_internal (MessagingMenuApp *app,
- const gchar *source_id)
-{
- GList *node;
-
- node = g_list_find_custom (app->sources, source_id, compare_source_id);
- if (node)
- {
- source_free (node->data);
- app->sources = g_list_delete_link (app->sources, node);
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-messaging_menu_app_remove_message_internal (MessagingMenuApp *app,
- const gchar *message_id)
-{
- return g_hash_table_remove (app->messages, message_id);
-}
-
-static gboolean
-messaging_menu_app_activate_source (IndicatorMessagesApplication *app_interface,
- GDBusMethodInvocation *invocation,
- const gchar *source_id,
- gpointer user_data)
-{
- MessagingMenuApp *app = user_data;
- GQuark q = g_quark_from_string (source_id);
-
- /* Activate implies removing the source, no need for SourceRemoved */
- if (messaging_menu_app_remove_source_internal (app, source_id))
- g_signal_emit (app, signals[ACTIVATE_SOURCE], q, source_id);
-
- indicator_messages_application_complete_activate_source (app_interface, invocation);
-
- return TRUE;
-}
-
-static gboolean
-messaging_menu_app_list_messages (IndicatorMessagesApplication *app_interface,
- GDBusMethodInvocation *invocation,
- gpointer user_data)
-{
- MessagingMenuApp *app = user_data;
- GVariantBuilder builder;
- GHashTableIter iter;
- MessagingMenuMessage *message;
-
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sssssxaa{sv}b)"));
-
- g_hash_table_iter_init (&iter, app->messages);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &message))
- g_variant_builder_add_value (&builder, _messaging_menu_message_to_variant (message));
-
- indicator_messages_application_complete_list_messages (app_interface,
- invocation,
- g_variant_builder_end (&builder));
-
- return TRUE;
-}
-
-static gboolean
-messaging_menu_app_activate_message (IndicatorMessagesApplication *app_interface,
- GDBusMethodInvocation *invocation,
- const gchar *message_id,
- const gchar *action_id,
- GVariant *params,
- gpointer user_data)
-{
- MessagingMenuApp *app = user_data;
- MessagingMenuMessage *msg;
-
- msg = g_hash_table_lookup (app->messages, message_id);
- if (msg)
- {
- if (*action_id)
- {
- gchar *signal;
-
- signal = g_strconcat ("activate::", action_id, NULL);
-
- if (g_variant_n_children (params))
- {
- GVariant *param;
-
- g_variant_get_child (params, 0, "v", &param);
- g_signal_emit_by_name (msg, signal, action_id, param);
-
- g_variant_unref (param);
- }
- else
- g_signal_emit_by_name (msg, signal, action_id, NULL);
-
- g_free (signal);
- }
- else
- g_signal_emit_by_name (msg, "activate", NULL, NULL);
-
-
- /* Activate implies removing the message, no need for MessageRemoved */
- messaging_menu_app_remove_message_internal (app, message_id);
- }
-
- indicator_messages_application_complete_activate_message (app_interface, invocation);
-
- return TRUE;
-}
-
-static gboolean
-messaging_menu_app_dismiss (IndicatorMessagesApplication *app_interface,
- GDBusMethodInvocation *invocation,
- const gchar * const *sources,
- const gchar * const *messages,
- gpointer user_data)
-{
- MessagingMenuApp *app = user_data;
- const gchar * const *it;
-
- for (it = sources; *it; it++)
- messaging_menu_app_remove_source_internal (app, *it);
-
- for (it = messages; *it; it++)
- messaging_menu_app_remove_message_internal (app, *it);
-
- return TRUE;
-}
-
static void
messaging_menu_app_init (MessagingMenuApp *app)
{
@@ -619,21 +427,15 @@ messaging_menu_app_init (MessagingMenuApp *app)
app->status_set = FALSE;
app->bus = NULL;
+ app->action_export_id = 0;
+ app->menu_export_id = 0;
+
app->cancellable = g_cancellable_new ();
- app->app_interface = indicator_messages_application_skeleton_new ();
- g_signal_connect (app->app_interface, "handle-list-sources",
- G_CALLBACK (messaging_menu_app_list_sources), app);
- g_signal_connect (app->app_interface, "handle-activate-source",
- G_CALLBACK (messaging_menu_app_activate_source), app);
- g_signal_connect (app->app_interface, "handle-list-messages",
- G_CALLBACK (messaging_menu_app_list_messages), app);
- g_signal_connect (app->app_interface, "handle-activate-message",
- G_CALLBACK (messaging_menu_app_activate_message), app);
- g_signal_connect (app->app_interface, "handle-dismiss",
- G_CALLBACK (messaging_menu_app_dismiss), app);
+ app->source_actions = g_simple_action_group_new ();
+ app->menu = g_menu_new ();
- app->messages = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ app->cancellable = g_cancellable_new ();
app->watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
"com.canonical.indicator.messages",
@@ -806,73 +608,124 @@ global_status_changed (IndicatorMessagesService *service,
g_signal_emit (app, signals[STATUS_CHANGED], 0, status);
}
-static Source *
-messaging_menu_app_lookup_source (MessagingMenuApp *app,
- const gchar *id)
+static void
+source_action_activated (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ MessagingMenuApp *app = user_data;
+ const gchar *name = g_action_get_name (G_ACTION (action));
+ GQuark q = g_quark_from_string (name);
+
+ messaging_menu_app_remove_source (app, name);
+
+ g_signal_emit (app, signals[ACTIVATE_SOURCE], q, name);
+}
+
+static void
+messaging_menu_app_insert_source_action (MessagingMenuApp *app,
+ gint position,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ GVariant *state)
{
- GList *node;
+ GSimpleAction *action;
+ GMenuItem *menuitem;
+
+ g_return_if_fail (MESSAGING_MENU_IS_APP (app));
+ g_return_if_fail (id != NULL);
+
+ if (g_simple_action_group_lookup (app->source_actions, id))
+ {
+ g_warning ("a source with id '%s' already exists", id);
+ return;
+ }
- node = g_list_find_custom (app->sources, id, compare_source_id);
+ action = g_simple_action_new_stateful (id, NULL, state);
+ g_signal_connect (action, "activate",
+ G_CALLBACK (source_action_activated), app);
+ g_simple_action_group_insert (app->source_actions, G_ACTION (action));
+ g_object_unref (action);
- return node ? node->data : NULL;
+ menuitem = g_menu_item_new (label, NULL);
+ g_menu_item_set_action_and_target_value (menuitem, id, NULL);
+ g_menu_item_set_attribute (menuitem, "x-canonical-type", "s", "ImSourceMenuItem");
+ if (icon)
+ {
+ gchar *iconstr = g_icon_to_string (icon);
+ g_menu_item_set_attribute (menuitem, "x-canonical-icon", "s", iconstr);
+ g_free (iconstr);
+ }
+ g_menu_insert_item (app->menu, position, menuitem);
+ g_object_unref (menuitem);
}
-static Source *
-messaging_menu_app_get_source (MessagingMenuApp *app,
- const gchar *id)
+static GSimpleAction *
+messaging_menu_app_get_source_action (MessagingMenuApp *app,
+ const gchar *source_id)
+
{
- Source *source;
+ GAction *action;
- source = messaging_menu_app_lookup_source (app, id);
- if (!source)
- g_warning ("a source with id '%s' doesn't exist", id);
+ g_return_val_if_fail (MESSAGING_MENU_IS_APP (app), NULL);
+ g_return_val_if_fail (source_id != NULL, NULL);
- return source;
+ action = g_simple_action_group_lookup (app->source_actions, source_id);
+ if (action == NULL)
+ g_warning ("a source with id '%s' doesn't exist", source_id);
+
+ return G_SIMPLE_ACTION (action);
}
static void
-messaging_menu_app_notify_source_changed (MessagingMenuApp *app,
- Source *source)
+messaging_menu_app_set_source_action (MessagingMenuApp *app,
+ const gchar *source_id,
+ guint count,
+ gint64 time,
+ const gchar *string)
{
- indicator_messages_application_emit_source_changed (app->app_interface,
- source_to_variant (source));
+ GSimpleAction *action;
+ GVariant *state;
+ gboolean draws_attention;
+ GVariant *new_state;
+
+ action = messaging_menu_app_get_source_action (app, source_id);
+ if (!action)
+ return;
+
+ state = g_action_get_state (G_ACTION (action));
+ g_variant_get_child (state, 3, "b", &draws_attention);
+
+ new_state = g_variant_new ("(uxsb)", count, time, string, draws_attention);
+ g_simple_action_set_state (action, new_state);
+
+ g_variant_unref (state);
}
static void
-messaging_menu_app_insert_source_internal (MessagingMenuApp *app,
- gint position,
- const gchar *id,
- GIcon *icon,
- const gchar *label,
- guint count,
- gint64 time,
- const gchar *string)
+messaging_menu_app_set_draws_attention (MessagingMenuApp *app,
+ const gchar *source_id,
+ gboolean draws_attention)
{
- Source *source;
+ GSimpleAction *action;
+ GVariant *state;
+ guint count;
+ gint64 time;
+ const gchar *string;
+ GVariant *new_state;
- g_return_if_fail (MESSAGING_MENU_IS_APP (app));
- g_return_if_fail (id != NULL);
- g_return_if_fail (label != NULL);
+ action = messaging_menu_app_get_source_action (app, source_id);
+ if (!action)
+ return;
- if (messaging_menu_app_lookup_source (app, id))
- {
- g_warning ("a source with id '%s' already exists", id);
- return;
- }
+ state = g_action_get_state (G_ACTION (action));
+ g_variant_get (state, "(ux&sb)", &count, &time, &string, NULL);
- source = g_slice_new0 (Source);
- source->id = g_strdup (id);
- source->label = g_strdup (label);
- if (icon)
- source->icon = g_object_ref (icon);
- source->count = count;
- source->time = time;
- source->string = g_strdup (string);
- app->sources = g_list_insert (app->sources, source, position);
-
- indicator_messages_application_emit_source_added (app->app_interface,
- position,
- source_to_variant (source));
+ new_state = g_variant_new ("(uxsb)", count, time, string, draws_attention);
+ g_simple_action_set_state (action, new_state);
+
+ g_variant_unref (state);
}
/**
@@ -949,7 +802,8 @@ messaging_menu_app_insert_source_with_count (MessagingMenuApp *app,
const gchar *label,
guint count)
{
- messaging_menu_app_insert_source_internal (app, position, id, icon, label, count, 0, "");
+ messaging_menu_app_insert_source_action (app, position, id, icon, label,
+ g_variant_new ("(uxsb)", count, 0, "", FALSE));
}
/**
@@ -1003,7 +857,8 @@ messaging_menu_app_insert_source_with_time (MessagingMenuApp *app,
const gchar *label,
gint64 time)
{
- messaging_menu_app_insert_source_internal (app, position, id, icon, label, 0, time, "");
+ messaging_menu_app_insert_source_action (app, position, id, icon, label,
+ g_variant_new ("(uxsb)", 0, time, "", FALSE));
}
/**
@@ -1059,7 +914,8 @@ messaging_menu_app_insert_source_with_string (MessagingMenuApp *app,
const gchar *label,
const gchar *str)
{
- messaging_menu_app_insert_source_internal (app, position, id, icon, label, 0, 0, str);
+ messaging_menu_app_insert_source_action (app, position, id, icon, label,
+ g_variant_new ("(uxsb)", 0, 0, str, FALSE));
}
/**
@@ -1099,11 +955,34 @@ void
messaging_menu_app_remove_source (MessagingMenuApp *app,
const gchar *source_id)
{
+ int n_items;
+ int i;
+
g_return_if_fail (MESSAGING_MENU_IS_APP (app));
g_return_if_fail (source_id != NULL);
- if (messaging_menu_app_remove_source_internal (app, source_id))
- indicator_messages_application_emit_source_removed (app->app_interface, source_id);
+ if (g_simple_action_group_lookup (app->source_actions, source_id) == NULL)
+ return;
+
+ n_items = g_menu_model_get_n_items (G_MENU_MODEL (app->menu));
+ for (i = 0; i < n_items; i++)
+ {
+ gchar *action;
+
+ if (g_menu_model_get_item_attribute (G_MENU_MODEL (app->menu), i,
+ "action", "s", &action))
+ {
+ if (!g_strcmp0 (action, source_id))
+ {
+ g_menu_remove (app->menu, i);
+ break;
+ }
+
+ g_free (action);
+ }
+ }
+
+ g_simple_action_group_remove (app->source_actions, source_id);
}
/**
@@ -1120,7 +999,46 @@ messaging_menu_app_has_source (MessagingMenuApp *app,
g_return_val_if_fail (MESSAGING_MENU_IS_APP (app), FALSE);
g_return_val_if_fail (source_id != NULL, FALSE);
- return messaging_menu_app_lookup_source (app, source_id) != NULL;
+ return g_simple_action_group_lookup (app->source_actions, source_id) != NULL;
+}
+
+static GMenuItem *
+g_menu_find_item_with_action (GMenu *menu,
+ const gchar *action,
+ gint *out_pos)
+{
+ gint i;
+ gint n_elements;
+ GMenuItem *item = NULL;
+
+ n_elements = g_menu_model_get_n_items (G_MENU_MODEL (menu));
+
+ for (i = 0; i < n_elements && item == NULL; i++)
+ {
+ GVariant *attr;
+
+ item = g_menu_item_new_from_model (G_MENU_MODEL (menu), i);
+ attr = g_menu_item_get_attribute_value (item, G_MENU_ATTRIBUTE_ACTION, G_VARIANT_TYPE_STRING);
+
+ if (!g_str_equal (action, g_variant_get_string (attr, NULL)))
+ g_clear_object (&item);
+
+ g_variant_unref (attr);
+ }
+
+ if (item && out_pos)
+ *out_pos = i - 1;
+
+ return item;
+}
+
+static void
+g_menu_replace_item (GMenu *menu,
+ gint pos,
+ GMenuItem *item)
+{
+ g_menu_remove (menu, pos);
+ g_menu_insert_item (menu, pos, item);
}
/**
@@ -1136,19 +1054,21 @@ messaging_menu_app_set_source_label (MessagingMenuApp *app,
const gchar *source_id,
const gchar *label)
{
- Source *source;
+ gint pos;
+ GMenuItem *item;
g_return_if_fail (MESSAGING_MENU_IS_APP (app));
g_return_if_fail (source_id != NULL);
g_return_if_fail (label != NULL);
- source = messaging_menu_app_get_source (app, source_id);
- if (source)
- {
- g_free (source->label);
- source->label = g_strdup (label);
- messaging_menu_app_notify_source_changed (app, source);
- }
+ item = g_menu_find_item_with_action (app->menu, source_id, &pos);
+ if (item == NULL)
+ return;
+
+ g_menu_item_set_attribute (item, G_MENU_ATTRIBUTE_LABEL, "s", label);
+ g_menu_replace_item (app->menu, pos, item);
+
+ g_object_unref (item);
}
/**
@@ -1164,19 +1084,33 @@ messaging_menu_app_set_source_icon (MessagingMenuApp *app,
const gchar *source_id,
GIcon *icon)
{
- Source *source;
+ gint pos;
+ GMenuItem *item;
g_return_if_fail (MESSAGING_MENU_IS_APP (app));
g_return_if_fail (source_id != NULL);
- source = messaging_menu_app_get_source (app, source_id);
- if (source)
+ item = g_menu_find_item_with_action (app->menu, source_id, &pos);
+ if (item == NULL)
+ return;
+
+ if (icon)
+ {
+ gchar *iconstr;
+
+ iconstr = g_icon_to_string (icon);
+ g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr);
+
+ g_free (iconstr);
+ }
+ else
{
- g_clear_object (&source->icon);
- if (icon)
- source->icon = g_object_ref (icon);
- messaging_menu_app_notify_source_changed (app, source);
+ g_menu_item_set_attribute_value (item, "x-canonical-icon", NULL);
}
+
+ g_menu_replace_item (app->menu, pos, item);
+
+ g_object_unref (item);
}
/**
@@ -1191,17 +1125,7 @@ void messaging_menu_app_set_source_count (MessagingMenuApp *app,
const gchar *source_id,
guint count)
{
- Source *source;
-
- g_return_if_fail (MESSAGING_MENU_IS_APP (app));
- g_return_if_fail (source_id != NULL);
-
- source = messaging_menu_app_get_source (app, source_id);
- if (source)
- {
- source->count = count;
- messaging_menu_app_notify_source_changed (app, source);
- }
+ messaging_menu_app_set_source_action (app, source_id, count, 0, "");
}
/**
@@ -1217,17 +1141,7 @@ messaging_menu_app_set_source_time (MessagingMenuApp *app,
const gchar *source_id,
gint64 time)
{
- Source *source;
-
- g_return_if_fail (MESSAGING_MENU_IS_APP (app));
- g_return_if_fail (source_id != NULL);
-
- source = messaging_menu_app_get_source (app, source_id);
- if (source)
- {
- source->time = time;
- messaging_menu_app_notify_source_changed (app, source);
- }
+ messaging_menu_app_set_source_action (app, source_id, 0, time, "");
}
/**
@@ -1243,18 +1157,7 @@ messaging_menu_app_set_source_string (MessagingMenuApp *app,
const gchar *source_id,
const gchar *str)
{
- Source *source;
-
- g_return_if_fail (MESSAGING_MENU_IS_APP (app));
- g_return_if_fail (source_id != NULL);
-
- source = messaging_menu_app_get_source (app, source_id);
- if (source)
- {
- g_free (source->string);
- source->string = g_strdup (str);
- messaging_menu_app_notify_source_changed (app, source);
- }
+ messaging_menu_app_set_source_action (app, source_id, 0, 0, str);
}
/**
@@ -1272,17 +1175,7 @@ void
messaging_menu_app_draw_attention (MessagingMenuApp *app,
const gchar *source_id)
{
- Source *source;
-
- g_return_if_fail (MESSAGING_MENU_IS_APP (app));
- g_return_if_fail (source_id != NULL);
-
- source = messaging_menu_app_get_source (app, source_id);
- if (source)
- {
- source->draws_attention = TRUE;
- messaging_menu_app_notify_source_changed (app, source);
- }
+ messaging_menu_app_set_draws_attention (app, source_id, TRUE);
}
/**
@@ -1303,131 +1196,5 @@ void
messaging_menu_app_remove_attention (MessagingMenuApp *app,
const gchar *source_id)
{
- Source *source;
-
- g_return_if_fail (MESSAGING_MENU_IS_APP (app));
- g_return_if_fail (source_id != NULL);
-
- source = messaging_menu_app_get_source (app, source_id);
- if (source)
- {
- source->draws_attention = FALSE;
- messaging_menu_app_notify_source_changed (app, source);
- }
-}
-
-/**
- * messaging_menu_app_append_message:
- * @app: a #MessagingMenuApp
- * @msg: the #MessagingMenuMessage to append
- * @source_id: (allow-none): the source id to which @msg is added, or NULL
- * @notify: whether a notification bubble should be shown for this
- * message
- *
- * Appends @msg to the source with id @source_id of @app. The messaging
- * menu might not display this message immediately if other messages are
- * queued before this one.
- *
- * If @source_id has a count associated with it, that count will be
- * increased by one.
- *
- * If @source_id is %NULL, @msg won't be associated with a source.
- */
-void
-messaging_menu_app_append_message (MessagingMenuApp *app,
- MessagingMenuMessage *msg,
- const gchar *source_id,
- gboolean notify)
-{
- const gchar *id;
-
- g_return_if_fail (MESSAGING_MENU_IS_APP (app));
- g_return_if_fail (MESSAGING_MENU_IS_MESSAGE (msg));
-
- id = messaging_menu_message_get_id (msg);
-
- if (g_hash_table_lookup (app->messages, id))
- {
- g_warning ("a message with id '%s' already exists", id);
- return;
- }
-
- g_hash_table_insert (app->messages, g_strdup (id), g_object_ref (msg));
- indicator_messages_application_emit_message_added (app->app_interface,
- _messaging_menu_message_to_variant (msg));
-
- if (source_id)
- {
- Source *source;
-
- source = messaging_menu_app_get_source (app, source_id);
- if (source && source->count >= 0)
- {
- source->count++;
- messaging_menu_app_notify_source_changed (app, source);
- }
- }
-}
-
-/**
- * messaging_menu_app_get_message:
- * @app: a #MessagingMenuApp
- * @id: id of the message to retrieve
- *
- * Retrieves the message with @id, that was added with
- * messaging_menu_app_append_message().
- *
- * Returns: (transfer none) (allow-none): the #MessagingMenuApp with
- * @id, or %NULL
- */
-MessagingMenuMessage *
-messaging_menu_app_get_message (MessagingMenuApp *app,
- const gchar *id)
-{
- g_return_val_if_fail (MESSAGING_MENU_IS_APP (app), NULL);
- g_return_val_if_fail (id != NULL, NULL);
-
- return g_hash_table_lookup (app->messages, id);
-}
-
-/**
- * messaging_menu_app_remove_message:
- * @app: a #MessagingMenuApp
- * @msg: the #MessagingMenuMessage to remove
- *
- * Removes @msg from @app.
- *
- * If @source_id has a count associated with it, that count will be
- * decreased by one.
- */
-void
-messaging_menu_app_remove_message (MessagingMenuApp *app,
- MessagingMenuMessage *msg)
-{
- /* take a ref of @msg here to make sure the pointer returned by
- * _get_id() is valid for the duration of remove_message_by_id. */
- g_object_ref (msg);
- messaging_menu_app_remove_message_by_id (app, messaging_menu_message_get_id (msg));
- g_object_unref (msg);
-}
-
-/**
- * messaging_menu_app_remove_message_by_id:
- * @app: a #MessagingMenuApp
- * @id: the unique id of @msg
- *
- * Removes the message with the id @id from @app.
- *
- * If @source_id has a count associated with it, that count will be
- * decreased by one.
- */
-void
-messaging_menu_app_remove_message_by_id (MessagingMenuApp *app,
- const gchar *id)
-{
- g_return_if_fail (MESSAGING_MENU_IS_APP (app));
- g_return_if_fail (id != NULL);
-
- if (messaging_menu_app_remove_message_internal (app, id))
- indicator_messages_application_emit_message_removed (app->app_interface, id);
+ messaging_menu_app_set_draws_attention (app, source_id, FALSE);
}
diff --git a/libmessaging-menu/messaging-menu.h b/libmessaging-menu/messaging-menu.h
index 929b229..6c405c7 100644
--- a/libmessaging-menu/messaging-menu.h
+++ b/libmessaging-menu/messaging-menu.h
@@ -20,6 +20,129 @@
#ifndef __messaging_menu_h__
#define __messaging_menu_h__
-#include "messaging-menu-app.h"
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define MESSAGING_MENU_TYPE_APP messaging_menu_app_get_type()
+#define MESSAGING_MENU_APP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MESSAGING_MENU_TYPE_APP, MessagingMenuApp))
+#define MESSAGING_MENU_APP_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), MESSAGING_MENU_TYPE_APP, MessagingMenuAppClass))
+#define MESSAGING_MENU_IS_APP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MESSAGING_MENU_TYPE_APP))
+
+/**
+ * MessagingMenuStatus:
+ * @MESSAGING_MENU_STATUS_AVAILABLE: available
+ * @MESSAGING_MENU_STATUS_AWAY: away
+ * @MESSAGING_MENU_STATUS_BUSY: busy
+ * @MESSAGING_MENU_STATUS_INVISIBLE: invisible
+ * @MESSAGING_MENU_STATUS_OFFLINE: offline
+ *
+ * An enumeration for the possible chat statuses the messaging menu can be in.
+ */
+typedef enum {
+ MESSAGING_MENU_STATUS_AVAILABLE,
+ MESSAGING_MENU_STATUS_AWAY,
+ MESSAGING_MENU_STATUS_BUSY,
+ MESSAGING_MENU_STATUS_INVISIBLE,
+ MESSAGING_MENU_STATUS_OFFLINE
+} MessagingMenuStatus;
+
+
+typedef GObjectClass MessagingMenuAppClass;
+typedef struct _MessagingMenuApp MessagingMenuApp;
+
+GType messaging_menu_app_get_type (void) G_GNUC_CONST;
+
+MessagingMenuApp * messaging_menu_app_new (const gchar *desktop_id);
+
+void messaging_menu_app_register (MessagingMenuApp *app);
+void messaging_menu_app_unregister (MessagingMenuApp *app);
+
+void messaging_menu_app_set_status (MessagingMenuApp *app,
+ MessagingMenuStatus status);
+
+void messaging_menu_app_insert_source (MessagingMenuApp *app,
+ gint position,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label);
+
+void messaging_menu_app_append_source (MessagingMenuApp *app,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label);
+
+void messaging_menu_app_insert_source_with_count (MessagingMenuApp *app,
+ gint position,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ guint count);
+
+void messaging_menu_app_append_source_with_count (MessagingMenuApp *app,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ guint count);
+
+void messaging_menu_app_insert_source_with_time (MessagingMenuApp *app,
+ gint position,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ gint64 time);
+
+void messaging_menu_app_append_source_with_time (MessagingMenuApp *app,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ gint64 time);
+
+void messaging_menu_app_append_source_with_string (MessagingMenuApp *app,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ const gchar *str);
+
+void messaging_menu_app_insert_source_with_string (MessagingMenuApp *app,
+ gint position,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ const gchar *str);
+
+void messaging_menu_app_remove_source (MessagingMenuApp *app,
+ const gchar *source_id);
+
+gboolean messaging_menu_app_has_source (MessagingMenuApp *app,
+ const gchar *source_id);
+
+void messaging_menu_app_set_source_label (MessagingMenuApp *app,
+ const gchar *source_id,
+ const gchar *label);
+
+void messaging_menu_app_set_source_icon (MessagingMenuApp *app,
+ const gchar *source_id,
+ GIcon *icon);
+
+void messaging_menu_app_set_source_count (MessagingMenuApp *app,
+ const gchar *source_id,
+ guint count);
+
+void messaging_menu_app_set_source_time (MessagingMenuApp *app,
+ const gchar *source_id,
+ gint64 time);
+
+void messaging_menu_app_set_source_string (MessagingMenuApp *app,
+ const gchar *source_id,
+ const gchar *str);
+
+void messaging_menu_app_draw_attention (MessagingMenuApp *app,
+ const gchar *source_id);
+
+void messaging_menu_app_remove_attention (MessagingMenuApp *app,
+ const gchar *source_id);
+
+G_END_DECLS
#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 70c7f6b..edb0388 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,15 +1,3 @@
[encoding: UTF-8]
-common/indicator-messages-service.c
-common/indicator-messages-application.c
-test/indicator-messages-service-activate.c
-src/im-phone-menu.c
-src/app-section.c
+src/indicator-messages.c
src/messages-service.c
-src/im-desktop-menu.c
-src/im-menu.c
-src/im-application-list.c
-src/gsettingsstrv.c
-src/gmenuutils.c
-src/gactionmuxer.c
-libmessaging-menu/messaging-menu-message.c
-libmessaging-menu/messaging-menu-app.c
diff --git a/po/indicator-messages.pot b/po/indicator-messages.pot
new file mode 100644
index 0000000..3788bb5
--- /dev/null
+++ b/po/indicator-messages.pot
@@ -0,0 +1,46 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-11-16 14:53+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../src/messages-service.c:329
+msgid "Messages"
+msgstr ""
+
+#: ../src/messages-service.c:552
+msgid "Available"
+msgstr ""
+
+#: ../src/messages-service.c:553
+msgid "Away"
+msgstr ""
+
+#: ../src/messages-service.c:554
+msgid "Busy"
+msgstr ""
+
+#: ../src/messages-service.c:555
+msgid "Invisible"
+msgstr ""
+
+#: ../src/messages-service.c:556
+msgid "Offline"
+msgstr ""
+
+#: ../src/messages-service.c:659
+msgid "Clear"
+msgstr ""
diff --git a/src/Makefile.am b/src/Makefile.am
index e03406a..1df80e5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,10 +1,53 @@
+BUILT_SOURCES =
EXTRA_DIST =
+CLEANFILES =
+DISTCLEANFILES =
libexec_PROGRAMS = indicator-messages-service
+
+######################################
+# Building the messages indicator
+######################################
+
+messaginglibdir = $(INDICATORDIR)
+messaginglib_LTLIBRARIES = libmessaging.la
+libmessaging_la_SOURCES = \
+ indicator-messages.c \
+ ido-menu-item.c \
+ ido-menu-item.h \
+ im-app-menu-item.c \
+ im-app-menu-item.h \
+ im-source-menu-item.c \
+ im-source-menu-item.h \
+ ido-detail-label.c \
+ ido-detail-label.h \
+ indicator-messages-service.c \
+ indicator-messages-service.h
+ dbus-data.h
+libmessaging_la_CFLAGS = \
+ $(APPLET_CFLAGS) \
+ $(COVERAGE_CFLAGS) \
+ -Wall \
+ -Wl,-Bsymbolic-functions \
+ -Wl,-z,defs \
+ -Wl,--as-needed \
+ -Werror \
+ -DG_LOG_DOMAIN=\"Indicator-Messages\"
+libmessaging_la_LIBADD = $(APPLET_LIBS) -lm
+libmessaging_la_LDFLAGS = \
+ $(COVERAGE_LDFLAGS) \
+ -module -avoid-version
+
+######################################
+# Building the messages service
+######################################
+
indicator_messages_service_SOURCES = \
messages-service.c \
+ indicator-messages-service.c \
+ indicator-messages-service.h \
app-section.c \
app-section.h \
dbus-data.h \
@@ -13,20 +56,11 @@ indicator_messages_service_SOURCES = \
gsettingsstrv.c \
gsettingsstrv.h \
gmenuutils.c \
- gmenuutils.h \
- im-menu.c \
- im-menu.h \
- im-phone-menu.c \
- im-phone-menu.h \
- im-desktop-menu.c \
- im-desktop-menu.h \
- im-application-list.c \
- im-application-list.h
+ gmenuutils.h
indicator_messages_service_CFLAGS = \
$(APPLET_CFLAGS) \
$(COVERAGE_CFLAGS) \
- -I$(top_builddir)/common \
-Wall \
-Wl,-Bsymbolic-functions \
-Wl,-z,defs \
@@ -35,11 +69,26 @@ indicator_messages_service_CFLAGS = \
-DG_LOG_DOMAIN=\"Indicator-Messages\"
indicator_messages_service_LDADD = \
- $(top_builddir)/common/libmessaging-common.la \
$(APPLET_LIBS)
indicator_messages_service_LDFLAGS = \
$(COVERAGE_LDFLAGS)
+indicator-messages-service.c: $(top_srcdir)/src/messages-service.xml
+ $(AM_V_GEN) gdbus-codegen \
+ --interface-prefix com.canonical.indicator.messages. \
+ --generate-c-code indicator-messages-service \
+ --c-namespace IndicatorMessages \
+ $^
+indicator-messages-service.h: indicator-messages-service.c
+
+BUILT_SOURCES += \
+ indicator-messages-service.c \
+ indicator-messages-service.h
+
EXTRA_DIST += \
messages-service.xml
+
+CLEANFILES += \
+ $(BUILT_SOURCES)
+
diff --git a/src/app-section.c b/src/app-section.c
index aad994d..1106c62 100644
--- a/src/app-section.c
+++ b/src/app-section.c
@@ -34,7 +34,6 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include "dbus-data.h"
#include "gmenuutils.h"
#include "gactionmuxer.h"
-#include "indicator-messages-application.h"
struct _AppSectionPrivate
{
@@ -43,14 +42,11 @@ struct _AppSectionPrivate
IndicatorDesktopShortcuts * ids;
- GCancellable *app_proxy_cancellable;
- IndicatorMessagesApplication *app_proxy;
-
GMenu *menu;
- GMenu *source_menu;
+ GMenuModel *source_menu;
GSimpleActionGroup *static_shortcuts;
- GSimpleActionGroup *source_actions;
+ GActionGroup *source_actions;
GActionMuxer *muxer;
gboolean draws_attention;
@@ -94,6 +90,19 @@ static void launch_action_change_state (GSimpleAction *action,
gpointer user_data);
static void app_section_set_app_info (AppSection *self,
GDesktopAppInfo *appinfo);
+static gboolean any_action_draws_attention (GActionGroup *group,
+ const gchar *ignored_action);
+static void action_added (GActionGroup *group,
+ const gchar *action_name,
+ gpointer user_data);
+static void action_state_changed (GActionGroup *group,
+ const gchar *action_name,
+ GVariant *value,
+ gpointer user_data);
+static void action_removed (GActionGroup *group,
+ const gchar *action_name,
+ gpointer user_data);
+static gboolean action_draws_attention (GVariant *state);
static void desktop_file_changed_cb (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
@@ -161,7 +170,6 @@ static void
app_section_init (AppSection *self)
{
AppSectionPrivate *priv;
- GMenuItem *item;
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
APP_SECTION_TYPE,
@@ -171,19 +179,10 @@ app_section_init (AppSection *self)
priv->appinfo = NULL;
priv->menu = g_menu_new ();
-
- priv->source_menu = g_menu_new ();
- item = g_menu_item_new_section (NULL, G_MENU_MODEL (priv->source_menu));
- g_menu_item_set_attribute (item, "action-namespace", "s", "source");
- g_menu_append_item (priv->menu, item);
- g_object_unref (item);
-
priv->static_shortcuts = g_simple_action_group_new ();
- priv->source_actions = g_simple_action_group_new ();
priv->muxer = g_action_muxer_new ();
g_action_muxer_insert (priv->muxer, NULL, G_ACTION_GROUP (priv->static_shortcuts));
- g_action_muxer_insert (priv->muxer, "source", G_ACTION_GROUP (priv->source_actions));
priv->draws_attention = FALSE;
@@ -250,30 +249,32 @@ app_section_dispose (GObject *object)
AppSection * self = APP_SECTION(object);
AppSectionPrivate * priv = self->priv;
- if (priv->app_proxy_cancellable) {
- g_cancellable_cancel (priv->app_proxy_cancellable);
- g_clear_object (&priv->app_proxy_cancellable);
- }
-
if (priv->desktop_file_monitor) {
g_signal_handlers_disconnect_by_func (priv->desktop_file_monitor, desktop_file_changed_cb, self);
g_clear_object (&priv->desktop_file_monitor);
}
- g_clear_object (&priv->app_proxy);
-
g_clear_object (&priv->menu);
- g_clear_object (&priv->source_menu);
g_clear_object (&priv->static_shortcuts);
- g_clear_object (&priv->source_actions);
if (priv->name_watch_id) {
g_bus_unwatch_name (priv->name_watch_id);
priv->name_watch_id = 0;
}
+ if (priv->source_actions) {
+ g_action_muxer_remove (priv->muxer, "source");
+ g_object_disconnect (priv->source_actions,
+ "any_signal::action-added", action_added, self,
+ "any_signal::action-state-changed", action_state_changed, self,
+ "any_signal::action-removed", action_removed, self,
+ NULL);
+ g_clear_object (&priv->source_actions);
+ }
+
g_clear_object (&priv->muxer);
+ g_clear_object (&priv->source_menu);
g_clear_object (&priv->ids);
g_clear_object (&priv->appinfo);
@@ -313,12 +314,14 @@ nick_activate_cb (GSimpleAction *action,
g_return_if_fail(priv->ids != NULL);
- if (!indicator_desktop_shortcuts_nick_exec_with_context(priv->ids, nick, NULL)) {
+ GAppLaunchContext *context = get_launch_context (g_variant_get_uint32 (param));
+
+ if (!indicator_desktop_shortcuts_nick_exec_with_context(priv->ids, nick, context)) {
g_warning("Unable to execute nick '%s' for desktop file '%s'",
nick, g_desktop_app_info_get_filename (priv->appinfo));
}
- return;
+ g_object_unref (context);
}
static void
@@ -411,9 +414,7 @@ app_section_update_menu (AppSection *self)
item = g_menu_item_new (g_app_info_get_name (G_APP_INFO (priv->appinfo)), "launch");
g_menu_item_set_attribute (item, "x-canonical-type", "s", "ImAppMenuItem");
iconstr = g_icon_to_string (g_app_info_get_icon (G_APP_INFO (priv->appinfo)));
- if (iconstr != NULL) {
- g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr);
- }
+ g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr);
g_free (iconstr);
g_menu_append_item (priv->menu, item);
@@ -444,11 +445,6 @@ app_section_update_menu (AppSection *self)
g_free(name);
}
- item = g_menu_item_new_section (NULL, G_MENU_MODEL (priv->source_menu));
- g_menu_item_set_attribute (item, "action-namespace", "s", "source");
- g_menu_append_item (priv->menu, item);
- g_object_unref (item);
-
keyfile = g_file_new_for_path (g_desktop_app_info_get_filename (priv->appinfo));
g_file_load_contents_async (keyfile, NULL, keyfile_loaded, self);
@@ -587,242 +583,49 @@ app_section_get_draws_attention (AppSection *self)
void
app_section_clear_draws_attention (AppSection *self)
{
- self->priv->draws_attention = FALSE;
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
-}
-
-static void
-application_vanished (GDBusConnection *bus,
- const gchar *name,
- gpointer user_data)
-{
- AppSection *self = user_data;
-
- app_section_unset_object_path (self);
-}
-
-static void
-update_draws_attention (AppSection *self)
-{
- AppSectionPrivate *priv = self->priv;
- gchar **actions;
+ AppSectionPrivate * priv = self->priv;
+ gchar **action_names;
gchar **it;
- gboolean draws_attention = FALSE;
-
- actions = g_action_group_list_actions (G_ACTION_GROUP (priv->source_actions));
-
- for (it = actions; *it; it++) {
- GVariant *state;
-
- state = g_action_group_get_action_state (G_ACTION_GROUP (priv->source_actions), *it);
- if (state) {
- gboolean b;
- g_variant_get (state, "(uxsb)", NULL, NULL, NULL, &b);
- draws_attention = b || draws_attention;
- g_variant_unref (state);
- }
-
- if (draws_attention)
- break;
- }
-
- if (draws_attention != priv->draws_attention) {
- priv->draws_attention = draws_attention;
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
- }
-
- g_strfreev (actions);
-}
-
-static void
-remove_source (AppSection *self,
- const gchar *id)
-{
- AppSectionPrivate *priv = self->priv;
- guint n_items;
- guint i;
-
- n_items = g_menu_model_get_n_items (G_MENU_MODEL (priv->source_menu));
- for (i = 0; i < n_items; i++) {
- gchar *action;
- gboolean found = FALSE;
-
- if (g_menu_model_get_item_attribute (G_MENU_MODEL (priv->source_menu), i,
- G_MENU_ATTRIBUTE_ACTION, "s", &action)) {
- found = g_str_equal (action, id);
- g_free (action);
- }
-
- if (found) {
- g_menu_remove (priv->source_menu, i);
- break;
- }
- }
-
- g_simple_action_group_remove (priv->source_actions, id);
- update_draws_attention (self);
-}
-
-static void
-source_action_activated (GSimpleAction *action,
- GVariant *parameter,
- gpointer user_data)
-{
- AppSection *self = APP_SECTION (user_data);
- AppSectionPrivate *priv = APP_SECTION (user_data)->priv;
-
- g_return_if_fail (priv->app_proxy != NULL);
-
- indicator_messages_application_call_activate_source (priv->app_proxy,
- g_action_get_name (G_ACTION (action)),
- priv->app_proxy_cancellable,
- NULL, NULL);
-
- remove_source (self, g_action_get_name (G_ACTION (action)));
-}
-static void
-sources_listed (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- AppSection *self = user_data;
- AppSectionPrivate *priv = self->priv;
- GVariant *sources = NULL;
- GError *error = NULL;
- GVariantIter iter;
- const gchar *id;
- const gchar *label;
- const gchar *iconstr;
- guint32 count;
- gint64 time;
- const gchar *string;
- gboolean draws_attention;
-
- if (!indicator_messages_application_call_list_sources_finish (INDICATOR_MESSAGES_APPLICATION (source_object),
- &sources, result, &error))
- {
- g_warning ("could not fetch the list of sources: %s", error->message);
- g_error_free (error);
+ if (priv->source_actions == NULL)
return;
- }
- g_menu_clear (priv->source_menu);
- g_simple_action_group_clear (priv->source_actions);
- priv->draws_attention = FALSE;
+ action_names = g_action_group_list_actions (priv->source_actions);
- g_variant_iter_init (&iter, sources);
- while (g_variant_iter_next (&iter, "(&s&s&sux&sb)", &id, &label, &iconstr,
- &count, &time, &string, &draws_attention))
- {
+ for (it = action_names; *it; it++) {
GVariant *state;
- GSimpleAction *action;
- GMenuItem *item;
- state = g_variant_new ("(uxsb)", count, time, string, draws_attention);
- action = g_simple_action_new_stateful (id, NULL, state);
- g_signal_connect (action, "activate", G_CALLBACK (source_action_activated), self);
- g_simple_action_group_insert (priv->source_actions, G_ACTION (action));
+ state = g_action_group_get_action_state (priv->source_actions, *it);
+ if (!state)
+ continue;
- item = g_menu_item_new (label, id);
- g_menu_item_set_attribute (item, "x-canonical-type", "s", "ImSourceMenuItem");
- g_menu_append_item (priv->source_menu, item);
+ /* clear draws-attention while preserving other state */
+ if (action_draws_attention (state)) {
+ guint32 count;
+ gint64 time;
+ const gchar *str;
+ GVariant *new_state;
- priv->draws_attention = priv->draws_attention || draws_attention;
-
- g_object_unref (item);
- g_object_unref (action);
- }
-
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
-
- g_variant_unref (sources);
-}
+ g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL);
-static void
-source_added (IndicatorMessagesApplication *app,
- const gchar *id,
- const gchar *label,
- const gchar *iconstr,
- guint count,
- gint64 time,
- const gchar *string,
- gboolean draws_attention,
- gpointer user_data)
-{
- AppSection *self = user_data;
- AppSectionPrivate *priv = self->priv;
- GVariant *state;
- GSimpleAction *action;
-
- /* TODO put label and icon into the action as well */
-
- state = g_variant_new ("(uxsb)", count, time, string, draws_attention);
- action = g_simple_action_new_stateful (id, NULL, state);
-
- g_simple_action_group_insert (priv->source_actions, G_ACTION (action));
+ new_state = g_variant_new ("(uxsb)", count, time, str, FALSE);
+ g_action_group_change_action_state (priv->source_actions, *it, new_state);
+ }
- if (draws_attention && !priv->draws_attention) {
- priv->draws_attention = TRUE;
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
+ g_variant_unref (state);
}
- g_object_unref (action);
-}
-static void
-source_changed (IndicatorMessagesApplication *app,
- const gchar *id,
- const gchar *label,
- const gchar *iconstr,
- guint count,
- gint64 time,
- const gchar *string,
- gboolean draws_attention,
- gpointer user_data)
-{
- AppSection *self = user_data;
- AppSectionPrivate *priv = self->priv;
- GVariant *state;
-
- /* TODO put label and icon into the action as well */
-
- state = g_variant_new ("(uxsb)", count, time, string, draws_attention);
- g_action_group_change_action_state (G_ACTION_GROUP (priv->source_actions), id, state);
-
- update_draws_attention (self);
+ g_strfreev (action_names);
}
static void
-source_removed (IndicatorMessagesApplication *app,
- const gchar *id,
- gpointer user_data)
+application_vanished (GDBusConnection *bus,
+ const gchar *name,
+ gpointer user_data)
{
AppSection *self = user_data;
- remove_source (self, id);
-}
-
-static void
-app_proxy_created (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- AppSectionPrivate *priv = APP_SECTION (user_data)->priv;
- GError *error = NULL;
-
- priv->app_proxy = indicator_messages_application_proxy_new_finish (result, &error);
- if (!priv->app_proxy) {
- g_warning ("could not create application proxy: %s", error->message);
- g_error_free (error);
- return;
- }
-
- indicator_messages_application_call_list_sources (priv->app_proxy, priv->app_proxy_cancellable,
- sources_listed, user_data);
-
- g_signal_connect (priv->app_proxy, "source-added", G_CALLBACK (source_added), user_data);
- g_signal_connect (priv->app_proxy, "source-changed", G_CALLBACK (source_changed), user_data);
- g_signal_connect (priv->app_proxy, "source-removed", G_CALLBACK (source_removed), user_data);
+ app_section_unset_object_path (self);
}
/*
@@ -843,20 +646,27 @@ app_section_set_object_path (AppSection *self,
const gchar *object_path)
{
AppSectionPrivate *priv = self->priv;
+ GMenuItem *item;
g_object_freeze_notify (G_OBJECT (self));
app_section_unset_object_path (self);
- priv->app_proxy_cancellable = g_cancellable_new ();
- indicator_messages_application_proxy_new (bus,
- G_DBUS_PROXY_FLAGS_NONE,
- bus_name,
- object_path,
- priv->app_proxy_cancellable,
- app_proxy_created,
- self);
+ priv->source_actions = G_ACTION_GROUP (g_dbus_action_group_get (bus, bus_name, object_path));
+ g_action_muxer_insert (priv->muxer, "source", priv->source_actions);
- priv->draws_attention = FALSE;
+ priv->draws_attention = any_action_draws_attention (priv->source_actions, NULL);
+ g_object_connect (priv->source_actions,
+ "signal::action-added", action_added, self,
+ "signal::action-state-changed", action_state_changed, self,
+ "signal::action-removed", action_removed, self,
+ NULL);
+
+ priv->source_menu = G_MENU_MODEL (g_dbus_menu_model_get (bus, bus_name, object_path));
+
+ item = g_menu_item_new_section (NULL, priv->source_menu);
+ g_menu_item_set_attribute (item, "action-namespace", "s", "source");
+ g_menu_append_item (priv->menu, item);
+ g_object_unref (item);
priv->name_watch_id = g_bus_watch_name_on_connection (bus, bus_name, 0,
NULL, application_vanished,
@@ -884,19 +694,26 @@ app_section_unset_object_path (AppSection *self)
{
AppSectionPrivate *priv = self->priv;
- if (priv->app_proxy_cancellable) {
- g_cancellable_cancel (priv->app_proxy_cancellable);
- g_clear_object (&priv->app_proxy_cancellable);
- }
- g_clear_object (&priv->app_proxy);
-
if (priv->name_watch_id) {
g_bus_unwatch_name (priv->name_watch_id);
priv->name_watch_id = 0;
}
- g_simple_action_group_clear (priv->source_actions);
- g_menu_clear (priv->source_menu);
+ if (priv->source_actions) {
+ g_object_disconnect (priv->source_actions,
+ "any_signal::action-added", action_added, self,
+ "any_signal::action-state-changed", action_state_changed, self,
+ "any_signal::action-removed", action_removed, self,
+ NULL);
+ g_clear_object (&priv->source_actions);
+ }
+
+ if (priv->source_menu) {
+ /* the last menu item points is linked to the app's menumodel */
+ gint n_items = g_menu_model_get_n_items (G_MENU_MODEL (priv->menu));
+ g_menu_remove (priv->menu, n_items -1);
+ g_clear_object (&priv->source_menu);
+ }
priv->draws_attention = FALSE;
g_clear_pointer (&priv->chat_status, g_free);
@@ -910,6 +727,85 @@ app_section_unset_object_path (AppSection *self)
"launch", g_variant_new_boolean (FALSE));
}
+static gboolean
+action_draws_attention (GVariant *state)
+{
+ gboolean attention;
+
+ if (state && g_variant_is_of_type (state, G_VARIANT_TYPE ("(uxsb)")))
+ g_variant_get_child (state, 3, "b", &attention);
+ else
+ attention = FALSE;
+
+ return attention;
+}
+
+static gboolean
+any_action_draws_attention (GActionGroup *group,
+ const gchar *ignored_action)
+{
+ gchar **actions;
+ gchar **it;
+ gboolean attention = FALSE;
+
+ actions = g_action_group_list_actions (group);
+
+ for (it = actions; *it && !attention; it++) {
+ GVariant *state;
+
+ if (ignored_action && g_str_equal (ignored_action, *it))
+ continue;
+
+ state = g_action_group_get_action_state (group, *it);
+ if (state) {
+ attention = action_draws_attention (state);
+ g_variant_unref (state);
+ }
+ }
+
+ g_strfreev (actions);
+ return attention;
+}
+
+static void
+action_added (GActionGroup *group,
+ const gchar *action_name,
+ gpointer user_data)
+{
+ AppSection *self = user_data;
+ GVariant *state;
+
+ state = g_action_group_get_action_state (group, action_name);
+ if (state) {
+ self->priv->draws_attention |= action_draws_attention (state);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
+ g_variant_unref (state);
+ }
+}
+
+static void
+action_state_changed (GActionGroup *group,
+ const gchar *action_name,
+ GVariant *value,
+ gpointer user_data)
+{
+ AppSection *self = user_data;
+
+ self->priv->draws_attention = any_action_draws_attention (group, NULL);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
+}
+
+static void
+action_removed (GActionGroup *group,
+ const gchar *action_name,
+ gpointer user_data)
+{
+ AppSection *self = user_data;
+
+ self->priv->draws_attention = any_action_draws_attention (group, action_name);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
+}
+
gboolean
app_section_get_uses_chat_status (AppSection *self)
{
diff --git a/src/dbus-data.h b/src/dbus-data.h
index 59bd305..64747a9 100644
--- a/src/dbus-data.h
+++ b/src/dbus-data.h
@@ -1,24 +1,9 @@
-/*
- * Copyright 2012-2013 Canonical Ltd.
- *
- * 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/>.
- */
#ifndef __DBUS_DATA_H__
#define __DBUS_DATA_H__ 1
#define INDICATOR_MESSAGES_DBUS_NAME "com.canonical.indicator.messages"
-#define INDICATOR_MESSAGES_DBUS_OBJECT "/com/canonical/indicator/messages"
+#define INDICATOR_MESSAGES_DBUS_OBJECT "/com/canonical/indicator/messages/menu"
#define INDICATOR_MESSAGES_DBUS_SERVICE_OBJECT "/com/canonical/indicator/messages/service"
#define INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE "com.canonical.indicator.messages.service"
diff --git a/src/gactionmuxer.c b/src/gactionmuxer.c
index 0f3cda4..2b1d11a 100644
--- a/src/gactionmuxer.c
+++ b/src/gactionmuxer.c
@@ -483,11 +483,3 @@ g_action_muxer_remove (GActionMuxer *muxer,
g_clear_object (&muxer->global_actions);
}
-GActionGroup *
-g_action_muxer_get_group (GActionMuxer *muxer,
- const gchar *prefix)
-{
- g_return_val_if_fail (G_IS_ACTION_MUXER (muxer), NULL);
-
- return prefix ? g_hash_table_lookup (muxer->groups, prefix) : muxer->global_actions;
-}
diff --git a/src/gactionmuxer.h b/src/gactionmuxer.h
index caf9ec7..5c5e839 100644
--- a/src/gactionmuxer.h
+++ b/src/gactionmuxer.h
@@ -40,8 +40,5 @@ void g_action_muxer_insert (GActionMuxer *muxer,
void g_action_muxer_remove (GActionMuxer *muxer,
const gchar *prefix);
-GActionGroup * g_action_muxer_get_group (GActionMuxer *muxer,
- const gchar *prefix);
-
#endif
diff --git a/src/gmenuutils.c b/src/gmenuutils.c
index cfd751e..f63615b 100644
--- a/src/gmenuutils.c
+++ b/src/gmenuutils.c
@@ -79,7 +79,7 @@ g_menu_append_with_icon_name (GMenu *menu,
GMenuItem *item;
item = g_menu_item_new (label, detailed_action);
- g_menu_item_set_attribute (item, "icon", "s", icon_name);
+ g_menu_item_set_attribute (item, "x-canonical-icon", "s", icon_name);
g_menu_append_item (menu, item);
diff --git a/src/ido-detail-label.c b/src/ido-detail-label.c
new file mode 100644
index 0000000..8b7ef90
--- /dev/null
+++ b/src/ido-detail-label.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * 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/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#include "ido-detail-label.h"
+
+#include <math.h>
+
+G_DEFINE_TYPE (IdoDetailLabel, ido_detail_label, GTK_TYPE_WIDGET)
+
+struct _IdoDetailLabelPrivate
+{
+ gchar *text;
+ PangoLayout *layout;
+ gboolean draw_lozenge;
+};
+
+enum
+{
+ PROP_0,
+ PROP_TEXT,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES];
+
+static void
+ido_detail_label_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdoDetailLabel *self = IDO_DETAIL_LABEL (object);
+
+ switch (property_id)
+ {
+ case PROP_TEXT:
+ g_value_set_string (value, self->priv->text);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ido_detail_label_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdoDetailLabel *self = IDO_DETAIL_LABEL (object);
+
+ switch (property_id)
+ {
+ case PROP_TEXT:
+ ido_detail_label_set_text (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+
+static void
+ido_detail_label_finalize (GObject *object)
+{
+ IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (object)->priv;
+
+ g_free (priv->text);
+
+ G_OBJECT_CLASS (ido_detail_label_parent_class)->finalize (object);
+}
+
+static void
+ido_detail_label_dispose (GObject *object)
+{
+ IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (object)->priv;
+
+ g_clear_object (&priv->layout);
+
+ G_OBJECT_CLASS (ido_detail_label_parent_class)->dispose (object);
+}
+
+static void
+ido_detail_label_ensure_layout (IdoDetailLabel *label)
+{
+ IdoDetailLabelPrivate *priv = label->priv;
+
+ if (priv->layout == NULL)
+ {
+ priv->layout = gtk_widget_create_pango_layout (GTK_WIDGET (label), priv->text);
+ pango_layout_set_alignment (priv->layout, PANGO_ALIGN_CENTER);
+ pango_layout_set_ellipsize (priv->layout, PANGO_ELLIPSIZE_END);
+ pango_layout_set_height (priv->layout, -1);
+
+ // TODO update layout on "style-updated" and "direction-changed"
+ }
+}
+
+static void
+cairo_lozenge (cairo_t *cr,
+ double x,
+ double y,
+ double w,
+ double h,
+ double radius)
+{
+ double x1 = x + w - radius;
+ double x2 = x + radius;
+ double y1 = y + radius;
+ double y2 = y + h - radius;
+
+ cairo_move_to (cr, x + radius, y);
+ cairo_arc (cr, x1, y1, radius, G_PI * 1.5, G_PI * 2);
+ cairo_arc (cr, x1, y2, radius, 0, G_PI * 0.5);
+ cairo_arc (cr, x2, y2, radius, G_PI * 0.5, G_PI);
+ cairo_arc (cr, x2, y1, radius, G_PI, G_PI * 1.5);
+}
+
+static PangoFontMetrics *
+gtk_widget_get_font_metrics (GtkWidget *widget,
+ PangoContext *context)
+{
+ PangoFontDescription *font;
+ PangoFontMetrics *metrics;
+
+ gtk_style_context_get (gtk_widget_get_style_context (widget),
+ gtk_widget_get_state_flags (widget),
+ "font", &font, NULL);
+
+ metrics = pango_context_get_metrics (context,
+ font,
+ pango_context_get_language (context));
+
+ pango_font_description_free (font);
+ return metrics;
+}
+
+static gint
+ido_detail_label_get_minimum_text_width (IdoDetailLabel *label)
+{
+ IdoDetailLabelPrivate *priv = label->priv;
+ PangoContext *context;
+ PangoFontMetrics *metrics;
+ gint char_width;
+ gint w;
+
+ context = pango_layout_get_context (priv->layout);
+ metrics = gtk_widget_get_font_metrics (GTK_WIDGET (label), context);
+ char_width = pango_font_metrics_get_approximate_digit_width (metrics);
+
+ w = 2 * char_width / PANGO_SCALE;
+ pango_font_metrics_unref (metrics);
+ return w;
+}
+
+static gboolean
+ido_detail_label_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ IdoDetailLabel *label = IDO_DETAIL_LABEL (widget);
+ IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv;
+ PangoRectangle extents;
+ GtkAllocation allocation;
+ double x, w, h, radius;
+ GdkRGBA color;
+
+ if (!priv->text || !*priv->text)
+ return TRUE;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget));
+
+ pango_layout_get_extents (priv->layout, NULL, &extents);
+ pango_extents_to_pixels (&extents, NULL);
+
+ h = MIN (allocation.height, extents.height);
+ radius = floor (h / 2.0);
+ w = MAX (ido_detail_label_get_minimum_text_width (label), extents.width) + 2.0 * radius;
+ x = allocation.width - w;
+
+ pango_layout_set_width (priv->layout, (allocation.width - 2 * radius) * PANGO_SCALE);
+ pango_layout_get_extents (priv->layout, NULL, &extents);
+ pango_extents_to_pixels (&extents, NULL);
+
+ gtk_style_context_get_color (gtk_widget_get_style_context (widget),
+ gtk_widget_get_state_flags (widget),
+ &color);
+ gdk_cairo_set_source_rgba (cr, &color);
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+ if (priv->draw_lozenge)
+ cairo_lozenge (cr, x, 0.0, w, h, radius);
+
+ cairo_move_to (cr, x + radius, (allocation.height - extents.height) / 2.0);
+ pango_cairo_layout_path (cr, priv->layout);
+ cairo_fill (cr);
+
+ return TRUE;
+}
+
+static void
+ido_detail_label_get_preferred_width (GtkWidget *widget,
+ gint *minimum,
+ gint *natural)
+{
+ IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv;
+ PangoRectangle extents;
+ double radius;
+
+ ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget));
+
+ pango_layout_get_extents (priv->layout, NULL, &extents);
+ pango_extents_to_pixels (&extents, NULL);
+
+ radius = floor (extents.height / 2.0);
+
+ *minimum = ido_detail_label_get_minimum_text_width (IDO_DETAIL_LABEL (widget)) + 2.0 * radius;
+ *natural = MAX (*minimum, extents.width + 2.0 * radius);
+}
+
+static void
+ido_detail_label_get_preferred_height (GtkWidget *widget,
+ gint *minimum,
+ gint *natural)
+{
+ IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv;
+ PangoContext *context;
+ PangoFontMetrics *metrics;
+ PangoRectangle extents;
+
+ ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget));
+
+ pango_layout_get_extents (priv->layout, NULL, &extents);
+ pango_extents_to_pixels (&extents, NULL);
+ context = pango_layout_get_context (priv->layout);
+ metrics = gtk_widget_get_font_metrics (widget, context);
+
+ *minimum = *natural = (pango_font_metrics_get_ascent (metrics) +
+ pango_font_metrics_get_descent (metrics)) / PANGO_SCALE;
+
+ pango_font_metrics_unref (metrics);
+}
+
+static void
+ido_detail_label_class_init (IdoDetailLabelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->get_property = ido_detail_label_get_property;
+ object_class->set_property = ido_detail_label_set_property;
+ object_class->finalize = ido_detail_label_finalize;
+ object_class->dispose = ido_detail_label_dispose;
+
+ widget_class->draw = ido_detail_label_draw;
+ widget_class->get_preferred_width = ido_detail_label_get_preferred_width;
+ widget_class->get_preferred_height = ido_detail_label_get_preferred_height;
+
+ g_type_class_add_private (klass, sizeof (IdoDetailLabelPrivate));
+
+ properties[PROP_TEXT] = g_param_spec_string ("text",
+ "Text",
+ "The text of the label",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+}
+
+static void
+ido_detail_label_init (IdoDetailLabel *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ IDO_TYPE_DETAIL_LABEL,
+ IdoDetailLabelPrivate);
+
+ gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
+}
+
+GtkWidget *
+ido_detail_label_new (const gchar *label)
+{
+ return g_object_new (IDO_TYPE_DETAIL_LABEL,
+ "text", label,
+ NULL);
+}
+
+const gchar *
+ido_detail_label_get_text (IdoDetailLabel *label)
+{
+ g_return_val_if_fail (IDO_IS_DETAIL_LABEL (label), NULL);
+ return label->priv->text;
+}
+
+/* collapse_whitespace:
+ * @str: the source string
+ *
+ * Collapses all occurences of consecutive whitespace charactes in @str
+ * into a single space.
+ *
+ * Returns: (transfer full): a newly-allocated string
+ */
+static gchar *
+collapse_whitespace (const gchar *str)
+{
+ GString *result;
+ gboolean in_space = FALSE;
+
+ if (str == NULL)
+ return NULL;
+
+ result = g_string_new ("");
+
+ while (*str)
+ {
+ gunichar c = g_utf8_get_char_validated (str, -1);
+
+ if (c < 0)
+ break;
+
+ if (!g_unichar_isspace (c))
+ {
+ g_string_append_unichar (result, c);
+ in_space = FALSE;
+ }
+ else if (!in_space)
+ {
+ g_string_append_c (result, ' ');
+ in_space = TRUE;
+ }
+
+ str = g_utf8_next_char (str);
+ }
+
+ return g_string_free (result, FALSE);
+}
+
+static void
+ido_detail_label_set_text_impl (IdoDetailLabel *label,
+ const gchar *text,
+ gboolean draw_lozenge)
+{
+ IdoDetailLabelPrivate * priv = label->priv;
+
+ g_clear_object (&priv->layout);
+ g_free (priv->text);
+
+ priv->text = g_strdup (text);
+ priv->draw_lozenge = draw_lozenge;
+
+ g_object_notify_by_pspec (G_OBJECT (label), properties[PROP_TEXT]);
+ gtk_widget_queue_resize (GTK_WIDGET (label));
+}
+
+void
+ido_detail_label_set_text (IdoDetailLabel *label,
+ const gchar *text)
+{
+ gchar *str;
+
+ g_return_if_fail (IDO_IS_DETAIL_LABEL (label));
+
+ str = collapse_whitespace (text);
+ ido_detail_label_set_text_impl (label, str, FALSE);
+ g_free (str);
+}
+
+void
+ido_detail_label_set_count (IdoDetailLabel *label,
+ gint count)
+{
+ gchar *text;
+
+ g_return_if_fail (IDO_IS_DETAIL_LABEL (label));
+
+ text = g_strdup_printf ("%d", count);
+ ido_detail_label_set_text_impl (label, text, TRUE);
+ g_free (text);
+}
diff --git a/src/ido-detail-label.h b/src/ido-detail-label.h
new file mode 100644
index 0000000..1995fee
--- /dev/null
+++ b/src/ido-detail-label.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * 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/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#ifndef __IDO_DETAIL_LABEL_H__
+#define __IDO_DETAIL_LABEL_H__
+
+#include <gtk/gtk.h>
+
+#define IDO_TYPE_DETAIL_LABEL (ido_detail_label_get_type())
+#define IDO_DETAIL_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDO_TYPE_DETAIL_LABEL, IdoDetailLabel))
+#define IDO_DETAIL_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IDO_TYPE_DETAIL_LABEL, IdoDetailLabelClass))
+#define IDO_IS_DETAIL_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDO_TYPE_DETAIL_LABEL))
+#define IDO_IS_DETAIL_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IDO_TYPE_DETAIL_LABEL))
+#define IDO_DETAIL_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IDO_TYPE_DETAIL_LABEL, IdoDetailLabelClass))
+
+typedef struct _IdoDetailLabel IdoDetailLabel;
+typedef struct _IdoDetailLabelClass IdoDetailLabelClass;
+typedef struct _IdoDetailLabelPrivate IdoDetailLabelPrivate;
+
+struct _IdoDetailLabel
+{
+ GtkWidget parent;
+ IdoDetailLabelPrivate *priv;
+};
+
+struct _IdoDetailLabelClass
+{
+ GtkWidgetClass parent_class;
+};
+
+GType ido_detail_label_get_type (void) G_GNUC_CONST;
+
+GtkWidget * ido_detail_label_new (const gchar *str);
+
+const gchar * ido_detail_label_get_text (IdoDetailLabel *label);
+
+void ido_detail_label_set_text (IdoDetailLabel *label,
+ const gchar *text);
+
+void ido_detail_label_set_count (IdoDetailLabel *label,
+ gint count);
+
+#endif
diff --git a/src/ido-menu-item.c b/src/ido-menu-item.c
new file mode 100644
index 0000000..f702828
--- /dev/null
+++ b/src/ido-menu-item.c
@@ -0,0 +1,439 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * 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/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#include "ido-menu-item.h"
+
+struct _IdoMenuItemPrivate
+{
+ GActionGroup *action_group;
+ gchar *action;
+ GVariant *target;
+
+ GtkWidget *icon;
+ GtkWidget *label;
+
+ gboolean has_indicator;
+ gboolean in_set_active;
+};
+
+enum
+{
+ PROP_0,
+ PROP_MENU_ITEM,
+ PROP_ACTION_GROUP,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES];
+
+G_DEFINE_TYPE (IdoMenuItem, ido_menu_item, GTK_TYPE_CHECK_MENU_ITEM);
+
+static void
+ido_menu_item_constructed (GObject *object)
+{
+ IdoMenuItemPrivate *priv = IDO_MENU_ITEM (object)->priv;
+ GtkWidget *grid;
+
+ priv->icon = g_object_ref (gtk_image_new ());
+ gtk_widget_set_margin_right (priv->icon, 6);
+
+ priv->label = g_object_ref (gtk_label_new (""));
+
+ grid = gtk_grid_new ();
+ gtk_grid_attach (GTK_GRID (grid), priv->icon, 0, 0, 1, 1);
+ gtk_grid_attach (GTK_GRID (grid), priv->label, 1, 0, 1, 1);
+
+ gtk_container_add (GTK_CONTAINER (object), grid);
+ gtk_widget_show_all (grid);
+
+ G_OBJECT_CLASS (ido_menu_item_parent_class)->constructed (object);
+}
+
+static void
+ido_menu_item_set_active (IdoMenuItem *self,
+ gboolean active)
+{
+ /* HACK gtk_check_menu_item_set_active calls gtk_menu_item_activate.
+ * Make sure our activate handler doesn't toggle the action as a
+ * result of calling this function. */
+
+ self->priv->in_set_active = TRUE;
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (self), active);
+ self->priv->in_set_active = FALSE;
+}
+
+static void
+ido_menu_item_set_has_indicator (IdoMenuItem *self,
+ gboolean has_indicator)
+{
+ if (has_indicator == self->priv->has_indicator)
+ return;
+
+ self->priv->has_indicator = has_indicator;
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+}
+
+static void
+ido_menu_item_set_state (IdoMenuItem *self,
+ GVariant *state)
+{
+ IdoMenuItemPrivate *priv = self->priv;
+
+ if (priv->target)
+ {
+ ido_menu_item_set_has_indicator (self, TRUE);
+ ido_menu_item_set_active (self, FALSE);
+ gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (self), TRUE);
+ gtk_check_menu_item_set_inconsistent (GTK_CHECK_MENU_ITEM (self), FALSE);
+
+ if (g_variant_is_of_type (state, G_VARIANT_TYPE_STRING))
+ {
+ ido_menu_item_set_active (self, g_variant_equal (priv->target, state));
+ }
+ else if (g_variant_is_of_type (state, G_VARIANT_TYPE ("as")) &&
+ g_variant_is_of_type (priv->target, G_VARIANT_TYPE_STRING))
+ {
+ const gchar *target_str;
+ const gchar **state_strs;
+ const gchar **it;
+
+ target_str = g_variant_get_string (priv->target, NULL);
+ state_strs = g_variant_get_strv (state, NULL);
+
+ it = state_strs;
+ while (*it != NULL && !g_str_equal (*it, target_str))
+ it++;
+
+ if (*it != NULL)
+ {
+ ido_menu_item_set_active (self, TRUE);
+ gtk_check_menu_item_set_inconsistent (GTK_CHECK_MENU_ITEM (self),
+ g_strv_length ((gchar **)state_strs) > 1);
+ }
+
+ g_free (state_strs);
+ }
+ }
+ else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
+ {
+ ido_menu_item_set_has_indicator (self, TRUE);
+ gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (self), FALSE);
+ ido_menu_item_set_active (self, g_variant_get_boolean (state));
+ }
+ else
+ {
+ ido_menu_item_set_has_indicator (self, FALSE);
+ }
+}
+
+static void
+ido_menu_item_set_action_name (IdoMenuItem *self,
+ const gchar *action_name)
+{
+ IdoMenuItemPrivate *priv = self->priv;
+ gboolean enabled = FALSE;
+ GVariant *state;
+ const GVariantType *param_type;
+
+ if (priv->action != NULL)
+ g_free (priv->action);
+
+ priv->action = g_strdup (action_name);
+
+ if (priv->action_group != NULL && priv->action != NULL &&
+ g_action_group_query_action (priv->action_group, priv->action,
+ &enabled, &param_type, NULL, NULL, &state))
+ {
+ gtk_widget_set_sensitive (GTK_WIDGET (self), enabled);
+
+ if (state)
+ {
+ ido_menu_item_set_state (self, state);
+ g_variant_unref (state);
+ }
+ }
+ else
+ {
+ ido_menu_item_set_active (self, FALSE);
+ gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
+ ido_menu_item_set_has_indicator (self, FALSE);
+ }
+}
+
+static void
+ido_menu_item_action_added (GActionGroup *action_group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ IdoMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ ido_menu_item_set_action_name (self, action_name);
+}
+
+static void
+ido_menu_item_action_removed (GActionGroup *action_group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ IdoMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ {
+ gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
+ }
+}
+
+static void
+ido_menu_item_action_enabled_changed (GActionGroup *action_group,
+ gchar *action_name,
+ gboolean enabled,
+ gpointer user_data)
+{
+ IdoMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ gtk_widget_set_sensitive (GTK_WIDGET (self), enabled);
+}
+
+static void
+ido_menu_item_action_state_changed (GActionGroup *action_group,
+ gchar *action_name,
+ GVariant *value,
+ gpointer user_data)
+{
+ IdoMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ ido_menu_item_set_state (self, value);
+}
+
+static void
+ido_menu_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdoMenuItem *self = IDO_MENU_ITEM (object);
+
+ switch (property_id)
+ {
+ case PROP_MENU_ITEM:
+ ido_menu_item_set_menu_item (self, G_MENU_ITEM (g_value_get_object (value)));
+ break;
+
+ case PROP_ACTION_GROUP:
+ ido_menu_item_set_action_group (self, G_ACTION_GROUP (g_value_get_object (value)));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ido_menu_item_dispose (GObject *object)
+{
+ IdoMenuItem *self = IDO_MENU_ITEM (object);
+
+ if (self->priv->action_group)
+ ido_menu_item_set_action_group (self, NULL);
+
+ g_clear_object (&self->priv->icon);
+ g_clear_object (&self->priv->label);
+
+ if (self->priv->target)
+ {
+ g_variant_unref (self->priv->target);
+ self->priv->target = NULL;
+ }
+
+ G_OBJECT_CLASS (ido_menu_item_parent_class)->dispose (object);
+}
+
+static void
+ido_menu_item_finalize (GObject *object)
+{
+ IdoMenuItemPrivate *priv = IDO_MENU_ITEM (object)->priv;
+
+ g_free (priv->action);
+
+ G_OBJECT_CLASS (ido_menu_item_parent_class)->finalize (object);
+}
+
+static void
+ido_menu_item_activate (GtkMenuItem *item)
+{
+ IdoMenuItemPrivate *priv = IDO_MENU_ITEM (item)->priv;
+ GVariant *parameter;
+
+ /* see ido_menu_item_set_active */
+ if (!priv->in_set_active && priv->action && priv->action_group)
+ {
+ guint32 event_time = gtk_get_current_event_time ();
+
+ if (priv->target)
+ {
+ parameter = priv->target;
+ }
+ else
+ {
+ parameter = g_variant_new_uint32 (event_time);
+ }
+
+ g_action_group_activate_action (priv->action_group, priv->action, parameter);
+ }
+
+ if (priv->in_set_active)
+ GTK_MENU_ITEM_CLASS (ido_menu_item_parent_class)->activate (item);
+}
+
+static void
+ido_menu_item_draw_indicator (GtkCheckMenuItem *item,
+ cairo_t *cr)
+{
+ IdoMenuItem *self = IDO_MENU_ITEM (item);
+
+ if (self->priv->has_indicator)
+ GTK_CHECK_MENU_ITEM_CLASS (ido_menu_item_parent_class)
+ ->draw_indicator (item, cr);
+}
+
+static void
+ido_menu_item_class_init (IdoMenuItemClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkMenuItemClass *menu_item_class = GTK_MENU_ITEM_CLASS (klass);
+ GtkCheckMenuItemClass *check_class = GTK_CHECK_MENU_ITEM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (IdoMenuItemPrivate));
+
+ object_class->constructed = ido_menu_item_constructed;
+ object_class->set_property = ido_menu_set_property;
+ object_class->dispose = ido_menu_item_dispose;
+ object_class->finalize = ido_menu_item_finalize;
+
+ menu_item_class->activate = ido_menu_item_activate;
+
+ check_class->draw_indicator = ido_menu_item_draw_indicator;
+
+ properties[PROP_MENU_ITEM] = g_param_spec_object ("menu-item",
+ "Menu item",
+ "The model GMenuItem for this menu item",
+ G_TYPE_MENU_ITEM,
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_ACTION_GROUP] = g_param_spec_object ("action-group",
+ "Action group",
+ "The action group associated with this menu item",
+ G_TYPE_ACTION_GROUP,
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+}
+
+static void
+ido_menu_item_init (IdoMenuItem *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ IDO_TYPE_MENU_ITEM,
+ IdoMenuItemPrivate);
+}
+
+void
+ido_menu_item_set_menu_item (IdoMenuItem *self,
+ GMenuItem *menuitem)
+{
+ gchar *iconstr = NULL;
+ GIcon *icon = NULL;
+ gchar *label = NULL;
+ gchar *action = NULL;
+
+ if (g_menu_item_get_attribute (menuitem, "x-canonical-icon", "s", &iconstr))
+ {
+ GError *error;
+
+ /* only indent the label if icon is set to "" */
+ if (iconstr[0] == '\0')
+ {
+ gint width;
+
+ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
+ gtk_widget_set_size_request (self->priv->icon, width, -1);
+ }
+ else
+ {
+ icon = g_icon_new_for_string (iconstr, &error);
+ if (icon == NULL)
+ {
+ g_warning ("unable to set icon: %s", error->message);
+ g_error_free (error);
+ }
+ }
+ g_free (iconstr);
+ }
+ gtk_image_set_from_gicon (GTK_IMAGE (self->priv->icon), icon, GTK_ICON_SIZE_MENU);
+
+ g_menu_item_get_attribute (menuitem, "label", "s", &label);
+ gtk_label_set_label (GTK_LABEL (self->priv->label), label ? label : "");
+
+ self->priv->target = g_menu_item_get_attribute_value (menuitem, "target", NULL);
+
+ g_menu_item_get_attribute (menuitem, "action", "s", &action);
+ ido_menu_item_set_action_name (self, action);
+
+ if (icon)
+ g_object_unref (icon);
+ g_free (label);
+ g_free (action);
+}
+
+void
+ido_menu_item_set_action_group (IdoMenuItem *self,
+ GActionGroup *action_group)
+{
+ IdoMenuItemPrivate *priv = self->priv;
+
+ if (priv->action_group != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (priv->action_group, ido_menu_item_action_added, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, ido_menu_item_action_removed, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, ido_menu_item_action_enabled_changed, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, ido_menu_item_action_state_changed, self);
+
+ g_clear_object (&priv->action_group);
+ }
+
+ if (action_group != NULL)
+ {
+ priv->action_group = g_object_ref (action_group);
+
+ g_signal_connect (priv->action_group, "action-added",
+ G_CALLBACK (ido_menu_item_action_added), self);
+ g_signal_connect (priv->action_group, "action-removed",
+ G_CALLBACK (ido_menu_item_action_removed), self);
+ g_signal_connect (priv->action_group, "action-enabled-changed",
+ G_CALLBACK (ido_menu_item_action_enabled_changed), self);
+ g_signal_connect (priv->action_group, "action-state-changed",
+ G_CALLBACK (ido_menu_item_action_state_changed), self);
+ }
+}
diff --git a/src/ido-menu-item.h b/src/ido-menu-item.h
new file mode 100644
index 0000000..0521928
--- /dev/null
+++ b/src/ido-menu-item.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * 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/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#ifndef __IDO_MENU_ITEM_H__
+#define __IDO_MENU_ITEM_H__
+
+#include <gtk/gtk.h>
+
+#define IDO_TYPE_MENU_ITEM (ido_menu_item_get_type ())
+#define IDO_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDO_TYPE_MENU_ITEM, IdoMenuItem))
+#define IDO_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IDO_TYPE_MENU_ITEM, IdoMenuItemClass))
+#define IS_IDO_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDO_TYPE_MENU_ITEM))
+#define IS_IDO_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IDO_TYPE_MENU_ITEM))
+#define IDO_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IDO_TYPE_MENU_ITEM, IdoMenuItemClass))
+
+typedef struct _IdoMenuItem IdoMenuItem;
+typedef struct _IdoMenuItemClass IdoMenuItemClass;
+typedef struct _IdoMenuItemPrivate IdoMenuItemPrivate;
+
+struct _IdoMenuItemClass
+{
+ GtkCheckMenuItemClass parent_class;
+};
+
+struct _IdoMenuItem
+{
+ GtkCheckMenuItem parent;
+ IdoMenuItemPrivate *priv;
+};
+
+GType ido_menu_item_get_type (void);
+
+void ido_menu_item_set_menu_item (IdoMenuItem *item,
+ GMenuItem *menuitem);
+void ido_menu_item_set_action_group (IdoMenuItem *self,
+ GActionGroup *action_group);
+
+#endif
diff --git a/src/im-app-menu-item.c b/src/im-app-menu-item.c
new file mode 100644
index 0000000..03b11c2
--- /dev/null
+++ b/src/im-app-menu-item.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * 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/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#include "im-app-menu-item.h"
+
+struct _ImAppMenuItemPrivate
+{
+ GActionGroup *action_group;
+ gchar *action;
+ gboolean is_running;
+
+ GtkWidget *icon;
+ GtkWidget *label;
+};
+
+enum
+{
+ PROP_0,
+ PROP_MENU_ITEM,
+ PROP_ACTION_GROUP,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES];
+
+G_DEFINE_TYPE (ImAppMenuItem, im_app_menu_item, GTK_TYPE_MENU_ITEM);
+
+static void
+im_app_menu_item_constructed (GObject *object)
+{
+ ImAppMenuItemPrivate *priv = IM_APP_MENU_ITEM (object)->priv;
+ GtkWidget *grid;
+
+ priv->icon = g_object_ref (gtk_image_new ());
+ gtk_widget_set_margin_right (priv->icon, 6);
+
+ priv->label = g_object_ref (gtk_label_new (""));
+
+ grid = gtk_grid_new ();
+ gtk_grid_attach (GTK_GRID (grid), priv->icon, 0, 0, 1, 1);
+ gtk_grid_attach (GTK_GRID (grid), priv->label, 1, 0, 1, 1);
+
+ gtk_container_add (GTK_CONTAINER (object), grid);
+ gtk_widget_show_all (grid);
+
+ G_OBJECT_CLASS (im_app_menu_item_parent_class)->constructed (object);
+}
+
+static void
+im_app_menu_item_set_action_name (ImAppMenuItem *self,
+ const gchar *action_name)
+{
+ ImAppMenuItemPrivate *priv = self->priv;
+ gboolean enabled = FALSE;
+ GVariant *state;
+
+ if (priv->action != NULL)
+ g_free (priv->action);
+
+ priv->action = g_strdup (action_name);
+
+ priv->is_running = FALSE;
+
+ if (priv->action_group != NULL && priv->action != NULL &&
+ g_action_group_query_action (priv->action_group, priv->action,
+ &enabled, NULL, NULL, NULL, &state))
+ {
+ if (state && g_variant_is_of_type (state, G_VARIANT_TYPE ("b")))
+ priv->is_running = g_variant_get_boolean (state);
+ else
+ enabled = FALSE;
+
+ if (state)
+ g_variant_unref (state);
+ }
+
+ gtk_widget_set_sensitive (GTK_WIDGET (self), enabled);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+im_app_menu_item_action_added (GActionGroup *action_group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ ImAppMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ im_app_menu_item_set_action_name (self, action_name);
+}
+
+static void
+im_app_menu_item_action_removed (GActionGroup *action_group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ ImAppMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ {
+ gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
+ self->priv->is_running = FALSE;
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+static void
+im_app_menu_item_action_enabled_changed (GActionGroup *action_group,
+ gchar *action_name,
+ gboolean enabled,
+ gpointer user_data)
+{
+ ImAppMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ gtk_widget_set_sensitive (GTK_WIDGET (self), enabled);
+}
+
+static void
+im_app_menu_item_action_state_changed (GActionGroup *action_group,
+ gchar *action_name,
+ GVariant *value,
+ gpointer user_data)
+{
+ ImAppMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ {
+ g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("b")));
+
+ self->priv->is_running = g_variant_get_boolean (value);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+static void
+im_app_menu_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ImAppMenuItem *self = IM_APP_MENU_ITEM (object);
+
+ switch (property_id)
+ {
+ case PROP_MENU_ITEM:
+ im_app_menu_item_set_menu_item (self, G_MENU_ITEM (g_value_get_object (value)));
+ break;
+
+ case PROP_ACTION_GROUP:
+ im_app_menu_item_set_action_group (self, G_ACTION_GROUP (g_value_get_object (value)));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+im_app_menu_item_dispose (GObject *object)
+{
+ ImAppMenuItem *self = IM_APP_MENU_ITEM (object);
+
+ if (self->priv->action_group)
+ im_app_menu_item_set_action_group (self, NULL);
+
+ g_clear_object (&self->priv->icon);
+ g_clear_object (&self->priv->label);
+
+ G_OBJECT_CLASS (im_app_menu_item_parent_class)->dispose (object);
+}
+
+static void
+im_app_menu_item_finalize (GObject *object)
+{
+ ImAppMenuItemPrivate *priv = IM_APP_MENU_ITEM (object)->priv;
+
+ g_free (priv->action);
+
+ G_OBJECT_CLASS (im_app_menu_item_parent_class)->finalize (object);
+}
+
+static gboolean
+im_app_menu_item_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ ImAppMenuItemPrivate *priv = IM_APP_MENU_ITEM (widget)->priv;
+
+ GTK_WIDGET_CLASS (im_app_menu_item_parent_class)->draw (widget, cr);
+
+ if (priv->is_running)
+ {
+ const int arrow_width = 5;
+ const double half_arrow_height = 4.5;
+ GtkAllocation alloc;
+ GdkRGBA color;
+ double center;
+
+ gtk_widget_get_allocation (widget, &alloc);
+
+ gtk_style_context_get_color (gtk_widget_get_style_context (widget),
+ gtk_widget_get_state_flags (widget),
+ &color);
+ gdk_cairo_set_source_rgba (cr, &color);
+
+ center = alloc.height / 2 + 0.5;
+
+ cairo_move_to (cr, 0, center - half_arrow_height);
+ cairo_line_to (cr, 0, center + half_arrow_height);
+ cairo_line_to (cr, arrow_width, center);
+ cairo_close_path (cr);
+
+ cairo_fill (cr);
+ }
+
+ return FALSE;
+}
+
+static void
+im_app_menu_item_activate (GtkMenuItem *item)
+{
+ ImAppMenuItemPrivate *priv = IM_APP_MENU_ITEM (item)->priv;
+
+ if (priv->action && priv->action_group)
+ {
+ guint32 event_time = gtk_get_current_event_time ();
+ g_action_group_activate_action (priv->action_group, priv->action, g_variant_new_uint32 (event_time));
+ }
+}
+
+static void
+im_app_menu_item_class_init (ImAppMenuItemClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkMenuItemClass *menu_item_class = GTK_MENU_ITEM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (ImAppMenuItemPrivate));
+
+ object_class->constructed = im_app_menu_item_constructed;
+ object_class->set_property = im_app_menu_set_property;
+ object_class->dispose = im_app_menu_item_dispose;
+ object_class->finalize = im_app_menu_item_finalize;
+
+ widget_class->draw = im_app_menu_item_draw;
+
+ menu_item_class->activate = im_app_menu_item_activate;
+
+ properties[PROP_MENU_ITEM] = g_param_spec_object ("menu-item",
+ "Menu item",
+ "The model GMenuItem for this menu item",
+ G_TYPE_MENU_ITEM,
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_ACTION_GROUP] = g_param_spec_object ("action-group",
+ "Action group",
+ "The action group associated with this menu item",
+ G_TYPE_ACTION_GROUP,
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+}
+
+static void
+im_app_menu_item_init (ImAppMenuItem *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ IM_TYPE_APP_MENU_ITEM,
+ ImAppMenuItemPrivate);
+}
+
+void
+im_app_menu_item_set_menu_item (ImAppMenuItem *self,
+ GMenuItem *menuitem)
+{
+ gchar *iconstr = NULL;
+ GIcon *icon = NULL;
+ gchar *label;
+ gchar *action = NULL;
+
+ if (g_menu_item_get_attribute (menuitem, "x-canonical-icon", "s", &iconstr))
+ {
+ GError *error;
+
+ icon = g_icon_new_for_string (iconstr, &error);
+ if (icon == NULL)
+ {
+ g_warning ("unable to set icon: %s", error->message);
+ g_error_free (error);
+ }
+ g_free (iconstr);
+ }
+ gtk_image_set_from_gicon (GTK_IMAGE (self->priv->icon), icon, GTK_ICON_SIZE_MENU);
+
+ g_menu_item_get_attribute (menuitem, "label", "s", &label);
+ gtk_label_set_label (GTK_LABEL (self->priv->label), label ? label : "");
+
+ g_menu_item_get_attribute (menuitem, "action", "s", &action);
+ im_app_menu_item_set_action_name (self, action);
+
+ if (icon)
+ g_object_unref (icon);
+ g_free (label);
+ g_free (action);
+}
+
+void
+im_app_menu_item_set_action_group (ImAppMenuItem *self,
+ GActionGroup *action_group)
+{
+ ImAppMenuItemPrivate *priv = self->priv;
+
+ if (priv->action_group != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_app_menu_item_action_added, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_app_menu_item_action_removed, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_app_menu_item_action_enabled_changed, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_app_menu_item_action_state_changed, self);
+
+ g_clear_object (&priv->action_group);
+ }
+
+ if (action_group != NULL)
+ {
+ priv->action_group = g_object_ref (action_group);
+
+ g_signal_connect (priv->action_group, "action-added",
+ G_CALLBACK (im_app_menu_item_action_added), self);
+ g_signal_connect (priv->action_group, "action-removed",
+ G_CALLBACK (im_app_menu_item_action_removed), self);
+ g_signal_connect (priv->action_group, "action-enabled-changed",
+ G_CALLBACK (im_app_menu_item_action_enabled_changed), self);
+ g_signal_connect (priv->action_group, "action-state-changed",
+ G_CALLBACK (im_app_menu_item_action_state_changed), self);
+ }
+}
diff --git a/src/im-app-menu-item.h b/src/im-app-menu-item.h
new file mode 100644
index 0000000..519de8d
--- /dev/null
+++ b/src/im-app-menu-item.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * 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/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#ifndef __IM_APP_MENU_ITEM_H__
+#define __IM_APP_MENU_ITEM_H__
+
+#include <gtk/gtk.h>
+
+#define IM_TYPE_APP_MENU_ITEM (im_app_menu_item_get_type ())
+#define IM_APP_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IM_TYPE_APP_MENU_ITEM, ImAppMenuItem))
+#define IM_APP_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IM_TYPE_APP_MENU_ITEM, ImAppMenuItemClass))
+#define IS_IM_APP_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IM_TYPE_APP_MENU_ITEM))
+#define IS_IM_APP_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IM_TYPE_APP_MENU_ITEM))
+#define IM_APP_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IM_TYPE_APP_MENU_ITEM, ImAppMenuItemClass))
+
+typedef struct _ImAppMenuItem ImAppMenuItem;
+typedef struct _ImAppMenuItemClass ImAppMenuItemClass;
+typedef struct _ImAppMenuItemPrivate ImAppMenuItemPrivate;
+
+struct _ImAppMenuItemClass
+{
+ GtkMenuItemClass parent_class;
+};
+
+struct _ImAppMenuItem
+{
+ GtkMenuItem parent;
+ ImAppMenuItemPrivate *priv;
+};
+
+GType im_app_menu_item_get_type (void);
+
+void im_app_menu_item_set_menu_item (ImAppMenuItem *item,
+ GMenuItem *menuitem);
+void im_app_menu_item_set_action_group (ImAppMenuItem *self,
+ GActionGroup *action_group);
+
+#endif
diff --git a/src/im-application-list.c b/src/im-application-list.c
deleted file mode 100644
index bba7972..0000000
--- a/src/im-application-list.c
+++ /dev/null
@@ -1,1139 +0,0 @@
-/*
- * Copyright 2012 Canonical Ltd.
- *
- * 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/>.
- *
- * Authors:
- * Lars Uebernickel <lars.uebernickel@canonical.com>
- */
-
-#include "im-application-list.h"
-
-#include "indicator-messages-application.h"
-#include "gactionmuxer.h"
-
-#include <gio/gdesktopappinfo.h>
-#include <string.h>
-
-typedef GObjectClass ImApplicationListClass;
-
-struct _ImApplicationList
-{
- GObject parent;
-
- GHashTable *applications;
- GActionMuxer *muxer;
-
- GSimpleActionGroup * globalactions;
- GSimpleAction * statusaction;
-
- GHashTable *app_status;
-};
-
-G_DEFINE_TYPE (ImApplicationList, im_application_list, G_TYPE_OBJECT);
-G_DEFINE_QUARK (draws_attention, message_action_draws_attention);
-
-enum
-{
- SOURCE_ADDED,
- SOURCE_CHANGED,
- SOURCE_REMOVED,
- MESSAGE_ADDED,
- MESSAGE_REMOVED,
- APP_ADDED,
- APP_STOPPED,
- REMOVE_ALL,
- STATUS_SET,
- N_SIGNALS
-};
-
-static guint signals[N_SIGNALS];
-
-typedef struct
-{
- ImApplicationList *list;
- GDesktopAppInfo *info;
- gchar *id;
- IndicatorMessagesApplication *proxy;
- GActionMuxer *muxer;
- GSimpleActionGroup *actions;
- GSimpleActionGroup *source_actions;
- GSimpleActionGroup *message_actions;
- GActionMuxer *message_sub_actions;
- GCancellable *cancellable;
- gboolean draws_attention;
-} Application;
-
-
-/* Prototypes */
-static void status_activated (GSimpleAction * action,
- GVariant * param,
- gpointer user_data);
-
-static void
-application_free (gpointer data)
-{
- Application *app = data;
-
- if (!app)
- return;
-
- g_object_unref (app->info);
- g_free (app->id);
-
- if (app->cancellable)
- {
- g_cancellable_cancel (app->cancellable);
- g_clear_object (&app->cancellable);
- }
-
- if (app->proxy)
- g_object_unref (app->proxy);
-
- if (app->muxer)
- {
- g_object_unref (app->muxer);
- g_object_unref (app->source_actions);
- g_object_unref (app->message_actions);
- g_object_unref (app->message_sub_actions);
- }
-
- g_slice_free (Application, app);
-}
-
-static gboolean
-application_draws_attention (gpointer key,
- gpointer value,
- gpointer user_data)
-{
- Application *app = value;
-
- return app->draws_attention;
-}
-
-static void
-im_application_list_update_draws_attention (ImApplicationList *list)
-{
- const gchar *icon_name;
- GVariant *state;
- GActionGroup *main_actions;
-
- if (g_hash_table_find (list->applications, application_draws_attention, NULL))
- icon_name = "indicator-messages-new";
- else
- icon_name = "indicator-messages";
-
- main_actions = g_action_muxer_get_group (list->muxer, NULL);
- state = g_variant_new ("(sssb)", "", icon_name, "Messages", TRUE);
- g_action_group_change_action_state (main_actions, "messages", state);
-}
-
-/* Check a source action to see if it draws */
-static gboolean
-app_source_action_check_draw (Application * app, const gchar * action_name)
-{
- gboolean retval = FALSE;
- GVariant * state;
- GVariant * draw;
-
- state = g_action_group_get_action_state (G_ACTION_GROUP(app->source_actions), action_name);
-
- /* uxsb */
- draw = g_variant_get_child_value(state, 3);
- retval = g_variant_get_boolean(draw);
-
- g_variant_unref(draw);
- g_variant_unref(state);
-
- return retval;
-}
-
-/* Check a message action to see if it draws */
-static gboolean
-app_message_action_check_draw (Application * app, const gchar * action_name)
-{
- GAction * action = NULL;
- action = g_simple_action_group_lookup (app->message_actions, action_name);
- return GPOINTER_TO_INT(g_object_get_qdata(G_OBJECT(action), message_action_draws_attention_quark()));
-}
-
-/* Regenerate the draw attention flag based on the sources and messages
- that we have in the action groups */
-static void
-app_check_draw_attention (Application * app)
-{
- gchar **source_actions = NULL;
- gchar **message_actions = NULL;
- gchar **it;
-
- source_actions = g_action_group_list_actions (G_ACTION_GROUP (app->source_actions));
- for (it = source_actions; *it && !app->draws_attention; it++)
- app->draws_attention = app_source_action_check_draw (app, *it);
-
- message_actions = g_action_group_list_actions (G_ACTION_GROUP (app->message_actions));
- for (it = message_actions; *it; it++)
- app->draws_attention = app_message_action_check_draw (app, *it);
-
- g_strfreev (source_actions);
- g_strfreev (message_actions);
-
- return;
-}
-
-/* Remove a source from an application, signal up and update the status
- of the draws attention flag. */
-static void
-im_application_list_source_removed (Application *app,
- const gchar *id)
-{
- g_simple_action_group_remove (app->source_actions, id);
-
- g_signal_emit (app->list, signals[SOURCE_REMOVED], 0, app->id, id);
-
- if (app->draws_attention)
- {
- app->draws_attention = FALSE;
- app_check_draw_attention(app);
- }
-
- im_application_list_update_draws_attention (app->list);
-}
-
-static void
-im_application_list_source_activated (GSimpleAction *action,
- GVariant *parameter,
- gpointer user_data)
-{
- Application *app = user_data;
- const gchar *source_id;
-
- source_id = g_action_get_name (G_ACTION (action));
-
- if (g_variant_get_boolean (parameter))
- {
- indicator_messages_application_call_activate_source (app->proxy,
- source_id,
- app->cancellable,
- NULL, NULL);
- }
- else
- {
- const gchar *sources[] = { source_id, NULL };
- const gchar *messages[] = { NULL };
- indicator_messages_application_call_dismiss (app->proxy, sources, messages,
- app->cancellable, NULL, NULL);
- }
-
- im_application_list_source_removed (app, source_id);
-}
-
-static void
-im_application_list_message_removed (Application *app,
- const gchar *id)
-{
- g_simple_action_group_remove (app->message_actions, id);
- g_action_muxer_remove (app->message_sub_actions, id);
-
- im_application_list_update_draws_attention (app->list);
-
- g_signal_emit (app->list, signals[MESSAGE_REMOVED], 0, app->id, id);
-}
-
-static void
-im_application_list_message_activated (GSimpleAction *action,
- GVariant *parameter,
- gpointer user_data)
-{
- Application *app = user_data;
- const gchar *message_id;
-
- message_id = g_action_get_name (G_ACTION (action));
-
- if (g_variant_get_boolean (parameter))
- {
- indicator_messages_application_call_activate_message (app->proxy,
- message_id,
- "",
- g_variant_new_array (G_VARIANT_TYPE_VARIANT, NULL, 0),
- app->cancellable,
- NULL, NULL);
- }
- else
- {
- const gchar *sources[] = { NULL };
- const gchar *messages[] = { message_id, NULL };
- indicator_messages_application_call_dismiss (app->proxy, sources, messages,
- app->cancellable, NULL, NULL);
- }
-
- im_application_list_message_removed (app, message_id);
-}
-
-static void
-im_application_list_sub_message_activated (GSimpleAction *action,
- GVariant *parameter,
- gpointer user_data)
-{
- Application *app = user_data;
- const gchar *message_id;
- const gchar *action_id;
- GVariantBuilder builder;
-
- message_id = g_object_get_data (G_OBJECT (action), "message");
- action_id = g_action_get_name (G_ACTION (action));
-
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
- if (parameter)
- g_variant_builder_add (&builder, "v", parameter);
-
- indicator_messages_application_call_activate_message (app->proxy,
- message_id,
- action_id,
- g_variant_builder_end (&builder),
- app->cancellable,
- NULL, NULL);
-
- im_application_list_message_removed (app, message_id);
-}
-
-static void
-im_application_list_remove_all (GSimpleAction *action,
- GVariant *parameter,
- gpointer user_data)
-{
- ImApplicationList *list = user_data;
- GHashTableIter iter;
- Application *app;
-
- g_signal_emit (list, signals[REMOVE_ALL], 0);
-
- g_hash_table_iter_init (&iter, list->applications);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &app))
- {
- gchar **source_actions;
- gchar **message_actions;
- gchar **it;
-
- app->draws_attention = FALSE;
-
- source_actions = g_action_group_list_actions (G_ACTION_GROUP (app->source_actions));
- for (it = source_actions; *it; it++)
- im_application_list_source_removed (app, *it);
-
- message_actions = g_action_group_list_actions (G_ACTION_GROUP (app->message_actions));
- for (it = message_actions; *it; it++)
- im_application_list_message_removed (app, *it);
-
- if (app->proxy != NULL) /* If it is remote, we tell the app we've cleared */
- {
- indicator_messages_application_call_dismiss (app->proxy,
- (const gchar * const *) source_actions,
- (const gchar * const *) message_actions,
- app->cancellable, NULL, NULL);
- }
-
- g_strfreev (source_actions);
- g_strfreev (message_actions);
- }
-
- im_application_list_update_draws_attention (list);
-}
-
-static void
-im_application_list_dispose (GObject *object)
-{
- ImApplicationList *list = IM_APPLICATION_LIST (object);
-
- g_clear_object (&list->statusaction);
- g_clear_object (&list->globalactions);
- g_clear_pointer (&list->app_status, g_hash_table_unref);
-
- g_clear_pointer (&list->applications, g_hash_table_unref);
- g_clear_object (&list->muxer);
-
- G_OBJECT_CLASS (im_application_list_parent_class)->dispose (object);
-}
-
-static void
-im_application_list_finalize (GObject *object)
-{
- G_OBJECT_CLASS (im_application_list_parent_class)->finalize (object);
-}
-
-static void
-im_application_list_class_init (ImApplicationListClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->dispose = im_application_list_dispose;
- object_class->finalize = im_application_list_finalize;
-
- signals[SOURCE_ADDED] = g_signal_new ("source-added",
- IM_TYPE_APPLICATION_LIST,
- G_SIGNAL_RUN_FIRST,
- 0,
- NULL, NULL,
- g_cclosure_marshal_generic,
- G_TYPE_NONE,
- 4,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING);
-
- signals[SOURCE_CHANGED] = g_signal_new ("source-changed",
- IM_TYPE_APPLICATION_LIST,
- G_SIGNAL_RUN_FIRST,
- 0,
- NULL, NULL,
- g_cclosure_marshal_generic,
- G_TYPE_NONE,
- 4,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING);
-
- signals[SOURCE_REMOVED] = g_signal_new ("source-removed",
- IM_TYPE_APPLICATION_LIST,
- G_SIGNAL_RUN_FIRST,
- 0,
- NULL, NULL,
- g_cclosure_marshal_generic,
- G_TYPE_NONE,
- 2,
- G_TYPE_STRING,
- G_TYPE_STRING);
-
- signals[MESSAGE_ADDED] = g_signal_new ("message-added",
- IM_TYPE_APPLICATION_LIST,
- G_SIGNAL_RUN_FIRST,
- 0,
- NULL, NULL,
- g_cclosure_marshal_generic,
- G_TYPE_NONE,
- 10,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_VARIANT,
- G_TYPE_INT64,
- G_TYPE_BOOLEAN);
-
- signals[MESSAGE_REMOVED] = g_signal_new ("message-removed",
- IM_TYPE_APPLICATION_LIST,
- G_SIGNAL_RUN_FIRST,
- 0,
- NULL, NULL,
- g_cclosure_marshal_generic,
- G_TYPE_NONE,
- 2,
- G_TYPE_STRING,
- G_TYPE_STRING);
-
- signals[APP_ADDED] = g_signal_new ("app-added",
- IM_TYPE_APPLICATION_LIST,
- G_SIGNAL_RUN_FIRST,
- 0,
- NULL, NULL,
- g_cclosure_marshal_generic,
- G_TYPE_NONE,
- 2,
- G_TYPE_STRING,
- G_TYPE_DESKTOP_APP_INFO);
-
- signals[APP_STOPPED] = g_signal_new ("app-stopped",
- IM_TYPE_APPLICATION_LIST,
- G_SIGNAL_RUN_FIRST,
- 0,
- NULL, NULL,
- g_cclosure_marshal_VOID__OBJECT,
- G_TYPE_NONE,
- 1,
- G_TYPE_STRING);
-
- signals[REMOVE_ALL] = g_signal_new ("remove-all",
- IM_TYPE_APPLICATION_LIST,
- G_SIGNAL_RUN_FIRST,
- 0,
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE,
- 0);
-
- signals[STATUS_SET] = g_signal_new ("status-set",
- IM_TYPE_APPLICATION_LIST,
- G_SIGNAL_RUN_FIRST,
- 0,
- NULL, NULL,
- g_cclosure_marshal_generic,
- G_TYPE_NONE,
- 1,
- G_TYPE_STRING);
-}
-
-static void
-im_application_list_init (ImApplicationList *list)
-{
- const GActionEntry action_entries[] = {
- { "messages", NULL, NULL, "('', 'indicator-messages', 'Messages', true)", NULL },
- { "remove-all", im_application_list_remove_all }
- };
-
- list->applications = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, application_free);
- list->app_status = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-
- list->globalactions = g_simple_action_group_new ();
- g_simple_action_group_add_entries (list->globalactions, action_entries, G_N_ELEMENTS (action_entries), list);
-
- list->statusaction = g_simple_action_new_stateful("status", G_VARIANT_TYPE_STRING, g_variant_new_string("offline"));
- g_signal_connect(list->statusaction, "activate", G_CALLBACK(status_activated), list);
- g_simple_action_group_insert(list->globalactions, G_ACTION(list->statusaction));
-
- list->muxer = g_action_muxer_new ();
- g_action_muxer_insert (list->muxer, NULL, G_ACTION_GROUP (list->globalactions));
-
-}
-
-ImApplicationList *
-im_application_list_new (void)
-{
- return g_object_new (IM_TYPE_APPLICATION_LIST, NULL);
-}
-
-static gchar *
-im_application_list_canonical_id (const gchar *id)
-{
- gchar *str;
- gchar *p;
- int len;
-
- len = strlen (id);
- if (g_str_has_suffix (id, ".desktop"))
- len -= 8;
-
- str = g_strndup (id, len);
-
- for (p = str; *p; p++)
- {
- if (*p == '.')
- *p = '_';
- }
-
- return str;
-}
-
-static Application *
-im_application_list_lookup (ImApplicationList *list,
- const gchar *desktop_id)
-{
- gchar *id;
- Application *app;
-
- id = im_application_list_canonical_id (desktop_id);
- app = g_hash_table_lookup (list->applications, id);
-
- g_free (id);
- return app;
-}
-
-void
-im_application_list_activate_launch (GSimpleAction *action,
- GVariant *parameter,
- gpointer user_data)
-{
- Application *app = user_data;
- GError *error = NULL;
-
- if (!g_app_info_launch (G_APP_INFO (app->info), NULL, NULL, &error))
- {
- g_warning ("unable to launch application: %s", error->message);
- g_error_free (error);
- }
-}
-
-void
-im_application_list_activate_app_action (GSimpleAction *action,
- GVariant *parameter,
- gpointer user_data)
-{
- Application *app = user_data;
-
- g_desktop_app_info_launch_action (app->info, g_action_get_name (G_ACTION (action)), NULL);
-}
-
-void
-im_application_list_add (ImApplicationList *list,
- const gchar *desktop_id)
-{
- GDesktopAppInfo *info;
- Application *app;
- const gchar *id;
- GSimpleActionGroup *actions;
- GSimpleAction *launch_action;
-
- g_return_if_fail (IM_IS_APPLICATION_LIST (list));
- g_return_if_fail (desktop_id != NULL);
-
- if (im_application_list_lookup (list, desktop_id))
- return;
-
- info = g_desktop_app_info_new (desktop_id);
- if (!info)
- {
- g_warning ("an application with id '%s' is not installed", desktop_id);
- return;
- }
-
- id = g_app_info_get_id (G_APP_INFO (info));
- g_return_if_fail (id != NULL);
-
- app = g_slice_new0 (Application);
- app->info = info;
- app->id = im_application_list_canonical_id (id);
- app->list = list;
- app->muxer = g_action_muxer_new ();
- app->source_actions = g_simple_action_group_new ();
- app->message_actions = g_simple_action_group_new ();
- app->message_sub_actions = g_action_muxer_new ();
- app->draws_attention = FALSE;
-
- actions = g_simple_action_group_new ();
-
- launch_action = g_simple_action_new_stateful ("launch", NULL, g_variant_new_boolean (FALSE));
- g_signal_connect (launch_action, "activate", G_CALLBACK (im_application_list_activate_launch), app);
- g_action_map_add_action (G_ACTION_MAP (actions), G_ACTION (launch_action));
-
- {
- const gchar *const *app_actions;
-
- for (app_actions = g_desktop_app_info_list_actions (app->info); *app_actions; app_actions++)
- {
- GSimpleAction *action;
-
- action = g_simple_action_new (*app_actions, NULL);
- g_signal_connect (action, "activate", G_CALLBACK (im_application_list_activate_app_action), app);
- g_action_map_add_action (G_ACTION_MAP (actions), G_ACTION (action));
-
- g_object_unref (action);
- }
- }
-
- g_action_muxer_insert (app->muxer, NULL, G_ACTION_GROUP (actions));
- g_action_muxer_insert (app->muxer, "src", G_ACTION_GROUP (app->source_actions));
- g_action_muxer_insert (app->muxer, "msg", G_ACTION_GROUP (app->message_actions));
- g_action_muxer_insert (app->muxer, "msg-actions", G_ACTION_GROUP (app->message_sub_actions));
-
- g_hash_table_insert (list->applications, (gpointer) app->id, app);
- g_action_muxer_insert (list->muxer, app->id, G_ACTION_GROUP (app->muxer));
-
- g_signal_emit (app->list, signals[APP_ADDED], 0, app->id, app->info);
-
- g_object_unref (launch_action);
- g_object_unref (actions);
-}
-
-void
-im_application_list_remove (ImApplicationList *list,
- const gchar *id)
-{
- Application *app;
-
- g_return_if_fail (IM_IS_APPLICATION_LIST (list));
-
- app = im_application_list_lookup (list, id);
- if (app)
- {
- if (app->proxy || app->cancellable)
- g_signal_emit (app->list, signals[APP_STOPPED], 0, app->id);
-
- g_hash_table_remove (list->applications, id);
- g_action_muxer_remove (list->muxer, id);
-
- im_application_list_update_draws_attention (list);
- }
-}
-
-static void
-im_application_list_source_added (Application *app,
- guint position,
- GVariant *source)
-{
- const gchar *id;
- const gchar *label;
- const gchar *iconstr;
- guint32 count;
- gint64 time;
- const gchar *string;
- gboolean draws_attention;
- GVariant *state;
- GSimpleAction *action;
-
- g_variant_get (source, "(&s&s&sux&sb)",
- &id, &label, &iconstr, &count, &time, &string, &draws_attention);
-
- state = g_variant_new ("(uxsb)", count, time, string, draws_attention);
- action = g_simple_action_new_stateful (id, G_VARIANT_TYPE_BOOLEAN, state);
- g_signal_connect (action, "activate", G_CALLBACK (im_application_list_source_activated), app);
-
- g_simple_action_group_insert (app->source_actions, G_ACTION (action));
-
- g_signal_emit (app->list, signals[SOURCE_ADDED], 0, app->id, id, label, iconstr);
-
- if (draws_attention)
- app->draws_attention = TRUE;
-
- im_application_list_update_draws_attention (app->list);
-
- g_object_unref (action);
-}
-
-static void
-im_application_list_source_changed (Application *app,
- GVariant *source)
-{
- const gchar *id;
- const gchar *label;
- const gchar *iconstr;
- guint32 count;
- gint64 time;
- const gchar *string;
- gboolean draws_attention;
-
- g_variant_get (source, "(&s&s&sux&sb)",
- &id, &label, &iconstr, &count, &time, &string, &draws_attention);
-
- g_action_group_change_action_state (G_ACTION_GROUP (app->source_actions), id,
- g_variant_new ("(uxsb)", count, time, string, draws_attention));
-
- g_signal_emit (app->list, signals[SOURCE_CHANGED], 0, app->id, id, label, iconstr);
-
- im_application_list_update_draws_attention (app->list);
-}
-
-static void
-im_application_list_sources_listed (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- Application *app = user_data;
- GVariant *sources;
- GError *error = NULL;
-
- if (indicator_messages_application_call_list_sources_finish (app->proxy, &sources, result, &error))
- {
- GVariantIter iter;
- GVariant *source;
- guint i = 0;
-
- g_variant_iter_init (&iter, sources);
- while ((source = g_variant_iter_next_value (&iter)))
- {
- im_application_list_source_added (app, i++, source);
- g_variant_unref (source);
- }
-
- g_variant_unref (sources);
- }
- else
- {
- g_warning ("could not fetch the list of sources: %s", error->message);
- g_error_free (error);
- }
-}
-
-static gchar *
-get_symbolic_app_icon_string (GIcon *icon)
-{
- const gchar * const *names;
- gchar *symbolic_name;
- GIcon *symbolic_icon;
- gchar *str;
-
- if (!G_IS_THEMED_ICON (icon))
- return NULL;
-
- names = g_themed_icon_get_names (G_THEMED_ICON (icon));
- if (!names || !names[0])
- return NULL;
-
- symbolic_icon = g_themed_icon_new_from_names ((gchar **) names, -1);
-
- symbolic_name = g_strconcat (names[0], "-symbolic", NULL);
- g_themed_icon_prepend_name (G_THEMED_ICON (symbolic_icon), symbolic_name);
-
- str = g_icon_to_string (symbolic_icon);
-
- g_free (symbolic_name);
- g_object_unref (symbolic_icon);
- return str;
-}
-
-static void
-im_application_list_message_added (Application *app,
- GVariant *message)
-{
- const gchar *id;
- const gchar *iconstr;
- const gchar *title;
- const gchar *subtitle;
- const gchar *body;
- gint64 time;
- GVariantIter *action_iter;
- gboolean draws_attention;
- GSimpleAction *action;
- GIcon *app_icon;
- gchar *app_iconstr = NULL;
- GVariant *actions = NULL;
-
- g_variant_get (message, "(&s&s&s&s&sxaa{sv}b)",
- &id, &iconstr, &title, &subtitle, &body, &time, &action_iter, &draws_attention);
-
- app_icon = g_app_info_get_icon (G_APP_INFO (app->info));
- if (app_icon)
- app_iconstr = get_symbolic_app_icon_string (app_icon);
-
- action = g_simple_action_new (id, G_VARIANT_TYPE_BOOLEAN);
- g_object_set_qdata(G_OBJECT(action), message_action_draws_attention_quark(), GINT_TO_POINTER(draws_attention));
- g_signal_connect (action, "activate", G_CALLBACK (im_application_list_message_activated), app);
- g_simple_action_group_insert (app->message_actions, G_ACTION (action));
-
- {
- GVariant *entry;
- GSimpleActionGroup *action_group;
- GVariantBuilder actions_builder;
-
- g_variant_builder_init (&actions_builder, G_VARIANT_TYPE ("aa{sv}"));
- action_group = g_simple_action_group_new ();
-
- while ((entry = g_variant_iter_next_value (action_iter)))
- {
- const gchar *name;
- GSimpleAction *action;
- GVariant *label;
- const gchar *type = NULL;
- GVariant *hint;
- GVariantBuilder dict_builder;
- gchar *prefixed_name;
-
- if (!g_variant_lookup (entry, "name", "&s", &name))
- {
- g_warning ("action dictionary for message '%s' is missing 'name' key", id);
- continue;
- }
-
- label = g_variant_lookup_value (entry, "label", G_VARIANT_TYPE_STRING);
- g_variant_lookup (entry, "parameter-type", "&g", &type);
- hint = g_variant_lookup_value (entry, "parameter-hint", NULL);
-
- action = g_simple_action_new (name, type ? G_VARIANT_TYPE (type) : NULL);
- g_object_set_data_full (G_OBJECT (action), "message", g_strdup (id), g_free);
- g_signal_connect (action, "activate", G_CALLBACK (im_application_list_sub_message_activated), app);
- g_simple_action_group_insert (action_group, G_ACTION (action));
-
- g_variant_builder_init (&dict_builder, G_VARIANT_TYPE ("a{sv}"));
-
- prefixed_name = g_strjoin (".", app->id, "msg-actions", id, name, NULL);
- g_variant_builder_add (&dict_builder, "{sv}", "name", g_variant_new_string (prefixed_name));
-
- if (label)
- {
- g_variant_builder_add (&dict_builder, "{sv}", "label", label);
- g_variant_unref (label);
- }
-
- if (type)
- g_variant_builder_add (&dict_builder, "{sv}", "parameter-type", g_variant_new_string (type));
-
- if (hint)
- {
- g_variant_builder_add (&dict_builder, "{sv}", "parameter-hint", hint);
- g_variant_unref (hint);
- }
-
- g_variant_builder_add (&actions_builder, "a{sv}", &dict_builder);
-
- g_object_unref (action);
- g_variant_unref (entry);
- g_free (prefixed_name);
- }
-
- g_action_muxer_insert (app->message_sub_actions, id, G_ACTION_GROUP (action_group));
- actions = g_variant_builder_end (&actions_builder);
-
- g_object_unref (action_group);
- }
-
- if (draws_attention)
- app->draws_attention = TRUE;
-
- im_application_list_update_draws_attention (app->list);
-
- g_signal_emit (app->list, signals[MESSAGE_ADDED], 0,
- app->id, app_iconstr, id, iconstr, title,
- subtitle, body, actions, time, draws_attention);
-
- g_variant_iter_free (action_iter);
- g_free (app_iconstr);
- g_object_unref (action);
-}
-
-static void
-im_application_list_messages_listed (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- Application *app = user_data;
- GVariant *messages;
- GError *error = NULL;
-
- if (indicator_messages_application_call_list_messages_finish (app->proxy, &messages, result, &error))
- {
- GVariantIter iter;
- GVariant *message;
-
- g_variant_iter_init (&iter, messages);
- while ((message = g_variant_iter_next_value (&iter)))
- {
- im_application_list_message_added (app, message);
- g_variant_unref (message);
- }
-
- g_variant_unref (messages);
- }
- else
- {
- g_warning ("could not fetch the list of messages: %s", error->message);
- g_error_free (error);
- }
-}
-
-static void
-im_application_list_unset_remote (Application *app)
-{
- gboolean was_running;
-
- was_running = app->proxy || app->cancellable;
-
- if (app->cancellable)
- {
- g_cancellable_cancel (app->cancellable);
- g_clear_object (&app->cancellable);
- }
- g_clear_object (&app->proxy);
-
- /* clear actions by creating a new action group and overriding it in
- * the muxer */
- g_object_unref (app->source_actions);
- g_object_unref (app->message_actions);
- g_object_unref (app->message_sub_actions);
- app->source_actions = g_simple_action_group_new ();
- app->message_actions = g_simple_action_group_new ();
- app->message_sub_actions = g_action_muxer_new ();
- g_action_muxer_insert (app->muxer, "src", G_ACTION_GROUP (app->source_actions));
- g_action_muxer_insert (app->muxer, "msg", G_ACTION_GROUP (app->message_actions));
- g_action_muxer_insert (app->muxer, "msg-actions", G_ACTION_GROUP (app->message_sub_actions));
-
- im_application_list_update_draws_attention (app->list);
- g_action_group_change_action_state (G_ACTION_GROUP (app->muxer), "launch", g_variant_new_boolean (FALSE));
-
- if (was_running)
- g_signal_emit (app->list, signals[APP_STOPPED], 0, app->id);
-}
-
-static void
-im_application_list_app_vanished (GDBusConnection *connection,
- const gchar *name,
- gpointer user_data)
-{
- Application *app = user_data;
-
- im_application_list_unset_remote (app);
-}
-
-static void
-im_application_list_proxy_created (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- Application *app = user_data;
- GError *error = NULL;
-
- app->proxy = indicator_messages_application_proxy_new_finish (result, &error);
- if (!app->proxy)
- {
- if (error->code != G_IO_ERROR_CANCELLED)
- g_warning ("could not create application proxy: %s", error->message);
- g_error_free (error);
- return;
- }
-
- indicator_messages_application_call_list_sources (app->proxy, app->cancellable,
- im_application_list_sources_listed, app);
- indicator_messages_application_call_list_messages (app->proxy, app->cancellable,
- im_application_list_messages_listed, app);
-
- g_signal_connect_swapped (app->proxy, "source-added", G_CALLBACK (im_application_list_source_added), app);
- g_signal_connect_swapped (app->proxy, "source-changed", G_CALLBACK (im_application_list_source_changed), app);
- g_signal_connect_swapped (app->proxy, "source-removed", G_CALLBACK (im_application_list_source_removed), app);
- g_signal_connect_swapped (app->proxy, "message-added", G_CALLBACK (im_application_list_message_added), app);
- g_signal_connect_swapped (app->proxy, "message-removed", G_CALLBACK (im_application_list_message_removed), app);
-
- g_action_group_change_action_state (G_ACTION_GROUP (app->muxer), "launch", g_variant_new_boolean (TRUE));
-
- g_bus_watch_name_on_connection (g_dbus_proxy_get_connection (G_DBUS_PROXY (app->proxy)),
- g_dbus_proxy_get_name (G_DBUS_PROXY (app->proxy)),
- G_BUS_NAME_WATCHER_FLAGS_NONE,
- NULL, im_application_list_app_vanished,
- app, NULL);
-}
-
-void
-im_application_list_set_remote (ImApplicationList *list,
- const gchar *id,
- GDBusConnection *connection,
- const gchar *unique_bus_name,
- const gchar *object_path)
-{
- Application *app;
-
- g_return_if_fail (IM_IS_APPLICATION_LIST (list));
-
- app = im_application_list_lookup (list, id);
- if (!app)
- {
- g_warning ("'%s' is not a registered application", id);
- return;
- }
-
- if (app->cancellable)
- {
- gchar *name_owner = NULL;
-
- if (app->proxy)
- name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (app->proxy));
- g_warning ("replacing '%s' at %s with %s", id, name_owner, unique_bus_name);
-
- im_application_list_unset_remote (app);
-
- g_free (name_owner);
- }
-
- app->cancellable = g_cancellable_new ();
- indicator_messages_application_proxy_new (connection, G_DBUS_PROXY_FLAGS_NONE,
- unique_bus_name, object_path, app->cancellable,
- im_application_list_proxy_created, app);
-}
-
-GActionGroup *
-im_application_list_get_action_group (ImApplicationList *list)
-{
- g_return_val_if_fail (IM_IS_APPLICATION_LIST (list), NULL);
-
- return G_ACTION_GROUP (list->muxer);
-}
-
-GList *
-im_application_list_get_applications (ImApplicationList *list)
-{
- g_return_val_if_fail (IM_IS_APPLICATION_LIST (list), NULL);
-
- return g_hash_table_get_keys (list->applications);
-}
-
-GDesktopAppInfo *
-im_application_list_get_application (ImApplicationList *list,
- const gchar *id)
-{
- Application *app;
-
- g_return_val_if_fail (IM_IS_APPLICATION_LIST (list), NULL);
-
- app = g_hash_table_lookup (list->applications, id);
- return app ? app->info : NULL;
-}
-
-static void
-status_activated (GSimpleAction * action, GVariant * param, gpointer user_data)
-{
- g_return_if_fail (IM_IS_APPLICATION_LIST(user_data));
- ImApplicationList * list = IM_APPLICATION_LIST(user_data);
- const gchar * status = g_variant_get_string(param, NULL);
-
- g_simple_action_set_state(action, param);
-
- GList * appshash = g_hash_table_get_keys(list->app_status);
- GList * appsfree = g_list_copy_deep(appshash, (GCopyFunc)g_strdup, NULL);
- GList * app;
-
- for (app = appsfree; app != NULL; app = g_list_next(app)) {
- g_hash_table_insert(list->app_status, app->data, g_strdup(status));
- }
-
- g_list_free(appshash);
- g_list_free(appsfree);
-
- g_signal_emit (list, signals[STATUS_SET], 0, status);
-
- return;
-}
-
-#define STATUS_ID_OFFLINE (G_N_ELEMENTS(status_ids) - 1)
-static const gchar *status_ids[] = { "available", "away", "busy", "invisible", "offline" };
-
-static guint
-status2val (const gchar * string)
-{
- if (string == NULL) return STATUS_ID_OFFLINE;
-
- guint i;
- for (i = 0; i < G_N_ELEMENTS(status_ids); i++) {
- if (g_strcmp0(status_ids[i], string) == 0) {
- break;
- }
- }
-
- if (i > STATUS_ID_OFFLINE)
- i = STATUS_ID_OFFLINE;
-
- return i;
-}
-
-void
-im_application_list_set_status (ImApplicationList * list, const gchar * id, const gchar *status)
-{
- g_return_if_fail (IM_IS_APPLICATION_LIST (list));
-
- g_hash_table_insert(list->app_status, im_application_list_canonical_id(id), g_strdup(status));
-
- guint final_status = STATUS_ID_OFFLINE;
-
- GList * statuses = g_hash_table_get_values(list->app_status);
- GList * statusentry;
-
- for (statusentry = statuses; statusentry != NULL; statusentry = g_list_next(statusentry)) {
- guint statusval = status2val((gchar *)statusentry->data);
- final_status = MIN(final_status, statusval);
- }
-
- g_list_free(statuses);
-
- g_simple_action_set_state(list->statusaction, g_variant_new_string(status_ids[final_status]));
-
- return;
-}
-
diff --git a/src/im-application-list.h b/src/im-application-list.h
deleted file mode 100644
index f1de220..0000000
--- a/src/im-application-list.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2012 Canonical Ltd.
- *
- * 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/>.
- *
- * Authors:
- * Lars Uebernickel <lars.uebernickel@canonical.com>
- */
-
-#ifndef __IM_APPLICATION_LIST_H__
-#define __IM_APPLICATION_LIST_H__
-
-#include <gio/gio.h>
-#include <gio/gdesktopappinfo.h>
-
-#define IM_TYPE_APPLICATION_LIST (im_application_list_get_type ())
-#define IM_APPLICATION_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IM_TYPE_APPLICATION_LIST, ImApplicationList))
-#define IM_APPLICATION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IM_TYPE_APPLICATION_LIST, ImApplicationListClass))
-#define IM_IS_APPLICATION_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IM_TYPE_APPLICATION_LIST))
-#define IM_IS_APPLICATION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IM_TYPE_APPLICATION_LIST))
-#define IM_APPLICATION_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IM_TYPE_APPLICATION_LIST, ImApplicationListClass))
-
-typedef struct _ImApplicationList ImApplicationList;
-
-GType im_application_list_get_type (void);
-
-ImApplicationList * im_application_list_new (void);
-
-void im_application_list_add (ImApplicationList *list,
- const gchar *desktop_id);
-
-void im_application_list_remove (ImApplicationList *list,
- const gchar *id);
-
-void im_application_list_set_remote (ImApplicationList *list,
- const gchar *id,
- GDBusConnection *connection,
- const gchar *unique_bus_name,
- const gchar *object_path);
-
-GActionGroup * im_application_list_get_action_group (ImApplicationList *list);
-
-GList * im_application_list_get_applications (ImApplicationList *list);
-
-GDesktopAppInfo * im_application_list_get_application (ImApplicationList *list,
- const gchar *id);
-
-void im_application_list_set_status (ImApplicationList *list,
- const gchar *id,
- const gchar *status);
-
-#endif
diff --git a/src/im-desktop-menu.c b/src/im-desktop-menu.c
deleted file mode 100644
index 1040d62..0000000
--- a/src/im-desktop-menu.c
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright 2012 Canonical Ltd.
- *
- * 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/>.
- *
- * Authors:
- * Lars Uebernickel <lars.uebernickel@canonical.com>
- */
-
-#include "im-desktop-menu.h"
-#include <glib/gi18n.h>
-
-typedef ImMenuClass ImDesktopMenuClass;
-
-struct _ImDesktopMenu
-{
- ImMenu parent;
-
- GHashTable *source_sections;
-};
-
-G_DEFINE_TYPE (ImDesktopMenu, im_desktop_menu, IM_TYPE_MENU);
-
-static void
-im_desktop_menu_app_added (ImApplicationList *applist,
- const gchar *app_id,
- GDesktopAppInfo *app_info,
- gpointer user_data)
-{
- ImDesktopMenu *menu = user_data;
- GMenu *section;
- GMenu *app_section;
- GMenu *source_section;
- gchar *namespace;
-
- app_section = g_menu_new ();
-
- /* application launcher */
- {
- GMenuItem *item;
- GVariant *icon;
-
- item = g_menu_item_new (g_app_info_get_name (G_APP_INFO (app_info)), "launch");
- g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.application");
-
- icon = g_icon_serialize (g_app_info_get_icon (G_APP_INFO (app_info)));
- if (icon)
- {
- g_menu_item_set_attribute_value (item, "icon", icon);
- g_variant_unref (icon);
- }
-
- g_menu_append_item (app_section, item);
-
- g_object_unref (item);
- }
-
- /* application actions */
-#if 0
- {
- const gchar *const *actions;
-
- for (actions = g_desktop_app_info_list_actions (app_info); *actions; actions++)
- {
- gchar *label;
-
- label = g_desktop_app_info_get_action_name (app_info, *actions);
- g_menu_append (app_section, label, *actions);
-
- g_free (label);
- }
- }
-#endif
-
- source_section = g_menu_new ();
-
- section = g_menu_new ();
- g_menu_append_section (section, NULL, G_MENU_MODEL (app_section));
- g_menu_append_section (section, NULL, G_MENU_MODEL (source_section));
-
- namespace = g_strconcat ("indicator.", app_id, NULL);
- im_menu_insert_section (IM_MENU (menu), -1, namespace, G_MENU_MODEL (section));
- g_hash_table_insert (menu->source_sections, g_strdup (app_id), source_section);
-
- g_free (namespace);
- g_object_unref (section);
- g_object_unref (app_section);
-}
-
-static void
-im_desktop_menu_source_added (ImApplicationList *applist,
- const gchar *app_id,
- const gchar *source_id,
- const gchar *label,
- const gchar *icon,
- gpointer user_data)
-{
- ImDesktopMenu *menu = user_data;
- GMenu *source_section;
- GMenuItem *item;
- gchar *action;
-
- source_section = g_hash_table_lookup (menu->source_sections, app_id);
- g_return_if_fail (source_section != NULL);
-
- action = g_strconcat ("src.", source_id, NULL);
- item = g_menu_item_new (label, action);
- g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.messages.source");
- if (icon && *icon)
- g_menu_item_set_attribute (item, "icon", "s", icon);
-
- g_menu_append_item (source_section, item);
-
- g_free (action);
- g_object_unref (item);
-}
-
-static void
-im_desktop_menu_source_removed (ImApplicationList *applist,
- const gchar *app_id,
- const gchar *source_id,
- gpointer user_data)
-{
- ImDesktopMenu *menu = user_data;
- GMenu *source_section;
- gint n_items;
- gchar *action;
- gint i;
-
- source_section = g_hash_table_lookup (menu->source_sections, app_id);
- g_return_if_fail (source_section != NULL);
-
- n_items = g_menu_model_get_n_items (G_MENU_MODEL (source_section));
- action = g_strconcat ("src.", source_id, NULL);
-
- for (i = 0; i < n_items; i++)
- {
- gchar *item_action;
-
- if (g_menu_model_get_item_attribute (G_MENU_MODEL (source_section), i, "action", "s", &item_action))
- {
- if (g_str_equal (action, item_action))
- g_menu_remove (source_section, i);
-
- g_free (item_action);
- }
- }
-
- g_free (action);
-}
-
-static void
-im_desktop_menu_remove_all (ImApplicationList *applist,
- gpointer user_data)
-{
- ImDesktopMenu *menu = user_data;
- GHashTableIter it;
- GMenu *section;
-
- g_hash_table_iter_init (&it, menu->source_sections);
- while (g_hash_table_iter_next (&it, NULL, (gpointer *) &section))
- {
- while (g_menu_model_get_n_items (G_MENU_MODEL (section)) > 0)
- g_menu_remove (section, 0);
- }
-}
-
-static GMenu *
-create_status_section (void)
-{
- GMenu *menu;
- GMenuItem *item;
- struct status_item {
- gchar *label;
- gchar *action;
- gchar *icon_name;
- } status_items[] = {
- { _("Available"), "indicator.status::available", "user-available" },
- { _("Away"), "indicator.status::away", "user-away" },
- { _("Busy"), "indicator.status::busy", "user-busy" },
- { _("Invisible"), "indicator.status::invisible", "user-invisible" },
- { _("Offline"), "indicator.status::offline", "user-offline" }
- };
- int i;
-
- menu = g_menu_new ();
-
- item = g_menu_item_new (NULL, NULL);
-
- for (i = 0; i < G_N_ELEMENTS (status_items); i++) {
- g_menu_item_set_label (item, status_items[i].label);
- g_menu_item_set_detailed_action (item, status_items[i].action);
- g_menu_item_set_attribute (item, "icon", "s", status_items[i].icon_name);
- g_menu_append_item (menu, item);
- }
-
- g_object_unref (item);
- return menu;
-}
-
-static void
-im_desktop_menu_constructed (GObject *object)
-{
- ImDesktopMenu *menu = IM_DESKTOP_MENU (object);
- ImApplicationList *applist;
-
- {
- GMenu *status_section;
-
- status_section = create_status_section();
- im_menu_append_section (IM_MENU (menu), G_MENU_MODEL (status_section));
-
- g_object_unref (status_section);
- }
-
- {
- GMenu *clear_section;
-
- clear_section = g_menu_new ();
- g_menu_append (clear_section, "Clear", "indicator.remove-all");
- im_menu_append_section (IM_MENU (menu), G_MENU_MODEL (clear_section));
-
- g_object_unref (clear_section);
- }
-
- applist = im_menu_get_application_list (IM_MENU (menu));
-
- {
- GList *apps;
- GList *it;
-
- apps = im_application_list_get_applications (applist);
- for (it = apps; it != NULL; it = it->next)
- {
- const gchar *id = it->data;
- im_desktop_menu_app_added (applist, id, im_application_list_get_application (applist, id), menu);
- }
-
- g_list_free (apps);
- }
-
-
- g_signal_connect (applist, "app-added", G_CALLBACK (im_desktop_menu_app_added), menu);
- g_signal_connect (applist, "source-added", G_CALLBACK (im_desktop_menu_source_added), menu);
- g_signal_connect (applist, "source-removed", G_CALLBACK (im_desktop_menu_source_removed), menu);
- g_signal_connect (applist, "remove-all", G_CALLBACK (im_desktop_menu_remove_all), menu);
-
- G_OBJECT_CLASS (im_desktop_menu_parent_class)->constructed (object);
-}
-
-static void
-im_desktop_menu_finalize (GObject *object)
-{
- ImDesktopMenu *menu = IM_DESKTOP_MENU (object);
-
- g_hash_table_unref (menu->source_sections);
-
- G_OBJECT_CLASS (im_desktop_menu_parent_class)->finalize (object);
-}
-
-static void
-im_desktop_menu_class_init (ImDesktopMenuClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->constructed = im_desktop_menu_constructed;
- object_class->finalize = im_desktop_menu_finalize;
-}
-
-static void
-im_desktop_menu_init (ImDesktopMenu *menu)
-{
- menu->source_sections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
-}
-
-ImDesktopMenu *
-im_desktop_menu_new (ImApplicationList *applist)
-{
- g_return_val_if_fail (IM_IS_APPLICATION_LIST (applist), NULL);
-
- return g_object_new (IM_TYPE_DESKTOP_MENU,
- "application-list", applist,
- NULL);
-}
diff --git a/src/im-desktop-menu.h b/src/im-desktop-menu.h
deleted file mode 100644
index 9469ea6..0000000
--- a/src/im-desktop-menu.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2013 Canonical Ltd.
- *
- * 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/>.
- *
- * Authors:
- * Lars Uebernickel <lars.uebernickel@canonical.com>
- */
-
-#ifndef __IM_DESKTOP_MENU_H__
-#define __IM_DESKTOP_MENU_H__
-
-#include "im-menu.h"
-
-#define IM_TYPE_DESKTOP_MENU (im_desktop_menu_get_type ())
-#define IM_DESKTOP_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IM_TYPE_DESKTOP_MENU, ImDesktopMenu))
-#define IM_IS_DESKTOP_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IM_TYPE_DESKTOP_MENU))
-
-typedef struct _ImDesktopMenu ImDesktopMenu;
-
-GType im_desktop_menu_get_type (void);
-
-ImDesktopMenu * im_desktop_menu_new (ImApplicationList *applist);
-
-#endif
diff --git a/src/im-menu.c b/src/im-menu.c
deleted file mode 100644
index ac23a29..0000000
--- a/src/im-menu.c
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright 2013 Canonical Ltd.
- *
- * 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/>.
- *
- * Authors:
- * Lars Uebernickel <lars.uebernickel@canonical.com>
- */
-
-#include "im-menu.h"
-
-struct _ImMenuPrivate
-{
- GMenu *toplevel_menu;
- GMenu *menu;
- ImApplicationList *applist;
-};
-
-G_DEFINE_TYPE_WITH_PRIVATE (ImMenu, im_menu, G_TYPE_OBJECT)
-
-enum
-{
- PROP_0,
- PROP_APPLICATION_LIST,
- NUM_PROPERTIES
-};
-
-static void
-im_menu_finalize (GObject *object)
-{
- ImMenuPrivate *priv = im_menu_get_instance_private (IM_MENU (object));
-
- g_object_unref (priv->toplevel_menu);
- g_object_unref (priv->menu);
- g_object_unref (priv->applist);
-
- G_OBJECT_CLASS (im_menu_parent_class)->finalize (object);
-}
-
-static void
-im_menu_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- ImMenuPrivate *priv = im_menu_get_instance_private (IM_MENU (object));
-
- switch (property_id)
- {
- case PROP_APPLICATION_LIST:
- g_value_set_object (value, priv->applist);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- }
-}
-
-static void
-im_menu_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- ImMenuPrivate *priv = im_menu_get_instance_private (IM_MENU (object));
-
- switch (property_id)
- {
- case PROP_APPLICATION_LIST: /* construct only */
- priv->applist = g_value_dup_object (value);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- }
-}
-
-static void
-im_menu_class_init (ImMenuClass *class)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (class);
-
- object_class->finalize = im_menu_finalize;
- object_class->get_property = im_menu_get_property;
- object_class->set_property = im_menu_set_property;
-
- g_object_class_install_property (object_class, PROP_APPLICATION_LIST,
- g_param_spec_object ("application-list", "", "",
- IM_TYPE_APPLICATION_LIST,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS));
-}
-
-static void
-im_menu_init (ImMenu *menu)
-{
- ImMenuPrivate *priv = im_menu_get_instance_private (menu);
- GMenuItem *root;
-
- priv->toplevel_menu = g_menu_new ();
- priv->menu = g_menu_new ();
-
- root = g_menu_item_new (NULL, "indicator.messages");
- g_menu_item_set_attribute (root, "x-canonical-type", "s", "com.canonical.indicator.root");
- g_menu_item_set_attribute (root, "action-namespace", "s", "indicator");
- g_menu_item_set_submenu (root, G_MENU_MODEL (priv->menu));
- g_menu_append_item (priv->toplevel_menu, root);
-
- g_object_unref (root);
-}
-
-ImApplicationList *
-im_menu_get_application_list (ImMenu *menu)
-{
- ImMenuPrivate *priv;
-
- g_return_val_if_fail (IM_IS_MENU (menu), FALSE);
-
- priv = im_menu_get_instance_private (menu);
- return priv->applist;
-}
-
-gboolean
-im_menu_export (ImMenu *menu,
- GDBusConnection *connection,
- const gchar *object_path,
- GError **error)
-{
- ImMenuPrivate *priv;
-
- g_return_val_if_fail (IM_IS_MENU (menu), FALSE);
-
- priv = im_menu_get_instance_private (menu);
- return g_dbus_connection_export_menu_model (connection,
- object_path,
- G_MENU_MODEL (priv->toplevel_menu),
- error) > 0;
-}
-
-void
-im_menu_append_section (ImMenu *menu,
- GMenuModel *section)
-{
- ImMenuPrivate *priv;
-
- g_return_if_fail (IM_IS_MENU (menu));
- g_return_if_fail (G_IS_MENU_MODEL (section));
-
- priv = im_menu_get_instance_private (menu);
-
- g_menu_append_section (priv->menu, NULL, section);
-}
-
-void
-im_menu_insert_section (ImMenu *menu,
- gint position,
- const gchar *namespace,
- GMenuModel *section)
-{
- ImMenuPrivate *priv;
- GMenuItem *item;
-
- g_return_if_fail (IM_IS_MENU (menu));
- g_return_if_fail (G_IS_MENU_MODEL (section));
-
- priv = im_menu_get_instance_private (menu);
-
- /* count from the back if position is < 0 */
- if (position < 0)
- position = g_menu_model_get_n_items (G_MENU_MODEL (priv->menu)) + position;
-
- item = g_menu_item_new_section (NULL, section);
-
- if (namespace)
- g_menu_item_set_attribute (item, "action-namespace", "s", namespace);
-
- g_menu_insert_item (priv->menu, position, item);
-
- g_object_unref (item);
-}
diff --git a/src/im-menu.h b/src/im-menu.h
deleted file mode 100644
index d3775ad..0000000
--- a/src/im-menu.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2013 Canonical Ltd.
- *
- * 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/>.
- *
- * Authors:
- * Lars Uebernickel <lars.uebernickel@canonical.com>
- */
-
-#ifndef __IM_MENU_H__
-#define __IM_MENU_H__
-
-#include <gio/gio.h>
-#include "im-application-list.h"
-
-#define IM_TYPE_MENU (im_menu_get_type ())
-#define IM_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IM_TYPE_MENU, ImMenu))
-#define IM_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IM_TYPE_MENU, ImMenuClass))
-#define IM_IS_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IM_TYPE_MENU))
-#define IM_IS_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IM_TYPE_MENU))
-#define IM_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IM_TYPE_MENU, ImMenuClass))
-
-typedef struct _ImMenuClass ImMenuClass;
-typedef struct _ImMenu ImMenu;
-typedef struct _ImMenuPrivate ImMenuPrivate;
-
-struct _ImMenuClass
-{
- GObjectClass parent_class;
-};
-
-struct _ImMenu
-{
- GObject parent_instance;
-};
-
-GType im_menu_get_type (void) G_GNUC_CONST;
-
-ImApplicationList * im_menu_get_application_list (ImMenu *menu);
-
-gboolean im_menu_export (ImMenu *menu,
- GDBusConnection *connection,
- const gchar *object_path,
- GError **error);
-
-void im_menu_append_section (ImMenu *menu,
- GMenuModel *section);
-
-void im_menu_insert_section (ImMenu *menu,
- gint position,
- const gchar *namespace,
- GMenuModel *section);
-
-#endif
diff --git a/src/im-phone-menu.c b/src/im-phone-menu.c
deleted file mode 100644
index 7381097..0000000
--- a/src/im-phone-menu.c
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright 2012 Canonical Ltd.
- *
- * 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/>.
- *
- * Authors:
- * Lars Uebernickel <lars.uebernickel@canonical.com>
- */
-
-#include "im-phone-menu.h"
-
-#include <string.h>
-
-typedef ImMenuClass ImPhoneMenuClass;
-
-struct _ImPhoneMenu
-{
- ImMenu parent;
-
- GMenu *message_section;
- GMenu *source_section;
-};
-
-G_DEFINE_TYPE (ImPhoneMenu, im_phone_menu, IM_TYPE_MENU);
-
-typedef void (*ImMenuForeachFunc) (GMenuModel *menu, gint pos);
-
-static void
-im_phone_menu_foreach_item_with_action (GMenuModel *menu,
- const gchar *action,
- ImMenuForeachFunc func)
-{
- gint n_items;
- gint i;
-
- n_items = g_menu_model_get_n_items (menu);
- for (i = 0; i < n_items; i++)
- {
- gchar *item_action;
-
- g_menu_model_get_item_attribute (menu, i, G_MENU_ATTRIBUTE_ACTION, "s", &item_action);
-
- if (g_str_equal (action, item_action))
- func (menu, i);
-
- g_free (item_action);
- }
-}
-
-static void
-im_phone_menu_constructed (GObject *object)
-{
- ImPhoneMenu *menu = IM_PHONE_MENU (object);
- ImApplicationList *applist;
-
- im_menu_append_section (IM_MENU (menu), G_MENU_MODEL (menu->message_section));
- im_menu_append_section (IM_MENU (menu), G_MENU_MODEL (menu->source_section));
-
- {
- GMenu *clear_section;
- GMenuItem *item;
-
- clear_section = g_menu_new ();
-
- item = g_menu_item_new ("Clear All", "remove-all");
- g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.button");
- g_menu_append_item (clear_section, item);
-
- im_menu_append_section (IM_MENU (menu), G_MENU_MODEL (clear_section));
-
- g_object_unref (item);
- g_object_unref (clear_section);
- }
-
- applist = im_menu_get_application_list (IM_MENU (menu));
-
- g_signal_connect_swapped (applist, "message-added", G_CALLBACK (im_phone_menu_add_message), menu);
- g_signal_connect_swapped (applist, "message-removed", G_CALLBACK (im_phone_menu_remove_message), menu);
- g_signal_connect_swapped (applist, "app-stopped", G_CALLBACK (im_phone_menu_remove_application), menu);
- g_signal_connect_swapped (applist, "remove-all", G_CALLBACK (im_phone_menu_remove_all), menu);
-
- G_OBJECT_CLASS (im_phone_menu_parent_class)->constructed (object);
-}
-
-static void
-im_phone_menu_dispose (GObject *object)
-{
- ImPhoneMenu *menu = IM_PHONE_MENU (object);
-
- g_clear_object (&menu->message_section);
- g_clear_object (&menu->source_section);
-
- G_OBJECT_CLASS (im_phone_menu_parent_class)->dispose (object);
-}
-
-static void
-im_phone_menu_finalize (GObject *object)
-{
- G_OBJECT_CLASS (im_phone_menu_parent_class)->finalize (object);
-}
-
-static void
-im_phone_menu_class_init (ImPhoneMenuClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->constructed = im_phone_menu_constructed;
- object_class->dispose = im_phone_menu_dispose;
- object_class->finalize = im_phone_menu_finalize;
-}
-
-static void
-im_phone_menu_init (ImPhoneMenu *menu)
-{
- menu->message_section = g_menu_new ();
- menu->source_section = g_menu_new ();
-}
-
-ImPhoneMenu *
-im_phone_menu_new (ImApplicationList *applist)
-{
- g_return_val_if_fail (IM_IS_APPLICATION_LIST (applist), NULL);
-
- return g_object_new (IM_TYPE_PHONE_MENU,
- "application-list", applist,
- NULL);
-}
-
-static gint64
-im_phone_menu_get_message_time (GMenuModel *model,
- gint i)
-{
- gint64 time;
-
- g_menu_model_get_item_attribute (model, i, "x-canonical-time", "x", &time);
-
- return time;
-}
-
-void
-im_phone_menu_add_message (ImPhoneMenu *menu,
- const gchar *app_id,
- const gchar *app_icon,
- const gchar *id,
- const gchar *iconstr,
- const gchar *title,
- const gchar *subtitle,
- const gchar *body,
- GVariant *actions,
- gint64 time)
-{
- GMenuItem *item;
- gchar *action_name;
- gint n_messages;
- gint pos;
-
- g_return_if_fail (IM_IS_PHONE_MENU (menu));
- g_return_if_fail (app_id);
-
- action_name = g_strconcat (app_id, ".msg.", id, NULL);
-
- item = g_menu_item_new (title, action_name);
-
- g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.messages.messageitem");
- g_menu_item_set_attribute (item, "x-canonical-message-id", "s", id);
- g_menu_item_set_attribute (item, "x-canonical-subtitle", "s", subtitle);
- g_menu_item_set_attribute (item, "x-canonical-text", "s", body);
- g_menu_item_set_attribute (item, "x-canonical-time", "x", time);
-
- if (iconstr)
- g_menu_item_set_attribute (item, "icon", "s", iconstr);
-
- if (app_icon)
- g_menu_item_set_attribute (item, "x-canonical-app-icon", "s", app_icon);
-
- if (actions)
- g_menu_item_set_attribute (item, "x-canonical-message-actions", "v", actions);
-
- n_messages = g_menu_model_get_n_items (G_MENU_MODEL (menu->message_section));
- pos = 0;
- while (pos < n_messages &&
- time < im_phone_menu_get_message_time (G_MENU_MODEL (menu->message_section), pos))
- pos++;
-
- g_menu_insert_item (menu->message_section, pos, item);
-
- g_free (action_name);
- g_object_unref (item);
-}
-
-void
-im_phone_menu_remove_message (ImPhoneMenu *menu,
- const gchar *app_id,
- const gchar *id)
-{
- gchar *action_name;
-
- g_return_if_fail (IM_IS_PHONE_MENU (menu));
- g_return_if_fail (app_id != NULL);
-
- action_name = g_strconcat (app_id, ".msg.", id, NULL);
- im_phone_menu_foreach_item_with_action (G_MENU_MODEL (menu->message_section),
- action_name,
- (ImMenuForeachFunc) g_menu_remove);
-
- g_free (action_name);
-}
-
-void
-im_phone_menu_add_source (ImPhoneMenu *menu,
- const gchar *app_id,
- const gchar *id,
- const gchar *label,
- const gchar *iconstr)
-{
- GMenuItem *item;
- gchar *action_name;
-
- g_return_if_fail (IM_IS_PHONE_MENU (menu));
- g_return_if_fail (app_id != NULL);
-
- action_name = g_strconcat (app_id, ".src.", id, NULL);
-
- item = g_menu_item_new (label, action_name);
- g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.messages.sourceitem");
-
- if (iconstr)
- g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr);
-
- g_menu_prepend_item (menu->source_section, item);
-
- g_free (action_name);
- g_object_unref (item);
-}
-
-void
-im_phone_menu_remove_source (ImPhoneMenu *menu,
- const gchar *app_id,
- const gchar *id)
-{
- gchar *action_name;
-
- g_return_if_fail (IM_IS_PHONE_MENU (menu));
- g_return_if_fail (app_id != NULL);
-
- action_name = g_strconcat (app_id, ".src.", id, NULL);
- im_phone_menu_foreach_item_with_action (G_MENU_MODEL (menu->source_section),
- action_name,
- (ImMenuForeachFunc) g_menu_remove);
-
- g_free (action_name);
-}
-
-static void
-im_phone_menu_remove_all_for_app (GMenu *menu,
- const gchar *app_id)
-{
- gchar *prefix;
- gint n_items;
- gint i = 0;
-
- prefix = g_strconcat (app_id, ".", NULL);
-
- n_items = g_menu_model_get_n_items (G_MENU_MODEL (menu));
- while (i < n_items)
- {
- gchar *action;
-
- g_menu_model_get_item_attribute (G_MENU_MODEL (menu), i, G_MENU_ATTRIBUTE_ACTION, "s", &action);
- if (g_str_has_prefix (action, prefix))
- {
- g_menu_remove (menu, i);
- n_items--;
- }
- else
- {
- i++;
- }
-
- g_free (action);
- }
-
- g_free (prefix);
-}
-
-void
-im_phone_menu_remove_application (ImPhoneMenu *menu,
- const gchar *app_id)
-{
- g_return_if_fail (IM_IS_PHONE_MENU (menu));
- g_return_if_fail (app_id != NULL);
-
- im_phone_menu_remove_all_for_app (menu->source_section, app_id);
- im_phone_menu_remove_all_for_app (menu->message_section, app_id);
-}
-
-void
-im_phone_menu_remove_all (ImPhoneMenu *menu)
-{
- g_return_if_fail (IM_IS_PHONE_MENU (menu));
-
- while (g_menu_model_get_n_items (G_MENU_MODEL (menu->message_section)))
- g_menu_remove (menu->message_section, 0);
-
- while (g_menu_model_get_n_items (G_MENU_MODEL (menu->source_section)))
- g_menu_remove (menu->source_section, 0);
-}
diff --git a/src/im-phone-menu.h b/src/im-phone-menu.h
deleted file mode 100644
index 9742f61..0000000
--- a/src/im-phone-menu.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2012 Canonical Ltd.
- *
- * 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/>.
- *
- * Authors:
- * Lars Uebernickel <lars.uebernickel@canonical.com>
- */
-
-#ifndef __IM_PHONE_MENU_H__
-#define __IM_PHONE_MENU_H__
-
-#include "im-menu.h"
-
-#define IM_TYPE_PHONE_MENU (im_phone_menu_get_type ())
-#define IM_PHONE_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IM_TYPE_PHONE_MENU, ImPhoneMenu))
-#define IM_PHONE_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IM_TYPE_PHONE_MENU, ImPhoneMenuClass))
-#define IM_IS_PHONE_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IM_TYPE_PHONE_MENU))
-#define IM_IS_PHONE_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IM_TYPE_PHONE_MENU))
-#define IM_PHONE_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IM_TYPE_PHONE_MENU, ImPhoneMenuClass))
-
-typedef struct _ImPhoneMenu ImPhoneMenu;
-
-GType im_phone_menu_get_type (void);
-
-ImPhoneMenu * im_phone_menu_new (ImApplicationList *applist);
-
-void im_phone_menu_add_message (ImPhoneMenu *menu,
- const gchar *app_id,
- const gchar *app_icon,
- const gchar *id,
- const gchar *iconstr,
- const gchar *title,
- const gchar *subtitle,
- const gchar *body,
- GVariant *actions,
- gint64 time);
-
-void im_phone_menu_remove_message (ImPhoneMenu *menu,
- const gchar *app_id,
- const gchar *id);
-
-void im_phone_menu_add_source (ImPhoneMenu *menu,
- const gchar *app_id,
- const gchar *id,
- const gchar *label,
- const gchar *iconstr);
-
-void im_phone_menu_remove_source (ImPhoneMenu *menu,
- const gchar *app_id,
- const gchar *id);
-
-void im_phone_menu_remove_application (ImPhoneMenu *menu,
- const gchar *app_id);
-
-void im_phone_menu_remove_all (ImPhoneMenu *menu);
-
-#endif
diff --git a/src/im-source-menu-item.c b/src/im-source-menu-item.c
new file mode 100644
index 0000000..2577c30
--- /dev/null
+++ b/src/im-source-menu-item.c
@@ -0,0 +1,407 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * 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/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#include "im-source-menu-item.h"
+
+#include <libintl.h>
+#include "ido-detail-label.h"
+
+struct _ImSourceMenuItemPrivate
+{
+ GActionGroup *action_group;
+ gchar *action;
+
+ GtkWidget *icon;
+ GtkWidget *label;
+ GtkWidget *detail;
+
+ gint64 time;
+ guint timer_id;
+};
+
+enum
+{
+ PROP_0,
+ PROP_MENU_ITEM,
+ PROP_ACTION_GROUP,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES];
+
+G_DEFINE_TYPE (ImSourceMenuItem, im_source_menu_item, GTK_TYPE_MENU_ITEM);
+
+static void
+im_source_menu_item_constructed (GObject *object)
+{
+ ImSourceMenuItemPrivate *priv = IM_SOURCE_MENU_ITEM (object)->priv;
+ GtkWidget *grid;
+ gint icon_width;
+
+ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, NULL);
+
+ priv->icon = g_object_ref (gtk_image_new ());
+ gtk_widget_set_margin_left (priv->icon, icon_width + 6);
+
+ priv->label = g_object_ref (gtk_label_new (""));
+ gtk_label_set_max_width_chars (GTK_LABEL (priv->label), 40);
+ gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END);
+ gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5);
+
+ priv->detail = g_object_ref (ido_detail_label_new (""));
+ gtk_widget_set_halign (priv->detail, GTK_ALIGN_END);
+ gtk_widget_set_hexpand (priv->detail, TRUE);
+ gtk_style_context_add_class (gtk_widget_get_style_context (priv->detail), "accelerator");
+
+ grid = gtk_grid_new ();
+ gtk_grid_attach (GTK_GRID (grid), priv->icon, 0, 0, 1, 1);
+ gtk_grid_attach (GTK_GRID (grid), priv->label, 1, 0, 1, 1);
+ gtk_grid_attach (GTK_GRID (grid), priv->detail, 2, 0, 1, 1);
+
+ gtk_container_add (GTK_CONTAINER (object), grid);
+ gtk_widget_show_all (grid);
+
+ G_OBJECT_CLASS (im_source_menu_item_parent_class)->constructed (object);
+}
+
+static gchar *
+im_source_menu_item_time_span_string (gint64 timestamp)
+{
+ gchar *str;
+ gint64 span;
+ gint hours;
+ gint minutes;
+
+ span = MAX (g_get_real_time () - timestamp, 0) / G_USEC_PER_SEC;
+ hours = span / 3600;
+ minutes = (span / 60) % 60;
+
+ if (hours == 0)
+ {
+ /* TRANSLATORS: number of minutes that have passed */
+ str = g_strdup_printf (ngettext ("%d min", "%d min", minutes), minutes);
+ }
+ else
+ {
+ /* TRANSLATORS: number of hours that have passed */
+ str = g_strdup_printf (ngettext ("%d h", "%d h", hours), hours);
+ }
+
+ return str;
+}
+
+static void
+im_source_menu_item_set_detail_time (ImSourceMenuItem *self,
+ gint64 time)
+{
+ ImSourceMenuItemPrivate *priv = self->priv;
+ gchar *str;
+
+ priv->time = time;
+
+ str = im_source_menu_item_time_span_string (priv->time);
+ ido_detail_label_set_text (IDO_DETAIL_LABEL (priv->detail), str);
+
+ g_free (str);
+}
+
+static gboolean
+im_source_menu_item_update_time (gpointer data)
+{
+ ImSourceMenuItem *self = data;
+
+ im_source_menu_item_set_detail_time (self, self->priv->time);
+
+ return TRUE;
+}
+
+static gboolean
+im_source_menu_item_set_state (ImSourceMenuItem *self,
+ GVariant *state)
+{
+ ImSourceMenuItemPrivate *priv = self->priv;
+ guint32 count;
+ gint64 time;
+ const gchar *str;
+
+ if (priv->timer_id != 0)
+ {
+ g_source_remove (priv->timer_id);
+ priv->timer_id = 0;
+ }
+
+ g_return_val_if_fail (g_variant_is_of_type (state, G_VARIANT_TYPE ("(uxsb)")), FALSE);
+
+ g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL);
+
+ if (count != 0)
+ ido_detail_label_set_count (IDO_DETAIL_LABEL (priv->detail), count);
+ else if (time != 0)
+ {
+ im_source_menu_item_set_detail_time (self, time);
+ priv->timer_id = g_timeout_add_seconds (59, im_source_menu_item_update_time, self);
+ }
+ else if (str != NULL && *str)
+ ido_detail_label_set_text (IDO_DETAIL_LABEL (priv->detail), str);
+
+ return TRUE;
+}
+
+static void
+im_source_menu_item_set_action_name (ImSourceMenuItem *self,
+ const gchar *action_name)
+{
+ ImSourceMenuItemPrivate *priv = self->priv;
+ gboolean enabled = FALSE;
+ GVariant *state;
+
+ if (priv->action != NULL)
+ g_free (priv->action);
+
+ priv->action = g_strdup (action_name);
+
+ if (priv->action_group != NULL && priv->action != NULL &&
+ g_action_group_query_action (priv->action_group, priv->action,
+ &enabled, NULL, NULL, NULL, &state))
+ {
+ if (!state || !im_source_menu_item_set_state (self, state))
+ enabled = FALSE;
+
+ if (state)
+ g_variant_unref (state);
+ }
+
+ gtk_widget_set_sensitive (GTK_WIDGET (self), enabled);
+}
+
+static void
+im_source_menu_item_action_added (GActionGroup *action_group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ ImSourceMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ im_source_menu_item_set_action_name (self, action_name);
+}
+
+static void
+im_source_menu_item_action_removed (GActionGroup *action_group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ ImSourceMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ {
+ gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
+ }
+}
+
+static void
+im_source_menu_item_action_enabled_changed (GActionGroup *action_group,
+ gchar *action_name,
+ gboolean enabled,
+ gpointer user_data)
+{
+ ImSourceMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ gtk_widget_set_sensitive (GTK_WIDGET (self), enabled);
+}
+
+static void
+im_source_menu_item_action_state_changed (GActionGroup *action_group,
+ gchar *action_name,
+ GVariant *value,
+ gpointer user_data)
+{
+ ImSourceMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ im_source_menu_item_set_state (self, value);
+}
+
+static void
+im_source_menu_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ImSourceMenuItem *self = IM_SOURCE_MENU_ITEM (object);
+
+ switch (property_id)
+ {
+ case PROP_MENU_ITEM:
+ im_source_menu_item_set_menu_item (self, G_MENU_ITEM (g_value_get_object (value)));
+ break;
+
+ case PROP_ACTION_GROUP:
+ im_source_menu_item_set_action_group (self, G_ACTION_GROUP (g_value_get_object (value)));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+im_source_menu_item_dispose (GObject *object)
+{
+ ImSourceMenuItem *self = IM_SOURCE_MENU_ITEM (object);
+
+ if (self->priv->timer_id != 0)
+ {
+ g_source_remove (self->priv->timer_id);
+ self->priv->timer_id = 0;
+ }
+
+ if (self->priv->action_group)
+ im_source_menu_item_set_action_group (self, NULL);
+
+ g_clear_object (&self->priv->icon);
+ g_clear_object (&self->priv->label);
+ g_clear_object (&self->priv->detail);
+
+ G_OBJECT_CLASS (im_source_menu_item_parent_class)->dispose (object);
+}
+
+static void
+im_source_menu_item_finalize (GObject *object)
+{
+ ImSourceMenuItemPrivate *priv = IM_SOURCE_MENU_ITEM (object)->priv;
+
+ g_free (priv->action);
+
+ G_OBJECT_CLASS (im_source_menu_item_parent_class)->finalize (object);
+}
+
+static void
+im_source_menu_item_activate (GtkMenuItem *item)
+{
+ ImSourceMenuItemPrivate *priv = IM_SOURCE_MENU_ITEM (item)->priv;
+
+ if (priv->action && priv->action_group)
+ g_action_group_activate_action (priv->action_group, priv->action, NULL);
+}
+
+static void
+im_source_menu_item_class_init (ImSourceMenuItemClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkMenuItemClass *menu_item_class = GTK_MENU_ITEM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (ImSourceMenuItemPrivate));
+
+ object_class->constructed = im_source_menu_item_constructed;
+ object_class->set_property = im_source_menu_set_property;
+ object_class->dispose = im_source_menu_item_dispose;
+ object_class->finalize = im_source_menu_item_finalize;
+
+ menu_item_class->activate = im_source_menu_item_activate;
+
+ properties[PROP_MENU_ITEM] = g_param_spec_object ("menu-item",
+ "Menu item",
+ "The model GMenuItem for this menu item",
+ G_TYPE_MENU_ITEM,
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_ACTION_GROUP] = g_param_spec_object ("action-group",
+ "Action group",
+ "The action group associated with this menu item",
+ G_TYPE_ACTION_GROUP,
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+}
+
+static void
+im_source_menu_item_init (ImSourceMenuItem *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ IM_TYPE_SOURCE_MENU_ITEM,
+ ImSourceMenuItemPrivate);
+}
+
+void
+im_source_menu_item_set_menu_item (ImSourceMenuItem *self,
+ GMenuItem *menuitem)
+{
+ gchar *iconstr = NULL;
+ GIcon *icon = NULL;
+ gchar *label;
+ gchar *action = NULL;
+
+ if (g_menu_item_get_attribute (menuitem, "x-canonical-icon", "s", &iconstr))
+ {
+ GError *error;
+ icon = g_icon_new_for_string (iconstr, &error);
+ if (icon == NULL)
+ {
+ g_warning ("unable to set icon: %s", error->message);
+ g_error_free (error);
+ }
+ g_free (iconstr);
+ }
+ gtk_image_set_from_gicon (GTK_IMAGE (self->priv->icon), icon, GTK_ICON_SIZE_MENU);
+
+ g_menu_item_get_attribute (menuitem, "label", "s", &label);
+ gtk_label_set_label (GTK_LABEL (self->priv->label), label ? label : "");
+
+ g_menu_item_get_attribute (menuitem, "action", "s", &action);
+ im_source_menu_item_set_action_name (self, action);
+
+ if (icon)
+ g_object_unref (icon);
+ g_free (label);
+ g_free (action);
+}
+
+void
+im_source_menu_item_set_action_group (ImSourceMenuItem *self,
+ GActionGroup *action_group)
+{
+ ImSourceMenuItemPrivate *priv = self->priv;
+
+ if (priv->action_group != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_source_menu_item_action_added, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_source_menu_item_action_removed, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_source_menu_item_action_enabled_changed, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_source_menu_item_action_state_changed, self);
+
+ g_clear_object (&priv->action_group);
+ }
+
+ if (action_group != NULL)
+ {
+ priv->action_group = g_object_ref (action_group);
+
+ g_signal_connect (priv->action_group, "action-added",
+ G_CALLBACK (im_source_menu_item_action_added), self);
+ g_signal_connect (priv->action_group, "action-removed",
+ G_CALLBACK (im_source_menu_item_action_removed), self);
+ g_signal_connect (priv->action_group, "action-enabled-changed",
+ G_CALLBACK (im_source_menu_item_action_enabled_changed), self);
+ g_signal_connect (priv->action_group, "action-state-changed",
+ G_CALLBACK (im_source_menu_item_action_state_changed), self);
+ }
+}
diff --git a/src/im-source-menu-item.h b/src/im-source-menu-item.h
new file mode 100644
index 0000000..c359b94
--- /dev/null
+++ b/src/im-source-menu-item.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * 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/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#ifndef __IM_SOURCE_MENU_ITEM_H__
+#define __IM_SOURCE_MENU_ITEM_H__
+
+#include <gtk/gtk.h>
+
+#define IM_TYPE_SOURCE_MENU_ITEM (im_source_menu_item_get_type ())
+#define IM_SOURCE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IM_TYPE_SOURCE_MENU_ITEM, ImSourceMenuItem))
+#define IM_SOURCE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IM_TYPE_SOURCE_MENU_ITEM, ImSourceMenuItemClass))
+#define IS_IM_SOURCE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IM_TYPE_SOURCE_MENU_ITEM))
+#define IS_IM_SOURCE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IM_TYPE_SOURCE_MENU_ITEM))
+#define IM_SOURCE_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IM_TYPE_SOURCE_MENU_ITEM, ImSourceMenuItemClass))
+
+typedef struct _ImSourceMenuItem ImSourceMenuItem;
+typedef struct _ImSourceMenuItemClass ImSourceMenuItemClass;
+typedef struct _ImSourceMenuItemPrivate ImSourceMenuItemPrivate;
+
+struct _ImSourceMenuItemClass
+{
+ GtkMenuItemClass parent_class;
+};
+
+struct _ImSourceMenuItem
+{
+ GtkMenuItem parent;
+ ImSourceMenuItemPrivate *priv;
+};
+
+GType im_source_menu_item_get_type (void);
+
+void im_source_menu_item_set_menu_item (ImSourceMenuItem *item,
+ GMenuItem *menuitem);
+void im_source_menu_item_set_action_group (ImSourceMenuItem *self,
+ GActionGroup *action_group);
+
+#endif
diff --git a/src/indicator-messages.c b/src/indicator-messages.c
new file mode 100644
index 0000000..5c5df31
--- /dev/null
+++ b/src/indicator-messages.c
@@ -0,0 +1,382 @@
+/*
+An indicator to show information that is in messaging applications
+that the user is using.
+
+Copyright 2012 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+ Lars Uebernickel <lars.uebernickel@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 "config.h"
+
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <libindicator/indicator.h>
+#include <libindicator/indicator-object.h>
+#include <libindicator/indicator-service-manager.h>
+
+#include "dbus-data.h"
+
+#include "ido-menu-item.h"
+#include "im-app-menu-item.h"
+#include "im-source-menu-item.h"
+
+#define INDICATOR_MESSAGES_TYPE (indicator_messages_get_type ())
+#define INDICATOR_MESSAGES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_MESSAGES_TYPE, IndicatorMessages))
+#define INDICATOR_MESSAGES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_MESSAGES_TYPE, IndicatorMessagesClass))
+#define IS_INDICATOR_MESSAGES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_MESSAGES_TYPE))
+#define IS_INDICATOR_MESSAGES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_MESSAGES_TYPE))
+#define INDICATOR_MESSAGES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_MESSAGES_TYPE, IndicatorMessagesClass))
+
+typedef struct _IndicatorMessages IndicatorMessages;
+typedef struct _IndicatorMessagesClass IndicatorMessagesClass;
+
+struct _IndicatorMessagesClass {
+ IndicatorObjectClass parent_class;
+};
+
+struct _IndicatorMessages {
+ IndicatorObject parent;
+ IndicatorServiceManager * service;
+ GActionGroup *actions;
+ GMenuModel *menu;
+ GtkWidget *image;
+ GtkWidget *gtkmenu;
+ gchar *accessible_desc;
+};
+
+GType indicator_messages_get_type (void);
+
+/* Indicator Module Config */
+INDICATOR_SET_VERSION
+INDICATOR_SET_TYPE(INDICATOR_MESSAGES_TYPE)
+
+/* Prototypes */
+static void indicator_messages_class_init (IndicatorMessagesClass *klass);
+static void indicator_messages_init (IndicatorMessages *self);
+static void indicator_messages_dispose (GObject *object);
+static void indicator_messages_finalize (GObject *object);
+static void service_connection_changed (IndicatorServiceManager *sm,
+ gboolean connected,
+ gpointer user_data);
+static GtkImage * get_image (IndicatorObject * io);
+static GtkMenu * get_menu (IndicatorObject * io);
+static const gchar * get_accessible_desc (IndicatorObject * io);
+static const gchar * get_name_hint (IndicatorObject * io);
+static void menu_items_changed (GMenuModel *menu,
+ gint position,
+ gint removed,
+ gint added,
+ gpointer user_data);
+static void messages_action_added (GActionGroup *action_group,
+ gchar *action_name,
+ gpointer user_data);
+static void messages_state_changed (GActionGroup *action_group,
+ gchar *action_name,
+ GVariant *value,
+ gpointer user_data);
+static void indicator_messages_add_toplevel_menu (IndicatorMessages *self);
+
+G_DEFINE_TYPE (IndicatorMessages, indicator_messages, INDICATOR_OBJECT_TYPE);
+
+/* Initialize the one-timers */
+static void
+indicator_messages_class_init (IndicatorMessagesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = indicator_messages_dispose;
+ object_class->finalize = indicator_messages_finalize;
+
+ IndicatorObjectClass * io_class = INDICATOR_OBJECT_CLASS(klass);
+
+ io_class->get_image = get_image;
+ io_class->get_menu = get_menu;
+ io_class->get_accessible_desc = get_accessible_desc;
+ io_class->get_name_hint = get_name_hint;
+}
+
+/* Build up our per-instance variables */
+static void
+indicator_messages_init (IndicatorMessages *self)
+{
+ self->service = indicator_service_manager_new_version(INDICATOR_MESSAGES_DBUS_NAME, 1);
+ g_signal_connect (self->service, "connection-change",
+ G_CALLBACK (service_connection_changed), self);
+
+ self->gtkmenu = gtk_menu_new ();
+ g_object_ref_sink (self->gtkmenu);
+
+ self->image = g_object_ref_sink (gtk_image_new ());
+
+ /* make sure custom menu item types are registered (so that
+ * gtk_model_new_from_menu can pick them up */
+ ido_menu_item_get_type ();
+ im_app_menu_item_get_type ();
+ im_source_menu_item_get_type ();
+}
+
+/* Unref stuff */
+static void
+indicator_messages_dispose (GObject *object)
+{
+ IndicatorMessages * self = INDICATOR_MESSAGES(object);
+ g_return_if_fail(self != NULL);
+
+ g_clear_object (&self->service);
+ g_clear_object (&self->actions);
+ g_clear_object (&self->menu);
+ g_clear_object (&self->gtkmenu);
+ g_clear_object (&self->image);
+
+ G_OBJECT_CLASS (indicator_messages_parent_class)->dispose (object);
+ return;
+}
+
+/* Destory all memory users */
+static void
+indicator_messages_finalize (GObject *object)
+{
+ IndicatorMessages *self = INDICATOR_MESSAGES (object);
+
+ g_free (self->accessible_desc);
+
+ G_OBJECT_CLASS (indicator_messages_parent_class)->finalize (object);
+ return;
+}
+
+
+
+/* Functions */
+
+static void service_connection_changed (IndicatorServiceManager *sm,
+ gboolean connected,
+ gpointer user_data)
+{
+ IndicatorMessages *self = user_data;
+ GDBusConnection *bus;
+ GError *error = NULL;
+
+ if (self->actions != NULL) {
+ g_signal_handlers_disconnect_by_func (self->actions, messages_action_added, self);
+ g_signal_handlers_disconnect_by_func (self->actions, messages_state_changed, self);
+ g_clear_object (&self->actions);
+ }
+ if (self->menu != NULL) {
+ g_signal_handlers_disconnect_by_func (self->menu, menu_items_changed, self);
+ g_clear_object (&self->menu);
+ }
+ gtk_menu_shell_bind_model (GTK_MENU_SHELL (self->gtkmenu), NULL, NULL, FALSE);
+
+ if (connected == FALSE)
+ return;
+
+ bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ if (!bus) {
+ g_warning ("error connecting to the session bus: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ self->actions = G_ACTION_GROUP (g_dbus_action_group_get (bus,
+ INDICATOR_MESSAGES_DBUS_NAME,
+ INDICATOR_MESSAGES_DBUS_OBJECT));
+ gtk_widget_insert_action_group (self->gtkmenu,
+ get_name_hint (INDICATOR_OBJECT (self)),
+ self->actions);
+ g_signal_connect (self->actions, "action-added::messages",
+ G_CALLBACK (messages_action_added), self);
+ g_signal_connect (self->actions, "action-state-changed::messages",
+ G_CALLBACK (messages_state_changed), self);
+
+ self->menu = G_MENU_MODEL (g_dbus_menu_model_get (bus,
+ INDICATOR_MESSAGES_DBUS_NAME,
+ INDICATOR_MESSAGES_DBUS_OBJECT));
+ g_signal_connect (self->menu, "items-changed", G_CALLBACK (menu_items_changed), self);
+
+ if (g_menu_model_get_n_items (self->menu) == 1)
+ indicator_messages_add_toplevel_menu (self);
+ else
+ indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE);
+
+ g_object_unref (bus);
+}
+
+static GtkImage *
+get_image (IndicatorObject * io)
+{
+ IndicatorMessages *self = INDICATOR_MESSAGES (io);
+
+ gtk_widget_show (self->image);
+ return GTK_IMAGE (self->image);
+}
+
+static GtkMenu *
+get_menu (IndicatorObject * io)
+{
+ IndicatorMessages *self = INDICATOR_MESSAGES (io);
+
+ return GTK_MENU (self->gtkmenu);
+}
+
+static const gchar *
+get_accessible_desc (IndicatorObject * io)
+{
+ IndicatorMessages *self = INDICATOR_MESSAGES (io);
+ return self->accessible_desc;
+}
+
+static const gchar *
+get_name_hint (IndicatorObject *io)
+{
+ return PACKAGE;
+}
+
+static void
+indicator_messages_accessible_desc_updated (IndicatorMessages *self)
+{
+ GList *entries;
+
+ entries = indicator_object_get_entries (INDICATOR_OBJECT (self));
+ g_return_if_fail (entries != NULL);
+
+ g_signal_emit_by_name (self, INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, entries->data);
+
+ g_list_free (entries);
+}
+
+static GIcon *
+g_menu_model_get_item_attribute_icon (GMenuModel *menu,
+ gint index,
+ const gchar *attribute)
+{
+ gchar *iconstr;
+ GIcon *icon = NULL;
+
+ if (g_menu_model_get_item_attribute (menu, index, attribute, "s", &iconstr)) {
+ GError *error;
+
+ icon = g_icon_new_for_string (iconstr, &error);
+ if (icon == NULL) {
+ g_warning ("unable to load icon: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_free (iconstr);
+ }
+
+ return icon;
+}
+
+static void
+indicator_messages_add_toplevel_menu (IndicatorMessages *self)
+{
+ GIcon *icon;
+ GMenuModel *popup;
+
+ indicator_object_set_visible (INDICATOR_OBJECT (self), TRUE);
+
+ icon = g_menu_model_get_item_attribute_icon (self->menu, 0, "x-canonical-icon");
+ if (icon) {
+ gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon, GTK_ICON_SIZE_LARGE_TOOLBAR);
+ g_object_unref (icon);
+ }
+
+ g_free (self->accessible_desc);
+ self->accessible_desc = NULL;
+ if (g_menu_model_get_item_attribute (self->menu, 0, "x-canonical-accessible-description",
+ "s", &self->accessible_desc)) {
+ indicator_messages_accessible_desc_updated (self);
+ }
+
+ popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU);
+ if (popup) {
+ gtk_menu_shell_bind_model (GTK_MENU_SHELL (self->gtkmenu),
+ popup,
+ get_name_hint (INDICATOR_OBJECT (self)),
+ TRUE);
+
+ g_object_unref (popup);
+ }
+}
+
+static void
+menu_items_changed (GMenuModel *menu,
+ gint position,
+ gint removed,
+ gint added,
+ gpointer user_data)
+{
+ IndicatorMessages *self = user_data;
+
+ g_return_if_fail (position == 0);
+
+ if (added == 1)
+ indicator_messages_add_toplevel_menu (self);
+ else if (removed == 1)
+ indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE);
+}
+
+static void
+indicator_messages_update_icon (IndicatorMessages *self,
+ GVariant *state)
+{
+ GIcon *icon;
+ GError *error = NULL;
+
+ g_return_if_fail (g_variant_is_of_type (state, G_VARIANT_TYPE_STRING));
+
+ icon = g_icon_new_for_string (g_variant_get_string (state, NULL), &error);
+ if (icon == NULL) {
+ g_warning ("unable to load icon: %s", error->message);
+ g_error_free (error);
+ }
+ else {
+ gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon, GTK_ICON_SIZE_LARGE_TOOLBAR);
+ g_object_unref (icon);
+ }
+}
+
+static void
+messages_action_added (GActionGroup *action_group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ IndicatorMessages *self = user_data;
+ GVariant *state;
+
+ state = g_action_group_get_action_state (action_group, "messages");
+ indicator_messages_update_icon (self, state);
+
+ g_variant_unref (state);
+}
+
+static void
+messages_state_changed (GActionGroup *action_group,
+ gchar *action_name,
+ GVariant *value,
+ gpointer user_data)
+{
+ IndicatorMessages *self = user_data;
+
+ indicator_messages_update_icon (self, value);
+}
diff --git a/src/messages-service.c b/src/messages-service.c
index 71fa09b..25a19b9 100644
--- a/src/messages-service.c
+++ b/src/messages-service.c
@@ -23,25 +23,419 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <config.h>
#include <locale.h>
+#include <libindicator/indicator-service.h>
+#include <gdk/gdk.h>
#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
#include <glib/gi18n.h>
+#include "app-section.h"
#include "dbus-data.h"
+#include "gactionmuxer.h"
#include "gsettingsstrv.h"
#include "gmenuutils.h"
#include "indicator-messages-service.h"
-#include "indicator-messages-application.h"
-#include "im-phone-menu.h"
-#include "im-desktop-menu.h"
-#include "im-application-list.h"
#define NUM_STATUSES 5
-static ImApplicationList *applications;
+static GHashTable *applications;
static IndicatorMessagesService *messages_service;
-static GHashTable *menus;
+static GSimpleActionGroup *actions;
+static GActionMuxer *action_muxer;
+static GMenu *toplevel_menu;
+static GMenu *menu;
+static GMenuModel *chat_section;
static GSettings *settings;
+static gboolean draws_attention;
+static const gchar *global_status[6]; /* max 5: available, away, busy, invisible, offline */
+
+static gchar *
+indicator_messages_get_icon_name ()
+{
+ GString *name;
+ GIcon *icon;
+ gchar *iconstr;
+
+ name = g_string_new ("indicator-messages");
+
+ if (global_status[0] != NULL)
+ {
+ if (global_status[1] != NULL)
+ g_string_append (name, "-mixed");
+ else
+ g_string_append_printf (name, "-%s", global_status[0]);
+ }
+
+ if (draws_attention)
+ g_string_append (name, "-new");
+
+ icon = g_themed_icon_new (name->str);
+ g_themed_icon_append_name (G_THEMED_ICON (icon),
+ draws_attention ? "indicator-messages-new"
+ : "indicator-messages");
+
+ iconstr = g_icon_to_string (icon);
+
+ g_object_unref (icon);
+ g_string_free (name, TRUE);
+
+ return iconstr;
+}
+
+static void
+indicator_messages_update_icon ()
+{
+ GSimpleAction *messages;
+ gchar *icon;
+
+ messages = G_SIMPLE_ACTION (g_simple_action_group_lookup (actions, "messages"));
+ g_return_if_fail (messages != NULL);
+
+ icon = indicator_messages_get_icon_name ();
+ g_simple_action_set_state (messages, g_variant_new_string (icon));
+
+ g_free (icon);
+}
+
+static gchar *
+g_app_info_get_simple_id (GAppInfo *appinfo)
+{
+ const gchar *id;
+
+ id = g_app_info_get_id (appinfo);
+ if (!id)
+ return NULL;
+
+ if (g_str_has_suffix (id, ".desktop"))
+ return g_strndup (id, strlen (id) - 8);
+ else
+ return g_strdup (id);
+}
+
+static void
+actions_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ AppSection *section = APP_SECTION (object);
+ gchar *id;
+ GActionGroup *actions;
+
+ id = g_app_info_get_simple_id (app_section_get_app_info (section));
+ actions = app_section_get_actions (section);
+
+ g_action_muxer_insert (action_muxer, id, actions);
+ g_free (id);
+}
+
+
+static gboolean
+app_section_draws_attention (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ AppSection *section = value;
+ return app_section_get_draws_attention (section);
+}
+
+static void
+draws_attention_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GSimpleAction *clear;
+
+ clear = G_SIMPLE_ACTION (g_simple_action_group_lookup (actions, "clear"));
+ g_return_if_fail (clear != NULL);
+
+ draws_attention = g_hash_table_find (applications, app_section_draws_attention, NULL) != NULL;
+
+ g_simple_action_set_enabled (clear, draws_attention);
+
+ indicator_messages_update_icon ();
+}
+
+static gboolean
+app_section_uses_chat (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ AppSection *section = value;
+ return app_section_get_uses_chat_status (section);
+}
+
+static void
+update_chat_section ()
+{
+ gboolean show_chat;
+ GMenuModel *first_section;
+
+ show_chat = g_hash_table_find (applications, app_section_uses_chat, NULL) != NULL;
+
+ first_section = g_menu_model_get_item_link (G_MENU_MODEL (menu), 0, G_MENU_LINK_SECTION);
+ if (first_section == chat_section) {
+ if (!show_chat)
+ g_menu_remove (menu, 0);
+ }
+ else {
+ if (show_chat)
+ g_menu_insert_section (menu, 0, NULL, chat_section);
+ }
+
+ if (first_section != NULL)
+ g_object_unref (first_section);
+
+ indicator_messages_update_icon ();
+}
+
+static void
+uses_chat_status_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ update_chat_section ();
+}
+
+static gboolean
+strv_contains (const gchar **strv,
+ const gchar *needle)
+{
+ const gchar **it;
+
+ it = strv;
+ while (*it != NULL && !g_str_equal (*it, needle))
+ it++;
+
+ return *it != NULL;
+}
+
+static void
+update_chat_status ()
+{
+ GHashTableIter iter;
+ AppSection *section;
+ int pos;
+ GAction *status;
+
+ for (pos = 0; pos < G_N_ELEMENTS (global_status); pos++)
+ global_status[pos] = NULL;
+
+ pos = 0;
+ g_hash_table_iter_init (&iter, applications);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &section) &&
+ pos < G_N_ELEMENTS (global_status))
+ {
+ const gchar *status_str = NULL;
+
+ status_str = app_section_get_status (section);
+ if (status_str != NULL && !strv_contains (global_status, status_str))
+ global_status[pos++] = status_str;
+ }
+
+ if (pos == 0)
+ global_status[0] = "offline";
+
+ status = g_simple_action_group_lookup (actions, "status");
+ g_return_if_fail (status != NULL);
+
+ g_simple_action_set_state (G_SIMPLE_ACTION (status), g_variant_new_strv (global_status, -1));
+
+ indicator_messages_update_icon ();
+}
+
+static void
+chat_status_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ update_chat_status ();
+}
+
+static void
+remove_section (AppSection *section,
+ const gchar *id)
+{
+ int pos = g_menu_find_section (menu, app_section_get_menu (section));
+ if (pos >= 0)
+ g_menu_remove (menu, pos);
+ g_action_muxer_remove (action_muxer, id);
+
+ g_signal_handlers_disconnect_by_func (section, actions_changed, NULL);
+ g_signal_handlers_disconnect_by_func (section, draws_attention_changed, NULL);
+ g_signal_handlers_disconnect_by_func (section, uses_chat_status_changed, NULL);
+ g_signal_handlers_disconnect_by_func (section, chat_status_changed, NULL);
+ g_signal_handlers_disconnect_by_func (section, remove_section, NULL);
+
+ g_hash_table_remove (applications, id);
+
+ if (g_hash_table_size (applications) == 0 &&
+ g_menu_model_get_n_items (G_MENU_MODEL (toplevel_menu)) == 1) {
+ g_menu_remove (toplevel_menu, 0);
+ }
+
+ update_chat_status ();
+ update_chat_section ();
+}
+
+static AppSection *
+add_application (const gchar *desktop_id)
+{
+ GDesktopAppInfo *appinfo;
+ gchar *id;
+ AppSection *section;
+
+ appinfo = g_desktop_app_info_new (desktop_id);
+ if (!appinfo) {
+ g_warning ("could not add '%s', there's no desktop file with that id", desktop_id);
+ return NULL;
+ }
+
+ id = g_app_info_get_simple_id (G_APP_INFO (appinfo));
+ section = g_hash_table_lookup (applications, id);
+
+ if (!section) {
+ GMenuItem *menuitem;
+
+ section = app_section_new(appinfo);
+ g_hash_table_insert (applications, g_strdup (id), section);
+
+ g_action_muxer_insert (action_muxer, id, app_section_get_actions (section));
+ g_signal_connect (section, "notify::actions",
+ G_CALLBACK (actions_changed), NULL);
+ g_signal_connect (section, "notify::draws-attention",
+ G_CALLBACK (draws_attention_changed), NULL);
+ g_signal_connect (section, "notify::uses-chat-status",
+ G_CALLBACK (uses_chat_status_changed), NULL);
+ g_signal_connect (section, "notify::chat-status",
+ G_CALLBACK (chat_status_changed), NULL);
+ g_signal_connect_data (section, "destroy",
+ G_CALLBACK (remove_section),
+ g_strdup (id),
+ (GClosureNotify) g_free,
+ 0);
+
+ /* TODO insert it at the right position (alphabetically by application name) */
+ menuitem = g_menu_item_new_section (NULL, app_section_get_menu (section));
+ g_menu_item_set_attribute (menuitem, "action-namespace", "s", id);
+ g_menu_insert_item (menu, g_menu_model_get_n_items (G_MENU_MODEL (menu)) -1, menuitem);
+ g_object_unref (menuitem);
+ }
+
+ if (g_menu_model_get_n_items (G_MENU_MODEL (toplevel_menu)) == 0) {
+ GMenuItem *header;
+
+ header = g_menu_item_new (NULL, "messages");
+ g_menu_item_set_submenu (header, G_MENU_MODEL (menu));
+ g_menu_item_set_attribute (header, "x-canonical-accessible-description", "s", _("Messages"));
+ g_menu_append_item (toplevel_menu, header);
+
+ g_object_unref (header);
+ }
+
+ g_free (id);
+ g_object_unref (appinfo);
+ return section;
+}
+
+static void
+remove_application (const char *desktop_id)
+{
+ GDesktopAppInfo *appinfo;
+ gchar *id;
+ AppSection *section;
+
+ appinfo = g_desktop_app_info_new (desktop_id);
+ if (!appinfo) {
+ g_warning ("could not remove '%s', there's no desktop file with that id", desktop_id);
+ return;
+ }
+
+ id = g_app_info_get_simple_id (G_APP_INFO (appinfo));
+
+ section = g_hash_table_lookup (applications, id);
+ if (section) {
+ remove_section (section, id);
+ }
+ else {
+ g_warning ("could not remove '%s', it's not registered", desktop_id);
+ }
+
+ g_free (id);
+ g_object_unref (appinfo);
+}
+
+/* This function turns a specific desktop id into a menu
+ item and registers it appropriately with everyone */
+static gboolean
+build_launcher (gpointer data)
+{
+ gchar *desktop_id = data;
+
+ add_application (desktop_id);
+
+ g_free (desktop_id);
+ return FALSE;
+}
+
+/* This function goes through all the launchers that we're
+ supposed to be grabbing and decides to show turn them
+ into menu items or not. It doens't do the work, but it
+ makes the decision. */
+static gboolean
+build_launchers (gpointer data)
+{
+ gchar **applications = g_settings_get_strv (settings, "applications");
+ gchar **app;
+
+ g_return_val_if_fail (applications != NULL, FALSE);
+
+ for (app = applications; *app; app++)
+ {
+ g_idle_add(build_launcher, g_strdup (*app));
+ }
+
+ g_strfreev (applications);
+ return FALSE;
+}
+
+static void
+service_shutdown (IndicatorService * service, gpointer user_data)
+{
+ GMainLoop *mainloop = user_data;
+
+ g_warning("Shutting down service!");
+ g_main_loop_quit(mainloop);
+}
+
+static void
+app_section_remove_attention (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ AppSection *section = value;
+ app_section_clear_draws_attention (section);
+}
+
+static void
+clear_action_activate (GSimpleAction *simple,
+ GVariant *param,
+ gpointer user_data)
+{
+ g_hash_table_foreach (applications, app_section_remove_attention, NULL);
+}
+
+static void
+status_action_activate (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ const gchar *status;
+
+ status = g_variant_get_string (parameter, NULL);
+
+ indicator_messages_service_emit_status_changed (messages_service, status);
+}
static void
register_application (IndicatorMessagesService *service,
@@ -50,15 +444,18 @@ register_application (IndicatorMessagesService *service,
const gchar *menu_path,
gpointer user_data)
{
+ AppSection *section;
GDBusConnection *bus;
const gchar *sender;
- im_application_list_add (applications, desktop_id);
+ section = add_application (desktop_id);
+ if (!section)
+ return;
bus = g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (service));
sender = g_dbus_method_invocation_get_sender (invocation);
- im_application_list_set_remote (applications, desktop_id, bus, sender, menu_path);
+ app_section_set_object_path (section, bus, sender, menu_path);
g_settings_strv_append_unique (settings, "applications", desktop_id);
indicator_messages_service_complete_register_application (service, invocation);
@@ -70,13 +467,36 @@ unregister_application (IndicatorMessagesService *service,
const gchar *desktop_id,
gpointer user_data)
{
- im_application_list_remove (applications, desktop_id);
+ remove_application (desktop_id);
g_settings_strv_remove (settings, "applications", desktop_id);
indicator_messages_service_complete_unregister_application (service, invocation);
}
static void
+application_stopped_running (IndicatorMessagesService *service,
+ GDBusMethodInvocation *invocation,
+ const gchar *desktop_id,
+ gpointer user_data)
+{
+ GDesktopAppInfo *appinfo;
+ gchar *id;
+ AppSection *section;
+
+ indicator_messages_service_complete_application_stopped_running (service, invocation);
+
+ if (!(appinfo = g_desktop_app_info_new (desktop_id)))
+ return;
+
+ id = g_app_info_get_simple_id (G_APP_INFO (appinfo));
+ section = g_hash_table_lookup (applications, id);
+ app_section_unset_object_path (section);
+
+ g_free (id);
+ g_object_unref (appinfo);
+}
+
+static void
set_status (IndicatorMessagesService *service,
GDBusMethodInvocation *invocation,
const gchar *desktop_id,
@@ -84,7 +504,8 @@ set_status (IndicatorMessagesService *service,
gpointer user_data)
{
GDesktopAppInfo *appinfo;
- const gchar *id;
+ gchar *id;
+ AppSection *section;
g_return_if_fail (g_str_equal (status_str, "available") ||
g_str_equal (status_str, "away")||
@@ -98,53 +519,113 @@ set_status (IndicatorMessagesService *service,
return;
}
- id = g_app_info_get_id (G_APP_INFO (appinfo));
-
- im_application_list_set_status(applications, id, status_str);
+ id = g_app_info_get_simple_id (G_APP_INFO (appinfo));
+ section = g_hash_table_lookup (applications, id);
+ if (section != NULL)
+ app_section_set_status (section, status_str);
indicator_messages_service_complete_set_status (service, invocation);
+ g_free (id);
g_object_unref (appinfo);
}
-/* The status has been set by the user, let's tell the world! */
-static void
-status_set_by_user (ImApplicationList * list, const gchar * status, gpointer user_data)
+static GSimpleActionGroup *
+create_action_group (void)
+{
+ GSimpleActionGroup *actions;
+ GSimpleAction *messages;
+ GSimpleAction *clear;
+ GSimpleAction *status;
+ const gchar *default_status[] = { "offline", NULL };
+ gchar *icon;
+
+ actions = g_simple_action_group_new ();
+
+ /* state of the messages action is its icon name */
+ icon = indicator_messages_get_icon_name ();
+ messages = g_simple_action_new_stateful ("messages", G_VARIANT_TYPE ("s"),
+ g_variant_new_string (icon));
+
+ status = g_simple_action_new_stateful ("status", G_VARIANT_TYPE ("s"),
+ g_variant_new_strv (default_status, -1));
+ g_signal_connect (status, "activate", G_CALLBACK (status_action_activate), NULL);
+
+ clear = g_simple_action_new ("clear", NULL);
+ g_simple_action_set_enabled (clear, FALSE);
+ g_signal_connect (clear, "activate", G_CALLBACK (clear_action_activate), NULL);
+
+ g_simple_action_group_insert (actions, G_ACTION (messages));
+ g_simple_action_group_insert (actions, G_ACTION (status));
+ g_simple_action_group_insert (actions, G_ACTION (clear));
+
+ g_free (icon);
+ return actions;
+}
+
+static GMenuModel *
+create_status_section (void)
{
- indicator_messages_service_emit_status_changed(messages_service, status);
- return;
+ GMenu *menu;
+ GMenuItem *item;
+ struct status_item {
+ gchar *label;
+ gchar *action;
+ gchar *icon_name;
+ } status_items[] = {
+ { _("Available"), "status::available", "user-available" },
+ { _("Away"), "status::away", "user-away" },
+ { _("Busy"), "status::busy", "user-busy" },
+ { _("Invisible"), "status::invisible", "user-invisible" },
+ { _("Offline"), "status::offline", "user-offline" }
+ };
+ int i;
+
+ menu = g_menu_new ();
+
+ item = g_menu_item_new (NULL, NULL);
+ g_menu_item_set_attribute (item, "x-canonical-type", "s", "IdoMenuItem");
+
+ for (i = 0; i < G_N_ELEMENTS (status_items); i++) {
+ g_menu_item_set_label (item, status_items[i].label);
+ g_menu_item_set_detailed_action (item, status_items[i].action);
+ g_menu_item_set_attribute (item, "x-canonical-icon", "s", status_items[i].icon_name);
+ g_menu_append_item (menu, item);
+ }
+
+ g_object_unref (item);
+ return G_MENU_MODEL (menu);
}
static void
-on_bus_acquired (GDBusConnection *bus,
- const gchar *name,
- gpointer user_data)
+got_bus (GObject *object,
+ GAsyncResult * res,
+ gpointer user_data)
{
+ GDBusConnection *bus;
GError *error = NULL;
- GHashTableIter it;
- const gchar *profile;
- ImMenu *menu;
+
+ bus = g_bus_get_finish (res, &error);
+ if (!bus) {
+ g_warning ("unable to connect to the session bus: %s", error->message);
+ g_error_free (error);
+ return;
+ }
g_dbus_connection_export_action_group (bus, INDICATOR_MESSAGES_DBUS_OBJECT,
- im_application_list_get_action_group (applications),
- &error);
+ G_ACTION_GROUP (action_muxer), &error);
if (error) {
g_warning ("unable to export action group on dbus: %s", error->message);
g_error_free (error);
return;
}
- g_hash_table_iter_init (&it, menus);
- while (g_hash_table_iter_next (&it, (gpointer *) &profile, (gpointer *) &menu)) {
- gchar *object_path;
-
- object_path = g_strconcat (INDICATOR_MESSAGES_DBUS_OBJECT, "/", profile, NULL);
- if (!im_menu_export (menu, bus, object_path, &error)) {
- g_warning ("unable to export menu for profile '%s': %s", profile, error->message);
- g_clear_error (&error);
- }
-
- g_free (object_path);
+ g_dbus_connection_export_menu_model (bus, INDICATOR_MESSAGES_DBUS_OBJECT,
+ G_MENU_MODEL (toplevel_menu), &error);
+ if (error) {
+ g_warning ("unable to export menu on dbus: %s", error->message);
+ g_error_free (error);
+ return;
}
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (messages_service),
@@ -159,29 +640,19 @@ on_bus_acquired (GDBusConnection *bus,
g_object_unref (bus);
}
-static void
-on_name_lost (GDBusConnection *bus,
- const gchar *name,
- gpointer user_data)
-{
- GMainLoop *mainloop = user_data;
-
- g_main_loop_quit (mainloop);
-}
-
int
main (int argc, char ** argv)
{
- GMainLoop * mainloop = NULL;
- GBusNameOwnerFlags flags;
-
- /* Glib init */
-#if G_ENCODE_VERSION(GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION) <= GLIB_VERSION_2_34
- g_type_init();
-#endif
+ GMainLoop * mainloop;
+ IndicatorService * service;
+ gdk_init(&argc, &argv);
mainloop = g_main_loop_new (NULL, FALSE);
+ /* Create the Indicator Service interface */
+ service = indicator_service_new_version(INDICATOR_MESSAGES_DBUS_NAME, 1);
+ g_signal_connect(service, INDICATOR_SERVICE_SIGNAL_SHUTDOWN, G_CALLBACK(service_shutdown), mainloop);
+
/* Setting up i18n and gettext. Apparently, we need
all of these. */
setlocale (LC_ALL, "");
@@ -191,46 +662,40 @@ main (int argc, char ** argv)
/* Bring up the service DBus interface */
messages_service = indicator_messages_service_skeleton_new ();
- flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
- if (argc >= 2 && g_str_equal (argv[1], "--replace"))
- flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
+ g_bus_get (G_BUS_TYPE_SESSION, NULL, got_bus, NULL);
+
+ actions = create_action_group ();
- g_bus_own_name (G_BUS_TYPE_SESSION, "com.canonical.indicator.messages", flags,
- on_bus_acquired, NULL, on_name_lost, mainloop, NULL);
+ action_muxer = g_action_muxer_new ();
+ g_action_muxer_insert (action_muxer, NULL, G_ACTION_GROUP (actions));
g_signal_connect (messages_service, "handle-register-application",
G_CALLBACK (register_application), NULL);
g_signal_connect (messages_service, "handle-unregister-application",
G_CALLBACK (unregister_application), NULL);
+ g_signal_connect (messages_service, "handle-application-stopped-running",
+ G_CALLBACK (application_stopped_running), NULL);
g_signal_connect (messages_service, "handle-set-status",
G_CALLBACK (set_status), NULL);
- applications = im_application_list_new ();
- g_signal_connect (applications, "status-set",
- G_CALLBACK (status_set_by_user), NULL);
+ menu = g_menu_new ();
+ chat_section = create_status_section ();
+ g_menu_append (menu, _("Clear"), "clear");
- settings = g_settings_new ("com.canonical.indicator.messages");
- {
- gchar **app_ids;
- gchar **id;
+ toplevel_menu = g_menu_new ();
- app_ids = g_settings_get_strv (settings, "applications");
- for (id = app_ids; *id; id++)
- im_application_list_add (applications, *id);
+ settings = g_settings_new ("com.canonical.indicator.messages");
- g_strfreev (app_ids);
- }
+ applications = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
- menus = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
- g_hash_table_insert (menus, "phone", im_phone_menu_new (applications));
- g_hash_table_insert (menus, "desktop", im_desktop_menu_new (applications));
+ g_idle_add(build_launchers, NULL);
g_main_loop_run(mainloop);
/* Clean up */
- g_hash_table_unref (menus);
g_object_unref (messages_service);
+ g_object_unref (chat_section);
g_object_unref (settings);
- g_object_unref (applications);
+ g_hash_table_unref (applications);
return 0;
}
diff --git a/common/com.canonical.indicator.messages.service.xml b/src/messages-service.xml
index 3c3c779..3c3c779 100644
--- a/common/com.canonical.indicator.messages.service.xml
+++ b/src/messages-service.xml
diff --git a/test/Makefile.am b/test/Makefile.am
index c39aea4..4f1a163 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -38,8 +38,8 @@ noinst_LTLIBRARIES = \
libindicator-messages-service.la
libindicator_messages_service_la_SOURCES = \
- $(top_builddir)/common/indicator-messages-service.c \
- $(top_builddir)/common/indicator-messages-service.h \
+ $(top_builddir)/src/indicator-messages-service.c \
+ $(top_builddir)/src/indicator-messages-service.h \
$(top_srcdir)/src/app-section.c \
$(top_srcdir)/src/app-section.h \
$(top_srcdir)/src/gactionmuxer.c \
@@ -53,7 +53,6 @@ libindicator_messages_service_la_CFLAGS = \
$(APPLET_CFLAGS) \
$(COVERAGE_CFLAGS) \
-I$(top_builddir)/src \
- -I$(top_builddir)/common \
-Wall \
-Wl,-Bsymbolic-functions \
-Wl,-z,defs \
diff --git a/test/indicator-messages-service-activate.c b/test/indicator-messages-service-activate.c
index f5a26b0..b0ec9b7 100644
--- a/test/indicator-messages-service-activate.c
+++ b/test/indicator-messages-service-activate.c
@@ -28,10 +28,6 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
int
main (int argc, char ** argv)
{
-#if G_ENCODE_VERSION(GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION) <= GLIB_VERSION_2_34
- g_type_init();
-#endif
-
guint returnval = 0;
GError * error = NULL;
diff --git a/test/test-gactionmuxer.cpp b/test/test-gactionmuxer.cpp
index efd6e4f..b80f86d 100644
--- a/test/test-gactionmuxer.cpp
+++ b/test/test-gactionmuxer.cpp
@@ -1,21 +1,3 @@
-/*
-An indicator to show information that is in messaging applications
-that the user is using.
-
-Copyright 2012 Canonical Ltd.
-
-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 <gio/gio.h>
@@ -43,10 +25,6 @@ strv_contains (gchar **str_array,
TEST(GActionMuxerTest, Sanity) {
GActionMuxer *muxer;
-#if G_ENCODE_VERSION(GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION) <= GLIB_VERSION_2_34
- g_type_init ();
-#endif
-
g_action_muxer_insert (NULL, NULL, NULL);
g_action_muxer_remove (NULL, NULL);
@@ -66,10 +44,6 @@ TEST(GActionMuxerTest, Empty) {
GActionMuxer *muxer;
gchar **actions;
-#if G_ENCODE_VERSION(GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION) <= GLIB_VERSION_2_34
- g_type_init ();
-#endif
-
muxer = g_action_muxer_new ();
actions = g_action_group_list_actions (G_ACTION_GROUP (muxer));
@@ -89,10 +63,6 @@ TEST(GActionMuxerTest, AddAndRemove) {
GActionMuxer *muxer;
gchar **actions;
-#if G_ENCODE_VERSION(GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION) <= GLIB_VERSION_2_34
- g_type_init ();
-#endif
-
group1 = g_simple_action_group_new ();
g_simple_action_group_add_entries (group1,
entries1,
@@ -184,10 +154,6 @@ TEST(GActionMuxerTest, ActionAttributes) {
GVariant *state_hint[2];
GVariant *state[2];
-#if G_ENCODE_VERSION(GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION) <= GLIB_VERSION_2_34
- g_type_init ();
-#endif
-
group = g_simple_action_group_new ();
action = g_simple_action_new ("one", G_VARIANT_TYPE_STRING);
g_simple_action_group_insert (group, G_ACTION (action));