pax_global_header00006660000000000000000000000064135637515060014525gustar00rootroot0000000000000052 comment=43b32e785148b6fec07ade208752d70d17c4cbbb cinnamon-menus-4.4.0/000077500000000000000000000000001356375150600144615ustar00rootroot00000000000000cinnamon-menus-4.4.0/.circleci/000077500000000000000000000000001356375150600163145ustar00rootroot00000000000000cinnamon-menus-4.4.0/.circleci/config.yml000066400000000000000000000030461356375150600203070ustar00rootroot00000000000000version: 2.0 shared: &shared steps: - checkout - run: name: Prepare environment command: apt-get update - run: name: Build project command: mint-build -i - run: name: Prepare packages command: | if [ -z $CI_PULL_REQUEST ]; then mkdir /packages mv /root/*.deb /packages/ git log > /packages/git.log cd / tar zcvf packages.tar.gz packages fi - run: name: Deploy packages to Github command: | if [ -z $CI_PULL_REQUEST ]; then wget https://github.com/tcnksm/ghr/releases/download/v0.5.4/ghr_v0.5.4_linux_amd64.zip apt-get install --yes unzip unzip ghr_v0.5.4_linux_amd64.zip TAG="master".$CIRCLE_JOB ./ghr -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME -replace $TAG /packages.tar.gz ./ghr -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME -recreate -b "Latest unstable packages" $TAG /packages.tar.gz fi jobs: "mint19": <<: *shared docker: - image: linuxmintd/mint19-amd64 "lmde3": <<: *shared docker: - image: linuxmintd/lmde3-amd64 workflows: version: 2 build: jobs: - "mint19" - "lmde3" cinnamon-menus-4.4.0/AUTHORS000066400000000000000000000001411356375150600155250ustar00rootroot00000000000000Mark McLoughlin Havoc Pennington Vincent Untz cinnamon-menus-4.4.0/COPYING000066400000000000000000000432541356375150600155240ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) 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 this service 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 make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. 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. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the 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 a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE 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. 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 convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision 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, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This 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. cinnamon-menus-4.4.0/COPYING.LIB000066400000000000000000000614471356375150600161350ustar00rootroot00000000000000 GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, 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 this service 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 make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! cinnamon-menus-4.4.0/NEWS000066400000000000000000001102331356375150600151600ustar00rootroot00000000000000============= Version 3.8.0 ============= * Add more java things to sundry * Add im-chooser, power statistics and personal file sharing to sundry * Remove special-casing for Fedora vendor prefixdd * Translation updates ============== Version 3.7.90 ============== Bugs Fixed: * Fix error reporting * Memory leak fixes (William John McCann) * Remove the simple editor (William John McCann) * New menu layout for gnome-shell tweaks (William John McCann, Florian Muellner, Jasper St. Pierre) * Remove old gnome-control-center files (Jasper St. Pierre) Translation updates: * Alexandre Franke * Fabio Tomat * Inaki Larranaga Murgoitio * Piotr Drąg * William Jon McCann ============= Version 3.6.2 ============= * 688972 call menu_layout_load() with non_prefixed_name parameter * Translation updates (Arabic, Kannada, Uyghur) ============= Version 3.6.1 ============= * Translation updates (Catalan, Catalan (Valencian), Ukrainian, Aragonese, Slovak, Uzbek) ============= Version 3.6.0 ============= * Translation updates ============== Version 3.5.92 ============== libmenu * Add proper header reference to GMenu-3.0.gir (Rico Tzschichholz) Translators * Nilamdyuti Goswami (as) * Petr Kovar (cs) * Peter Bach (da) * Bruce Cowan (en_GB) * Arash Mousavi (fa) * Jiri Grönroos (fi) * Claude Paroz (fr) * Leandro Regueiro (gl) * Gabor Kelemen (hu) * Dirgita (id) * Milo Casagrande (it) * Changwoo Ryu (ko) * Aurimas Černius (lt) * Sandeep Shedmake (mr) * A S Alam (pa) * Piotr Drąg (pl) * Duarte Loreto (pt) * Daniel Nylander (sv) * Dr.T.Vasudevan (ta) * Theppitak Karoonboonyanan (th) * Muhammet Kara (tr) * Nguyễn Thái Ngọc Duy (vi) * Chrovex Fan (zh_CN) ============= Version 3.5.5 ============= Translations Updates: * Traditional Chinese (Chao-Hsiung Liao) * German (Tobias Endrigkeit) * Kazakh (Baurzhan Muftakhidinov) * Silesian (Przemysław Buczkowski) * Gujarati (Sweta Kothari) * Serbian (Мирослав Николић) ============= Version 3.5.4 ============= Translations Updates * Belarusian (Alexander Shopov) ============= Version 3.5.3 ============= libmenu * Fix scanner warnings (Colin Walters) * Add gmenu_tree_iter_get_separator (Jasper St. Pierre) * Add a recursive NoDisplay getter (Jasper St. Pierre) * Add a way to get a GMenuTree from a GMenuTreeItem (Jasper St. Pierre) * Add a way to get the parent item for a GMenuTreeItem (Jasper St. Pierre) Translations Updates * Belarusian (Ihar Hrachyshka) * Greek (Tom Tryfonidis) * Assamese (Nilamdyuti Goswami) * Telugu (Sasi Bhushan Boddepalli) ============= Version 3.5.2 ============= Layout * Add a separate category for Web Applications (Giovanni Campagna) libmenu * Add a new GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED flag (Vincent Untz) Translations * Bulgarian * Kashubian * Spanish * Galician * Slovenian * Hebrew * Russian * Norwegian bokmål ============= Version 3.4.0 ============= Translators * Morn Met (km) * Bahodir Mansurov (uz@cyrillic) ============= Version 3.3.5 ============= Translators * Jiro Matsuzawa (ja) * Kjartan Maraas (nb) * Danishka Navin (si) * Andiswa Mvanyashe (xh) ============= Version 3.3.1 ============= libmenu * Ignore desktop entries with no Name or Exec key (Florian Müllner) Layout * Put the Other menu at the end (Vincent) =============== Version 3.2.0.1 =============== Menu Editor * Work with latest pygobject (Vincent) ============= Version 3.2.0 ============= Translators * Nilamdyuti Goswami (as) * Praveen Illa (te) ============== Version 3.1.92 ============== libmenu * Fix build failure with --enable-debug (Vincent) Translators * Ігар Грачышка (be) * Daniel Mustieles (es) * Jiro Matsuzawa (ja) ============== Version 3.1.90 ============== libmenu * Don't try to unref potentially NULL pointers (Jasper St. Pierre) ============= Version 3.1.5 ============= This version of gnome-menus comes with a significant API and ABI break, to modernize the code. As a result, the name of the library and the pkg-config filename have changed, so that this new version is parallel-installable with previous ones. As of now, there is no guide to migrate to the new API, but it is rather straight-forward as changes were mostly done to improve the experience for introspection-based bindings. The examples shipped in util/ are a good basis. libmenu * Rebase internal representation of .desktop files on top of GDesktopAppInfo (Colin Walters) * Make GMenuTree a GObject (Colin Walters) * Use GSlice for various data (Colin Walters) * Use thread-default main context for callbacks for future flexibility for thread support (Colin Walters) * Many API changes, see new headers for changes. The most visible one is that gmenu_tree_load_sync() should explicitly be used to load the menu now. (Colin Walters, Vincent) * Drop support for "KDE Desktop Entry" group (Vincent) * Return GIcon instead of char * for icon-related API (Vincent) * Various fixes and cleanups to merge Colin's branch (Vincent, Colin Walters) * Add gmenu_tree_get_entry_by_id() API (Colin Walters) * Support XDG_CURRENT_DESKTOP (Vincent) Menu Editor * Port to introspection-based bindings (Vincent) * Stop editing settings.menu (Vincent) Python * Drop static python bindings; introspection-based ones should be used now (Colin Walters) Misc * Replace example of python code with javascript code (Colin Walters) * Change library name, header directory, pkg-config filename (Vincent) * Require glib 2.29.15 for new API (Vincent) Translators * Ігар Грачышка (be) * Gil Forcada (ca@valencia) * Priscilla Mahlangu (zu) ============= Version 3.0.1 ============= Translators * Anousak Souphavanh (lo) * Theppitak Karoonboonyanan (th) ============= Version 3.0.0 ============= Layout * Show administration tools and old capplets in Other (Vincent) Translators * Khaled Hosny (ar) * Zenat Rahnuma (bn) * Gil Forcada (ca) * Sense Hofstede (fy) * Praveen Illa (te) * Sahran (ug) * Clytie Siddall (vi) =============== Version 2.91.91 =============== Menu Editor * Fix to work with latest pygi (Vincent) Misc * Build fix (Vincent) Translators * F Wolff (af) * Changwoo Ryu (ko) * Pavol Klačanský (sk) * Korostil Daniel (uk) ============== Version 2.91.6 ============== libmenu * Do not send multiple notifications for one file change (Vincent) Menu Editor * Port to pygobject-based introspection bindings (Vincent) * Make editor GTK+ 3 ready (Vincent) Misc * Improve introspection build (Vincent) * Drop settings.menu (Vincent) Translators * Gil Forcada (ca@valencia) * Mattias Põldaru (et) * Mahyar Moghimi (fa) * Fran Diéguez (gl) * Kikongo Translation Team (kg) * Sahran (ug) * Clytie Siddall (vi) ============== Version 2.30.4 ============== libmenu * Clear cache of desktop entries set when files are added/removed (Vincent) Misc * Associate .gir with pkg-config file (Vincent) * Rename --enable-deprecations configure option to --enable-deprecation-flags (Vincent) Translators * Daniel Martinez (an) * Takayuki KUSANO (ja) * Baurzhan Muftakhidinov (kk) * Torstein Winterseth (nn) ============== Version 2.30.3 ============== Menu Editor * Respect XDG_MENU_PREFIX when writing user menu file (Vincent) Misc * Update information in README and other files (Vincent) Translators * Kristjan SCHMIDT (eo) * Sense Hofstede (fy) * Fran Diéguez (gl) * Reuben Potts (gv) * Dirgita (id) * Baurzhan M. (kk) * Sahran (ug) ============== Version 2.30.2 ============== Misc * Do not ship gir files in the tarball (Yaakov Selkowitz) Translators * Daniel Martinez (an) * Gil Forcada (ca) * Gil Forcada (ca@valencia) * Reşat SABIQ (crh) * Christian Kirbach (de) * Thomas Thurman (en@shaw) * Fran Diéguez (gl) * Shankar Prasad (kn) * Peteris Krisjanis (lv) * Wouter Bolsterlee (nl) * Matej Urbančič (sl) ============== Version 2.30.0 ============== libmenu * Fix layout processing for Menuname nodes (Vincent) * Never ignore Menuname nodes from DefaultLayout (Vincent) Misc * Add configure summary (Vincent) Translators * Gil Forcada (ca) * Kostas Papadimas (el) * Iñaki Larrañaga Murgoitio (eu) * Changwoo Ryu (ko) * Badral (mn) =============== Version 2.29.92 =============== libmenu * Add gobject-introspection support (Vincent) Misc * Build fix (Vincent) Translators * Nikos Charonitakis (el) * Pavol Klačanský (sk) =============== Version 2.29.91 =============== Misc * Make some non-visible strings non-translatable (Vincent) * Add translator comment (Vincent) Translators * Fran Diéguez (gl) * Torstein Adolf Winterseth (nn) ============== Version 2.29.6 ============== libmenu * Fix miscalculation for inlining when inline_header = true (Vincent) * Add real support for inline aliases during layout processing (Vincent) * Support inline alias of an inline alias (Vincent) * Do not count non-inlining submenus as inlining with header (Vincent) Translators * Sadia Afroz (bn) * Gil Forcada (ca) * Reşat SABIQ (crh) * Thomas Thurman (en@shaw) * Sveinn í Felli (is) * Nils-Christoph Fiedler (nds) * Dmitry Yacenko (ru) * Krishna Babu K (te) * 甘露(Gan Lu) (zh_CN) ================ Version 2.28.0.1 ================ libmenu * Fix sorting of menu items during merge to actually work (and not crash) (Frédéric Crozat) Python * Link the python module to libpython (Frédéric Crozat, Vincent) ============== Version 2.28.0 ============== Translators * Amitakhya Phukan (as) * Petr Kovar (cs) * Peter Bach (da) * Philip Withnall (en_GB) * Rajesh Ranjan (hi) * Luca Ferretti (it) * Shankar Prasad (kn) * Gintautas Miliauskas (lt) * Rajesh Ranjan (mai) * Peter Ani * Sandeep Shedmake (mr) * Nils-Christoph Fiedler (nds) * A S Alam (pa) * Adi Roiban (ro) * Matej Urbančič (sl) * Daniel Nylander (sv) * Krishna Babu K (te) * Baris Cicek (tr) * Maxim Dziumanenko (uk) * Chao-Hsiung Liao (zh_HK) * Chao-Hsiung Liao (zh_TW) =============== Version 2.27.92 =============== This releases features new API that applications can use to display the full name contained in .desktop files that is now in the X-GNOME-FullName key. If an application chooses to use this, then it should set the sort key so that .desktop files are correctly sorted. libmenu * Add gmenu_tree_entry_get_display_name() API (Vincent) This will return X-GNOME-FullName if available, and fallback to Name. * Add gmenu_tree_get_sort_key()/gmenu_tree_set_sort_key() (Vincent) The default sort key is still Name. Users of gmenu_tree_entry_get_display_name() should use gmenu_tree_set_sort_key(). Python * Bind new API (Vincent) * Add missing bindings for gmenu_tree_entry_get_generic_name() (Vincent) Menu Editor * Remove deprecated Encoding key from desktop file (Frédéric Péters) * Use display name instead of name (Vincent) Translators * Khaled Hosny (ar) * Alexander Shopov (bg) * Runa Bhattacharjee (bn_IN) * Denis (br) * Mario Blättermann (de) * Iñaki Larrañaga Murgoitio (eu) * Claude Paroz (fr) * Seán de Búrca (ga) * Anton Meixome (gl) * Sweta Kothari (gu) * Gabor Kelemen (hu) * Francesco Marletta (it) * Takayuki KUSANO (ja) * Changwoo Ryu (ko) * Kjartan Maraas (nb) * Mario Blättermann (nds) * Manoj Kumar Giri (or) * Tomasz Dominikowski (pl) * Duarte Loreto (pt) * Krix Apolinário (pt_BR) * Горан Ракић (sr) * Goran Rakić (sr@latin) * Dr.T.Vasudevan (ta) * Theppitak Karoonboonyanan (th) * 甘露 (Lu Gan) (zh_CN) ============== Version 2.27.5 ============== Misc * Use silent-rules with automake 1.11 (Vincent) Translators * Ilkka Tuohela (fi) ============== Version 2.27.4 ============== libmenu * Improve performance by using a cache to not compute the same thing again and again (Michael Meeks, Vincent) * Add API to access GenericName (Robert Staudinger) * Fix DefaultLayout attributes not being inherited (Vincent) * Do not always inherit parent DefaultLayout attributes (Vincent) * Sort inlined items unless inline_header is used (Vincent) Menu Editor * Add --help and --version arguments (Vincent) * Port to GtkBuilder (Pedro Fragoso, Vincent) Misc * Use shave to improve build log readability (Vincent) Translators * Jordi Mallach (ca@valencia) * Huxain (dv) * Jorge González (es) * Mattias Põldaru (et) * Seán de Búrca (ga) * Yaron Shahrabani (he) * Daniel Nylander (sv) ============== Version 2.26.1 ============== Translators * Khaled Hosny (ar) * Daniel Nylander (sv) ============== Version 2.26.0 ============== Translators * Reşat SABIQ (crh) * Suso Baleato (gl) * Rajesh Ranjan (hi) * Francesco Marletta (it) * Manoj Kumar Giri (or) =============== Version 2.25.91 =============== Translators * Changwoo Ryu (ko) * Raivis Dejus (lv) * Sandeep Shedmake (mr) * Горан Ракић (sr) * Daniel Nylander (sv) * Woodman Tuen (zh_HK) * Woodman Tuen (zh_TW) ============== Version 2.25.5 ============== Misc * Use gnome-common macro to define DEPRECATED build variables (Vincent) Translators * Reşat SABIQ (crh) * Saudat Mohammed (ha) * Sylvester Onye (ig) * Fajuyitan, Sunday Ayo (yo) ============== Version 2.25.2 ============== Fixes * Fix a critical warning in the python binding for monitoring a file (Vincent) Misc * Ship a gnome-menus-ls.py script that is an example of python bindings and that can be used as a replacement for gnome-menu-spec-test (Vincent) ============== Version 2.24.2 ============== Translators * Mikel González (ast) ============== Version 2.24.1 ============== Translators * Khaled Hosny (ar) * Rostislav "zbrox" Raykov (bg) * Margulan Moldabekov (kk) ============== Version 2.24.0 ============== Translators * F Wolff (af) * Khaled Hosny (ar) * Roozbeh Pournader (fa) * Rajesh Ranjan (hi) * Gabor Kelemen (hu) * Francesco Marletta (it) * Shankar Prasad (kn) * Leonardo Ferreira Fontenelle (pt_BR) * Mișu Moldovan (ro) =============== Version 2.23.92 =============== Translators * Seán de Búrca (ga) * Krešo Kunjas (hr) * Praveen Arimbrathodiyil (ml) * Leonardo Ferreira Fontenelle (pt_BR) =============== Version 2.23.91 =============== Translators * Khaled Hosny (ar) * Reuven Gonen (he) * Shankar Prasad (kn) ============== Version 2.23.6 ============== Layout * Fix the icon for the accessibility menu (Matthias Clasen) Translators * Khaled Hosny (ar) * Fabrício Godoy (pt_BR) ============== Version 2.23.5 ============== Translators * Yannig Marchegay (Kokoyaya) (oc) * Wadim Dziedzic (pl) * Zabeeh Khan (ps) * Laurent Dhima (sq) ============== Version 2.23.4 ============== Misc * Require intltool 0.40.0 (Vincent) Translators * 甘露(Lu Gan) (zh_CN) ============== Version 2.23.3 ============== Features * Implement handling of $XDG_MENU_PREFIX from the xdg menu specification (Vincent) Fixes * Fix the values of (ie, show_empty, inline, inline_limit, etc.) not being inherited by submenus when the node is after the node in the .menu file (Vincent) * Fix a bug where the fallback on the filename in couldn't be used (Vincent) Translators * Djihed Afifi (ar) * Anna Ármansdóttir (is) * Manoj Kumar Giri (or) ============== Version 2.23.1 ============== Features * Do not show separators at the beginning/end of a menu, or after another separator, but add an option to show them (Vincent) Fixes * Call g_type_init() because GIO needs it and we use GIO for file monitoring (Vincent) * Fix a crash when a file notification is emitted with a non-ascii filename (Vincent) * Remove entries from the excluded set if they are included after they were excluded (eg: somethingsomething) (Vincent) * Implicitly add nodes to and that are missing some (Vincent) * Correctly order the move operations to execute so that moving something and moving it back again works as undo (Vincent) * Simplify some code (William Jon McCann, Vincent) * Plug leak (Vincent) Layout * Do not show accessibility items in the accessories submenu (Josselin Mouette) * Merge menus and files at the end of the layout of settings.menu * Explicitly do not include gnomecc.menu in the preferences menu instead of explicitly excluding it, so that alacarte doesn't know it was excluded (Vincent) * Rename many .directory files so they use fd.o Categories as name (Vincent) * Remove preferences.menu and directly include things in settings.menu (Vincent) * Update a few icons used in .directory files according to the icon naming spec (Vincent) * Remove the accessibility submenu from Preferences, since it's empty now anyway (Vincent) Misc * Remove shebangs from non-executable Python scripts. Translators * Žygimantas Beručka (lt) * Kjartan Maraas (nb) ============== Version 2.22.1 ============== Misc * Remove shebangs from non-executable Python scripts. Translators * David Lodge (en_GB) * Massimo Furlani (fur) * Eskild Hustvedt (nn) ============== Version 2.22.0 ============== Translators * Petr Kovar (cs) * nikosCharonitakis (el) * Changwoo Ryu (ko) * Žygimantas Beručka (lt) * Sandeep Shedmake (mr) * Woodman Tuen (zh_HK) * Woodman Tuen (zh_TW) =============== Version 2.21.92 =============== Translators * Massimo Furlani (fur) * Sangeeta Kumari (mai) * Nabin Gautam (ne) =============== Version 2.21.91 =============== Fixes * Remove the various monitor backends, and unconditionnaly use gio (Vincent) Misc * Do not install gnome-menu-spec-test, it's useless for the user (Vincent) * Add a hard dependency on gio (Vincent) Translators * Woodman Tuen (zh_HK) * Woodman Tuen (zh_TW) =============== Version 2.21.90 =============== Misc * When using gio, require version 2.15.2 (Saleem Abdulrasool) Translators * Djihed Afifi (ar) * Alexander Nyakhaychyk (be) * Michael Terry (io) ============== Version 2.21.5 ============== Fixes * Fix for API changes in gio (Sebastian Bacher, Wouter Bolsterlee, Vincent) Translators * F Wolff (af) * Iñaki Larrañaga Murgoitio (eu) * Erdal Ronahi (ku) * Kjartan Maraas (nn) * Yannig Marchegay (Kokoyaya) (oc) ============== Version 2.21.3 ============== Features * Fix for api change in gio (Kjartan Maraas) * Prevent a major memory leak (Sebastian Droge) Translators * Petr Kovar (cs) * Seán de Búrca (ga) * Danishka Navin (si) ============== Version 2.21.2 ============== Features * Add a new GIO-based monitor backend. It is now the preferred one. The --with-monitor-backend configure flag can be used to select the backend to build. (Sebastian Dröge) Translators * Anas Husseini (ar) * Peter Bach (da) * Jorge González (es) * Seán de Búrca (ga) * Ignacio Casal Quinteiro (gl) * Huda Toriq (id) * Matej Urbančič (sl) ============== Version 2.20.1 ============== Translators * Peter Bach (da) ============== Version 2.20.0 ============== Translators * Anas Husseini (ar) * Amitakhya Phukan (as) * Jordi Mallach (ca) * Peter Bach (da) * Simos Xenitellis (el) * Francesco Marletta (it) * Shankar Prasad (kn) * Žygimantas Beručka (lt) * Alexandru Szasz (ro) * Nickolay V. Shmyrev (ru) * Peter Tuhársky (sk) * Горан Ракић (sr) * Maxim Dziumanenko (uk) =============== Version 2.19.92 =============== Fixes * Fix potential crash (Rob Bradford) Translators * Rostislav "zbrox" Raykov (bg) * Stéphane Raimbault (fr) * Arangel Angov (mk) * Tomasz Dominikowski (pl) * Leonardo Ferreira Fontenelle (pt_BR) * Duarte Loreto (pt) * Funda Wang (zh_CN) =============== Version 2.19.90 =============== Translators * Ani Peter (ml) * Inaki Larranaga Murgoitio (eu) * Ankit Patel (gu) * Ilkka Tuohela (fi) * Yair Hershkovitz (he) * Sean Burke (ga) ============== Version 2.19.6 ============== Translators * Ihar Hrachyshka (be@latin) * Runa Bhattacharjee (bn_IN) * Hendrik Richter (de) * Ilkka Tuohela (fi) * Gabor Kelemen (hu) * Eunju Kim (ko) * Raivis Dejus (lv) * Wouter Bolsterlee (nl) * Bharat Kumar (te) * Nurali Abdurahmonov (uz@cyrillic) ============== Version 2.19.5 ============== Fixes * Use python-config to get python includes (Sebastien Bacher) * Don't show screensavers in the menus (Ray Strode) Translators * Tshewang Norbu (dz) * Takeshi AIHANA (ja) * Tomasz Dominikowski (pl) * Danishka Navin (si) * Daniel Nylander (sv) * Dr.T.Vasudevan (ta) * Bharat Kumar (te) * Nurali Abdurahmonov (uz) * Clytie Siddall (vi) ============== Version 2.19.4 ============== Fixes * Fix crashes in python bindings (Colin Walters) * Fix crash in inotify backend when ~/.config/menus is created (Vincent) Translators * Alexander Nyakhaychyk (be) ============== Version 2.19.3 ============== Fixes * Use G_DIR_SEPARATOR instead of '/' (Vincent) * Fix small leak (William KJon McCann) Translators * David Lodge (en_GB) * Jorge González (es) * Ivar Smolin (et) * Theppitak Karoonboonyanan (th) ============== Version 2.19.2 ============== Menu Layout * Fix "system-wide" typo (Vincent) * Put Preferences before Administration in the System menu (Vincent) * Use icons from the icon naming spec (Luca Ferreti, Matthias Clasen, Vincent) * Use Universal Access instead of Accessibility (Calum Benson, Vincent) * Use System instead of Desktop since the menu got renamed (Sébastien Bacher) * Do not require the Application category in the Other submenu (Vincent) Menu Editor * Fix a crash when unselecting the current menu (Vincent) * Require pygtk at runtime (Vincent) * Use the python executable found by configure (Vincent) Misc * Require automake 1.9 Translators * Ihar Hrachyshka (be@latin) * norbu (dz) * Jorge González (es) * Iñaki Larrañaga Murgoitio (eu) * Ignacio Casal Quinteiro (gl) * Francesco Marletta (it) * Raivis Dejus (lv) * Kjartan Maraas (nb) * Yannig MARCHEGAY (Kokoyaya) (oc) * Matej Urbančič (sl) * Daniel Nylander (sv) ============== Version 2.18.0 ============== Translators * Alaksandar Navicki (be@latin) * Peter Bach (da) * Simos Xenitellis (el) * Ankit Patel (gu) * Žygimantas Beručka (lt) * wadim dziedzic (pl) * Elian Myftiu (sq) * Горан Ракић (sr) =============== Version 2.17.92 =============== Fixes * Show the system menu directories by default (as it was in 2.16) (Denis Washington) Translators * Peter Bach (da) * Takeshi AIHANA (ja) * Duarte Loreto (pt) * Clytie Siddall (vi) * Funda Wang (zh_CN) * Woodman Tuen (zh_HK) * Woodman Tuen (zh_TW) =============== Version 2.17.91 =============== Features * Rework the layout so that it's easy to have the old layout. The control center will ship its own menu file to have the layout for the shell. (Denis Washington) Translators * Khaled Hosny (ar) * Ihar Hrachyshka (be) * Alaksandar Navicki (be@latin) * Rostislav "zbrox" Raykov (bg) * Runa Bhattacharjee (bn_IN) * Mahay Alam Khan (bn) * Jordi Mallach (ca) * Jakub Friedl (cs) * Rhys Jones (cy) * Hendrik Richter (de) * David Lodge (en_GB) * Ivar Smolin (et) * Ilkka Tuohela (fi) * Robert-André Mauchin (fr) * Reuven Gonen (he) * Rajesh Ranjan (hi) * Gabor Kelemen (hu) * Young-Ho Cha (ko) * Žygimantas Beručka (lt) * Jovan Naumovski (mk) * Badral (mn) * Kjartan Maraas (nb) * Wouter Bolsterlee (nl) * Og Maciel (pt_BR) * Leonid Kanter (ru) * Steve Murphy (rw) * Daniel Nylander (sv) * Theppitak Karoonboonyanan (th) * Clytie Siddall (vi) ============== Version 2.17.5 ============== Features * New menu layout for the control center capplets (Denis Washington) Translators * Khaled Hosny (ar) * Ihar Hrachyshka (be) ============== Version 2.17.2 ============== Features * Flesh out inotify support (use --enable-inotify) (Mark) Fixes * Don't load incorrectly encoded .desktop files (Mark) * Fix compile warning (Mark) Translators * Khaled Hosny (ar) * Guillaume Savaton (eo) ============== Version 2.16.1 ============== Translators * David Lodge (en_GB) * Francesco Marletta (it) ============== Version 2.16.0 ============== Translators * Gabor Kelemen (hu) * Jovan Naumovski (mk) * Badral (mn) * Rahul Bhalerao (mr) * Matic Žgur (sl) * Onur Can Çakmak (tr) =============== Version 2.15.91 =============== Translators * Runa Bhattacharjee (bn_IN) * Francisco Javier F. Serrador (es) * Arangel Angov (mk) * Matic Žgur (sl) =============== Version 2.15.90 =============== Translators * Ani Peter (ml) * Subhransu Behera (or) * Theppitak Karoonboonyanan (th) ================ Version 2.15.4.1 ================ Fixes * Correctly update LT_VERSION (Vincent) ============== Version 2.15.4 ============== Features * Add new API to know if an application should be launched in a terminal and to know the path to the desktop file (Travis Watkins) * Complete python bindings for the "No Display" flag (Travis Watkins) Menu Editor * Allow specifying alternate menu files as command line arguments (William Jon McCann) Misc * Use po/LINGUAS (Wouter Bolsterlee) * Require intltool 0.35.0 (Vincent Untz) Translators * Runa Bhattacharjee (bn_IN) * Matheus Grandi (gn) * Swapnil Hajare (mr) ============== Version 2.14.0 ============== Features * Start inotify support (not compiled by default) (Mark McLoughlin) Fixes * Small fix for the python bindings (Mark McLoughlin) * Fix infinite loop (Mark McLoughlin) Translators * Ales Nyakhaychyk (be) * Jérémy Le Floc'h (br) * Petr Tomeš (cs) * Rhys Jones (cy) * Ole Laursen (da) * Hendrik Richter (de) * Pema Geyleg (dz) * Kostas Papadimas (el) * Reuven Gonen (he) * Rajesh Ranjan (hi) * Takeshi AIHANA (ja) * Vladimer Sichinava (ka) * Erdal Ronahi (ku) * Žygimantas Beručka (lt) * Raivis Dejus (lv) * Thierry Randrianiriana (mg) * Wouter Bolsterlee (nl) * Kjartan Maraas (nn) * GNOME PL Team (pl) * Evandro Fernandes Giovanini (pt_BR) * Duarte Loreto (pt) * Sebastian Ivan (ro) * Leonid Kanter (ru) * Elian Myftiu (sq) * Слободан Д. Средојевић (sr) * Maxim Dziumanenko (uk) * Funda Wang (zh_CN) * Woodman Tuen (zh_HK) * Woodman Tuen (zh_TW) ============== Version 2.13.5 ============== Features * Add "include NoDisplay" flag (Mark McLoughlin) Fixes * Fix issue where menu wouldn't fully reload after lots of file change events (Mark McLoughlin, Frederic Crozat) * Remove some unused code (Kjartan Maraas) * Fix incorrect escaping of C format string (The Written Word) Translators * Rostislav Raykov (bg) * Mahay Alam Khan (bn) * Jordi Mallach (ca) * Miloslav Trmac (cs) * Adam Weinberger (en_CA) * Francisco Javier F. Serrador (es) * Priit Laes (et) * Iñaki Larrañaga (eu) * Ilkka Tuohela (fi) * Christophe Merlet (RedFox) (fr) * Ignacio Casal Quinteiro (gl) * Ankit Patel (gu) * Gabor Kelemen (hu) * Francesco Marletta (it) * Erdal Ronahî (ku) * Timur Jamakeev (ky) * Kjartan Maraas (nb, no) * Tino Meinen (nl) * Marcel Telka (sk) * Christian Rose (sv) * Y.Kiran Chandra (te) * Theppitak Karoonboonyanan (th) * Abduxukur Abdurixit (ug) * Clytie Siddall (vi) * Woodman Tuen (zh_HK, zh_TW) ============== Version 2.12.0 ============== Fixes * Fix FAM crasher in gmenu-simple-editor (Ed Catmur) Translators * Rhys Jones (cy) * Vincent Untz (fr) * Ignacio Casal Quinteiro (gl) * Norayr Chilingaryan (hy) * Žygimantas Beručka (lt) * Duarte Loreto (pt) * Leonid Kanter (ru) * Elian Myftiu (sq) * Данило Шеган (sr) * Onur Can Cakmak (tr) * Clytie Siddall (vi) =============== Version 2.11.92 =============== Fixes * Fix memory corruption crasher handling notifies (Mark) * Fix python syntax warning (Mark) * Fix build when FAM isn't found (Elijah Newren) * Fix crasher when a references a subdir of another (Mark) * Fix duplicate entries after updating (Mark) * Fix infinite loop (Frederic Crozat) * Make with prefix work again (Chris Lahey, Mark) Translators * Rostislav "zbrox" Raykov (bg) * Jordi Mallach (ca) * Hendrik Brandt (de) * Nikos Charonitakis (el) * Roozbeh Pournader (fa) * ahmad riza h nst (id) * Takeshi AIHANA (ja) * Young-Ho Cha (ko) * GNOME PL Team (pl) * Sebastian Ivan (ro) * Maxim Dziumanenko (uk) * Clytie Siddall (vi) =============== Version 2.11.91 =============== Fixes * Install .desktop file for editor (Dennis Cranston, Mark) * Fix the window icon in the editor (Jaap A. Haitsma, Mark) * Allow running editor in different prefix from python (Mark) Translators * Miloslav Trmac (cs) * Hendrik Brandt (de) * Adam Weinberger (en_CA) * Francisco Javier F. Serrador (es) * Priit Laes (et) * Ilkka Tuohela (fi) * Ankit Patel (gu) * Reuven Gonen (he) * Gabor Kelemen (hu) * Takeshi AIHANA (ja) * Kjartan Maraas (nb) * Tino Meinen (nl) * Kjartan Maraas (no) * Afonso Celso Medina (pt_BR) * Marcel Telka (sk) * Theppitak Karoonboonyanan (th) * Clytie Siddall (vi) * Funda Wang (zh_CN) * Woodman Tuen (zh_TW) =============== Version 2.11.90 =============== Fixes * Fix issue with handling of filename encodings (Mark) * Only try to include ".directory" for if it exists (Mark) * Re-name the Edutainment sub-menu to Education (Mark) * Fix spec compliance issue with tag handling (Mark) * Remove some unused code (Mark) * Plug some leaks (Mark) Menu Editor * HIGify menu editor (Dennis Cranston) * Make "Desktop" menu appear correctly (Mark) Misc * Allow building against uninstalled library (Brian Cameron) Translators * Ales Nyakhaychyk (be) * Rostislav "zbrox" Raykov (bg) * Miloslav Trmac (cs) * Martin Willemoes Hansen (da) * Hendrik Brandt (de) * Nikos Charonitakis (el) * Adam Weinberger (en_CA) * Francisco Javier F. Serrador (es) * Priit Laes (et) * Iñaki Larrañaga (eu) * Ilkka Tuohela (fi) * Ignacio Casal Quinteiro (gl) * Ankit Patel (gu) * Yuval Tanny (he) * Swapnil Hajare (mr) * Terance Edward Sola (nb) * Ganesh Ghimire (ne) * Tino Meinen (nl) * Terance Edward Sola (no) * Marcel Telka (sk) * Elian Myftiu (sq) * Данило Шеган (sr) * Theppitak Karoonboonyanan (th) * Onur Can Cakmak (tr) * Clytie Siddall (vi) * Funda Wang (zh_CN) * Woodman Tuen (zh_TW) ================ Version 2.11.1.1 ================ Fixes * Fix crasher bug in libgnome-menu triggered by editor (Mark) * Make the editor create $XDG_CONFIG_HOME/menus if it doesn't exist (Mark) ============== Version 2.11.1 ============== Features * Simple menu editor (Mark) * Python bindings (Mark) * Support for and (Mark, Frederic Crozat) * Use FAM directly for monitoring rather than gnome-vfs (Mark) * Add API for retaining empty sub-menus and excluded items in the GMenuTree (Mark, Christian Neumair) * Add gmenu_tree_directory_get_menu_id() API (Mark) * Add gmenu_tree_directory_get_tree() and gmenu_tree_get_menu_file() API (Mark) * Namespace the API - i.e. MenuTree -> GMenuTree (Mark) Fixes * Plug major memory leak when the menu is reloaded (Mark) * Fix "recursive inclusion" crash (Mark) * Fix problem where you could end up with identical items in the same menu (Mark) * Fix issue where you could end up with more than one menu with the same name (Mark) * Update for changes to behaviour in spec (Mark) * Fix off-by-one errors shown up in valgrind (Mark) * Remove s from default menu (Mark) Translators * Vladimir "Kaladan" Petkov (bg) * Miloslav Trmac (cs) * Hendrik Brandt (de) * Adam Weinberger (en_CA) * David Lodge (en_GB) * Francisco Javier F. Serrador (es) * Priit Laes (et) * Iñaki Larrañaga (eu) * Takeshi AIHANA (ja) * Steve Murphy (rw) * Canonical Ltd (xh) ============== Version 2.10.1 ============== Fixes * Add support for new "type" argument to (Mark) * Monitor s for changes (Mark) * Make user desktop entries override system ones (Mark) * Make .directory files in s be pulled in (Mark) * Fix weirdess with [KDE Desktop Entry] files (Mark) * Fix s which don't contain any entries in the toplevel (Mark) * Make sure items in s as allocated (Mark) * Make s with a prefix work correctly (Mark) Translators * Adam Weinberger (en_CA) * Daniel van Eeden (nl) ============== Version 2.10.0 ============== Fixes * Fix 64-bit crasher (Jeremy Katz) Translators * Dafydd Harries (cy) * Farzaneh Sarafraz (fa) * Rajesh Ranjan (hi) * Žygimantas Beručka (lt) * Данило Шеган (sr) * Woodman Tuen (zh_TW) ============== Version 2.9.92 ============== Fixes * Fix issue with file monitoring and subdirs of (Mark) * Fix bug with the directive (Mark) * Make gnome-menu-spec-test work with menu-spec test framework again (Mark) Translators * Arafat Medini (ar) * Jordi Mallach (ca) * Martin Willemoes Hansen (da) * Nikos Charonitakis (el) * David Lodge (en_GB) * Ankit Patel (gu) * Laszlo Dvornik (hu) * ahmad riza h nst (id) * Francesco Marletta (it) * Takeshi AIHANA (ja) * Sang-Gju Kim (ko) * Rajeev Shrestha (ne) * Daniel van Eeden (nl) * GNOME PL Team (pl) * Duarte Loreto (pt) * Dan Damian (ro) * Leonid Kanter (ru) * Elian Myftiu (sq) ============== Version 2.9.90 ============== Fixes * Do not include the Core category in the Other menu (Vincent Untz) Translators * Vladimir "Kaladan" Petkov (bg) * Francisco Javier F. Serrador (es) * Priit Laes (et) * Tommi Vainikainen (fi) * Baptiste Mille-Mathias (fr) * Žygimantas Beručka (lt) * Kjartan Maraas (nb) * Kjartan Maraas (nn) * Raphael Higino (pt_BR) * Marcel Telka (sk) * Christian Rose (sv) * Theppitak Karoonboonyanan (th) * Maxim Dziumanenko (uk) =============== Version 2.9.4.1 =============== Features * Add menu_tree_entry_get_exec() (Richard Hult) Translators * Miloslav Trmac (cs) * Kjartan Maraas (nb) ============= Version 2.9.4 ============= Fixes * New menus layout (Vincent Untz) * Reload menus correctly when they are deleted/updated (Frederic Crozat) * Ref the return value from menu_tree_entry_get_parent() (Mark) Translators * Hendrik Brandt (de) * Adam Weinberger (en_CA) * Baptiste Mille-Mathias (fr) * Arangel Angov (mk) ============= Version 2.9.3 ============= Fixes * Find the right icon path in desktop files (Frederic Crozat) * Handle root path correctly (Mark) * Always remove file monitors (Mark) * Plug leak (Vincent Untz) * Implement behaviour defined in version 0.9 of the spec: entries that match an rule and an rule are marked as "allocated" (Mark) Translators * Vladimir "Kaladan" Petkov (bg) * David Nielsen (da) * Hendrik Brandt (de) * Simos Xenitellis (el) * Iñaki Larrañaga (eu) * Tommi Vainikainen (fi) * Gabor Kelemen (hu) * Žygimantas Beručka (lt) * Duarte Loreto (pt) * Dmitry G. Mastrukov (ru) * Marcel Telka (sk) * Данило Шеган (sr) * Christian Rose (sv) * Theppitak Karoonboonyanan (th) ============= Version 2.9.2 ============= Fixes * Fix a bunch of leaks (Frederic Crozat) * Fix problem where menu entries appear in random places (Mark) * Don't go into an infinite loop if $XDG_CONFIG_DIRS is set wrong (Mark) * Put the user config/data dirs before the system dirs (Mark) * Allow removing monitors from handlers (Mark) Translators * Miloslav Trmac (cs) * Hendrik Brandt (de) * Adam Weinberger (en_CA) * Francisco Javier F. Serrador (es) * Satoru SATOH (ja) * Hasbullah Bin Pit (ms) * Kjartan Maraas (nb) * Daniel van Eeden (nl) * Raphael Higino (pt_BR) * Funda Wang (zh_CN) cinnamon-menus-4.4.0/README000066400000000000000000000023051356375150600153410ustar00rootroot00000000000000cinnamon-menus =========== cinnamon-menus contains the libcinnamon-menu library, the layout configuration files for the Cinnamon menu, as well as a simple menu editor. The libcinnamon-menu library implements the "Desktop Menu Specification" from freedesktop.org: http://freedesktop.org/wiki/Specifications/menu-spec http://specifications.freedesktop.org/menu-spec/menu-spec-latest.html You may download updates to the package from: https://github.com/linuxmint/cinnamon-menus/releases Installation ============ 1) Run meson with options you like. The following configuration installs all binaries, libs, and shared files into /usr/local, and enables all available options: meson debian/build \ --prefix=/usr/local \ --buildtype=plain \ -D deprecated_warnings=false 2) Compile and install (sudo is needed for install) ninja -C debian/build ninja -C debian/build install 3) You can uninstall the installed files with ninja -C debian/build uninstall How to report bugs ================== Bugs should be reported to the Cinnamon bug tracking system: https://github.com/linuxmint/cinnamon-menus/issues You will need to create an account for yourself. cinnamon-menus-4.4.0/debian/000077500000000000000000000000001356375150600157035ustar00rootroot00000000000000cinnamon-menus-4.4.0/debian/changelog000066400000000000000000001170431356375150600175630ustar00rootroot00000000000000cinnamon-menus (4.4.0) tricia; urgency=medium * 4.4.0 -- Clement Lefebvre Sat, 16 Nov 2019 11:44:53 +0100 cinnamon-menus (4.2.0) tina; urgency=medium [ Pino Toscano ] * Switch to modern realpath() (#20) [ Stephen Collins ] * Port to meson (#21) [ Leigh Scott ] * Update for meson changes and also adapt for cinnamon (#23) [ Stephen Collins ] * Build: fix the build options (#22) * Add devhelp docs to cinnamon menus for easier reference (#24) [ Clement Lefebvre ] * Build: Set enable_debug to false [ Leigh Scott ] * Add option to disable docs (#25) [ Eli Schwartz ] * Cleanup (#26) * Meson simplification (#27) -- Clement Lefebvre Sun, 23 Jun 2019 15:33:32 +0200 cinnamon-menus (4.0.0) tessa; urgency=medium * CI: Remove Mint 18 -- Clement Lefebvre Tue, 30 Oct 2018 14:07:22 +0000 cinnamon-menus (3.8.2) tara; urgency=medium [ Michael Webster ] * entry-directories.c: restore retry handling to state prior to commit 7516e8d138072d167ea93b3. -- Clement Lefebvre Fri, 08 Jun 2018 11:32:14 +0100 cinnamon-menus (3.8.1) tara; urgency=medium [ Michael Webster ] * Check for a valid GDesktopAppInfo before calling GDesktopAppInfo methods. Fix retry handling to only apply to new entries. Use g_timeout_add instead of g_idle_add for change signal accumulation. -- Clement Lefebvre Sun, 06 May 2018 14:38:48 +0100 cinnamon-menus (3.8.0) tara; urgency=medium * Add CI configuration -- Clement Lefebvre Mon, 16 Apr 2018 12:17:38 +0100 cinnamon-menus (3.6.0) sylvia; urgency=medium [ Jasper St. Pierre ] * Revert "Memory leak fixes" * Memory leak fixes * entry-directories: Fix unref [ Giovanni Campagna ] * entry-directories: don't modify a list while iterating it * entry-directories: protect event handling for directories [ Florian Müllner ] * libmenu: Remove support for legacy-dirs [ Jasper St. Pierre ] * entry-directories: Only log about invalidations if it was handled -- Clement Lefebvre Mon, 23 Oct 2017 13:20:27 +0100 cinnamon-menus (3.4.0) sonya; urgency=medium [ leigh123linux ] * remove autogenerated files and remove aclocal command from autogen -- Clement Lefebvre Wed, 03 May 2017 11:46:01 +0100 cinnamon-menus (3.2.0) serena; urgency=medium [ Maximiliano Curia ] * Migrate away from gnome-common deprecated vars and macros [ Clement Lefebvre ] * Fixed build [ Maximiliano Curia ] * Drop gtkdocize invocation * Make AX_ macros optional -- Clement Lefebvre Mon, 07 Nov 2016 10:36:18 +0000 cinnamon-menus (3.0.2) sarah; urgency=medium [ Michael Webster ] * Fix a couple issues with incorrect result evaluations when loading desktop files caused by 44ee2de737e53b0 * Revert 44ee2de737e53b and go back to loading .desktop files *as* GDesktopAppInfo - this is the only way g_desktop_app_info_get_filename() will work properly. -- Clement Lefebvre Mon, 30 May 2016 15:45:57 +0100 cinnamon-menus (3.0.1) sarah; urgency=medium [ Michael Webster ] * entry-directories.c: Monitor mimeinfo.cache file and re-attempt failed .desktop files when it changes (which is usually just after the .desktop file is added, causing loading to fail due to unavailable GAppInfo). See inline comments. * Follow-up to previous commit - retry only those desktop files that failed because of appinfo problems. * desktop-entries.c: Refactor to eliminate double-loading of desktop file. Use g_key_file_load_from_file, followed by g_desktop_app_info_new_from_keyfile. -- Clement Lefebvre Mon, 23 May 2016 12:54:12 +0100 cinnamon-menus (3.0.0) sarah; urgency=medium [ monsta ] * configure.ac: drop obsolete macro -- Clement Lefebvre Sat, 23 Apr 2016 15:56:27 +0100 cinnamon-menus (2.8.0) rosa; urgency=medium * 2.8.0 -- Clement Lefebvre Fri, 16 Oct 2015 14:02:11 +0100 cinnamon-menus (2.6.0) rafaela; urgency=medium * 2.6.0 -- Clement Lefebvre Tue, 19 May 2015 17:05:48 +0200 cinnamon-menus (2.5.0) unstable; urgency=medium * Bump for development -- Michael Webster Sat, 11 Apr 2015 09:07:20 -0400 cinnamon-menus (2.4.2) rebecca; urgency=medium * Added missing gnome-common in build-dependencies (needed by GNOME_COMPILE_WARNINGS in configure.ac) -- Clement Lefebvre Tue, 31 Mar 2015 12:26:33 +0200 cinnamon-menus (2.4.1) rebecca; urgency=medium * Merge debian folder improvements from debian git * gir1.2-cmenu-3.0.install based on gir libdir -- Clement Lefebvre Tue, 13 Jan 2015 12:07:31 +0100 cinnamon-menus (2.4.0) rebecca; urgency=medium * 2.4.0 -- Clement Lefebvre Thu, 30 Oct 2014 15:22:32 +0100 cinnamon-menus (2.3.0) unstable; urgency=medium * 2.3.0 -- Clement Lefebvre Fri, 27 Jun 2014 14:27:33 +0100 cinnamon-menus (2.2.0) qiana; urgency=medium * 2.2.0 -- Clement Lefebvre Sat, 12 Apr 2014 11:09:28 +0100 cinnamon-menus (2.1) petra; urgency=low * Rename -- Michael Webster Mon, 20 Jan 2014 10:19:41 -0500 gnome-menus (3.8.0-1ubuntu5) saucy; urgency=low * debian/patches/ubuntu_gcc_translations.patch: revert dropping of gnome-control-center categories, they got deprecated in g-c-c 3.8 but we are still using 3.6. That should fix the issue where the settings categories show as untranslated (lp: #1232534). -- Sebastien Bacher Wed, 09 Oct 2013 15:39:21 +0200 gnome-menus (3.8.0-1ubuntu4) saucy; urgency=low * Update some of the directory icon names (LP: #1201128) -- Jeremy Bicha Sun, 08 Sep 2013 14:31:02 -0400 gnome-menus (3.8.0-1ubuntu3) saucy; urgency=low * gnome-menus.maintscript: - Clean up after rename from /etc/xdg/menus/applications.menu to /etc/xdg/menus/gnome-applications.menu * debian/gnome-menus.postinst, debian/gnome-menus.prerm: - Remove since they may cause issues with ubuntu-gnome-default-settings * debian/patches/08_settings-menus.patch: + Exclude Sundry items so that they aren't duplicated in the menus -- Jeremy Bicha Fri, 14 Jun 2013 16:57:54 -0400 gnome-menus (3.8.0-1ubuntu2) saucy; urgency=medium * debian/patches/01_default_prefix.patch: - Re-enabled as its absense is causing problems for GNOME Shell (LP: #1189722) -- Jeremy Bicha Wed, 12 Jun 2013 16:22:35 -0400 gnome-menus (3.8.0-1ubuntu1) saucy; urgency=low * Merge with Debian, remaining changes: - debian/gnome-menus.postinst: + Disable blacklist to not break old applications - debian/gnome-menus.triggers: Drop "gmenucache". - debian/patches/series: Disable 01_default_prefix.patch - debian/patches/09_app_install_entry.patch: + Include Software Center in menus - debian/patches/70_ubuntu-directories.patch + Add Ubuntu-specific directories back to POTFILES.in * Dropped changes: - debian/patches/22_directory-suffix-for-icons.patch - debian/gnome-menus.maintscript: + ensure conffile is removed on upgrade (not needed after 12.04 LTS) * Refreshed patches * debian/patches/50_add-gcc-apps.patch - Unhide Settings panels in GNOME Shell since we're sticking with Settings 3.6 for now. Settings 3.8 includes a GNOME Shell 3.8 search provider. -- Jeremy Bicha Sat, 08 Jun 2013 16:15:46 -0400 gnome-menus (3.8.0-1) unstable; urgency=low [ Josselin Mouette ] * Switch to interest-noawait for triggers. [ Andreas Henriksson ] * New upstream release. * Upload to unstable. -- Andreas Henriksson Sat, 25 May 2013 16:41:40 +0200 gnome-menus (3.7.90-1) experimental; urgency=low [ Josselin Mouette ] * Team upload * New upstream release. * gnome-menus-blacklist: patch from Fabian Greffrath to handle correctly menu files with missing ending newlines. Closes: #692141. * Drop Debian menu support entirely. In the case it gets enabled by mistake by users, it breaks the shell beyond all repair. Closes: #694356. * 02_kill_debian_menu.patch: new patch. Completely remove Debian menu entries by discarding them at the parsing stage. This should work around bug #696530 in menu-xdg. * gnome-menus.postinst: clean up the desktop files once upon upgrades, in order to get rid of files generated by a buggy script. [ Dmitrijs Ledkovs ] * Port gnome-menus-blacklist to python3. [ Sjoerd Simons ] * New upstream release (3.7.90) * debian/patches/10_use-default-python-path.patch, debian/patches/21_default-python-in-shebang.patch: + Dropped, simple-editor is no longer part of gnome-menus * debian/patches/*: refreshed -- Sjoerd Simons Sat, 23 Mar 2013 20:51:31 +0100 gnome-menus (3.6.0-2) experimental; urgency=low * Team upload * Move to debhelper compat level 9, for compressed debug symbols * Add libgnome-menus-3-0-dbg -- Simon McVittie Thu, 25 Oct 2012 13:00:52 +0100 gnome-menus (3.6.0-1) experimental; urgency=low [ Josselin Mouette ] * Update blacklist for OpenJDK and Icedtea control panels. Closes: #679565. [ Sjoerd Simons ] * New upstream release (3.6.0) -- Sjoerd Simons Sun, 14 Oct 2012 18:42:49 +0200 gnome-menus (3.4.2-3) unstable; urgency=low * 61_nodisplay_recurse.patch: backported from upstream git. Add a function to check for NoDisplay=true recursively. This is needed for gnome-shell. * Update symbols file accordingly. -- Josselin Mouette Sat, 23 Jun 2012 20:28:18 +0200 gnome-menus (3.4.2-2) unstable; urgency=low * Blacklist imagemagick. Closes: #678406. * 60_missing_function.patch: backported from upstream git. Add a missing function in the library. Closes: #676566. * Update symbols file. -- Josselin Mouette Sat, 23 Jun 2012 19:02:38 +0200 gnome-menus (3.4.2-1) unstable; urgency=low [ Josselin Mouette ] * menus.blacklist: update to the latest KDE versions. Closes: #650017, #650019. [ Michael Biebl ] * New upstream release. * Change section of gir1.2-gmenu-3.0 to introspection. * debian/gnome-menus.maintscript: Use new versioning scheme as recommended by dpkg-maintscript-helper which better handles local modifications and (bin)NMUs. * debian/patches/05_debian_menu.patch: Refreshed. * Drop explicit Build-Depends on gir1.2-glib-2.0 and bump libgirepository1.0-dev accordingly. * Bump Stanards-Version to 3.9.3. * debian/menus.blacklist: - Remove stale entries which are no longer relevant, mostly KDE3 related. - Remove KDE entries which use OnlyShowIn=KDE; - Add notes for which .desktop files bugs have been filed. Once they are fixed the corresponding entries should be removed again. - Add Thunar (XFCE) and related applications to blacklist. -- Michael Biebl Sat, 19 May 2012 08:11:53 +0200 gnome-menus (3.2.0.1-2) unstable; urgency=low [ Jordi Mallach ] * Update po-up/ca.po. [ Michael Biebl ] * Upload to unstable. -- Michael Biebl Sun, 20 Nov 2011 14:18:58 +0100 gnome-menus (3.2.0.1-1) experimental; urgency=low [ Sjoerd Simons ] * New upstream release * Partial sync from Ubuntu, ensure we're using the same package names and initial 3.2.0 packaging * debian/patches/01_default_prefix.patch: Refreshed * debian/libgnome-menu-3-0.symbols: Add symbols file * debian/gnome-menus.install: Install gmenu-simple-editor * Remove pysupport depends, gnome-menus bindings go via GI now. [ Michael Biebl ] * debian/rules: - Call dh_python2 for gnome-menus, so python:Depends is set. * debian/control.in: - Bump Build-Depends on python to (>= 2.6.6-3~) for dh_python2. [ Sjoerd Simons ] * Stop installing gmenu-simple-editor -- Sjoerd Simons Fri, 21 Oct 2011 22:26:35 +0200 gnome-menus (3.0.1-3) unstable; urgency=low [ Josselin Mouette ] * Add xterm to the blacklist. [ Michael Biebl ] * Use the .maintscript facility provided by dh_installdeb to cleanup the old conffiles. * Update the last unfixed version to 3.0.1-2 as we missed to remove the old conffile for users upgrading from 2.30.3-2+b1. Closes: #645446 -- Michael Biebl Wed, 19 Oct 2011 23:21:39 +0200 gnome-menus (3.0.1-2) unstable; urgency=low * Upload to unstable. * debian/control.in: - Set pkg-gnome-maintainers@lists.alioth.debian.org as Maintainer. - Remove obsolete Build-Depends on dpkg-dev. - Remove old Conflicts and Replaces. - Bump Standards-Version to 3.9.2. No further changes. - Add Vcs-* fields. * Bump debhelper compatibility level to 8. - Update Build-Depends on debhelper. - Strip debian/tmp/ from .install files. * debian/watch: - Update to version 3. - Switch to .xz tarballs. -- Michael Biebl Fri, 14 Oct 2011 08:52:20 +0200 gnome-menus (3.0.1-1) experimental; urgency=low * Break alacarte < 0.13.2-2 (version without support for settings.menu). * New upstream release. * 08_settings-menus.patch: new patch. Move the old settings and system settings to submenus in the “system” submenu. This is merely so that they have a place until we clean them up entirely. + desktop-files/Settings{,-System}.directory.in: taken from the 2.30 package. + Update translations accordingly. * po-up/fr.po: add a missing French translation. * Clean up old comments in translations. * 09_games-menu.patch: refreshed. -- Josselin Mouette Sat, 04 Jun 2011 22:13:14 +0200 gnome-menus (3.0.0-1) experimental; urgency=low [ Josselin Mouette ] * Add Sun Java VisualVM to the blacklist. * Update qtconfig names. [ Raphaël Hertzog ] * New upstream release (3.0.0). * Disable 06_menus_rename.patch and 08_menus_prefix.patch as they are replaced with the new XDG_MENU_PREFIX feature supported by upstream. * Do not try to install settings.menu, it's gone. * Add some copyright notices to debian/copyright to please lintian. * Add Build-Depends on gobject-introspection (>= 0.9.5) to match configure requirements. [ Josselin Mouette ] * Break gnome-panel and gnome-control-center < 2.91 since settings.menu is gone. * 01_default_prefix.patch: new patch. Provide backwards compatibility with previous versions by using "gnome-" as a default value for XDG_MENU_PREFIX. * Re-introduce the gir package. * Use dpkg-maintscript-helper to cleanup conffiles. -- Josselin Mouette Mon, 11 Apr 2011 01:04:12 +0200 gnome-menus (2.30.3-2) unstable; urgency=low * Remove gir1.0-gmenu-2.0 since nothing uses it, to ease the gir1.2 transition. -- Emilio Pozuelo Monfort Wed, 16 Feb 2011 20:15:51 +0000 gnome-menus (2.30.3-1) unstable; urgency=low * New upstream translation and bugfix release. * 06_menus_rename.patch: refreshed. Note that upstream uses a different fix but it relies on an environment variable (XDG_MENU_PREFIX) and only applies to applications.menu. -- Josselin Mouette Sat, 18 Sep 2010 10:08:06 +0200 gnome-menus (2.30.2-1) unstable; urgency=low [ Emilio Pozuelo Monfort ] * debian/control.in, debian/rules: - Switch to CDBS' python-autotools.mk. * debian/control.in, debian/rules, debian/source/format: - Switch to source format 3.0 (quilt). [ Josselin Mouette ] * Add foo2zjs and its obnoxious icon to the blacklist. * New upstream translation release. -- Josselin Mouette Tue, 20 Jul 2010 23:21:32 +0200 gnome-menus (2.30.0-1) unstable; urgency=low * New upstream release. * Now include gir1.0-gmenu-2.0, moved from gir-repository. -- Josselin Mouette Sat, 03 Apr 2010 23:19:34 +0200 gnome-menus (2.28.0.1-4) unstable; urgency=low * debian/python-gmenu.install: - Install the extension for python2.6 too. Thanks Jakub Wilk. Closes: #573303. -- Emilio Pozuelo Monfort Wed, 10 Mar 2010 15:55:51 +0100 gnome-menus (2.28.0.1-3) unstable; urgency=low * Unbreak gnome-menus purge (ACKed by Joss) -- Marc 'HE' Brockschmidt Tue, 12 Jan 2010 10:30:20 +0100 gnome-menus (2.28.0.1-2) unstable; urgency=low * gnome-menus-blacklist: new script. Implements a blacklist of menu entries that won't be shown by default, until the packages shipping them are fixed to use NotShowIn=GNOME. * menus.blacklist: the configuration file for this script. * gnome-menus.triggers, gnome-menus.postinst: re-run it every time something has changed in /usr/share/applications. * gnome-menus.install: install this. * gnome-menus.prerm: clean up before removing. -- Josselin Mouette Sat, 09 Jan 2010 13:18:59 +0100 gnome-menus (2.28.0.1-1) unstable; urgency=low * New upstream release. * Standards-Version is 3.8.3, no changes needed. * debian/watch: don't uupdate. -- Emilio Pozuelo Monfort Wed, 04 Nov 2009 18:34:41 +0100 gnome-menus (2.28.0-1) unstable; urgency=low * New upstream release. * Bump shlibs for the library. * 12_submenus_inherit.patch, 12_merge_duplicates.patch: dropped, merged upstream. * Update list of installed files. * Dropped dependency on python-glade2. -- Josselin Mouette Sat, 26 Sep 2009 01:44:51 +0200 gnome-menus (2.26.1-2.1) unstable; urgency=low * Non-maintainer upload. * Update simplified Chinese translation for menu. -- Deng Xiyue Wed, 01 Jul 2009 14:47:15 +0800 gnome-menus (2.26.1-2) unstable; urgency=low * 12_merge_duplicates.patch: use the version provided by upstream instead of my gross hack. Thanks a lot, Vincent. * 12_submenus_inherit.patch: stolen upstream. Make children correctly inherit their parents’ layout. Necessary for 12_merge_duplicates.patch to work. * Point to versioned GPL. -- Josselin Mouette Sat, 27 Jun 2009 13:01:24 +0200 gnome-menus (2.26.1-1) unstable; urgency=low * New upstream release. * python-gmenu.examples: add gnome-menus-ls.py. * gnome-menus.install: don’t ship the example in here. * 03_kde-legacydirs.patch: refreshed. -- Josselin Mouette Wed, 17 Jun 2009 22:54:01 +0200 gnome-menus (2.24.2-2) unstable; urgency=low * Upload to unstable. -- Josselin Mouette Thu, 12 Mar 2009 13:04:00 +0100 gnome-menus (2.24.2-1) experimental; urgency=low [ Loic Minier ] * Drop Encoding=UTF-8 from debian/desktop-files/*.directory.in; deprecated. * Don't purge /usr/lib/python2.?/site-packages/GMenuSimpleEditor during first configuration. [ Josselin Mouette ] * New upstream release. * Bump intltool requirement; drop the libxml-parser-perl one. * Bump shlibs version to 2.23.3. * 01_preferences-legacydir.patch, 02_applications-legacydir.patch, 04_settings-legacydir.patch: dropped, obsolete. * Don’t rename preferences.menu, it doesn’t exist anymore. * gnome-menus.preinst: remove gnome-preferences.menu upon upgrade. * 06_menus_rename.patch: drop obsolete part. * 08_menus_prefix.patch: refreshed. * 11_science-menu.patch: updated to apply cleanly. * 12_merge_duplicates.patch: do not re-sort merged menus that are not inlined. -- Josselin Mouette Fri, 26 Dec 2008 17:43:03 +0100 gnome-menus (2.22.2-4) unstable; urgency=low * 12_merge_duplicates.patch: also merge entries which appear more than twice. Closes: #494667. -- Josselin Mouette Fri, 29 Aug 2008 11:21:11 +0200 gnome-menus (2.22.2-3) unstable; urgency=low * Rename Science.directory to GnomeScience.directory. Closes: #491184. -- Josselin Mouette Fri, 18 Jul 2008 18:14:21 +0200 gnome-menus (2.22.2-2) unstable; urgency=low * debian/po-up/ro.po: new Romanian translation from Eddy Petrișor. Closes: #489070. * 11_science-menu.patch: new patch. + Split Science out of the Education menu. + Use "science" icon for Science, "accessories" for Education, and "utilities" for Accessories, since that fits better how the icons were designed. * Update translations accordingly. * Standards version is 3.8.0. * 09_games-menu.patch: lower the inline limit to 6. -- Josselin Mouette Tue, 15 Jul 2008 12:34:21 +0200 gnome-menus (2.22.2-1) unstable; urgency=low * New upstream release. * 11_accessibility_accessories.patch: dropped, merged upstream. -- Josselin Mouette Thu, 29 May 2008 00:52:40 +0200 gnome-menus (2.22.1-3) unstable; urgency=low * 12_merge_duplicates.patch: when merging subdirectories without the inline_header property, sort again the entries after the merge. Closes: #447823. Also filter out duplicates. Closes: #444587. -- Josselin Mouette Tue, 13 May 2008 13:14:12 +0200 gnome-menus (2.22.1-2) unstable; urgency=low * 09_games-menu.patch: don't use the flawed marker, simply filter out all subcategories of the games menu. This should avoid issues when the user or another menu system displays these entries elsewhere. Closes: #479761. * Fix capitalization of Python in the description. * Move the .po updating process from the clean target to the update-po target. * Switch to python-support. * python-gmenu.postinst: work around python-central not removing the old files during upgrades. -- Josselin Mouette Tue, 06 May 2008 20:52:16 +0200 gnome-menus (2.22.1-1) unstable; urgency=low * New upstream bugfix release. -- Sebastian Dröge Tue, 08 Apr 2008 12:59:28 +0200 gnome-menus (2.22.0-1) unstable; urgency=low [ Josselin Mouette ] * 11_accessibility_accessories.patch: new patch; exclude accessibility tools from the Accessories directory, they are already in the Universal Access menu. [ Sebastian Dröge ] * New upstream stable release: + debian/control.in: - Update build dependencies. + debian/patches/07_gnomevfs.patch: - Dropped, not necessary anymore as GIO is used for monitoring now. + debian/patches/70_reautogen.patch: - Dropped, not necessary anymore. -- Sebastian Dröge Tue, 11 Mar 2008 17:12:21 +0100 gnome-menus (2.20.3-1) unstable; urgency=low * New upstream release with translation updates only: + debian/patches/70_reautogen.patch: - Regenerated for the new version. * debian/control.in: + Update Standards-Version to 3.7.3, no additional changes needed. -- Sebastian Dröge Thu, 10 Jan 2008 10:53:18 +0100 gnome-menus (2.20.2-1) unstable; urgency=low * New upstream bugfix release with translation updates: + debian/patches/70_reautogen.patch: - Regenerated for the new version. -- Sebastian Dröge Tue, 27 Nov 2007 06:27:02 +0100 gnome-menus (2.20.1-1) unstable; urgency=low * New upstream bugfix release: + debian/patches/70_reautogen.patch: - Regenerated for the new version. -- Sebastian Dröge Fri, 26 Oct 2007 20:16:34 +0200 gnome-menus (2.20.0-2) unstable; urgency=low * debian/patches/07_gnomevfs.patch: + Include gnome-vfs-utils.h to fix implicit pointer conversion which breaks on archs where sizeof(void*)>sizeof(int). Thanks to Dann Frazier for the patch (Closes: #443339). -- Sebastian Dröge Thu, 20 Sep 2007 20:20:12 +0200 gnome-menus (2.20.0-1) unstable; urgency=low [ Loic Minier ] * Expand tabs in control. * Cleanup rules. * Update path 07_gnomevfs to use GnomeVFS to escape URIs; solves handling of pathnames with spaces; from Ubuntu; thanks Sébastien Bacher. [ Sebastian Dröge ] * New upstream release. * Upload to unstable, drop check-dist include. * debian/patches/70_reautogen.patch: + Updated for the new version. -- Sebastian Dröge Thu, 20 Sep 2007 11:16:44 +0200 gnome-menus (2.19.6-1) experimental; urgency=low * New patch, but disabled by default, 20_show-admin-tools-for-admin-group, permits hiding menu entries requiring root rights when the user isn't in the admin group; found in the Ubuntu package. * New upstream release series; these are development releases, the API may still change incompatibly; no API change in this release though. - Target at experimental; include check-dist.mk. - Refresh patches 09_games-menu, 20_show-admin-tools-for-admin-group to apply cleanly. - Update relibtoolizing patch, 70_reautogen; run intltoolize too. - New patch, 21_default-python-in-shebang, fixes shebang of gmenu-simple-editor to use the default Python version. -- Loic Minier Fri, 03 Aug 2007 11:55:44 +0200 gnome-menus (2.18.3-2) unstable; urgency=low [ Josselin Mouette ] * Debian.directory: don't display the Debian menu by default. It can be enabled with the menu editor. * Convert patches to quilt; build-depend on quilt. * Remove pycompat and the dh_python call. * Make the included menu translatable; build-depend on intltool. * 09_games-menu.patch: split the games menu in submenus following the desktop specification keywords. Use a default layout that doesn't show them unless there are at least 8 games in the submenu. This should avoid showing submenus by default while making the menu usable for people with lots of games installed. * The desktop-files/ directory contains desktop entries for these submenus. * The po-up/ directory contains translations for these entries. [ Christian Perrier ] * Translations for po-up/: - Picked 56 PO files from po-sections/ in the menu package. Updates: - Punjabi. Closes: #433570, #433571 - Korean. Closes: #433576 - Tamil. Closes: #433588 - Gujarati. Closes: #433595 - Thai. Closes: #433600 - Spanish. Closes: #433601 - Hungarian. Closes: #433605 - Simplified Chinese. Closes: #433606 - Basque. Closes: #433610 - Polish. Closes: #433614 - Bulgarian. Closes: #433617 - German. Closes: #433628 - Brazilian Portuguese. Closes: #433680 - Wolof. Closes: #433696, #433702 - Indonesian. Closes: #433764 - Vietnamese. Closes: #433795 - Swedish. Closes: #433930 - Marathi. - Czech. Closes: #434001 - Simplified Chinese. Closes: #433606 - Russian. Closes: #434780 - German. Closes: #434785 [ Josselin Mouette ] * Translations for po-up/: - Lithuanian. Closes: #434805. - Portuguese. Closes: #435032. * Minor fixes in French translation. -- Josselin Mouette Sun, 29 Jul 2007 18:57:54 +0200 gnome-menus (2.18.3-1) unstable; urgency=low * New upstream stable release; no API change. - Update autotools patch, 70_reautogen. -- Loic Minier Tue, 03 Jul 2007 19:58:18 +0200 gnome-menus (2.18.2-1) unstable; urgency=low * New upstream stable release; no API change. - Let python-gmenu depend on python-gtk2 as GMenuSimpleEditor uses it. - Update relibtoolizing patch which is required; add comments on how to generate the patch. -- Loic Minier Mon, 28 May 2007 15:41:52 +0200 gnome-menus (2.18.0-2) unstable; urgency=low * Fix .orig file in patch 07_gnomevfs; fixes FTBFS on double build; closes: #424341. * Wrap build-deps and deps. -- Loic Minier Wed, 16 May 2007 16:37:00 +0200 gnome-menus (2.18.0-1) unstable; urgency=low * New upstream release. * 07_gnomevfs.patch, 70_reautogen.patch: updated from the Ubuntu package. gnome-vfs will still be used as default backend until inotify support is enabled upstream by default. -- Sebastian Dröge Wed, 25 Apr 2007 06:26:26 +0200 gnome-menus (2.16.1-3) unstable; urgency=medium [ Loic Minier ] * Add a get-orig-source target to retrieve the upstream tarball. [ Josselin Mouette ] * 06_menus_rename.patch: reinstate the renaming in the menu editor, as we are lacking an interface for *saving* a renamed menu. Closes: #411246. -- Josselin Mouette Sat, 17 Feb 2007 14:14:35 +0100 gnome-menus (2.16.1-2) unstable; urgency=low * 08_menus_prefix.patch: + Only rename the specific menus we are renaming, avoid lookups for gnome-anything.menu (closes: #403165, #403456). + Strip the "gnome-" part specifically when opening the merged directory. -- Josselin Mouette Thu, 21 Dec 2006 20:15:49 +0100 gnome-menus (2.16.1-1) unstable; urgency=low * New upstream translation release. * 08_menus_prefix.patch: try "gnome-" as a prefix for menu files instead of renaming them explicitly. This should make applications-merged/ work as expected. * 06_menus_rename.patch: only keep the gnome-preferences renaming. * Build-depend on python-central 0.5 to please lintian. * Bump shlibs version to 2.16.1-1 to allow relying on this feature. -- Josselin Mouette Mon, 4 Dec 2006 22:36:55 +0100 gnome-menus (2.16.0-2) unstable; urgency=low * Upload to unstable. -- Loic Minier Thu, 19 Oct 2006 11:41:08 +0200 gnome-menus (2.16.0-1) experimental; urgency=low * New upstream release; no API change. -- Loic Minier Mon, 25 Sep 2006 16:42:44 +0200 gnome-menus (2.15.91-1) experimental; urgency=low * New upstream development releases, with API additions. - Target at experimental. - Bump up shlibs to >= 2.15.4. - Update patches: 03_kde-legacydirs, 04_settings-legacydir, 05_debian_menu, 06_menus_rename, 07_gnomevfs, 70_reautogen. * Add CDBS' utils. -- Loic Minier Sun, 13 Aug 2006 19:04:07 +0200 gnome-menus (2.14.3-1) unstable; urgency=low * New upstream release, no API change. - Update patch 70_reautogen. -- Loic Minier Tue, 8 Aug 2006 11:02:56 +0200 gnome-menus (2.14.0-8) unstable; urgency=low * Add debian/control to the pyversions -r call since it's mandatory, even if the man page claims it's optional. -- Loic Minier Sat, 5 Aug 2006 20:53:29 +0200 gnome-menus (2.14.0-7) unstable; urgency=low * Let python-gmenu also Conflict with gnome-menus << 2.14.0-3, since the historical Replace is not honored in Python packages following the newer policy; thanks Josselin Mouette. (Closes: #381426) -- Loic Minier Sat, 5 Aug 2006 20:02:52 +0200 gnome-menus (2.14.0-6) unstable; urgency=low * Fix 07_gnomevfs patch which shipped a backup file. -- Loic Minier Thu, 27 Jul 2006 22:42:24 +0200 gnome-menus (2.14.0-5) unstable; urgency=medium * Add ${python:Provides} to python-gmenu. * Build python-gmenu for all available versions of Python >= 2.3; build-depend on python-all-dev. * Remove trailing spaces. * Move relibtoolizing patch (08_reautogen) to apply later, at 70_reautogen. * New patch to use the default python path in gmenu-simple-editor, thanks Joe Wreschnig. -- Loic Minier Thu, 27 Jul 2006 22:32:19 +0200 gnome-menus (2.14.0-4) unstable; urgency=low * Fix watch file. * Update to new Python policy. (Closes: #373435) - Bump up debhelper build-dep to 5.0.37.2. - Set Python compatibility level to 2. - Add XB-Python-Version to python-gmenu and gnome-menus. - Add a python-central (>= 0.4.17) build-dep. - Bump up cdbs build-dep to >= 0.4.43. - Add a XS-Python-Version: current as gnome-menus is the only package to depend on python-gmenu. - Call dh_pycentral to fill XB-Python-Version and add a pycentral dep. - Move the fixup snippet for the executable bit from binary-post-install to binary-install, before dh_pycentral moves the files around. -- Loic Minier Thu, 13 Jul 2006 20:15:34 +0200 gnome-menus (2.14.0-3) unstable; urgency=low * Drop *.la files from libgnome-menu-dev as these were removed from its reverse dependencies. * Bump up Standards-Version to 3.7.2. * Add even more ${misc:Depends}. * Make package binNMU-safe. - Add a dpkg-dev >= 1.13.19 build-dep. - Use ${binary:Version} in inter-dependencies. * Bump up Debhelper compatibility level to 5. * Set executable bit on Python files with a shebang to make lintian happy. * Install /usr/lib/python*/site-packages/GMenuSimpleEditor in python-gmenu and not gnome-menus; let python-gmenu Replace gnome-menus << 2.14.0-3. -- Loic Minier Thu, 22 Jun 2006 15:28:27 +0200 gnome-menus (2.14.0-2) unstable; urgency=low [ Loic Minier ] * Sync with overrides. - Set libgnome-menu-dev Section to libdevel. [debian/control, debian/control.in] - Set python-gmenu Section to python. [debian/control, debian/control.in] [ Josselin Mouette ] * 07_gnomevfs.patch: use gnome-vfs instead of fam for monitoring. This should be the end of the "menu disappeared" bugs. * 08_reautogen.patch: the re-autogenisation that follows. * control.in: replace dependencies and build-dependencies on fam by gnome-vfs. Entirely remove the dependency for the -dev package, it's not needed for a backend. -- Josselin Mouette Sat, 20 May 2006 07:11:17 +0200 gnome-menus (2.14.0-1) unstable; urgency=low * New upstream version * Renamed the patches to use .patch instead of .diff * debian/control.in: - dropped gnomevfs requirement it doesn't use it * debian/libgnome-menu2.shlibs: - updated shlibs version * debian/patches/06_menus_rename.patch: - updated * debian/patches/07_relibtoolise.patch: - not required * debian/watch: - updated [ J.H.M. Dassen (Ray) ] * [patches/07_relibtoolise.diff] Added to do away with unneeded direct library dependencies in the gnome-menus package. [ Josselin Mouette ] * Acknowledge NMU: + Add python-glade2 depends for gnome-menus (Closes: #347176). -- Sebastien Bacher Sat, 29 Apr 2006 15:26:42 +0200 gnome-menus (2.12.0-2) unstable; urgency=low * debian/control.in: - depend on ${python:Depends}. * Upload to unstable. -- Josselin Mouette Mon, 2 Jan 2006 14:03:46 +0100 gnome-menus (2.12.0-1) experimental; urgency=low * New upstream version: - ship a menu editor (Closes: #332976). - use Education instead of Edutainment (Closes: #314491). * debian/control.in: - new python-gmenu package. - updated for the soname change. - updated the Build-Depends/Depends for the new packages. - updated the Standards-Version. * debian/gnome-menus.install: - updated for new files. * debian/patches/04_settings-legacydir.diff: - updated. * debian/patches/06_menus_rename.diff: - updated. * debian/rules: - use dh_python. * debian/watch: - updated -- Sebastien Bacher Mon, 17 Oct 2005 14:45:15 +0200 gnome-menus (2.10.2-1) unstable; urgency=low * New upstream version: - make user .desktop files correctly override system ones. - remove from settings menu. - fix memory leaks on re-load. - fix issue with duplicate sub-menus. -- Sebastien Bacher Tue, 28 Jun 2005 11:19:50 +0200 gnome-menus (2.10.1-3) unstable; urgency=low * debian/patches/04_settings-legacydir.diff: - don't use LegacyDir for the Desktop menu (Closes: #304326). -- Sebastien Bacher Tue, 14 Jun 2005 14:24:47 +0200 gnome-menus (2.10.1-2) unstable; urgency=low * Upload to unstable. * debian/patches/06_menus_rename.diff: - patch for the menus rename. * debian/rules: - renamed menu files to not conflict with other desktops (Closes: #307098). -- Sebastien Bacher Tue, 7 Jun 2005 19:47:00 +0200 gnome-menus (2.10.1-1) experimental; urgency=low * Initial upload to debian. -- Sebastien Bacher Wed, 23 Mar 2005 18:35:08 +0100 gnome-menus (2.10.1-0ubuntu1) hoary; urgency=low * New upstream release: - add support for new "type" argument to . - monitor s for changes. - make user desktop entries override system ones. - make .directory files in s be pulled in. - fix weirdess with [KDE Desktop Entry] files. - fix s which don't contain any entries in the toplevel. - make sure items in s as allocated. - make s with a prefix work correctly. * debian/patches/03_kde-legacydirs.diff: - don't use KDELegacyDirs. -- Sebastien Bacher Wed, 23 Mar 2005 15:28:55 +0100 gnome-menus (2.10.0-0ubuntu1) hoary; urgency=low * New upstream release. * debian/watch: - updated. -- Sebastien Bacher Mon, 7 Mar 2005 16:09:23 +0100 gnome-menus (2.9.92-0ubuntu1) hoary; urgency=low * New upstream release: - fix issue with file monitoring and subdirs of (Hoary: #5934). -- Sebastien Bacher Tue, 1 Mar 2005 12:39:23 +0100 gnome-menus (2.9.90-0ubuntu1) hoary; urgency=low * New upstream release: - do not include the Core category in the Other menu (Hoary: #5484). * debian/patches/06_launchbox.diff: - removed, these changes are in the new version. -- Sebastien Bacher Tue, 25 Jan 2005 19:24:59 +0100 gnome-menus (2.9.4-0ubuntu2) hoary; urgency=low * debian/patches/06_launchbox.diff: - added the patch for GNOME launch box. -- Sebastien Bacher Thu, 20 Jan 2005 13:09:25 +0100 gnome-menus (2.9.4-0ubuntu1) hoary; urgency=low * New upstream release: - new menus layout. - reload menus correctly when they are deleted/updated . * debian/patches/03_menu_layout.diff: - removed, the changes are included upstream. -- Sebastien Bacher Tue, 11 Jan 2005 00:18:45 +0100 gnome-menus (2.9.3-0ubuntu1) hoary; urgency=low * New upstream release. * debian/patches/05_debian_menu.diff: - patch for the Debian menu. -- Sebastien Bacher Tue, 21 Dec 2004 22:34:04 +0100 gnome-menus (2.9.2cvs041212-0ubuntu4) hoary; urgency=low * The Applications menu has an entry for the Debian menu now. If you want to get it you only need to install menu and menu-xdg. * layout/applications.menu: - added an entry for the Debian menu. * debian/Debian.directory: - added. * debian/gnome-menus.install: - install Debian.directory. -- Sebastien Bacher Sun, 19 Dec 2004 21:32:34 +0100 gnome-menus (2.9.2cvs041212-0ubuntu3) hoary; urgency=low * control.in: should have been kdelibs-data not kdelibs -- Chris Halls Wed, 15 Dec 2004 00:29:08 +0000 gnome-menus (2.9.2cvs041212-0ubuntu2) hoary; urgency=low * control.in: gnome-menus Replaces pre-hoary kdelibs-data (file conflict) -- Chris Halls Tue, 14 Dec 2004 18:51:16 +0000 gnome-menus (2.9.2cvs041212-0ubuntu1) hoary; urgency=low * CVS snapshot. * debian/patches/03_menu_layout.diff: - patch by Vincent Untz for the new menu layout. * debian/patches/04_settings-legacydir.diff: - merged the old capplet .desktop locations here too. -- Sebastien Bacher Sun, 12 Dec 2004 19:44:20 +0100 gnome-menus (2.9.2-0ubuntu3) hoary; urgency=low * debian/patches/*: - Merge the old capplet .desktop locations. -- Jeff Waugh Tue, 7 Dec 2004 16:55:37 +0100 gnome-menus (2.9.2-0ubuntu2) hoary; urgency=low * debian/control.in: - updated the Build-Depends. -- Sebastien Bacher Tue, 30 Nov 2004 19:21:39 +0100 gnome-menus (2.9.2-0ubuntu1) hoary; urgency=low * Initial Release. -- Sebastien Bacher Tue, 30 Nov 2004 12:49:00 +0100 cinnamon-menus-4.4.0/debian/compat000066400000000000000000000000021356375150600171010ustar00rootroot000000000000009 cinnamon-menus-4.4.0/debian/control000066400000000000000000000045151356375150600173130ustar00rootroot00000000000000Source: cinnamon-menus Section: x11 Priority: optional Maintainer: Clement Lefebvre Build-Depends: debhelper (>= 9), meson, dh-python, gnome-common, gnome-pkg-tools, gobject-introspection (>= 0.9.12-4~), gtk-doc-tools (>= 1.4), intltool (>= 0.40.0), libgirepository1.0-dev (>= 0.10.7-1~), libglib2.0-dev (>= 2.30.0), python3 (>= 3.1) Standards-Version: 3.9.5 X-Python3-Version: >= 3.1 Package: libcinnamon-menu-3-0 Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends} Description: Cinnamon implementation of the freedesktop menu specification The package contains an implementation of the draft "Desktop Menu Specification" from freedesktop.org: . http://www.freedesktop.org/Standards/menu-spec . This package contains the shared library. Package: libcinnamon-menu-3-0-dbg Section: debug Priority: extra Architecture: any Depends: libcinnamon-menu-3-0 (= ${binary:Version}), ${misc:Depends} Description: Cinnamon implementation of the freedesktop menu specification The package contains an implementation of the draft "Desktop Menu Specification" from freedesktop.org: . http://www.freedesktop.org/Standards/menu-spec . This package contains debugging symbols. Package: libcinnamon-menu-3-dev Architecture: any Section: libdevel Depends: gir1.2-cmenu-3.0 (= ${binary:Version}), libcinnamon-menu-3-0 (= ${binary:Version}), libglib2.0-dev (>= 2.30.0), ${misc:Depends} Replaces: gir-repository-dev (<< 0.6.5-6) Description: Cinnamon implementation of the freedesktop menu specification The package contains an implementation of the draft "Desktop Menu Specification" from freedesktop.org: . http://www.freedesktop.org/Standards/menu-spec . This package contains the development headers. Package: gir1.2-cmenu-3.0 Section: introspection Architecture: any Depends: ${gir:Depends}, ${misc:Depends}, ${shlibs:Depends} Conflicts: gobject-introspection-repository Description: GObject introspection data for the Cinnamon menu library This package contains introspection data for Cinnamon menu, an implementation of the desktop menu specification from freedesktop.org. . It can be used by languages supporting dynamic bindings with the GIRepository format. cinnamon-menus-4.4.0/debian/copyright000066400000000000000000000076031356375150600176440ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: cinnamon-menus Upstream-Contact: Linux Mint Project Source: https://github.com/linuxmint/cinnamon-menus.git Files: * Copyright: 1996-2011, Free Software Foundation, Inc. License: GPL-2+ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. . On Debian systems, the complete text of the GNU General Public License version 2 can be found in `/usr/share/common-licenses/GPL-2'. Files: libmenu/desktop-entries.c libmenu/desktop-entries.h libmenu/entry-directories.c libmenu/entry-directories.h libmenu/gmenu-tree.c libmenu/gmenu-tree.h libmenu/menu-layout.c libmenu/menu-layout.h libmenu/menu-monitor.c libmenu/menu-monitor.h libmenu/menu-util.c libmenu/menu-util.h Copyright: 2006, Mark McLoughlin 2002-2011, Red Hat, Inc 2007, Sebastian Dröge 2008, Vincent Untz License: LGPL-2+ Files: ltmain.sh Copyright: 1996-2011, Free Software Foundation, Inc. License: GPL-2+ or license of distributed software GNU Libtool is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. . As a special exception to the GNU General Public License, if you distribute this file as part of a program or library that is built using GNU Libtool, you may include this file under the same distribution terms that you use for the rest of that program. . GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy can be downloaded from http://www.gnu.org/licenses/gpl.html, or obtained by writing to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. . On Debian systems, the complete text of the GNU General Public License version 2 can be found in `/usr/share/common-licenses/GPL-2'. Files: debian/* Copyright: 2014, Clement Lefebvre 2014, Maximiliano Curia License: LGPL-2+ License: LGPL-2+ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. . This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. . You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. . On Debian systems, the complete text of the GNU Lesser General Public License version 2 can be found in `/usr/share/common-licenses/LGPL-2'. cinnamon-menus-4.4.0/debian/gir1.2-cmenu-3.0.install.in000066400000000000000000000000341356375150600223020ustar00rootroot00000000000000TYPELIBDIR/girepository-1.0 cinnamon-menus-4.4.0/debian/libcinnamon-menu-3-0.install000066400000000000000000000000211356375150600230140ustar00rootroot00000000000000usr/lib/*/*.so.* cinnamon-menus-4.4.0/debian/libcinnamon-menu-3-0.symbols000066400000000000000000000044421356375150600230510ustar00rootroot00000000000000libcinnamon-menu-3.so.0 libcinnamon-menu-3-0 #MINVER# gmenu_tree_alias_get_aliased_directory@Base 2.2.0 gmenu_tree_alias_get_aliased_entry@Base 2.2.0 gmenu_tree_alias_get_aliased_item_type@Base 2.2.0 gmenu_tree_alias_get_directory@Base 2.2.0 gmenu_tree_alias_get_parent@Base 2.2.0 gmenu_tree_alias_get_tree@Base 2.2.0 gmenu_tree_alias_get_type@Base 2.2.0 gmenu_tree_directory_get_comment@Base 2.2.0 gmenu_tree_directory_get_desktop_file_path@Base 2.2.0 gmenu_tree_directory_get_generic_name@Base 2.2.0 gmenu_tree_directory_get_icon@Base 2.2.0 gmenu_tree_directory_get_is_nodisplay@Base 2.2.0 gmenu_tree_directory_get_menu_id@Base 2.2.0 gmenu_tree_directory_get_name@Base 2.2.0 gmenu_tree_directory_get_parent@Base 2.2.0 gmenu_tree_directory_get_tree@Base 2.2.0 gmenu_tree_directory_get_type@Base 2.2.0 gmenu_tree_directory_iter@Base 2.2.0 gmenu_tree_directory_make_path@Base 2.2.0 gmenu_tree_entry_get_app_info@Base 2.2.0 gmenu_tree_entry_get_desktop_file_id@Base 2.2.0 gmenu_tree_entry_get_desktop_file_path@Base 2.2.0 gmenu_tree_entry_get_is_excluded@Base 2.2.0 gmenu_tree_entry_get_is_nodisplay_recurse@Base 2.2.0 gmenu_tree_entry_get_is_unallocated@Base 2.2.0 gmenu_tree_entry_get_parent@Base 2.2.0 gmenu_tree_entry_get_tree@Base 2.2.0 gmenu_tree_entry_get_type@Base 2.2.0 gmenu_tree_flags_get_type@Base 2.2.0 gmenu_tree_get_canonical_menu_path@Base 2.2.0 gmenu_tree_get_directory_from_path@Base 2.2.0 gmenu_tree_get_entry_by_id@Base 2.2.0 gmenu_tree_get_root_directory@Base 2.2.0 gmenu_tree_get_type@Base 2.2.0 gmenu_tree_header_get_directory@Base 2.2.0 gmenu_tree_header_get_parent@Base 2.2.0 gmenu_tree_header_get_tree@Base 2.2.0 gmenu_tree_header_get_type@Base 2.2.0 gmenu_tree_item_ref@Base 2.2.0 gmenu_tree_item_unref@Base 2.2.0 gmenu_tree_iter_get_alias@Base 2.2.0 gmenu_tree_iter_get_directory@Base 2.2.0 gmenu_tree_iter_get_entry@Base 2.2.0 gmenu_tree_iter_get_header@Base 2.2.0 gmenu_tree_iter_get_separator@Base 2.2.0 gmenu_tree_iter_get_type@Base 2.2.0 gmenu_tree_iter_next@Base 2.2.0 gmenu_tree_iter_ref@Base 2.2.0 gmenu_tree_iter_unref@Base 2.2.0 gmenu_tree_load_sync@Base 2.2.0 gmenu_tree_new@Base 2.2.0 gmenu_tree_new_for_path@Base 2.2.0 gmenu_tree_separator_get_parent@Base 2.2.0 gmenu_tree_separator_get_tree@Base 2.2.0 gmenu_tree_separator_get_type@Base 2.2.0 cinnamon-menus-4.4.0/debian/libcinnamon-menu-3-dev.install000066400000000000000000000001231356375150600234360ustar00rootroot00000000000000usr/include usr/lib/*/*.so usr/lib/*/pkgconfig usr/share/gir-1.0 usr/share/gtk-doc cinnamon-menus-4.4.0/debian/patches/000077500000000000000000000000001356375150600173325ustar00rootroot00000000000000cinnamon-menus-4.4.0/debian/patches/01_default_prefix.patch000066400000000000000000000026451356375150600236630ustar00rootroot00000000000000--- a/libmenu/gmenu-tree.c +++ b/libmenu/gmenu-tree.c @@ -391,6 +391,16 @@ } } +static char * +prefix_menu_name (const char *orig_name) +{ + char *prefix; + prefix = g_getenv ("XDG_MENU_PREFIX"); + if (prefix == NULL) + prefix = "gnome-"; + return g_strconcat (prefix, orig_name, NULL); +} + static gboolean gmenu_tree_canonicalize_path (GMenuTree *tree, GError **error) @@ -416,6 +426,9 @@ menu_file = tree->basename; xdg_menu_prefix = g_getenv ("XDG_MENU_PREFIX"); + if (xdg_menu_prefix == NULL) + xdg_menu_prefix = "gnome-"; + if (xdg_menu_prefix != NULL) { gchar *prefixed_basename; @@ -2077,13 +2090,10 @@ found = FALSE; menu_file = g_strconcat (menu_name, ".menu", NULL); - if (strcmp (menu_file, "applications.menu") == 0 && - g_getenv ("XDG_MENU_PREFIX")) + if (strcmp (menu_file, "applications.menu") == 0) { char *prefixed_basename; - prefixed_basename = g_strdup_printf ("%s%s", - g_getenv ("XDG_MENU_PREFIX"), - menu_file); + prefixed_basename = prefix_menu_name (menu_file); found = load_parent_merge_file_from_basename (tree, loaded_menu_files, layout, prefixed_basename, canonical_basedir); cinnamon-menus-4.4.0/debian/patches/02_kill_debian_menu.patch000066400000000000000000000011321356375150600241320ustar00rootroot00000000000000Index: gnome-menus-3.4.2/libmenu/desktop-entries.c =================================================================== --- gnome-menus-3.4.2.orig/libmenu/desktop-entries.c 2011-10-24 13:48:12.000000000 +0200 +++ gnome-menus-3.4.2/libmenu/desktop-entries.c 2013-01-02 21:11:23.617525227 +0100 @@ -250,6 +250,8 @@ desktop_entry_load_directory (DesktopEnt static gboolean desktop_entry_load (DesktopEntry *entry) { + if (strstr (entry->path, "/menu-xdg/")) + return FALSE; if (entry->type == DESKTOP_ENTRY_DESKTOP) { DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop*)entry; cinnamon-menus-4.4.0/debian/patches/03_kde-legacydirs.patch000066400000000000000000000005131356375150600235430ustar00rootroot00000000000000--- a/layout/gnome-applications.menu +++ b/layout/gnome-applications.menu @@ -7,7 +7,6 @@ X-GNOME-Menu-Applications.directory - /etc/X11/applnk /usr/share/gnome/apps cinnamon-menus-4.4.0/debian/patches/08_settings-menus.patch000066400000000000000000000141621356375150600236530ustar00rootroot00000000000000Index: gnome-menus-3.8.0/layout/gnome-applications.menu =================================================================== --- gnome-menus-3.8.0.orig/layout/gnome-applications.menu 2013-06-14 15:48:17.509604188 -0400 +++ gnome-menus-3.8.0/layout/gnome-applications.menu 2013-06-14 15:57:09.689581931 -0400 @@ -233,6 +233,118 @@ gnome-system-monitor.desktop + + Preferences + Settings.directory + + + Settings + + + System + X-GNOME-Settings-Panel + alacarte.desktop + caribou.desktop + dconf-editor.desktop + fedora-im-chooser.desktop + fedora-release-notes.desktop + firewall-config.desktop + flash-player-properties.desktop + gconf-editor.desktop + gnome-abrt.desktop + fedora-abrt.desktop + gnome-orca.desktop + gnome-power-statistics.desktop + gnome-user-share-properties.desktop + ibus.desktop + ibus-daemon.desktop + ibus-setup-anthy.desktop + ibus-setup.desktop + ibus-setup-hangul.desktop + ibus-setup-libbopomofo.desktop + ibus-setup-libpinyin.desktop + ibus-setup-m17n.desktop + ibus-setup-typing-booster.desktop + im-chooser.desktop + itweb-settings.desktop + jhbuild.desktop + javaws.desktop + java-1.7.0-openjdk-jconsole.desktop + java-1.7.0-openjdk-policytool.desktop + log4j-chainsaw.desktop + log4j-logfactor5.desktop + nm-connection-editor.desktop + orca.desktop + setroubleshoot.desktop + authconfig.desktop + system-config-date.desktop + system-config-firewall.desktop + system-config-keyboard.desktop + system-config-language.desktop + system-config-printer.desktop + system-config-users.desktop + vino-preferences.desktop + + + + + + + Administration + Settings-System.directory + + + Settings + System + + + X-GNOME-Settings-Panel + alacarte.desktop + caribou.desktop + dconf-editor.desktop + fedora-im-chooser.desktop + fedora-release-notes.desktop + firewall-config.desktop + flash-player-properties.desktop + gconf-editor.desktop + gnome-abrt.desktop + fedora-abrt.desktop + gnome-orca.desktop + gnome-power-statistics.desktop + gnome-user-share-properties.desktop + ibus.desktop + ibus-daemon.desktop + ibus-setup-anthy.desktop + ibus-setup.desktop + ibus-setup-hangul.desktop + ibus-setup-libbopomofo.desktop + ibus-setup-libpinyin.desktop + ibus-setup-m17n.desktop + ibus-setup-typing-booster.desktop + im-chooser.desktop + itweb-settings.desktop + jhbuild.desktop + javaws.desktop + java-1.7.0-openjdk-jconsole.desktop + java-1.7.0-openjdk-policytool.desktop + log4j-chainsaw.desktop + log4j-logfactor5.desktop + nm-connection-editor.desktop + orca.desktop + setroubleshoot.desktop + authconfig.desktop + system-config-date.desktop + system-config-firewall.desktop + system-config-keyboard.desktop + system-config-language.desktop + system-config-printer.desktop + system-config-users.desktop + vino-preferences.desktop + + + + + cinnamon-menus-4.4.0/debian/patches/09_app_install_entry.patch000066400000000000000000000013371356375150600244160ustar00rootroot00000000000000Description: Include Software Center in menus Index: gnome-menus-3.8.0/layout/gnome-applications.menu =================================================================== --- gnome-menus-3.8.0.orig/layout/gnome-applications.menu 2013-06-04 18:22:04.293014377 -0400 +++ gnome-menus-3.8.0/layout/gnome-applications.menu 2013-06-04 18:22:04.293014377 -0400 @@ -325,4 +325,16 @@ + + ubuntu-software-center.desktop + + + + + + + + ubuntu-software-center.desktop + + cinnamon-menus-4.4.0/debian/patches/09_games-menu.patch000066400000000000000000000067131356375150600227300ustar00rootroot00000000000000--- a/layout/gnome-applications.menu +++ b/layout/gnome-applications.menu @@ -99,8 +99,107 @@ Game - - + ActionGame + AdventureGame + ArcadeGame + BoardGame + BlocksGame + CardGame + KidsGame + LogicGame + Simulation + SportsGame + StrategyGame + + + + + + + + Action + ActionGames.directory + + ActionGame + + + + Adventure + AdventureGames.directory + + AdventureGame + + + + Arcade + ArcadeGames.directory + + ArcadeGame + + + + Board + BoardGames.directory + + BoardGame + + + + Blocks + BlocksGames.directory + + BlocksGame + + + + Cards + CardGames.directory + + CardGame + + + + Kids + KidsGames.directory + + KidsGame + + + + Logic + LogicGames.directory + + LogicGame + + + + Role Playing + RolePlayingGames.directory + + RolePlaying + + + + Simulation + SimulationGames.directory + + Simulation + + + + Sports + SportsGames.directory + + SportsGame + + + + Strategy + StrategyGames.directory + + StrategyGame + + @@ -223,6 +322,7 @@ System Settings + Game baobab.desktop gnome-system-log.desktop gnome-system-monitor.desktop cinnamon-menus-4.4.0/debian/patches/11_science-menu.patch000066400000000000000000000020701356375150600232260ustar00rootroot00000000000000--- a/layout/gnome-applications.menu +++ b/layout/gnome-applications.menu @@ -88,10 +88,23 @@ Education + Science + + + Science + GnomeScience.directory + + + Education + Science + + + + Games --- a/desktop-directories/Education.directory.in +++ b/desktop-directories/Education.directory.in @@ -1,4 +1,4 @@ [Desktop Entry] _Name=Education -Icon=applications-science +Icon=applications-accessories Type=Directory --- a/desktop-directories/Utility.directory.in +++ b/desktop-directories/Utility.directory.in @@ -1,5 +1,5 @@ [Desktop Entry] _Name=Accessories _Comment=Desktop accessories -Icon=applications-accessories +Icon=applications-utilities Type=Directory cinnamon-menus-4.4.0/debian/patches/20_show-admin-tools-for-admin-group.patch000066400000000000000000000146741356375150600271000ustar00rootroot00000000000000--- gnome-menus-2.19.6.orig/libmenu/desktop-entries.c 2007-07-30 22:06:51.000000000 +0200 +++ gnome-menus-2.19.6/libmenu/desktop-entries.c 2007-08-03 11:38:29.000000000 +0200 @@ -24,6 +24,7 @@ #include #include "menu-util.h" +#include "user-is-sudoer.h" #define DESKTOP_ENTRY_GROUP "Desktop Entry" #define KDE_DESKTOP_ENTRY_GROUP "KDE Desktop Entry" @@ -33,7 +34,8 @@ DESKTOP_ENTRY_NO_DISPLAY = 1 << 0, DESKTOP_ENTRY_HIDDEN = 1 << 1, DESKTOP_ENTRY_SHOW_IN_GNOME = 1 << 2, - DESKTOP_ENTRY_TRYEXEC_FAILED = 1 << 3 + DESKTOP_ENTRY_TRYEXEC_FAILED = 1 << 3, + DESKTOP_ENTRY_ROOT_REQUIRED = 1 << 4 }; struct DesktopEntry @@ -50,7 +52,7 @@ gboolean terminal; guint type : 2; - guint flags : 4; + guint flags : 5; guint refcount : 24; }; @@ -75,9 +77,20 @@ gboolean hidden; gboolean show_in_gnome; gboolean tryexec_failed; + gboolean root_required_flag; char *tryexec; guint flags; int i; + + static gboolean sudoer_flag_set = FALSE; + static gboolean sudoer_flag = TRUE; + + /* If we don't know yet whether the user is sudoer or not, let's see */ + if (!sudoer_flag_set) + { + sudoer_flag = user_is_sudoer (); + sudoer_flag_set = TRUE; + } error = NULL; no_display = g_key_file_get_boolean (key_file, @@ -157,6 +170,47 @@ g_free (tryexec); } + error = NULL; + root_required_flag = g_key_file_get_boolean (key_file, + desktop_entry_group, + "X-KDE-SubstituteUID", + &error); + + if (error) + { + root_required_flag = FALSE; + g_error_free (error); + } + else { + if (root_required_flag) { + char *username = NULL; + username = g_key_file_get_value (key_file, + desktop_entry_group, + "X-KDE-Username", + NULL); + + if (!username || (username && !strcmp (username, "root"))) + root_required_flag = TRUE; + else + root_required_flag = FALSE; + g_free (username); + } + else + root_required_flag = FALSE; + } + + /* + * If the desktop entry has the field and indeed requires root + * privilege and the user isn't sudoer, then the entry needs to be + * hidden. + */ + + if (root_required_flag + && (!sudoer_flag)) + { + no_display = TRUE; + } + flags = 0; if (no_display) flags |= DESKTOP_ENTRY_NO_DISPLAY; @@ -166,6 +220,8 @@ flags |= DESKTOP_ENTRY_SHOW_IN_GNOME; if (tryexec_failed) flags |= DESKTOP_ENTRY_TRYEXEC_FAILED; + if (root_required_flag) + flags |= DESKTOP_ENTRY_ROOT_REQUIRED; return flags; } @@ -297,13 +353,14 @@ #undef GET_LOCALE_STRING - menu_verbose ("Desktop entry \"%s\" (%s, %s, %s) flags: NoDisplay=%s, Hidden=%s, ShowInGNOME=%s, TryExecFailed=%s\n", + menu_verbose ("Desktop entry \"%s\" (%s, %s, %s) flags: NoDisplay=%s, Hidden=%s, RootRequired: %s, ShowInGNOME=%s, TryExecFailed=%s\n", retval->basename, retval->name, retval->comment ? retval->comment : "(null)", retval->icon ? retval->icon : "(null)", retval->flags & DESKTOP_ENTRY_NO_DISPLAY ? "(true)" : "(false)", retval->flags & DESKTOP_ENTRY_HIDDEN ? "(true)" : "(false)", + retval->flags & DESKTOP_ENTRY_ROOT_REQUIRED ? "(true)" : "(false)", retval->flags & DESKTOP_ENTRY_SHOW_IN_GNOME ? "(true)" : "(false)", retval->flags & DESKTOP_ENTRY_TRYEXEC_FAILED ? "(true)" : "(false)"); --- gnome-menus-2.19.6.orig/libmenu/Makefile.am 2007-08-03 11:38:07.000000000 +0200 +++ gnome-menus-2.19.6/libmenu/Makefile.am 2007-08-03 11:38:29.000000000 +0200 @@ -35,6 +35,8 @@ menu-monitor-backend.h \ menu-util.c \ menu-util.h \ + user-is-sudoer.c \ + user-is-sudoer.h \ $(MONITOR_BACKEND_SOURCES) \ $(NULL) --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gnome-menus-2.19.6/libmenu/user-is-sudoer.c 2007-08-03 11:38:29.000000000 +0200 @@ -0,0 +1,58 @@ +/* + * user-is-sudoer.c: + * + * Copyright (C) 2005 Manu Cornet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: + * Manu Cornet + */ + +#include +#include +#include +#include +#include +#include "user-is-sudoer.h" + +#define ADMIN_GROUP_NAME "admin" + +gboolean +user_is_sudoer (void) +{ + const gchar *user_name; + int i = 0; + struct group *group; + + if (getuid() == 0 || g_getenv ("USER_IS_ADMIN")) + return TRUE; + + group = getgrnam (ADMIN_GROUP_NAME); + + if (!group) + return TRUE; + else { + user_name = g_get_user_name (); + + while (group->gr_mem[i]) { + if (!strcmp (user_name, group->gr_mem[i++])) + return TRUE; + } + } + + return FALSE; +} --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gnome-menus-2.19.6/libmenu/user-is-sudoer.h 2007-08-03 11:38:29.000000000 +0200 @@ -0,0 +1,20 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +gboolean user_is_sudoer (void); cinnamon-menus-4.4.0/debian/patches/50_add-gcc-apps.patch000066400000000000000000000007341356375150600231060ustar00rootroot00000000000000Index: gnome-menus-3.7.90/desktop-directories/X-GNOME-SystemSettings.directory.in =================================================================== --- gnome-menus-3.7.90.orig/desktop-directories/X-GNOME-SystemSettings.directory.in 2013-02-15 20:48:56.000000000 -0500 +++ gnome-menus-3.7.90/desktop-directories/X-GNOME-SystemSettings.directory.in 2013-03-03 01:12:40.241503804 -0500 @@ -2,4 +2,3 @@ Name=System Settings Icon=gnome-settings Type=Directory -NoDisplay=true cinnamon-menus-4.4.0/debian/patches/70_ubuntu-directories.patch000066400000000000000000000022551356375150600245210ustar00rootroot00000000000000Index: gnome-menus-3.7.90/po/POTFILES.in =================================================================== --- gnome-menus-3.7.90.orig/po/POTFILES.in 2013-02-19 18:39:14.000000000 -0500 +++ gnome-menus-3.7.90/po/POTFILES.in 2013-03-03 00:59:16.629486723 -0500 @@ -15,3 +15,19 @@ desktop-directories/X-GNOME-Sundry.directory.in desktop-directories/X-GNOME-Utilities.directory.in desktop-directories/X-GNOME-WebApplications.directory.in +debian/desktop-files/ActionGames.directory.in +debian/desktop-files/AdventureGames.directory.in +debian/desktop-files/ArcadeGames.directory.in +debian/desktop-files/BlocksGames.directory.in +debian/desktop-files/BoardGames.directory.in +debian/desktop-files/CardGames.directory.in +debian/desktop-files/Debian.directory.in +debian/desktop-files/GnomeScience.directory.in +debian/desktop-files/KidsGames.directory.in +debian/desktop-files/LogicGames.directory.in +debian/desktop-files/RolePlayingGames.directory.in +debian/desktop-files/Settings-System.directory.in +debian/desktop-files/Settings.directory.in +debian/desktop-files/SimulationGames.directory.in +debian/desktop-files/SportsGames.directory.in +debian/desktop-files/StrategyGames.directory.in cinnamon-menus-4.4.0/debian/patches/git_restore_calculator.patch000066400000000000000000000033271356375150600251170ustar00rootroot00000000000000From 599c7b05c432b1571a7105f1ebf6bbe30c36dbdf Mon Sep 17 00:00:00 2001 From: Kalev Lember Date: Thu, 28 Mar 2013 21:54:34 +0000 Subject: Adapt for gnome-calculator -> gcalctool desktop file rename gnome-calculator.desktop was renamed back to gcalctool.desktop, at the very last minute before the 3.8.0 release. https://bugzilla.gnome.org/show_bug.cgi?id=696816 --- diff --git a/layout/gnome-applications.menu b/layout/gnome-applications.menu index 84d13ed..503f9ca 100644 --- a/layout/gnome-applications.menu +++ b/layout/gnome-applications.menu @@ -38,7 +38,6 @@ file-roller.desktop gnome-file-roller.desktop deja-dup-preferences.desktop - gnome-calculator.desktop gcalctool.desktop gucharmap.desktop gnome-gucharmap.desktop @@ -251,7 +250,7 @@ X-GNOME-Utilities.directory file-roller.desktop - gnome-calculator.desktop + gcalctool.desktop gnome-font-viewer.desktop gucharmap.desktop seahorse.desktop @@ -287,7 +286,6 @@ gnome-eog.desktop gnome-file-roller.desktop gnome-gucharmap.desktop - gcalctool.desktop cinnamon-menus-4.4.0/debian/patches/series000066400000000000000000000004241356375150600205470ustar00rootroot0000000000000001_default_prefix.patch 02_kill_debian_menu.patch 03_kde-legacydirs.patch 08_settings-menus.patch 09_app_install_entry.patch 09_games-menu.patch 11_science-menu.patch 50_add-gcc-apps.patch 70_ubuntu-directories.patch git_restore_calculator.patch ubuntu_gcc_translations.patch cinnamon-menus-4.4.0/debian/patches/ubuntu_gcc_translations.patch000066400000000000000000000077711356375150600253260ustar00rootroot00000000000000# Description: revert dropping of gnome-control-center categories, they got # deprecated in g-c-c 3.8 but we are still using 3.6. We can drop those # changes once we do the update. # Ubuntu: https://bugs.launchpad.net/ubuntu/+source/gnome-menus/+bug/1232534 Index: gnome-menus-3.8.0/desktop-directories/Hardware.directory.in =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gnome-menus-3.8.0/desktop-directories/Hardware.directory.in 2013-10-09 15:21:15.269679845 +0200 @@ -0,0 +1,5 @@ +[Desktop Entry] +_Name=Hardware +_Comment=Settings for several hardware devices +Icon=preferences-desktop-peripherals +Type=Directory Index: gnome-menus-3.8.0/desktop-directories/Makefile.am =================================================================== --- gnome-menus-3.8.0.orig/desktop-directories/Makefile.am 2013-10-09 15:21:15.277679844 +0200 +++ gnome-menus-3.8.0/desktop-directories/Makefile.am 2013-10-09 15:21:15.269679845 +0200 @@ -1,5 +1,11 @@ directorydir = $(datadir)/desktop-directories +# Should be moved to gnome-control-center: +directory_in_controlcenterfiles = \ + Hardware.directory.in \ + Personal.directory.in \ + System.directory.in + directory_in_files = \ AudioVideo.directory.in \ Development.directory.in \ @@ -16,7 +22,8 @@ X-GNOME-Sundry.directory.in \ X-GNOME-Utilities.directory.in \ X-GNOME-WebApplications.directory.in \ - X-GNOME-SystemSettings.directory.in + X-GNOME-SystemSettings.directory.in \ + $(directory_in_controlcenterfiles) directory_DATA = $(directory_in_files:.directory.in=.directory) Index: gnome-menus-3.8.0/desktop-directories/Makefile.in =================================================================== --- gnome-menus-3.8.0.orig/desktop-directories/Makefile.in 2013-10-09 15:21:15.277679844 +0200 +++ gnome-menus-3.8.0/desktop-directories/Makefile.in 2013-10-09 15:21:15.273679844 +0200 @@ -277,6 +277,13 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ directorydir = $(datadir)/desktop-directories + +# Should be moved to gnome-control-center: +directory_in_controlcenterfiles = \ + Hardware.directory.in \ + Personal.directory.in \ + System.directory.in + directory_in_files = \ AudioVideo.directory.in \ Development.directory.in \ @@ -293,7 +300,8 @@ X-GNOME-Sundry.directory.in \ X-GNOME-Utilities.directory.in \ X-GNOME-WebApplications.directory.in \ - X-GNOME-SystemSettings.directory.in + X-GNOME-SystemSettings.directory.in \ + $(directory_in_controlcenterfiles) directory_DATA = $(directory_in_files:.directory.in=.directory) EXTRA_DIST = $(directory_in_files) Index: gnome-menus-3.8.0/desktop-directories/Personal.directory.in =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gnome-menus-3.8.0/desktop-directories/Personal.directory.in 2013-10-09 15:21:15.273679844 +0200 @@ -0,0 +1,6 @@ +[Desktop Entry] +# Translators: this is Personal as in "Personal settings" +_Name=Personal +_Comment=Personal settings +Icon=preferences-desktop-personal +Type=Directory Index: gnome-menus-3.8.0/desktop-directories/System.directory.in =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gnome-menus-3.8.0/desktop-directories/System.directory.in 2013-10-09 15:21:15.273679844 +0200 @@ -0,0 +1,5 @@ +[Desktop Entry] +_Name=System +_Comment=System settings +Icon=preferences-system +Type=Directory Index: gnome-menus-3.8.0/po/POTFILES.in =================================================================== --- gnome-menus-3.8.0.orig/po/POTFILES.in 2013-10-09 15:21:15.277679844 +0200 +++ gnome-menus-3.8.0/po/POTFILES.in 2013-10-09 15:21:15.273679844 +0200 @@ -31,3 +31,7 @@ debian/desktop-files/SimulationGames.directory.in debian/desktop-files/SportsGames.directory.in debian/desktop-files/StrategyGames.directory.in +desktop-directories/Hardware.directory.in +desktop-directories/Personal.directory.in +desktop-directories/System.directory.in + cinnamon-menus-4.4.0/debian/rules000077500000000000000000000015121356375150600167620ustar00rootroot00000000000000#!/usr/bin/make -f export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) TYPELIBDIR=$(shell pkg-config gobject-introspection-1.0 --variable libdir | sed -e 's/.//') %: dh $@ --parallel --with=python3 override_dh_strip: dh_strip --dbg-package=libcinnamon-menu-3-0-dbg override_dh_install: sed 's@TYPELIBDIR@${TYPELIBDIR}@' debian/gir1.2-cmenu-3.0.install.in > debian/gir1.2-cmenu-3.0.install dh_install --list-missing override_dh_auto_configure: meson debian/build \ --prefix=/usr \ --buildtype=plain \ -D deprecated_warnings=false \ -D enable_debug=false \ -D enable_docs=true override_dh_auto_clean: -dh_auto_clean override_dh_auto_install: DESTDIR=${CURDIR}/debian/tmp \ ninja -C debian/build install override_dh_auto_build: ninja -C debian/build cinnamon-menus-4.4.0/debian/source/000077500000000000000000000000001356375150600172035ustar00rootroot00000000000000cinnamon-menus-4.4.0/debian/source/format000066400000000000000000000000151356375150600204120ustar00rootroot000000000000003.0 (native) cinnamon-menus-4.4.0/docs/000077500000000000000000000000001356375150600154115ustar00rootroot00000000000000cinnamon-menus-4.4.0/docs/reference/000077500000000000000000000000001356375150600173475ustar00rootroot00000000000000cinnamon-menus-4.4.0/docs/reference/cmenu-docs.xml000066400000000000000000000016441356375150600221330ustar00rootroot00000000000000 ]> Cinnamon Menus Reference Manual For Cinnamon Menus &version; API Reference Classes API Index Index cinnamon-menus-4.4.0/docs/reference/meson.build000066400000000000000000000006551356375150600215170ustar00rootroot00000000000000version_conf = configuration_data() version_conf.set('VERSION', version) configure_file( input: 'version.xml.in', output: 'version.xml', configuration: version_conf, ) gnome.gtkdoc( 'cmenu', src_dir: join_paths(meson.source_root(), 'libmenu'), main_xml: 'cmenu-docs.xml', scan_args: ['--rebuild-types'], ignore_headers: libmenu_private_headers, dependencies: cmenu_dep, install: true, ) cinnamon-menus-4.4.0/docs/reference/version.xml.in000066400000000000000000000000121356375150600221540ustar00rootroot00000000000000@VERSION@ cinnamon-menus-4.4.0/libmenu/000077500000000000000000000000001356375150600161145ustar00rootroot00000000000000cinnamon-menus-4.4.0/libmenu/desktop-entries.c000066400000000000000000000576171356375150600214200ustar00rootroot00000000000000/* * Copyright (C) 2002 - 2004 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "desktop-entries.h" #include #include #include "menu-util.h" #define DESKTOP_ENTRY_GROUP "Desktop Entry" struct DesktopEntry { guint refcount; char *path; const char *basename; guint type : 2; guint reserved : 30; }; typedef struct { DesktopEntry base; GDesktopAppInfo *appinfo; GQuark *categories; guint showin : 1; } DesktopEntryDesktop; typedef struct { DesktopEntry base; char *name; char *generic_name; char *comment; GIcon *icon; guint nodisplay : 1; guint hidden : 1; guint showin : 1; } DesktopEntryDirectory; struct DesktopEntrySet { int refcount; GHashTable *hash; }; /* * Desktop entries */ /** * unix_basename_from_path: * @path: Path string * * Returns: A constant pointer into the basename of @path */ static const char * unix_basename_from_path (const char *path) { const char *basename = g_strrstr (path, "/"); if (basename) return basename + 1; else return path; } static const char * get_current_desktop (void) { static char *current_desktop = NULL; /* Support XDG_CURRENT_DESKTOP environment variable; this can be used * to abuse gnome-menus in non-GNOME desktops. */ if (!current_desktop) { const char *desktop; desktop = g_getenv ("XDG_CURRENT_DESKTOP"); /* Note: if XDG_CURRENT_DESKTOP is set but empty, do as if it * was not set */ if (!desktop || desktop[0] == '\0') current_desktop = g_strdup ("GNOME"); else current_desktop = g_strdup (desktop); } /* Using "*" means skipping desktop-related checks */ if (g_strcmp0 (current_desktop, "*") == 0) return NULL; return current_desktop; } static GIcon * key_file_get_icon (GKeyFile *key_file) { GIcon *icon = NULL; gchar *icon_name; icon_name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "Icon", NULL, NULL); if (!icon_name) return NULL; if (g_path_is_absolute (icon_name)) { GFile *file; file = g_file_new_for_path (icon_name); icon = g_file_icon_new (file); g_object_unref (file); } else { char *p; /* Work around a common mistake in desktop files */ if ((p = strrchr (icon_name, '.')) != NULL && (strcmp (p, ".png") == 0 || strcmp (p, ".xpm") == 0 || strcmp (p, ".svg") == 0)) *p = 0; icon = g_themed_icon_new (icon_name); } g_free (icon_name); return icon; } static gboolean key_file_get_show_in (GKeyFile *key_file) { const gchar *current_desktop; gchar **strv; gboolean show_in = TRUE; int i; gchar *exec; current_desktop = get_current_desktop (); if (!current_desktop) return TRUE; exec = g_key_file_get_string (key_file, DESKTOP_ENTRY_GROUP, "Exec", NULL); if (exec) { if (g_str_has_prefix (exec, "gnome-control-center")) { g_free (exec); return FALSE; } g_free (exec); } strv = g_key_file_get_string_list (key_file, DESKTOP_ENTRY_GROUP, "OnlyShowIn", NULL, NULL); if (strv) { show_in = FALSE; for (i = 0; strv[i]; i++) { if (!strcmp (strv[i], "GNOME") || !strcmp (strv[i], "X-Cinnamon")) { show_in = TRUE; break; } } } else { strv = g_key_file_get_string_list (key_file, DESKTOP_ENTRY_GROUP, "NotShowIn", NULL, NULL); if (strv) { show_in = TRUE; for (i = 0; strv[i]; i++) { if (!strcmp (strv[i], current_desktop)) { show_in = FALSE; } } } } g_strfreev (strv); return show_in; } static gboolean desktop_entry_load_directory (DesktopEntry *entry, GKeyFile *key_file, GError **error) { DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*)entry; char *type_str; type_str = g_key_file_get_string (key_file, DESKTOP_ENTRY_GROUP, "Type", error); if (!type_str) return FALSE; if (strcmp (type_str, "Directory") != 0) { g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, "\"%s\" does not contain the correct \"Type\" value\n", entry->path); g_free (type_str); return FALSE; } g_free (type_str); entry_directory->name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "Name", NULL, error); if (entry_directory->name == NULL) return FALSE; entry_directory->generic_name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "GenericName", NULL, NULL); entry_directory->comment = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "Comment", NULL, NULL); entry_directory->icon = key_file_get_icon (key_file); entry_directory->nodisplay = g_key_file_get_boolean (key_file, DESKTOP_ENTRY_GROUP, "NoDisplay", NULL); entry_directory->hidden = g_key_file_get_boolean (key_file, DESKTOP_ENTRY_GROUP, "Hidden", NULL); entry_directory->showin = key_file_get_show_in (key_file); return TRUE; } static DesktopEntryResultCode desktop_entry_load (DesktopEntry *entry) { if (strstr (entry->path, "/menu-xdg/")) return DESKTOP_ENTRY_LOAD_FAIL_OTHER; if (entry->type == DESKTOP_ENTRY_DESKTOP) { GKeyFile *key_file = NULL; DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop*)entry; const char *categories_str; entry_desktop->appinfo = g_desktop_app_info_new_from_filename (entry->path); if (!G_IS_DESKTOP_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo)) { menu_verbose ("Failed to load \"%s\"\n", entry->path); return DESKTOP_ENTRY_LOAD_FAIL_APPINFO; } categories_str = g_desktop_app_info_get_categories (entry_desktop->appinfo); if (categories_str) { char **categories; int i; categories = g_strsplit (categories_str, ";", -1); entry_desktop->categories = g_new0 (GQuark, g_strv_length (categories) + 1); for (i = 0; categories[i]; i++) entry_desktop->categories[i] = g_quark_from_string (categories[i]); g_strfreev (categories); } key_file = g_key_file_new (); if (!g_key_file_load_from_file (key_file, entry->path, 0, NULL)) entry_desktop->showin = TRUE; else entry_desktop->showin = key_file_get_show_in (key_file); g_key_file_free (key_file); return DESKTOP_ENTRY_LOAD_SUCCESS; } else if (entry->type == DESKTOP_ENTRY_DIRECTORY) { GKeyFile *key_file = NULL; GError *error = NULL; DesktopEntryResultCode rescode = DESKTOP_ENTRY_LOAD_SUCCESS; key_file = g_key_file_new (); if (!g_key_file_load_from_file (key_file, entry->path, 0, &error)) { rescode = DESKTOP_ENTRY_LOAD_FAIL_OTHER; goto out; } if (!desktop_entry_load_directory (entry, key_file, &error)) { rescode = DESKTOP_ENTRY_LOAD_FAIL_OTHER; goto out; } rescode = DESKTOP_ENTRY_LOAD_SUCCESS; out: g_key_file_free (key_file); if (rescode == DESKTOP_ENTRY_LOAD_FAIL_OTHER) { if (error) { menu_verbose ("Failed to load \"%s\": %s\n", entry->path, error->message); g_error_free (error); } else menu_verbose ("Failed to load \"%s\"\n", entry->path); } return rescode; } else g_assert_not_reached (); return DESKTOP_ENTRY_LOAD_FAIL_OTHER; } DesktopEntry * desktop_entry_new (const char *path, DesktopEntryResultCode *res_code) { DesktopEntryType type; DesktopEntry *retval; DesktopEntryResultCode code; menu_verbose ("Loading desktop entry \"%s\"\n", path); if (g_str_has_suffix (path, ".desktop")) { type = DESKTOP_ENTRY_DESKTOP; retval = (DesktopEntry*)g_new0 (DesktopEntryDesktop, 1); } else if (g_str_has_suffix (path, ".directory")) { type = DESKTOP_ENTRY_DIRECTORY; retval = (DesktopEntry*)g_new0 (DesktopEntryDirectory, 1); } else { menu_verbose ("Unknown desktop entry suffix in \"%s\"\n", path); *res_code = DESKTOP_ENTRY_LOAD_FAIL_OTHER; return NULL; } retval->refcount = 1; retval->type = type; retval->path = g_strdup (path); retval->basename = unix_basename_from_path (retval->path); code = desktop_entry_load (retval); *res_code = code; if (code < DESKTOP_ENTRY_LOAD_SUCCESS) { desktop_entry_unref (retval); return NULL; } return retval; } DesktopEntry * desktop_entry_reload (DesktopEntry *entry) { g_return_val_if_fail (entry != NULL, NULL); menu_verbose ("Re-loading desktop entry \"%s\"\n", entry->path); if (entry->type == DESKTOP_ENTRY_DESKTOP) { DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop *) entry; g_object_unref (entry_desktop->appinfo); entry_desktop->appinfo = NULL; g_free (entry_desktop->categories); entry_desktop->categories = NULL; } else if (entry->type == DESKTOP_ENTRY_DIRECTORY) { DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*) entry; g_free (entry_directory->name); entry_directory->name = NULL; g_free (entry_directory->comment); entry_directory->comment = NULL; g_object_unref (entry_directory->icon); entry_directory->icon = NULL; } else g_assert_not_reached (); if (desktop_entry_load (entry) < DESKTOP_ENTRY_LOAD_SUCCESS) { desktop_entry_unref (entry); return NULL; } return entry; } DesktopEntry * desktop_entry_ref (DesktopEntry *entry) { g_return_val_if_fail (entry != NULL, NULL); g_return_val_if_fail (entry->refcount > 0, NULL); entry->refcount += 1; return entry; } DesktopEntry * desktop_entry_copy (DesktopEntry *entry) { DesktopEntry *retval; menu_verbose ("Copying desktop entry \"%s\"\n", entry->basename); if (entry->type == DESKTOP_ENTRY_DESKTOP) retval = (DesktopEntry*)g_new0 (DesktopEntryDesktop, 1); else if (entry->type == DESKTOP_ENTRY_DIRECTORY) retval = (DesktopEntry*)g_new0 (DesktopEntryDirectory, 1); else g_assert_not_reached (); retval->refcount = 1; retval->type = entry->type; retval->path = g_strdup (entry->path); retval->basename = unix_basename_from_path (retval->path); if (retval->type == DESKTOP_ENTRY_DESKTOP) { DesktopEntryDesktop *desktop_entry = (DesktopEntryDesktop*) entry; DesktopEntryDesktop *retval_desktop_entry = (DesktopEntryDesktop*) retval; int i; retval_desktop_entry->appinfo = g_object_ref (desktop_entry->appinfo); if (desktop_entry->categories != NULL) { i = 0; for (; desktop_entry->categories[i]; i++); retval_desktop_entry->categories = g_new0 (GQuark, i + 1); i = 0; for (; desktop_entry->categories[i]; i++) retval_desktop_entry->categories[i] = desktop_entry->categories[i]; } else retval_desktop_entry->categories = NULL; } else if (entry->type == DESKTOP_ENTRY_DIRECTORY) { DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*)entry; DesktopEntryDirectory *retval_directory = (DesktopEntryDirectory*)retval; retval_directory->name = g_strdup (entry_directory->name); retval_directory->comment = g_strdup (entry_directory->comment); retval_directory->icon = g_object_ref (entry_directory->icon); retval_directory->nodisplay = entry_directory->nodisplay; retval_directory->hidden = entry_directory->hidden; retval_directory->showin = entry_directory->showin; } return retval; } void desktop_entry_unref (DesktopEntry *entry) { g_return_if_fail (entry != NULL); g_return_if_fail (entry->refcount > 0); entry->refcount -= 1; if (entry->refcount != 0) return; g_free (entry->path); entry->path = NULL; if (entry->type == DESKTOP_ENTRY_DESKTOP) { DesktopEntryDesktop *desktop_entry = (DesktopEntryDesktop*) entry; g_free (desktop_entry->categories); if (desktop_entry->appinfo) g_object_unref (desktop_entry->appinfo); } else if (entry->type == DESKTOP_ENTRY_DIRECTORY) { DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*) entry; g_free (entry_directory->name); entry_directory->name = NULL; g_free (entry_directory->comment); entry_directory->comment = NULL; if (entry_directory->icon != NULL) { g_object_unref (entry_directory->icon); entry_directory->icon = NULL; } } else g_assert_not_reached (); g_free (entry); } DesktopEntryType desktop_entry_get_type (DesktopEntry *entry) { return entry->type; } const char * desktop_entry_get_path (DesktopEntry *entry) { return entry->path; } const char * desktop_entry_get_basename (DesktopEntry *entry) { return entry->basename; } const char * desktop_entry_get_name (DesktopEntry *entry) { if (entry->type == DESKTOP_ENTRY_DESKTOP) { g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo), NULL); return g_app_info_get_name (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo)); } return ((DesktopEntryDirectory*)entry)->name; } const char * desktop_entry_get_generic_name (DesktopEntry *entry) { if (entry->type == DESKTOP_ENTRY_DESKTOP) { g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo), NULL); return g_desktop_app_info_get_generic_name (((DesktopEntryDesktop*)entry)->appinfo); } return ((DesktopEntryDirectory*)entry)->generic_name; } const char * desktop_entry_get_comment (DesktopEntry *entry) { if (entry->type == DESKTOP_ENTRY_DESKTOP) { g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo), NULL); return g_app_info_get_description (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo)); } return ((DesktopEntryDirectory*)entry)->comment; } GIcon * desktop_entry_get_icon (DesktopEntry *entry) { if (entry->type == DESKTOP_ENTRY_DESKTOP) { g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo), NULL); return g_app_info_get_icon (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo)); } return ((DesktopEntryDirectory*)entry)->icon; } gboolean desktop_entry_get_no_display (DesktopEntry *entry) { if (entry->type == DESKTOP_ENTRY_DESKTOP) { g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo), FALSE); return g_desktop_app_info_get_nodisplay (((DesktopEntryDesktop*)entry)->appinfo); } return ((DesktopEntryDirectory*)entry)->nodisplay; } gboolean desktop_entry_get_hidden (DesktopEntry *entry) { if (entry->type == DESKTOP_ENTRY_DESKTOP) { g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo), FALSE); return g_desktop_app_info_get_is_hidden (((DesktopEntryDesktop*)entry)->appinfo); } return ((DesktopEntryDirectory*)entry)->hidden; } gboolean desktop_entry_get_show_in (DesktopEntry *entry) { if (entry->type == DESKTOP_ENTRY_DESKTOP) { const char *current_desktop = get_current_desktop (); if (current_desktop == NULL) return TRUE; else { return ((DesktopEntryDesktop *)entry)->showin; } } return ((DesktopEntryDirectory*)entry)->showin; } GDesktopAppInfo * desktop_entry_get_app_info (DesktopEntry *entry) { g_return_val_if_fail (entry->type == DESKTOP_ENTRY_DESKTOP, NULL); return ((DesktopEntryDesktop*)entry)->appinfo; } gboolean desktop_entry_has_categories (DesktopEntry *entry) { DesktopEntryDesktop *desktop_entry; if (entry->type != DESKTOP_ENTRY_DESKTOP) return FALSE; desktop_entry = (DesktopEntryDesktop*) entry; return (desktop_entry->categories != NULL && desktop_entry->categories[0] != 0); } gboolean desktop_entry_has_category (DesktopEntry *entry, const char *category) { GQuark quark; int i; DesktopEntryDesktop *desktop_entry; if (entry->type != DESKTOP_ENTRY_DESKTOP) return FALSE; desktop_entry = (DesktopEntryDesktop*) entry; if (desktop_entry->categories == NULL) return FALSE; if (!(quark = g_quark_try_string (category))) return FALSE; for (i = 0; desktop_entry->categories[i]; i++) { if (quark == desktop_entry->categories[i]) return TRUE; } return FALSE; } /* * Entry sets */ DesktopEntrySet * desktop_entry_set_new (void) { DesktopEntrySet *set; set = g_new0 (DesktopEntrySet, 1); set->refcount = 1; menu_verbose (" New entry set %p\n", set); return set; } DesktopEntrySet * desktop_entry_set_ref (DesktopEntrySet *set) { g_return_val_if_fail (set != NULL, NULL); g_return_val_if_fail (set->refcount > 0, NULL); set->refcount += 1; return set; } void desktop_entry_set_unref (DesktopEntrySet *set) { g_return_if_fail (set != NULL); g_return_if_fail (set->refcount > 0); set->refcount -= 1; if (set->refcount == 0) { menu_verbose (" Deleting entry set %p\n", set); if (set->hash) g_hash_table_destroy (set->hash); set->hash = NULL; g_free (set); } } void desktop_entry_set_add_entry (DesktopEntrySet *set, DesktopEntry *entry, const char *file_id) { menu_verbose (" Adding to set %p entry %s\n", set, file_id); if (set->hash == NULL) { set->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) desktop_entry_unref); } g_hash_table_replace (set->hash, g_strdup (file_id), desktop_entry_ref (entry)); } DesktopEntry * desktop_entry_set_lookup (DesktopEntrySet *set, const char *file_id) { if (set->hash == NULL) return NULL; return g_hash_table_lookup (set->hash, file_id); } typedef struct { DesktopEntrySetForeachFunc func; gpointer user_data; } EntryHashForeachData; static void entry_hash_foreach (const char *file_id, DesktopEntry *entry, EntryHashForeachData *fd) { fd->func (file_id, entry, fd->user_data); } void desktop_entry_set_foreach (DesktopEntrySet *set, DesktopEntrySetForeachFunc func, gpointer user_data) { g_return_if_fail (set != NULL); g_return_if_fail (func != NULL); if (set->hash != NULL) { EntryHashForeachData fd; fd.func = func; fd.user_data = user_data; g_hash_table_foreach (set->hash, (GHFunc) entry_hash_foreach, &fd); } } static void desktop_entry_set_clear (DesktopEntrySet *set) { menu_verbose (" Clearing set %p\n", set); if (set->hash != NULL) { g_hash_table_destroy (set->hash); set->hash = NULL; } } int desktop_entry_set_get_count (DesktopEntrySet *set) { if (set->hash == NULL) return 0; return g_hash_table_size (set->hash); } static void union_foreach (const char *file_id, DesktopEntry *entry, DesktopEntrySet *set) { /* we are iterating over "with" adding anything not * already in "set". We unconditionally overwrite * the stuff in "set" because we can assume * two entries with the same name are equivalent. */ desktop_entry_set_add_entry (set, entry, file_id); } void desktop_entry_set_union (DesktopEntrySet *set, DesktopEntrySet *with) { menu_verbose (" Union of %p and %p\n", set, with); if (desktop_entry_set_get_count (with) == 0) return; /* A fast simple case */ g_hash_table_foreach (with->hash, (GHFunc) union_foreach, set); } typedef struct { DesktopEntrySet *set; DesktopEntrySet *with; } IntersectData; static gboolean intersect_foreach_remove (const char *file_id, DesktopEntry *entry, IntersectData *id) { /* Remove everything in "set" which is not in "with" */ if (g_hash_table_lookup (id->with->hash, file_id) != NULL) return FALSE; menu_verbose (" Removing from %p entry %s\n", id->set, file_id); return TRUE; /* return TRUE to remove */ } void desktop_entry_set_intersection (DesktopEntrySet *set, DesktopEntrySet *with) { IntersectData id; menu_verbose (" Intersection of %p and %p\n", set, with); if (desktop_entry_set_get_count (set) == 0 || desktop_entry_set_get_count (with) == 0) { /* A fast simple case */ desktop_entry_set_clear (set); return; } id.set = set; id.with = with; g_hash_table_foreach_remove (set->hash, (GHRFunc) intersect_foreach_remove, &id); } typedef struct { DesktopEntrySet *set; DesktopEntrySet *other; } SubtractData; static gboolean subtract_foreach_remove (const char *file_id, DesktopEntry *entry, SubtractData *sd) { /* Remove everything in "set" which is not in "other" */ if (g_hash_table_lookup (sd->other->hash, file_id) == NULL) return FALSE; menu_verbose (" Removing from %p entry %s\n", sd->set, file_id); return TRUE; /* return TRUE to remove */ } void desktop_entry_set_subtract (DesktopEntrySet *set, DesktopEntrySet *other) { SubtractData sd; menu_verbose (" Subtract from %p set %p\n", set, other); if (desktop_entry_set_get_count (set) == 0 || desktop_entry_set_get_count (other) == 0) return; /* A fast simple case */ sd.set = set; sd.other = other; g_hash_table_foreach_remove (set->hash, (GHRFunc) subtract_foreach_remove, &sd); } void desktop_entry_set_swap_contents (DesktopEntrySet *a, DesktopEntrySet *b) { GHashTable *tmp; menu_verbose (" Swap contents of %p and %p\n", a, b); tmp = a->hash; a->hash = b->hash; b->hash = tmp; } cinnamon-menus-4.4.0/libmenu/desktop-entries.h000066400000000000000000000100371356375150600214060ustar00rootroot00000000000000/* * Copyright (C) 2002 - 2004 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __DESKTOP_ENTRIES_H__ #define __DESKTOP_ENTRIES_H__ #include G_BEGIN_DECLS typedef enum { DESKTOP_ENTRY_INVALID = 0, DESKTOP_ENTRY_DESKTOP, DESKTOP_ENTRY_DIRECTORY } DesktopEntryType; typedef enum { DESKTOP_ENTRY_LOAD_FAIL_OTHER = 0, DESKTOP_ENTRY_LOAD_FAIL_APPINFO, DESKTOP_ENTRY_LOAD_SUCCESS } DesktopEntryResultCode; typedef struct DesktopEntry DesktopEntry; DesktopEntry *desktop_entry_new (const char *path, DesktopEntryResultCode *res_code); DesktopEntry *desktop_entry_ref (DesktopEntry *entry); DesktopEntry *desktop_entry_copy (DesktopEntry *entry); DesktopEntry *desktop_entry_reload (DesktopEntry *entry); void desktop_entry_unref (DesktopEntry *entry); DesktopEntryType desktop_entry_get_type (DesktopEntry *entry); const char *desktop_entry_get_path (DesktopEntry *entry); const char *desktop_entry_get_basename (DesktopEntry *entry); const char *desktop_entry_get_name (DesktopEntry *entry); const char *desktop_entry_get_generic_name (DesktopEntry *entry); const char *desktop_entry_get_comment (DesktopEntry *entry); GIcon *desktop_entry_get_icon (DesktopEntry *entry); gboolean desktop_entry_get_hidden (DesktopEntry *entry); gboolean desktop_entry_get_no_display (DesktopEntry *entry); gboolean desktop_entry_get_show_in (DesktopEntry *entry); /* Only valid for DESKTOP_ENTRY_DESKTOP */ GDesktopAppInfo *desktop_entry_get_app_info (DesktopEntry *entry); gboolean desktop_entry_has_categories (DesktopEntry *entry); gboolean desktop_entry_has_category (DesktopEntry *entry, const char *category); typedef struct DesktopEntrySet DesktopEntrySet; DesktopEntrySet *desktop_entry_set_new (void); DesktopEntrySet *desktop_entry_set_ref (DesktopEntrySet *set); void desktop_entry_set_unref (DesktopEntrySet *set); void desktop_entry_set_add_entry (DesktopEntrySet *set, DesktopEntry *entry, const char *file_id); DesktopEntry* desktop_entry_set_lookup (DesktopEntrySet *set, const char *file_id); int desktop_entry_set_get_count (DesktopEntrySet *set); void desktop_entry_set_union (DesktopEntrySet *set, DesktopEntrySet *with); void desktop_entry_set_intersection (DesktopEntrySet *set, DesktopEntrySet *with); void desktop_entry_set_subtract (DesktopEntrySet *set, DesktopEntrySet *other); void desktop_entry_set_swap_contents (DesktopEntrySet *a, DesktopEntrySet *b); typedef void (*DesktopEntrySetForeachFunc) (const char *file_id, DesktopEntry *entry, gpointer user_data); void desktop_entry_set_foreach (DesktopEntrySet *set, DesktopEntrySetForeachFunc func, gpointer user_data); G_END_DECLS #endif /* __DESKTOP_ENTRIES_H__ */ cinnamon-menus-4.4.0/libmenu/entry-directories.c000066400000000000000000000762751356375150600217540ustar00rootroot00000000000000/* * Copyright (C) 2002 - 2004 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "entry-directories.h" #include #include #include #include #include #include "menu-util.h" #include "menu-monitor.h" typedef struct CachedDir CachedDir; typedef struct CachedDirMonitor CachedDirMonitor; struct EntryDirectory { CachedDir *dir; guint entry_type : 2; guint refcount : 24; }; struct EntryDirectoryList { int refcount; int length; GList *dirs; }; struct CachedDir { CachedDir *parent; char *name; GSList *entries; GSList *subdirs; GSList *retry_later_desktop_entries; MenuMonitor *dir_monitor; GSList *monitors; guint have_read_entries : 1; guint deleted : 1; guint references; GFunc notify; gpointer notify_data; }; struct CachedDirMonitor { EntryDirectory *ed; EntryDirectoryChangedFunc callback; gpointer user_data; }; static void cached_dir_add_reference (CachedDir *dir); static void cached_dir_remove_reference (CachedDir *dir); static void cached_dir_free (CachedDir *dir); static gboolean cached_dir_load_entries_recursive (CachedDir *dir, const char *dirname); static void cached_dir_unref (CachedDir *dir); static void cached_dir_unref_noparent (CachedDir *dir); static CachedDir * cached_dir_add_subdir (CachedDir *dir, const char *basename, const char *path); static gboolean cached_dir_remove_subdir (CachedDir *dir, const char *basename); static void handle_cached_dir_changed (MenuMonitor *monitor, MenuMonitorEvent event, const char *path, CachedDir *dir); /* * Entry directory cache */ static CachedDir *dir_cache = NULL; static void clear_cache (CachedDir *dir, gpointer *cache) { *cache = NULL; } static CachedDir * cached_dir_new (const char *name) { CachedDir *dir; dir = g_new0 (CachedDir, 1); dir->name = g_strdup (name); return dir; } static CachedDir * cached_dir_new_full (const char *name, GFunc notify, gpointer notify_data) { CachedDir *dir; dir = cached_dir_new (name); dir->notify = notify; dir->notify_data = notify_data; return dir; } static void cached_dir_free (CachedDir *dir) { if (dir->dir_monitor) { menu_monitor_remove_notify (dir->dir_monitor, (MenuMonitorNotifyFunc) handle_cached_dir_changed, dir); menu_monitor_unref (dir->dir_monitor); dir->dir_monitor = NULL; } g_slist_foreach (dir->monitors, (GFunc) g_free, NULL); g_slist_free (dir->monitors); dir->monitors = NULL; g_slist_foreach (dir->entries, (GFunc) desktop_entry_unref, NULL); g_slist_free (dir->entries); dir->entries = NULL; g_slist_foreach (dir->subdirs, (GFunc) cached_dir_unref_noparent, NULL); g_slist_free (dir->subdirs); dir->subdirs = NULL; g_slist_free_full (dir->retry_later_desktop_entries, g_free); dir->retry_later_desktop_entries = NULL; g_free (dir->name); g_free (dir); } static CachedDir * cached_dir_ref (CachedDir *dir) { dir->references++; return dir; } static void cached_dir_unref (CachedDir *dir) { if (--dir->references == 0) { CachedDir *parent; parent = dir->parent; if (parent != NULL) cached_dir_remove_subdir (parent, dir->name); if (dir->notify) dir->notify (dir, dir->notify_data); cached_dir_free (dir); } } static void cached_dir_unref_noparent (CachedDir *dir) { if (--dir->references == 0) { if (dir->notify) dir->notify (dir, dir->notify_data); cached_dir_free (dir); } } static inline CachedDir * find_subdir (CachedDir *dir, const char *subdir) { GSList *tmp; tmp = dir->subdirs; while (tmp != NULL) { CachedDir *sub = tmp->data; if (strcmp (sub->name, subdir) == 0) return sub; tmp = tmp->next; } return NULL; } static DesktopEntry * find_entry (CachedDir *dir, const char *basename) { GSList *tmp; tmp = dir->entries; while (tmp != NULL) { if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0) return tmp->data; tmp = tmp->next; } return NULL; } static DesktopEntry * cached_dir_find_relative_path (CachedDir *dir, const char *relative_path) { DesktopEntry *retval = NULL; char **split; int i; split = g_strsplit (relative_path, "/", -1); i = 0; while (split[i] != NULL) { if (split[i + 1] != NULL) { if ((dir = find_subdir (dir, split[i])) == NULL) break; } else { retval = find_entry (dir, split[i]); break; } ++i; } g_strfreev (split); return retval; } static CachedDir * cached_dir_lookup (const char *canonical) { CachedDir *dir; char **split; int i; if (dir_cache == NULL) dir_cache = cached_dir_new_full ("/", (GFunc) clear_cache, &dir_cache); dir = dir_cache; g_assert (canonical != NULL && canonical[0] == G_DIR_SEPARATOR); menu_verbose ("Looking up cached dir \"%s\"\n", canonical); split = g_strsplit (canonical + 1, "/", -1); i = 0; while (split[i] != NULL) { CachedDir *subdir; subdir = cached_dir_add_subdir (dir, split[i], NULL); dir = subdir; ++i; } g_strfreev (split); g_assert (dir != NULL); return dir; } static gboolean cached_dir_add_entry (CachedDir *dir, const char *basename, const char *path) { DesktopEntry *entry; DesktopEntryResultCode code; entry = desktop_entry_new (path, &code); if (entry == NULL) { if (code == DESKTOP_ENTRY_LOAD_FAIL_APPINFO) { menu_verbose ("Adding %s to the retry list (mimeinfo.cache maybe isn't done getting updated yet\n", path); dir->retry_later_desktop_entries = g_slist_prepend (dir->retry_later_desktop_entries, g_strdup (path)); } return FALSE; } dir->entries = g_slist_prepend (dir->entries, entry); return TRUE; } static gboolean cached_dir_update_entry (CachedDir *dir, const char *basename, const char *path) { GSList *tmp; tmp = dir->entries; while (tmp != NULL) { if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0) { if (!desktop_entry_reload (tmp->data)) { dir->entries = g_slist_delete_link (dir->entries, tmp); } return TRUE; } tmp = tmp->next; } return cached_dir_add_entry (dir, basename, path); } static gboolean cached_dir_remove_entry (CachedDir *dir, const char *basename) { GSList *tmp; tmp = dir->entries; while (tmp != NULL) { if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0) { desktop_entry_unref (tmp->data); dir->entries = g_slist_delete_link (dir->entries, tmp); return TRUE; } tmp = tmp->next; } return FALSE; } static CachedDir * cached_dir_add_subdir (CachedDir *dir, const char *basename, const char *path) { CachedDir *subdir; subdir = find_subdir (dir, basename); if (subdir != NULL) { subdir->deleted = FALSE; return subdir; } subdir = cached_dir_new (basename); if (path != NULL && !cached_dir_load_entries_recursive (subdir, path)) { cached_dir_free (subdir); return NULL; } menu_verbose ("Caching dir \"%s\"\n", basename); subdir->parent = dir; dir->subdirs = g_slist_prepend (dir->subdirs, cached_dir_ref (subdir)); return subdir; } static gboolean cached_dir_remove_subdir (CachedDir *dir, const char *basename) { CachedDir *subdir; subdir = find_subdir (dir, basename); if (subdir != NULL) { subdir->deleted = TRUE; cached_dir_unref (subdir); dir->subdirs = g_slist_remove (dir->subdirs, subdir); return TRUE; } return FALSE; } static guint monitors_idle_handler = 0; static GSList *pending_monitors_dirs = NULL; static void cached_dir_invoke_monitors (CachedDir *dir) { GSList *tmp; tmp = dir->monitors; while (tmp != NULL) { CachedDirMonitor *monitor = tmp->data; GSList *next = tmp->next; monitor->callback (monitor->ed, monitor->user_data); tmp = next; } /* we explicitly don't invoke monitors of the parent since an * event has been queued for it too */ } static gboolean emit_monitors_in_idle (void) { GSList *monitors_to_emit; GSList *tmp; monitors_to_emit = pending_monitors_dirs; pending_monitors_dirs = NULL; monitors_idle_handler = 0; tmp = monitors_to_emit; while (tmp != NULL) { CachedDir *dir = tmp->data; cached_dir_invoke_monitors (dir); cached_dir_remove_reference (dir); tmp = tmp->next; } g_slist_free (monitors_to_emit); return FALSE; } static void cached_dir_queue_monitor_event (CachedDir *dir) { GSList *tmp; tmp = pending_monitors_dirs; while (tmp != NULL) { CachedDir *d = tmp->data; GSList *next = tmp->next; if (dir->parent == d->parent && g_strcmp0 (dir->name, d->name) == 0) break; tmp = next; } /* not found, so let's queue it */ if (tmp == NULL) { cached_dir_add_reference (dir); pending_monitors_dirs = g_slist_append (pending_monitors_dirs, dir); } if (dir->parent) { cached_dir_queue_monitor_event (dir->parent); } if (monitors_idle_handler > 0) { g_source_remove (monitors_idle_handler); } monitors_idle_handler = g_timeout_add (100, (GSourceFunc) emit_monitors_in_idle, NULL); } static void handle_cached_dir_changed (MenuMonitor *monitor, MenuMonitorEvent event, const char *path, CachedDir *dir) { gboolean handled = FALSE; gboolean retry_changes = FALSE; char *basename; char *dirname; dirname = g_path_get_dirname (path); basename = g_path_get_basename (path); dir = cached_dir_lookup (dirname); cached_dir_add_reference (dir); if (g_str_has_suffix (basename, ".desktop") || g_str_has_suffix (basename, ".directory")) { switch (event) { case MENU_MONITOR_EVENT_CREATED: case MENU_MONITOR_EVENT_CHANGED: handled = cached_dir_update_entry (dir, basename, path); break; case MENU_MONITOR_EVENT_DELETED: handled = cached_dir_remove_entry (dir, basename); break; default: g_assert_not_reached (); break; } } else if (g_strcmp0 (basename, "mimeinfo.cache") == 0) { /* The observed file notifies when a new desktop file is added * (but fails to load) go something like: * * NOTIFY: foo.desktop * NOTIFY: mimeinfo.cache.tempfile * NOTIFY: mimeinfo.cache.tempfile * NOTIFY: mimeinfo.cache * * Additionally, the failure is not upon trying to read the file, * but attempting to get its GAppInfo (g_desktop_app_info_new_from_filename() * in desktop-entries.c ln 277). If you jigger desktop_entry_load() around * and read the file as a keyfile *first*, it succeeds. If you then try * to run g_desktop_app_info_new_from_keyfile(), *then* it fails. * * The theory here is there is a race condition where app info (which includes * mimetype stuff) is unavailable because mimeinfo.cache is updated immediately * after the app is installed. * * What we do here is, when a desktop fails to load, we add it to a temporary * list. We wait until mimeinfo.cache changes, then retry that desktop file, * which succeeds this second time. * * Note: An alternative fix (presented more as a proof than a suggestion) is to * change line 151 in menu-monitor.c to use g_timeout_add_seconds, and delay * for one second. This also avoids the issue (but it remains a race condition). */ GSList *iter; menu_verbose ("mimeinfo changed, checking for failed entries\n"); for (iter = dir->retry_later_desktop_entries; iter != NULL; iter = iter->next) { const gchar *retry_path = iter->data; menu_verbose ("retrying %s\n", retry_path); char *retry_basename = g_path_get_basename (retry_path); if (cached_dir_update_entry (dir, retry_basename, retry_path)) retry_changes = TRUE; g_free (retry_basename); } g_slist_free_full (dir->retry_later_desktop_entries, g_free); dir->retry_later_desktop_entries = NULL; handled = retry_changes; } else /* Try recursing */ { switch (event) { case MENU_MONITOR_EVENT_CREATED: handled = cached_dir_add_subdir (dir, basename, path) != NULL; break; case MENU_MONITOR_EVENT_CHANGED: break; case MENU_MONITOR_EVENT_DELETED: handled = cached_dir_remove_subdir (dir, basename); break; default: g_assert_not_reached (); break; } } g_free (basename); g_free (dirname); if (handled) { menu_verbose ("'%s' notified of '%s' %s - invalidating cache\n", dir->name, path, event == MENU_MONITOR_EVENT_CREATED ? ("created") : event == MENU_MONITOR_EVENT_DELETED ? ("deleted") : ("changed")); /* CHANGED events don't change the set of desktop entries, unless it's the mimeinfo.cache file changing */ if (retry_changes || (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED)) { _entry_directory_list_empty_desktop_cache (); } cached_dir_queue_monitor_event (dir); } cached_dir_remove_reference (dir); } static void cached_dir_ensure_monitor (CachedDir *dir, const char *dirname) { if (dir->dir_monitor == NULL) { dir->dir_monitor = menu_get_directory_monitor (dirname); menu_monitor_add_notify (dir->dir_monitor, (MenuMonitorNotifyFunc) handle_cached_dir_changed, dir); } } static gboolean cached_dir_load_entries_recursive (CachedDir *dir, const char *dirname) { DIR *dp; struct dirent *dent; GString *fullpath; gsize fullpath_len; g_assert (dir != NULL); if (dir->have_read_entries) return TRUE; menu_verbose ("Attempting to read entries from %s (full path %s)\n", dir->name, dirname); dp = opendir (dirname); if (dp == NULL) { menu_verbose ("Unable to list directory \"%s\"\n", dirname); return FALSE; } cached_dir_ensure_monitor (dir, dirname); fullpath = g_string_new (dirname); if (fullpath->str[fullpath->len - 1] != G_DIR_SEPARATOR) g_string_append_c (fullpath, G_DIR_SEPARATOR); fullpath_len = fullpath->len; while ((dent = readdir (dp)) != NULL) { /* ignore . and .. */ if (dent->d_name[0] == '.' && (dent->d_name[1] == '\0' || (dent->d_name[1] == '.' && dent->d_name[2] == '\0'))) continue; g_string_append (fullpath, dent->d_name); if (g_str_has_suffix (dent->d_name, ".desktop") || g_str_has_suffix (dent->d_name, ".directory")) { cached_dir_add_entry (dir, dent->d_name, fullpath->str); } else /* Try recursing */ { cached_dir_add_subdir (dir, dent->d_name, fullpath->str); } g_string_truncate (fullpath, fullpath_len); } closedir (dp); g_string_free (fullpath, TRUE); dir->have_read_entries = TRUE; return TRUE; } static void cached_dir_add_monitor (CachedDir *dir, EntryDirectory *ed, EntryDirectoryChangedFunc callback, gpointer user_data) { CachedDirMonitor *monitor; GSList *tmp; tmp = dir->monitors; while (tmp != NULL) { monitor = tmp->data; if (monitor->ed == ed && monitor->callback == callback && monitor->user_data == user_data) break; tmp = tmp->next; } if (tmp == NULL) { monitor = g_new0 (CachedDirMonitor, 1); monitor->ed = ed; monitor->callback = callback; monitor->user_data = user_data; dir->monitors = g_slist_append (dir->monitors, monitor); } } static void cached_dir_remove_monitor (CachedDir *dir, EntryDirectory *ed, EntryDirectoryChangedFunc callback, gpointer user_data) { GSList *tmp; tmp = dir->monitors; while (tmp != NULL) { CachedDirMonitor *monitor = tmp->data; GSList *next = tmp->next; if (monitor->ed == ed && monitor->callback == callback && monitor->user_data == user_data) { dir->monitors = g_slist_delete_link (dir->monitors, tmp); g_free (monitor); } tmp = next; } } static void cached_dir_add_reference (CachedDir *dir) { cached_dir_ref (dir); if (dir->parent != NULL) { cached_dir_add_reference (dir->parent); } } static void cached_dir_remove_reference (CachedDir *dir) { CachedDir *parent; parent = dir->parent; cached_dir_unref (dir); if (parent != NULL) { cached_dir_remove_reference (parent); } } /* * Entry directories */ EntryDirectory * entry_directory_new (DesktopEntryType entry_type, const char *path) { EntryDirectory *ed; char *canonical; menu_verbose ("Loading entry directory \"%s\"\n", path); canonical = realpath (path, NULL); if (canonical == NULL) { menu_verbose ("Failed to canonicalize \"%s\": %s\n", path, g_strerror (errno)); return NULL; } ed = g_new0 (EntryDirectory, 1); ed->dir = cached_dir_lookup (canonical); g_assert (ed->dir != NULL); cached_dir_add_reference (ed->dir); cached_dir_load_entries_recursive (ed->dir, canonical); ed->entry_type = entry_type; ed->refcount = 1; g_free (canonical); return ed; } EntryDirectory * entry_directory_ref (EntryDirectory *ed) { g_return_val_if_fail (ed != NULL, NULL); g_return_val_if_fail (ed->refcount > 0, NULL); ed->refcount++; return ed; } void entry_directory_unref (EntryDirectory *ed) { g_return_if_fail (ed != NULL); g_return_if_fail (ed->refcount > 0); if (--ed->refcount == 0) { cached_dir_remove_reference (ed->dir); ed->dir = NULL; ed->entry_type = DESKTOP_ENTRY_INVALID; g_free (ed); } } static void entry_directory_add_monitor (EntryDirectory *ed, EntryDirectoryChangedFunc callback, gpointer user_data) { cached_dir_add_monitor (ed->dir, ed, callback, user_data); } static void entry_directory_remove_monitor (EntryDirectory *ed, EntryDirectoryChangedFunc callback, gpointer user_data) { cached_dir_remove_monitor (ed->dir, ed, callback, user_data); } static DesktopEntry * entry_directory_get_directory (EntryDirectory *ed, const char *relative_path) { DesktopEntry *entry; if (ed->entry_type != DESKTOP_ENTRY_DIRECTORY) return NULL; entry = cached_dir_find_relative_path (ed->dir, relative_path); if (entry == NULL || desktop_entry_get_type (entry) != DESKTOP_ENTRY_DIRECTORY) return NULL; return desktop_entry_ref (entry); } static char * get_desktop_file_id_from_path (EntryDirectory *ed, DesktopEntryType entry_type, const char *relative_path) { char *retval; retval = NULL; if (entry_type == DESKTOP_ENTRY_DESKTOP) { retval = g_strdelimit (g_strdup (relative_path), "/", '-'); } else { retval = g_strdup (relative_path); } return retval; } typedef gboolean (* EntryDirectoryForeachFunc) (EntryDirectory *ed, DesktopEntry *entry, const char *file_id, DesktopEntrySet *set, gpointer user_data); static gboolean entry_directory_foreach_recursive (EntryDirectory *ed, CachedDir *cd, GString *relative_path, EntryDirectoryForeachFunc func, DesktopEntrySet *set, gpointer user_data) { GSList *tmp; int relative_path_len; if (cd->deleted) return TRUE; relative_path_len = relative_path->len; tmp = cd->entries; while (tmp != NULL) { DesktopEntry *entry = tmp->data; if (desktop_entry_get_type (entry) == ed->entry_type) { gboolean ret; char *file_id; g_string_append (relative_path, desktop_entry_get_basename (entry)); file_id = get_desktop_file_id_from_path (ed, ed->entry_type, relative_path->str); ret = func (ed, entry, file_id, set, user_data); g_free (file_id); g_string_truncate (relative_path, relative_path_len); if (!ret) return FALSE; } tmp = tmp->next; } tmp = cd->subdirs; while (tmp != NULL) { CachedDir *subdir = tmp->data; g_string_append (relative_path, subdir->name); g_string_append_c (relative_path, G_DIR_SEPARATOR); if (!entry_directory_foreach_recursive (ed, subdir, relative_path, func, set, user_data)) return FALSE; g_string_truncate (relative_path, relative_path_len); tmp = tmp->next; } return TRUE; } static void entry_directory_foreach (EntryDirectory *ed, EntryDirectoryForeachFunc func, DesktopEntrySet *set, gpointer user_data) { GString *path; path = g_string_new (NULL); entry_directory_foreach_recursive (ed, ed->dir, path, func, set, user_data); g_string_free (path, TRUE); } void entry_directory_get_flat_contents (EntryDirectory *ed, DesktopEntrySet *desktop_entries, DesktopEntrySet *directory_entries, GSList **subdirs) { GSList *tmp; if (subdirs) *subdirs = NULL; tmp = ed->dir->entries; while (tmp != NULL) { DesktopEntry *entry = tmp->data; const char *basename; basename = desktop_entry_get_basename (entry); if (desktop_entries && desktop_entry_get_type (entry) == DESKTOP_ENTRY_DESKTOP) { char *file_id; file_id = get_desktop_file_id_from_path (ed, DESKTOP_ENTRY_DESKTOP, basename); desktop_entry_set_add_entry (desktop_entries, entry, file_id); g_free (file_id); } if (directory_entries && desktop_entry_get_type (entry) == DESKTOP_ENTRY_DIRECTORY) { desktop_entry_set_add_entry (directory_entries, entry, basename); } tmp = tmp->next; } if (subdirs) { tmp = ed->dir->subdirs; while (tmp != NULL) { CachedDir *cd = tmp->data; if (!cd->deleted) { *subdirs = g_slist_prepend (*subdirs, g_strdup (cd->name)); } tmp = tmp->next; } } if (subdirs) *subdirs = g_slist_reverse (*subdirs); } /* * Entry directory lists */ EntryDirectoryList * entry_directory_list_new (void) { EntryDirectoryList *list; list = g_new0 (EntryDirectoryList, 1); list->refcount = 1; list->dirs = NULL; list->length = 0; return list; } EntryDirectoryList * entry_directory_list_ref (EntryDirectoryList *list) { g_return_val_if_fail (list != NULL, NULL); g_return_val_if_fail (list->refcount > 0, NULL); list->refcount += 1; return list; } void entry_directory_list_unref (EntryDirectoryList *list) { g_return_if_fail (list != NULL); g_return_if_fail (list->refcount > 0); list->refcount -= 1; if (list->refcount == 0) { g_list_foreach (list->dirs, (GFunc) entry_directory_unref, NULL); g_list_free (list->dirs); list->dirs = NULL; list->length = 0; g_free (list); } } void entry_directory_list_prepend (EntryDirectoryList *list, EntryDirectory *ed) { list->length += 1; list->dirs = g_list_prepend (list->dirs, entry_directory_ref (ed)); } int entry_directory_list_get_length (EntryDirectoryList *list) { return list->length; } void entry_directory_list_append_list (EntryDirectoryList *list, EntryDirectoryList *to_append) { GList *tmp; GList *new_dirs = NULL; if (to_append->length == 0) return; tmp = to_append->dirs; while (tmp != NULL) { list->length += 1; new_dirs = g_list_prepend (new_dirs, entry_directory_ref (tmp->data)); tmp = tmp->next; } new_dirs = g_list_reverse (new_dirs); list->dirs = g_list_concat (list->dirs, new_dirs); } DesktopEntry * entry_directory_list_get_directory (EntryDirectoryList *list, const char *relative_path) { DesktopEntry *retval = NULL; GList *tmp; tmp = list->dirs; while (tmp != NULL) { if ((retval = entry_directory_get_directory (tmp->data, relative_path)) != NULL) break; tmp = tmp->next; } return retval; } gboolean _entry_directory_list_compare (const EntryDirectoryList *a, const EntryDirectoryList *b) { GList *al, *bl; if (a == NULL && b == NULL) return TRUE; if ((a == NULL || b == NULL)) return FALSE; if (a->length != b->length) return FALSE; al = a->dirs; bl = b->dirs; while (al && bl && al->data == bl->data) { al = al->next; bl = bl->next; } return (al == NULL && bl == NULL); } static gboolean get_all_func (EntryDirectory *ed, DesktopEntry *entry, const char *file_id, DesktopEntrySet *set, gpointer user_data) { entry = desktop_entry_ref (entry); desktop_entry_set_add_entry (set, entry, file_id); desktop_entry_unref (entry); return TRUE; } static DesktopEntrySet *entry_directory_last_set = NULL; static EntryDirectoryList *entry_directory_last_list = NULL; void _entry_directory_list_empty_desktop_cache (void) { if (entry_directory_last_set != NULL) desktop_entry_set_unref (entry_directory_last_set); entry_directory_last_set = NULL; if (entry_directory_last_list != NULL) entry_directory_list_unref (entry_directory_last_list); entry_directory_last_list = NULL; } DesktopEntrySet * _entry_directory_list_get_all_desktops (EntryDirectoryList *list) { GList *tmp; DesktopEntrySet *set; /* The only tricky thing here is that desktop files later * in the search list with the same relative path * are "hidden" by desktop files earlier in the path, * so we have to do the earlier files first causing * the later files to replace the earlier files * in the DesktopEntrySet * * We go from the end of the list so we can just * g_hash_table_replace and not have to do two * hash lookups (check for existing entry, then insert new * entry) */ /* This method is -extremely- slow, so we have a simple one-entry cache here */ if (_entry_directory_list_compare (list, entry_directory_last_list)) { menu_verbose (" Hit desktop list (%p) cache\n", list); return desktop_entry_set_ref (entry_directory_last_set); } if (entry_directory_last_set != NULL) desktop_entry_set_unref (entry_directory_last_set); if (entry_directory_last_list != NULL) entry_directory_list_unref (entry_directory_last_list); set = desktop_entry_set_new (); menu_verbose (" Storing all of list %p in set %p\n", list, set); tmp = g_list_last (list->dirs); while (tmp != NULL) { entry_directory_foreach (tmp->data, get_all_func, set, NULL); tmp = tmp->prev; } entry_directory_last_list = entry_directory_list_ref (list); entry_directory_last_set = desktop_entry_set_ref (set); return set; } void entry_directory_list_add_monitors (EntryDirectoryList *list, EntryDirectoryChangedFunc callback, gpointer user_data) { GList *tmp; tmp = list->dirs; while (tmp != NULL) { entry_directory_add_monitor (tmp->data, callback, user_data); tmp = tmp->next; } } void entry_directory_list_remove_monitors (EntryDirectoryList *list, EntryDirectoryChangedFunc callback, gpointer user_data) { GList *tmp; tmp = list->dirs; while (tmp != NULL) { entry_directory_remove_monitor (tmp->data, callback, user_data); tmp = tmp->next; } } cinnamon-menus-4.4.0/libmenu/entry-directories.h000066400000000000000000000062321356375150600217430ustar00rootroot00000000000000/* * Copyright (C) 2002 - 2004 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ENTRY_DIRECTORIES_H__ #define __ENTRY_DIRECTORIES_H__ #include #include "desktop-entries.h" G_BEGIN_DECLS typedef struct EntryDirectory EntryDirectory; typedef void (*EntryDirectoryChangedFunc) (EntryDirectory *ed, gpointer user_data); EntryDirectory *entry_directory_new (DesktopEntryType entry_type, const char *path); EntryDirectory *entry_directory_ref (EntryDirectory *ed); void entry_directory_unref (EntryDirectory *ed); void entry_directory_get_flat_contents (EntryDirectory *ed, DesktopEntrySet *desktop_entries, DesktopEntrySet *directory_entries, GSList **subdirs); typedef struct EntryDirectoryList EntryDirectoryList; EntryDirectoryList *entry_directory_list_new (void); EntryDirectoryList *entry_directory_list_ref (EntryDirectoryList *list); void entry_directory_list_unref (EntryDirectoryList *list); int entry_directory_list_get_length (EntryDirectoryList *list); gboolean _entry_directory_list_compare (const EntryDirectoryList *a, const EntryDirectoryList *b); void entry_directory_list_prepend (EntryDirectoryList *list, EntryDirectory *ed); void entry_directory_list_append_list (EntryDirectoryList *list, EntryDirectoryList *to_append); void entry_directory_list_add_monitors (EntryDirectoryList *list, EntryDirectoryChangedFunc callback, gpointer user_data); void entry_directory_list_remove_monitors (EntryDirectoryList *list, EntryDirectoryChangedFunc callback, gpointer user_data); DesktopEntry* entry_directory_list_get_directory (EntryDirectoryList *list, const char *relative_path); DesktopEntrySet *_entry_directory_list_get_all_desktops (EntryDirectoryList *list); void _entry_directory_list_empty_desktop_cache (void); G_END_DECLS #endif /* __ENTRY_DIRECTORIES_H__ */ cinnamon-menus-4.4.0/libmenu/gmenu-tree.c000066400000000000000000003631071356375150600203420ustar00rootroot00000000000000/* -*- mode:c; c-file-style: "gnu"; indent-tabs-mode: nil -*- * Copyright (C) 2003, 2004, 2011 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "gmenu-tree.h" #include #include #include #include "menu-layout.h" #include "menu-monitor.h" #include "menu-util.h" /* private */ typedef struct GMenuTreeItem GMenuTreeItem; #define GMENU_TREE_ITEM(i) ((GMenuTreeItem *)(i)) #define GMENU_TREE_DIRECTORY(i) ((GMenuTreeDirectory *)(i)) #define GMENU_TREE_ENTRY(i) ((GMenuTreeEntry *)(i)) #define GMENU_TREE_SEPARATOR(i) ((GMenuTreeSeparator *)(i)) #define GMENU_TREE_HEADER(i) ((GMenuTreeHeader *)(i)) #define GMENU_TREE_ALIAS(i) ((GMenuTreeAlias *)(i)) enum { PROP_0, PROP_MENU_BASENAME, PROP_MENU_PATH, PROP_FLAGS }; /* Signals */ enum { CHANGED, LAST_SIGNAL }; static guint gmenu_tree_signals [LAST_SIGNAL] = { 0 }; struct _GMenuTree { GObject parent_instance; char *basename; char *non_prefixed_basename; char *path; char *canonical_path; GMenuTreeFlags flags; GSList *menu_file_monitors; MenuLayoutNode *layout; GMenuTreeDirectory *root; GHashTable *entries_by_id; guint canonical : 1; guint loaded : 1; }; G_DEFINE_TYPE (GMenuTree, gmenu_tree, G_TYPE_OBJECT) struct GMenuTreeItem { volatile gint refcount; GMenuTreeItemType type; GMenuTreeDirectory *parent; GMenuTree *tree; }; struct GMenuTreeIter { volatile gint refcount; GMenuTreeItem *item; GSList *contents; GSList *contents_iter; }; struct GMenuTreeDirectory { GMenuTreeItem item; DesktopEntry *directory_entry; char *name; GSList *entries; GSList *subdirs; MenuLayoutValues default_layout_values; GSList *default_layout_info; GSList *layout_info; GSList *contents; guint only_unallocated : 1; guint is_nodisplay : 1; guint layout_pending_separator : 1; guint preprocessed : 1; /* 16 bits should be more than enough; G_MAXUINT16 means no inline header */ guint will_inline_header : 16; }; struct GMenuTreeEntry { GMenuTreeItem item; DesktopEntry *desktop_entry; char *desktop_file_id; guint is_excluded : 1; guint is_unallocated : 1; }; struct GMenuTreeSeparator { GMenuTreeItem item; }; struct GMenuTreeHeader { GMenuTreeItem item; GMenuTreeDirectory *directory; }; struct GMenuTreeAlias { GMenuTreeItem item; GMenuTreeDirectory *directory; GMenuTreeItem *aliased_item; }; static gboolean gmenu_tree_load_layout (GMenuTree *tree, GError **error); static void gmenu_tree_force_reload (GMenuTree *tree); static gboolean gmenu_tree_build_from_layout (GMenuTree *tree, GError **error); static void gmenu_tree_force_rebuild (GMenuTree *tree); static void gmenu_tree_resolve_files (GMenuTree *tree, GHashTable *loaded_menu_files, MenuLayoutNode *layout); static void gmenu_tree_force_recanonicalize (GMenuTree *tree); static void gmenu_tree_invoke_monitors (GMenuTree *tree); static void gmenu_tree_item_unref_and_unset_parent (gpointer itemp); typedef enum { MENU_FILE_MONITOR_INVALID = 0, MENU_FILE_MONITOR_FILE, MENU_FILE_MONITOR_NONEXISTENT_FILE, MENU_FILE_MONITOR_DIRECTORY } MenuFileMonitorType; typedef struct { MenuFileMonitorType type; MenuMonitor *monitor; } MenuFileMonitor; static void handle_nonexistent_menu_file_changed (MenuMonitor *monitor, MenuMonitorEvent event, const char *path, GMenuTree *tree) { if (event == MENU_MONITOR_EVENT_CHANGED || event == MENU_MONITOR_EVENT_CREATED) { menu_verbose ("\"%s\" %s, marking tree for recanonicalization\n", path, event == MENU_MONITOR_EVENT_CREATED ? "created" : "changed"); gmenu_tree_force_recanonicalize (tree); gmenu_tree_invoke_monitors (tree); } } static void handle_menu_file_changed (MenuMonitor *monitor, MenuMonitorEvent event, const char *path, GMenuTree *tree) { menu_verbose ("\"%s\" %s, marking tree for recanicalization\n", path, event == MENU_MONITOR_EVENT_CREATED ? "created" : event == MENU_MONITOR_EVENT_CHANGED ? "changed" : "deleted"); gmenu_tree_force_recanonicalize (tree); gmenu_tree_invoke_monitors (tree); } static void handle_menu_file_directory_changed (MenuMonitor *monitor, MenuMonitorEvent event, const char *path, GMenuTree *tree) { if (!g_str_has_suffix (path, ".menu")) return; menu_verbose ("\"%s\" %s, marking tree for recanicalization\n", path, event == MENU_MONITOR_EVENT_CREATED ? "created" : event == MENU_MONITOR_EVENT_CHANGED ? "changed" : "deleted"); gmenu_tree_force_recanonicalize (tree); gmenu_tree_invoke_monitors (tree); } static void gmenu_tree_add_menu_file_monitor (GMenuTree *tree, const char *path, MenuFileMonitorType type) { MenuFileMonitor *monitor; monitor = g_slice_new0 (MenuFileMonitor); monitor->type = type; switch (type) { case MENU_FILE_MONITOR_FILE: menu_verbose ("Adding a menu file monitor for \"%s\"\n", path); monitor->monitor = menu_get_file_monitor (path); menu_monitor_add_notify (monitor->monitor, (MenuMonitorNotifyFunc) handle_menu_file_changed, tree); break; case MENU_FILE_MONITOR_NONEXISTENT_FILE: menu_verbose ("Adding a menu file monitor for non-existent \"%s\"\n", path); monitor->monitor = menu_get_file_monitor (path); menu_monitor_add_notify (monitor->monitor, (MenuMonitorNotifyFunc) handle_nonexistent_menu_file_changed, tree); break; case MENU_FILE_MONITOR_DIRECTORY: menu_verbose ("Adding a menu directory monitor for \"%s\"\n", path); monitor->monitor = menu_get_directory_monitor (path); menu_monitor_add_notify (monitor->monitor, (MenuMonitorNotifyFunc) handle_menu_file_directory_changed, tree); break; default: g_assert_not_reached (); break; } tree->menu_file_monitors = g_slist_prepend (tree->menu_file_monitors, monitor); } static void remove_menu_file_monitor (MenuFileMonitor *monitor, GMenuTree *tree) { switch (monitor->type) { case MENU_FILE_MONITOR_FILE: menu_monitor_remove_notify (monitor->monitor, (MenuMonitorNotifyFunc) handle_menu_file_changed, tree); break; case MENU_FILE_MONITOR_NONEXISTENT_FILE: menu_monitor_remove_notify (monitor->monitor, (MenuMonitorNotifyFunc) handle_nonexistent_menu_file_changed, tree); break; case MENU_FILE_MONITOR_DIRECTORY: menu_monitor_remove_notify (monitor->monitor, (MenuMonitorNotifyFunc) handle_menu_file_directory_changed, tree); break; default: g_assert_not_reached (); break; } menu_monitor_unref (monitor->monitor); monitor->monitor = NULL; monitor->type = MENU_FILE_MONITOR_INVALID; g_slice_free (MenuFileMonitor, monitor); } static void gmenu_tree_remove_menu_file_monitors (GMenuTree *tree) { menu_verbose ("Removing all menu file monitors\n"); g_slist_foreach (tree->menu_file_monitors, (GFunc) remove_menu_file_monitor, tree); g_slist_free (tree->menu_file_monitors); tree->menu_file_monitors = NULL; } static gboolean canonicalize_path (GMenuTree *tree, const char *path) { tree->canonical_path = realpath (path, NULL); if (tree->canonical_path) { tree->canonical = TRUE; gmenu_tree_add_menu_file_monitor (tree, tree->canonical_path, MENU_FILE_MONITOR_FILE); } else { gmenu_tree_add_menu_file_monitor (tree, path, MENU_FILE_MONITOR_NONEXISTENT_FILE); } return tree->canonical; } static gboolean canonicalize_basename_with_config_dir (GMenuTree *tree, const char *basename, const char *config_dir) { gboolean ret; char *path; path = g_build_filename (config_dir, "menus", basename, NULL); ret = canonicalize_path (tree, path); g_free (path); return ret; } static void canonicalize_basename (GMenuTree *tree, const char *basename) { if (!canonicalize_basename_with_config_dir (tree, basename, g_get_user_config_dir ())) { const char * const *system_config_dirs; int i; system_config_dirs = g_get_system_config_dirs (); i = 0; while (system_config_dirs[i] != NULL) { if (canonicalize_basename_with_config_dir (tree, basename, system_config_dirs[i])) break; ++i; } } } static char * prefix_menu_name (const char *orig_name) { char *prefix; prefix = (char *) g_getenv ("XDG_MENU_PREFIX"); if (prefix == NULL) prefix = "gnome-"; return g_strconcat (prefix, orig_name, NULL); } static gboolean gmenu_tree_canonicalize_path (GMenuTree *tree, GError **error) { const char *menu_file = NULL; if (tree->canonical) return TRUE; g_assert (tree->canonical_path == NULL); gmenu_tree_remove_menu_file_monitors (tree); if (tree->path) { menu_file = tree->path; canonicalize_path (tree, tree->path); } else { const gchar *xdg_menu_prefix; menu_file = tree->basename; xdg_menu_prefix = g_getenv ("XDG_MENU_PREFIX"); if (xdg_menu_prefix == NULL) xdg_menu_prefix = "gnome-"; if (xdg_menu_prefix != NULL) { gchar *prefixed_basename; prefixed_basename = g_strdup_printf ("%sapplications.menu", xdg_menu_prefix); /* Some gnome-menus using applications just use "applications.menu" * as the basename and expect gnome-menus to prefix it. Others (e.g. * Alacarte) explicitly use "${XDG_MENU_PREFIX}applications.menu" as * the basename, because they want to save changes to the right files * in ~. In both cases, we want to use "applications-merged" as the * merge directory (as required by the fd.o menu spec), so we save * the non-prefixed basename and use it later when calling * menu_layout_load(). */ if (!g_strcmp0 (tree->basename, "applications.menu") || !g_strcmp0 (tree->basename, prefixed_basename)) { canonicalize_basename (tree, prefixed_basename); g_free (tree->non_prefixed_basename); tree->non_prefixed_basename = g_strdup ("applications.menu"); } g_free (prefixed_basename); } if (!tree->canonical) canonicalize_basename (tree, tree->basename); } if (tree->canonical) { menu_verbose ("Successfully looked up menu_file for \"%s\": %s\n", menu_file, tree->canonical_path); return TRUE; } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to look up menu_file for \"%s\"\n", menu_file); return FALSE; } } static void gmenu_tree_force_recanonicalize (GMenuTree *tree) { gmenu_tree_remove_menu_file_monitors (tree); if (tree->canonical) { gmenu_tree_force_reload (tree); g_free (tree->canonical_path); tree->canonical_path = NULL; tree->canonical = FALSE; } } /** * gmenu_tree_new: * @menu_basename: Basename of menu file * @flags: Flags controlling menu content * * Returns: (transfer full): A new #GMenuTree instance */ GMenuTree * gmenu_tree_new (const char *menu_basename, GMenuTreeFlags flags) { g_return_val_if_fail (menu_basename != NULL, NULL); return g_object_new (GMENU_TYPE_TREE, "menu-basename", menu_basename, "flags", flags, NULL); } /** * gmenu_tree_new_fo_path: * @menu_path: Path of menu file * @flags: Flags controlling menu content * * Returns: (transfer full): A new #GMenuTree instance */ GMenuTree * gmenu_tree_new_for_path (const char *menu_path, GMenuTreeFlags flags) { g_return_val_if_fail (menu_path != NULL, NULL); return g_object_new (GMENU_TYPE_TREE, "menu-path", menu_path, "flags", flags, NULL); } static GObject * gmenu_tree_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GObject *obj; GMenuTree *self; obj = G_OBJECT_CLASS (gmenu_tree_parent_class)->constructor (type, n_construct_properties, construct_properties); /* If GMenuTree:menu-path is set, then we should make sure that * GMenuTree:menu-basename is unset (especially as it has a default * value). This has to be done here, in the constructor, since the * properties are construct-only. */ self = GMENU_TREE (obj); if (self->path != NULL) g_object_set (self, "menu-basename", NULL, NULL); return obj; } static void gmenu_tree_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GMenuTree *self = GMENU_TREE (object); switch (prop_id) { case PROP_MENU_BASENAME: self->basename = g_value_dup_string (value); break; case PROP_MENU_PATH: self->path = g_value_dup_string (value); break; case PROP_FLAGS: self->flags = g_value_get_flags (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gmenu_tree_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GMenuTree *self = GMENU_TREE (object); switch (prop_id) { case PROP_MENU_BASENAME: g_value_set_string (value, self->basename); break; case PROP_MENU_PATH: g_value_set_string (value, self->path); break; case PROP_FLAGS: g_value_set_flags (value, self->flags); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gmenu_tree_finalize (GObject *object) { GMenuTree *tree = GMENU_TREE (object); gmenu_tree_force_recanonicalize (tree); if (tree->basename != NULL) g_free (tree->basename); tree->basename = NULL; g_free (tree->non_prefixed_basename); tree->non_prefixed_basename = NULL; if (tree->path != NULL) g_free (tree->path); tree->path = NULL; if (tree->canonical_path != NULL) g_free (tree->canonical_path); tree->canonical_path = NULL; g_hash_table_destroy (tree->entries_by_id); tree->entries_by_id = NULL; G_OBJECT_CLASS (gmenu_tree_parent_class)->finalize (object); } static void gmenu_tree_init (GMenuTree *self) { self->entries_by_id = g_hash_table_new (g_str_hash, g_str_equal); } static void gmenu_tree_class_init (GMenuTreeClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->constructor = gmenu_tree_constructor; gobject_class->get_property = gmenu_tree_get_property; gobject_class->set_property = gmenu_tree_set_property; gobject_class->finalize = gmenu_tree_finalize; /** * GMenuTree:menu-basename: * * The name of the menu file; must be a basename or a relative path. The file * will be looked up in $XDG_CONFIG_DIRS/menus/. See the Desktop Menu * specification. */ g_object_class_install_property (gobject_class, PROP_MENU_BASENAME, g_param_spec_string ("menu-basename", "", "", "applications.menu", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** * GMenuTree:menu-path: * * The full path of the menu file. If set, GMenuTree:menu-basename will get * ignored. */ g_object_class_install_property (gobject_class, PROP_MENU_PATH, g_param_spec_string ("menu-path", "", "", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** * GMenuTree:flags: * * Flags controlling the content of the menu. */ g_object_class_install_property (gobject_class, PROP_FLAGS, g_param_spec_flags ("flags", "", "", GMENU_TYPE_TREE_FLAGS, GMENU_TREE_FLAGS_NONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** * GMenuTree:changed: * * This signal is emitted when applications are added, removed, or * upgraded. But note the new data will only be visible after * gmenu_tree_load_sync() or a variant thereof is invoked. */ gmenu_tree_signals[CHANGED] = g_signal_new ("changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } /** * gmenu_tree_get_canonical_menu_path: * @tree: a #GMenuTree * * This function is only available if the tree has been loaded via * gmenu_tree_load_sync() or a variant thereof. * * Returns: The absolute and canonicalized path to the loaded menu file */ const char * gmenu_tree_get_canonical_menu_path (GMenuTree *tree) { g_return_val_if_fail (GMENU_IS_TREE (tree), NULL); g_return_val_if_fail (tree->loaded, NULL); return tree->canonical_path; } /** * gmenu_tree_load_sync: * @tree: a #GMenuTree * @error: a #GError * * Synchronously load the menu contents. This function * performs a significant amount of blocking I/O if the * tree has not been loaded yet. * * Returns: %TRUE on success, %FALSE on error */ gboolean gmenu_tree_load_sync (GMenuTree *tree, GError **error) { GError *local_error = NULL; if (tree->loaded) return TRUE; if (!gmenu_tree_build_from_layout (tree, &local_error)) { if (local_error) g_propagate_error (error, local_error); return FALSE; } tree->loaded = TRUE; return TRUE; } /** * gmenu_tree_get_root_directory: * @tree: a #GMenuTree * * Get the root directory; you must have loaded the tree first (at * least once) via gmenu_tree_load_sync() or a variant thereof. * * Returns: (transfer full): Root of the tree */ GMenuTreeDirectory * gmenu_tree_get_root_directory (GMenuTree *tree) { g_return_val_if_fail (tree != NULL, NULL); g_return_val_if_fail (tree->loaded, NULL); return gmenu_tree_item_ref (tree->root); } static GMenuTreeDirectory * find_path (GMenuTreeDirectory *directory, const char *path) { const char *name; char *slash; char *freeme; GSList *tmp; while (path[0] == G_DIR_SEPARATOR) path++; if (path[0] == '\0') return directory; freeme = NULL; slash = strchr (path, G_DIR_SEPARATOR); if (slash) { name = freeme = g_strndup (path, slash - path); path = slash + 1; } else { name = path; path = NULL; } tmp = directory->contents; while (tmp != NULL) { GMenuTreeItem *item = tmp->data; if (item->type != GMENU_TREE_ITEM_DIRECTORY) { tmp = tmp->next; continue; } if (!strcmp (name, GMENU_TREE_DIRECTORY (item)->name)) { g_free (freeme); if (path) return find_path (GMENU_TREE_DIRECTORY (item), path); else return GMENU_TREE_DIRECTORY (item); } tmp = tmp->next; } g_free (freeme); return NULL; } GMenuTreeDirectory * gmenu_tree_get_directory_from_path (GMenuTree *tree, const char *path) { GMenuTreeDirectory *root; GMenuTreeDirectory *directory; g_return_val_if_fail (tree != NULL, NULL); g_return_val_if_fail (path != NULL, NULL); if (path[0] != G_DIR_SEPARATOR) return NULL; if (!(root = gmenu_tree_get_root_directory (tree))) return NULL; directory = find_path (root, path); gmenu_tree_item_unref (root); return directory ? gmenu_tree_item_ref (directory) : NULL; } /** * gmenu_tree_get_entry_by_id: * @tree: a #GMenuTree * @id: a desktop file ID * * Look up the entry corresponding to the given "desktop file id". * * Returns: (transfer full): A newly referenced #GMenuTreeEntry, or %NULL if none */ GMenuTreeEntry * gmenu_tree_get_entry_by_id (GMenuTree *tree, const char *id) { GMenuTreeEntry *entry; g_return_val_if_fail (tree->loaded, NULL); entry = g_hash_table_lookup (tree->entries_by_id, id); if (entry != NULL) gmenu_tree_item_ref (entry); return entry; } static void gmenu_tree_invoke_monitors (GMenuTree *tree) { g_signal_emit (tree, gmenu_tree_signals[CHANGED], 0); } static GMenuTreeDirectory * get_parent (GMenuTreeItem *item) { g_return_val_if_fail (item != NULL, NULL); return item->parent ? gmenu_tree_item_ref (item->parent) : NULL; } /** * gmenu_tree_directory_get_parent: * @directory: a #GMenuTreeDirectory * * Returns: (transfer full): The parent directory, or %NULL if none */ GMenuTreeDirectory * gmenu_tree_directory_get_parent (GMenuTreeDirectory *directory) { return get_parent ((GMenuTreeItem *)directory); } /** * gmenu_tree_entry_get_parent: * @entry: a #GMenuTreeEntry * * Returns: (transfer full): The parent directory, or %NULL if none */ GMenuTreeDirectory * gmenu_tree_entry_get_parent (GMenuTreeEntry *entry) { return get_parent ((GMenuTreeItem *)entry); } /** * gmenu_tree_alias_get_parent: * @alias: a #GMenuTreeAlias * * Returns: (transfer full): The parent directory, or %NULL if none */ GMenuTreeDirectory * gmenu_tree_alias_get_parent (GMenuTreeAlias *alias) { return get_parent ((GMenuTreeItem *)alias); } /** * gmenu_tree_header_get_parent: * @header: a #GMenuTreeHeader * * Returns: (transfer full): The parent directory, or %NULL if none */ GMenuTreeDirectory * gmenu_tree_header_get_parent (GMenuTreeHeader *header) { return get_parent ((GMenuTreeItem *)header); } /** * gmenu_tree_separator_get_parent: * @separator: a #GMenuTreeSeparator * * Returns: (transfer full): The parent directory, or %NULL if none */ GMenuTreeDirectory * gmenu_tree_separator_get_parent (GMenuTreeSeparator *separator) { return get_parent ((GMenuTreeItem *)separator); } static void gmenu_tree_item_set_parent (GMenuTreeItem *item, GMenuTreeDirectory *parent) { g_return_if_fail (item != NULL); item->parent = parent; } /** * gmenu_tree_iter_ref: (skip) * @iter: iter * * Increment the reference count of @iter */ GMenuTreeIter * gmenu_tree_iter_ref (GMenuTreeIter *iter) { g_atomic_int_inc (&iter->refcount); return iter; } /** * gmenu_tree_iter_unref: (skip) * @iter: iter * * Decrement the reference count of @iter */ void gmenu_tree_iter_unref (GMenuTreeIter *iter) { if (!g_atomic_int_dec_and_test (&iter->refcount)) return; g_slist_foreach (iter->contents, (GFunc)gmenu_tree_item_unref, NULL); g_slist_free (iter->contents); g_slice_free (GMenuTreeIter, iter); } /** * gmenu_tree_directory_iter: * @directory: directory * * Returns: (transfer full): A new iterator over the directory contents */ GMenuTreeIter * gmenu_tree_directory_iter (GMenuTreeDirectory *directory) { GMenuTreeIter *iter; g_return_val_if_fail (directory != NULL, NULL); iter = g_slice_new0 (GMenuTreeIter); iter->refcount = 1; iter->contents = g_slist_copy (directory->contents); iter->contents_iter = iter->contents; g_slist_foreach (iter->contents, (GFunc) gmenu_tree_item_ref, NULL); return iter; } /** * gmenu_tree_iter_next: * @iter: iter * * Change the iterator to the next item, and return its type. If * there are no more items, %GMENU_TREE_ITEM_INVALID is returned. * * Returns: The type of the next item that can be retrived from the iterator */ GMenuTreeItemType gmenu_tree_iter_next (GMenuTreeIter *iter) { g_return_val_if_fail (iter != NULL, GMENU_TREE_ITEM_INVALID); if (iter->contents_iter) { iter->item = iter->contents_iter->data; iter->contents_iter = iter->contents_iter->next; return iter->item->type; } else return GMENU_TREE_ITEM_INVALID; } /** * gmenu_tree_iter_get_directory: * @iter: iter * * This method may only be called if gmenu_tree_iter_next() * returned GMENU_TREE_ITEM_DIRECTORY. * * Returns: (transfer full): A directory */ GMenuTreeDirectory * gmenu_tree_iter_get_directory (GMenuTreeIter *iter) { g_return_val_if_fail (iter != NULL, NULL); g_return_val_if_fail (iter->item != NULL, NULL); g_return_val_if_fail (iter->item->type == GMENU_TREE_ITEM_DIRECTORY, NULL); return (GMenuTreeDirectory*)gmenu_tree_item_ref (iter->item); } /** * gmenu_tree_iter_get_entry: * @iter: iter * * This method may only be called if gmenu_tree_iter_next() * returned GMENU_TREE_ITEM_ENTRY. * * Returns: (transfer full): An entry */ GMenuTreeEntry * gmenu_tree_iter_get_entry (GMenuTreeIter *iter) { g_return_val_if_fail (iter != NULL, NULL); g_return_val_if_fail (iter->item != NULL, NULL); g_return_val_if_fail (iter->item->type == GMENU_TREE_ITEM_ENTRY, NULL); return (GMenuTreeEntry*)gmenu_tree_item_ref (iter->item); } /** * gmenu_tree_iter_get_header: * @iter: iter * * This method may only be called if gmenu_tree_iter_next() * returned GMENU_TREE_ITEM_HEADER. * * Returns: (transfer full): A header */ GMenuTreeHeader * gmenu_tree_iter_get_header (GMenuTreeIter *iter) { g_return_val_if_fail (iter != NULL, NULL); g_return_val_if_fail (iter->item != NULL, NULL); g_return_val_if_fail (iter->item->type == GMENU_TREE_ITEM_HEADER, NULL); return (GMenuTreeHeader*)gmenu_tree_item_ref (iter->item); } /** * gmenu_tree_iter_get_alias: * @iter: iter * * This method may only be called if gmenu_tree_iter_next() * returned GMENU_TREE_ITEM_ALIAS. * * Returns: (transfer full): An alias */ GMenuTreeAlias * gmenu_tree_iter_get_alias (GMenuTreeIter *iter) { g_return_val_if_fail (iter != NULL, NULL); g_return_val_if_fail (iter->item != NULL, NULL); g_return_val_if_fail (iter->item->type == GMENU_TREE_ITEM_ALIAS, NULL); return (GMenuTreeAlias*)gmenu_tree_item_ref (iter->item); } /** * gmenu_tree_iter_get_separator: * @iter: iter * * This method may only be called if gmenu_tree_iter_next() * returned #GMENU_TREE_ITEM_SEPARATOR. * * Returns: (transfer full): A separator */ GMenuTreeSeparator * gmenu_tree_iter_get_separator (GMenuTreeIter *iter) { g_return_val_if_fail (iter != NULL, NULL); g_return_val_if_fail (iter->item != NULL, NULL); g_return_val_if_fail (iter->item->type == GMENU_TREE_ITEM_SEPARATOR, NULL); return (GMenuTreeSeparator*)gmenu_tree_item_ref (iter->item); } const char * gmenu_tree_directory_get_name (GMenuTreeDirectory *directory) { g_return_val_if_fail (directory != NULL, NULL); if (!directory->directory_entry) return directory->name; return desktop_entry_get_name (directory->directory_entry); } const char * gmenu_tree_directory_get_generic_name (GMenuTreeDirectory *directory) { g_return_val_if_fail (directory != NULL, NULL); if (!directory->directory_entry) return NULL; return desktop_entry_get_generic_name (directory->directory_entry); } const char * gmenu_tree_directory_get_comment (GMenuTreeDirectory *directory) { g_return_val_if_fail (directory != NULL, NULL); if (!directory->directory_entry) return NULL; return desktop_entry_get_comment (directory->directory_entry); } /** * gmenu_tree_directory_get_icon: * @directory: a #GMenuTreeDirectory * * Gets the icon for the directory. * * Returns: (transfer none): The #GIcon for this directory */ GIcon * gmenu_tree_directory_get_icon (GMenuTreeDirectory *directory) { g_return_val_if_fail (directory != NULL, NULL); if (!directory->directory_entry) return NULL; return desktop_entry_get_icon (directory->directory_entry); } const char * gmenu_tree_directory_get_desktop_file_path (GMenuTreeDirectory *directory) { g_return_val_if_fail (directory != NULL, NULL); if (!directory->directory_entry) return NULL; return desktop_entry_get_path (directory->directory_entry); } const char * gmenu_tree_directory_get_menu_id (GMenuTreeDirectory *directory) { g_return_val_if_fail (directory != NULL, NULL); return directory->name; } gboolean gmenu_tree_directory_get_is_nodisplay (GMenuTreeDirectory *directory) { g_return_val_if_fail (directory != NULL, FALSE); return directory->is_nodisplay; } /** * gmenu_tree_directory_get_tree: * @directory: A #GMenuTreeDirectory * * Grab the tree associated with a #GMenuTreeItem. * * Returns: (transfer full): The #GMenuTree */ GMenuTree * gmenu_tree_directory_get_tree (GMenuTreeDirectory *directory) { g_return_val_if_fail (directory != NULL, NULL); return g_object_ref (directory->item.tree); } static void append_directory_path (GMenuTreeDirectory *directory, GString *path) { if (!directory->item.parent) { g_string_append_c (path, G_DIR_SEPARATOR); return; } append_directory_path (directory->item.parent, path); g_string_append (path, directory->name); g_string_append_c (path, G_DIR_SEPARATOR); } char * gmenu_tree_directory_make_path (GMenuTreeDirectory *directory, GMenuTreeEntry *entry) { GString *path; g_return_val_if_fail (directory != NULL, NULL); path = g_string_new (NULL); append_directory_path (directory, path); if (entry != NULL) g_string_append (path, desktop_entry_get_basename (entry->desktop_entry)); return g_string_free (path, FALSE); } /** * gmenu_tree_entry_get_app_info: * @entry: a #GMenuTreeEntry * * Returns: (transfer none): The #GDesktopAppInfo for this entry */ GDesktopAppInfo * gmenu_tree_entry_get_app_info (GMenuTreeEntry *entry) { g_return_val_if_fail (entry != NULL, NULL); return desktop_entry_get_app_info (entry->desktop_entry); } const char * gmenu_tree_entry_get_desktop_file_path (GMenuTreeEntry *entry) { g_return_val_if_fail (entry != NULL, NULL); return desktop_entry_get_path (entry->desktop_entry); } const char * gmenu_tree_entry_get_desktop_file_id (GMenuTreeEntry *entry) { g_return_val_if_fail (entry != NULL, NULL); return entry->desktop_file_id; } gboolean gmenu_tree_entry_get_is_nodisplay_recurse (GMenuTreeEntry *entry) { GMenuTreeDirectory *directory; GDesktopAppInfo *app_info; g_return_val_if_fail (entry != NULL, FALSE); app_info = gmenu_tree_entry_get_app_info (entry); if (g_desktop_app_info_get_nodisplay (app_info)) return TRUE; directory = entry->item.parent; while (directory != NULL) { if (directory->is_nodisplay) return TRUE; directory = directory->item.parent; } return FALSE; } gboolean gmenu_tree_entry_get_is_excluded (GMenuTreeEntry *entry) { g_return_val_if_fail (entry != NULL, FALSE); return entry->is_excluded; } gboolean gmenu_tree_entry_get_is_unallocated (GMenuTreeEntry *entry) { g_return_val_if_fail (entry != NULL, FALSE); return entry->is_unallocated; } /** * gmenu_tree_entry_get_tree: * @entry: A #GMenuTreeEntry * * Grab the tree associated with a #GMenuTreeEntry. * * Returns: (transfer full): The #GMenuTree */ GMenuTree * gmenu_tree_entry_get_tree (GMenuTreeEntry *entry) { g_return_val_if_fail (entry != NULL, NULL); return g_object_ref (entry->item.tree); } GMenuTreeDirectory * gmenu_tree_header_get_directory (GMenuTreeHeader *header) { g_return_val_if_fail (header != NULL, NULL); return gmenu_tree_item_ref (header->directory); } /** * gmenu_tree_header_get_tree: * @header: A #GMenuTreeHeader * * Grab the tree associated with a #GMenuTreeHeader. * * Returns: (transfer full): The #GMenuTree */ GMenuTree * gmenu_tree_header_get_tree (GMenuTreeHeader *header) { g_return_val_if_fail (header != NULL, NULL); return g_object_ref (header->item.tree); } GMenuTreeItemType gmenu_tree_alias_get_aliased_item_type (GMenuTreeAlias *alias) { g_return_val_if_fail (alias != NULL, GMENU_TREE_ITEM_INVALID); g_assert (alias->aliased_item != NULL); return alias->aliased_item->type; } GMenuTreeDirectory * gmenu_tree_alias_get_directory (GMenuTreeAlias *alias) { g_return_val_if_fail (alias != NULL, NULL); return gmenu_tree_item_ref (alias->directory); } /** * gmenu_tree_alias_get_tree: * @alias: A #GMenuTreeAlias * * Grab the tree associated with a #GMenuTreeAlias. * * Returns: (transfer full): The #GMenuTree */ GMenuTree * gmenu_tree_alias_get_tree (GMenuTreeAlias *alias) { g_return_val_if_fail (alias != NULL, NULL); return g_object_ref (alias->item.tree); } /** * gmenu_tree_separator_get_tree: * @separator: A #GMenuTreeSeparator * * Grab the tree associated with a #GMenuTreeSeparator. * * Returns: (transfer full): The #GMenuTree */ GMenuTree * gmenu_tree_separator_get_tree (GMenuTreeSeparator *separator) { g_return_val_if_fail (separator != NULL, NULL); return g_object_ref (separator->item.tree); } /** * gmenu_tree_alias_get_aliased_directory: * @alias: alias * * Returns: (transfer full): The aliased directory entry */ GMenuTreeDirectory * gmenu_tree_alias_get_aliased_directory (GMenuTreeAlias *alias) { g_return_val_if_fail (alias != NULL, NULL); g_return_val_if_fail (alias->aliased_item->type == GMENU_TREE_ITEM_DIRECTORY, NULL); return (GMenuTreeDirectory *) gmenu_tree_item_ref (alias->aliased_item); } /** * gmenu_tree_alias_get_aliased_entry: * @alias: alias * * Returns: (transfer full): The aliased entry */ GMenuTreeEntry * gmenu_tree_alias_get_aliased_entry (GMenuTreeAlias *alias) { g_return_val_if_fail (alias != NULL, NULL); g_return_val_if_fail (alias->aliased_item->type == GMENU_TREE_ITEM_ENTRY, NULL); return (GMenuTreeEntry *) gmenu_tree_item_ref (alias->aliased_item); } static GMenuTreeDirectory * gmenu_tree_directory_new (GMenuTree *tree, GMenuTreeDirectory *parent, const char *name) { GMenuTreeDirectory *retval; retval = g_slice_new0 (GMenuTreeDirectory); retval->item.type = GMENU_TREE_ITEM_DIRECTORY; retval->item.parent = parent; retval->item.refcount = 1; retval->item.tree = tree; retval->name = g_strdup (name); retval->directory_entry = NULL; retval->entries = NULL; retval->subdirs = NULL; retval->default_layout_info = NULL; retval->layout_info = NULL; retval->contents = NULL; retval->only_unallocated = FALSE; retval->is_nodisplay = FALSE; retval->layout_pending_separator = FALSE; retval->preprocessed = FALSE; retval->will_inline_header = G_MAXUINT16; retval->default_layout_values.mask = MENU_LAYOUT_VALUES_NONE; retval->default_layout_values.show_empty = FALSE; retval->default_layout_values.inline_menus = FALSE; retval->default_layout_values.inline_limit = 4; retval->default_layout_values.inline_header = FALSE; retval->default_layout_values.inline_alias = FALSE; return retval; } static void gmenu_tree_directory_finalize (GMenuTreeDirectory *directory) { g_assert (directory->item.refcount == 0); g_slist_foreach (directory->contents, (GFunc) gmenu_tree_item_unref_and_unset_parent, NULL); g_slist_free (directory->contents); directory->contents = NULL; g_slist_foreach (directory->default_layout_info, (GFunc) menu_layout_node_unref, NULL); g_slist_free (directory->default_layout_info); directory->default_layout_info = NULL; g_slist_foreach (directory->layout_info, (GFunc) menu_layout_node_unref, NULL); g_slist_free (directory->layout_info); directory->layout_info = NULL; g_slist_foreach (directory->subdirs, (GFunc) gmenu_tree_item_unref_and_unset_parent, NULL); g_slist_free (directory->subdirs); directory->subdirs = NULL; g_slist_foreach (directory->entries, (GFunc) gmenu_tree_item_unref_and_unset_parent, NULL); g_slist_free (directory->entries); directory->entries = NULL; if (directory->directory_entry) desktop_entry_unref (directory->directory_entry); directory->directory_entry = NULL; g_free (directory->name); directory->name = NULL; g_slice_free (GMenuTreeDirectory, directory); } static GMenuTreeSeparator * gmenu_tree_separator_new (GMenuTreeDirectory *parent) { GMenuTreeSeparator *retval; retval = g_slice_new0 (GMenuTreeSeparator); retval->item.type = GMENU_TREE_ITEM_SEPARATOR; retval->item.parent = parent; retval->item.refcount = 1; retval->item.tree = parent->item.tree; return retval; } static void gmenu_tree_separator_finalize (GMenuTreeSeparator *separator) { g_assert (separator->item.refcount == 0); g_slice_free (GMenuTreeSeparator, separator); } static GMenuTreeHeader * gmenu_tree_header_new (GMenuTreeDirectory *parent, GMenuTreeDirectory *directory) { GMenuTreeHeader *retval; retval = g_slice_new0 (GMenuTreeHeader); retval->item.type = GMENU_TREE_ITEM_HEADER; retval->item.parent = parent; retval->item.refcount = 1; retval->item.tree = parent->item.tree; retval->directory = gmenu_tree_item_ref (directory); gmenu_tree_item_set_parent (GMENU_TREE_ITEM (retval->directory), NULL); return retval; } static void gmenu_tree_header_finalize (GMenuTreeHeader *header) { g_assert (header->item.refcount == 0); if (header->directory != NULL) gmenu_tree_item_unref (header->directory); header->directory = NULL; g_slice_free (GMenuTreeHeader, header); } static GMenuTreeAlias * gmenu_tree_alias_new (GMenuTreeDirectory *parent, GMenuTreeDirectory *directory, GMenuTreeItem *item) { GMenuTreeAlias *retval; retval = g_slice_new0 (GMenuTreeAlias); retval->item.type = GMENU_TREE_ITEM_ALIAS; retval->item.parent = parent; retval->item.refcount = 1; retval->item.tree = parent->item.tree; retval->directory = gmenu_tree_item_ref (directory); if (item->type != GMENU_TREE_ITEM_ALIAS) retval->aliased_item = gmenu_tree_item_ref (item); else { GMenuTreeAlias *alias = GMENU_TREE_ALIAS (item); retval->aliased_item = gmenu_tree_item_ref (alias->aliased_item); } gmenu_tree_item_set_parent (GMENU_TREE_ITEM (retval->directory), NULL); gmenu_tree_item_set_parent (retval->aliased_item, NULL); return retval; } static void gmenu_tree_alias_finalize (GMenuTreeAlias *alias) { g_assert (alias->item.refcount == 0); if (alias->directory != NULL) gmenu_tree_item_unref (alias->directory); alias->directory = NULL; if (alias->aliased_item != NULL) gmenu_tree_item_unref (alias->aliased_item); alias->aliased_item = NULL; g_slice_free (GMenuTreeAlias, alias); } static GMenuTreeEntry * gmenu_tree_entry_new (GMenuTreeDirectory *parent, DesktopEntry *desktop_entry, const char *desktop_file_id, gboolean is_excluded, gboolean is_unallocated) { GMenuTreeEntry *retval; retval = g_slice_new0 (GMenuTreeEntry); retval->item.type = GMENU_TREE_ITEM_ENTRY; retval->item.parent = parent; retval->item.refcount = 1; retval->item.tree = parent->item.tree; retval->desktop_entry = desktop_entry_ref (desktop_entry); retval->desktop_file_id = g_strdup (desktop_file_id); retval->is_excluded = is_excluded != FALSE; retval->is_unallocated = is_unallocated != FALSE; return retval; } static void gmenu_tree_entry_finalize (GMenuTreeEntry *entry) { g_assert (entry->item.refcount == 0); g_free (entry->desktop_file_id); entry->desktop_file_id = NULL; if (entry->desktop_entry) desktop_entry_unref (entry->desktop_entry); entry->desktop_entry = NULL; g_slice_free (GMenuTreeEntry, entry); } static int gmenu_tree_entry_compare_by_id (GMenuTreeItem *a, GMenuTreeItem *b) { if (a->type == GMENU_TREE_ITEM_ALIAS) a = GMENU_TREE_ALIAS (a)->aliased_item; if (b->type == GMENU_TREE_ITEM_ALIAS) b = GMENU_TREE_ALIAS (b)->aliased_item; return strcmp (GMENU_TREE_ENTRY (a)->desktop_file_id, GMENU_TREE_ENTRY (b)->desktop_file_id); } /** * gmenu_tree_item_ref: * @item: a #GMenuTreeItem * * Returns: (transfer full): The same @item, or %NULL if @item is not a valid #GMenuTreeItem */ gpointer gmenu_tree_item_ref (gpointer itemp) { GMenuTreeItem *item; item = (GMenuTreeItem *) itemp; g_return_val_if_fail (item != NULL, NULL); g_return_val_if_fail (item->refcount > 0, NULL); g_atomic_int_inc (&item->refcount); return item; } void gmenu_tree_item_unref (gpointer itemp) { GMenuTreeItem *item; item = (GMenuTreeItem *) itemp; g_return_if_fail (item != NULL); g_return_if_fail (item->refcount > 0); if (g_atomic_int_dec_and_test (&(item->refcount))) { switch (item->type) { case GMENU_TREE_ITEM_DIRECTORY: gmenu_tree_directory_finalize (GMENU_TREE_DIRECTORY (item)); break; case GMENU_TREE_ITEM_ENTRY: gmenu_tree_entry_finalize (GMENU_TREE_ENTRY (item)); break; case GMENU_TREE_ITEM_SEPARATOR: gmenu_tree_separator_finalize (GMENU_TREE_SEPARATOR (item)); break; case GMENU_TREE_ITEM_HEADER: gmenu_tree_header_finalize (GMENU_TREE_HEADER (item)); break; case GMENU_TREE_ITEM_ALIAS: gmenu_tree_alias_finalize (GMENU_TREE_ALIAS (item)); break; default: g_assert_not_reached (); break; } } } static void gmenu_tree_item_unref_and_unset_parent (gpointer itemp) { GMenuTreeItem *item; item = (GMenuTreeItem *) itemp; g_return_if_fail (item != NULL); gmenu_tree_item_set_parent (item, NULL); gmenu_tree_item_unref (item); } static inline const char * gmenu_tree_item_compare_get_name_helper (GMenuTreeItem *item, GMenuTreeFlags flags) { const char *name; name = NULL; switch (item->type) { case GMENU_TREE_ITEM_DIRECTORY: if (GMENU_TREE_DIRECTORY (item)->directory_entry) name = desktop_entry_get_name (GMENU_TREE_DIRECTORY (item)->directory_entry); else name = GMENU_TREE_DIRECTORY (item)->name; break; case GMENU_TREE_ITEM_ENTRY: if (flags & GMENU_TREE_FLAGS_SORT_DISPLAY_NAME) name = g_app_info_get_display_name (G_APP_INFO (gmenu_tree_entry_get_app_info (GMENU_TREE_ENTRY (item)))); else name = desktop_entry_get_name (GMENU_TREE_ENTRY (item)->desktop_entry); break; case GMENU_TREE_ITEM_ALIAS: { GMenuTreeItem *dir; dir = GMENU_TREE_ITEM (GMENU_TREE_ALIAS (item)->directory); name = gmenu_tree_item_compare_get_name_helper (dir, flags); } break; case GMENU_TREE_ITEM_SEPARATOR: case GMENU_TREE_ITEM_HEADER: default: g_assert_not_reached (); break; } return name; } static int gmenu_tree_item_compare (GMenuTreeItem *a, GMenuTreeItem *b, gpointer flags_p) { const char *name_a; const char *name_b; GMenuTreeFlags flags; flags = GPOINTER_TO_INT (flags_p); name_a = gmenu_tree_item_compare_get_name_helper (a, flags); name_b = gmenu_tree_item_compare_get_name_helper (b, flags); return g_utf8_collate (name_a, name_b); } static MenuLayoutNode * find_menu_child (MenuLayoutNode *layout) { MenuLayoutNode *child; child = menu_layout_node_get_children (layout); while (child && menu_layout_node_get_type (child) != MENU_LAYOUT_NODE_MENU) child = menu_layout_node_get_next (child); return child; } static void merge_resolved_children (GMenuTree *tree, GHashTable *loaded_menu_files, MenuLayoutNode *where, MenuLayoutNode *from) { MenuLayoutNode *insert_after; MenuLayoutNode *menu_child; MenuLayoutNode *from_child; gmenu_tree_resolve_files (tree, loaded_menu_files, from); insert_after = where; g_assert (menu_layout_node_get_type (insert_after) != MENU_LAYOUT_NODE_ROOT); g_assert (menu_layout_node_get_parent (insert_after) != NULL); /* skip root node */ menu_child = find_menu_child (from); g_assert (menu_child != NULL); g_assert (menu_layout_node_get_type (menu_child) == MENU_LAYOUT_NODE_MENU); /* merge children of toplevel */ from_child = menu_layout_node_get_children (menu_child); while (from_child != NULL) { MenuLayoutNode *next; next = menu_layout_node_get_next (from_child); menu_verbose ("Merging "); menu_debug_print_layout (from_child, FALSE); menu_verbose (" after "); menu_debug_print_layout (insert_after, FALSE); switch (menu_layout_node_get_type (from_child)) { case MENU_LAYOUT_NODE_NAME: menu_layout_node_unlink (from_child); /* delete this */ break; default: menu_layout_node_steal (from_child); menu_layout_node_insert_after (insert_after, from_child); menu_layout_node_unref (from_child); insert_after = from_child; break; } from_child = next; } } static gboolean load_merge_file (GMenuTree *tree, GHashTable *loaded_menu_files, const char *filename, gboolean is_canonical, gboolean add_monitor, MenuLayoutNode *where) { MenuLayoutNode *to_merge; const char *canonical; char *freeme; gboolean retval; freeme = NULL; retval = FALSE; if (!is_canonical) { canonical = freeme = realpath (filename, NULL); if (canonical == NULL) { if (add_monitor) gmenu_tree_add_menu_file_monitor (tree, filename, MENU_FILE_MONITOR_NONEXISTENT_FILE); menu_verbose ("Failed to canonicalize merge file path \"%s\": %s\n", filename, g_strerror (errno)); goto out; } } else { canonical = filename; } if (g_hash_table_lookup (loaded_menu_files, canonical) != NULL) { g_warning ("Not loading \"%s\": recursive loop detected in .menu files", canonical); retval = TRUE; goto out; } menu_verbose ("Merging file \"%s\"\n", canonical); to_merge = menu_layout_load (canonical, tree->non_prefixed_basename, NULL); if (to_merge == NULL) { menu_verbose ("No menu for file \"%s\" found when merging\n", canonical); goto out; } retval = TRUE; g_hash_table_insert (loaded_menu_files, (char *) canonical, GUINT_TO_POINTER (TRUE)); if (add_monitor) gmenu_tree_add_menu_file_monitor (tree, canonical, MENU_FILE_MONITOR_FILE); merge_resolved_children (tree, loaded_menu_files, where, to_merge); g_hash_table_remove (loaded_menu_files, canonical); menu_layout_node_unref (to_merge); out: if (freeme) g_free (freeme); return retval; } static gboolean load_merge_file_with_config_dir (GMenuTree *tree, GHashTable *loaded_menu_files, const char *menu_file, const char *config_dir, MenuLayoutNode *where) { char *merge_file; gboolean loaded; loaded = FALSE; merge_file = g_build_filename (config_dir, "menus", menu_file, NULL); if (load_merge_file (tree, loaded_menu_files, merge_file, FALSE, TRUE, where)) loaded = TRUE; g_free (merge_file); return loaded; } static gboolean compare_basedir_to_config_dir (const char *canonical_basedir, const char *config_dir) { char *dirname; char *canonical_menus_dir; gboolean retval; menu_verbose ("Checking to see if basedir '%s' is in '%s'\n", canonical_basedir, config_dir); dirname = g_build_filename (config_dir, "menus", NULL); retval = FALSE; canonical_menus_dir = realpath (dirname, NULL); if (canonical_menus_dir != NULL && strcmp (canonical_basedir, canonical_menus_dir) == 0) { retval = TRUE; } g_free (canonical_menus_dir); g_free (dirname); return retval; } static gboolean load_parent_merge_file_from_basename (GMenuTree *tree, GHashTable *loaded_menu_files, MenuLayoutNode *layout, const char *menu_file, const char *canonical_basedir) { gboolean found_basedir; const char * const *system_config_dirs; int i; /* We're not interested in menu files that are in directories which are not a * parent of the base directory of this menu file */ found_basedir = compare_basedir_to_config_dir (canonical_basedir, g_get_user_config_dir ()); system_config_dirs = g_get_system_config_dirs (); i = 0; while (system_config_dirs[i] != NULL) { if (!found_basedir) { found_basedir = compare_basedir_to_config_dir (canonical_basedir, system_config_dirs[i]); } else { menu_verbose ("Looking for parent menu file '%s' in '%s'\n", menu_file, system_config_dirs[i]); if (load_merge_file_with_config_dir (tree, loaded_menu_files, menu_file, system_config_dirs[i], layout)) { break; } } ++i; } return system_config_dirs[i] != NULL; } static gboolean load_parent_merge_file (GMenuTree *tree, GHashTable *loaded_menu_files, MenuLayoutNode *layout) { MenuLayoutNode *root; const char *basedir; const char *menu_name; char *canonical_basedir; char *menu_file; gboolean found; root = menu_layout_node_get_root (layout); basedir = menu_layout_node_root_get_basedir (root); menu_name = menu_layout_node_root_get_name (root); canonical_basedir = realpath (basedir, NULL); if (canonical_basedir == NULL) { menu_verbose ("Menu basedir '%s' no longer exists, not merging parent\n", basedir); return FALSE; } found = FALSE; menu_file = g_strconcat (menu_name, ".menu", NULL); if (strcmp (menu_file, "applications.menu") == 0) { char *prefixed_basename; prefixed_basename = prefix_menu_name (menu_file); found = load_parent_merge_file_from_basename (tree, loaded_menu_files, layout, prefixed_basename, canonical_basedir); g_free (prefixed_basename); } if (!found) { found = load_parent_merge_file_from_basename (tree, loaded_menu_files, layout, menu_file, canonical_basedir); } g_free (menu_file); g_free (canonical_basedir); return found; } static void load_merge_dir (GMenuTree *tree, GHashTable *loaded_menu_files, const char *dirname, MenuLayoutNode *where) { GDir *dir; const char *menu_file; menu_verbose ("Loading merge dir \"%s\"\n", dirname); gmenu_tree_add_menu_file_monitor (tree, dirname, MENU_FILE_MONITOR_DIRECTORY); if ((dir = g_dir_open (dirname, 0, NULL)) == NULL) return; while ((menu_file = g_dir_read_name (dir))) { if (g_str_has_suffix (menu_file, ".menu")) { char *full_path; full_path = g_build_filename (dirname, menu_file, NULL); load_merge_file (tree, loaded_menu_files, full_path, TRUE, FALSE, where); g_free (full_path); } } g_dir_close (dir); } static void load_merge_dir_with_config_dir (GMenuTree *tree, GHashTable *loaded_menu_files, const char *config_dir, const char *dirname, MenuLayoutNode *where) { char *path; path = g_build_filename (config_dir, "menus", dirname, NULL); load_merge_dir (tree, loaded_menu_files, path, where); g_free (path); } static void resolve_merge_file (GMenuTree *tree, GHashTable *loaded_menu_files, MenuLayoutNode *layout) { char *filename; if (menu_layout_node_merge_file_get_type (layout) == MENU_MERGE_FILE_TYPE_PARENT) { if (load_parent_merge_file (tree, loaded_menu_files, layout)) return; } filename = menu_layout_node_get_content_as_path (layout); if (filename == NULL) { menu_verbose ("didn't get node content as a path, not merging file\n"); } else { load_merge_file (tree, loaded_menu_files, filename, FALSE, TRUE, layout); g_free (filename); } /* remove the now-replaced node */ menu_layout_node_unlink (layout); } static void resolve_merge_dir (GMenuTree *tree, GHashTable *loaded_menu_files, MenuLayoutNode *layout) { char *path; path = menu_layout_node_get_content_as_path (layout); if (path == NULL) { menu_verbose ("didn't get layout node content as a path, not merging dir\n"); } else { load_merge_dir (tree, loaded_menu_files, path, layout); g_free (path); } /* remove the now-replaced node */ menu_layout_node_unlink (layout); } static MenuLayoutNode * add_app_dir (GMenuTree *tree, MenuLayoutNode *before, const char *data_dir) { MenuLayoutNode *tmp; char *dirname; tmp = menu_layout_node_new (MENU_LAYOUT_NODE_APP_DIR); dirname = g_build_filename (data_dir, "applications", NULL); menu_layout_node_set_content (tmp, dirname); menu_layout_node_insert_before (before, tmp); menu_layout_node_unref (before); menu_verbose ("Adding %s in \n", dirname); g_free (dirname); return tmp; } static void resolve_default_app_dirs (GMenuTree *tree, MenuLayoutNode *layout) { MenuLayoutNode *before; const char * const *system_data_dirs; int i; system_data_dirs = g_get_system_data_dirs (); before = add_app_dir (tree, menu_layout_node_ref (layout), g_get_user_data_dir ()); i = 0; while (system_data_dirs[i] != NULL) { before = add_app_dir (tree, before, system_data_dirs[i]); ++i; } menu_layout_node_unref (before); /* remove the now-replaced node */ menu_layout_node_unlink (layout); } static MenuLayoutNode * add_directory_dir (GMenuTree *tree, MenuLayoutNode *before, const char *data_dir) { MenuLayoutNode *tmp; char *dirname; tmp = menu_layout_node_new (MENU_LAYOUT_NODE_DIRECTORY_DIR); dirname = g_build_filename (data_dir, "desktop-directories", NULL); menu_layout_node_set_content (tmp, dirname); menu_layout_node_insert_before (before, tmp); menu_layout_node_unref (before); menu_verbose ("Adding %s in \n", dirname); g_free (dirname); return tmp; } static void resolve_default_directory_dirs (GMenuTree *tree, MenuLayoutNode *layout) { MenuLayoutNode *before; const char * const *system_data_dirs; int i; system_data_dirs = g_get_system_data_dirs (); before = add_directory_dir (tree, menu_layout_node_ref (layout), g_get_user_data_dir ()); i = 0; while (system_data_dirs[i] != NULL) { before = add_directory_dir (tree, before, system_data_dirs[i]); ++i; } menu_layout_node_unref (before); /* remove the now-replaced node */ menu_layout_node_unlink (layout); } static void resolve_default_merge_dirs (GMenuTree *tree, GHashTable *loaded_menu_files, MenuLayoutNode *layout) { MenuLayoutNode *root; const char *menu_name; char *merge_name; const char * const *system_config_dirs; int i; root = menu_layout_node_get_root (layout); menu_name = menu_layout_node_root_get_name (root); merge_name = g_strconcat (menu_name, "-merged", NULL); system_config_dirs = g_get_system_config_dirs (); /* Merge in reverse order */ i = 0; while (system_config_dirs[i] != NULL) i++; while (i > 0) { i--; load_merge_dir_with_config_dir (tree, loaded_menu_files, system_config_dirs[i], merge_name, layout); } load_merge_dir_with_config_dir (tree, loaded_menu_files, g_get_user_config_dir (), merge_name, layout); g_free (merge_name); /* remove the now-replaced node */ menu_layout_node_unlink (layout); } static void gmenu_tree_resolve_files (GMenuTree *tree, GHashTable *loaded_menu_files, MenuLayoutNode *layout) { MenuLayoutNode *child; menu_verbose ("Resolving files in: "); menu_debug_print_layout (layout, TRUE); switch (menu_layout_node_get_type (layout)) { case MENU_LAYOUT_NODE_MERGE_FILE: resolve_merge_file (tree, loaded_menu_files, layout); break; case MENU_LAYOUT_NODE_MERGE_DIR: resolve_merge_dir (tree, loaded_menu_files, layout); break; case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS: resolve_default_app_dirs (tree, layout); break; case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS: resolve_default_directory_dirs (tree, layout); break; case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS: resolve_default_merge_dirs (tree, loaded_menu_files, layout); break; case MENU_LAYOUT_NODE_LEGACY_DIR: menu_verbose ("Ignoring obsolete legacy dir"); break; case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS: menu_verbose ("Ignoring obsolete KDE legacy dirs"); break; case MENU_LAYOUT_NODE_PASSTHROUGH: /* Just get rid of these, we don't need the memory usage */ menu_layout_node_unlink (layout); break; default: /* Recurse */ child = menu_layout_node_get_children (layout); while (child != NULL) { MenuLayoutNode *next = menu_layout_node_get_next (child); gmenu_tree_resolve_files (tree, loaded_menu_files, child); child = next; } break; } } static void move_children (MenuLayoutNode *from, MenuLayoutNode *to) { MenuLayoutNode *from_child; MenuLayoutNode *insert_before; insert_before = menu_layout_node_get_children (to); from_child = menu_layout_node_get_children (from); while (from_child != NULL) { MenuLayoutNode *next; next = menu_layout_node_get_next (from_child); menu_layout_node_steal (from_child); if (menu_layout_node_get_type (from_child) == MENU_LAYOUT_NODE_NAME) { ; /* just drop the Name in the old */ } else if (insert_before) { menu_layout_node_insert_before (insert_before, from_child); g_assert (menu_layout_node_get_next (from_child) == insert_before); } else { menu_layout_node_append_child (to, from_child); } menu_layout_node_unref (from_child); from_child = next; } } static int null_safe_strcmp (const char *a, const char *b) { if (a == NULL && b == NULL) return 0; else if (a == NULL) return -1; else if (b == NULL) return 1; else return strcmp (a, b); } static int node_compare_func (const void *a, const void *b) { MenuLayoutNode *node_a = (MenuLayoutNode*) a; MenuLayoutNode *node_b = (MenuLayoutNode*) b; MenuLayoutNodeType t_a = menu_layout_node_get_type (node_a); MenuLayoutNodeType t_b = menu_layout_node_get_type (node_b); if (t_a < t_b) return -1; else if (t_a > t_b) return 1; else { const char *c_a = menu_layout_node_get_content (node_a); const char *c_b = menu_layout_node_get_content (node_b); return null_safe_strcmp (c_a, c_b); } } static int node_menu_compare_func (const void *a, const void *b) { MenuLayoutNode *node_a = (MenuLayoutNode*) a; MenuLayoutNode *node_b = (MenuLayoutNode*) b; MenuLayoutNode *parent_a = menu_layout_node_get_parent (node_a); MenuLayoutNode *parent_b = menu_layout_node_get_parent (node_b); if (parent_a < parent_b) return -1; else if (parent_a > parent_b) return 1; else return null_safe_strcmp (menu_layout_node_menu_get_name (node_a), menu_layout_node_menu_get_name (node_b)); } static void gmenu_tree_strip_duplicate_children (GMenuTree *tree, MenuLayoutNode *layout) { MenuLayoutNode *child; GSList *simple_nodes; GSList *menu_layout_nodes; GSList *prev; GSList *tmp; /* to strip dups, we find all the child nodes where * we want to kill dups, sort them, * then nuke the adjacent nodes that are equal */ simple_nodes = NULL; menu_layout_nodes = NULL; child = menu_layout_node_get_children (layout); while (child != NULL) { switch (menu_layout_node_get_type (child)) { /* These are dups if their content is the same */ case MENU_LAYOUT_NODE_APP_DIR: case MENU_LAYOUT_NODE_DIRECTORY_DIR: case MENU_LAYOUT_NODE_DIRECTORY: simple_nodes = g_slist_prepend (simple_nodes, child); break; /* These have to be merged in a more complicated way, * and then recursed */ case MENU_LAYOUT_NODE_MENU: menu_layout_nodes = g_slist_prepend (menu_layout_nodes, child); break; default: break; } child = menu_layout_node_get_next (child); } /* Note that the lists are all backward. So we want to keep * the items that are earlier in the list, because they were * later in the file */ /* stable sort the simple nodes */ simple_nodes = g_slist_sort (simple_nodes, node_compare_func); prev = NULL; tmp = simple_nodes; while (tmp != NULL) { GSList *next = tmp->next; if (prev) { MenuLayoutNode *p = prev->data; MenuLayoutNode *n = tmp->data; if (node_compare_func (p, n) == 0) { /* nuke it! */ menu_layout_node_unlink (n); simple_nodes = g_slist_delete_link (simple_nodes, tmp); tmp = prev; } } prev = tmp; tmp = next; } g_slist_free (simple_nodes); simple_nodes = NULL; /* stable sort the menu nodes (the sort includes the * parents of the nodes in the comparison). Remember * the list is backward. */ menu_layout_nodes = g_slist_sort (menu_layout_nodes, node_menu_compare_func); prev = NULL; tmp = menu_layout_nodes; while (tmp != NULL) { GSList *next = tmp->next; if (prev) { MenuLayoutNode *p = prev->data; MenuLayoutNode *n = tmp->data; if (node_menu_compare_func (p, n) == 0) { /* Move children of first menu to the start of second * menu and nuke the first menu */ move_children (n, p); menu_layout_node_unlink (n); menu_layout_nodes = g_slist_delete_link (menu_layout_nodes, tmp); tmp = prev; } } prev = tmp; tmp = next; } g_slist_free (menu_layout_nodes); menu_layout_nodes = NULL; /* Recursively clean up all children */ child = menu_layout_node_get_children (layout); while (child != NULL) { if (menu_layout_node_get_type (child) == MENU_LAYOUT_NODE_MENU) gmenu_tree_strip_duplicate_children (tree, child); child = menu_layout_node_get_next (child); } } static MenuLayoutNode * find_submenu (MenuLayoutNode *layout, const char *path, gboolean create_if_not_found) { MenuLayoutNode *child; const char *slash; const char *next_path; char *name; menu_verbose (" (splitting \"%s\")\n", path); if (path[0] == '\0' || path[0] == G_DIR_SEPARATOR) return NULL; slash = strchr (path, G_DIR_SEPARATOR); if (slash != NULL) { name = g_strndup (path, slash - path); next_path = slash + 1; if (*next_path == '\0') next_path = NULL; } else { name = g_strdup (path); next_path = NULL; } child = menu_layout_node_get_children (layout); while (child != NULL) { switch (menu_layout_node_get_type (child)) { case MENU_LAYOUT_NODE_MENU: { if (strcmp (name, menu_layout_node_menu_get_name (child)) == 0) { menu_verbose ("MenuNode %p found for path component \"%s\"\n", child, name); g_free (name); if (!next_path) { menu_verbose (" Found menu node %p parent is %p\n", child, layout); return child; } return find_submenu (child, next_path, create_if_not_found); } } break; default: break; } child = menu_layout_node_get_next (child); } if (create_if_not_found) { MenuLayoutNode *name_node; child = menu_layout_node_new (MENU_LAYOUT_NODE_MENU); menu_layout_node_append_child (layout, child); name_node = menu_layout_node_new (MENU_LAYOUT_NODE_NAME); menu_layout_node_set_content (name_node, name); menu_layout_node_append_child (child, name_node); menu_layout_node_unref (name_node); menu_verbose (" Created menu node %p parent is %p\n", child, layout); menu_layout_node_unref (child); g_free (name); if (!next_path) return child; return find_submenu (child, next_path, create_if_not_found); } else { g_free (name); return NULL; } } /* To call this you first have to strip duplicate children once, * otherwise when you move a menu Foo to Bar then you may only * move one of Foo, not all the merged Foo. */ static void gmenu_tree_execute_moves (GMenuTree *tree, MenuLayoutNode *layout, gboolean *need_remove_dups_p) { MenuLayoutNode *child; gboolean need_remove_dups; GSList *move_nodes; GSList *tmp; need_remove_dups = FALSE; move_nodes = NULL; child = menu_layout_node_get_children (layout); while (child != NULL) { switch (menu_layout_node_get_type (child)) { case MENU_LAYOUT_NODE_MENU: /* Recurse - we recurse first and process the current node * second, as the spec dictates. */ gmenu_tree_execute_moves (tree, child, &need_remove_dups); break; case MENU_LAYOUT_NODE_MOVE: move_nodes = g_slist_prepend (move_nodes, child); break; default: break; } child = menu_layout_node_get_next (child); } /* We need to execute the move operations in the order that they appear */ move_nodes = g_slist_reverse (move_nodes); tmp = move_nodes; while (tmp != NULL) { MenuLayoutNode *move_node = tmp->data; MenuLayoutNode *old_node; GSList *next = tmp->next; const char *old; const char *new; old = menu_layout_node_move_get_old (move_node); new = menu_layout_node_move_get_new (move_node); g_assert (old != NULL && new != NULL); menu_verbose ("executing old = \"%s\" new = \"%s\"\n", old, new); old_node = find_submenu (layout, old, FALSE); if (old_node != NULL) { MenuLayoutNode *new_node; /* here we can create duplicates anywhere below the * node */ need_remove_dups = TRUE; /* look up new node creating it and its parents if * required */ new_node = find_submenu (layout, new, TRUE); g_assert (new_node != NULL); move_children (old_node, new_node); menu_layout_node_unlink (old_node); } menu_layout_node_unlink (move_node); tmp = next; } g_slist_free (move_nodes); /* This oddness is to ensure we only remove dups once, * at the root, instead of recursing the tree over * and over. */ if (need_remove_dups_p) *need_remove_dups_p = need_remove_dups; else if (need_remove_dups) gmenu_tree_strip_duplicate_children (tree, layout); } static gboolean gmenu_tree_load_layout (GMenuTree *tree, GError **error) { GHashTable *loaded_menu_files; if (tree->layout) return TRUE; if (!gmenu_tree_canonicalize_path (tree, error)) return FALSE; menu_verbose ("Loading menu layout from \"%s\"\n", tree->canonical_path); tree->layout = menu_layout_load (tree->canonical_path, tree->non_prefixed_basename, error); if (!tree->layout) return FALSE; loaded_menu_files = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (loaded_menu_files, tree->canonical_path, GUINT_TO_POINTER (TRUE)); gmenu_tree_resolve_files (tree, loaded_menu_files, tree->layout); g_hash_table_destroy (loaded_menu_files); gmenu_tree_strip_duplicate_children (tree, tree->layout); gmenu_tree_execute_moves (tree, tree->layout, NULL); return TRUE; } static void gmenu_tree_force_reload (GMenuTree *tree) { gmenu_tree_force_rebuild (tree); if (tree->layout) menu_layout_node_unref (tree->layout); tree->layout = NULL; } typedef struct { DesktopEntrySet *set; const char *category; } GetByCategoryForeachData; static void get_by_category_foreach (const char *file_id, DesktopEntry *entry, GetByCategoryForeachData *data) { if (desktop_entry_has_category (entry, data->category)) desktop_entry_set_add_entry (data->set, entry, file_id); } static void get_by_category (DesktopEntrySet *entry_pool, DesktopEntrySet *set, const char *category) { GetByCategoryForeachData data; data.set = set; data.category = category; desktop_entry_set_foreach (entry_pool, (DesktopEntrySetForeachFunc) get_by_category_foreach, &data); } static DesktopEntrySet * process_include_rules (MenuLayoutNode *layout, DesktopEntrySet *entry_pool) { DesktopEntrySet *set = NULL; switch (menu_layout_node_get_type (layout)) { case MENU_LAYOUT_NODE_AND: { MenuLayoutNode *child; menu_verbose ("Processing \n"); child = menu_layout_node_get_children (layout); while (child != NULL) { DesktopEntrySet *child_set; child_set = process_include_rules (child, entry_pool); if (set == NULL) { set = child_set; } else { desktop_entry_set_intersection (set, child_set); desktop_entry_set_unref (child_set); } /* as soon as we get empty results, we can bail, * because it's an AND */ if (desktop_entry_set_get_count (set) == 0) break; child = menu_layout_node_get_next (child); } menu_verbose ("Processed \n"); } break; case MENU_LAYOUT_NODE_OR: { MenuLayoutNode *child; menu_verbose ("Processing \n"); child = menu_layout_node_get_children (layout); while (child != NULL) { DesktopEntrySet *child_set; child_set = process_include_rules (child, entry_pool); if (set == NULL) { set = child_set; } else { desktop_entry_set_union (set, child_set); desktop_entry_set_unref (child_set); } child = menu_layout_node_get_next (child); } menu_verbose ("Processed \n"); } break; case MENU_LAYOUT_NODE_NOT: { /* First get the OR of all the rules */ MenuLayoutNode *child; menu_verbose ("Processing \n"); child = menu_layout_node_get_children (layout); while (child != NULL) { DesktopEntrySet *child_set; child_set = process_include_rules (child, entry_pool); if (set == NULL) { set = child_set; } else { desktop_entry_set_union (set, child_set); desktop_entry_set_unref (child_set); } child = menu_layout_node_get_next (child); } if (set != NULL) { DesktopEntrySet *inverted; /* Now invert the result */ inverted = desktop_entry_set_new (); desktop_entry_set_union (inverted, entry_pool); desktop_entry_set_subtract (inverted, set); desktop_entry_set_unref (set); set = inverted; } menu_verbose ("Processed \n"); } break; case MENU_LAYOUT_NODE_ALL: menu_verbose ("Processing \n"); set = desktop_entry_set_new (); desktop_entry_set_union (set, entry_pool); menu_verbose ("Processed \n"); break; case MENU_LAYOUT_NODE_FILENAME: { DesktopEntry *entry; menu_verbose ("Processing %s\n", menu_layout_node_get_content (layout)); entry = desktop_entry_set_lookup (entry_pool, menu_layout_node_get_content (layout)); if (entry != NULL) { set = desktop_entry_set_new (); desktop_entry_set_add_entry (set, entry, menu_layout_node_get_content (layout)); } menu_verbose ("Processed %s\n", menu_layout_node_get_content (layout)); } break; case MENU_LAYOUT_NODE_CATEGORY: menu_verbose ("Processing %s\n", menu_layout_node_get_content (layout)); set = desktop_entry_set_new (); get_by_category (entry_pool, set, menu_layout_node_get_content (layout)); menu_verbose ("Processed %s\n", menu_layout_node_get_content (layout)); break; default: break; } if (set == NULL) set = desktop_entry_set_new (); /* create an empty set */ menu_verbose ("Matched %d entries\n", desktop_entry_set_get_count (set)); return set; } static void collect_layout_info (MenuLayoutNode *layout, GSList **layout_info) { MenuLayoutNode *iter; g_slist_foreach (*layout_info, (GFunc) menu_layout_node_unref, NULL); g_slist_free (*layout_info); *layout_info = NULL; iter = menu_layout_node_get_children (layout); while (iter != NULL) { switch (menu_layout_node_get_type (iter)) { case MENU_LAYOUT_NODE_MENUNAME: case MENU_LAYOUT_NODE_FILENAME: case MENU_LAYOUT_NODE_SEPARATOR: case MENU_LAYOUT_NODE_MERGE: *layout_info = g_slist_prepend (*layout_info, menu_layout_node_ref (iter)); break; default: break; } iter = menu_layout_node_get_next (iter); } *layout_info = g_slist_reverse (*layout_info); } static void entries_listify_foreach (const char *desktop_file_id, DesktopEntry *desktop_entry, GMenuTreeDirectory *directory) { directory->entries = g_slist_prepend (directory->entries, gmenu_tree_entry_new (directory, desktop_entry, desktop_file_id, FALSE, FALSE)); } static void excluded_entries_listify_foreach (const char *desktop_file_id, DesktopEntry *desktop_entry, GMenuTreeDirectory *directory) { directory->entries = g_slist_prepend (directory->entries, gmenu_tree_entry_new (directory, desktop_entry, desktop_file_id, TRUE, FALSE)); } static void unallocated_entries_listify_foreach (const char *desktop_file_id, DesktopEntry *desktop_entry, GMenuTreeDirectory *directory) { directory->entries = g_slist_prepend (directory->entries, gmenu_tree_entry_new (directory, desktop_entry, desktop_file_id, FALSE, TRUE)); } static void set_default_layout_values (GMenuTreeDirectory *parent, GMenuTreeDirectory *child) { GSList *tmp; /* if the child has a defined default layout, we don't want to override its * values. The parent might have a non-defined layout info (ie, no child of * the DefaultLayout node) but it doesn't meant the default layout values * (ie, DefaultLayout attributes) aren't different from the global defaults. */ if (child->default_layout_info != NULL || child->default_layout_values.mask != MENU_LAYOUT_VALUES_NONE) return; child->default_layout_values = parent->default_layout_values; tmp = child->subdirs; while (tmp != NULL) { GMenuTreeDirectory *subdir = tmp->data; set_default_layout_values (child, subdir); tmp = tmp->next; } } static GMenuTreeDirectory * process_layout (GMenuTree *tree, GMenuTreeDirectory *parent, MenuLayoutNode *layout, DesktopEntrySet *allocated) { MenuLayoutNode *layout_iter; GMenuTreeDirectory *directory; DesktopEntrySet *entry_pool; DesktopEntrySet *entries; DesktopEntrySet *allocated_set; DesktopEntrySet *excluded_set; gboolean deleted; gboolean only_unallocated; GSList *tmp; g_assert (menu_layout_node_get_type (layout) == MENU_LAYOUT_NODE_MENU); g_assert (menu_layout_node_menu_get_name (layout) != NULL); directory = gmenu_tree_directory_new (tree, parent, menu_layout_node_menu_get_name (layout)); menu_verbose ("=== Menu name = %s ===\n", directory->name); deleted = FALSE; only_unallocated = FALSE; entries = desktop_entry_set_new (); allocated_set = desktop_entry_set_new (); if (tree->flags & GMENU_TREE_FLAGS_INCLUDE_EXCLUDED) excluded_set = desktop_entry_set_new (); else excluded_set = NULL; entry_pool = _entry_directory_list_get_all_desktops (menu_layout_node_menu_get_app_dirs (layout)); layout_iter = menu_layout_node_get_children (layout); while (layout_iter != NULL) { switch (menu_layout_node_get_type (layout_iter)) { case MENU_LAYOUT_NODE_MENU: /* recurse */ { GMenuTreeDirectory *child_dir; menu_verbose ("Processing \n"); child_dir = process_layout (tree, directory, layout_iter, allocated); if (child_dir) directory->subdirs = g_slist_prepend (directory->subdirs, child_dir); menu_verbose ("Processed \n"); } break; case MENU_LAYOUT_NODE_INCLUDE: { /* The match rule children of the are * independent (logical OR) so we can process each one by * itself */ MenuLayoutNode *rule; menu_verbose ("Processing (%d entries)\n", desktop_entry_set_get_count (entries)); rule = menu_layout_node_get_children (layout_iter); while (rule != NULL) { DesktopEntrySet *rule_set; rule_set = process_include_rules (rule, entry_pool); if (rule_set != NULL) { desktop_entry_set_union (entries, rule_set); desktop_entry_set_union (allocated_set, rule_set); if (excluded_set != NULL) desktop_entry_set_subtract (excluded_set, rule_set); desktop_entry_set_unref (rule_set); } rule = menu_layout_node_get_next (rule); } menu_verbose ("Processed (%d entries)\n", desktop_entry_set_get_count (entries)); } break; case MENU_LAYOUT_NODE_EXCLUDE: { /* The match rule children of the are * independent (logical OR) so we can process each one by * itself */ MenuLayoutNode *rule; menu_verbose ("Processing (%d entries)\n", desktop_entry_set_get_count (entries)); rule = menu_layout_node_get_children (layout_iter); while (rule != NULL) { DesktopEntrySet *rule_set; rule_set = process_include_rules (rule, entry_pool); if (rule_set != NULL) { if (excluded_set != NULL) desktop_entry_set_union (excluded_set, rule_set); desktop_entry_set_subtract (entries, rule_set); desktop_entry_set_unref (rule_set); } rule = menu_layout_node_get_next (rule); } menu_verbose ("Processed (%d entries)\n", desktop_entry_set_get_count (entries)); } break; case MENU_LAYOUT_NODE_DIRECTORY: { DesktopEntry *entry; menu_verbose ("Processing %s\n", menu_layout_node_get_content (layout_iter)); /* * The last to exist wins, so we always try overwriting */ entry = entry_directory_list_get_directory (menu_layout_node_menu_get_directory_dirs (layout), menu_layout_node_get_content (layout_iter)); if (entry != NULL) { if (!desktop_entry_get_hidden (entry)) { if (directory->directory_entry) desktop_entry_unref (directory->directory_entry); directory->directory_entry = entry; /* pass ref ownership */ } else { desktop_entry_unref (entry); } } menu_verbose ("Processed new directory entry = %p (%s)\n", directory->directory_entry, directory->directory_entry? desktop_entry_get_path (directory->directory_entry) : "null"); } break; case MENU_LAYOUT_NODE_DELETED: menu_verbose ("Processed \n"); deleted = TRUE; break; case MENU_LAYOUT_NODE_NOT_DELETED: menu_verbose ("Processed \n"); deleted = FALSE; break; case MENU_LAYOUT_NODE_ONLY_UNALLOCATED: menu_verbose ("Processed \n"); only_unallocated = TRUE; break; case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED: menu_verbose ("Processed \n"); only_unallocated = FALSE; break; case MENU_LAYOUT_NODE_DEFAULT_LAYOUT: menu_layout_node_default_layout_get_values (layout_iter, &directory->default_layout_values); collect_layout_info (layout_iter, &directory->default_layout_info); menu_verbose ("Processed \n"); break; case MENU_LAYOUT_NODE_LAYOUT: collect_layout_info (layout_iter, &directory->layout_info); menu_verbose ("Processed \n"); break; default: break; } layout_iter = menu_layout_node_get_next (layout_iter); } desktop_entry_set_unref (entry_pool); directory->only_unallocated = only_unallocated; if (!directory->only_unallocated) desktop_entry_set_union (allocated, allocated_set); desktop_entry_set_unref (allocated_set); if (directory->directory_entry) { if (desktop_entry_get_no_display (directory->directory_entry)) { directory->is_nodisplay = TRUE; if (!(tree->flags & GMENU_TREE_FLAGS_INCLUDE_NODISPLAY)) { menu_verbose ("Not showing menu %s because NoDisplay=true\n", desktop_entry_get_name (directory->directory_entry)); deleted = TRUE; } } if (!desktop_entry_get_show_in (directory->directory_entry)) { menu_verbose ("Not showing menu %s because OnlyShowIn!=$DESKTOP or NotShowIn=$DESKTOP (with $DESKTOP=${XDG_CURRENT_DESKTOP:-GNOME})\n", desktop_entry_get_name (directory->directory_entry)); deleted = TRUE; } } if (deleted) { if (excluded_set != NULL) desktop_entry_set_unref (excluded_set); desktop_entry_set_unref (entries); gmenu_tree_item_unref (directory); return NULL; } desktop_entry_set_foreach (entries, (DesktopEntrySetForeachFunc) entries_listify_foreach, directory); desktop_entry_set_unref (entries); if (excluded_set != NULL) { desktop_entry_set_foreach (excluded_set, (DesktopEntrySetForeachFunc) excluded_entries_listify_foreach, directory); desktop_entry_set_unref (excluded_set); } tmp = directory->subdirs; while (tmp != NULL) { GMenuTreeDirectory *subdir = tmp->data; set_default_layout_values (directory, subdir); tmp = tmp->next; } tmp = directory->entries; while (tmp != NULL) { GMenuTreeEntry *entry = tmp->data; GSList *next = tmp->next; gboolean delete = FALSE; /* If adding a new condition to delete here, it has to be added to * get_still_unallocated_foreach() too */ if (desktop_entry_get_hidden (entry->desktop_entry)) { menu_verbose ("Deleting %s because Hidden=true\n", desktop_entry_get_name (entry->desktop_entry)); delete = TRUE; } if (!(tree->flags & GMENU_TREE_FLAGS_INCLUDE_NODISPLAY) && desktop_entry_get_no_display (entry->desktop_entry)) { menu_verbose ("Deleting %s because NoDisplay=true\n", desktop_entry_get_name (entry->desktop_entry)); delete = TRUE; } if (!desktop_entry_get_show_in (entry->desktop_entry)) { menu_verbose ("Deleting %s because OnlyShowIn!=$DESKTOP or NotShowIn=$DESKTOP (with $DESKTOP=${XDG_CURRENT_DESKTOP:-GNOME})\n", desktop_entry_get_name (entry->desktop_entry)); delete = TRUE; } /* No need to filter out based on TryExec since GDesktopAppInfo cannot * deal with .desktop files with a failed TryExec. */ if (delete) { directory->entries = g_slist_delete_link (directory->entries, tmp); gmenu_tree_item_unref_and_unset_parent (entry); } tmp = next; } g_assert (directory->name != NULL); return directory; } static void process_only_unallocated (GMenuTree *tree, GMenuTreeDirectory *directory, DesktopEntrySet *allocated, DesktopEntrySet *unallocated_used) { GSList *tmp; /* For any directory marked only_unallocated, we have to remove any * entries that were in fact allocated. */ if (directory->only_unallocated) { tmp = directory->entries; while (tmp != NULL) { GMenuTreeEntry *entry = tmp->data; GSList *next = tmp->next; if (desktop_entry_set_lookup (allocated, entry->desktop_file_id)) { directory->entries = g_slist_delete_link (directory->entries, tmp); gmenu_tree_item_unref_and_unset_parent (entry); } else { desktop_entry_set_add_entry (unallocated_used, entry->desktop_entry, entry->desktop_file_id); } tmp = next; } } tmp = directory->subdirs; while (tmp != NULL) { GMenuTreeDirectory *subdir = tmp->data; process_only_unallocated (tree, subdir, allocated, unallocated_used); tmp = tmp->next; } } typedef struct { GMenuTree *tree; DesktopEntrySet *allocated; DesktopEntrySet *unallocated_used; DesktopEntrySet *still_unallocated; } GetStillUnallocatedForeachData; static void get_still_unallocated_foreach (const char *file_id, DesktopEntry *entry, GetStillUnallocatedForeachData *data) { if (desktop_entry_set_lookup (data->allocated, file_id)) return; if (desktop_entry_set_lookup (data->unallocated_used, file_id)) return; /* Same rules than at the end of process_layout() */ if (desktop_entry_get_hidden (entry)) return; if (!(data->tree->flags & GMENU_TREE_FLAGS_INCLUDE_NODISPLAY) && desktop_entry_get_no_display (entry)) return; if (!desktop_entry_get_show_in (entry)) return; desktop_entry_set_add_entry (data->still_unallocated, entry, file_id); } static void preprocess_layout_info (GMenuTree *tree, GMenuTreeDirectory *directory); static GSList * get_layout_info (GMenuTreeDirectory *directory, gboolean *is_default_layout) { GMenuTreeDirectory *iter; if (directory->layout_info != NULL) { if (is_default_layout) { *is_default_layout = FALSE; } return directory->layout_info; } /* Even if there's no layout information at all, the result will be an * implicit default layout */ if (is_default_layout) { *is_default_layout = TRUE; } iter = directory; while (iter != NULL) { /* FIXME: this is broken: we might skip real parent in the * XML structure, that are hidden because of inlining. */ if (iter->default_layout_info != NULL) { return iter->default_layout_info; } iter = GMENU_TREE_ITEM (iter)->parent; } return NULL; } static void get_values_with_defaults (MenuLayoutNode *node, MenuLayoutValues *layout_values, MenuLayoutValues *default_layout_values) { menu_layout_node_menuname_get_values (node, layout_values); if (!(layout_values->mask & MENU_LAYOUT_VALUES_SHOW_EMPTY)) layout_values->show_empty = default_layout_values->show_empty; if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_MENUS)) layout_values->inline_menus = default_layout_values->inline_menus; if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_LIMIT)) layout_values->inline_limit = default_layout_values->inline_limit; if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_HEADER)) layout_values->inline_header = default_layout_values->inline_header; if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_ALIAS)) layout_values->inline_alias = default_layout_values->inline_alias; } static guint get_real_subdirs_len (GMenuTreeDirectory *directory) { guint len; GSList *tmp; len = 0; tmp = directory->subdirs; while (tmp != NULL) { GMenuTreeDirectory *subdir = tmp->data; tmp = tmp->next; if (subdir->will_inline_header != G_MAXUINT16) { len += get_real_subdirs_len (subdir) + g_slist_length (subdir->entries) + 1; } else len += 1; } return len; } static void preprocess_layout_info_subdir_helper (GMenuTree *tree, GMenuTreeDirectory *directory, GMenuTreeDirectory *subdir, MenuLayoutValues *layout_values, gboolean *contents_added, gboolean *should_remove) { preprocess_layout_info (tree, subdir); *should_remove = FALSE; *contents_added = FALSE; if (subdir->subdirs == NULL && subdir->entries == NULL) { if (!(tree->flags & GMENU_TREE_FLAGS_SHOW_EMPTY) && !layout_values->show_empty) { menu_verbose ("Not showing empty menu '%s'\n", subdir->name); *should_remove = TRUE; } } else if (layout_values->inline_menus) { guint real_subdirs_len; real_subdirs_len = get_real_subdirs_len (subdir); if (layout_values->inline_alias && real_subdirs_len + g_slist_length (subdir->entries) == 1) { GMenuTreeAlias *alias; GMenuTreeItem *item; GSList *list; if (subdir->subdirs != NULL) list = subdir->subdirs; else list = subdir->entries; item = GMENU_TREE_ITEM (list->data); menu_verbose ("Inline aliasing '%s' to '%s'\n", item->type == GMENU_TREE_ITEM_ENTRY ? g_app_info_get_name (G_APP_INFO (gmenu_tree_entry_get_app_info (GMENU_TREE_ENTRY (item)))) : (item->type == GMENU_TREE_ITEM_DIRECTORY ? gmenu_tree_directory_get_name (GMENU_TREE_DIRECTORY (item)) : gmenu_tree_directory_get_name (GMENU_TREE_ALIAS (item)->directory)), subdir->name); alias = gmenu_tree_alias_new (directory, subdir, item); g_slist_foreach (list, (GFunc) gmenu_tree_item_unref_and_unset_parent, NULL); g_slist_free (list); subdir->subdirs = NULL; subdir->entries = NULL; if (item->type == GMENU_TREE_ITEM_DIRECTORY) directory->subdirs = g_slist_append (directory->subdirs, alias); else directory->entries = g_slist_append (directory->entries, alias); *contents_added = TRUE; *should_remove = TRUE; } else if (layout_values->inline_limit == 0 || layout_values->inline_limit >= real_subdirs_len + g_slist_length (subdir->entries)) { if (layout_values->inline_header) { menu_verbose ("Creating inline header with name '%s'\n", subdir->name); /* we're limited to 16-bits to spare some memory; if the limit is * higher than that (would be crazy), we just consider it's * unlimited */ if (layout_values->inline_limit < G_MAXUINT16) subdir->will_inline_header = layout_values->inline_limit; else subdir->will_inline_header = 0; } else { g_slist_foreach (subdir->subdirs, (GFunc) gmenu_tree_item_set_parent, directory); directory->subdirs = g_slist_concat (directory->subdirs, subdir->subdirs); subdir->subdirs = NULL; g_slist_foreach (subdir->entries, (GFunc) gmenu_tree_item_set_parent, directory); directory->entries = g_slist_concat (directory->entries, subdir->entries); subdir->entries = NULL; *contents_added = TRUE; *should_remove = TRUE; } menu_verbose ("Inlining directory contents of '%s' to '%s'\n", subdir->name, directory->name); } } } static void preprocess_layout_info (GMenuTree *tree, GMenuTreeDirectory *directory) { GSList *tmp; GSList *layout_info; gboolean using_default_layout; GSList *last_subdir; gboolean strip_duplicates; gboolean contents_added; gboolean should_remove; GSList *subdirs_sentinel; /* Note: we need to preprocess all menus, even if the layout mask for a menu * is MENU_LAYOUT_VALUES_NONE: in this case, we need to remove empty menus; * and the layout mask can be different for a submenu anyway */ menu_verbose ("Processing menu layout inline hints for %s\n", directory->name); g_assert (!directory->preprocessed); strip_duplicates = FALSE; /* we use last_subdir to track the last non-inlined subdirectory */ last_subdir = g_slist_last (directory->subdirs); /* * First process subdirectories with explicit layout */ layout_info = get_layout_info (directory, &using_default_layout); tmp = layout_info; /* see comment below about Menuname to understand why we leave the loop if * last_subdir is NULL */ while (tmp != NULL && last_subdir != NULL) { MenuLayoutNode *node = tmp->data; MenuLayoutValues layout_values; const char *name; GMenuTreeDirectory *subdir; GSList *subdir_l; tmp = tmp->next; /* only Menuname nodes are relevant here */ if (menu_layout_node_get_type (node) != MENU_LAYOUT_NODE_MENUNAME) continue; get_values_with_defaults (node, &layout_values, &directory->default_layout_values); /* find the subdirectory that is affected by those attributes */ name = menu_layout_node_get_content (node); subdir = NULL; subdir_l = directory->subdirs; while (subdir_l != NULL) { subdir = subdir_l->data; if (!strcmp (subdir->name, name)) break; subdir = NULL; subdir_l = subdir_l->next; /* We do not want to use Menuname on a menu that appeared via * inlining: without inlining, the Menuname wouldn't have matched * anything, and we want to keep the same behavior. * Unless the layout is a default layout, in which case the Menuname * does match the subdirectory. */ if (!using_default_layout && subdir_l == last_subdir) { subdir_l = NULL; break; } } if (subdir == NULL) continue; preprocess_layout_info_subdir_helper (tree, directory, subdir, &layout_values, &contents_added, &should_remove); strip_duplicates = strip_duplicates || contents_added; if (should_remove) { if (last_subdir == subdir_l) { /* we need to recompute last_subdir since we'll remove it from * the list */ GSList *buf; if (subdir_l == directory->subdirs) last_subdir = NULL; else { buf = directory->subdirs; while (buf != NULL && buf->next != subdir_l) buf = buf->next; last_subdir = buf; } } directory->subdirs = g_slist_remove (directory->subdirs, subdir); gmenu_tree_item_unref_and_unset_parent (GMENU_TREE_ITEM (subdir)); } } /* * Now process the subdirectories with no explicit layout */ /* this is bogus data, but we just need the pointer anyway */ subdirs_sentinel = g_slist_prepend (directory->subdirs, PACKAGE); directory->subdirs = subdirs_sentinel; tmp = directory->subdirs; while (tmp->next != NULL) { GMenuTreeDirectory *subdir = tmp->next->data; if (subdir->preprocessed) { tmp = tmp->next; continue; } preprocess_layout_info_subdir_helper (tree, directory, subdir, &directory->default_layout_values, &contents_added, &should_remove); strip_duplicates = strip_duplicates || contents_added; if (should_remove) { tmp = g_slist_delete_link (tmp, tmp->next); gmenu_tree_item_unref_and_unset_parent (GMENU_TREE_ITEM (subdir)); } else tmp = tmp->next; } /* remove the sentinel */ directory->subdirs = g_slist_delete_link (directory->subdirs, directory->subdirs); /* * Finally, remove duplicates if needed */ if (strip_duplicates) { /* strip duplicate entries; there should be no duplicate directories */ directory->entries = g_slist_sort (directory->entries, (GCompareFunc) gmenu_tree_entry_compare_by_id); tmp = directory->entries; while (tmp != NULL && tmp->next != NULL) { GMenuTreeItem *a = tmp->data; GMenuTreeItem *b = tmp->next->data; if (a->type == GMENU_TREE_ITEM_ALIAS) a = GMENU_TREE_ALIAS (a)->aliased_item; if (b->type == GMENU_TREE_ITEM_ALIAS) b = GMENU_TREE_ALIAS (b)->aliased_item; if (strcmp (GMENU_TREE_ENTRY (a)->desktop_file_id, GMENU_TREE_ENTRY (b)->desktop_file_id) == 0) { tmp = g_slist_delete_link (tmp, tmp->next); gmenu_tree_item_unref (b); } else tmp = tmp->next; } } directory->preprocessed = TRUE; } static void process_layout_info (GMenuTree *tree, GMenuTreeDirectory *directory); static void check_pending_separator (GMenuTreeDirectory *directory) { if (directory->layout_pending_separator) { menu_verbose ("Adding pending separator in '%s'\n", directory->name); directory->contents = g_slist_append (directory->contents, gmenu_tree_separator_new (directory)); directory->layout_pending_separator = FALSE; } } static void merge_alias (GMenuTree *tree, GMenuTreeDirectory *directory, GMenuTreeAlias *alias) { menu_verbose ("Merging alias '%s' in directory '%s'\n", alias->directory->name, directory->name); if (alias->aliased_item->type == GMENU_TREE_ITEM_DIRECTORY) { process_layout_info (tree, GMENU_TREE_DIRECTORY (alias->aliased_item)); } check_pending_separator (directory); directory->contents = g_slist_append (directory->contents, gmenu_tree_item_ref (alias)); } static void merge_subdir (GMenuTree *tree, GMenuTreeDirectory *directory, GMenuTreeDirectory *subdir) { menu_verbose ("Merging subdir '%s' in directory '%s'\n", subdir->name, directory->name); process_layout_info (tree, subdir); check_pending_separator (directory); if (subdir->will_inline_header == 0 || (subdir->will_inline_header != G_MAXUINT16 && g_slist_length (subdir->contents) <= subdir->will_inline_header)) { GMenuTreeHeader *header; header = gmenu_tree_header_new (directory, subdir); directory->contents = g_slist_append (directory->contents, header); g_slist_foreach (subdir->contents, (GFunc) gmenu_tree_item_set_parent, directory); directory->contents = g_slist_concat (directory->contents, subdir->contents); subdir->contents = NULL; subdir->will_inline_header = G_MAXUINT16; gmenu_tree_item_set_parent (GMENU_TREE_ITEM (subdir), NULL); } else { directory->contents = g_slist_append (directory->contents, gmenu_tree_item_ref (subdir)); } } static void merge_subdir_by_name (GMenuTree *tree, GMenuTreeDirectory *directory, const char *subdir_name) { GSList *tmp; menu_verbose ("Attempting to merge subdir '%s' in directory '%s'\n", subdir_name, directory->name); tmp = directory->subdirs; while (tmp != NULL) { GMenuTreeDirectory *subdir = tmp->data; GSList *next = tmp->next; /* if it's an alias, then it cannot be affected by * the Merge nodes in the layout */ if (GMENU_TREE_ITEM (subdir)->type == GMENU_TREE_ITEM_ALIAS) continue; if (!strcmp (subdir->name, subdir_name)) { directory->subdirs = g_slist_delete_link (directory->subdirs, tmp); merge_subdir (tree, directory, subdir); gmenu_tree_item_unref (subdir); } tmp = next; } } static void merge_entry (GMenuTree *tree, GMenuTreeDirectory *directory, GMenuTreeEntry *entry) { menu_verbose ("Merging entry '%s' in directory '%s'\n", entry->desktop_file_id, directory->name); check_pending_separator (directory); directory->contents = g_slist_append (directory->contents, gmenu_tree_item_ref (entry)); } static void merge_entry_by_id (GMenuTree *tree, GMenuTreeDirectory *directory, const char *file_id) { GSList *tmp; menu_verbose ("Attempting to merge entry '%s' in directory '%s'\n", file_id, directory->name); tmp = directory->entries; while (tmp != NULL) { GMenuTreeEntry *entry = tmp->data; GSList *next = tmp->next; /* if it's an alias, then it cannot be affected by * the Merge nodes in the layout */ if (GMENU_TREE_ITEM (entry)->type == GMENU_TREE_ITEM_ALIAS) continue; if (!strcmp (entry->desktop_file_id, file_id)) { directory->entries = g_slist_delete_link (directory->entries, tmp); merge_entry (tree, directory, entry); gmenu_tree_item_unref (entry); } tmp = next; } } static inline gboolean find_name_in_list (const char *name, GSList *list) { while (list != NULL) { if (!strcmp (name, list->data)) return TRUE; list = list->next; } return FALSE; } static void merge_subdirs (GMenuTree *tree, GMenuTreeDirectory *directory, GSList *except) { GSList *subdirs; GSList *tmp; menu_verbose ("Merging subdirs in directory '%s'\n", directory->name); subdirs = directory->subdirs; directory->subdirs = NULL; subdirs = g_slist_sort_with_data (subdirs, (GCompareDataFunc) gmenu_tree_item_compare, GINT_TO_POINTER (GMENU_TREE_FLAGS_NONE)); tmp = subdirs; while (tmp != NULL) { GMenuTreeDirectory *subdir = tmp->data; if (GMENU_TREE_ITEM (subdir)->type == GMENU_TREE_ITEM_ALIAS) { merge_alias (tree, directory, GMENU_TREE_ALIAS (subdir)); gmenu_tree_item_unref (subdir); } else if (!find_name_in_list (subdir->name, except)) { merge_subdir (tree, directory, subdir); gmenu_tree_item_unref (subdir); } else { menu_verbose ("Not merging directory '%s' yet\n", subdir->name); directory->subdirs = g_slist_append (directory->subdirs, subdir); } tmp = tmp->next; } g_slist_free (subdirs); g_slist_free (except); } static void merge_entries (GMenuTree *tree, GMenuTreeDirectory *directory, GSList *except) { GSList *entries; GSList *tmp; menu_verbose ("Merging entries in directory '%s'\n", directory->name); entries = directory->entries; directory->entries = NULL; entries = g_slist_sort_with_data (entries, (GCompareDataFunc) gmenu_tree_item_compare, GINT_TO_POINTER (tree->flags)); tmp = entries; while (tmp != NULL) { GMenuTreeEntry *entry = tmp->data; if (GMENU_TREE_ITEM (entry)->type == GMENU_TREE_ITEM_ALIAS) { merge_alias (tree, directory, GMENU_TREE_ALIAS (entry)); gmenu_tree_item_unref (entry); } else if (!find_name_in_list (entry->desktop_file_id, except)) { merge_entry (tree, directory, entry); gmenu_tree_item_unref (entry); } else { menu_verbose ("Not merging entry '%s' yet\n", entry->desktop_file_id); directory->entries = g_slist_append (directory->entries, entry); } tmp = tmp->next; } g_slist_free (entries); g_slist_free (except); } static void merge_subdirs_and_entries (GMenuTree *tree, GMenuTreeDirectory *directory, GSList *except_subdirs, GSList *except_entries) { GSList *items; GSList *tmp; menu_verbose ("Merging subdirs and entries together in directory %s\n", directory->name); items = g_slist_concat (directory->subdirs, directory->entries); directory->subdirs = NULL; directory->entries = NULL; items = g_slist_sort_with_data (items, (GCompareDataFunc) gmenu_tree_item_compare, GINT_TO_POINTER (tree->flags)); tmp = items; while (tmp != NULL) { GMenuTreeItem *item = tmp->data; GMenuTreeItemType type; type = item->type; if (type == GMENU_TREE_ITEM_ALIAS) { merge_alias (tree, directory, GMENU_TREE_ALIAS (item)); gmenu_tree_item_unref (item); } else if (type == GMENU_TREE_ITEM_DIRECTORY) { if (!find_name_in_list (GMENU_TREE_DIRECTORY (item)->name, except_subdirs)) { merge_subdir (tree, directory, GMENU_TREE_DIRECTORY (item)); gmenu_tree_item_unref (item); } else { menu_verbose ("Not merging directory '%s' yet\n", GMENU_TREE_DIRECTORY (item)->name); directory->subdirs = g_slist_append (directory->subdirs, item); } } else if (type == GMENU_TREE_ITEM_ENTRY) { if (!find_name_in_list (GMENU_TREE_ENTRY (item)->desktop_file_id, except_entries)) { merge_entry (tree, directory, GMENU_TREE_ENTRY (item)); gmenu_tree_item_unref (item); } else { menu_verbose ("Not merging entry '%s' yet\n", GMENU_TREE_ENTRY (item)->desktop_file_id); directory->entries = g_slist_append (directory->entries, item); } } else { g_assert_not_reached (); } tmp = tmp->next; } g_slist_free (items); g_slist_free (except_subdirs); g_slist_free (except_entries); } static GSList * get_subdirs_from_layout_info (GSList *layout_info) { GSList *subdirs; GSList *tmp; subdirs = NULL; tmp = layout_info; while (tmp != NULL) { MenuLayoutNode *node = tmp->data; if (menu_layout_node_get_type (node) == MENU_LAYOUT_NODE_MENUNAME) { subdirs = g_slist_append (subdirs, (char *) menu_layout_node_get_content (node)); } tmp = tmp->next; } return subdirs; } static GSList * get_entries_from_layout_info (GSList *layout_info) { GSList *entries; GSList *tmp; entries = NULL; tmp = layout_info; while (tmp != NULL) { MenuLayoutNode *node = tmp->data; if (menu_layout_node_get_type (node) == MENU_LAYOUT_NODE_FILENAME) { entries = g_slist_append (entries, (char *) menu_layout_node_get_content (node)); } tmp = tmp->next; } return entries; } static void process_layout_info (GMenuTree *tree, GMenuTreeDirectory *directory) { GSList *layout_info; menu_verbose ("Processing menu layout hints for %s\n", directory->name); g_slist_foreach (directory->contents, (GFunc) gmenu_tree_item_unref_and_unset_parent, NULL); g_slist_free (directory->contents); directory->contents = NULL; directory->layout_pending_separator = FALSE; layout_info = get_layout_info (directory, NULL); if (layout_info == NULL) { merge_subdirs (tree, directory, NULL); merge_entries (tree, directory, NULL); } else { GSList *tmp; tmp = layout_info; while (tmp != NULL) { MenuLayoutNode *node = tmp->data; switch (menu_layout_node_get_type (node)) { case MENU_LAYOUT_NODE_MENUNAME: merge_subdir_by_name (tree, directory, menu_layout_node_get_content (node)); break; case MENU_LAYOUT_NODE_FILENAME: merge_entry_by_id (tree, directory, menu_layout_node_get_content (node)); break; case MENU_LAYOUT_NODE_SEPARATOR: /* Unless explicitly told to show all separators, do not show a * separator at the beginning of a menu. Note that we don't add * the separators now, and instead make it pending. This way, we * won't show two consecutive separators nor will we show a * separator at the end of a menu. */ if (tree->flags & GMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS) { directory->layout_pending_separator = TRUE; check_pending_separator (directory); } else if (directory->contents) { menu_verbose ("Adding a potential separator in '%s'\n", directory->name); directory->layout_pending_separator = TRUE; } else { menu_verbose ("Skipping separator at the beginning of '%s'\n", directory->name); } break; case MENU_LAYOUT_NODE_MERGE: switch (menu_layout_node_merge_get_type (node)) { case MENU_LAYOUT_MERGE_NONE: break; case MENU_LAYOUT_MERGE_MENUS: merge_subdirs (tree, directory, get_subdirs_from_layout_info (tmp->next)); break; case MENU_LAYOUT_MERGE_FILES: merge_entries (tree, directory, get_entries_from_layout_info (tmp->next)); break; case MENU_LAYOUT_MERGE_ALL: merge_subdirs_and_entries (tree, directory, get_subdirs_from_layout_info (tmp->next), get_entries_from_layout_info (tmp->next)); break; default: g_assert_not_reached (); break; } break; default: g_assert_not_reached (); break; } tmp = tmp->next; } } g_slist_foreach (directory->subdirs, (GFunc) gmenu_tree_item_unref, NULL); g_slist_free (directory->subdirs); directory->subdirs = NULL; g_slist_foreach (directory->entries, (GFunc) gmenu_tree_item_unref, NULL); g_slist_free (directory->entries); directory->entries = NULL; g_slist_foreach (directory->default_layout_info, (GFunc) menu_layout_node_unref, NULL); g_slist_free (directory->default_layout_info); directory->default_layout_info = NULL; g_slist_foreach (directory->layout_info, (GFunc) menu_layout_node_unref, NULL); g_slist_free (directory->layout_info); directory->layout_info = NULL; } static void handle_entries_changed (MenuLayoutNode *layout, GMenuTree *tree) { if (tree->layout == layout) { gmenu_tree_force_rebuild (tree); gmenu_tree_invoke_monitors (tree); } } static void update_entry_index (GMenuTree *tree, GMenuTreeDirectory *dir) { GMenuTreeIter *iter = gmenu_tree_directory_iter (dir); GMenuTreeItemType next_type; while ((next_type = gmenu_tree_iter_next (iter)) != GMENU_TREE_ITEM_INVALID) { gpointer item = NULL; switch (next_type) { case GMENU_TREE_ITEM_ENTRY: { const char *id; item = gmenu_tree_iter_get_entry (iter); id = gmenu_tree_entry_get_desktop_file_id (item); if (id != NULL) g_hash_table_insert (tree->entries_by_id, (char*)id, item); } break; case GMENU_TREE_ITEM_DIRECTORY: { item = gmenu_tree_iter_get_directory (iter); update_entry_index (tree, (GMenuTreeDirectory*)item); } break; default: break; } if (item != NULL) gmenu_tree_item_unref (item); } gmenu_tree_iter_unref (iter); } static gboolean gmenu_tree_build_from_layout (GMenuTree *tree, GError **error) { DesktopEntrySet *allocated; if (tree->root) return TRUE; if (!gmenu_tree_load_layout (tree, error)) return FALSE; menu_verbose ("Building menu tree from layout\n"); allocated = desktop_entry_set_new (); /* create the menu structure */ tree->root = process_layout (tree, NULL, find_menu_child (tree->layout), allocated); if (tree->root) { DesktopEntrySet *unallocated_used; unallocated_used = desktop_entry_set_new (); process_only_unallocated (tree, tree->root, allocated, unallocated_used); if (tree->flags & GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED) { DesktopEntrySet *entry_pool; DesktopEntrySet *still_unallocated; GetStillUnallocatedForeachData data; entry_pool = _entry_directory_list_get_all_desktops (menu_layout_node_menu_get_app_dirs (find_menu_child (tree->layout))); still_unallocated = desktop_entry_set_new (); data.tree = tree; data.allocated = allocated; data.unallocated_used = unallocated_used; data.still_unallocated = still_unallocated; desktop_entry_set_foreach (entry_pool, (DesktopEntrySetForeachFunc) get_still_unallocated_foreach, &data); desktop_entry_set_unref (entry_pool); desktop_entry_set_foreach (still_unallocated, (DesktopEntrySetForeachFunc) unallocated_entries_listify_foreach, tree->root); desktop_entry_set_unref (still_unallocated); } desktop_entry_set_unref (unallocated_used); /* process the layout info part that can move/remove items: * inline, show_empty, etc. */ preprocess_layout_info (tree, tree->root); /* populate the menu structure that we got with the items, and order it * according to the layout info */ process_layout_info (tree, tree->root); update_entry_index (tree, tree->root); menu_layout_node_root_add_entries_monitor (tree->layout, (MenuLayoutNodeEntriesChangedFunc) handle_entries_changed, tree); } desktop_entry_set_unref (allocated); return TRUE; } static void gmenu_tree_force_rebuild (GMenuTree *tree) { if (tree->root) { g_hash_table_remove_all (tree->entries_by_id); gmenu_tree_item_unref (tree->root); tree->root = NULL; tree->loaded = FALSE; g_assert (tree->layout != NULL); menu_layout_node_root_remove_entries_monitor (tree->layout, (MenuLayoutNodeEntriesChangedFunc) handle_entries_changed, tree); } } GType gmenu_tree_iter_get_type (void) { static GType gtype = G_TYPE_INVALID; if (gtype == G_TYPE_INVALID) { gtype = g_boxed_type_register_static ("GMenuTreeIter", (GBoxedCopyFunc)gmenu_tree_iter_ref, (GBoxedFreeFunc)gmenu_tree_iter_unref); } return gtype; } GType gmenu_tree_directory_get_type (void) { static GType gtype = G_TYPE_INVALID; if (gtype == G_TYPE_INVALID) { gtype = g_boxed_type_register_static ("GMenuTreeDirectory", (GBoxedCopyFunc)gmenu_tree_item_ref, (GBoxedFreeFunc)gmenu_tree_item_unref); } return gtype; } GType gmenu_tree_entry_get_type (void) { static GType gtype = G_TYPE_INVALID; if (gtype == G_TYPE_INVALID) { gtype = g_boxed_type_register_static ("GMenuTreeEntry", (GBoxedCopyFunc)gmenu_tree_item_ref, (GBoxedFreeFunc)gmenu_tree_item_unref); } return gtype; } GType gmenu_tree_separator_get_type (void) { static GType gtype = G_TYPE_INVALID; if (gtype == G_TYPE_INVALID) { gtype = g_boxed_type_register_static ("GMenuTreeSeparator", (GBoxedCopyFunc)gmenu_tree_item_ref, (GBoxedFreeFunc)gmenu_tree_item_unref); } return gtype; } GType gmenu_tree_header_get_type (void) { static GType gtype = G_TYPE_INVALID; if (gtype == G_TYPE_INVALID) { gtype = g_boxed_type_register_static ("GMenuTreeHeader", (GBoxedCopyFunc)gmenu_tree_item_ref, (GBoxedFreeFunc)gmenu_tree_item_unref); } return gtype; } GType gmenu_tree_alias_get_type (void) { static GType gtype = G_TYPE_INVALID; if (gtype == G_TYPE_INVALID) { gtype = g_boxed_type_register_static ("GMenuTreeAlias", (GBoxedCopyFunc)gmenu_tree_item_ref, (GBoxedFreeFunc)gmenu_tree_item_unref); } return gtype; } GType gmenu_tree_flags_get_type (void) { static GType enum_type_id = 0; if (G_UNLIKELY (!enum_type_id)) { static const GFlagsValue values[] = { { GMENU_TREE_FLAGS_NONE, "GMENU_TREE_FLAGS_NONE", "none" }, { GMENU_TREE_FLAGS_INCLUDE_EXCLUDED, "GMENU_TREE_FLAGS_INCLUDE_EXCLUDED", "include-excluded" }, { GMENU_TREE_FLAGS_SHOW_EMPTY, "GMENU_TREE_FLAGS_SHOW_EMPTY", "show-empty" }, { GMENU_TREE_FLAGS_INCLUDE_NODISPLAY, "GMENU_TREE_FLAGS_INCLUDE_NODISPLAY", "include-nodisplay" }, { GMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS, "GMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS", "show-all-separators" }, { GMENU_TREE_FLAGS_SORT_DISPLAY_NAME, "GMENU_TREE_FLAGS_SORT_DISPLAY_NAME", "sort-display-name" }, { GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED, "GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED,", "include-unallocated" }, { 0, NULL, NULL } }; enum_type_id = g_flags_register_static ("GMenuTreeFlags", values); } return enum_type_id; } cinnamon-menus-4.4.0/libmenu/gmenu-tree.h000066400000000000000000000160231356375150600203370ustar00rootroot00000000000000/* * Copyright (C) 2004, 2011 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __GMENU_TREE_H__ #define __GMENU_TREE_H__ #ifndef GMENU_I_KNOW_THIS_IS_UNSTABLE #error "libgnome-menu should only be used if you understand that it's subject to frequent change, and is not supported as a fixed API/ABI or as part of the platform" #endif #include G_BEGIN_DECLS #define GMENU_TYPE_TREE (gmenu_tree_get_type ()) #define GMENU_TREE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GMENU_TYPE_TREE, GMenuTree)) #define GMENU_TREE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GMENU_TYPE_TREE, GMenuTreeClass)) #define GMENU_IS_TREE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GMENU_TYPE_TREE)) #define GMENU_IS_TREE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GMENU_TYPE_TREE)) #define GMENU_TREE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DESKTOP_APP_INFO, GMenuTreeClass)) typedef struct _GMenuTree GMenuTree; typedef struct _GMenuTreeClass GMenuTreeClass; struct _GMenuTreeClass { GObjectClass parent_class; }; GType gmenu_tree_get_type (void) G_GNUC_CONST; typedef struct GMenuTreeIter GMenuTreeIter; typedef struct GMenuTreeDirectory GMenuTreeDirectory; typedef struct GMenuTreeEntry GMenuTreeEntry; typedef struct GMenuTreeSeparator GMenuTreeSeparator; typedef struct GMenuTreeHeader GMenuTreeHeader; typedef struct GMenuTreeAlias GMenuTreeAlias; typedef enum { GMENU_TREE_ITEM_INVALID = 0, GMENU_TREE_ITEM_DIRECTORY, GMENU_TREE_ITEM_ENTRY, GMENU_TREE_ITEM_SEPARATOR, GMENU_TREE_ITEM_HEADER, GMENU_TREE_ITEM_ALIAS } GMenuTreeItemType; GType gmenu_tree_iter_get_type (void); /* Explicitly skip item, it's a "hidden" base class */ GType gmenu_tree_directory_get_type (void); GType gmenu_tree_entry_get_type (void); GType gmenu_tree_separator_get_type (void); GType gmenu_tree_header_get_type (void); GType gmenu_tree_alias_get_type (void); typedef enum { GMENU_TREE_FLAGS_NONE = 0, GMENU_TREE_FLAGS_INCLUDE_EXCLUDED = 1 << 0, GMENU_TREE_FLAGS_INCLUDE_NODISPLAY = 1 << 1, GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED = 1 << 2, /* leave some space for more include flags */ GMENU_TREE_FLAGS_SHOW_EMPTY = 1 << 8, GMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS = 1 << 9, /* leave some space for more show flags */ GMENU_TREE_FLAGS_SORT_DISPLAY_NAME = 1 << 16 } GMenuTreeFlags; GType gmenu_tree_flags_get_type (void); #define GMENU_TYPE_TREE_FLAGS (gmenu_tree_flags_get_type ()) GMenuTree *gmenu_tree_new (const char *menu_basename, GMenuTreeFlags flags); GMenuTree *gmenu_tree_new_for_path (const char *menu_path, GMenuTreeFlags flags); gboolean gmenu_tree_load_sync (GMenuTree *tree, GError **error); const char *gmenu_tree_get_canonical_menu_path (GMenuTree *tree); GMenuTreeDirectory *gmenu_tree_get_root_directory (GMenuTree *tree); GMenuTreeDirectory *gmenu_tree_get_directory_from_path (GMenuTree *tree, const char *path); GMenuTreeEntry *gmenu_tree_get_entry_by_id (GMenuTree *tree, const char *id); gpointer gmenu_tree_item_ref (gpointer item); void gmenu_tree_item_unref (gpointer item); GMenuTreeDirectory *gmenu_tree_directory_get_parent (GMenuTreeDirectory *directory); const char *gmenu_tree_directory_get_name (GMenuTreeDirectory *directory); const char *gmenu_tree_directory_get_generic_name (GMenuTreeDirectory *directory); const char *gmenu_tree_directory_get_comment (GMenuTreeDirectory *directory); GIcon *gmenu_tree_directory_get_icon (GMenuTreeDirectory *directory); const char *gmenu_tree_directory_get_desktop_file_path (GMenuTreeDirectory *directory); const char *gmenu_tree_directory_get_menu_id (GMenuTreeDirectory *directory); GMenuTree *gmenu_tree_directory_get_tree (GMenuTreeDirectory *directory); gboolean gmenu_tree_directory_get_is_nodisplay (GMenuTreeDirectory *directory); GMenuTreeIter *gmenu_tree_directory_iter (GMenuTreeDirectory *directory); GMenuTreeIter *gmenu_tree_iter_ref (GMenuTreeIter *iter); void gmenu_tree_iter_unref (GMenuTreeIter *iter); GMenuTreeItemType gmenu_tree_iter_next (GMenuTreeIter *iter); GMenuTreeDirectory *gmenu_tree_iter_get_directory (GMenuTreeIter *iter); GMenuTreeEntry *gmenu_tree_iter_get_entry (GMenuTreeIter *iter); GMenuTreeHeader *gmenu_tree_iter_get_header (GMenuTreeIter *iter); GMenuTreeAlias *gmenu_tree_iter_get_alias (GMenuTreeIter *iter); GMenuTreeSeparator *gmenu_tree_iter_get_separator (GMenuTreeIter *iter); char *gmenu_tree_directory_make_path (GMenuTreeDirectory *directory, GMenuTreeEntry *entry); GDesktopAppInfo *gmenu_tree_entry_get_app_info (GMenuTreeEntry *entry); GMenuTreeDirectory *gmenu_tree_entry_get_parent (GMenuTreeEntry *entry); GMenuTree *gmenu_tree_entry_get_tree (GMenuTreeEntry *entry); const char *gmenu_tree_entry_get_desktop_file_path (GMenuTreeEntry *entry); const char *gmenu_tree_entry_get_desktop_file_id (GMenuTreeEntry *entry); gboolean gmenu_tree_entry_get_is_nodisplay_recurse (GMenuTreeEntry *entry); gboolean gmenu_tree_entry_get_is_excluded (GMenuTreeEntry *entry); gboolean gmenu_tree_entry_get_is_unallocated (GMenuTreeEntry *entry); GMenuTreeDirectory *gmenu_tree_header_get_directory (GMenuTreeHeader *header); GMenuTree *gmenu_tree_header_get_tree (GMenuTreeHeader *header); GMenuTreeDirectory *gmenu_tree_header_get_parent (GMenuTreeHeader *header); GMenuTreeDirectory *gmenu_tree_alias_get_directory (GMenuTreeAlias *alias); GMenuTreeItemType gmenu_tree_alias_get_aliased_item_type (GMenuTreeAlias *alias); GMenuTreeDirectory *gmenu_tree_alias_get_aliased_directory (GMenuTreeAlias *alias); GMenuTreeEntry *gmenu_tree_alias_get_aliased_entry (GMenuTreeAlias *alias); GMenuTree *gmenu_tree_alias_get_tree (GMenuTreeAlias *alias); GMenuTreeDirectory *gmenu_tree_alias_get_parent (GMenuTreeAlias *alias); GMenuTree *gmenu_tree_separator_get_tree (GMenuTreeSeparator *separator); GMenuTreeDirectory *gmenu_tree_separator_get_parent (GMenuTreeSeparator *separator); G_END_DECLS #endif /* __GMENU_TREE_H__ */ cinnamon-menus-4.4.0/libmenu/menu-layout.c000066400000000000000000001720551356375150600205510ustar00rootroot00000000000000/* Menu layout in-memory data structure (a custom "DOM tree") */ /* * Copyright (C) 2002 - 2004 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "menu-layout.h" #include #include #include #include #include #include "entry-directories.h" #include "menu-util.h" typedef struct MenuLayoutNodeMenu MenuLayoutNodeMenu; typedef struct MenuLayoutNodeRoot MenuLayoutNodeRoot; typedef struct MenuLayoutNodeLegacyDir MenuLayoutNodeLegacyDir; typedef struct MenuLayoutNodeMergeFile MenuLayoutNodeMergeFile; typedef struct MenuLayoutNodeDefaultLayout MenuLayoutNodeDefaultLayout; typedef struct MenuLayoutNodeMenuname MenuLayoutNodeMenuname; typedef struct MenuLayoutNodeMerge MenuLayoutNodeMerge; struct MenuLayoutNode { /* Node lists are circular, for length-one lists * prev/next point back to the node itself. */ MenuLayoutNode *prev; MenuLayoutNode *next; MenuLayoutNode *parent; MenuLayoutNode *children; char *content; guint refcount : 20; guint type : 7; }; struct MenuLayoutNodeRoot { MenuLayoutNode node; char *basedir; char *name; GMainContext *main_context; GSList *monitors; GSource *monitors_idle_handler; }; struct MenuLayoutNodeMenu { MenuLayoutNode node; MenuLayoutNode *name_node; /* cache of the node */ EntryDirectoryList *app_dirs; EntryDirectoryList *dir_dirs; }; struct MenuLayoutNodeLegacyDir { MenuLayoutNode node; char *prefix; }; struct MenuLayoutNodeMergeFile { MenuLayoutNode node; MenuMergeFileType type; }; struct MenuLayoutNodeDefaultLayout { MenuLayoutNode node; MenuLayoutValues layout_values; }; struct MenuLayoutNodeMenuname { MenuLayoutNode node; MenuLayoutValues layout_values; }; struct MenuLayoutNodeMerge { MenuLayoutNode node; MenuLayoutMergeType merge_type; }; typedef struct { MenuLayoutNodeEntriesChangedFunc callback; gpointer user_data; } MenuLayoutNodeEntriesMonitor; static inline MenuLayoutNode * node_next (MenuLayoutNode *node) { /* root nodes (no parent) never have siblings */ if (node->parent == NULL) return NULL; /* circular list */ if (node->next == node->parent->children) return NULL; return node->next; } static gboolean menu_layout_invoke_monitors (MenuLayoutNodeRoot *nr) { GSList *tmp; g_assert (nr->node.type == MENU_LAYOUT_NODE_ROOT); nr->monitors_idle_handler = NULL; tmp = nr->monitors; while (tmp != NULL) { MenuLayoutNodeEntriesMonitor *monitor = tmp->data; GSList *next = tmp->next; monitor->callback ((MenuLayoutNode *) nr, monitor->user_data); tmp = next; } return FALSE; } static void handle_entry_directory_changed (EntryDirectory *dir, MenuLayoutNode *node) { MenuLayoutNodeRoot *nr; g_assert (node->type == MENU_LAYOUT_NODE_MENU); nr = (MenuLayoutNodeRoot *) menu_layout_node_get_root (node); if (nr->monitors_idle_handler == NULL) { nr->monitors_idle_handler = g_idle_source_new (); g_source_set_callback (nr->monitors_idle_handler, (GSourceFunc) menu_layout_invoke_monitors, nr, NULL); g_source_attach (nr->monitors_idle_handler, nr->main_context); g_source_unref (nr->monitors_idle_handler); } } static void remove_entry_directory_list (MenuLayoutNodeMenu *nm, EntryDirectoryList **dirs) { if (*dirs) { entry_directory_list_remove_monitors (*dirs, (EntryDirectoryChangedFunc) handle_entry_directory_changed, nm); entry_directory_list_unref (*dirs); *dirs = NULL; } } MenuLayoutNode * menu_layout_node_ref (MenuLayoutNode *node) { g_return_val_if_fail (node != NULL, NULL); node->refcount += 1; return node; } void menu_layout_node_unref (MenuLayoutNode *node) { g_return_if_fail (node != NULL); g_return_if_fail (node->refcount > 0); node->refcount -= 1; if (node->refcount == 0) { MenuLayoutNode *iter; iter = node->children; while (iter != NULL) { MenuLayoutNode *next = node_next (iter); menu_layout_node_unref (iter); iter = next; } if (node->type == MENU_LAYOUT_NODE_MENU) { MenuLayoutNodeMenu *nm = (MenuLayoutNodeMenu *) node; if (nm->name_node) menu_layout_node_unref (nm->name_node); remove_entry_directory_list (nm, &nm->app_dirs); remove_entry_directory_list (nm, &nm->dir_dirs); } else if (node->type == MENU_LAYOUT_NODE_LEGACY_DIR) { MenuLayoutNodeLegacyDir *legacy = (MenuLayoutNodeLegacyDir *) node; g_free (legacy->prefix); } else if (node->type == MENU_LAYOUT_NODE_ROOT) { MenuLayoutNodeRoot *nr = (MenuLayoutNodeRoot*) node; g_slist_foreach (nr->monitors, (GFunc) g_free, NULL); g_slist_free (nr->monitors); if (nr->monitors_idle_handler != NULL) g_source_destroy (nr->monitors_idle_handler); nr->monitors_idle_handler = NULL; if (nr->main_context != NULL) g_main_context_unref (nr->main_context); nr->main_context = NULL; g_free (nr->basedir); g_free (nr->name); } g_free (node->content); g_free (node); } } MenuLayoutNode * menu_layout_node_new (MenuLayoutNodeType type) { MenuLayoutNode *node; switch (type) { case MENU_LAYOUT_NODE_MENU: node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMenu, 1); break; case MENU_LAYOUT_NODE_LEGACY_DIR: node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeLegacyDir, 1); break; case MENU_LAYOUT_NODE_ROOT: node = (MenuLayoutNode*) g_new0 (MenuLayoutNodeRoot, 1); break; case MENU_LAYOUT_NODE_MERGE_FILE: node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMergeFile, 1); break; case MENU_LAYOUT_NODE_DEFAULT_LAYOUT: node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeDefaultLayout, 1); break; case MENU_LAYOUT_NODE_MENUNAME: node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMenuname, 1); break; case MENU_LAYOUT_NODE_MERGE: node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMerge, 1); break; default: node = g_new0 (MenuLayoutNode, 1); break; } node->type = type; node->refcount = 1; /* we're in a list of one node */ node->next = node; node->prev = node; return node; } MenuLayoutNode * menu_layout_node_get_next (MenuLayoutNode *node) { return node_next (node); } MenuLayoutNode * menu_layout_node_get_parent (MenuLayoutNode *node) { return node->parent; } MenuLayoutNode * menu_layout_node_get_children (MenuLayoutNode *node) { return node->children; } MenuLayoutNode * menu_layout_node_get_root (MenuLayoutNode *node) { MenuLayoutNode *parent; parent = node; while (parent->parent != NULL) parent = parent->parent; g_assert (parent->type == MENU_LAYOUT_NODE_ROOT); return parent; } char * menu_layout_node_get_content_as_path (MenuLayoutNode *node) { if (node->content == NULL) { menu_verbose (" (node has no content to get as a path)\n"); return NULL; } if (g_path_is_absolute (node->content)) { return g_strdup (node->content); } else { MenuLayoutNodeRoot *root; root = (MenuLayoutNodeRoot *) menu_layout_node_get_root (node); if (root->basedir == NULL) { menu_verbose ("No basedir available, using \"%s\" as-is\n", node->content); return g_strdup (node->content); } else { menu_verbose ("Using basedir \"%s\" filename \"%s\"\n", root->basedir, node->content); return g_build_filename (root->basedir, node->content, NULL); } } } #define RETURN_IF_NO_PARENT(node) G_STMT_START { \ if ((node)->parent == NULL) \ { \ g_warning ("To add siblings to a menu node, " \ "it must not be the root node, " \ "and must be linked in below some root node\n" \ "node parent = %p and type = %d", \ (node)->parent, (node)->type); \ return; \ } \ } G_STMT_END #define RETURN_IF_HAS_ENTRY_DIRS(node) G_STMT_START { \ if ((node)->type == MENU_LAYOUT_NODE_MENU && \ (((MenuLayoutNodeMenu*)(node))->app_dirs != NULL || \ ((MenuLayoutNodeMenu*)(node))->dir_dirs != NULL)) \ { \ g_warning ("node acquired ->app_dirs or ->dir_dirs " \ "while not rooted in a tree\n"); \ return; \ } \ } G_STMT_END \ void menu_layout_node_insert_before (MenuLayoutNode *node, MenuLayoutNode *new_sibling) { g_return_if_fail (new_sibling != NULL); g_return_if_fail (new_sibling->parent == NULL); RETURN_IF_NO_PARENT (node); RETURN_IF_HAS_ENTRY_DIRS (new_sibling); new_sibling->next = node; new_sibling->prev = node->prev; node->prev = new_sibling; new_sibling->prev->next = new_sibling; new_sibling->parent = node->parent; if (node == node->parent->children) node->parent->children = new_sibling; menu_layout_node_ref (new_sibling); } void menu_layout_node_insert_after (MenuLayoutNode *node, MenuLayoutNode *new_sibling) { g_return_if_fail (new_sibling != NULL); g_return_if_fail (new_sibling->parent == NULL); RETURN_IF_NO_PARENT (node); RETURN_IF_HAS_ENTRY_DIRS (new_sibling); new_sibling->prev = node; new_sibling->next = node->next; node->next = new_sibling; new_sibling->next->prev = new_sibling; new_sibling->parent = node->parent; menu_layout_node_ref (new_sibling); } void menu_layout_node_prepend_child (MenuLayoutNode *parent, MenuLayoutNode *new_child) { RETURN_IF_HAS_ENTRY_DIRS (new_child); if (parent->children) { menu_layout_node_insert_before (parent->children, new_child); } else { parent->children = menu_layout_node_ref (new_child); new_child->parent = parent; } } void menu_layout_node_append_child (MenuLayoutNode *parent, MenuLayoutNode *new_child) { RETURN_IF_HAS_ENTRY_DIRS (new_child); if (parent->children) { menu_layout_node_insert_after (parent->children->prev, new_child); } else { parent->children = menu_layout_node_ref (new_child); new_child->parent = parent; } } void menu_layout_node_unlink (MenuLayoutNode *node) { g_return_if_fail (node != NULL); g_return_if_fail (node->parent != NULL); menu_layout_node_steal (node); menu_layout_node_unref (node); } static void recursive_clean_entry_directory_lists (MenuLayoutNode *node, gboolean apps) { EntryDirectoryList **dirs; MenuLayoutNodeMenu *nm; MenuLayoutNode *iter; if (node->type != MENU_LAYOUT_NODE_MENU) return; nm = (MenuLayoutNodeMenu *) node; dirs = apps ? &nm->app_dirs : &nm->dir_dirs; if (*dirs == NULL || entry_directory_list_get_length (*dirs) == 0) return; /* child menus continue to have valid lists */ remove_entry_directory_list (nm, dirs); iter = node->children; while (iter != NULL) { if (iter->type == MENU_LAYOUT_NODE_MENU) recursive_clean_entry_directory_lists (iter, apps); iter = node_next (iter); } } void menu_layout_node_steal (MenuLayoutNode *node) { g_return_if_fail (node != NULL); g_return_if_fail (node->parent != NULL); switch (node->type) { case MENU_LAYOUT_NODE_NAME: { MenuLayoutNodeMenu *nm = (MenuLayoutNodeMenu *) node->parent; if (nm->name_node == node) { menu_layout_node_unref (nm->name_node); nm->name_node = NULL; } } break; case MENU_LAYOUT_NODE_APP_DIR: recursive_clean_entry_directory_lists (node->parent, TRUE); break; case MENU_LAYOUT_NODE_DIRECTORY_DIR: recursive_clean_entry_directory_lists (node->parent, FALSE); break; default: break; } if (node->parent && node->parent->children == node) { if (node->next != node) node->parent->children = node->next; else node->parent->children = NULL; } /* these are no-ops for length-one node lists */ node->prev->next = node->next; node->next->prev = node->prev; node->parent = NULL; /* point to ourselves, now we're length one */ node->next = node; node->prev = node; } MenuLayoutNodeType menu_layout_node_get_type (MenuLayoutNode *node) { return node->type; } const char * menu_layout_node_get_content (MenuLayoutNode *node) { return node->content; } void menu_layout_node_set_content (MenuLayoutNode *node, const char *content) { if (node->content == content) return; g_free (node->content); node->content = g_strdup (content); } const char * menu_layout_node_root_get_name (MenuLayoutNode *node) { MenuLayoutNodeRoot *nr; g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_ROOT, NULL); nr = (MenuLayoutNodeRoot*) node; return nr->name; } const char * menu_layout_node_root_get_basedir (MenuLayoutNode *node) { MenuLayoutNodeRoot *nr; g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_ROOT, NULL); nr = (MenuLayoutNodeRoot*) node; return nr->basedir; } const char * menu_layout_node_menu_get_name (MenuLayoutNode *node) { MenuLayoutNodeMenu *nm; g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL); nm = (MenuLayoutNodeMenu*) node; if (nm->name_node == NULL) { MenuLayoutNode *iter; iter = node->children; while (iter != NULL) { if (iter->type == MENU_LAYOUT_NODE_NAME) { nm->name_node = menu_layout_node_ref (iter); break; } iter = node_next (iter); } } if (nm->name_node == NULL) return NULL; return menu_layout_node_get_content (nm->name_node); } static void ensure_dir_lists (MenuLayoutNodeMenu *nm) { MenuLayoutNode *node; MenuLayoutNode *iter; EntryDirectoryList *app_dirs; EntryDirectoryList *dir_dirs; node = (MenuLayoutNode *) nm; if (nm->app_dirs && nm->dir_dirs) return; app_dirs = NULL; dir_dirs = NULL; if (nm->app_dirs == NULL) { app_dirs = entry_directory_list_new (); if (node->parent && node->parent->type == MENU_LAYOUT_NODE_MENU) { EntryDirectoryList *dirs; if ((dirs = menu_layout_node_menu_get_app_dirs (node->parent))) entry_directory_list_append_list (app_dirs, dirs); } } if (nm->dir_dirs == NULL) { dir_dirs = entry_directory_list_new (); if (node->parent && node->parent->type == MENU_LAYOUT_NODE_MENU) { EntryDirectoryList *dirs; if ((dirs = menu_layout_node_menu_get_directory_dirs (node->parent))) entry_directory_list_append_list (dir_dirs, dirs); } } iter = node->children; while (iter != NULL) { EntryDirectory *ed; if (app_dirs != NULL && iter->type == MENU_LAYOUT_NODE_APP_DIR) { char *path; path = menu_layout_node_get_content_as_path (iter); ed = entry_directory_new (DESKTOP_ENTRY_DESKTOP, path); if (ed != NULL) { entry_directory_list_prepend (app_dirs, ed); entry_directory_unref (ed); } g_free (path); } if (dir_dirs != NULL && iter->type == MENU_LAYOUT_NODE_DIRECTORY_DIR) { char *path; path = menu_layout_node_get_content_as_path (iter); ed = entry_directory_new (DESKTOP_ENTRY_DIRECTORY, path); if (ed != NULL) { entry_directory_list_prepend (dir_dirs, ed); entry_directory_unref (ed); } g_free (path); } iter = node_next (iter); } if (app_dirs) { g_assert (nm->app_dirs == NULL); nm->app_dirs = app_dirs; entry_directory_list_add_monitors (nm->app_dirs, (EntryDirectoryChangedFunc) handle_entry_directory_changed, nm); } if (dir_dirs) { g_assert (nm->dir_dirs == NULL); nm->dir_dirs = dir_dirs; entry_directory_list_add_monitors (nm->dir_dirs, (EntryDirectoryChangedFunc) handle_entry_directory_changed, nm); } } EntryDirectoryList * menu_layout_node_menu_get_app_dirs (MenuLayoutNode *node) { MenuLayoutNodeMenu *nm; g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL); nm = (MenuLayoutNodeMenu *) node; ensure_dir_lists (nm); return nm->app_dirs; } EntryDirectoryList * menu_layout_node_menu_get_directory_dirs (MenuLayoutNode *node) { MenuLayoutNodeMenu *nm; g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL); nm = (MenuLayoutNodeMenu *) node; ensure_dir_lists (nm); return nm->dir_dirs; } const char * menu_layout_node_move_get_old (MenuLayoutNode *node) { MenuLayoutNode *iter; iter = node->children; while (iter != NULL) { if (iter->type == MENU_LAYOUT_NODE_OLD) return iter->content; iter = node_next (iter); } return NULL; } const char * menu_layout_node_move_get_new (MenuLayoutNode *node) { MenuLayoutNode *iter; iter = node->children; while (iter != NULL) { if (iter->type == MENU_LAYOUT_NODE_NEW) return iter->content; iter = node_next (iter); } return NULL; } const char * menu_layout_node_legacy_dir_get_prefix (MenuLayoutNode *node) { MenuLayoutNodeLegacyDir *legacy; g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_LEGACY_DIR, NULL); legacy = (MenuLayoutNodeLegacyDir *) node; return legacy->prefix; } void menu_layout_node_legacy_dir_set_prefix (MenuLayoutNode *node, const char *prefix) { MenuLayoutNodeLegacyDir *legacy; g_return_if_fail (node->type == MENU_LAYOUT_NODE_LEGACY_DIR); legacy = (MenuLayoutNodeLegacyDir *) node; g_free (legacy->prefix); legacy->prefix = g_strdup (prefix); } MenuMergeFileType menu_layout_node_merge_file_get_type (MenuLayoutNode *node) { MenuLayoutNodeMergeFile *merge_file; g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MERGE_FILE, FALSE); merge_file = (MenuLayoutNodeMergeFile *) node; return merge_file->type; } void menu_layout_node_merge_file_set_type (MenuLayoutNode *node, MenuMergeFileType type) { MenuLayoutNodeMergeFile *merge_file; g_return_if_fail (node->type == MENU_LAYOUT_NODE_MERGE_FILE); merge_file = (MenuLayoutNodeMergeFile *) node; merge_file->type = type; } MenuLayoutMergeType menu_layout_node_merge_get_type (MenuLayoutNode *node) { MenuLayoutNodeMerge *merge; g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MERGE, 0); merge = (MenuLayoutNodeMerge *) node; return merge->merge_type; } static void menu_layout_node_merge_set_type (MenuLayoutNode *node, const char *merge_type) { MenuLayoutNodeMerge *merge; g_return_if_fail (node->type == MENU_LAYOUT_NODE_MERGE); merge = (MenuLayoutNodeMerge *) node; merge->merge_type = MENU_LAYOUT_MERGE_NONE; if (strcmp (merge_type, "menus") == 0) { merge->merge_type = MENU_LAYOUT_MERGE_MENUS; } else if (strcmp (merge_type, "files") == 0) { merge->merge_type = MENU_LAYOUT_MERGE_FILES; } else if (strcmp (merge_type, "all") == 0) { merge->merge_type = MENU_LAYOUT_MERGE_ALL; } } void menu_layout_node_default_layout_get_values (MenuLayoutNode *node, MenuLayoutValues *values) { MenuLayoutNodeDefaultLayout *default_layout; g_return_if_fail (node->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT); g_return_if_fail (values != NULL); default_layout = (MenuLayoutNodeDefaultLayout *) node; *values = default_layout->layout_values; } void menu_layout_node_menuname_get_values (MenuLayoutNode *node, MenuLayoutValues *values) { MenuLayoutNodeMenuname *menuname; g_return_if_fail (node->type == MENU_LAYOUT_NODE_MENUNAME); g_return_if_fail (values != NULL); menuname = (MenuLayoutNodeMenuname *) node; *values = menuname->layout_values; } static void menu_layout_values_set (MenuLayoutValues *values, const char *show_empty, const char *inline_menus, const char *inline_limit, const char *inline_header, const char *inline_alias) { values->mask = MENU_LAYOUT_VALUES_NONE; values->show_empty = FALSE; values->inline_menus = FALSE; values->inline_limit = 4; values->inline_header = FALSE; values->inline_alias = FALSE; if (show_empty != NULL) { values->show_empty = strcmp (show_empty, "true") == 0; values->mask |= MENU_LAYOUT_VALUES_SHOW_EMPTY; } if (inline_menus != NULL) { values->inline_menus = strcmp (inline_menus, "true") == 0; values->mask |= MENU_LAYOUT_VALUES_INLINE_MENUS; } if (inline_limit != NULL) { char *end; int limit; limit = strtol (inline_limit, &end, 10); if (*end == '\0') { values->inline_limit = limit; values->mask |= MENU_LAYOUT_VALUES_INLINE_LIMIT; } } if (inline_header != NULL) { values->inline_header = strcmp (inline_header, "true") == 0; values->mask |= MENU_LAYOUT_VALUES_INLINE_HEADER; } if (inline_alias != NULL) { values->inline_alias = strcmp (inline_alias, "true") == 0; values->mask |= MENU_LAYOUT_VALUES_INLINE_ALIAS; } } static void menu_layout_node_default_layout_set_values (MenuLayoutNode *node, const char *show_empty, const char *inline_menus, const char *inline_limit, const char *inline_header, const char *inline_alias) { MenuLayoutNodeDefaultLayout *default_layout; g_return_if_fail (node->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT); default_layout = (MenuLayoutNodeDefaultLayout *) node; menu_layout_values_set (&default_layout->layout_values, show_empty, inline_menus, inline_limit, inline_header, inline_alias); } static void menu_layout_node_menuname_set_values (MenuLayoutNode *node, const char *show_empty, const char *inline_menus, const char *inline_limit, const char *inline_header, const char *inline_alias) { MenuLayoutNodeMenuname *menuname; g_return_if_fail (node->type == MENU_LAYOUT_NODE_MENUNAME); menuname = (MenuLayoutNodeMenuname *) node; menu_layout_values_set (&menuname->layout_values, show_empty, inline_menus, inline_limit, inline_header, inline_alias); } void menu_layout_node_root_add_entries_monitor (MenuLayoutNode *node, MenuLayoutNodeEntriesChangedFunc callback, gpointer user_data) { MenuLayoutNodeEntriesMonitor *monitor; MenuLayoutNodeRoot *nr; GSList *tmp; g_return_if_fail (node->type == MENU_LAYOUT_NODE_ROOT); nr = (MenuLayoutNodeRoot *) node; tmp = nr->monitors; while (tmp != NULL) { monitor = tmp->data; if (monitor->callback == callback && monitor->user_data == user_data) break; tmp = tmp->next; } if (tmp == NULL) { monitor = g_new0 (MenuLayoutNodeEntriesMonitor, 1); monitor->callback = callback; monitor->user_data = user_data; nr->monitors = g_slist_append (nr->monitors, monitor); } } void menu_layout_node_root_remove_entries_monitor (MenuLayoutNode *node, MenuLayoutNodeEntriesChangedFunc callback, gpointer user_data) { MenuLayoutNodeRoot *nr; GSList *tmp; g_return_if_fail (node->type == MENU_LAYOUT_NODE_ROOT); nr = (MenuLayoutNodeRoot *) node; tmp = nr->monitors; while (tmp != NULL) { MenuLayoutNodeEntriesMonitor *monitor = tmp->data; GSList *next = tmp->next; if (monitor->callback == callback && monitor->user_data == user_data) { nr->monitors = g_slist_delete_link (nr->monitors, tmp); g_free (monitor); } tmp = next; } } /* * Menu file parsing */ typedef struct { MenuLayoutNode *root; MenuLayoutNode *stack_top; } MenuParser; static void set_error (GError **err, GMarkupParseContext *context, int error_domain, int error_code, const char *format, ...) G_GNUC_PRINTF (5, 6); static void add_context_to_error (GError **err, GMarkupParseContext *context); static void start_element_handler (GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, gpointer user_data, GError **error); static void end_element_handler (GMarkupParseContext *context, const char *element_name, gpointer user_data, GError **error); static void text_handler (GMarkupParseContext *context, const char *text, gsize text_len, gpointer user_data, GError **error); static void passthrough_handler (GMarkupParseContext *context, const char *passthrough_text, gsize text_len, gpointer user_data, GError **error); static GMarkupParser menu_funcs = { start_element_handler, end_element_handler, text_handler, passthrough_handler, NULL }; static void set_error (GError **err, GMarkupParseContext *context, int error_domain, int error_code, const char *format, ...) { int line, ch; va_list args; char *str; g_markup_parse_context_get_position (context, &line, &ch); va_start (args, format); str = g_strdup_vprintf (format, args); va_end (args); g_set_error (err, error_domain, error_code, "Line %d character %d: %s", line, ch, str); g_free (str); } static void add_context_to_error (GError **err, GMarkupParseContext *context) { int line, ch; char *str; if (err == NULL || *err == NULL) return; g_markup_parse_context_get_position (context, &line, &ch); str = g_strdup_printf ("Line %d character %d: %s", line, ch, (*err)->message); g_free ((*err)->message); (*err)->message = str; } #define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0) typedef struct { const char *name; const char **retloc; } LocateAttr; static gboolean locate_attributes (GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, GError **error, const char *first_attribute_name, const char **first_attribute_retloc, ...) { #define MAX_ATTRS 24 LocateAttr attrs[MAX_ATTRS]; int n_attrs; va_list args; const char *name; const char **retloc; gboolean retval; int i; g_return_val_if_fail (first_attribute_name != NULL, FALSE); g_return_val_if_fail (first_attribute_retloc != NULL, FALSE); retval = TRUE; n_attrs = 1; attrs[0].name = first_attribute_name; attrs[0].retloc = first_attribute_retloc; *first_attribute_retloc = NULL; va_start (args, first_attribute_retloc); name = va_arg (args, const char *); retloc = va_arg (args, const char **); while (name != NULL) { g_return_val_if_fail (retloc != NULL, FALSE); g_assert (n_attrs < MAX_ATTRS); attrs[n_attrs].name = name; attrs[n_attrs].retloc = retloc; n_attrs += 1; *retloc = NULL; name = va_arg (args, const char *); retloc = va_arg (args, const char **); } va_end (args); i = 0; while (attribute_names[i]) { int j; j = 0; while (j < n_attrs) { if (strcmp (attrs[j].name, attribute_names[i]) == 0) { retloc = attrs[j].retloc; if (*retloc != NULL) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "Attribute \"%s\" repeated twice on the same <%s> element", attrs[j].name, element_name); retval = FALSE; goto out; } *retloc = attribute_values[i]; break; } ++j; } if (j == n_attrs) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "Attribute \"%s\" is invalid on <%s> element in this context", attribute_names[i], element_name); retval = FALSE; goto out; } ++i; } out: return retval; #undef MAX_ATTRS } static gboolean check_no_attributes (GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, GError **error) { if (attribute_names[0] != NULL) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "Attribute \"%s\" is invalid on <%s> element in this context", attribute_names[0], element_name); return FALSE; } return TRUE; } static int has_child_of_type (MenuLayoutNode *node, MenuLayoutNodeType type) { MenuLayoutNode *iter; iter = node->children; while (iter) { if (iter->type == type) return TRUE; iter = node_next (iter); } return FALSE; } static void push_node (MenuParser *parser, MenuLayoutNodeType type) { MenuLayoutNode *node; node = menu_layout_node_new (type); menu_layout_node_append_child (parser->stack_top, node); menu_layout_node_unref (node); parser->stack_top = node; } static void start_menu_element (MenuParser *parser, GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, GError **error) { if (!check_no_attributes (context, element_name, attribute_names, attribute_values, error)) return; if (!(parser->stack_top->type == MENU_LAYOUT_NODE_ROOT || parser->stack_top->type == MENU_LAYOUT_NODE_MENU)) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, " element can only appear below other elements or at toplevel\n"); } else { push_node (parser, MENU_LAYOUT_NODE_MENU); } } static void start_menu_child_element (MenuParser *parser, GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, GError **error) { if (ELEMENT_IS ("LegacyDir")) { const char *prefix; push_node (parser, MENU_LAYOUT_NODE_LEGACY_DIR); if (!locate_attributes (context, element_name, attribute_names, attribute_values, error, "prefix", &prefix, NULL)) return; menu_layout_node_legacy_dir_set_prefix (parser->stack_top, prefix); } else if (ELEMENT_IS ("MergeFile")) { const char *type; push_node (parser, MENU_LAYOUT_NODE_MERGE_FILE); if (!locate_attributes (context, element_name, attribute_names, attribute_values, error, "type", &type, NULL)) return; if (type != NULL && strcmp (type, "parent") == 0) { menu_layout_node_merge_file_set_type (parser->stack_top, MENU_MERGE_FILE_TYPE_PARENT); } } else if (ELEMENT_IS ("DefaultLayout")) { const char *show_empty; const char *inline_menus; const char *inline_limit; const char *inline_header; const char *inline_alias; push_node (parser, MENU_LAYOUT_NODE_DEFAULT_LAYOUT); locate_attributes (context, element_name, attribute_names, attribute_values, error, "show_empty", &show_empty, "inline", &inline_menus, "inline_limit", &inline_limit, "inline_header", &inline_header, "inline_alias", &inline_alias, NULL); menu_layout_node_default_layout_set_values (parser->stack_top, show_empty, inline_menus, inline_limit, inline_header, inline_alias); } else { if (!check_no_attributes (context, element_name, attribute_names, attribute_values, error)) return; if (ELEMENT_IS ("AppDir")) { push_node (parser, MENU_LAYOUT_NODE_APP_DIR); } else if (ELEMENT_IS ("DefaultAppDirs")) { push_node (parser, MENU_LAYOUT_NODE_DEFAULT_APP_DIRS); } else if (ELEMENT_IS ("DirectoryDir")) { push_node (parser, MENU_LAYOUT_NODE_DIRECTORY_DIR); } else if (ELEMENT_IS ("DefaultDirectoryDirs")) { push_node (parser, MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS); } else if (ELEMENT_IS ("DefaultMergeDirs")) { push_node (parser, MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS); } else if (ELEMENT_IS ("Name")) { if (has_child_of_type (parser->stack_top, MENU_LAYOUT_NODE_NAME)) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "Multiple elements in a element is not allowed\n"); return; } push_node (parser, MENU_LAYOUT_NODE_NAME); } else if (ELEMENT_IS ("Directory")) { push_node (parser, MENU_LAYOUT_NODE_DIRECTORY); } else if (ELEMENT_IS ("OnlyUnallocated")) { push_node (parser, MENU_LAYOUT_NODE_ONLY_UNALLOCATED); } else if (ELEMENT_IS ("NotOnlyUnallocated")) { push_node (parser, MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED); } else if (ELEMENT_IS ("Include")) { push_node (parser, MENU_LAYOUT_NODE_INCLUDE); } else if (ELEMENT_IS ("Exclude")) { push_node (parser, MENU_LAYOUT_NODE_EXCLUDE); } else if (ELEMENT_IS ("MergeDir")) { push_node (parser, MENU_LAYOUT_NODE_MERGE_DIR); } else if (ELEMENT_IS ("KDELegacyDirs")) { push_node (parser, MENU_LAYOUT_NODE_KDE_LEGACY_DIRS); } else if (ELEMENT_IS ("Move")) { push_node (parser, MENU_LAYOUT_NODE_MOVE); } else if (ELEMENT_IS ("Deleted")) { push_node (parser, MENU_LAYOUT_NODE_DELETED); } else if (ELEMENT_IS ("NotDeleted")) { push_node (parser, MENU_LAYOUT_NODE_NOT_DELETED); } else if (ELEMENT_IS ("Layout")) { push_node (parser, MENU_LAYOUT_NODE_LAYOUT); } else { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Element <%s> may not appear below <%s>\n", element_name, "Menu"); } } } static void start_matching_rule_element (MenuParser *parser, GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, GError **error) { if (!check_no_attributes (context, element_name, attribute_names, attribute_values, error)) return; if (ELEMENT_IS ("Filename")) { push_node (parser, MENU_LAYOUT_NODE_FILENAME); } else if (ELEMENT_IS ("Category")) { push_node (parser, MENU_LAYOUT_NODE_CATEGORY); } else if (ELEMENT_IS ("All")) { push_node (parser, MENU_LAYOUT_NODE_ALL); } else if (ELEMENT_IS ("And")) { push_node (parser, MENU_LAYOUT_NODE_AND); } else if (ELEMENT_IS ("Or")) { push_node (parser, MENU_LAYOUT_NODE_OR); } else if (ELEMENT_IS ("Not")) { push_node (parser, MENU_LAYOUT_NODE_NOT); } else { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Element <%s> may not appear in this context\n", element_name); } } static void start_move_child_element (MenuParser *parser, GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, GError **error) { if (!check_no_attributes (context, element_name, attribute_names, attribute_values, error)) return; if (ELEMENT_IS ("Old")) { push_node (parser, MENU_LAYOUT_NODE_OLD); } else if (ELEMENT_IS ("New")) { push_node (parser, MENU_LAYOUT_NODE_NEW); } else { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Element <%s> may not appear below <%s>\n", element_name, "Move"); } } static void start_layout_child_element (MenuParser *parser, GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, GError **error) { if (ELEMENT_IS ("Menuname")) { const char *show_empty; const char *inline_menus; const char *inline_limit; const char *inline_header; const char *inline_alias; push_node (parser, MENU_LAYOUT_NODE_MENUNAME); locate_attributes (context, element_name, attribute_names, attribute_values, error, "show_empty", &show_empty, "inline", &inline_menus, "inline_limit", &inline_limit, "inline_header", &inline_header, "inline_alias", &inline_alias, NULL); menu_layout_node_menuname_set_values (parser->stack_top, show_empty, inline_menus, inline_limit, inline_header, inline_alias); } else if (ELEMENT_IS ("Merge")) { const char *type; push_node (parser, MENU_LAYOUT_NODE_MERGE); locate_attributes (context, element_name, attribute_names, attribute_values, error, "type", &type, NULL); menu_layout_node_merge_set_type (parser->stack_top, type); } else { if (!check_no_attributes (context, element_name, attribute_names, attribute_values, error)) return; if (ELEMENT_IS ("Filename")) { push_node (parser, MENU_LAYOUT_NODE_FILENAME); } else if (ELEMENT_IS ("Separator")) { push_node (parser, MENU_LAYOUT_NODE_SEPARATOR); } else { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Element <%s> may not appear below <%s>\n", element_name, "Move"); } } } static void start_element_handler (GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, gpointer user_data, GError **error) { MenuParser *parser = user_data; if (ELEMENT_IS ("Menu")) { if (parser->stack_top == parser->root && has_child_of_type (parser->root, MENU_LAYOUT_NODE_MENU)) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "Multiple root elements in menu file, only one toplevel is allowed\n"); return; } start_menu_element (parser, context, element_name, attribute_names, attribute_values, error); } else if (parser->stack_top == parser->root) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "Root element in a menu file must be , not <%s>\n", element_name); } else if (parser->stack_top->type == MENU_LAYOUT_NODE_MENU) { start_menu_child_element (parser, context, element_name, attribute_names, attribute_values, error); } else if (parser->stack_top->type == MENU_LAYOUT_NODE_INCLUDE || parser->stack_top->type == MENU_LAYOUT_NODE_EXCLUDE || parser->stack_top->type == MENU_LAYOUT_NODE_AND || parser->stack_top->type == MENU_LAYOUT_NODE_OR || parser->stack_top->type == MENU_LAYOUT_NODE_NOT) { start_matching_rule_element (parser, context, element_name, attribute_names, attribute_values, error); } else if (parser->stack_top->type == MENU_LAYOUT_NODE_MOVE) { start_move_child_element (parser, context, element_name, attribute_names, attribute_values, error); } else if (parser->stack_top->type == MENU_LAYOUT_NODE_LAYOUT || parser->stack_top->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT) { start_layout_child_element (parser, context, element_name, attribute_names, attribute_values, error); } else { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Element <%s> may not appear in this context\n", element_name); } add_context_to_error (error, context); } /* we want to make sure that the or is either empty, * or contain one of type "all", or contain one of type "menus" * and one of type "files". If this is not the case, we try to clean up * things: * + if there is at least one of type "all", then we only keep the * last of type "all" and remove all others * + if there is no with type "all", we keep only the last of * type "menus" and the last of type "files". If there's no * of type "menus" we append one, and then if there's no of type * "files", we append one. (So menus are before files) */ static gboolean fixup_layout_node (GMarkupParseContext *context, MenuParser *parser, MenuLayoutNode *node, GError **error) { MenuLayoutNode *child; MenuLayoutNode *last_all; MenuLayoutNode *last_menus; MenuLayoutNode *last_files; int n_all; int n_menus; int n_files; if (!node->children) { return TRUE; } last_all = NULL; last_menus = NULL; last_files = NULL; n_all = 0; n_menus = 0; n_files = 0; child = node->children; while (child != NULL) { switch (child->type) { case MENU_LAYOUT_NODE_MERGE: switch (menu_layout_node_merge_get_type (child)) { case MENU_LAYOUT_MERGE_NONE: break; case MENU_LAYOUT_MERGE_MENUS: last_menus = child; n_menus++; break; case MENU_LAYOUT_MERGE_FILES: last_files = child; n_files++; break; case MENU_LAYOUT_MERGE_ALL: last_all = child; n_all++; break; default: g_assert_not_reached (); break; } break; default: break; } child = node_next (child); } if ((n_all == 1 && n_menus == 0 && n_files == 0) || (n_all == 0 && n_menus == 1 && n_files == 1)) { return TRUE; } else if (n_all > 1 || n_menus > 1 || n_files > 1 || (n_all == 1 && (n_menus != 0 || n_files != 0))) { child = node->children; while (child != NULL) { MenuLayoutNode *next; next = node_next (child); switch (child->type) { case MENU_LAYOUT_NODE_MERGE: switch (menu_layout_node_merge_get_type (child)) { case MENU_LAYOUT_MERGE_NONE: break; case MENU_LAYOUT_MERGE_MENUS: if (n_all || last_menus != child) { menu_verbose ("removing duplicated merge menus element\n"); menu_layout_node_unlink (child); } break; case MENU_LAYOUT_MERGE_FILES: if (n_all || last_files != child) { menu_verbose ("removing duplicated merge files element\n"); menu_layout_node_unlink (child); } break; case MENU_LAYOUT_MERGE_ALL: if (last_all != child) { menu_verbose ("removing duplicated merge all element\n"); menu_layout_node_unlink (child); } break; default: g_assert_not_reached (); break; } break; default: break; } child = next; } } if (n_all == 0 && n_menus == 0) { last_menus = menu_layout_node_new (MENU_LAYOUT_NODE_MERGE); menu_layout_node_merge_set_type (last_menus, "menus"); menu_verbose ("appending missing merge menus element\n"); menu_layout_node_append_child (node, last_menus); } if (n_all == 0 && n_files == 0) { last_files = menu_layout_node_new (MENU_LAYOUT_NODE_MERGE); menu_layout_node_merge_set_type (last_files, "files"); menu_verbose ("appending missing merge files element\n"); menu_layout_node_append_child (node, last_files); } return TRUE; } /* we want to a) check that we have old-new pairs and b) canonicalize * such that each has exactly one old-new pair */ static gboolean fixup_move_node (GMarkupParseContext *context, MenuParser *parser, MenuLayoutNode *node, GError **error) { MenuLayoutNode *child; int n_old; int n_new; n_old = 0; n_new = 0; child = node->children; while (child != NULL) { switch (child->type) { case MENU_LAYOUT_NODE_OLD: if (n_new != n_old) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "/ elements not paired properly\n"); return FALSE; } n_old += 1; break; case MENU_LAYOUT_NODE_NEW: n_new += 1; if (n_new != n_old) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "/ elements not paired properly\n"); return FALSE; } break; default: g_assert_not_reached (); break; } child = node_next (child); } if (n_new == 0 || n_old == 0) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "/ elements missing under \n"); return FALSE; } g_assert (n_new == n_old); g_assert ((n_new + n_old) % 2 == 0); if (n_new > 1) { MenuLayoutNode *prev; MenuLayoutNode *append_after; /* Need to split the into multiple */ n_old = 0; n_new = 0; prev = NULL; append_after = node; child = node->children; while (child != NULL) { MenuLayoutNode *next; next = node_next (child); switch (child->type) { case MENU_LAYOUT_NODE_OLD: n_old += 1; break; case MENU_LAYOUT_NODE_NEW: n_new += 1; break; default: g_assert_not_reached (); break; } if (n_old == n_new && n_old > 1) { /* Move the just-completed pair */ MenuLayoutNode *new_move; g_assert (prev != NULL); new_move = menu_layout_node_new (MENU_LAYOUT_NODE_MOVE); menu_verbose ("inserting new_move after append_after\n"); menu_layout_node_insert_after (append_after, new_move); append_after = new_move; menu_layout_node_steal (prev); menu_layout_node_steal (child); menu_verbose ("appending prev to new_move\n"); menu_layout_node_append_child (new_move, prev); menu_verbose ("appending child to new_move\n"); menu_layout_node_append_child (new_move, child); menu_verbose ("Created new move element old = %s new = %s\n", menu_layout_node_move_get_old (new_move), menu_layout_node_move_get_new (new_move)); menu_layout_node_unref (new_move); menu_layout_node_unref (prev); menu_layout_node_unref (child); prev = NULL; } else { prev = child; } prev = child; child = next; } } return TRUE; } static void end_element_handler (GMarkupParseContext *context, const char *element_name, gpointer user_data, GError **error) { MenuParser *parser = user_data; g_assert (parser->stack_top != NULL); switch (parser->stack_top->type) { case MENU_LAYOUT_NODE_APP_DIR: case MENU_LAYOUT_NODE_DIRECTORY_DIR: case MENU_LAYOUT_NODE_NAME: case MENU_LAYOUT_NODE_DIRECTORY: case MENU_LAYOUT_NODE_FILENAME: case MENU_LAYOUT_NODE_CATEGORY: case MENU_LAYOUT_NODE_MERGE_DIR: case MENU_LAYOUT_NODE_LEGACY_DIR: case MENU_LAYOUT_NODE_OLD: case MENU_LAYOUT_NODE_NEW: case MENU_LAYOUT_NODE_MENUNAME: if (menu_layout_node_get_content (parser->stack_top) == NULL) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Element <%s> is required to contain text and was empty\n", element_name); goto out; } break; case MENU_LAYOUT_NODE_MENU: if (!has_child_of_type (parser->stack_top, MENU_LAYOUT_NODE_NAME)) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, " elements are required to contain a element\n"); goto out; } break; case MENU_LAYOUT_NODE_ROOT: case MENU_LAYOUT_NODE_PASSTHROUGH: case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS: case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS: case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS: case MENU_LAYOUT_NODE_ONLY_UNALLOCATED: case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED: case MENU_LAYOUT_NODE_INCLUDE: case MENU_LAYOUT_NODE_EXCLUDE: case MENU_LAYOUT_NODE_ALL: case MENU_LAYOUT_NODE_AND: case MENU_LAYOUT_NODE_OR: case MENU_LAYOUT_NODE_NOT: case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS: case MENU_LAYOUT_NODE_DELETED: case MENU_LAYOUT_NODE_NOT_DELETED: case MENU_LAYOUT_NODE_SEPARATOR: case MENU_LAYOUT_NODE_MERGE: case MENU_LAYOUT_NODE_MERGE_FILE: break; case MENU_LAYOUT_NODE_LAYOUT: case MENU_LAYOUT_NODE_DEFAULT_LAYOUT: if (!fixup_layout_node (context, parser, parser->stack_top, error)) goto out; break; case MENU_LAYOUT_NODE_MOVE: if (!fixup_move_node (context, parser, parser->stack_top, error)) goto out; break; } out: parser->stack_top = parser->stack_top->parent; } static gboolean all_whitespace (const char *text, int text_len) { const char *p; const char *end; p = text; end = text + text_len; while (p != end) { if (!g_ascii_isspace (*p)) return FALSE; p = g_utf8_next_char (p); } return TRUE; } static void text_handler (GMarkupParseContext *context, const char *text, gsize text_len, gpointer user_data, GError **error) { MenuParser *parser = user_data; switch (parser->stack_top->type) { case MENU_LAYOUT_NODE_APP_DIR: case MENU_LAYOUT_NODE_DIRECTORY_DIR: case MENU_LAYOUT_NODE_NAME: case MENU_LAYOUT_NODE_DIRECTORY: case MENU_LAYOUT_NODE_FILENAME: case MENU_LAYOUT_NODE_CATEGORY: case MENU_LAYOUT_NODE_MERGE_FILE: case MENU_LAYOUT_NODE_MERGE_DIR: case MENU_LAYOUT_NODE_LEGACY_DIR: case MENU_LAYOUT_NODE_OLD: case MENU_LAYOUT_NODE_NEW: case MENU_LAYOUT_NODE_MENUNAME: g_assert (menu_layout_node_get_content (parser->stack_top) == NULL); menu_layout_node_set_content (parser->stack_top, text); break; case MENU_LAYOUT_NODE_ROOT: case MENU_LAYOUT_NODE_PASSTHROUGH: case MENU_LAYOUT_NODE_MENU: case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS: case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS: case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS: case MENU_LAYOUT_NODE_ONLY_UNALLOCATED: case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED: case MENU_LAYOUT_NODE_INCLUDE: case MENU_LAYOUT_NODE_EXCLUDE: case MENU_LAYOUT_NODE_ALL: case MENU_LAYOUT_NODE_AND: case MENU_LAYOUT_NODE_OR: case MENU_LAYOUT_NODE_NOT: case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS: case MENU_LAYOUT_NODE_MOVE: case MENU_LAYOUT_NODE_DELETED: case MENU_LAYOUT_NODE_NOT_DELETED: case MENU_LAYOUT_NODE_LAYOUT: case MENU_LAYOUT_NODE_DEFAULT_LAYOUT: case MENU_LAYOUT_NODE_SEPARATOR: case MENU_LAYOUT_NODE_MERGE: if (!all_whitespace (text, text_len)) { set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "No text is allowed inside element <%s>", g_markup_parse_context_get_element (context)); } break; } add_context_to_error (error, context); } static void passthrough_handler (GMarkupParseContext *context, const char *passthrough_text, gsize text_len, gpointer user_data, GError **error) { MenuParser *parser = user_data; MenuLayoutNode *node; /* don't push passthrough on the stack, it's not an element */ node = menu_layout_node_new (MENU_LAYOUT_NODE_PASSTHROUGH); menu_layout_node_set_content (node, passthrough_text); menu_layout_node_append_child (parser->stack_top, node); menu_layout_node_unref (node); add_context_to_error (error, context); } static void menu_parser_init (MenuParser *parser) { parser->root = menu_layout_node_new (MENU_LAYOUT_NODE_ROOT); parser->stack_top = parser->root; } static void menu_parser_free (MenuParser *parser) { if (parser->root) menu_layout_node_unref (parser->root); } MenuLayoutNode * menu_layout_load (const char *filename, const char *non_prefixed_basename, GError **err) { GMainContext *main_context; GMarkupParseContext *context; MenuLayoutNodeRoot *root; MenuLayoutNode *retval; MenuParser parser; GError *error; GString *str; char *text; char *s; gsize length; text = NULL; length = 0; retval = NULL; context = NULL; main_context = g_main_context_get_thread_default (); menu_verbose ("Loading \"%s\" from disk\n", filename); if (!g_file_get_contents (filename, &text, &length, err)) { menu_verbose ("Failed to load \"%s\"\n", filename); return NULL; } g_assert (text != NULL); menu_parser_init (&parser); root = (MenuLayoutNodeRoot *) parser.root; root->basedir = g_path_get_dirname (filename); menu_verbose ("Set basedir \"%s\"\n", root->basedir); if (non_prefixed_basename) s = g_strdup (non_prefixed_basename); else s = g_path_get_basename (filename); str = g_string_new (s); if (g_str_has_suffix (str->str, ".menu")) g_string_truncate (str, str->len - strlen (".menu")); root->name = str->str; menu_verbose ("Set menu name \"%s\"\n", root->name); g_string_free (str, FALSE); g_free (s); context = g_markup_parse_context_new (&menu_funcs, 0, &parser, NULL); error = NULL; if (!g_markup_parse_context_parse (context, text, length, &error)) goto out; error = NULL; g_markup_parse_context_end_parse (context, &error); root->main_context = main_context ? g_main_context_ref (main_context) : NULL; out: if (context) g_markup_parse_context_free (context); g_free (text); if (error) { menu_verbose ("Error \"%s\" loading \"%s\"\n", error->message, filename); g_propagate_error (err, error); } else if (has_child_of_type (parser.root, MENU_LAYOUT_NODE_MENU)) { menu_verbose ("File loaded OK\n"); retval = parser.root; parser.root = NULL; } else { menu_verbose ("Did not have a root element in file\n"); g_set_error (err, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "Menu file %s did not contain a root element", filename); } menu_parser_free (&parser); return retval; } cinnamon-menus-4.4.0/libmenu/menu-layout.h000066400000000000000000000144501356375150600205500ustar00rootroot00000000000000/* Menu layout in-memory data structure (a custom "DOM tree") */ /* * Copyright (C) 2002 - 2004 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __MENU_LAYOUT_H__ #define __MENU_LAYOUT_H__ #include #include "entry-directories.h" G_BEGIN_DECLS typedef struct MenuLayoutNode MenuLayoutNode; typedef enum { MENU_LAYOUT_NODE_ROOT, MENU_LAYOUT_NODE_PASSTHROUGH, MENU_LAYOUT_NODE_MENU, MENU_LAYOUT_NODE_APP_DIR, MENU_LAYOUT_NODE_DEFAULT_APP_DIRS, MENU_LAYOUT_NODE_DIRECTORY_DIR, MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS, MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS, MENU_LAYOUT_NODE_NAME, MENU_LAYOUT_NODE_DIRECTORY, MENU_LAYOUT_NODE_ONLY_UNALLOCATED, MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED, MENU_LAYOUT_NODE_INCLUDE, MENU_LAYOUT_NODE_EXCLUDE, MENU_LAYOUT_NODE_FILENAME, MENU_LAYOUT_NODE_CATEGORY, MENU_LAYOUT_NODE_ALL, MENU_LAYOUT_NODE_AND, MENU_LAYOUT_NODE_OR, MENU_LAYOUT_NODE_NOT, MENU_LAYOUT_NODE_MERGE_FILE, MENU_LAYOUT_NODE_MERGE_DIR, MENU_LAYOUT_NODE_LEGACY_DIR, MENU_LAYOUT_NODE_KDE_LEGACY_DIRS, MENU_LAYOUT_NODE_MOVE, MENU_LAYOUT_NODE_OLD, MENU_LAYOUT_NODE_NEW, MENU_LAYOUT_NODE_DELETED, MENU_LAYOUT_NODE_NOT_DELETED, MENU_LAYOUT_NODE_LAYOUT, MENU_LAYOUT_NODE_DEFAULT_LAYOUT, MENU_LAYOUT_NODE_MENUNAME, MENU_LAYOUT_NODE_SEPARATOR, MENU_LAYOUT_NODE_MERGE } MenuLayoutNodeType; typedef enum { MENU_MERGE_FILE_TYPE_PATH = 0, MENU_MERGE_FILE_TYPE_PARENT } MenuMergeFileType; typedef enum { MENU_LAYOUT_MERGE_NONE, MENU_LAYOUT_MERGE_MENUS, MENU_LAYOUT_MERGE_FILES, MENU_LAYOUT_MERGE_ALL } MenuLayoutMergeType; typedef enum { MENU_LAYOUT_VALUES_NONE = 0, MENU_LAYOUT_VALUES_SHOW_EMPTY = 1 << 0, MENU_LAYOUT_VALUES_INLINE_MENUS = 1 << 1, MENU_LAYOUT_VALUES_INLINE_LIMIT = 1 << 2, MENU_LAYOUT_VALUES_INLINE_HEADER = 1 << 3, MENU_LAYOUT_VALUES_INLINE_ALIAS = 1 << 4 } MenuLayoutValuesMask; typedef struct { MenuLayoutValuesMask mask; guint show_empty : 1; guint inline_menus : 1; guint inline_header : 1; guint inline_alias : 1; guint inline_limit; } MenuLayoutValues; MenuLayoutNode *menu_layout_load (const char *filename, const char *non_prefixed_basename, GError **error); MenuLayoutNode *menu_layout_node_new (MenuLayoutNodeType type); MenuLayoutNode *menu_layout_node_ref (MenuLayoutNode *node); void menu_layout_node_unref (MenuLayoutNode *node); MenuLayoutNodeType menu_layout_node_get_type (MenuLayoutNode *node); MenuLayoutNode *menu_layout_node_get_root (MenuLayoutNode *node); MenuLayoutNode *menu_layout_node_get_parent (MenuLayoutNode *node); MenuLayoutNode *menu_layout_node_get_children (MenuLayoutNode *node); MenuLayoutNode *menu_layout_node_get_next (MenuLayoutNode *node); void menu_layout_node_insert_before (MenuLayoutNode *node, MenuLayoutNode *new_sibling); void menu_layout_node_insert_after (MenuLayoutNode *node, MenuLayoutNode *new_sibling); void menu_layout_node_prepend_child (MenuLayoutNode *parent, MenuLayoutNode *new_child); void menu_layout_node_append_child (MenuLayoutNode *parent, MenuLayoutNode *new_child); void menu_layout_node_unlink (MenuLayoutNode *node); void menu_layout_node_steal (MenuLayoutNode *node); const char *menu_layout_node_get_content (MenuLayoutNode *node); void menu_layout_node_set_content (MenuLayoutNode *node, const char *content); char *menu_layout_node_get_content_as_path (MenuLayoutNode *node); const char *menu_layout_node_root_get_name (MenuLayoutNode *node); const char *menu_layout_node_root_get_basedir (MenuLayoutNode *node); const char *menu_layout_node_menu_get_name (MenuLayoutNode *node); EntryDirectoryList *menu_layout_node_menu_get_app_dirs (MenuLayoutNode *node); EntryDirectoryList *menu_layout_node_menu_get_directory_dirs (MenuLayoutNode *node); const char *menu_layout_node_move_get_old (MenuLayoutNode *node); const char *menu_layout_node_move_get_new (MenuLayoutNode *node); const char *menu_layout_node_legacy_dir_get_prefix (MenuLayoutNode *node); void menu_layout_node_legacy_dir_set_prefix (MenuLayoutNode *node, const char *prefix); MenuMergeFileType menu_layout_node_merge_file_get_type (MenuLayoutNode *node); void menu_layout_node_merge_file_set_type (MenuLayoutNode *node, MenuMergeFileType type); MenuLayoutMergeType menu_layout_node_merge_get_type (MenuLayoutNode *node); void menu_layout_node_default_layout_get_values (MenuLayoutNode *node, MenuLayoutValues *values); void menu_layout_node_menuname_get_values (MenuLayoutNode *node, MenuLayoutValues *values); typedef void (* MenuLayoutNodeEntriesChangedFunc) (MenuLayoutNode *node, gpointer user_data); void menu_layout_node_root_add_entries_monitor (MenuLayoutNode *node, MenuLayoutNodeEntriesChangedFunc callback, gpointer user_data); void menu_layout_node_root_remove_entries_monitor (MenuLayoutNode *node, MenuLayoutNodeEntriesChangedFunc callback, gpointer user_data); G_END_DECLS #endif /* __MENU_LAYOUT_H__ */ cinnamon-menus-4.4.0/libmenu/menu-monitor.c000066400000000000000000000241371356375150600207200ustar00rootroot00000000000000/* * Copyright (C) 2005 Red Hat, Inc. * Copyright (C) 2006 Mark McLoughlin * Copyright (C) 2007 Sebastian Dröge * Copyright (C) 2008 Vincent Untz * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "menu-monitor.h" #include #include "menu-util.h" struct MenuMonitor { char *path; guint refcount; GSList *notifies; GFileMonitor *monitor; guint is_directory : 1; }; typedef struct { MenuMonitor *monitor; MenuMonitorEvent event; char *path; } MenuMonitorEventInfo; typedef struct { MenuMonitorNotifyFunc notify_func; gpointer user_data; guint refcount; } MenuMonitorNotify; static MenuMonitorNotify *menu_monitor_notify_ref (MenuMonitorNotify *notify); static void menu_monitor_notify_unref (MenuMonitorNotify *notify); static GHashTable *monitors_registry = NULL; static guint events_idle_handler = 0; static GSList *pending_events = NULL; static void invoke_notifies (MenuMonitor *monitor, MenuMonitorEvent event, const char *path) { GSList *copy; GSList *tmp; copy = g_slist_copy (monitor->notifies); g_slist_foreach (copy, (GFunc) menu_monitor_notify_ref, NULL); tmp = copy; while (tmp != NULL) { MenuMonitorNotify *notify = tmp->data; GSList *next = tmp->next; if (notify->notify_func) { notify->notify_func (monitor, event, path, notify->user_data); } menu_monitor_notify_unref (notify); tmp = next; } g_slist_free (copy); } static gboolean emit_events_in_idle (void) { GSList *events_to_emit; GSList *tmp; events_to_emit = pending_events; pending_events = NULL; events_idle_handler = 0; tmp = events_to_emit; while (tmp != NULL) { MenuMonitorEventInfo *event_info = tmp->data; menu_monitor_ref (event_info->monitor); tmp = tmp->next; } tmp = events_to_emit; while (tmp != NULL) { MenuMonitorEventInfo *event_info = tmp->data; invoke_notifies (event_info->monitor, event_info->event, event_info->path); menu_monitor_unref (event_info->monitor); event_info->monitor = NULL; g_free (event_info->path); event_info->path = NULL; event_info->event = MENU_MONITOR_EVENT_INVALID; g_free (event_info); tmp = tmp->next; } g_slist_free (events_to_emit); return FALSE; } static void menu_monitor_queue_event (MenuMonitorEventInfo *event_info) { pending_events = g_slist_append (pending_events, event_info); if (events_idle_handler > 0) { g_source_remove (events_idle_handler); } events_idle_handler = g_timeout_add (100, (GSourceFunc) emit_events_in_idle, NULL); } static inline char * get_registry_key (const char *path, gboolean is_directory) { return g_strdup_printf ("%s:%s", path, is_directory ? "" : ""); } static gboolean monitor_callback (GFileMonitor *monitor, GFile *child, GFile *other_file, GFileMonitorEvent eflags, gpointer user_data) { MenuMonitorEventInfo *event_info; MenuMonitorEvent event; MenuMonitor *menu_monitor = (MenuMonitor *) user_data; event = MENU_MONITOR_EVENT_INVALID; switch (eflags) { case G_FILE_MONITOR_EVENT_CHANGED: event = MENU_MONITOR_EVENT_CHANGED; break; case G_FILE_MONITOR_EVENT_CREATED: event = MENU_MONITOR_EVENT_CREATED; break; case G_FILE_MONITOR_EVENT_DELETED: event = MENU_MONITOR_EVENT_DELETED; break; default: return TRUE; } event_info = g_new0 (MenuMonitorEventInfo, 1); event_info->path = g_file_get_path (child); event_info->event = event; event_info->monitor = menu_monitor; menu_monitor_queue_event (event_info); return TRUE; } static MenuMonitor * register_monitor (const char *path, gboolean is_directory) { MenuMonitor *retval; GFile *file; retval = g_new0 (MenuMonitor, 1); retval->path = g_strdup (path); retval->refcount = 1; retval->is_directory = is_directory != FALSE; file = g_file_new_for_path (retval->path); if (file == NULL) { menu_verbose ("Not adding monitor on '%s', failed to create GFile\n", retval->path); return retval; } if (retval->is_directory) retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL); else retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL); g_object_unref (G_OBJECT (file)); if (retval->monitor == NULL) { menu_verbose ("Not adding monitor on '%s', failed to create monitor\n", retval->path); return retval; } g_signal_connect (retval->monitor, "changed", G_CALLBACK (monitor_callback), retval); return retval; } static MenuMonitor * lookup_monitor (const char *path, gboolean is_directory) { MenuMonitor *retval; char *registry_key; retval = NULL; registry_key = get_registry_key (path, is_directory); if (monitors_registry == NULL) { monitors_registry = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } else { retval = g_hash_table_lookup (monitors_registry, registry_key); } if (retval == NULL) { retval = register_monitor (path, is_directory); g_hash_table_insert (monitors_registry, registry_key, retval); return retval; } else { g_free (registry_key); return menu_monitor_ref (retval); } } MenuMonitor * menu_get_file_monitor (const char *path) { g_return_val_if_fail (path != NULL, NULL); return lookup_monitor (path, FALSE); } MenuMonitor * menu_get_directory_monitor (const char *path) { g_return_val_if_fail (path != NULL, NULL); return lookup_monitor (path, TRUE); } MenuMonitor * menu_monitor_ref (MenuMonitor *monitor) { g_return_val_if_fail (monitor != NULL, NULL); g_return_val_if_fail (monitor->refcount > 0, NULL); monitor->refcount++; return monitor; } static void menu_monitor_clear_pending_events (MenuMonitor *monitor) { GSList *tmp; tmp = pending_events; while (tmp != NULL) { MenuMonitorEventInfo *event_info = tmp->data; GSList *next = tmp->next; if (event_info->monitor == monitor) { pending_events = g_slist_delete_link (pending_events, tmp); g_free (event_info->path); event_info->path = NULL; event_info->monitor = NULL; event_info->event = MENU_MONITOR_EVENT_INVALID; g_free (event_info); } tmp = next; } } void menu_monitor_unref (MenuMonitor *monitor) { char *registry_key; g_return_if_fail (monitor != NULL); g_return_if_fail (monitor->refcount > 0); if (--monitor->refcount > 0) return; registry_key = get_registry_key (monitor->path, monitor->is_directory); g_hash_table_remove (monitors_registry, registry_key); g_free (registry_key); if (g_hash_table_size (monitors_registry) == 0) { g_hash_table_destroy (monitors_registry); monitors_registry = NULL; } if (monitor->monitor) { g_file_monitor_cancel (monitor->monitor); g_object_unref (monitor->monitor); monitor->monitor = NULL; } g_slist_foreach (monitor->notifies, (GFunc) menu_monitor_notify_unref, NULL); g_slist_free (monitor->notifies); monitor->notifies = NULL; menu_monitor_clear_pending_events (monitor); g_free (monitor->path); monitor->path = NULL; g_free (monitor); } static MenuMonitorNotify * menu_monitor_notify_ref (MenuMonitorNotify *notify) { g_return_val_if_fail (notify != NULL, NULL); g_return_val_if_fail (notify->refcount > 0, NULL); notify->refcount++; return notify; } static void menu_monitor_notify_unref (MenuMonitorNotify *notify) { g_return_if_fail (notify != NULL); g_return_if_fail (notify->refcount > 0); if (--notify->refcount > 0) return; g_free (notify); } void menu_monitor_add_notify (MenuMonitor *monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data) { MenuMonitorNotify *notify; GSList *tmp; g_return_if_fail (monitor != NULL); g_return_if_fail (notify_func != NULL); tmp = monitor->notifies; while (tmp != NULL) { notify = tmp->data; if (notify->notify_func == notify_func && notify->user_data == user_data) break; tmp = tmp->next; } if (tmp == NULL) { notify = g_new0 (MenuMonitorNotify, 1); notify->notify_func = notify_func; notify->user_data = user_data; notify->refcount = 1; monitor->notifies = g_slist_append (monitor->notifies, notify); } } void menu_monitor_remove_notify (MenuMonitor *monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data) { GSList *tmp; tmp = monitor->notifies; while (tmp != NULL) { MenuMonitorNotify *notify = tmp->data; GSList *next = tmp->next; if (notify->notify_func == notify_func && notify->user_data == user_data) { notify->notify_func = NULL; notify->user_data = NULL; menu_monitor_notify_unref (notify); monitor->notifies = g_slist_delete_link (monitor->notifies, tmp); } tmp = next; } } cinnamon-menus-4.4.0/libmenu/menu-monitor.h000066400000000000000000000034561356375150600207260ustar00rootroot00000000000000/* * Copyright (C) 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __MENU_MONITOR_H__ #define __MENU_MONITOR_H__ #include G_BEGIN_DECLS typedef struct MenuMonitor MenuMonitor; typedef enum { MENU_MONITOR_EVENT_INVALID = 0, MENU_MONITOR_EVENT_CREATED = 1, MENU_MONITOR_EVENT_DELETED = 2, MENU_MONITOR_EVENT_CHANGED = 3 } MenuMonitorEvent; typedef void (*MenuMonitorNotifyFunc) (MenuMonitor *monitor, MenuMonitorEvent event, const char *path, gpointer user_data); MenuMonitor *menu_get_file_monitor (const char *path); MenuMonitor *menu_get_directory_monitor (const char *path); MenuMonitor *menu_monitor_ref (MenuMonitor *monitor); void menu_monitor_unref (MenuMonitor *monitor); void menu_monitor_add_notify (MenuMonitor *monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data); void menu_monitor_remove_notify (MenuMonitor *monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data); G_END_DECLS #endif /* __MENU_MONITOR_H__ */ cinnamon-menus-4.4.0/libmenu/menu-util.c000066400000000000000000000300051356375150600201750ustar00rootroot00000000000000/* Random utility functions for menu code */ /* * Copyright (C) 2003 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "menu-util.h" #include #include #ifdef G_ENABLE_DEBUG static gboolean verbose = FALSE; static gboolean initted = FALSE; static inline gboolean menu_verbose_enabled (void) { if (!initted) { verbose = g_getenv ("MENU_VERBOSE") != NULL; initted = TRUE; } return verbose; } static int utf8_fputs (const char *str, FILE *f) { char *l; int ret; l = g_locale_from_utf8 (str, -1, NULL, NULL, NULL); if (l == NULL) ret = fputs (str, f); /* just print it anyway, better than nothing */ else ret = fputs (l, f); g_free (l); return ret; } void menu_verbose (const char *format, ...) { va_list args; char *str; if (!menu_verbose_enabled ()) return; va_start (args, format); str = g_strdup_vprintf (format, args); va_end (args); utf8_fputs (str, stderr); fflush (stderr); g_free (str); } static void append_to_string (MenuLayoutNode *node, gboolean onelevel, int depth, GString *str); static void append_spaces (GString *str, int depth) { while (depth > 0) { g_string_append_c (str, ' '); --depth; } } static void append_children (MenuLayoutNode *node, int depth, GString *str) { MenuLayoutNode *iter; iter = menu_layout_node_get_children (node); while (iter != NULL) { append_to_string (iter, FALSE, depth, str); iter = menu_layout_node_get_next (iter); } } static void append_simple_with_attr (MenuLayoutNode *node, int depth, const char *node_name, const char *attr_name, const char *attr_value, GString *str) { const char *content; append_spaces (str, depth); if ((content = menu_layout_node_get_content (node))) { char *escaped; escaped = g_markup_escape_text (content, -1); if (attr_name && attr_value) { char *attr_escaped; attr_escaped = g_markup_escape_text (attr_value, -1); g_string_append_printf (str, "<%s %s=\"%s\">%s\n", node_name, attr_name, attr_escaped, escaped, node_name); g_free (attr_escaped); } else { g_string_append_printf (str, "<%s>%s\n", node_name, escaped, node_name); } g_free (escaped); } else { if (attr_name && attr_value) { char *attr_escaped; attr_escaped = g_markup_escape_text (attr_value, -1); g_string_append_printf (str, "<%s %s=\"%s\"/>\n", node_name, attr_name, attr_escaped); g_free (attr_escaped); } else { g_string_append_printf (str, "<%s/>\n", node_name); } } } static void append_layout (MenuLayoutNode *node, int depth, const char *node_name, MenuLayoutValues *layout_values, GString *str) { const char *content; append_spaces (str, depth); if ((content = menu_layout_node_get_content (node))) { char *escaped; escaped = g_markup_escape_text (content, -1); g_string_append_printf (str, "<%s show_empty=\"%s\" inline=\"%s\" inline_header=\"%s\"" " inline_alias=\"%s\" inline_limit=\"%d\">%s\n", node_name, layout_values->show_empty ? "true" : "false", layout_values->inline_menus ? "true" : "false", layout_values->inline_header ? "true" : "false", layout_values->inline_alias ? "true" : "false", layout_values->inline_limit, escaped, node_name); g_free (escaped); } else { g_string_append_printf (str, "<%s show_empty=\"%s\" inline=\"%s\" inline_header=\"%s\"" " inline_alias=\"%s\" inline_limit=\"%d\"/>\n", node_name, layout_values->show_empty ? "true" : "false", layout_values->inline_menus ? "true" : "false", layout_values->inline_header ? "true" : "false", layout_values->inline_alias ? "true" : "false", layout_values->inline_limit); } } static void append_merge (MenuLayoutNode *node, int depth, const char *node_name, MenuLayoutMergeType merge_type, GString *str) { const char *merge_type_str; merge_type_str = NULL; switch (merge_type) { case MENU_LAYOUT_MERGE_NONE: merge_type_str = "none"; break; case MENU_LAYOUT_MERGE_MENUS: merge_type_str = "menus"; break; case MENU_LAYOUT_MERGE_FILES: merge_type_str = "files"; break; case MENU_LAYOUT_MERGE_ALL: merge_type_str = "all"; break; default: g_assert_not_reached (); break; } append_simple_with_attr (node, depth, node_name, "type", merge_type_str, str); } static void append_simple (MenuLayoutNode *node, int depth, const char *node_name, GString *str) { append_simple_with_attr (node, depth, node_name, NULL, NULL, str); } static void append_start (MenuLayoutNode *node, int depth, const char *node_name, GString *str) { append_spaces (str, depth); g_string_append_printf (str, "<%s>\n", node_name); } static void append_end (MenuLayoutNode *node, int depth, const char *node_name, GString *str) { append_spaces (str, depth); g_string_append_printf (str, "\n", node_name); } static void append_container (MenuLayoutNode *node, gboolean onelevel, int depth, const char *node_name, GString *str) { append_start (node, depth, node_name, str); if (!onelevel) { append_children (node, depth + 2, str); append_end (node, depth, node_name, str); } } static void append_to_string (MenuLayoutNode *node, gboolean onelevel, int depth, GString *str) { MenuLayoutValues layout_values; switch (menu_layout_node_get_type (node)) { case MENU_LAYOUT_NODE_ROOT: if (!onelevel) append_children (node, depth - 1, str); /* -1 to ignore depth of root */ else append_start (node, depth - 1, "Root", str); break; case MENU_LAYOUT_NODE_PASSTHROUGH: g_string_append (str, menu_layout_node_get_content (node)); g_string_append_c (str, '\n'); break; case MENU_LAYOUT_NODE_MENU: append_container (node, onelevel, depth, "Menu", str); break; case MENU_LAYOUT_NODE_APP_DIR: append_simple (node, depth, "AppDir", str); break; case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS: append_simple (node, depth, "DefaultAppDirs", str); break; case MENU_LAYOUT_NODE_DIRECTORY_DIR: append_simple (node, depth, "DirectoryDir", str); break; case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS: append_simple (node, depth, "DefaultDirectoryDirs", str); break; case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS: append_simple (node, depth, "DefaultMergeDirs", str); break; case MENU_LAYOUT_NODE_NAME: append_simple (node, depth, "Name", str); break; case MENU_LAYOUT_NODE_DIRECTORY: append_simple (node, depth, "Directory", str); break; case MENU_LAYOUT_NODE_ONLY_UNALLOCATED: append_simple (node, depth, "OnlyUnallocated", str); break; case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED: append_simple (node, depth, "NotOnlyUnallocated", str); break; case MENU_LAYOUT_NODE_INCLUDE: append_container (node, onelevel, depth, "Include", str); break; case MENU_LAYOUT_NODE_EXCLUDE: append_container (node, onelevel, depth, "Exclude", str); break; case MENU_LAYOUT_NODE_FILENAME: append_simple (node, depth, "Filename", str); break; case MENU_LAYOUT_NODE_CATEGORY: append_simple (node, depth, "Category", str); break; case MENU_LAYOUT_NODE_ALL: append_simple (node, depth, "All", str); break; case MENU_LAYOUT_NODE_AND: append_container (node, onelevel, depth, "And", str); break; case MENU_LAYOUT_NODE_OR: append_container (node, onelevel, depth, "Or", str); break; case MENU_LAYOUT_NODE_NOT: append_container (node, onelevel, depth, "Not", str); break; case MENU_LAYOUT_NODE_MERGE_FILE: { MenuMergeFileType type; type = menu_layout_node_merge_file_get_type (node); append_simple_with_attr (node, depth, "MergeFile", "type", type == MENU_MERGE_FILE_TYPE_PARENT ? "parent" : "path", str); break; } case MENU_LAYOUT_NODE_MERGE_DIR: append_simple (node, depth, "MergeDir", str); break; case MENU_LAYOUT_NODE_LEGACY_DIR: append_simple_with_attr (node, depth, "LegacyDir", "prefix", menu_layout_node_legacy_dir_get_prefix (node), str); break; case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS: append_simple (node, depth, "KDELegacyDirs", str); break; case MENU_LAYOUT_NODE_MOVE: append_container (node, onelevel, depth, "Move", str); break; case MENU_LAYOUT_NODE_OLD: append_simple (node, depth, "Old", str); break; case MENU_LAYOUT_NODE_NEW: append_simple (node, depth, "New", str); break; case MENU_LAYOUT_NODE_DELETED: append_simple (node, depth, "Deleted", str); break; case MENU_LAYOUT_NODE_NOT_DELETED: append_simple (node, depth, "NotDeleted", str); break; case MENU_LAYOUT_NODE_LAYOUT: append_container (node, onelevel, depth, "Layout", str); break; case MENU_LAYOUT_NODE_DEFAULT_LAYOUT: menu_layout_node_default_layout_get_values (node, &layout_values); append_layout (node, depth, "DefaultLayout", &layout_values, str); break; case MENU_LAYOUT_NODE_MENUNAME: menu_layout_node_menuname_get_values (node, &layout_values); append_layout (node, depth, "MenuName", &layout_values, str); break; case MENU_LAYOUT_NODE_SEPARATOR: append_simple (node, depth, "Name", str); break; case MENU_LAYOUT_NODE_MERGE: append_merge (node, depth, "Merge", menu_layout_node_merge_get_type (node), str); break; default: g_assert_not_reached (); break; } } void menu_debug_print_layout (MenuLayoutNode *node, gboolean onelevel) { if (menu_verbose_enabled ()) { GString *str; str = g_string_new (NULL); append_to_string (node, onelevel, 0, str); utf8_fputs (str->str, stderr); fflush (stderr); g_string_free (str, TRUE); } } #endif /* G_ENABLE_DEBUG */ cinnamon-menus-4.4.0/libmenu/menu-util.h000066400000000000000000000027171356375150600202130ustar00rootroot00000000000000/* Random utility functions for menu code */ /* * Copyright (C) 2003 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __MENU_UTIL_H__ #define __MENU_UTIL_H__ #include #include "menu-layout.h" G_BEGIN_DECLS #ifdef G_ENABLE_DEBUG void menu_verbose (const char *format, ...) G_GNUC_PRINTF (1, 2); void menu_debug_print_layout (MenuLayoutNode *node, gboolean onelevel); #else /* !defined(G_ENABLE_DEBUG) */ #ifdef G_HAVE_ISO_VARARGS #define menu_verbose(...) #elif defined(G_HAVE_GNUC_VARARGS) #define menu_verbose(format...) #else #error "Cannot disable verbose mode due to lack of varargs macros" #endif #define menu_debug_print_layout(n,o) #endif /* G_ENABLE_DEBUG */ G_END_DECLS #endif /* __MENU_UTIL_H__ */ cinnamon-menus-4.4.0/libmenu/meson.build000066400000000000000000000032541356375150600202620ustar00rootroot00000000000000public_headers = [ 'gmenu-tree.h', ] public_sources = [ 'gmenu-tree.c', public_headers, ] libmenu_private_headers = [ 'desktop-entries.h', 'entry-directories.h', 'menu-layout.h', 'menu-monitor.h', 'menu-util.h', ] libmenu_sources = [ 'desktop-entries.c', 'entry-directories.c', 'menu-layout.c', 'menu-monitor.c', 'menu-util.c', public_sources, libmenu_private_headers, ] libmenu_deps = [ gio, config_h, ] libcinnamon_menus = library( 'cinnamon-menu-3', libmenu_sources, soversion: binary_major_version, version: binary_version, include_directories: include_root, dependencies: libmenu_deps, install: true, build_by_default: false, ) cmenu_dep = declare_dependency( include_directories: include_directories('.'), link_with: libcinnamon_menus, dependencies: libmenu_deps, link_args: ['-Wl,-Bsymbolic', '-Wl,-z,relro', '-Wl,-z,now'], ) install_headers( public_headers, subdir: 'cinnamon-menus-3.0' ) pkgconfig = import('pkgconfig') # meson 0.46.0 can drop the version keyword and move libraries to a # positional argument pkgconfig.generate( name: 'libcinnamon-menu-3.0', description: 'Desktop Menu Specification Implementation', version: version, libraries: libcinnamon_menus, subdirs: 'cinnamon-menus-3.0' ) gnome.generate_gir( libcinnamon_menus, namespace: 'CMenu', nsversion: '3.0', sources: public_sources, identifier_prefix: 'GMenu', symbol_prefix: 'gmenu', includes: 'Gio-2.0', header: 'gmenu-tree.h', install: true, install_dir_gir: join_paths(datadir, 'gir-1.0'), export_packages: 'libcinnamon-menu-3.0', ) cinnamon-menus-4.4.0/meson.build000066400000000000000000000023501356375150600166230ustar00rootroot00000000000000project('cinnamon-menus', 'c', version : '4.4.0', meson_version: '>=0.40.0') gnome = import('gnome') version = meson.project_version() binary_version = '0.0.1' binary_major_version = binary_version.split('.')[0] cmenu_conf = configuration_data() cmenu_conf.set_quoted('PACKAGE', meson.project_name()) # directories prefix = get_option('prefix') datadir = get_option('datadir') libdir = get_option('libdir') includedir = get_option('includedir') # generate config.h config_h_file = configure_file( output : 'config.h', configuration : cmenu_conf ) config_h = declare_dependency( sources: config_h_file ) include_root = include_directories('.') c_args = [ '-DGMENU_I_KNOW_THIS_IS_UNSTABLE', ] if get_option('enable_debug') c_args += '-DG_ENABLE_DEBUG' else c_args += '-DG_DISABLE_ASSERT' c_args += '-DG_DISABLE_CHECKS' c_args += '-DG_DISABLE_CAST_CHECKS' endif if not get_option('deprecated_warnings') c_args += '-Wno-deprecated-declarations' c_args += '-Wno-deprecated' c_args += '-Wno-declaration-after-statement' endif add_global_arguments(c_args, language: 'c') gio = dependency('gio-unix-2.0', version: '>= 2.29.15') subdir('libmenu') if get_option('enable_docs') subdir('docs/reference') endif cinnamon-menus-4.4.0/meson_options.txt000066400000000000000000000005151356375150600201170ustar00rootroot00000000000000option('deprecated_warnings', type: 'boolean', value: true, description: 'Show build warnings for deprecations' ) option('enable_debug', type: 'boolean', value: true, description: 'Enable debugging' ) option('enable_docs', type: 'boolean', value: false, description: 'Build the API references (requires gtk-doc)' )