diff options
44 files changed, 4903 insertions, 1480 deletions
diff --git a/.bzr-builddeb/default.conf b/.bzr-builddeb/default.conf new file mode 100644 index 0000000..6c96a98 --- /dev/null +++ b/.bzr-builddeb/default.conf @@ -0,0 +1,2 @@ +[BUILDDEB] +split = True diff --git a/COPYING.GPLv3 b/COPYING.GPLv3 new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING.GPLv3 @@ -0,0 +1,674 @@ + 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 daeb2b7..f8141a8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,6 @@ SUBDIRS = \ + common \ src \ libmessaging-menu \ data \ @@ -1,4 +1,11 @@ +12.10.5 + +* fix crash caused by GError not being cleared (lp #1064314) +* fix crash when registering an app that was previously unregistered (lp #1065169) +* remove the icon if NULL is passed to source_set_icon (lp #1070421) +* call gtk-update-icon-cache (lp #1060618) + 12.10.4 * specify fallback icons for the ones with status emblem (lp #1056595) diff --git a/common/Makefile.am b/common/Makefile.am new file mode 100644 index 0000000..0b8bad4 --- /dev/null +++ b/common/Makefile.am @@ -0,0 +1,32 @@ + +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 new file mode 100644 index 0000000..6f038e6 --- /dev/null +++ b/common/com.canonical.indicator.messages.application.xml @@ -0,0 +1,39 @@ +<?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/src/messages-service.xml b/common/com.canonical.indicator.messages.service.xml index 00ae154..00ae154 100644 --- a/src/messages-service.xml +++ b/common/com.canonical.indicator.messages.service.xml diff --git a/configure.ac b/configure.ac index 971bd96..b020d30 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,10 @@ -AC_INIT(indicator-messages, 12.10.4) +AC_INIT(indicator-messages, 12.10.5) AC_PREREQ(2.62) AM_CONFIG_HEADER(config.h) -AM_INIT_AUTOMAKE() +AM_INIT_AUTOMAKE([check-news]) AM_MAINTAINER_MODE @@ -156,6 +156,7 @@ AM_GLIB_GNU_GETTEXT AC_OUTPUT([ Makefile src/Makefile +common/Makefile data/Makefile data/icons/Makefile data/icons/16x16/Makefile diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..6919c93 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,912 @@ +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.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.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 + + -- Sebastien Bacher <seb128@ubuntu.com> Wed, 07 Nov 2012 15:16:08 +0100 + +indicator-messages (12.10.5-0ubuntu1) quantal-proposed; urgency=low + + * New upstream release: + - fix crash caused by GError not being cleared (lp #1064314) + - fix crash when registering an app that was previously unregistered + (lp #1065169) + - remove the icon if NULL is passed to source_set_icon (lp #1070421) + + -- Sebastien Bacher <seb128@ubuntu.com> Wed, 24 Oct 2012 17:30:45 +0200 + +indicator-messages (12.10.4-0ubuntu1) quantal; urgency=low + + * New upstream release: + - don't segfault if .desktop file is not installed (lp: #1058386) + - Use fallback icon names (without status emblems). + (lp: #1056595) + + -- Sebastien Bacher <seb128@ubuntu.com> Tue, 02 Oct 2012 23:20:19 +0200 + +indicator-messages (12.10.2-0ubuntu1) quantal; urgency=low + + * New upstream release, includes apis to allow changing the label and + the icon of sources and add documentation + * debian/libmessaging-menu0.symbols: + - new version update + * debian/libmessaging-menu-dev.install: + - install the documentation + * debian/rules: + - build the documentation + - updated shlibs version + - use --with gir so gir:Depends work as it should + (lp: #1044125) + + -- Sebastien Bacher <seb128@ubuntu.com> Sat, 01 Sep 2012 12:36:35 +0200 + +indicator-messages (12.10.1-0ubuntu1) quantal-proposed; urgency=low + + * New upstream version + * debian/control: + - breaks on version of ported clients using libindicate + - set some conflicts on the old deprecated status provider binaries + - updated Build-Depends + * debian/control, debian/*.install: + - drop indicator-status-provider-*, those are deprecated + - new binaries for the libmessaging-menu library + * debian/libmessaging-menu0.symbols: + - symbol file for the new library + * Updated packaging to dh9, current standards + * Drop gtk2 build from the packaging since support for it was + dropped in trunk, thanks Lars Uebernickel + + -- Sebastien Bacher <seb128@ubuntu.com> Mon, 27 Aug 2012 18:56:59 +0200 + +indicator-messages (0.6.0-0ubuntu1) precise; urgency=low + + * New upstream release. + * Handle blank icons properly (LP: #956147) + * Fix telepathy offline/disconnected state bug + + -- Charles Kerr <charles.kerr@canonical.com> Wed, 11 Apr 2012 11:09:46 -0500 + +indicator-messages (0.5.95-0ubuntu1) precise; urgency=low + + [ Charles Kerr ] + * New upstream release. + * Fix 0.5.94 blacklist regression. + + [ Ken VanDine ] + * src/indicator-messages.c + * revert fix for the broken images in indicator-messages-service, it + caused the service to build against gtk and we can't parallel install + the service anyway + + -- Ken VanDine <ken.vandine@canonical.com> Thu, 05 Apr 2012 16:47:19 -0400 + +indicator-messages (0.5.94-0ubuntu1) precise; urgency=low + + [ Ted Gould ] + * New upstream release. + * Fix for missing icons in the messaging menu (LP: #960553) + * Fix i18n for description (LP: #957525) + * Implementing initial testing + * Fixing blacklists (LP: #939258) + * debian/control: Adding dep on libgtest-dev + + [ Ken VanDine ] + * debian/control: Don't build depend on libgtest-dev, it isn't in main + * debian/rules: disable tests, they require libgtest-dev + + -- Ken VanDine <ken.vandine@canonical.com> Wed, 21 Mar 2012 17:00:10 -0400 + +indicator-messages (0.5.93-0ubuntu2) precise; urgency=low + + * src/im-menu-item.c + - cherry picked fix for missing icons in the messaging menu (LP: #960553) + + -- Ken VanDine <ken.vandine@canonical.com> Tue, 20 Mar 2012 16:41:13 -0400 + +indicator-messages (0.5.93-0ubuntu1) precise; urgency=low + + * debian/control: + - drop build-depends on gnome-doc-utils and scrollkeeper, not required + + [ Ted Gould ] + * New upstream release. + * Fixing alignment of menu items (LP: #939953) + * Vertically centering alignment of double high items (LP: #770486) + * Fixing setting of status on telepathy (LP: #943757) + + -- Sebastien Bacher <seb128@ubuntu.com> Thu, 08 Mar 2012 22:11:24 +0100 + +indicator-messages (0.5.92-0ubuntu1) precise; urgency=low + + [ Ted Gould ] + * New upstream release. + * Fix goto handling (by removing) (LP: #937441, LP: #937438) + * Tell accountsservice when user has messages for the benefit of + LightDM. + * Dropping debian/patches: Merged upstream + + [ Ken VanDine ] + * debian/source/format + - dropped "3.0 (quilt), it doesn't play well with bzr maintained packages + + -- Ken VanDine <ken.vandine@canonical.com> Thu, 23 Feb 2012 02:48:09 -0500 + +indicator-messages (0.5.91-0ubuntu3) precise; urgency=low + + * debian/source/format: + - Make "3.0 (quilt)" + * debian/patches/tell-accounts-service.patch: + - Tell accountsservice when user has messages for the benefit of + LightDM. + + -- Michael Terry <mterry@ubuntu.com> Wed, 15 Feb 2012 15:32:16 -0500 + +indicator-messages (0.5.91-0ubuntu2) precise; urgency=low + + * debian/control: build-depends on dh-autoreconf as well + + -- Sebastien Bacher <seb128@ubuntu.com> Tue, 14 Feb 2012 23:50:15 +0100 + +indicator-messages (0.5.91-0ubuntu1) precise; urgency=low + + * New upstream release. + * Better lozenges that look nice + * debian/rules: Adding autoreconf and running make check + * debian/compat: 5 + * debian/source/format: Dropping + + -- Ted Gould <ted@ubuntu.com> Tue, 14 Feb 2012 15:29:52 -0600 + +indicator-messages (0.5.90-0ubuntu1) precise; urgency=low + + [ Ted Gould ] + * New upstream release. + * Adding code coverage targets + * Match libindicate 0.6.90 + * Updating for Dbusmenu 0.5.90 + * Plug leak in launcher_menu_item_new + * Fix memory leak: Free path string. + * Compare server and path for multi-server clients + * Using the new GTK3 box API to avoid deprecations + * Adding a name hint + * debian/control: Requiring libindicate 0.6.90 and dbusmenu 0.5.90 + + [ Ken VanDine ] + * debian/rules + - drop no-error=deprecated-declarations from CFLAGS + + -- Ken VanDine <ken.vandine@canonical.com> Fri, 10 Feb 2012 14:29:59 -0500 + +indicator-messages (0.5.0-1ubuntu1) precise; urgency=low + + * rebuild for libindicator7 + * debian/rules + - build with no-error=deprecated-declarations + * debian/control + - set ubuntu-desktop VCS and maintainer + + -- Ken VanDine <ken.vandine@canonical.com> Wed, 25 Jan 2012 09:56:59 -0500 + +indicator-messages (0.5.0-1) experimental; urgency=low + + * Merge new upstream from Ubuntu. + + -- Evgeni Golov <evgeni@debian.org> Wed, 19 Oct 2011 13:36:00 +0200 + +indicator-messages (0.5.0-0ubuntu1) oneiric; urgency=low + + * New upstream release. + * Fix memory leak (LP: #690668) + * Drop debian/patches/lp_690668.patch: Merged upstream + + -- Ted Gould <ted@ubuntu.com> Wed, 28 Sep 2011 10:39:26 -0500 + +indicator-messages (0.4.95-0ubuntu1) oneiric; urgency=low + + * New upstream release. + * Fix naming of "Clear" item + * Fix alignment of items to there is a consistent gutter + * debian/patches/lp_690668.patch: Updating + + -- Ted Gould <ted@ubuntu.com> Thu, 25 Aug 2011 09:09:20 -0500 + +indicator-messages (0.4.94-0ubuntu1) oneiric; urgency=low + + * New upstream release. + ∘ Fix drawing of triangles and capsules in GTK3 + ∘ Fix Emesene statuses (LP: #817504) + + -- Ted Gould <ted@ubuntu.com> Thu, 18 Aug 2011 10:57:40 -0500 + +indicator-messages (0.4.93-0ubuntu2) oneiric; urgency=low + + * debian/rules + - run intltool-update to update the translations template + + -- Ken VanDine <ken.vandine@canonical.com> Fri, 12 Aug 2011 10:05:01 -0400 + +indicator-messages (0.4.93-0ubuntu1) oneiric; urgency=low + + [ Ted Gould ] + * New upstream release. + * Add an item to clear the alert + * Choose mail applications based on the default application + * Clear based on middle click + + [ Ken VanDine ] + * debian/control + - Bumped build depends for libindicate to >= 0.5.90 + * debian/rules + - Fixed configure arg for gtk version + + -- Ken VanDine <ken.vandine@canonical.com> Thu, 11 Aug 2011 14:18:05 -0400 + +indicator-messages (0.4.92-0ubuntu4) oneiric; urgency=low + + * debian/control + - Dropped recommends for gwibber now that it is seeded + * cherry picked r220 from trunk + - Make sure to insert in the right locations + * cherry picked r221 from trunk + - Making mail applications based on the default mail client not a + hardcoded desktop file. + + -- Ken VanDine <ken.vandine@canonical.com> Fri, 05 Aug 2011 14:33:39 -0400 + +indicator-messages (0.4.92-0ubuntu3) oneiric; urgency=low + + * debian/control + - Added recommends for gwibber, it got missed when we dropped indicator-me + should get removed after we get it seeded properly + + -- Ken VanDine <ken.vandine@canonical.com> Sun, 31 Jul 2011 16:36:09 -0400 + +indicator-messages (0.4.92-0ubuntu2) oneiric; urgency=low + + * debian/control: + - use conflicts against indicator-me + + -- Michael Vogt <michael.vogt@ubuntu.com> Fri, 29 Jul 2011 15:49:40 +0200 + +indicator-messages (0.4.92-0ubuntu1) oneiric; urgency=low + + * New upstream release. + * debian/control + - Added Replaces indicator-me + - Added build depends for libtelepathy-glib-dev + - Added new binary packages, libindicator-messages-status-provider-dev, + libindicator-messages-status-provider1, and each provider + - Make indicator-messages recommend indicator-status-provider-mc5 + * debian/libindicator-messages-status-provider1.symbols + - Added symbols file + + -- Ken VanDine <ken.vandine@canonical.com> Thu, 14 Jul 2011 16:28:23 -0400 + +indicator-messages (0.4.91-0ubuntu1) oneiric; urgency=low + + [ Ted Gould ] + * New upstream release. + * Update for libindicator 0.4 + + [ Ken VanDine ] + * debian/control + - Bumped build depends for libindicator to >= 0.3.90 + + -- Ken VanDine <ken.vandine@canonical.com> Fri, 08 Jul 2011 11:23:58 -0400 + +indicator-messages (0.4.90-0ubuntu2) oneiric; urgency=low + + * Rebuild for libdbusmenu-gtk3-4 + * debian/control + - Removed duplicate build depends + - Dropped build depends on indicator-messages-gtk2 + + -- Ken VanDine <ken.vandine@canonical.com> Tue, 28 Jun 2011 09:01:12 -0400 + +indicator-messages (0.4.90-0ubuntu1) oneiric; urgency=low + + [ Ted Gould ] + * New upstream release. + * GTK 3 + + [ Ken VanDine ] + * debian/control + - Bumped standards version to 3.9.2 + - Add new binary for indicator-messages-gtk2 + - Make indicator-messages recommend indicator-messages-gtk2 (until unity + can load gtk3 indicators) + - indicator-messages-gtk2 replaces indicator-messages << 0.4.90 + - Added build depends for libdbusmenu-gtk3-dev and libindicator3-dev + * debian/rules + - build for both gtk2 and gtk3 + + -- Ken VanDine <ken.vandine@canonical.com> Wed, 22 Jun 2011 12:34:43 -0400 + +indicator-messages (0.4.0-1) unstable; urgency=low + + * Merge new upstream from Ubuntu. + * Standards-Version: 3.9.2 + + -- Evgeni Golov <evgeni@debian.org> Sun, 05 Jun 2011 13:16:03 +0200 + +indicator-messages (0.4.0-0ubuntu1) natty; urgency=low + + * New upstream release. + ∘ Set the type before other variables to get default handling more + reliable (LP: #723873) + ∘ Add support for overriding the default icon with a specific one + for the messaging menu (LP: #741068) + + -- Ted Gould <ted@ubuntu.com> Thu, 07 Apr 2011 12:35:36 -0500 + +indicator-messages (0.3.92-1) unstable; urgency=low + + * Merge new upstream from Ubuntu. + * Fix 0.3.11-0ubuntu3 changelog entry (missed a space), so that + bzr builddeb won't go crazy (see Debian Bug #620242) + * Fix debian/copyright, was refering to libdbusmenu by mistake. + + -- Evgeni Golov <evgeni@debian.org> Sun, 03 Apr 2011 13:37:33 +0200 + +indicator-messages (0.3.92-0ubuntu3) natty; urgency=low + + * debian/patches/lp_690668.patch + - Memory leak fixes (LP: #690668) + * debian/control + - Bump standards version to 3.9.1 + * debian/source/format + - Use source format 3.0 (quilt) + + -- Ken VanDine <ken.vandine@canonical.com> Thu, 24 Feb 2011 12:13:33 -0500 + +indicator-messages (0.3.92-0ubuntu2) natty; urgency=low + + * src/launcher-menu-item.c + - Make sure menu entries are displayed for launchers that aren't running (LP: #723873) + + -- Ken VanDine <ken.vandine@canonical.com> Wed, 23 Feb 2011 16:06:37 -0500 + +indicator-messages (0.3.92-0ubuntu1) natty; urgency=low + + * New upstream release. + * Adding in accessible description support + * debian/control: libindicator version 0.3.19 + + -- Ted Gould <ted@ubuntu.com> Thu, 17 Feb 2011 12:18:32 -0600 + +indicator-messages (0.3.91-0ubuntu1) natty; urgency=low + + * New upstream release. + * Fixing the service file + * Adding a log domain + * Fixing type handler for dbusmenu 0.3.94 + * debian/control: dbusmenu to 0.3.94 + + -- Ted Gould <ted@ubuntu.com> Thu, 27 Jan 2011 17:01:34 -0600 + +indicator-messages (0.3.90-0ubuntu1) natty; urgency=low + + [ Ted Gould ] + * New upstream release. + * GDBus Port + * New libindicate and dbusmenu + + [ Ken VanDine ] + * debian/control + - Build depends on libindicate-dev to 0.4.91 + - Build depends on dbusmenu to 0.3.91 + - Set Vcs to ~ubuntu-desktop + * data/indicator-messages.service.in + - Fixed dbus interface name + + -- Ken VanDine <ken.vandine@canonical.com> Fri, 14 Jan 2011 14:50:30 -0600 + +indicator-messages (0.3.11-0ubuntu4) natty; urgency=low + + * debian/control + - Bump build depends for libdbusmenu-* to >= 0.3.90 + + -- Ken VanDine <ken.vandine@canonical.com> Thu, 09 Dec 2010 11:39:40 -0500 + +indicator-messages (0.3.11-0ubuntu3) UNRELEASED; urgency=low + + * debian/control: package description spelling (LP: #658096) + + -- Greg Auger <gregory.auger@googlemail.com> Tue, 09 Nov 2010 18:49:38 +0000 + +indicator-messages (0.3.11-0ubuntu2) maverick; urgency=low + + * Rebuild for libindicator ABI change (LP: #637692) + * debian/control: depends on latest libindicator-dev + + -- Didier Roche <didrocks@ubuntu.com> Wed, 22 Sep 2010 18:39:27 +0200 + +indicator-messages (0.3.11-0ubuntu1) maverick; urgency=low + + * New upstream release. + * Fixing triangles by passing data to callback (LP: #623453) + + -- Ted Gould <ted@ubuntu.com> Thu, 09 Sep 2010 13:24:13 -0500 + +indicator-messages (0.3.10-0ubuntu1) maverick; urgency=low + + * New upstream release. + * Shifting icons into the gutter and adjusting the arrow + padding. + + -- Ted Gould <ted@ubuntu.com> Thu, 26 Aug 2010 15:08:31 -0500 + +indicator-messages (0.3.9-0ubuntu1) maverick; urgency=low + + * New upstream release. + * Remove parens from counts + + -- Ted Gould <ted@ubuntu.com> Thu, 12 Aug 2010 13:10:10 -0500 + +indicator-messages (0.3.8-0ubuntu1) maverick; urgency=low + + * New upstream release. + - running apps triangle indicator overlay + - message counters are now rendered with a nice rounded background + + -- Ken VanDine <ken.vandine@canonical.com> Thu, 29 Jul 2010 13:33:14 -0400 + +indicator-messages (0.3.7-1) unstable; urgency=low + + * Merge from Ubuntu. + Closes: #586132 + * debian/control: + - Homepage is at LP/indicator-messages, not -applet. + - Update package description. + - Update Vcs-* fields. + - Standards-Version: 3.8.4. + * debian/copyright: + - Convert to DEP5 format. + - Add missing copyright holders of debian/. + + -- Evgeni Golov <evgeni@debian.org> Thu, 17 Jun 2010 13:02:37 +0200 + +indicator-messages (0.3.7-0ubuntu1~ppa1) lucid; urgency=low + + * New upstream release. + * When loading desktop files check to see if they've + already been eclipsed by an app. (LP: #549096) + * Track better application shortcuts and ensure that they + get removed when the application is. (LP: #539167) + * Track blacklisted apps to ensure that the messaging menu + hides when there is no app. (LP: #533021) + + -- Ted Gould <ted@ubuntu.com> Fri, 21 May 2010 15:02:16 -0500 + +indicator-messages (0.3.6-0ubuntu2) lucid; urgency=low + + * Upstream Merge + * When loading desktop files check to see if they've + already been eclipsed by an app. (LP: #549096) + * Track better application shortcuts and ensure that they + get removed when the application is. (LP: #539167) + + -- Ted Gould <ted@ubuntu.com> Sat, 17 Apr 2010 13:10:42 -0500 + +indicator-messages (0.3.6-0ubuntu1) lucid; urgency=low + + * New upstream release. + * Fixing several bugs with regards to visibility and placement + if items and separators in the menu. (LP: #446914) + * Changing the service management over to using libindicator + services and service manager for robustness. + + -- Ted Gould <ted@ubuntu.com> Thu, 01 Apr 2010 10:13:38 -0500 + +indicator-messages (0.3.5-0ubuntu2) lucid; urgency=low + + * debian/control: + - change the indicator-applet depends to a recommends to let other + desktops use other options rather than having to trigger GNOME + + -- Sebastien Bacher <seb128@ubuntu.com> Tue, 30 Mar 2010 10:35:59 +0200 + +indicator-messages (0.3.5-0ubuntu1) lucid; urgency=low + + * Upstream release 0.3.5 + * Show which applications are running with a small icon + next to their entry. (LP: #438526) + * Shift command items to line up with application names. + (LP: #537312) + * Track applications we've seen so that we can show "Set Up" + text if we've not seen the app before. + * Fix avatar spacing to match other menu items + * Use standard libindicator icon handling in panel + * Fix translation of non-running applications (LP: #540148) + * Handling bools for requesting attention from apps + * Changing microblogging to broadcast to match Me Menu (LP: #534952) + * debian/control: + - Updating requirement to libindicator 0.3.5 + - Increasing dbusmenu-glib dep to 0.2.8 + + -- Ted Gould <ted@ubuntu.com> Thu, 25 Mar 2010 10:53:01 -0500 + +indicator-messages (0.3.4-0ubuntu1~ppa1) lucid; urgency=low + + * Upstream release 0.3.4 + * Fix avatar spacing to match other menu items + * Use standard libindicator icon handling in panel + * Fix translation of non-running applications (LP: #540148) + * Handling bools for requesting attention from apps + * Changing microblogging to broadcast to match Me Menu (LP: #534952) + * debian/control: Updating requirement to libindicator 0.3.5 + + -- Ted Gould <ted@ubuntu.com> Thu, 18 Mar 2010 13:45:19 -0500 + +indicator-messages (0.3.3-0ubuntu2) lucid; urgency=low + + * src/default-applications.c + - string change for consistency (LP: #534952) + + -- Ken VanDine <ken.vandine@canonical.com> Fri, 12 Mar 2010 11:44:46 -0500 + +indicator-messages (0.3.3-0ubuntu1) lucid; urgency=low + + * Upstream release 0.3.3 + * Changing names of default applications to generic names + * Making icons of default applications match panel theme + + -- Ted Gould <ted@ubuntu.com> Thu, 04 Mar 2010 16:02:11 -0600 + +indicator-messages (0.3.2-0ubuntu1) lucid; urgency=low + + * Upstream release 0.3.2 + * Removing extra ref + * Moving locally defined variable to use the one defined in + the function to fix a NULL pointer. (lp: #518547) + * Disconnect the count changed signal property + * Adding static desktop shortcuts to application items + * Adding dynamic application menuitems + * Switching application menu items to remove descriptions + and add in application icons + * debian/control: + * libindicator build dependency to 0.3.3 + * dbusmenu build dependency to 0.2.5 + + -- Ted Gould <ted@ubuntu.com> Thu, 18 Feb 2010 12:23:29 -0600 + +indicator-messages (0.3.1-0ubuntu2) lucid; urgency=low + + * Upstream Merge + * Removing extra ref + * Moving locally defined variable to use the one defined in + the function to fix a NULL pointer. (lp: #518547) + + -- Ted Gould <ted@ubuntu.com> Tue, 09 Feb 2010 09:30:10 -0600 + +indicator-messages (0.3.1-0ubuntu1) lucid; urgency=low + + * Upstream release 0.3.1 + * Updates for dbusmenu 0.2.2 + * Updates for libindicate 0.3.0 + * debian/control: + * dbusmenu* depends to 0.2.2 + * libindicate* depends to 0.3.0 + + -- Ted Gould <ted@ubuntu.com> Thu, 04 Feb 2010 18:23:34 -0800 + +indicator-messages (0.3.0-0ubuntu1) lucid; urgency=low + + * Upstream release 0.3.0 + - Disabling static builds. + - Porting to libindicator 0.3.0 + - Adding translator comments from hours and minutes strings. + (LP: #456437) + - Upgrading to dbusmenu 0.2.0 + * debian/control: Updating dbusmenu dependency to 0.2.0 + * debian/control: Increasing libindicator-dev dependency to + 0.3.0 + + -- Ted Gould <ted@ubuntu.com> Fri, 08 Jan 2010 12:05:25 -0600 + +indicator-messages (0.2.6+r156-0ubuntu2) lucid; urgency=low + + * debian/control + - add a Breaks for indicator-applet (<< 0.3.0) + + -- Ken VanDine <ken.vandine@canonical.com> Tue, 15 Dec 2009 11:57:42 -0500 + +indicator-messages (0.2.6+r156-0ubuntu1) lucid; urgency=low + + * New snapshot for libindicator 0.3.0 + * debian/control + - Build-Depends on libindicator >= 0.3.0 + + -- Ken VanDine <ken.vandine@canonical.com> Thu, 10 Dec 2009 14:35:46 -0500 + +indicator-messages (0.2.6-0ubuntu1) karmic; urgency=low + + * Upstream release 0.2.6 (LP: #446629) + * Building the menu after checking the blacklist so that the + separators are all correct. + * Small fix for a perspective memory leak. + + -- Ted Gould <ted@ubuntu.com> Fri, 09 Oct 2009 08:14:27 +0200 + +indicator-messages (0.2.5-0ubuntu1) karmic; urgency=low + + * Upstream release 0.2.5 (LP: #440217) + * Adding a check on addition of launchers to ensure icon is correctly + visible. (LP: #433274) + * Making it so that if applications don't set the time on an indicator + the time isn't shown in the menu. (LP: #438237) + * Putting icons in the right places. (LP: #436460) + + -- Ted Gould <ted@ubuntu.com> Thu, 01 Oct 2009 23:34:28 +0200 + +indicator-messages (0.2.4-0ubuntu1) karmic; urgency=low + + * Upstream release 0.2.4 (LP: #436093) + - Merging in the min menu width feature (LP: #428292) + - Merging in icon scaling branch from trunk. Now better icons (LP: #433143) + - Merging in updated trunk (LP: #434097 and LP: #435184) + - add i18n support + - Prevent null entries (LP: #435184) + - add a path for indicators in /usr/share (LP: #434097) + + -- Ted Gould <ted@ubuntu.com> Thu, 24 Sep 2009 13:07:46 -0500 + +indicator-messages (0.2.2-0ubuntu2) karmic; urgency=low + + * src/messages-service.c: initialise the translations + + -- Sebastien Bacher <seb128@ubuntu.com> Tue, 15 Sep 2009 16:41:03 +0200 + +indicator-messages (0.2.2-0ubuntu1) karmic; urgency=low + + * New release (LP: #427357) + * Adds in separators between application groups + * Merging in v2-api stuff which will become 0.2.1 + * debian/control: + - Requiring > 0.2.0 of libindicate-* + - bump dbusmenu dependencies. + + -- Ted Gould <ted@ubuntu.com> Thu, 10 Sep 2009 09:11:57 -0500 + +indicator-messages (0.2.0-0ubuntu2) karmic; urgency=low + + * debian/control + - Bump build depends on dbusmenu to 0.1.0 + + -- Ken VanDine <ken.vandine@canonical.com> Thu, 27 Aug 2009 21:14:46 +0200 + +indicator-messages (0.2.0-0ubuntu1) karmic; urgency=low + + [ Ted Gould ] + * Upstream version 0.2.0 + * debian/control: Adding dependency information for libdbusmenu + to say >= 0.0.2 to match upstream build system. + * debian/watch: Changing to use indicator-messages project. + + [ Martin Pitt ] + * debian/control: Update Vcs-Bzr: for new branch location. + + -- Ted Gould <ted@ubuntu.com> Thu, 27 Aug 2009 20:46:27 +0200 + +indicator-messages (0.2.0~bzr124-0ubuntu1) karmic; urgency=low + + * Update to fix build issue + + -- Sebastien Bacher <seb128@ubuntu.com> Fri, 07 Aug 2009 17:12:40 +0100 + +indicator-messages (0.2.0~bzr121-0ubuntu3) karmic; urgency=low + + * Fix to dereference the application menu items correctly so that + the signal handlers are dropped as well. (lp: #410251) + + -- Ted Gould <ted@ubuntu.com> Wed, 05 Aug 2009 19:00:31 +0100 + +indicator-messages (0.2.0~bzr121-0ubuntu2) karmic; urgency=low + + * Run autogen.sh before upload + + -- Jonathan Riddell <jriddell@ubuntu.com> Wed, 05 Aug 2009 00:42:39 +0100 + +indicator-messages (0.2.0~bzr121-0ubuntu1) karmic; urgency=low + + [ Ted Gould ] + * debian/control: Adding in a build dep on libindicate-gtk-dev + * Changes for the changing libindicate stuff. + * Merge in the dbusmenu changes from the dbusmenu branch + * debian/control: Adding in a build dependency on libdbusmenu-glib and + libdbusmenu-gtk to catch up with the merge. + + -- Jonathan Riddell <jriddell@ubuntu.com> Wed, 05 Aug 2009 00:21:50 +0100 + +indicator-messages (0.2.0~bzr120-0ubuntu1) jaunty; urgency=low + + * Fix to track the timer. (LP: #365187) + + -- Ted Gould <ted@ubuntu.com> Wed, 13 May 2009 09:56:20 -0500 + +indicator-messages (0.2.0~bzr119-0ubuntu1) jaunty; urgency=low + + * Upstream update + + -- Ted Gould <ted@ubuntu.com> Wed, 22 Apr 2009 23:34:21 -0500 + +indicator-messages (0.2.0~bzr116-0ubuntu3) jaunty; urgency=low + + * debian/rules: Adding a rule to remove the .la/.a clutter + + -- Ted Gould <ted@ubuntu.com> Wed, 22 Apr 2009 16:46:59 -0500 + +indicator-messages (0.2.0~bzr116-0ubuntu2) jaunty; urgency=low + + * debian/control: libindicator-dev to ~bzr301 + + -- Ted Gould <ted@ubuntu.com> Wed, 22 Apr 2009 15:58:45 -0500 + +indicator-messages (0.2.0~bzr116-0ubuntu1) jaunty; urgency=low + + * Upstream release + * Bug fixes + * Update API to new libindicator + * debian/control: Adding new dependency on libindicator-dev + + -- Ted Gould <ted@ubuntu.com> Wed, 22 Apr 2009 15:45:21 -0500 + +indicator-messages (0.1.6-0ubuntu1) jaunty; urgency=low + + * New upstream version + * Patch from Cody Russell to fix LP: #359018 by correctly implementing + the finalize functions. + + -- Ted Gould <ted@ubuntu.com> Tue, 14 Apr 2009 11:32:00 +0200 + +indicator-messages (0.1.5-0ubuntu1) jaunty; urgency=low + + * New upstream version + * Fixes the lifecycle of the various structures tracking the messages + and applications. Fixing bugs like (LP: #355616) (LP: #352881) + * Fixes the visual appearance by setting the widget name to grab the + style settings from the main applet. (LP: #351979) + * debian/control: Upgrading dependency on libindicate-dev to 0.1.5 or + higher as the new version requires that. + + -- Ted Gould <ted@ubuntu.com> Fri, 03 Apr 2009 16:32:49 -0500 + +indicator-messages (0.1.4-0ubuntu1) jaunty; urgency=low + + * New upstream version + * Adding the display of indicators that are login messages coming from + other applications. (LP: #345494) + * Making all times displayed for IM messages relative instead of + absolute. (LP: #346345) + * Cleaning up the server removal code. May fix (LP: #345599), I can't + recreate it anymore afterwards. + + -- Ted Gould <ted@ubuntu.com> Mon, 30 Mar 2009 09:40:40 +0200 + +indicator-messages (0.1.3-0ubuntu1) jaunty; urgency=low + + [ Ted Gould ] + * New upstream version. + - Now changes the icon based on non-IM indicators so that Evolution + works much better. (LP: #342480) + - Now the menu items are in a predictable order, alphabetical. + - The Messages for a particular client (i.e. Pidgin) are grouped with + the client they're associated with. + - Adjusting the icon size to match the new one in the Human theme. + - Adjusting the build so that all the different libraries are not + built in a versioned manner. Now it's just one .so, which is + what it should have been originally. + + [ Martin Pitt ] + * Add debian/watch. + * Add bzr-builddeb configuration. + * debian/copyright: Fix download location. + + -- Martin Pitt <martin.pitt@ubuntu.com> Thu, 19 Mar 2009 12:23:17 +0100 + +indicator-messages (0.1-0ubuntu1) jaunty; urgency=low + + * Initial release, based on DX team's PPA packaging branch. + (lp:~indicator-applet-developers/indicator-applet/messages-packaging) + * debian/control: Add Homepage:, Vcs-Bzr:, and fix package + description. + * debian/copyright: Properly describe license. + + -- Martin Pitt <martin.pitt@ubuntu.com> Tue, 17 Feb 2009 11:35:38 +0100 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..546b5c9 --- /dev/null +++ b/debian/control @@ -0,0 +1,84 @@ +Source: indicator-messages +Section: gnome +Priority: optional +Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> +XSBC-Original-Maintainer: The Ayatana Packagers <pkg-ayatana-devel@lists.alioth.debian.org> +Uploaders: Evgeni Golov <evgeni@debian.org> +Build-Depends: debhelper (>= 9), + dh-autoreconf, + dh-translations, + 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, +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 +# packaging changes, just go ahead. ~indicator-applet-developers will notice +# and sync up the code again. +Vcs-Bzr: https://code.launchpad.net/~indicator-applet-developers/indicator-messages/trunk.13.04 +Vcs-Browser: https://bazaar.launchpad.net/~indicator-applet-developers/indicator-messages/trunk.13.04/files + +Package: indicator-messages +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Recommends: indicator-applet | indicator-renderer +Replaces: libindicator-messages-status-provider1, + libindicator-messages-status-provider-dev, + indicator-status-provider-mc5, + indicator-status-provider-pidgin, + indicator-status-provider-telepathy, + indicator-status-provider-emesene, +Conflicts: libindicator-messages-status-provider1, + libindicator-messages-status-provider-dev, + indicator-status-provider-mc5, + indicator-status-provider-pidgin, + indicator-status-provider-telepathy, + indicator-status-provider-emesene, +Breaks: gwibber-services (<< 3.5.4-0ubuntu1~), + telepathy-indicator (<< 0.3.0-0ubuntu1~), + thunderbird (<< 15.0+build1-0ubuntu1~), + xchat-indicator (<< 0.3.11-0ubuntu2~), + xchat-gnome-indicator (<< 0.3.11-0ubuntu2~), +Description: indicator that collects messages that need a response + A place on the user's desktop that collects messages that need a response. + This menu provides a condensed and collected view of all of those messages + for quick access, but without making them annoying in times that you want + to ignore them. + +Package: libmessaging-menu0 +Architecture: any +Section: libs +Depends: ${shlibs:Depends}, + ${misc:Depends}, +Description: Messaging Menu - shared library + This library contains information to build status providers to go into + the messaging menu. + +Package: libmessaging-menu-dev +Architecture: any +Section: libdevel +Depends: ${shlibs:Depends}, + ${misc:Depends}, + libmessaging-menu0 (=${binary:Version}), + libglib2.0-dev, +Description: Messaging Menu - library development files + This library contains information to build status providers to go into + the messaging menu. + +Package: gir1.2-messagingmenu-1.0 +Section: libs +Architecture: any +Depends: ${misc:Depends}, + ${gir:Depends}, +Description: Messaging Menu - gir bindings + . + This package can be used by other packages using the GIRepository format + to generate dynamic bindings. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..ba41392 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,19 @@ +Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135 +Name: indicator-messages +Maintainer: Ted Gould <ted@canonical.com> +Source: https://launchpad.net/indicator-messages + +Files: * +Copyright: 2009-2010 Ted Gould <ted@canonical.com>, Canonical Ltd. +License: GPL-3 + +Files: debian/* +Copyright: 2009-2010 Evgeni Golov <evgeni@debian.org> +Copyright: 2009-2010 Ted Gould <ted@canonical.com>, Canonical Ltd. +Copyright: 2009-2010 Sebastien Bacher <seb128@ubuntu.com> +Copyright: 2009-2010 Ken VanDine <ken.vandine@canonical.com> +License: GPL-3 + +License: GPL-3 + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in `/usr/share/common-licenses/GPL-3'. diff --git a/debian/gir1.2-messagingmenu-1.0.install b/debian/gir1.2-messagingmenu-1.0.install new file mode 100644 index 0000000..c1d8cfe --- /dev/null +++ b/debian/gir1.2-messagingmenu-1.0.install @@ -0,0 +1 @@ +usr/lib/girepository-1.0/*.typelib diff --git a/debian/indicator-messages.install b/debian/indicator-messages.install new file mode 100644 index 0000000..c0f6e98 --- /dev/null +++ b/debian/indicator-messages.install @@ -0,0 +1,5 @@ +usr/lib/indicators3 +usr/lib/indicator-messages/indicator-messages-service +usr/share/dbus-1 +usr/share/glib-2.0 +usr/share/icons diff --git a/debian/libmessaging-menu-dev.install b/debian/libmessaging-menu-dev.install new file mode 100644 index 0000000..75ef089 --- /dev/null +++ b/debian/libmessaging-menu-dev.install @@ -0,0 +1,5 @@ +usr/lib/libmessaging-menu.so +usr/lib/pkgconfig +usr/include /usr/ +usr/share/gir-1.0/ +usr/share/gtk-doc diff --git a/debian/libmessaging-menu0.install b/debian/libmessaging-menu0.install new file mode 100644 index 0000000..54b098e --- /dev/null +++ b/debian/libmessaging-menu0.install @@ -0,0 +1 @@ +usr/lib/libmessaging-menu.so.* /usr/lib/ diff --git a/debian/libmessaging-menu0.symbols b/debian/libmessaging-menu0.symbols new file mode 100644 index 0000000..d5eaed1 --- /dev/null +++ b/debian/libmessaging-menu0.symbols @@ -0,0 +1,38 @@ +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 0replaceme + 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 + messaging_menu_app_insert_source_with_count@Base 12.10.0 + messaging_menu_app_insert_source_with_string@Base 12.10.0 + messaging_menu_app_insert_source_with_time@Base 12.10.0 + 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 + messaging_menu_app_set_source_label@Base 12.10.2 + messaging_menu_app_set_source_string@Base 12.10.0 + 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/rules b/debian/rules new file mode 100755 index 0000000..649b0ad --- /dev/null +++ b/debian/rules @@ -0,0 +1,17 @@ +#!/usr/bin/make -f + +export DPKG_GENSYMBOLS_CHECK_LEVEL=4 + +%: + dh $@ --with autoreconf,translations,gir + +override_dh_autoreconf: + NO_CONFIGURE=1 dh_autoreconf ./autogen.sh + +override_dh_auto_configure: + dh_auto_configure -- --libdir="\$${prefix}/lib" --libexecdir="\$${prefix}/lib/indicator-messages" --enable-gtk-doc + +override_dh_install: + find debian/tmp/usr/lib -name *.la -delete + dh_install --fail-missing + diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..d3827e7 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +1.0 diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..a744070 --- /dev/null +++ b/debian/watch @@ -0,0 +1,2 @@ +version=3 +http://launchpad.net/indicator-messages/+download .*/indicator-messages-([0-9.]+)\.tar\.gz diff --git a/doc/reference/Makefile.am b/doc/reference/Makefile.am index 023f1e7..3993b41 100644 --- a/doc/reference/Makefile.am +++ b/doc/reference/Makefile.am @@ -4,7 +4,6 @@ DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml DOC_SOURCE_DIR = $(top_srcdir)/libmessaging-menu -SCANGOBJ_OPTIONS=--type-init-func="g_type_init()" MKDB_OPTIONS=--xml-mode --output-format=xml # Used for dependencies. The docs will be rebuilt if any of these change. @@ -12,8 +11,7 @@ HFILE_GLOB = $(top_srcdir)/libmessaging-menu/*.h CFILE_GLOB = $(top_srcdir)/libmessaging-menu/*.c IGNORE_HFILES= \ - indicator-messages-service.h \ - gtupleaction.h + indicator-messages-service.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 742d37b..4c1cec1 100644 --- a/doc/reference/messaging-menu-docs.xml.in +++ b/doc/reference/messaging-menu-docs.xml.in @@ -18,7 +18,8 @@ <chapter> <title>API Reference</title> - <xi:include href="xml/messaging-menu.xml"/> + <xi:include href="xml/messaging-menu-app.xml" /> + <xi:include href="xml/messaging-menu-message.xml" /> </chapter> <chapter id="object-tree"> diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am index 7a6ee31..d18538b 100644 --- a/libmessaging-menu/Makefile.am +++ b/libmessaging-menu/Makefile.am @@ -4,36 +4,25 @@ lib_LTLIBRARIES = libmessaging-menu.la libmessaging_menu_ladir = $(includedir)/messaging-menu libmessaging_menu_la_SOURCES = \ - messaging-menu.c \ - gtupleaction.c \ - gtupleaction.h \ - $(BUILT_SOURCES) + messaging-menu-app.c \ + messaging-menu-message.c libmessaging_menu_la_HEADERS = \ - messaging-menu.h + messaging-menu-app.h \ + messaging-menu.h \ + messaging-menu-message.h -libmessaging_menu_la_LIBADD = $(GIO_LIBS) +libmessaging_menu_la_LIBADD = \ + $(GIO_LIBS) \ + $(top_builddir)/common/libmessaging-common.la 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 @@ -52,7 +41,11 @@ 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.c messaging-menu.h +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_EXPORT_PACKAGES = messaging-menu INTROSPECTION_GIRS += MessagingMenu-1.0.gir @@ -62,5 +55,5 @@ gir_DATA = $(INTROSPECTION_GIRS) typelibdir = $(libdir)/girepository-1.0 typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) -CLEANFILES +=$(gir_DATA) $(typelib_DATA) +CLEANFILES = $(gir_DATA) $(typelib_DATA) endif diff --git a/libmessaging-menu/gtupleaction.c b/libmessaging-menu/gtupleaction.c deleted file mode 100644 index 21bc003..0000000 --- a/libmessaging-menu/gtupleaction.c +++ /dev/null @@ -1,354 +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 "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 deleted file mode 100644 index c447d71..0000000 --- a/libmessaging-menu/gtupleaction.h +++ /dev/null @@ -1,40 +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 __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.c b/libmessaging-menu/messaging-menu-app.c index 70861d3..c841db2 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -17,11 +17,12 @@ * Lars Uebernickel <lars.uebernickel@canonical.com> */ -#include "messaging-menu.h" +#include "messaging-menu-app.h" #include "indicator-messages-service.h" -#include "gtupleaction.h" +#include "indicator-messages-application.h" #include <gio/gdesktopappinfo.h> +#include <string.h> /** * SECTION:messaging-menu @@ -103,8 +104,11 @@ 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; @@ -114,14 +118,6 @@ struct _MessagingMenuApp G_DEFINE_TYPE (MessagingMenuApp, messaging_menu_app, G_TYPE_OBJECT); -enum -{ - INDEX_COUNT, - INDEX_TIME, - INDEX_STRING, - INDEX_DRAWS_ATTENTION -}; - enum { PROP_0, PROP_DESKTOP_ID, @@ -139,10 +135,61 @@ 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) { @@ -161,49 +208,32 @@ messaging_menu_app_get_dbus_object_path (MessagingMenuApp *app) } static void -export_menus_and_actions (GObject *source, - GAsyncResult *res, - gpointer user_data) +messaging_menu_app_got_bus (GObject *source, + GAsyncResult *res, + gpointer user_data) { MessagingMenuApp *app = user_data; - GDBusConnection *bus; GError *error = NULL; - guint id; gchar *object_path; - object_path = messaging_menu_app_get_dbus_object_path (app); - if (!object_path) - return; - - bus = g_bus_get_finish (res, &error); - if (bus == NULL) + app->bus = g_bus_get_finish (res, &error); + if (app->bus == NULL) { g_warning ("unable to connect to session bus: %s", error->message); g_error_free (error); return; } - id = g_dbus_connection_export_action_group (bus, - object_path, - G_ACTION_GROUP (app->source_actions), - &error); - if (!id) - { - g_warning ("unable to export action group: %s", error->message); - g_error_free (error); - } + object_path = messaging_menu_app_get_dbus_object_path (app); - id = g_dbus_connection_export_menu_model (bus, - object_path, - G_MENU_MODEL (app->menu), - &error); - if (!id) + if (object_path && + !g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (app->app_interface), + app->bus, object_path, &error)) { - g_warning ("unable to export menu: %s", error->message); - g_error_free (error); + g_warning ("unable to export application interface: %s", error->message); + g_clear_error (&error); } - g_object_unref (bus); g_free (object_path); } @@ -223,7 +253,7 @@ messaging_menu_app_set_desktop_id (MessagingMenuApp *app, g_bus_get (G_BUS_TYPE_SESSION, app->cancellable, - export_menus_and_actions, + messaging_menu_app_got_bus, app); } @@ -278,9 +308,14 @@ 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->source_actions); - g_clear_object (&app->menu); + g_clear_object (&app->bus); G_OBJECT_CLASS (messaging_menu_app_parent_class)->dispose (object); } @@ -411,18 +446,190 @@ 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", ¶m); + 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) { app->registered = -1; app->status_set = FALSE; + app->bus = NULL; app->cancellable = g_cancellable_new (); - app->source_actions = g_simple_action_group_new (); - app->menu = g_menu_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->cancellable = g_cancellable_new (); + app->messages = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); app->watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, "com.canonical.indicator.messages", @@ -595,77 +802,73 @@ global_status_changed (IndicatorMessagesService *service, g_signal_emit (app, signals[STATUS_CHANGED], 0, status); } -static void -source_action_activated (GTupleAction *action, - GVariant *parameter, - gpointer user_data) +static Source * +messaging_menu_app_lookup_source (MessagingMenuApp *app, + const gchar *id) { - MessagingMenuApp *app = user_data; - const gchar *name = g_action_get_name (G_ACTION (action)); - GQuark q = g_quark_from_string (name); + GList *node; - messaging_menu_app_remove_source (app, name); + node = g_list_find_custom (app->sources, id, compare_source_id); - g_signal_emit (app, signals[ACTIVATE_SOURCE], q, name); + return node ? node->data : NULL; } -static void -messaging_menu_app_insert_source_action (MessagingMenuApp *app, - gint position, - const gchar *id, - GIcon *icon, - const gchar *label, - GVariant *state) +static Source * +messaging_menu_app_get_source (MessagingMenuApp *app, + const gchar *id) { - GTupleAction *action; - GMenuItem *menuitem; + Source *source; - g_return_if_fail (MESSAGING_MENU_IS_APP (app)); - g_return_if_fail (id != NULL); + source = messaging_menu_app_lookup_source (app, id); + if (!source) + g_warning ("a source with id '%s' doesn't exist", id); - if (g_simple_action_group_lookup (app->source_actions, id)) - { - g_warning ("a source with id '%s' already exists", id); - return; - } - - action = g_tuple_action_new (id, 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 source; +} - menuitem = g_menu_item_new (label, id); - 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 void +messaging_menu_app_notify_source_changed (MessagingMenuApp *app, + Source *source) +{ + indicator_messages_application_emit_source_changed (app->app_interface, + source_to_variant (source)); } static void -messaging_menu_app_set_source_action (MessagingMenuApp *app, - const gchar *source_id, - gsize index, - GVariant *child) +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) { - GAction *action; + Source *source; g_return_if_fail (MESSAGING_MENU_IS_APP (app)); - g_return_if_fail (source_id != NULL); + g_return_if_fail (id != NULL); + g_return_if_fail (label != NULL); - action = g_simple_action_group_lookup (app->source_actions, source_id); - if (action == NULL) + if (messaging_menu_app_lookup_source (app, id)) { - g_warning ("a source with id '%s' doesn't exist", source_id); + g_warning ("a source with id '%s' already exists", id); return; } - g_tuple_action_set_child (G_TUPLE_ACTION (action), index, child); + 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)); } /** @@ -742,8 +945,7 @@ messaging_menu_app_insert_source_with_count (MessagingMenuApp *app, const gchar *label, guint count) { - messaging_menu_app_insert_source_action (app, position, id, icon, label, - g_variant_new ("(uxsb)", count, 0, "", FALSE)); + messaging_menu_app_insert_source_internal (app, position, id, icon, label, count, 0, ""); } /** @@ -797,8 +999,7 @@ messaging_menu_app_insert_source_with_time (MessagingMenuApp *app, const gchar *label, gint64 time) { - messaging_menu_app_insert_source_action (app, position, id, icon, label, - g_variant_new ("(uxsb)", 0, time, "", FALSE)); + messaging_menu_app_insert_source_internal (app, position, id, icon, label, 0, time, ""); } /** @@ -854,8 +1055,7 @@ messaging_menu_app_insert_source_with_string (MessagingMenuApp *app, const gchar *label, const gchar *str) { - messaging_menu_app_insert_source_action (app, position, id, icon, label, - g_variant_new ("(uxsb)", 0, 0, str, FALSE)); + messaging_menu_app_insert_source_internal (app, position, id, icon, label, 0, 0, str); } /** @@ -895,34 +1095,11 @@ 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 (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); + if (messaging_menu_app_remove_source_internal (app, source_id)) + indicator_messages_application_emit_source_removed (app->app_interface, source_id); } /** @@ -939,46 +1116,7 @@ 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 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); + return messaging_menu_app_lookup_source (app, source_id) != NULL; } /** @@ -994,28 +1132,26 @@ messaging_menu_app_set_source_label (MessagingMenuApp *app, const gchar *source_id, const gchar *label) { - gint pos; - GMenuItem *item; + Source *source; g_return_if_fail (MESSAGING_MENU_IS_APP (app)); g_return_if_fail (source_id != NULL); g_return_if_fail (label != NULL); - 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); + 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); + } } /** * messaging_menu_app_set_source_icon: * @app: a #MessagingMenuApp * @source_id: a source id - * @icon: the new icon for the source + * @icon: (allow-none): the new icon for the source * * Changes the icon of @source_id to @icon. */ @@ -1024,23 +1160,19 @@ messaging_menu_app_set_source_icon (MessagingMenuApp *app, const gchar *source_id, GIcon *icon) { - gint pos; - GMenuItem *item; - gchar *iconstr; + Source *source; g_return_if_fail (MESSAGING_MENU_IS_APP (app)); g_return_if_fail (source_id != NULL); - item = g_menu_find_item_with_action (app->menu, source_id, &pos); - if (item == NULL) - return; - - iconstr = icon ? g_icon_to_string (icon) : NULL; - g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr); - g_menu_replace_item (app->menu, pos, item); - - g_free (iconstr); - g_object_unref (item); + source = messaging_menu_app_get_source (app, source_id); + if (source) + { + g_clear_object (&source->icon); + if (icon) + source->icon = g_object_ref (icon); + messaging_menu_app_notify_source_changed (app, source); + } } /** @@ -1055,8 +1187,17 @@ void messaging_menu_app_set_source_count (MessagingMenuApp *app, const gchar *source_id, guint count) { - messaging_menu_app_set_source_action (app, source_id, INDEX_COUNT, - g_variant_new_uint32 (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); + } } /** @@ -1066,17 +1207,23 @@ void messaging_menu_app_set_source_count (MessagingMenuApp *app, * @time: the new time for the source, in microseconds * * Updates the time of @source_id to @time. - * - * Note that the time is only displayed if the source does not also have a - * count associated with it. */ void messaging_menu_app_set_source_time (MessagingMenuApp *app, const gchar *source_id, gint64 time) { - messaging_menu_app_set_source_action (app, source_id, INDEX_TIME, - g_variant_new_int64 (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); + } } /** @@ -1086,17 +1233,24 @@ messaging_menu_app_set_source_time (MessagingMenuApp *app, * @str: the new string for the source * * Updates the string displayed next to @source_id to @str. - * - * Note that the string is only displayed if the source does not also have a - * count or time associated with it. */ void messaging_menu_app_set_source_string (MessagingMenuApp *app, const gchar *source_id, const gchar *str) { - messaging_menu_app_set_source_action (app, source_id, INDEX_STRING, - g_variant_new_string (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); + } } /** @@ -1114,8 +1268,17 @@ void messaging_menu_app_draw_attention (MessagingMenuApp *app, const gchar *source_id) { - messaging_menu_app_set_source_action (app, source_id, INDEX_DRAWS_ATTENTION, - g_variant_new_boolean (TRUE)); + 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); + } } /** @@ -1136,6 +1299,131 @@ void messaging_menu_app_remove_attention (MessagingMenuApp *app, const gchar *source_id) { - messaging_menu_app_set_source_action (app, source_id, INDEX_DRAWS_ATTENTION, - g_variant_new_boolean (FALSE)); + 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); } diff --git a/libmessaging-menu/messaging-menu-app.h b/libmessaging-menu/messaging-menu-app.h new file mode 100644 index 0000000..c8097e1 --- /dev/null +++ b/libmessaging-menu/messaging-menu-app.h @@ -0,0 +1,163 @@ +/* + * 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 new file mode 100644 index 0000000..b81cbea --- /dev/null +++ b/libmessaging-menu/messaging-menu-message.c @@ -0,0 +1,547 @@ +/* + * 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 new file mode 100644 index 0000000..4708246 --- /dev/null +++ b/libmessaging-menu/messaging-menu-message.h @@ -0,0 +1,70 @@ +/* + * 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.h b/libmessaging-menu/messaging-menu.h index 6c405c7..929b229 100644 --- a/libmessaging-menu/messaging-menu.h +++ b/libmessaging-menu/messaging-menu.h @@ -20,129 +20,6 @@ #ifndef __messaging_menu_h__ #define __messaging_menu_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 +#include "messaging-menu-app.h" #endif 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 1df80e5..5d8bad8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,8 +1,5 @@ -BUILT_SOURCES = EXTRA_DIST = -CLEANFILES = -DISTCLEANFILES = libexec_PROGRAMS = indicator-messages-service @@ -23,19 +20,20 @@ libmessaging_la_SOURCES = \ 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) \ + -I$(top_builddir)/common \ -Wall \ -Wl,-Bsymbolic-functions \ -Wl,-z,defs \ -Wl,--as-needed \ -Werror \ -DG_LOG_DOMAIN=\"Indicator-Messages\" -libmessaging_la_LIBADD = $(APPLET_LIBS) -lm +libmessaging_la_LIBADD = \ + $(top_builddir)/common/libmessaging-common.la \ + $(APPLET_LIBS) -lm libmessaging_la_LDFLAGS = \ $(COVERAGE_LDFLAGS) \ -module -avoid-version @@ -46,8 +44,6 @@ libmessaging_la_LDFLAGS = \ indicator_messages_service_SOURCES = \ messages-service.c \ - indicator-messages-service.c \ - indicator-messages-service.h \ app-section.c \ app-section.h \ dbus-data.h \ @@ -56,11 +52,16 @@ indicator_messages_service_SOURCES = \ gsettingsstrv.c \ gsettingsstrv.h \ gmenuutils.c \ - gmenuutils.h + gmenuutils.h \ + im-phone-menu.c \ + im-phone-menu.h \ + im-application-list.c \ + im-application-list.h indicator_messages_service_CFLAGS = \ $(APPLET_CFLAGS) \ $(COVERAGE_CFLAGS) \ + -I$(top_builddir)/common \ -Wall \ -Wl,-Bsymbolic-functions \ -Wl,-z,defs \ @@ -69,26 +70,11 @@ 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 823d75a..3a55147 100644 --- a/src/app-section.c +++ b/src/app-section.c @@ -33,6 +33,7 @@ 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 { @@ -41,11 +42,14 @@ struct _AppSectionPrivate IndicatorDesktopShortcuts * ids; + GCancellable *app_proxy_cancellable; + IndicatorMessagesApplication *app_proxy; + GMenu *menu; - GMenuModel *source_menu; + GMenu *source_menu; GSimpleActionGroup *static_shortcuts; - GActionGroup *source_actions; + GSimpleActionGroup *source_actions; GActionMuxer *muxer; gboolean draws_attention; @@ -89,19 +93,6 @@ 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, @@ -169,6 +160,7 @@ static void app_section_init (AppSection *self) { AppSectionPrivate *priv; + GMenuItem *item; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, APP_SECTION_TYPE, @@ -178,10 +170,19 @@ 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; @@ -248,32 +249,30 @@ 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); @@ -429,6 +428,11 @@ 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); @@ -564,49 +568,242 @@ app_section_get_draws_attention (AppSection *self) void app_section_clear_draws_attention (AppSection *self) { - AppSectionPrivate * priv = self->priv; - gchar **action_names; + 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; 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 (priv->source_actions == NULL) + 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); return; + } - action_names = g_action_group_list_actions (priv->source_actions); + g_menu_clear (priv->source_menu); + g_simple_action_group_clear (priv->source_actions); + priv->draws_attention = FALSE; - for (it = action_names; *it; it++) { + g_variant_iter_init (&iter, sources); + while (g_variant_iter_next (&iter, "(&s&s&sux&sb)", &id, &label, &iconstr, + &count, &time, &string, &draws_attention)) + { GVariant *state; + GSimpleAction *action; + GMenuItem *item; - state = g_action_group_get_action_state (priv->source_actions, *it); - if (!state) - continue; + 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)); - /* clear draws-attention while preserving other state */ - if (action_draws_attention (state)) { - guint32 count; - gint64 time; - const gchar *str; - GVariant *new_state; + 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); - g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL); + priv->draws_attention = priv->draws_attention || draws_attention; - new_state = g_variant_new ("(uxsb)", count, time, str, FALSE); - g_action_group_change_action_state (priv->source_actions, *it, new_state); - } + g_object_unref (item); + g_object_unref (action); + } + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); + + g_variant_unref (sources); +} + +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 */ - g_variant_unref (state); + 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)); + + if (draws_attention && !priv->draws_attention) { + priv->draws_attention = TRUE; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); } - g_strfreev (action_names); + 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); } static void -application_vanished (GDBusConnection *bus, - const gchar *name, - gpointer user_data) +source_removed (IndicatorMessagesApplication *app, + const gchar *id, + gpointer user_data) { AppSection *self = user_data; - app_section_unset_object_path (self); + 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); } /* @@ -627,27 +824,20 @@ 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->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 = 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->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_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->draws_attention = FALSE; priv->name_watch_id = g_bus_watch_name_on_connection (bus, bus_name, 0, NULL, application_vanished, @@ -675,26 +865,19 @@ 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; } - 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); - } + g_simple_action_group_clear (priv->source_actions); + g_menu_clear (priv->source_menu); priv->draws_attention = FALSE; g_clear_pointer (&priv->chat_status, g_free); @@ -708,85 +891,6 @@ 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 64747a9..59bd305 100644 --- a/src/dbus-data.h +++ b/src/dbus-data.h @@ -1,9 +1,24 @@ +/* + * 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/menu" +#define INDICATOR_MESSAGES_DBUS_OBJECT "/com/canonical/indicator/messages" #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 2b1d11a..0f3cda4 100644 --- a/src/gactionmuxer.c +++ b/src/gactionmuxer.c @@ -483,3 +483,11 @@ 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 5c5e839..caf9ec7 100644 --- a/src/gactionmuxer.h +++ b/src/gactionmuxer.h @@ -40,5 +40,8 @@ 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/im-application-list.c b/src/im-application-list.c new file mode 100644 index 0000000..9a673c9 --- /dev/null +++ b/src/im-application-list.c @@ -0,0 +1,880 @@ +/* + * 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; +}; + +G_DEFINE_TYPE (ImApplicationList, im_application_list, G_TYPE_OBJECT); + +enum +{ + SOURCE_ADDED, + SOURCE_CHANGED, + SOURCE_REMOVED, + MESSAGE_ADDED, + MESSAGE_REMOVED, + APP_STOPPED, + REMOVE_ALL, + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + +typedef struct +{ + ImApplicationList *list; + GDesktopAppInfo *info; + gchar *id; + IndicatorMessagesApplication *proxy; + GActionMuxer *actions; + GSimpleActionGroup *source_actions; + GSimpleActionGroup *message_actions; + GActionMuxer *message_sub_actions; + GCancellable *cancellable; +} Application; + +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->actions) + { + g_object_unref (app->actions); + 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 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); +} + +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 guint +g_action_group_get_n_actions (GActionGroup *group) +{ + guint len; + gchar **actions; + + actions = g_action_group_list_actions (group); + len = g_strv_length (actions); + + g_strfreev (actions); + return len; +} + +static gboolean +application_draws_attention (gpointer key, + gpointer value, + gpointer user_data) +{ + Application *app = value; + + return (g_action_group_get_n_actions (G_ACTION_GROUP (app->source_actions)) + + g_action_group_get_n_actions (G_ACTION_GROUP (app->message_actions))) > 0; +} + +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); +} + +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; + + 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); + + 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); + } +} + +static void +im_application_list_dispose (GObject *object) +{ + ImApplicationList *list = IM_APPLICATION_LIST (object); + + 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_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); +} + +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 } + }; + + GSimpleActionGroup *actions; + + list->applications = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, application_free); + + actions = g_simple_action_group_new (); + g_simple_action_group_add_entries (actions, action_entries, G_N_ELEMENTS (action_entries), list); + + list->muxer = g_action_muxer_new (); + g_action_muxer_insert (list->muxer, NULL, G_ACTION_GROUP (actions)); + + g_object_unref (actions); +} + +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_add (ImApplicationList *list, + const gchar *desktop_id) +{ + GDesktopAppInfo *info; + Application *app; + const gchar *id; + + 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->actions = 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 (); + + g_action_muxer_insert (app->actions, "src", G_ACTION_GROUP (app->source_actions)); + g_action_muxer_insert (app->actions, "msg", G_ACTION_GROUP (app->message_actions)); + g_action_muxer_insert (app->actions, "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->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); + } +} + +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); + + 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); +} + +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_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); + } + + 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->actions, "src", G_ACTION_GROUP (app->source_actions)); + g_action_muxer_insert (app->actions, "msg", G_ACTION_GROUP (app->message_actions)); + g_action_muxer_insert (app->actions, "msg-actions", G_ACTION_GROUP (app->message_sub_actions)); + + im_application_list_update_draws_attention (app->list); + + 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_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); +} diff --git a/src/im-application-list.h b/src/im-application-list.h new file mode 100644 index 0000000..ecba312 --- /dev/null +++ b/src/im-application-list.h @@ -0,0 +1,52 @@ +/* + * 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> + +#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); + +#endif diff --git a/src/im-phone-menu.c b/src/im-phone-menu.c new file mode 100644 index 0000000..3d5f94d --- /dev/null +++ b/src/im-phone-menu.c @@ -0,0 +1,331 @@ +/* + * 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 GObjectClass ImPhoneMenuClass; + +struct _ImPhoneMenu +{ + GObject parent; + + GMenu *toplevel_menu; + GMenu *message_section; + GMenu *source_section; + +}; + +G_DEFINE_TYPE (ImPhoneMenu, im_phone_menu, G_TYPE_OBJECT); + +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_update_toplevel (ImPhoneMenu *menu) +{ + if (g_menu_model_get_n_items (G_MENU_MODEL (menu->message_section)) || + g_menu_model_get_n_items (G_MENU_MODEL (menu->source_section))) + { + if (g_menu_model_get_n_items (G_MENU_MODEL (menu->toplevel_menu)) == 0) + { + GMenuItem *item; + + g_menu_append_section (menu->toplevel_menu, NULL, G_MENU_MODEL (menu->message_section)); + g_menu_append_section (menu->toplevel_menu, NULL, G_MENU_MODEL (menu->source_section)); + + 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 (menu->toplevel_menu, item); + + g_object_unref (item); + } + } + else + { + while (g_menu_model_get_n_items (G_MENU_MODEL (menu->toplevel_menu))) + g_menu_remove (menu->toplevel_menu, 0); + } +} + +static void +im_phone_menu_dispose (GObject *object) +{ + ImPhoneMenu *menu = IM_PHONE_MENU (object); + + g_clear_object (&menu->toplevel_menu); + 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->dispose = im_phone_menu_dispose; + object_class->finalize = im_phone_menu_finalize; +} + +static void +im_phone_menu_init (ImPhoneMenu *menu) +{ + menu->toplevel_menu = g_menu_new (); + menu->message_section = g_menu_new (); + menu->source_section = g_menu_new (); + + g_signal_connect_swapped (menu->message_section, "items-changed", + G_CALLBACK (im_phone_menu_update_toplevel), menu); + g_signal_connect_swapped (menu->source_section, "items-changed", + G_CALLBACK (im_phone_menu_update_toplevel), menu); + + im_phone_menu_update_toplevel (menu); +} + +ImPhoneMenu * +im_phone_menu_new (void) +{ + return g_object_new (IM_TYPE_PHONE_MENU, NULL); +} + +GMenuModel * +im_phone_menu_get_model (ImPhoneMenu *menu) +{ + g_return_val_if_fail (IM_IS_PHONE_MENU (menu), NULL); + + return G_MENU_MODEL (menu->toplevel_menu); +} + +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, "x-canonical-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->toplevel_menu))) + g_menu_remove (menu->toplevel_menu, 0); + + g_object_unref (menu->message_section); + g_object_unref (menu->source_section); + + menu->message_section = g_menu_new (); + menu->source_section = g_menu_new (); + + g_signal_connect_swapped (menu->message_section, "items-changed", + G_CALLBACK (im_phone_menu_update_toplevel), menu); + g_signal_connect_swapped (menu->source_section, "items-changed", + G_CALLBACK (im_phone_menu_update_toplevel), menu); +} diff --git a/src/im-phone-menu.h b/src/im-phone-menu.h new file mode 100644 index 0000000..258ce73 --- /dev/null +++ b/src/im-phone-menu.h @@ -0,0 +1,70 @@ +/* + * 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 <gio/gio.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 (void); + +GMenuModel * im_phone_menu_get_model (ImPhoneMenu *menu); + +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/messages-service.c b/src/messages-service.c index 8f64478..5f320fc 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -23,418 +23,25 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <config.h> #include <locale.h> -#include <libindicator/indicator-service.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-application-list.h" #define NUM_STATUSES 5 -static GHashTable *applications; +static ImApplicationList *applications; static IndicatorMessagesService *messages_service; -static GSimpleActionGroup *actions; -static GActionMuxer *action_muxer; static GMenu *toplevel_menu; -static GMenu *menu; -static GMenuModel *chat_section; +static ImPhoneMenu *menu; 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) §ion) && - 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, @@ -443,18 +50,15 @@ register_application (IndicatorMessagesService *service, const gchar *menu_path, gpointer user_data) { - AppSection *section; GDBusConnection *bus; const gchar *sender; - section = add_application (desktop_id); - if (!section) - return; + im_application_list_add (applications, desktop_id); bus = g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (service)); sender = g_dbus_method_invocation_get_sender (invocation); - app_section_set_object_path (section, bus, sender, menu_path); + im_application_list_set_remote (applications, desktop_id, bus, sender, menu_path); g_settings_strv_append_unique (settings, "applications", desktop_id); indicator_messages_service_complete_register_application (service, invocation); @@ -466,137 +70,29 @@ unregister_application (IndicatorMessagesService *service, const gchar *desktop_id, gpointer user_data) { - remove_application (desktop_id); + im_application_list_remove (applications, desktop_id); g_settings_strv_remove (settings, "applications", desktop_id); indicator_messages_service_complete_unregister_application (service, invocation); } static void -set_status (IndicatorMessagesService *service, - GDBusMethodInvocation *invocation, - const gchar *desktop_id, - const gchar *status_str, - gpointer user_data) +on_bus_acquired (GDBusConnection *bus, + const gchar *name, + gpointer user_data) { - GDesktopAppInfo *appinfo; - gchar *id; - AppSection *section; - - g_return_if_fail (g_str_equal (status_str, "available") || - g_str_equal (status_str, "away")|| - g_str_equal (status_str, "busy") || - g_str_equal (status_str, "invisible") || - g_str_equal (status_str, "offline")); - - appinfo = g_desktop_app_info_new (desktop_id); - if (!appinfo) { - g_warning ("could not set status for '%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 != NULL) - app_section_set_status (section, status_str); - - indicator_messages_service_complete_set_status (service, invocation); - - g_free (id); - g_object_unref (appinfo); -} - -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) -{ - 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 -got_bus (GObject *object, - GAsyncResult * res, - gpointer user_data) -{ - GDBusConnection *bus; GError *error = NULL; - 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, - G_ACTION_GROUP (action_muxer), &error); + im_application_list_get_action_group (applications), + &error); if (error) { g_warning ("unable to export action group on dbus: %s", error->message); g_error_free (error); return; } - g_dbus_connection_export_menu_model (bus, INDICATOR_MESSAGES_DBUS_OBJECT, + g_dbus_connection_export_menu_model (bus, INDICATOR_MESSAGES_DBUS_OBJECT "/phone", G_MENU_MODEL (toplevel_menu), &error); if (error) { g_warning ("unable to export menu on dbus: %s", error->message); @@ -616,17 +112,29 @@ got_bus (GObject *object, 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; - IndicatorService * service = NULL; + GMenuItem *root; + GBusNameOwnerFlags flags; - mainloop = g_main_loop_new (NULL, FALSE); + /* Glib init */ +#if G_ENCODE_VERSION(GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION) <= GLIB_VERSION_2_34 + g_type_init(); +#endif - /* 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); + mainloop = g_main_loop_new (NULL, FALSE); /* Setting up i18n and gettext. Apparently, we need all of these. */ @@ -637,38 +145,44 @@ main (int argc, char ** argv) /* Bring up the service DBus interface */ messages_service = indicator_messages_service_skeleton_new (); - g_bus_get (G_BUS_TYPE_SESSION, NULL, got_bus, NULL); + flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT; + if (argc >= 2 && g_str_equal (argv[1], "--replace")) + flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE; - actions = create_action_group (); - - action_muxer = g_action_muxer_new (); - g_action_muxer_insert (action_muxer, NULL, G_ACTION_GROUP (actions)); + g_bus_own_name (G_BUS_TYPE_SESSION, "com.canonical.indicator.messages", flags, + on_bus_acquired, NULL, on_name_lost, mainloop, NULL); 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-set-status", - G_CALLBACK (set_status), NULL); - menu = g_menu_new (); - chat_section = create_status_section (); - g_menu_append (menu, _("Clear"), "clear"); + menu = im_phone_menu_new (); toplevel_menu = g_menu_new (); + root = g_menu_item_new (NULL, "messages"); + g_menu_item_set_attribute (root, "x-canonical-type", "s", "com.canonical.indicator.root"); + g_menu_item_set_submenu (root, im_phone_menu_get_model (menu)); + g_menu_append_item (toplevel_menu, root); settings = g_settings_new ("com.canonical.indicator.messages"); - applications = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - - g_idle_add(build_launchers, NULL); + applications = im_application_list_new (); + g_signal_connect_swapped (applications, "message-added", + G_CALLBACK (im_phone_menu_add_message), menu); + g_signal_connect_swapped (applications, "message-removed", + G_CALLBACK (im_phone_menu_remove_message), menu); + g_signal_connect_swapped (applications, "app-stopped", + G_CALLBACK (im_phone_menu_remove_application), menu); + g_signal_connect_swapped (applications, "remove-all", + G_CALLBACK (im_phone_menu_remove_all), menu); g_main_loop_run(mainloop); /* Clean up */ + g_object_unref (root); g_object_unref (messages_service); - g_object_unref (chat_section); g_object_unref (settings); - g_hash_table_unref (applications); + g_object_unref (applications); return 0; } diff --git a/test/Makefile.am b/test/Makefile.am index 4671446..8c8c160 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)/src/indicator-messages-service.c \ - $(top_builddir)/src/indicator-messages-service.h \ + $(top_builddir)/common/indicator-messages-service.c \ + $(top_builddir)/common/indicator-messages-service.h \ $(top_srcdir)/src/app-section.c \ $(top_srcdir)/src/app-section.h \ $(top_srcdir)/src/gactionmuxer.c \ @@ -53,6 +53,7 @@ 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 98c6522..f5a26b0 100644 --- a/test/indicator-messages-service-activate.c +++ b/test/indicator-messages-service-activate.c @@ -28,7 +28,9 @@ 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 6304853..efd6e4f 100644 --- a/test/test-gactionmuxer.cpp +++ b/test/test-gactionmuxer.cpp @@ -1,3 +1,21 @@ +/* +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> @@ -25,7 +43,9 @@ 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); @@ -46,7 +66,9 @@ 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 (); @@ -67,7 +89,9 @@ 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, @@ -160,7 +184,9 @@ 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); |