Codebase list cinnamon-menus / 69a46d2
New upstream version 3.4.0 Maximiliano Curia 6 years ago
30 changed file(s) with 19609 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 Mark McLoughlin <mark@skynet.ie>
1 Havoc Pennington <hp@redhat.com>
2 Vincent Untz <vuntz@gnome.org>
0 GNU GENERAL PUBLIC LICENSE
1 Version 2, June 1991
2
3 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
4 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
5 Everyone is permitted to copy and distribute verbatim copies
6 of this license document, but changing it is not allowed.
7
8 Preamble
9
10 The licenses for most software are designed to take away your
11 freedom to share and change it. By contrast, the GNU General Public
12 License is intended to guarantee your freedom to share and change free
13 software--to make sure the software is free for all its users. This
14 General Public License applies to most of the Free Software
15 Foundation's software and to any other program whose authors commit to
16 using it. (Some other Free Software Foundation software is covered by
17 the GNU Lesser General Public License instead.) You can apply it to
18 your programs, too.
19
20 When we speak of free software, we are referring to freedom, not
21 price. Our General Public Licenses are designed to make sure that you
22 have the freedom to distribute copies of free software (and charge for
23 this service if you wish), that you receive source code or can get it
24 if you want it, that you can change the software or use pieces of it
25 in new free programs; and that you know you can do these things.
26
27 To protect your rights, we need to make restrictions that forbid
28 anyone to deny you these rights or to ask you to surrender the rights.
29 These restrictions translate to certain responsibilities for you if you
30 distribute copies of the software, or if you modify it.
31
32 For example, if you distribute copies of such a program, whether
33 gratis or for a fee, you must give the recipients all the rights that
34 you have. You must make sure that they, too, receive or can get the
35 source code. And you must show them these terms so they know their
36 rights.
37
38 We protect your rights with two steps: (1) copyright the software, and
39 (2) offer you this license which gives you legal permission to copy,
40 distribute and/or modify the software.
41
42 Also, for each author's protection and ours, we want to make certain
43 that everyone understands that there is no warranty for this free
44 software. If the software is modified by someone else and passed on, we
45 want its recipients to know that what they have is not the original, so
46 that any problems introduced by others will not reflect on the original
47 authors' reputations.
48
49 Finally, any free program is threatened constantly by software
50 patents. We wish to avoid the danger that redistributors of a free
51 program will individually obtain patent licenses, in effect making the
52 program proprietary. To prevent this, we have made it clear that any
53 patent must be licensed for everyone's free use or not licensed at all.
54
55 The precise terms and conditions for copying, distribution and
56 modification follow.
57
58 GNU GENERAL PUBLIC LICENSE
59 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
60
61 0. This License applies to any program or other work which contains
62 a notice placed by the copyright holder saying it may be distributed
63 under the terms of this General Public License. The "Program", below,
64 refers to any such program or work, and a "work based on the Program"
65 means either the Program or any derivative work under copyright law:
66 that is to say, a work containing the Program or a portion of it,
67 either verbatim or with modifications and/or translated into another
68 language. (Hereinafter, translation is included without limitation in
69 the term "modification".) Each licensee is addressed as "you".
70
71 Activities other than copying, distribution and modification are not
72 covered by this License; they are outside its scope. The act of
73 running the Program is not restricted, and the output from the Program
74 is covered only if its contents constitute a work based on the
75 Program (independent of having been made by running the Program).
76 Whether that is true depends on what the Program does.
77
78 1. You may copy and distribute verbatim copies of the Program's
79 source code as you receive it, in any medium, provided that you
80 conspicuously and appropriately publish on each copy an appropriate
81 copyright notice and disclaimer of warranty; keep intact all the
82 notices that refer to this License and to the absence of any warranty;
83 and give any other recipients of the Program a copy of this License
84 along with the Program.
85
86 You may charge a fee for the physical act of transferring a copy, and
87 you may at your option offer warranty protection in exchange for a fee.
88
89 2. You may modify your copy or copies of the Program or any portion
90 of it, thus forming a work based on the Program, and copy and
91 distribute such modifications or work under the terms of Section 1
92 above, provided that you also meet all of these conditions:
93
94 a) You must cause the modified files to carry prominent notices
95 stating that you changed the files and the date of any change.
96
97 b) You must cause any work that you distribute or publish, that in
98 whole or in part contains or is derived from the Program or any
99 part thereof, to be licensed as a whole at no charge to all third
100 parties under the terms of this License.
101
102 c) If the modified program normally reads commands interactively
103 when run, you must cause it, when started running for such
104 interactive use in the most ordinary way, to print or display an
105 announcement including an appropriate copyright notice and a
106 notice that there is no warranty (or else, saying that you provide
107 a warranty) and that users may redistribute the program under
108 these conditions, and telling the user how to view a copy of this
109 License. (Exception: if the Program itself is interactive but
110 does not normally print such an announcement, your work based on
111 the Program is not required to print an announcement.)
112
113 These requirements apply to the modified work as a whole. If
114 identifiable sections of that work are not derived from the Program,
115 and can be reasonably considered independent and separate works in
116 themselves, then this License, and its terms, do not apply to those
117 sections when you distribute them as separate works. But when you
118 distribute the same sections as part of a whole which is a work based
119 on the Program, the distribution of the whole must be on the terms of
120 this License, whose permissions for other licensees extend to the
121 entire whole, and thus to each and every part regardless of who wrote it.
122
123 Thus, it is not the intent of this section to claim rights or contest
124 your rights to work written entirely by you; rather, the intent is to
125 exercise the right to control the distribution of derivative or
126 collective works based on the Program.
127
128 In addition, mere aggregation of another work not based on the Program
129 with the Program (or with a work based on the Program) on a volume of
130 a storage or distribution medium does not bring the other work under
131 the scope of this License.
132
133 3. You may copy and distribute the Program (or a work based on it,
134 under Section 2) in object code or executable form under the terms of
135 Sections 1 and 2 above provided that you also do one of the following:
136
137 a) Accompany it with the complete corresponding machine-readable
138 source code, which must be distributed under the terms of Sections
139 1 and 2 above on a medium customarily used for software interchange; or,
140
141 b) Accompany it with a written offer, valid for at least three
142 years, to give any third party, for a charge no more than your
143 cost of physically performing source distribution, a complete
144 machine-readable copy of the corresponding source code, to be
145 distributed under the terms of Sections 1 and 2 above on a medium
146 customarily used for software interchange; or,
147
148 c) Accompany it with the information you received as to the offer
149 to distribute corresponding source code. (This alternative is
150 allowed only for noncommercial distribution and only if you
151 received the program in object code or executable form with such
152 an offer, in accord with Subsection b above.)
153
154 The source code for a work means the preferred form of the work for
155 making modifications to it. For an executable work, complete source
156 code means all the source code for all modules it contains, plus any
157 associated interface definition files, plus the scripts used to
158 control compilation and installation of the executable. However, as a
159 special exception, the source code distributed need not include
160 anything that is normally distributed (in either source or binary
161 form) with the major components (compiler, kernel, and so on) of the
162 operating system on which the executable runs, unless that component
163 itself accompanies the executable.
164
165 If distribution of executable or object code is made by offering
166 access to copy from a designated place, then offering equivalent
167 access to copy the source code from the same place counts as
168 distribution of the source code, even though third parties are not
169 compelled to copy the source along with the object code.
170
171 4. You may not copy, modify, sublicense, or distribute the Program
172 except as expressly provided under this License. Any attempt
173 otherwise to copy, modify, sublicense or distribute the Program is
174 void, and will automatically terminate your rights under this License.
175 However, parties who have received copies, or rights, from you under
176 this License will not have their licenses terminated so long as such
177 parties remain in full compliance.
178
179 5. You are not required to accept this License, since you have not
180 signed it. However, nothing else grants you permission to modify or
181 distribute the Program or its derivative works. These actions are
182 prohibited by law if you do not accept this License. Therefore, by
183 modifying or distributing the Program (or any work based on the
184 Program), you indicate your acceptance of this License to do so, and
185 all its terms and conditions for copying, distributing or modifying
186 the Program or works based on it.
187
188 6. Each time you redistribute the Program (or any work based on the
189 Program), the recipient automatically receives a license from the
190 original licensor to copy, distribute or modify the Program subject to
191 these terms and conditions. You may not impose any further
192 restrictions on the recipients' exercise of the rights granted herein.
193 You are not responsible for enforcing compliance by third parties to
194 this License.
195
196 7. If, as a consequence of a court judgment or allegation of patent
197 infringement or for any other reason (not limited to patent issues),
198 conditions are imposed on you (whether by court order, agreement or
199 otherwise) that contradict the conditions of this License, they do not
200 excuse you from the conditions of this License. If you cannot
201 distribute so as to satisfy simultaneously your obligations under this
202 License and any other pertinent obligations, then as a consequence you
203 may not distribute the Program at all. For example, if a patent
204 license would not permit royalty-free redistribution of the Program by
205 all those who receive copies directly or indirectly through you, then
206 the only way you could satisfy both it and this License would be to
207 refrain entirely from distribution of the Program.
208
209 If any portion of this section is held invalid or unenforceable under
210 any particular circumstance, the balance of the section is intended to
211 apply and the section as a whole is intended to apply in other
212 circumstances.
213
214 It is not the purpose of this section to induce you to infringe any
215 patents or other property right claims or to contest validity of any
216 such claims; this section has the sole purpose of protecting the
217 integrity of the free software distribution system, which is
218 implemented by public license practices. Many people have made
219 generous contributions to the wide range of software distributed
220 through that system in reliance on consistent application of that
221 system; it is up to the author/donor to decide if he or she is willing
222 to distribute software through any other system and a licensee cannot
223 impose that choice.
224
225 This section is intended to make thoroughly clear what is believed to
226 be a consequence of the rest of this License.
227
228 8. If the distribution and/or use of the Program is restricted in
229 certain countries either by patents or by copyrighted interfaces, the
230 original copyright holder who places the Program under this License
231 may add an explicit geographical distribution limitation excluding
232 those countries, so that distribution is permitted only in or among
233 countries not thus excluded. In such case, this License incorporates
234 the limitation as if written in the body of this License.
235
236 9. The Free Software Foundation may publish revised and/or new versions
237 of the General Public License from time to time. Such new versions will
238 be similar in spirit to the present version, but may differ in detail to
239 address new problems or concerns.
240
241 Each version is given a distinguishing version number. If the Program
242 specifies a version number of this License which applies to it and "any
243 later version", you have the option of following the terms and conditions
244 either of that version or of any later version published by the Free
245 Software Foundation. If the Program does not specify a version number of
246 this License, you may choose any version ever published by the Free Software
247 Foundation.
248
249 10. If you wish to incorporate parts of the Program into other free
250 programs whose distribution conditions are different, write to the author
251 to ask for permission. For software which is copyrighted by the Free
252 Software Foundation, write to the Free Software Foundation; we sometimes
253 make exceptions for this. Our decision will be guided by the two goals
254 of preserving the free status of all derivatives of our free software and
255 of promoting the sharing and reuse of software generally.
256
257 NO WARRANTY
258
259 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
260 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
261 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
262 PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
263 OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
264 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
265 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
266 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
267 REPAIR OR CORRECTION.
268
269 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
270 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
271 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
272 INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
273 OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
274 TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
275 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
276 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
277 POSSIBILITY OF SUCH DAMAGES.
278
279 END OF TERMS AND CONDITIONS
280
281 How to Apply These Terms to Your New Programs
282
283 If you develop a new program, and you want it to be of the greatest
284 possible use to the public, the best way to achieve this is to make it
285 free software which everyone can redistribute and change under these terms.
286
287 To do so, attach the following notices to the program. It is safest
288 to attach them to the start of each source file to most effectively
289 convey the exclusion of warranty; and each file should have at least
290 the "copyright" line and a pointer to where the full notice is found.
291
292 <one line to give the program's name and a brief idea of what it does.>
293 Copyright (C) <year> <name of author>
294
295 This program is free software; you can redistribute it and/or modify
296 it under the terms of the GNU General Public License as published by
297 the Free Software Foundation; either version 2 of the License, or
298 (at your option) any later version.
299
300 This program is distributed in the hope that it will be useful,
301 but WITHOUT ANY WARRANTY; without even the implied warranty of
302 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
303 GNU General Public License for more details.
304
305 You should have received a copy of the GNU General Public License along
306 with this program; if not, write to the Free Software Foundation, Inc.,
307 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
308
309 Also add information on how to contact you by electronic and paper mail.
310
311 If the program is interactive, make it output a short notice like this
312 when it starts in an interactive mode:
313
314 Gnomovision version 69, Copyright (C) year name of author
315 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
316 This is free software, and you are welcome to redistribute it
317 under certain conditions; type `show c' for details.
318
319 The hypothetical commands `show w' and `show c' should show the appropriate
320 parts of the General Public License. Of course, the commands you use may
321 be called something other than `show w' and `show c'; they could even be
322 mouse-clicks or menu items--whatever suits your program.
323
324 You should also get your employer (if you work as a programmer) or your
325 school, if any, to sign a "copyright disclaimer" for the program, if
326 necessary. Here is a sample; alter the names:
327
328 Yoyodyne, Inc., hereby disclaims all copyright interest in the program
329 `Gnomovision' (which makes passes at compilers) written by James Hacker.
330
331 <signature of Ty Coon>, 1 April 1989
332 Ty Coon, President of Vice
333
334 This General Public License does not permit incorporating your program into
335 proprietary programs. If your program is a subroutine library, you may
336 consider it more useful to permit linking proprietary applications with the
337 library. If this is what you want to do, use the GNU Lesser General
338 Public License instead of this License.
0 GNU LIBRARY GENERAL PUBLIC LICENSE
1 Version 2, June 1991
2
3 Copyright (C) 1991 Free Software Foundation, Inc.
4 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
5 Everyone is permitted to copy and distribute verbatim copies
6 of this license document, but changing it is not allowed.
7
8 [This is the first released version of the library GPL. It is
9 numbered 2 because it goes with version 2 of the ordinary GPL.]
10
11 Preamble
12
13 The licenses for most software are designed to take away your
14 freedom to share and change it. By contrast, the GNU General Public
15 Licenses are intended to guarantee your freedom to share and change
16 free software--to make sure the software is free for all its users.
17
18 This license, the Library General Public License, applies to some
19 specially designated Free Software Foundation software, and to any
20 other libraries whose authors decide to use it. You can use it for
21 your libraries, too.
22
23 When we speak of free software, we are referring to freedom, not
24 price. Our General Public Licenses are designed to make sure that you
25 have the freedom to distribute copies of free software (and charge for
26 this service if you wish), that you receive source code or can get it
27 if you want it, that you can change the software or use pieces of it
28 in new free programs; and that you know you can do these things.
29
30 To protect your rights, we need to make restrictions that forbid
31 anyone to deny you these rights or to ask you to surrender the rights.
32 These restrictions translate to certain responsibilities for you if
33 you distribute copies of the library, or if you modify it.
34
35 For example, if you distribute copies of the library, whether gratis
36 or for a fee, you must give the recipients all the rights that we gave
37 you. You must make sure that they, too, receive or can get the source
38 code. If you link a program with the library, you must provide
39 complete object files to the recipients so that they can relink them
40 with the library, after making changes to the library and recompiling
41 it. And you must show them these terms so they know their rights.
42
43 Our method of protecting your rights has two steps: (1) copyright
44 the library, and (2) offer you this license which gives you legal
45 permission to copy, distribute and/or modify the library.
46
47 Also, for each distributor's protection, we want to make certain
48 that everyone understands that there is no warranty for this free
49 library. If the library is modified by someone else and passed on, we
50 want its recipients to know that what they have is not the original
51 version, so that any problems introduced by others will not reflect on
52 the original authors' reputations.
53
54 Finally, any free program is threatened constantly by software
55 patents. We wish to avoid the danger that companies distributing free
56 software will individually obtain patent licenses, thus in effect
57 transforming the program into proprietary software. To prevent this,
58 we have made it clear that any patent must be licensed for everyone's
59 free use or not licensed at all.
60
61 Most GNU software, including some libraries, is covered by the ordinary
62 GNU General Public License, which was designed for utility programs. This
63 license, the GNU Library General Public License, applies to certain
64 designated libraries. This license is quite different from the ordinary
65 one; be sure to read it in full, and don't assume that anything in it is
66 the same as in the ordinary license.
67
68 The reason we have a separate public license for some libraries is that
69 they blur the distinction we usually make between modifying or adding to a
70 program and simply using it. Linking a program with a library, without
71 changing the library, is in some sense simply using the library, and is
72 analogous to running a utility program or application program. However, in
73 a textual and legal sense, the linked executable is a combined work, a
74 derivative of the original library, and the ordinary General Public License
75 treats it as such.
76
77 Because of this blurred distinction, using the ordinary General
78 Public License for libraries did not effectively promote software
79 sharing, because most developers did not use the libraries. We
80 concluded that weaker conditions might promote sharing better.
81
82 However, unrestricted linking of non-free programs would deprive the
83 users of those programs of all benefit from the free status of the
84 libraries themselves. This Library General Public License is intended to
85 permit developers of non-free programs to use free libraries, while
86 preserving your freedom as a user of such programs to change the free
87 libraries that are incorporated in them. (We have not seen how to achieve
88 this as regards changes in header files, but we have achieved it as regards
89 changes in the actual functions of the Library.) The hope is that this
90 will lead to faster development of free libraries.
91
92 The precise terms and conditions for copying, distribution and
93 modification follow. Pay close attention to the difference between a
94 "work based on the library" and a "work that uses the library". The
95 former contains code derived from the library, while the latter only
96 works together with the library.
97
98 Note that it is possible for a library to be covered by the ordinary
99 General Public License rather than by this special one.
100
101 GNU LIBRARY GENERAL PUBLIC LICENSE
102 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
103
104 0. This License Agreement applies to any software library which
105 contains a notice placed by the copyright holder or other authorized
106 party saying it may be distributed under the terms of this Library
107 General Public License (also called "this License"). Each licensee is
108 addressed as "you".
109
110 A "library" means a collection of software functions and/or data
111 prepared so as to be conveniently linked with application programs
112 (which use some of those functions and data) to form executables.
113
114 The "Library", below, refers to any such software library or work
115 which has been distributed under these terms. A "work based on the
116 Library" means either the Library or any derivative work under
117 copyright law: that is to say, a work containing the Library or a
118 portion of it, either verbatim or with modifications and/or translated
119 straightforwardly into another language. (Hereinafter, translation is
120 included without limitation in the term "modification".)
121
122 "Source code" for a work means the preferred form of the work for
123 making modifications to it. For a library, complete source code means
124 all the source code for all modules it contains, plus any associated
125 interface definition files, plus the scripts used to control compilation
126 and installation of the library.
127
128 Activities other than copying, distribution and modification are not
129 covered by this License; they are outside its scope. The act of
130 running a program using the Library is not restricted, and output from
131 such a program is covered only if its contents constitute a work based
132 on the Library (independent of the use of the Library in a tool for
133 writing it). Whether that is true depends on what the Library does
134 and what the program that uses the Library does.
135
136 1. You may copy and distribute verbatim copies of the Library's
137 complete source code as you receive it, in any medium, provided that
138 you conspicuously and appropriately publish on each copy an
139 appropriate copyright notice and disclaimer of warranty; keep intact
140 all the notices that refer to this License and to the absence of any
141 warranty; and distribute a copy of this License along with the
142 Library.
143
144 You may charge a fee for the physical act of transferring a copy,
145 and you may at your option offer warranty protection in exchange for a
146 fee.
147
148 2. You may modify your copy or copies of the Library or any portion
149 of it, thus forming a work based on the Library, and copy and
150 distribute such modifications or work under the terms of Section 1
151 above, provided that you also meet all of these conditions:
152
153 a) The modified work must itself be a software library.
154
155 b) You must cause the files modified to carry prominent notices
156 stating that you changed the files and the date of any change.
157
158 c) You must cause the whole of the work to be licensed at no
159 charge to all third parties under the terms of this License.
160
161 d) If a facility in the modified Library refers to a function or a
162 table of data to be supplied by an application program that uses
163 the facility, other than as an argument passed when the facility
164 is invoked, then you must make a good faith effort to ensure that,
165 in the event an application does not supply such function or
166 table, the facility still operates, and performs whatever part of
167 its purpose remains meaningful.
168
169 (For example, a function in a library to compute square roots has
170 a purpose that is entirely well-defined independent of the
171 application. Therefore, Subsection 2d requires that any
172 application-supplied function or table used by this function must
173 be optional: if the application does not supply it, the square
174 root function must still compute square roots.)
175
176 These requirements apply to the modified work as a whole. If
177 identifiable sections of that work are not derived from the Library,
178 and can be reasonably considered independent and separate works in
179 themselves, then this License, and its terms, do not apply to those
180 sections when you distribute them as separate works. But when you
181 distribute the same sections as part of a whole which is a work based
182 on the Library, the distribution of the whole must be on the terms of
183 this License, whose permissions for other licensees extend to the
184 entire whole, and thus to each and every part regardless of who wrote
185 it.
186
187 Thus, it is not the intent of this section to claim rights or contest
188 your rights to work written entirely by you; rather, the intent is to
189 exercise the right to control the distribution of derivative or
190 collective works based on the Library.
191
192 In addition, mere aggregation of another work not based on the Library
193 with the Library (or with a work based on the Library) on a volume of
194 a storage or distribution medium does not bring the other work under
195 the scope of this License.
196
197 3. You may opt to apply the terms of the ordinary GNU General Public
198 License instead of this License to a given copy of the Library. To do
199 this, you must alter all the notices that refer to this License, so
200 that they refer to the ordinary GNU General Public License, version 2,
201 instead of to this License. (If a newer version than version 2 of the
202 ordinary GNU General Public License has appeared, then you can specify
203 that version instead if you wish.) Do not make any other change in
204 these notices.
205
206 Once this change is made in a given copy, it is irreversible for
207 that copy, so the ordinary GNU General Public License applies to all
208 subsequent copies and derivative works made from that copy.
209
210 This option is useful when you wish to copy part of the code of
211 the Library into a program that is not a library.
212
213 4. You may copy and distribute the Library (or a portion or
214 derivative of it, under Section 2) in object code or executable form
215 under the terms of Sections 1 and 2 above provided that you accompany
216 it with the complete corresponding machine-readable source code, which
217 must be distributed under the terms of Sections 1 and 2 above on a
218 medium customarily used for software interchange.
219
220 If distribution of object code is made by offering access to copy
221 from a designated place, then offering equivalent access to copy the
222 source code from the same place satisfies the requirement to
223 distribute the source code, even though third parties are not
224 compelled to copy the source along with the object code.
225
226 5. A program that contains no derivative of any portion of the
227 Library, but is designed to work with the Library by being compiled or
228 linked with it, is called a "work that uses the Library". Such a
229 work, in isolation, is not a derivative work of the Library, and
230 therefore falls outside the scope of this License.
231
232 However, linking a "work that uses the Library" with the Library
233 creates an executable that is a derivative of the Library (because it
234 contains portions of the Library), rather than a "work that uses the
235 library". The executable is therefore covered by this License.
236 Section 6 states terms for distribution of such executables.
237
238 When a "work that uses the Library" uses material from a header file
239 that is part of the Library, the object code for the work may be a
240 derivative work of the Library even though the source code is not.
241 Whether this is true is especially significant if the work can be
242 linked without the Library, or if the work is itself a library. The
243 threshold for this to be true is not precisely defined by law.
244
245 If such an object file uses only numerical parameters, data
246 structure layouts and accessors, and small macros and small inline
247 functions (ten lines or less in length), then the use of the object
248 file is unrestricted, regardless of whether it is legally a derivative
249 work. (Executables containing this object code plus portions of the
250 Library will still fall under Section 6.)
251
252 Otherwise, if the work is a derivative of the Library, you may
253 distribute the object code for the work under the terms of Section 6.
254 Any executables containing that work also fall under Section 6,
255 whether or not they are linked directly with the Library itself.
256
257 6. As an exception to the Sections above, you may also compile or
258 link a "work that uses the Library" with the Library to produce a
259 work containing portions of the Library, and distribute that work
260 under terms of your choice, provided that the terms permit
261 modification of the work for the customer's own use and reverse
262 engineering for debugging such modifications.
263
264 You must give prominent notice with each copy of the work that the
265 Library is used in it and that the Library and its use are covered by
266 this License. You must supply a copy of this License. If the work
267 during execution displays copyright notices, you must include the
268 copyright notice for the Library among them, as well as a reference
269 directing the user to the copy of this License. Also, you must do one
270 of these things:
271
272 a) Accompany the work with the complete corresponding
273 machine-readable source code for the Library including whatever
274 changes were used in the work (which must be distributed under
275 Sections 1 and 2 above); and, if the work is an executable linked
276 with the Library, with the complete machine-readable "work that
277 uses the Library", as object code and/or source code, so that the
278 user can modify the Library and then relink to produce a modified
279 executable containing the modified Library. (It is understood
280 that the user who changes the contents of definitions files in the
281 Library will not necessarily be able to recompile the application
282 to use the modified definitions.)
283
284 b) Accompany the work with a written offer, valid for at
285 least three years, to give the same user the materials
286 specified in Subsection 6a, above, for a charge no more
287 than the cost of performing this distribution.
288
289 c) If distribution of the work is made by offering access to copy
290 from a designated place, offer equivalent access to copy the above
291 specified materials from the same place.
292
293 d) Verify that the user has already received a copy of these
294 materials or that you have already sent this user a copy.
295
296 For an executable, the required form of the "work that uses the
297 Library" must include any data and utility programs needed for
298 reproducing the executable from it. However, as a special exception,
299 the source code distributed need not include anything that is normally
300 distributed (in either source or binary form) with the major
301 components (compiler, kernel, and so on) of the operating system on
302 which the executable runs, unless that component itself accompanies
303 the executable.
304
305 It may happen that this requirement contradicts the license
306 restrictions of other proprietary libraries that do not normally
307 accompany the operating system. Such a contradiction means you cannot
308 use both them and the Library together in an executable that you
309 distribute.
310
311 7. You may place library facilities that are a work based on the
312 Library side-by-side in a single library together with other library
313 facilities not covered by this License, and distribute such a combined
314 library, provided that the separate distribution of the work based on
315 the Library and of the other library facilities is otherwise
316 permitted, and provided that you do these two things:
317
318 a) Accompany the combined library with a copy of the same work
319 based on the Library, uncombined with any other library
320 facilities. This must be distributed under the terms of the
321 Sections above.
322
323 b) Give prominent notice with the combined library of the fact
324 that part of it is a work based on the Library, and explaining
325 where to find the accompanying uncombined form of the same work.
326
327 8. You may not copy, modify, sublicense, link with, or distribute
328 the Library except as expressly provided under this License. Any
329 attempt otherwise to copy, modify, sublicense, link with, or
330 distribute the Library is void, and will automatically terminate your
331 rights under this License. However, parties who have received copies,
332 or rights, from you under this License will not have their licenses
333 terminated so long as such parties remain in full compliance.
334
335 9. You are not required to accept this License, since you have not
336 signed it. However, nothing else grants you permission to modify or
337 distribute the Library or its derivative works. These actions are
338 prohibited by law if you do not accept this License. Therefore, by
339 modifying or distributing the Library (or any work based on the
340 Library), you indicate your acceptance of this License to do so, and
341 all its terms and conditions for copying, distributing or modifying
342 the Library or works based on it.
343
344 10. Each time you redistribute the Library (or any work based on the
345 Library), the recipient automatically receives a license from the
346 original licensor to copy, distribute, link with or modify the Library
347 subject to these terms and conditions. You may not impose any further
348 restrictions on the recipients' exercise of the rights granted herein.
349 You are not responsible for enforcing compliance by third parties to
350 this License.
351
352 11. If, as a consequence of a court judgment or allegation of patent
353 infringement or for any other reason (not limited to patent issues),
354 conditions are imposed on you (whether by court order, agreement or
355 otherwise) that contradict the conditions of this License, they do not
356 excuse you from the conditions of this License. If you cannot
357 distribute so as to satisfy simultaneously your obligations under this
358 License and any other pertinent obligations, then as a consequence you
359 may not distribute the Library at all. For example, if a patent
360 license would not permit royalty-free redistribution of the Library by
361 all those who receive copies directly or indirectly through you, then
362 the only way you could satisfy both it and this License would be to
363 refrain entirely from distribution of the Library.
364
365 If any portion of this section is held invalid or unenforceable under any
366 particular circumstance, the balance of the section is intended to apply,
367 and the section as a whole is intended to apply in other circumstances.
368
369 It is not the purpose of this section to induce you to infringe any
370 patents or other property right claims or to contest validity of any
371 such claims; this section has the sole purpose of protecting the
372 integrity of the free software distribution system which is
373 implemented by public license practices. Many people have made
374 generous contributions to the wide range of software distributed
375 through that system in reliance on consistent application of that
376 system; it is up to the author/donor to decide if he or she is willing
377 to distribute software through any other system and a licensee cannot
378 impose that choice.
379
380 This section is intended to make thoroughly clear what is believed to
381 be a consequence of the rest of this License.
382
383 12. If the distribution and/or use of the Library is restricted in
384 certain countries either by patents or by copyrighted interfaces, the
385 original copyright holder who places the Library under this License may add
386 an explicit geographical distribution limitation excluding those countries,
387 so that distribution is permitted only in or among countries not thus
388 excluded. In such case, this License incorporates the limitation as if
389 written in the body of this License.
390
391 13. The Free Software Foundation may publish revised and/or new
392 versions of the Library General Public License from time to time.
393 Such new versions will be similar in spirit to the present version,
394 but may differ in detail to address new problems or concerns.
395
396 Each version is given a distinguishing version number. If the Library
397 specifies a version number of this License which applies to it and
398 "any later version", you have the option of following the terms and
399 conditions either of that version or of any later version published by
400 the Free Software Foundation. If the Library does not specify a
401 license version number, you may choose any version ever published by
402 the Free Software Foundation.
403
404 14. If you wish to incorporate parts of the Library into other free
405 programs whose distribution conditions are incompatible with these,
406 write to the author to ask for permission. For software which is
407 copyrighted by the Free Software Foundation, write to the Free
408 Software Foundation; we sometimes make exceptions for this. Our
409 decision will be guided by the two goals of preserving the free status
410 of all derivatives of our free software and of promoting the sharing
411 and reuse of software generally.
412
413 NO WARRANTY
414
415 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
416 WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
417 EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
418 OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
419 KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
420 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
421 PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
422 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
423 THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
424
425 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
426 WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
427 AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
428 FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
429 CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
430 LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
431 RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
432 FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
433 SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
434 DAMAGES.
435
436 END OF TERMS AND CONDITIONS
437
438 How to Apply These Terms to Your New Libraries
439
440 If you develop a new library, and you want it to be of the greatest
441 possible use to the public, we recommend making it free software that
442 everyone can redistribute and change. You can do so by permitting
443 redistribution under these terms (or, alternatively, under the terms of the
444 ordinary General Public License).
445
446 To apply these terms, attach the following notices to the library. It is
447 safest to attach them to the start of each source file to most effectively
448 convey the exclusion of warranty; and each file should have at least the
449 "copyright" line and a pointer to where the full notice is found.
450
451 <one line to give the library's name and a brief idea of what it does.>
452 Copyright (C) <year> <name of author>
453
454 This library is free software; you can redistribute it and/or
455 modify it under the terms of the GNU Library General Public
456 License as published by the Free Software Foundation; either
457 version 2 of the License, or (at your option) any later version.
458
459 This library is distributed in the hope that it will be useful,
460 but WITHOUT ANY WARRANTY; without even the implied warranty of
461 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
462 Library General Public License for more details.
463
464 You should have received a copy of the GNU Library General Public
465 License along with this library; if not, write to the Free Software
466 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
467
468 Also add information on how to contact you by electronic and paper mail.
469
470 You should also get your employer (if you work as a programmer) or your
471 school, if any, to sign a "copyright disclaimer" for the library, if
472 necessary. Here is a sample; alter the names:
473
474 Yoyodyne, Inc., hereby disclaims all copyright interest in the
475 library `Frob' (a library for tweaking knobs) written by James Random Hacker.
476
477 <signature of Ty Coon>, 1 April 1990
478 Ty Coon, President of Vice
479
480 That's all there is to it!
0 # Generated by Makefile. Do not edit.
1
2 commit 246a46e6f240915c7ba58c455f8d45ea59cebb2e
3 Author: Matthias Clasen <mclasen@redhat.com>
4 Date: Mon Mar 25 22:31:16 2013 -0400
5
6 3.8.0
7
8 M NEWS
9 M configure.ac
10
11 commit 0a37bcb06b0094a677178bd9777758cc16441274
12 Author: Ani Peter <apeter@redhat.com>
13 Date: Mon Mar 25 19:19:17 2013 +0530
14
15 Completed for Malayalam
16
17 M po/ml.po
18
19 commit e28da785eccf196ef0b987b1f4dbe74a5102aff5
20 Author: Krishnababu Krothapalli <kkrothap@redhat.com>
21 Date: Mon Mar 25 15:49:39 2013 +0530
22
23 Updated Telugu Translations
24
25 M po/te.po
26
27 commit 4343c5db75e0b306b835212a98b3edee8ea66db5
28 Author: Reşat SABIQ <tilde.birlik@gmail.com>
29 Date: Sun Mar 24 13:26:35 2013 -0500
30
31 Minor update for Crimean Tatar/Turkish translation
32
33 M po/crh.po
34
35 commit 292ecdb7084cd52c5c8016c2e4b25a78f1debdef
36 Author: Reşat SABIQ <tilde.birlik@gmail.com>
37 Date: Sun Mar 24 13:24:38 2013 -0500
38
39 Updated Crimean Tatar (Crimean Turkish) translation
40
41 M po/crh.po
42
43 commit 47378bcc11d754c3490c7ce06b6a258b8c8afa8c
44 Author: Jiro Matsuzawa <jmatsuzawa@gnome.org>
45 Date: Mon Mar 25 02:30:49 2013 +0900
46
47 l10n: Update Japanese translation
48
49 M po/ja.po
50
51 commit 5e5b387cfd590cd028df6dc323a547e66c76c23b
52 Author: Wouter Bolsterlee <uws@xs4all.nl>
53 Date: Sat Mar 23 23:39:30 2013 +0100
54
55 Updated Dutch translation
56
57 M po/nl.po
58
59 commit 5a6cb52314c39f577ff6faaa6e94bb9620d96f00
60 Author: Daniel Korostil <ted.korostiled@gmail.com>
61 Date: Sat Mar 23 12:42:16 2013 +0200
62
63 Uploaded Ukranian
64
65 M po/uk.po
66
67 commit 370d50f85550cd5e2398198f7148e52c69e2d38f
68 Author: Shantha kumar <shkumar@redhat.com>
69 Date: Fri Mar 22 15:45:10 2013 +0530
70
71 Tamil Translations Updated
72
73 M po/ta.po
74
75 commit 8770020280a690de1f047aa55d360968d187f94b
76 Author: Rajesh Ranjan <rranjan@redhat.com>
77 Date: Fri Mar 22 12:40:18 2013 +0530
78
79 hindi translation
80
81 M po/hi.po
82
83 commit c03b16747e734de180c3dfd678f9657f1f5f163d
84 Author: Sandeep Sheshrao Shedmake <sshedmak@redhat.com>
85 Date: Thu Mar 21 18:46:00 2013 +0530
86
87 Updated Marathi Translations
88
89 M po/mr.po
90
91 commit 5c50a43768e2eff9f68f94b3a1207b0c1d8cd905
92 Author: ManojKumar Giri <mgiri@redhat.com>
93 Date: Wed Mar 20 18:04:23 2013 +0530
94
95 Updated Odia Translation.
96
97 M po/or.po
98
99 commit fb40f7702b8d443e5c6a4983da133b1eafad300c
100 Author: Danial Behzadi <dani.behzi@gmail.com>
101 Date: Wed Mar 20 09:26:27 2013 +0330
102
103 l10n: Updated Persian translation
104
105 M po/fa.po
106
107 commit 107172dc7b4c8b7936113d74f3c53fe53b24bdeb
108 Author: Victor Ibragimov <victor.ibragimov@gmail.com>
109 Date: Tue Mar 19 20:40:30 2013 +0100
110
111 [l10n] Added Tadjik translation
112
113 M po/LINGUAS
114 A po/tg.po
115
116 commit c573fa0e08616a908e6f1c335641ec6c1b9105f4
117 Author: Shankar Prasad <svenkate@redhat.com>
118 Date: Mon Mar 18 16:02:23 2013 +0530
119
120 Updated kn translations
121
122 M po/kn.po
123
124 commit e26a4cd263ffc78db3104a9aca6e85034d48a67b
125 Author: Marek Černocký <marek@manet.cz>
126 Date: Sun Mar 17 22:46:15 2013 +0100
127
128 Updated Czech translation
129
130 M po/cs.po
131
132 commit 3fa134531fa6a7f1ed2ac4b572ea455a55eb1dd4
133 Author: Carles Ferrando <carles.ferrando@gmail.com>
134 Date: Sun Mar 17 19:51:46 2013 +0100
135
136 [l10n] Updated Catalan (Valencian) translation
137
138 M po/ca@valencia.po
139
140 commit 94a7ab3216a5d64c38272f616a20715ea6028b5f
141 Author: Ask H. Larsen <asklarsen@gmail.com>
142 Date: Sun Mar 17 14:44:39 2013 +0100
143
144 Updated Danish translation
145
146 M po/da.po
147
148 commit f4adf5966ff0a6ebc86cdad7b22126dd88859804
149 Author: Ihar Hrachyshka <ihar.hrachyshka@gmail.com>
150 Date: Sun Mar 17 16:24:34 2013 +0300
151
152 Updated Belarusian translation.
153
154 M po/be.po
155
156 commit b717f6e4f5d9a792f441f626bb769ec150e4bdd9
157 Author: Changwoo Ryu <cwryu@debian.org>
158 Date: Sat Mar 16 22:26:34 2013 +0900
159
160 Updated Korean translation
161
162 M po/ko.po
163
164 commit 8da5f3028860bfa6f5d8b38f6071285b501bb800
165 Author: Balázs Úr <urbalazs@src.gnome.org>
166 Date: Thu Mar 14 22:14:33 2013 +0100
167
168 Updated Hungarian translation
169
170 M po/hu.po
171
172 commit ca3a6029128122ee4f4b4b991686529174f25d02
173 Author: Duarte Loreto <happyguy_pt@hotmail.com>
174 Date: Tue Mar 12 13:05:37 2013 +0000
175
176 Updated Portuguese translation and converted to New Spelling (Novo AO)
177
178 M po/pt.po
179
180 commit 758c22ec25528869dc96a3a77b76f378e4339d1e
181 Author: Gil Forcada <gforcada@gnome.org>
182 Date: Mon Mar 11 23:01:17 2013 +0100
183
184 [l10n] Updated Catalan translation
185
186 M po/ca.po
187
188 commit ed0dbd6eba3bcff96ce03dc7ba03c1d632e9e14d
189 Author: Mattias Põldaru <mahfiaz@gmail.com>
190 Date: Mon Mar 11 15:31:45 2013 +0200
191
192 [l10n] Updated Estonian translation
193
194 M po/et.po
195
196 commit 2be1f73f29a753a243f8dfcdf540916c8c9f20ad
197 Author: Andika Triwidada <andika@gmail.com>
198 Date: Mon Mar 11 12:39:01 2013 +0700
199
200 Updated Indonesian translation
201
202 M po/id.po
203
204 commit 8d0d9190809b44cdd94ae293d47df2abc4b1fe8d
205 Author: Khaled Hosny <khaledhosny@eglug.org>
206 Date: Sun Mar 10 00:06:18 2013 +0200
207
208 Update Arabic translation
209
210 M po/ar.po
211
212 commit a6c9fb7eaa9c3df19dbe6d555795f563fcdfa7c8
213 Author: Baurzhan Muftakhidinov <baurthefirst@gmail.com>
214 Date: Sat Mar 9 21:28:34 2013 +0600
215
216 Updated Kazakh translation.
217
218 M po/kk.po
219
220 commit eebcb71254cc92334da975505e07446d1a00bf09
221 Author: Ville-Pekka Vainio <vpvainio@iki.fi>
222 Date: Thu Mar 7 20:57:21 2013 +0200
223
224 Finnish translation update by Jiri Grönroos
225
226 M po/fi.po
227
228 commit 47aec8856134fb8960679260a92c499578228b26
229 Author: Rafael Ferreira <rafael.f.f1@gmail.com>
230 Date: Tue Mar 5 10:16:37 2013 -0300
231
232 Updated Brazilian Portuguese translation
233
234 M po/pt_BR.po
235
236 commit e63b9b880787e06c945a525048a7a7d92f8e19c8
237 Author: Nilamdyuti Goswami <ngoswami@redhat.com>
238 Date: Tue Mar 5 15:34:11 2013 +0530
239
240 Assamese translation updated for gnome 3.8
241
242 M po/as.po
243
244 commit 192c6566cc281c52a2ca438da965da32c5e752f2
245 Author: Inaki Larranaga Murgoitio <dooteo@zundan.com>
246 Date: Mon Mar 4 23:08:53 2013 +0100
247
248 Updated Basque language
249
250 M po/eu.po
251
252 commit 0f929355820faafef8712b17be906e834c3c97cc
253 Author: Jeremy Bicha <jbicha@ubuntu.com>
254 Date: Sun Mar 3 01:49:02 2013 -0500
255
256 sundry: Add Power Statistics & Personal File Sharing
257
258 These seem to sort of duplicate what we have in Settings.
259 Perhaps the extra graphs and info from Power Statistics should find
260 a new home in System Monitor?
261
262 https://bugzilla.gnome.org/show_bug.cgi?id=695047
263
264 M layout/gnome-applications.menu
265
266 commit af5d71c573cda0e3527ea935516454dc3cd35e28
267 Author: William Jon McCann <william.jon.mccann@gmail.com>
268 Date: Sun Mar 3 15:33:51 2013 -0500
269
270 Revert "Remove the special casing for Fedora desktop vendor renames"
271
272 This reverts commit 5cc05790a308bbf7a5905254e377d3ca47df603b.
273
274 We should really wait a bit before pushing this so the changes have
275 time to propagate.
276
277 M layout/gnome-applications.menu
278
279 commit a5382598d7f982e49b6267869db69ac7cf562b26
280 Author: William Jon McCann <william.jon.mccann@gmail.com>
281 Date: Sun Mar 3 15:19:45 2013 -0500
282
283 layout: put im-chooser into Sundry
284
285 M layout/gnome-applications.menu
286
287 commit 28ab24749488986519a21a51ca8060e5d9002256
288 Author: Benjamin Steinwender <b@stbe.at>
289 Date: Sun Mar 3 19:52:08 2013 +0100
290
291 [l10n] Updated German translation
292
293 M po/de.po
294
295 commit 6f27ae37ae12ee34b15479bb2a76d8a83ad5be81
296 Author: Luca Ferretti <lferrett@gnome.org>
297 Date: Sun Mar 3 01:19:56 2013 +0100
298
299 l10n: Updated Italian translation
300
301 M po/it.po
302
303 commit 5cc05790a308bbf7a5905254e377d3ca47df603b
304 Author: Kalev Lember <kalevlember@gmail.com>
305 Date: Fri Feb 22 13:15:18 2013 +0100
306
307 Remove the special casing for Fedora desktop vendor renames
308
309 The desktop file vendor prefixes are gone from eog.desktop,
310 file-roller.desktop, gucharmap.desktop, and yelp.desktop in Fedora 19+,
311 so we no longer need to special case them in gnome-applications.menu.
312
313 https://bugzilla.gnome.org/show_bug.cgi?id=694444
314
315 M layout/gnome-applications.menu
316
317 commit e85f2419023afef93e25d2058560fc833c22d310
318 Author: Мирослав Николић <miroslavnikolic@rocketmail.com>
319 Date: Fri Mar 1 09:26:42 2013 +0100
320
321 Updated Serbian translation
322
323 M po/sr.po
324 M po/sr@latin.po
325
326 commit aab9d5ed860175bb86063a58a1fb82b3083d3431
327 Author: Chao-Hsiung Liao <j_h_liau@yahoo.com.tw>
328 Date: Wed Feb 27 22:51:08 2013 +0800
329
330 Updated Traditional Chinese translation(Hong Kong and Taiwan)
331
332 M po/zh_HK.po
333 M po/zh_TW.po
334
335 commit 7ba60014ce6bcc74996b519540ad58940ecd7205
336 Author: Sweta Kothari <swkothar@redhat.com>
337 Date: Wed Feb 27 13:58:35 2013 +0530
338
339 Updated gujarati file
340
341 M po/gu.po
342
343 commit 4a4d04d822e226eaf2c13a24a79ec639b620eb8a
344 Author: Pavol Klačanský <pavol@klacansky.com>
345 Date: Mon Feb 25 20:57:24 2013 +0100
346
347 Updated slovak translation
348
349 M po/sk.po
350
351 commit 3b28046273ca53e7a4f2d78f26fad10ea7c06424
352 Author: Kjartan Maraas <kmaraas@gnome.org>
353 Date: Mon Feb 25 19:18:08 2013 +0100
354
355 Updated Norwegian bokmål translation
356
357 M po/nb.po
358
359 commit 7c94f1773354a31f9db1f140bd164f9bb5a2f872
360 Author: Jasper St. Pierre <jstpierre@mecheye.net>
361 Date: Mon Feb 25 11:13:14 2013 -0500
362
363 configure: Remove AM_PATH_PYTHON
364
365 This was a leftover from removing the simple-editor.
366
367 M configure.ac
368
369 commit fe172ebde5ff50e55784611bc6048ecf56580699
370 Author: Aurimas Černius <aurisc4@gmail.com>
371 Date: Sun Feb 24 21:59:22 2013 +0200
372
373 Updated Lithuanian translation
374
375 M po/lt.po
376
377 commit 4338482231743c5e8bab7dd565e269e3a0cbe2cd
378 Author: Dimitris Spingos <dmtrs32@gmail.com>
379 Date: Sun Feb 24 10:23:11 2013 +0200
380
381 Updated Greek translation
382
383 M po/el.po
384
385 commit 3e5fe2ec16b5683ffdf225f1b2721ac4e4f902c6
386 Author: Matej Urbančič <mateju@svn.gnome.org>
387 Date: Fri Feb 22 22:28:14 2013 +0100
388
389 Updated Slovenian translation
390
391 M po/sl.po
392
393 commit 57b724f42917bcde9cf6b52d885bea4662b18f89
394 Author: A S Alam <aalam@users.sf.net>
395 Date: Fri Feb 22 14:55:42 2013 +0000
396
397 Punjabi: Translation updated (aalam)
398
399 M po/pa.po
400
401 commit 0cfe9c374d664492a77dbda436da5fe3bed7da4c
402 Author: Gheyret Kenji <gheyret@gmail.com>
403 Date: Fri Feb 22 22:58:01 2013 +0900
404
405 Updated Uyghur translation
406
407 Signed-off-by: Gheyret Kenji <gheyret@gmail.com>
408
409 M po/ug.po
410
411 commit e1849964ad1a91d2a017f0bcc743bb936c52fb0d
412 Author: Yaron Shahrabani <sh.yaron@gmail.com>
413 Date: Thu Feb 21 19:41:32 2013 +0200
414
415 Updated Hebrew translation.
416
417 M po/he.po
418
419 commit ecc8ace4d8d7603f3f1c182de3ae107c20701302
420 Author: Daniel Mustieles <daniel.mustieles@gmail.com>
421 Date: Thu Feb 21 16:55:32 2013 +0100
422
423 Updated Spanish translation
424
425 M po/es.po
426
427 commit eb5f8823904a79787d0e6057f36fc9d08d44c332
428 Author: Yuri Myasoedov <omerta13@yandex.ru>
429 Date: Thu Feb 21 15:54:56 2013 +0400
430
431 Updated Russian translation
432
433 M po/ru.po
434
435 commit 207fa196dd04124ea3919cc7e66b44be75f982f9
436 Author: Cosimo Cecchi <cosimoc@gnome.org>
437 Date: Wed Feb 20 18:48:01 2013 -0500
438
439 sundry: add more Java stuff
440
441 I get these indecipherable desktop files when I install Eclipse.
442
443 https://bugzilla.gnome.org/show_bug.cgi?id=694324
444
445 M layout/gnome-applications.menu
446
447 commit 31b32c027426007639369832e4d6bb48f4f6c0fc
448 Author: Daniel Martinez <dmartinez@src.gnome.org>
449 Date: Wed Feb 20 23:11:23 2013 +0100
450
451 Updated Aragonese translation
452
453 M po/an.po
454
455 commit 48307035146fb3b1e6fea66f5b73a4bfe001a064
456 Author: Alexandre Franke <alexandre.franke@gmail.com>
457 Date: Wed Feb 20 21:15:02 2013 +0100
458
459 Update French translation
460
461 M po/fr.po
462
463 commit e775ffd58b91ec437c4a17d3fea2f5bc45b2423a
464 Author: Fran Diéguez <fran.dieguez@mabishu.com>
465 Date: Wed Feb 20 10:38:36 2013 +0100
466
467 Updated Galician translations
468
469 M po/gl.po
470
471 commit 96ae298b28c458bfd3f735a6ed8dac5d79b3d0b1
472 Author: Rūdolfs Mazurs <rudolfsm@src.gnome.org>
473 Date: Wed Feb 20 11:33:40 2013 +0200
474
475 Updated Latvian translation
476
477 M po/lv.po
478
479 commit a2f3bacaba079df016cf53b8331fb4e1c77f2eab
480 Author: Jasper St. Pierre <jstpierre@mecheye.net>
481 Date: Tue Feb 19 18:55:17 2013 -0500
482
483 Post-release version bump
484
485 M configure.ac
486
487 commit 005605f79c8fb7f13542c497ccc1f1fe52907902
488 Author: Jasper St. Pierre <jstpierre@mecheye.net>
489 Date: Tue Feb 19 18:53:34 2013 -0500
490
491 Release 3.7.90
492
493 M NEWS
494 M configure.ac
495
496 commit 64e189fedf5a04956cb96c54560f54736a012f4b
497 Author: Piotr Drąg <piotrdrag@gmail.com>
498 Date: Tue Feb 19 23:32:28 2013 +0100
499
500 Updated Polish translation
501
502 M po/pl.po
503
504 commit f3c636fbcf38846698c657d6aeb920cdabb0ea17
505 Author: Piotr Drąg <piotrdrag@gmail.com>
506 Date: Tue Feb 19 23:31:14 2013 +0100
507
508 Updated POTFILES.in
509
510 M po/POTFILES.in
511
512 commit ac21fd7730b0c7f84b06468f862592d9666dfac6
513 Author: Florian Müllner <fmuellner@gnome.org>
514 Date: Tue Feb 19 12:47:18 2013 +0100
515
516 Add .directory file for Sundry
517
518 https://bugzilla.gnome.org/show_bug.cgi?id=694131
519
520 M desktop-directories/Makefile.am
521 A desktop-directories/X-GNOME-Sundry.directory.in
522 M layout/gnome-applications.menu
523
524 commit 95858c40461f6a2c53e2d4427d0851a08e0181c7
525 Author: Alexandre Franke <alexandre.franke@gmail.com>
526 Date: Tue Feb 19 21:56:15 2013 +0100
527
528 Update French translation
529
530 M po/fr.po
531
532 commit acb41bfe71a4e4717707eb21aef6c2b19b331e76
533 Author: Jasper St. Pierre <jstpierre@mecheye.net>
534 Date: Tue Feb 19 15:14:48 2013 -0500
535
536 layout: Add some more items to Sundry
537
538 M layout/gnome-applications.menu
539
540 commit 1f4c3b6891637dfd2b8f42bae139166cd0e1fd8e
541 Author: Piotr Drąg <piotrdrag@gmail.com>
542 Date: Tue Feb 19 20:56:29 2013 +0100
543
544 Updated Polish translation
545
546 M po/pl.po
547
548 commit b1f831d2894df2501086f7c4bd523fe0e67f3676
549 Author: Piotr Drąg <piotrdrag@gmail.com>
550 Date: Tue Feb 19 20:44:51 2013 +0100
551
552 Updated POTFILES.in
553
554 M po/POTFILES.in
555
556 commit 36d5d699d7d4193a1b3d84777566466326f78b19
557 Author: Florian Müllner <fmuellner@gnome.org>
558 Date: Sun Feb 17 03:40:22 2013 +0100
559
560 layout: Add Sundry category
561
562 Creates a Sundry directory to contain things that either aren't really apps
563 or are scheduled for removal but that are hard to remove at the moment for
564 various reasons. We just need a temporary way to segregate these items so they
565 don't pollute the app view.
566
567 https://bugzilla.gnome.org/show_bug.cgi?id=694131
568
569 M layout/gnome-applications.menu
570
571 commit 8cb236fbf31dee1a533582f1f8ef72b7b96a33e0
572 Author: Florian Müllner <fmuellner@gnome.org>
573 Date: Sun Feb 17 02:51:00 2013 +0100
574
575 layout: Account for gcalctool => gnome-calculator name change
576
577 https://bugzilla.gnome.org/show_bug.cgi?id=694131
578
579 M layout/gnome-applications.menu
580
581 commit a06c0348c1a129ffd996e00615d5d6d25b17dcdc
582 Author: Florian Müllner <fmuellner@gnome.org>
583 Date: Sun Feb 17 02:25:42 2013 +0100
584
585 layout: Deal with some vendor renames
586
587 The menu spec doesn't offer very specific guidance for how
588 to use vendor prefixes. And it isn't even clear about what
589 a vendor is. Or how it helps avoid collisions with a naming scheme
590 that isn't fully qualified in the first place.
591
592 Longer term we should move to something that uses fully qualified
593 names like the dbus spec. In the meantime we have to deal with
594 "aliases" for app names.
595
596 https://bugzilla.gnome.org/show_bug.cgi?id=694131
597
598 M layout/gnome-applications.menu
599
600 commit e644a541b5bab530fe6649c3b8f212faca46eb66
601 Author: Florian Müllner <fmuellner@gnome.org>
602 Date: Sun Feb 17 02:18:51 2013 +0100
603
604 layout: Add a new X-GNOME-Utilities directory
605
606 Rather than using a developer-defined category, this directory
607 contains an explicit whitelist of known-good GNOME tools. This
608 more or less follows the definition set in the GNOME jhbuild
609 moduleset.
610
611 https://bugzilla.gnome.org/show_bug.cgi?id=694131
612
613 M desktop-directories/Makefile.am
614 A desktop-directories/X-GNOME-Utilities.directory.in
615 M layout/gnome-applications.menu
616
617 commit a5e2e6e4912c2dfcf6559828186063ecd6d09e57
618 Author: Jasper St. Pierre <jstpierre@mecheye.net>
619 Date: Mon Feb 18 14:36:03 2013 -0500
620
621 desktop-directories: Remove system settings desktop entries
622
623 They're unused by gnome-control-center.
624
625 D desktop-directories/Hardware.directory.in
626 M desktop-directories/Makefile.am
627 D desktop-directories/Personal.directory.in
628 D desktop-directories/System.directory.in
629
630 commit b53758987b4d04bf9c869bfd02c0ec1d252638bf
631 Author: Jasper St. Pierre <jstpierre@mecheye.net>
632 Date: Fri Feb 15 19:23:22 2013 -0500
633
634 layout: Install as gnome-applications.menu
635
636 gnome-session now sets XDG_MENU_PREFIX by default, so we should
637 install it where applications that use gnome-menus look.
638
639 M layout/Makefile.am
640 R100 layout/applications.menu layout/gnome-applications.menu
641
642 commit cfaa9d460ef9fb502585d0fcdfcf3bb4e25b19aa
643 Author: Jasper St. Pierre <jstpierre@mecheye.net>
644 Date: Fri Feb 15 17:33:00 2013 -0500
645
646 layout: Put control center apps in the applications menu
647
648 This allows gnome-shell to do app tracking for them. It should
649 not display them, as the desktop directory itself is marked as
650 NoDisplay=true.
651
652 M desktop-directories/Makefile.am
653 A desktop-directories/X-GNOME-SystemSettings.directory.in
654 M layout/applications.menu
655
656 commit a1c6559dcb1f9c9d12d9ebb6bd226a078a02d19a
657 Author: Jasper St. Pierre <jstpierre@mecheye.net>
658 Date: Fri Feb 15 20:44:48 2013 -0500
659
660 gmenu-tree: Fix error reporting
661
662 No idea why this was here..
663
664 M libmenu/gmenu-tree.c
665
666 commit 1e12d0f9f309c977702dad1363e56cd9989b7375
667 Author: Jasper St. Pierre <jstpierre@mecheye.net>
668 Date: Fri Feb 15 20:29:18 2013 -0500
669
670 configure: Have better debugging on by default
671
672 M configure.ac
673
674 commit 589ba554d79366dacf3592b3670459003e45ace4
675 Author: William Jon McCann <jmccann@redhat.com>
676 Date: Fri Sep 28 09:53:22 2012 -0400
677
678 Remove the simple editor
679
680 gnome-menus is a dependency component and we don't really want it
681 to be installing applications as a side effect. There are other
682 better and maintained options for menu editing now anyway.
683
684 https://bugzilla.gnome.org/show_bug.cgi?id=684900
685
686 M Makefile.am
687 M configure.ac
688 M po/POTFILES.in
689 D simple-editor/GMenuSimpleEditor/Makefile.am
690 D simple-editor/GMenuSimpleEditor/__init__.py
691 D simple-editor/GMenuSimpleEditor/config.py.in
692 D simple-editor/GMenuSimpleEditor/main.py
693 D simple-editor/GMenuSimpleEditor/maindialog.py
694 D simple-editor/GMenuSimpleEditor/menufilewriter.py
695 D simple-editor/GMenuSimpleEditor/menutreemodel.py
696 D simple-editor/Makefile.am
697 D simple-editor/gmenu-simple-editor.desktop.in
698 D simple-editor/gmenu-simple-editor.in
699 D simple-editor/gmenu-simple-editor.ui
700
701 commit 7d90221e27fcc72de497f45db4f3ef3c2d623fd6
702 Author: Jasper St. Pierre <jstpierre@mecheye.net>
703 Date: Fri Feb 1 21:46:13 2013 -0500
704
705 Memory leak fixes
706
707 Based on a patch by William Jon McCann <william.jon.mccann@gmail.com>
708
709 https://bugzilla.gnome.org/show_bug.cgi?id=349695
710
711 M libmenu/desktop-entries.c
712 M libmenu/entry-directories.c
713 M libmenu/gmenu-tree.c
714
715 commit db75a478f53f2619b1d96cd09d3a4f9abe682fc9
716 Author: Inaki Larranaga Murgoitio <dooteo@zundan.com>
717 Date: Wed Jan 30 21:26:01 2013 +0100
718
719 Updated Basque language
720
721 M po/eu.po
722
723 commit 2b4eb5f9db8411e62da6ec8d80ada1895772d05b
724 Author: Nuno Araujo <nuno.araujo@russo79.com>
725 Date: Tue Jan 15 22:36:11 2013 +0100
726
727 Fix the build with automake 1.13
728
729 In Automake 1.13, the long-deprecated macro AM_CONFIG_HEADER (deprecated
730 since 2002) has been removed in favour of AC_CONFIG_HEADERS.
731
732 https://bugzilla.gnome.org/show_bug.cgi?id=692108
733
734 M configure.ac
735
736 commit 86241e29cd1101fe638a5665b8c52e8d3c84444d
737 Author: Fabio Tomat <f.t.public@gmail.com>
738 Date: Sat Jan 19 02:34:16 2013 +0100
739
740 Updated Friulian translation
741
742 M po/fur.po
743
744 commit 8d161da704766d9f4a42f1213ca4dc6b8d0e7873
745 Author: Matthias Clasen <mclasen@redhat.com>
746 Date: Tue Jan 15 07:42:02 2013 -0500
747
748 Bump version
749
750 M configure.ac
751
752 commit d4a11d28df389f00550848a078c40a2438908df5
753 Author: Matthias Clasen <mclasen@redhat.com>
754 Date: Tue Jan 15 07:35:47 2013 -0500
755
756 3.6.2
757
758 M NEWS
759
760 commit 234940bf89aa82012c3622674cec151dcf4ff128
761 Author: Gheyret Kenji <gheyret@gmail.com>
762 Date: Sat Jan 12 14:00:36 2013 +0900
763
764 Updated Uyghur translation
765
766 Signed-off-by: Gheyret Kenji <gheyret@gmail.com>
767
768 M po/ug.po
769
770 commit 0594875c19b1bf981f1bbde416a1ca440c7caeb5
771 Author: Vincent Untz <vuntz@gnome.org>
772 Date: Mon Jan 7 16:36:24 2013 +0100
773
774 misc: Remove myself from maintainers
775
776 I'm not really doing anything on gnome-menus anymore, and Jasper is
777 happy being the sole maintainer.
778
779 M MAINTAINERS
780 M gnome-menus.doap
781
782 commit 9c1c50146b8d1a92ac556ba0a0ca174426bc6ffa
783 Author: Gheyret Kenji <gheyret@gmail.com>
784 Date: Fri Jan 4 21:38:40 2013 +0900
785
786 Uyghur translation
787
788 Signed-off-by: Gheyret Kenji <gheyret@gmail.com>
789
790 M po/ug.po
791
792 commit fd4a321b9eaf6ee11b80d0c377b8ff8873e6c777
793 Author: Khaled Hosny <khaledhosny@eglug.org>
794 Date: Mon Dec 24 13:38:26 2012 +0200
795
796 Update Arabic translation
797
798 M po/ar.po
799
800 commit 9d45c5369f4a486d2ba5c3a8c3262c84172b095a
801 Author: Alexandre Rostovtsev <tetromino@gentoo.org>
802 Date: Sat Nov 24 15:24:50 2012 -0500
803
804 libmenu: always call menu_layout_load() with non_prefixed_name parameter
805
806 We must ensure that when loading "${XDG_MENU_PREFIX}applications.menu"
807 or "applications.menu", the root layout node's name is set to "applications",
808 not "${XDG_MENU_PREFIX}applications", because the menu spec states that the
809 default merge directory for "${XDG_MENU_PREFIX}applications.menu" is
810 "applications-merged", not "${XDG_MENU_PREFIX}applications-merged".
811
812 https://bugzilla.gnome.org/show_bug.cgi?id=688972
813
814 M libmenu/gmenu-tree.c
815
816 commit dfae41dd0f0df6a72c1ef47dfe1e9efb1bfee746
817 Author: Shankar Prasad <svenkate@redhat.com>
818 Date: Thu Nov 29 14:24:44 2012 +0530
819
820 Updated kn translation
821
822 M po/kn.po
823
824 commit aad880ce305edc2fe61fb3070ec8df1d9d29f28a
825 Author: Shankar Prasad <svenkate@redhat.com>
826 Date: Thu Nov 29 14:21:37 2012 +0530
827
828 Updated kn translation
829
830 M po/kn.po
831
832 commit df21eb893653e0b3c0e0f43cd0bc0de00c28d54a
833 Author: Shankar Prasad <svenkate@redhat.com>
834 Date: Thu Nov 29 14:13:13 2012 +0530
835
836 Updated kn translation
837
838 M po/kn.po
839
840 commit 09ea3c32275547f8de8799bb47ef3f9441abf12f
841 Author: Shankar Prasad <svenkate@redhat.com>
842 Date: Tue Nov 27 16:00:31 2012 +0530
843
844 Updated kn translation
845
846 M po/kn.po
847
848 commit 6a113f67b993702417d9db189d36e63fd9e4806d
849 Author: Matthias Clasen <mclasen@redhat.com>
850 Date: Tue Nov 13 22:56:06 2012 -0500
851
852 Post-release version bump
853
854 M configure.ac
855
856 commit 0ebd911ccbd177b856d34f43a6ef680275ab0871
857 Author: Matthias Clasen <mclasen@redhat.com>
858 Date: Tue Nov 13 22:54:01 2012 -0500
859
860 3.6.1
861
862 M NEWS
863
864 commit fff8296aa0be3e257b2147e590da38cf470f4054
865 Author: Bahodir Mansurov <6ahodir@gmail.com>
866 Date: Sun Oct 14 13:42:00 2012 -0400
867
868 updating Uzbek@cyrillic translation
869
870 M po/uz@cyrillic.po
871
872 commit d9dce393a242ddc4799fdc776f0af305bf935ff5
873 Author: Bahodir Mansurov <6ahodir@gmail.com>
874 Date: Sun Oct 14 13:04:49 2012 -0400
875
876 updating Uzbek@cyrillic translation
877
878 M po/uz@cyrillic.po
879
880 commit 7acb8e2e5bff34c049c150d53190843996aa3f3d
881 Author: Pavol Klačanský <pavol@klacansky.com>
882 Date: Tue Oct 9 10:44:23 2012 +0100
883
884 Updated Slovak translation
885
886 M po/sk.po
887
888 commit 78b37e6142300931604566e2d8eb4123c70cfeb4
889 Author: Daniel Martinez Cucalon <dmartinez@src.gnome.org>
890 Date: Sat Sep 29 00:54:41 2012 +0200
891
892 Updated Aragonese translation
893
894 M po/an.po
895
896 commit 206da7e2fb133923f5898b42fbdc26db7f46d437
897 Author: Daniel Korostil <ted.korostiled@gmail.com>
898 Date: Wed Sep 26 09:15:57 2012 +0300
899
900 Added uk translation
901
902 M po/uk.po
903
904 commit 20f8d7169da967c39a92fb9bd97c74f5e1bb798d
905 Author: Carles Ferrando <carles.ferrando@gmail.com>
906 Date: Wed Sep 26 01:31:05 2012 +0200
907
908 [l10n] Updated Catalan (Valencian) translation
909
910 M po/ca@valencia.po
911
912 commit e8cda485fec0fb2480f8e232b790387c1f41c77f
913 Author: Gil Forcada <gforcada@gnome.org>
914 Date: Wed Sep 26 01:30:42 2012 +0200
915
916 [l10n] Updated Catalan translation
917
918 M po/ca.po
919
920 commit e2ad6f559992e253ee7b10db7358c709d3a54328
921 Author: Matthias Clasen <mclasen@redhat.com>
922 Date: Tue Sep 25 08:01:46 2012 -0400
923
924 post-release version bump
925
926 M configure.ac
927
928 commit ff379b515cdb3728dfd2f49c233797603b9658b8
929 Author: Matthias Clasen <mclasen@redhat.com>
930 Date: Tue Sep 25 07:54:10 2012 -0400
931
932 3.6.0
933
934 M NEWS
935
936 commit 1f596f13f5b793515dcb07cc7dd0834581f9f22e
937 Author: Rūdolfs Mazurs <rudolfsm@src.gnome.org>
938 Date: Mon Sep 24 13:22:34 2012 +0300
939
940 Updated Latvian translation
941
942 M po/lv.po
943
944 commit 3839fb3f2784f3575c454a7128fb7ff4fed12233
945 Author: Mattias Põldaru <mahfiaz@gmail.com>
946 Date: Mon Sep 24 11:15:01 2012 +0300
947
948 [l10n] Updated Estonian translation
949
950 M po/et.po
951
952 commit b2199a2b2972f37d85deab11818a94ce636a2d0a
953 Author: Timur Zhamakeev <ztimur@gmail.com>
954 Date: Mon Sep 24 09:21:40 2012 +0600
955
956 Updated Kyrgyz translation
957
958 M po/ky.po
959
960 commit d1ea18a03b123039f63e54360b5aac02d58e07fd
961 Author: Runa Bhattacharjee <runab@redhat.com>
962 Date: Fri Sep 21 19:23:07 2012 +0530
963
964 Updated Bengali India Translation
965
966 M po/bn_IN.po
967
968 commit 4883507b4742178707f227922f421e130ae0e612
969 Author: Rajesh Ranjan <rranjan@redhat.com>
970 Date: Fri Sep 21 14:21:36 2012 +0530
971
972 hindi update
973
974 M po/hi.po
975
976 commit 319bc9001ec45d837da239a4dbd1cf30546ba4ce
977 Author: Praveen Illa <mail2ipn@gmail.com>
978 Date: Wed Sep 19 22:08:18 2012 +0530
979
980 Updated Telugu Translation
981
982 M po/te.po
983
984 commit dd8991b15488f384122120ea31e8c6043e0144ea
985 Author: Ani Peter <apeter@redhat.com>
986 Date: Tue Sep 18 23:04:10 2012 +0530
987
988 Updated Malayalam file
989
990 M po/ml.po
991
992 commit e24c669b5331a6deab6730c0bb7f582ad98c138c
993 Author: Noriko Mizumoto <noriko@fedoraproject.org>
994 Date: Tue Sep 18 12:57:52 2012 +0900
995
996 [l10n] Update Japanese translation
997
998 M po/ja.po
999
1000 commit d529600f891fdd32d111e281a65677c9468d8f5c
1001 Author: Djavan Fagundes <djavanf@gnome.org>
1002 Date: Mon Sep 17 21:28:13 2012 -0300
1003
1004 Revert "Updated Brazilian Portuguese translation"
1005
1006 This reverts commit 0b8278f3dc7f834b5856a98762cda69b0d713707.
1007
1008 A po/pt_BR.po
1009
1010 commit 0b8278f3dc7f834b5856a98762cda69b0d713707
1011 Author: Djavan Fagundes <djavanf@gnome.org>
1012 Date: Mon Sep 17 20:40:04 2012 -0300
1013
1014 Updated Brazilian Portuguese translation
1015
1016 D po/pt_BR.po
1017
1018 commit 0619ec60ce602e023beb9d14df34d4958d326a31
1019 Author: Og B. Maciel <ogmaciel@gnome.org>
1020 Date: Mon Sep 17 16:51:48 2012 -0400
1021
1022 Updated translation for Brazilian Portuguese.
1023
1024 M po/pt_BR.po
1025
1026 commit 4ea3f1de69fec378262bc966f5d3e355f82c2b20
1027 Author: Vincent Untz <vuntz@gnome.org>
1028 Date: Mon Sep 17 13:10:55 2012 +0200
1029
1030 release: post-release bump to 3.6.0
1031
1032 M configure.ac
1033
1034 commit a4e3e70fb8150461af79a5314cdd3b9a8fe5956a
1035 Author: Vincent Untz <vuntz@gnome.org>
1036 Date: Mon Sep 17 13:10:14 2012 +0200
1037
1038 release: 3.5.92
1039
1040 M NEWS
1041 M configure.ac
1042
1043 commit c21dc9a2fbc2fbf288de0d688afd519e2f25da4b
1044 Author: Ask H. Larsen <asklarsen@gmail.com>
1045 Date: Sun Sep 16 13:25:58 2012 +0200
1046
1047 Updated Danish translation
1048
1049 M po/da.po
1050
1051 commit fe09bdbae726dbefa92f22d2e851b7ec5b30dfef
1052 Author: Changwoo Ryu <cwryu@debian.org>
1053 Date: Sat Sep 15 16:55:29 2012 +0900
1054
1055 Updated Korean translation
1056
1057 M po/ko.po
1058
1059 commit aceac14e0b728626c966d3ccfab51bb99a396ba6
1060 Author: Rico Tzschichholz <ricotz@t-online.de>
1061 Date: Wed Sep 12 08:40:37 2012 +0200
1062
1063 libmenu: Add proper header reference to GMenu-3.0.gir
1064
1065 M libmenu/Makefile.am
1066
1067 commit 491c7eccff4e5fedc3f68f2d9d33399d49eed9a8
1068 Author: Marek Černocký <marek@manet.cz>
1069 Date: Mon Sep 10 00:47:09 2012 +0200
1070
1071 Czech translation
1072
1073 M po/cs.po
1074
1075 commit 2c2c4f24c60952ef3e014ce0fd24c9db83c7f3fb
1076 Author: Milo Casagrande <milo@ubuntu.com>
1077 Date: Thu Sep 6 22:51:48 2012 +0200
1078
1079 [l10n] Updated Italian translation.
1080
1081 M po/it.po
1082
1083 commit f33d2b42226ae7c0d2fe871b55563b544dfcd252
1084 Author: Arash Mousavi <mousavi.arash@gmail.com>
1085 Date: Thu Sep 6 21:02:51 2012 +0430
1086
1087 l10n: Updated Persian translation
1088
1089 M po/fa.po
1090
1091 commit db8230cb5375eb951fc3385fa56e0fbd20222030
1092 Author: Theppitak Karoonboonyanan <thep@linux.thai.net>
1093 Date: Thu Sep 6 20:23:58 2012 +0700
1094
1095 Updated Thai translation.
1096
1097 M po/th.po
1098
1099 commit b2cd372cf3ef3d5e10ca7e724366c72322a44849
1100 Author: Timo Jyrinki <timo@debian.org>
1101 Date: Thu Sep 6 08:59:09 2012 +0300
1102
1103 Finnish translation update by Jiri Grönroos
1104
1105 M po/fi.po
1106
1107 commit 103fb06fe86990bc7ec95e7cd8f61b71b69f9d9a
1108 Author: Bruce Cowan <bruce@bcowan.me.uk>
1109 Date: Wed Sep 5 19:50:45 2012 +0100
1110
1111 Updated British English translation
1112
1113 M po/en_GB.po
1114
1115 commit a8f755a4b967738261540d2efd4c3ab45a9b283b
1116 Author: Alexandre Franke <alexandre.franke@gmail.com>
1117 Date: Tue Sep 4 20:52:48 2012 +0200
1118
1119 Update French translation
1120
1121 M po/fr.po
1122
1123 commit b4c27cda6d161dbffb3878bfb1fd9164bb292de7
1124 Author: Gabor Kelemen <kelemeng@gnome.hu>
1125 Date: Tue Sep 4 13:49:16 2012 +0200
1126
1127 Updated Hungarian translation
1128
1129 M po/hu.po
1130
1131 commit c66d0da3965cb3157d08b33278220c2d8d52c5f6
1132 Author: Muhammet Kara <muhammetk@acikkaynak.name.tr>
1133 Date: Tue Sep 4 06:48:19 2012 +0300
1134
1135 [l10n]Updated Turkish translation
1136
1137 M po/tr.po
1138
1139 commit 691760dbea68b46aa6b895f0d6c739302c372806
1140 Author: Piotr Drąg <piotrdrag@gmail.com>
1141 Date: Mon Sep 3 19:09:33 2012 +0200
1142
1143 Updated Polish translation
1144
1145 M po/pl.po
1146
1147 commit a797159ac6b20b5e608290731ff0c9ccc1792848
1148 Author: Dr.T.Vasudevan <agnihot3@gmail.com>
1149 Date: Sun Sep 2 18:02:37 2012 +0530
1150
1151 updated Tamil translation
1152
1153 M po/ta.po
1154
1155 commit a44c335288e7c50db6d95bb080329bb1151d40a7
1156 Author: Daniel Nylander <po@danielnylander.se>
1157 Date: Sun Sep 2 11:59:21 2012 +0200
1158
1159 Updated Swedish translation
1160
1161 M po/sv.po
1162
1163 commit 8a09d2aad657036cb91c6d26fc370fd3a7be3d80
1164 Author: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
1165 Date: Sat Sep 1 22:04:28 2012 +0700
1166
1167 Updated Vietnamese translation
1168
1169 M po/vi.po
1170
1171 commit c126f269866ed323926841157c72c48df5753a99
1172 Author: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
1173 Date: Sat Sep 1 22:01:50 2012 +0700
1174
1175 po/vi: import from Damned Lies
1176
1177 M po/vi.po
1178
1179 commit d2b6d0a7e02d36125a455b99f2a43fb2598fc7cc
1180 Author: Duarte Loreto <happyguy_pt@hotmail.com>
1181 Date: Sat Sep 1 01:17:02 2012 +0100
1182
1183 Updated Portuguese translation
1184
1185 M po/pt.po
1186
1187 commit 6cbb7189d43207cc7abb5709255ddf8a6d5e4562
1188 Author: Aurimas Černius <aurisc4@gmail.com>
1189 Date: Wed Aug 29 22:57:58 2012 +0300
1190
1191 Updated Lithuanian translation
1192
1193 M po/lt.po
1194
1195 commit 55149c45571e54ed59599429e5c131fd64789d80
1196 Author: Chrovex Fan <pixelnecro@foxmail.com>
1197 Date: Tue Aug 28 22:32:44 2012 +0800
1198
1199 update Simplified Chinese (zh_CN) translation
1200
1201 M po/zh_CN.po
1202
1203 commit 2bfb0ab9c702b811d26c5adbd276eebdf0e04592
1204 Author: Nilamdyuti Goswami <nilamdyuti@gmail.com>
1205 Date: Tue Aug 28 00:03:50 2012 +0530
1206
1207 Implemented FUEL entries to Assamese translation
1208
1209 M po/as.po
1210
1211 commit 387804f05c1c6a8648f3930b45df939bf1b684f2
1212 Author: A S Alam <aalam@users.sf.net>
1213 Date: Mon Aug 27 19:58:58 2012 +0530
1214
1215 update Punjabi Translation
1216
1217 M po/pa.po
1218
1219 commit 3be4bc499b3df622c149f89780ad8ffc8c67319a
1220 Author: Piotr Drąg <piotrdrag@gmail.com>
1221 Date: Thu Aug 23 03:24:55 2012 +0200
1222
1223 Updated Polish translation
1224
1225 M po/pl.po
1226
1227 commit 1682e39a113f5ea19e1fd8d0da5fb27db6a95274
1228 Author: Dirgita <dirgitadevina@yahoo.co.id>
1229 Date: Tue Aug 21 22:20:32 2012 +0700
1230
1231 Updated Indonesian translation
1232
1233 M po/id.po
1234
1235 commit 0ed913bb8276b06e3f305b5e1907d87cc23d906a
1236 Author: Fran Diéguez <fran.dieguez@mabishu.com>
1237 Date: Fri Aug 17 23:37:20 2012 +0200
1238
1239 Updated Galician translations
1240
1241 M po/gl.po
1242
1243 commit d0e752350b2a9d8f85845614fda54571e143cef5
1244 Author: Sandeep Sheshrao Shedmake <sshedmak@redhat.com>
1245 Date: Thu Aug 16 12:19:50 2012 +0530
1246
1247 Updated Marathi Translations
1248
1249 M po/mr.po
1250
1251 commit 37f8b405844aedc87169ee902a8ed150978ffb1a
1252 Author: Jasper St. Pierre <jstpierre@mecheye.net>
1253 Date: Mon Aug 6 18:42:20 2012 -0300
1254
1255 Post-release version bump
1256
1257 M configure.ac
1258
1259 commit 42ace8382ab15914f55c7dc6dffab418b38e0e50
1260 Author: Jasper St. Pierre <jstpierre@mecheye.net>
1261 Date: Mon Aug 6 18:36:56 2012 -0300
1262
1263 Release 3.5.5
1264
1265 M NEWS
1266
1267 commit 685323a7cae56b1ced25c10f26af8eda7c9c893a
1268 Author: Мирослав Николић <miroslavnikolic@rocketmail.com>
1269 Date: Mon Aug 6 11:30:07 2012 +0200
1270
1271 Updated Serbian translation
1272
1273 M po/sr.po
1274 M po/sr@latin.po
1275
1276 commit b0c0c0afbc6a563f987df95e508c66c881bb5c27
1277 Author: Sweta Kothari <swkothar@redhat.com>
1278 Date: Mon Jul 30 13:54:26 2012 +0530
1279
1280 Updated gujarati file
1281
1282 M po/gu.po
1283
1284 commit 70f681351441c9146e1e4eb1c48154c5db1d2a5b
1285 Author: Przemysław Buczkowski <przemub@yahoo.pl>
1286 Date: Thu Jul 26 19:41:14 2012 +0200
1287
1288 Added Silesian translation
1289
1290 M po/LINGUAS
1291 A po/szl.po
1292
1293 commit 09053a26df6431e32d706eda69c0b7bf9100994b
1294 Author: Baurzhan Muftakhidinov <baurthefirst@gmail.com>
1295 Date: Thu Jul 26 09:20:18 2012 +0600
1296
1297 Updated Kazakh translation
1298
1299 M po/kk.po
1300
1301 commit 73b36491c3a472c67a15f1684893d080c743c601
1302 Author: Tobias Endrigkeit <tobiasendrigkeit@googlemail.com>
1303 Date: Mon Jul 23 22:43:15 2012 +0200
1304
1305 Updated German translation
1306
1307 M po/de.po
1308
1309 commit 985728566f914aa8a3a3c571dd203ee4b50827f3
1310 Author: Chao-Hsiung Liao <j_h_liau@yahoo.com.tw>
1311 Date: Tue Jul 17 15:01:09 2012 +0800
1312
1313 Updated Traditional Chinese translation(Hong Kong and Taiwan)
1314
1315 M po/zh_HK.po
1316 M po/zh_TW.po
1317
1318 commit 50f09c92d3418043cbead8ccc014235ab3eb9bd3
1319 Author: Jasper St. Pierre <jstpierre@mecheye.net>
1320 Date: Mon Jul 16 20:24:57 2012 -0400
1321
1322 Post-release version bump
1323
1324 M configure.ac
1325
1326 commit f8e4cb42e10b7c2666f3771da158646a145abd34
1327 Author: Jasper St. Pierre <jstpierre@mecheye.net>
1328 Date: Mon Jul 16 20:24:15 2012 -0400
1329
1330 Release 3.5.4
1331
1332 M NEWS
1333
1334 commit 1e62d7d67525ea5628296435bd95ef325954786d
1335 Author: chingiz <chingis091@lavabit.com>
1336 Date: Mon Jul 16 11:36:35 2012 +0400
1337
1338 Updated Kirghiz translation
1339
1340 M po/ky.po
1341
1342 commit b3f0c47b46b5456d220b7cb8b384a52e71018fa3
1343 Author: Alexander Shopov <ash@kambanaria.org>
1344 Date: Wed Jul 4 07:17:08 2012 +0300
1345
1346 Updated Bulgarian translation
1347
1348 M po/bg.po
1349
1350 commit 2c31301300f13a4ced24a0b116a010f9e20b0756
1351 Author: Jasper St. Pierre <jstpierre@mecheye.net>
1352 Date: Tue Jun 26 13:19:56 2012 -0400
1353
1354 Post-release version bump
1355
1356 M configure.ac
1357
1358 commit 78ac358fcb3135574377cea00781edd19bad6065
1359 Author: Jasper St. Pierre <jstpierre@mecheye.net>
1360 Date: Tue Jun 26 13:18:33 2012 -0400
1361
1362 Release 3.5.3
1363
1364 M NEWS
1365
1366 commit 1fba5a23c30af1a8cd07a6bd2f85e3c805129813
1367 Author: Sasi Bhushan Boddepalli <sasi@swecha.net>
1368 Date: Thu Jun 21 14:27:51 2012 +0530
1369
1370 Updated Telugu Translation
1371
1372 M po/te.po
1373
1374 commit 4a00f1eb30bc33afc14e647efb6ec1450ca7ff51
1375 Author: Nilamdyuti Goswami <nilamdyuti@gmail.com>
1376 Date: Thu Jun 21 14:01:14 2012 +0530
1377
1378 Assamese translation reviewed
1379
1380 M po/as.po
1381
1382 commit d1beaf075f02281e28bb730f3e43dd15dfea2ef3
1383 Author: Jasper St. Pierre <jstpierre@mecheye.net>
1384 Date: Wed Jun 20 14:57:58 2012 -0400
1385
1386 libmenu: Allow grabbing the parent from any GMenuTreeItem
1387
1388 Oh how I wish we had real inheritance.
1389
1390 M libmenu/gmenu-tree.c
1391 M libmenu/gmenu-tree.h
1392
1393 commit ade49e7f07ef6cb0168002f6c12a4be6c1bdbee8
1394 Author: Jasper St. Pierre <jstpierre@mecheye.net>
1395 Date: Mon Jun 18 18:08:26 2012 -0400
1396
1397 libmenu: Add a way to grab NoDisplay on an item and all of its parents
1398
1399 Some applications may want a way to show or hide a tree entry based on
1400 its visibility in the actual tree.
1401
1402 https://bugzilla.gnome.org/show_bug.cgi?id=678419
1403
1404 M libmenu/gmenu-tree.c
1405 M libmenu/gmenu-tree.h
1406
1407 commit fef1440c7b440d0360992c2178ef636b02ef777a
1408 Author: Jasper St. Pierre <jstpierre@mecheye.net>
1409 Date: Fri Jun 1 09:16:01 2012 -0400
1410
1411 libmenu: Allow grabbing the GMenuTree from any GMenuTreeItem
1412
1413 https://bugzilla.gnome.org/show_bug.cgi?id=677270
1414
1415 M libmenu/gmenu-tree.c
1416 M libmenu/gmenu-tree.h
1417
1418 commit eec6221af6a8ee04dddb11af9dbac45819a296db
1419 Author: Vincent Untz <vuntz@gnome.org>
1420 Date: Wed Jun 20 19:56:20 2012 +0200
1421
1422 misc: Add Jasper as co-maintainer
1423
1424 Jasper is completely fixing alacarte, so he's the menu guy now :-)
1425
1426 M MAINTAINERS
1427 M gnome-menus.doap
1428
1429 commit 7a87b12a86d7f8dfebca2a33791c04d5249de692
1430 Author: Tom Tryfonidis <tomtryf@gmail.com>
1431 Date: Tue Jun 19 19:26:24 2012 +0300
1432
1433 Updated Greek translation
1434
1435 M po/el.po
1436
1437 commit 8cd6efdf03a7056ed4c7373e00a2eb7657e5c6cc
1438 Author: Jasper St. Pierre <jstpierre@mecheye.net>
1439 Date: Sat Jun 2 13:27:24 2012 -0400
1440
1441 libmenu: Add the forgotten gmenu_tree_iter_get_separator
1442
1443 https://bugzilla.gnome.org/show_bug.cgi?id=677344
1444
1445 M libmenu/gmenu-tree.c
1446 M libmenu/gmenu-tree.h
1447
1448 commit 2a83843d381cebf95a775a9ae2c176c5ae816fc8
1449 Author: Colin Walters <walters@verbum.org>
1450 Date: Fri Jun 8 16:34:12 2012 -0400
1451
1452 gnome-menus: Fix g-ir-scanner warnings
1453
1454 M libmenu/gmenu-tree.c
1455
1456 commit 4973cf303cb9d5e8b160fb1ac0649ac2f864b8b4
1457 Author: Ihar Hrachyshka <ihar.hrachyshka@gmail.com>
1458 Date: Fri Jun 8 18:55:01 2012 +0300
1459
1460 Updated Belarusian translation.
1461
1462 M po/be.po
1463
1464 commit 175f03aeef932bf9bf0527e11624e9a8e126e769
1465 Author: Matthias Clasen <mclasen@redhat.com>
1466 Date: Tue Jun 5 09:21:45 2012 -0400
1467
1468 Post-release version bump
1469
1470 M configure.ac
1471
1472 commit c43bc6bc305b156be066fba6ae385b46e0063409
1473 Author: Matthias Clasen <mclasen@redhat.com>
1474 Date: Tue Jun 5 09:17:34 2012 -0400
1475
1476 3.5.2
1477
1478 M NEWS
1479 M configure.ac
1480
1481 commit 326c6ca76a4a739bb3d669ed4ce2d36d9b89eabe
1482 Author: Kjartan Maraas <kmaraas@gnome.org>
1483 Date: Wed May 30 14:06:31 2012 +0200
1484
1485 Updated Norwegian bokmål translation
1486
1487 M po/nb.po
1488
1489 commit efc14ec052a53b706d8d2132ec62426cd46f54ea
1490 Author: Aleksej Kabanov <ak099@mail.ru>
1491 Date: Sun May 27 00:56:26 2012 +0400
1492
1493 Updated Russian translation
1494
1495 M po/ru.po
1496
1497 commit 4693fdd844b623a69e02ec853f90888aee82a728
1498 Author: Yaron Shahrabani <sh.yaron@gmail.com>
1499 Date: Fri May 25 15:29:40 2012 +0300
1500
1501 Updated Hebrew translation.
1502
1503 M po/he.po
1504
1505 commit ef10032c3c9d20cb46482829f7bbf5e09c311dee
1506 Author: Matej Urbančič <mateju@svn.gnome.org>
1507 Date: Wed May 23 20:02:27 2012 +0200
1508
1509 Updated Slovenian translation
1510
1511 M po/sl.po
1512
1513 commit 64606ea5a3c0f3faa5f406a63c53a51a1eb8823a
1514 Author: Fran Diéguez <fran.dieguez@mabishu.com>
1515 Date: Sun May 20 17:57:35 2012 +0200
1516
1517 Updated Galician translations
1518
1519 M po/gl.po
1520
1521 commit 068bfbdbcb328c0f7eb29faecaf41c82e5633bb5
1522 Author: Daniel Mustieles <daniel.mustieles@gmail.com>
1523 Date: Mon May 14 16:49:12 2012 +0200
1524
1525 Updated Spanish translation
1526
1527 M po/es.po
1528
1529 commit 9452548505add051e3ca099332edad2dc7764181
1530 Author: Giovanni Campagna <gcampagna@src.gnome.org>
1531 Date: Tue May 1 00:23:39 2012 +0200
1532
1533 layout: Add a separate category for Web Applications
1534
1535 For technical reasons, it is not possible to guess an appropriate
1536 category for web applications created with Epiphany, so they
1537 would all end up in Others. Instead, make up a category and submenu
1538 just for them.
1539
1540 https://bugzilla.gnome.org/show_bug.cgi?id=675198
1541
1542 M desktop-directories/Makefile.am
1543 A desktop-directories/X-GNOME-WebApplications.directory.in
1544 M layout/applications.menu
1545 M po/POTFILES.in
1546
1547 commit b93cc6818c7ee7be1339f0f7bf8d3ec24790df59
1548 Author: Vincent Untz <vuntz@gnome.org>
1549 Date: Thu Feb 2 10:19:02 2012 +0100
1550
1551 util: Add --include-unallocated option to gnome-menu-spec-test
1552
1553 We want easy testing for GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED.
1554
1555 https://bugzilla.gnome.org/show_bug.cgi?id=668512
1556
1557 M util/test-menu-spec.c
1558
1559 commit 159bfe766f024598e967765a418430dfa46d2197
1560 Author: Vincent Untz <vuntz@gnome.org>
1561 Date: Thu Feb 2 10:16:40 2012 +0100
1562
1563 libmenu: Add GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED flag
1564
1565 Add a new GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED flag to add in the root
1566 directory entries that are not allocated anywhere else. This is useful
1567 if the user really wants to get absolutely all entries (in addition to
1568 using INCLUDE_EXCLUDED, which is a bit different, and
1569 INCLUDE_NODISPLAY).
1570
1571 Add gmenu_tree_entry_get_is_unallocated() API matching this flag.
1572
1573 https://bugzilla.gnome.org/show_bug.cgi?id=668512
1574
1575 M libmenu/gmenu-tree.c
1576 M libmenu/gmenu-tree.h
1577
1578 commit 6b6956b75de4566772c7144a01b39634671b364f
1579 Author: Piotr Drąg <piotrdrag@gmail.com>
1580 Date: Mon Apr 16 19:15:35 2012 +0200
1581
1582 Added Kashubian translation
1583
1584 M po/LINGUAS
1585 A po/csb.po
1586
1587 commit 9d68b854b35573d5567fe79cc58d011ae23ed23e
1588 Author: Alexander Shopov <ash@kambanaria.org>
1589 Date: Sun Apr 1 18:11:32 2012 +0300
1590
1591 Updated Bulgarian translation
1592
1593 M po/bg.po
1594
1595 commit da8b0ff0c8733d8f94d7e29f1ba67ff2e9bace98
1596 Author: Vincent Untz <vuntz@gnome.org>
1597 Date: Mon Mar 26 10:37:18 2012 +0200
1598
1599 release: post-release bump to 3.4.1
1600
1601 M configure.ac
1602
1603 commit e1b3578220871cd5e607a93ab1b251da9e56d4d6
1604 Author: Vincent Untz <vuntz@gnome.org>
1605 Date: Mon Mar 26 10:36:57 2012 +0200
1606
1607 release: 3.4.0
1608
1609 M NEWS
1610 M configure.ac
1611
1612 commit f553195fee927f128963702fc9443ce6d84fb20b
1613 Author: Khoem Sokhem <khoemsokhem@khmeros.info>
1614 Date: Sat Mar 10 13:24:57 2012 +0100
1615
1616 Add initial Khmer translation
1617
1618 M po/LINGUAS
1619 A po/km.po
1620
1621 commit f289d086d2f3162bef06eada7276cd473f73fbfe
1622 Author: Bahodir Mansurov <6ahodir@gmail.com>
1623 Date: Fri Feb 24 21:24:22 2012 -0500
1624
1625 Updated Uzbek@cyrillic translation
1626
1627 M po/uz@cyrillic.po
1628
1629 commit 17b1dc3a3c850794bfe3df028ed72483fb21489e
1630 Author: Vincent Untz <vuntz@gnome.org>
1631 Date: Wed Feb 22 15:10:35 2012 +0100
1632
1633 build: Generate ChangeLog on make dist
1634
1635 M Makefile.am
1636
1637 commit ac467f5484a8c32ccb1484147003dbea8ee51d99
1638 Author: Vincent Untz <vuntz@gnome.org>
1639 Date: Wed Feb 22 15:03:08 2012 +0100
1640
1641 build: Update git.mk and ignore generated tarballs
1642
1643 M Makefile.am
1644 M git.mk
1645
1646 commit 82f575edbd949f682bc9242263e318f7e1d4d57c
1647 Author: Vincent Untz <vuntz@gnome.org>
1648 Date: Mon Feb 6 12:27:00 2012 +0100
1649
1650 release: post-release bump to 3.3.90
1651
1652 M configure.ac
1653
1654 commit 44030054b1752ea353c010ec0588fbc804ffc662
1655 Author: Vincent Untz <vuntz@gnome.org>
1656 Date: Mon Feb 6 12:26:54 2012 +0100
1657
1658 release: 3.3.5
1659
1660 M NEWS
1661 M configure.ac
1662
1663 commit 2adf8dc80b99459450e891161b3b7f254317d9b5
1664 Author: Danishka Navin <danishka@gmail.com>
1665 Date: Wed Feb 1 17:29:09 2012 +0530
1666
1667 fixed few typos in si.po file
1668
1669 M po/si.po
1670
1671 commit 17d5a7f7942f9045acdb8d5cbe18c967169d054e
1672 Author: Danishka Navin <danishka@gmail.com>
1673 Date: Wed Feb 1 16:08:30 2012 +0530
1674
1675 fixed few typos in si.po file
1676
1677 M po/si.po
1678
1679 commit 7b895320a73ef066508d0d411c9ce1e725188a96
1680 Author: Kjartan Maraas <kmaraas@gnome.org>
1681 Date: Wed Jan 25 14:47:48 2012 +0100
1682
1683 Updated Norwegian bokmål translation
1684
1685 M po/nb.po
1686
1687 commit a84c7d360b4cb22a10cac76c95eff4654effd730
1688 Author: Andiswa Mvanyashe <andiswamva@webmail.co.za>
1689 Date: Tue Jan 17 08:09:02 2012 +0200
1690
1691 Updated translation for Xhosa (xh)
1692
1693 M po/xh.po
1694
1695 commit 91a67746970ca4327c03c2f048e48c666a1f3776
1696 Author: Vincent Untz <vuntz@gnome.org>
1697 Date: Tue Dec 20 09:45:45 2011 +0100
1698
1699 build: Create xz tarballs
1700
1701 M configure.ac
1702
1703 commit 581bdf9920d18024413fa7afcacd83e733a26089
1704 Author: Jiro Matsuzawa <jmatsuzawa@src.gnome.org>
1705 Date: Fri Nov 11 02:51:39 2011 +0900
1706
1707 Updated Japanese translation
1708
1709 M po/ja.po
1710
1711 commit 8d46c3a20c4718384b66180990258c0ad6c12f49
1712 Author: Vincent Untz <vuntz@gnome.org>
1713 Date: Mon Oct 24 13:58:21 2011 +0200
1714
1715 release: post-release bump to 3.3.2
1716
1717 M configure.ac
1718
1719 commit f258c9c144c569772a1ea7ea81179c98110fc58f
1720 Author: Vincent Untz <vuntz@gnome.org>
1721 Date: Mon Oct 24 13:58:14 2011 +0200
1722
1723 release: 3.3.1
1724
1725 M NEWS
1726 M configure.ac
1727
1728 commit fbabc41cb6f2c6520f9f117137fa296a7f8340f3
1729 Author: Florian Müllner <fmuellner@gnome.org>
1730 Date: Fri Oct 21 19:57:55 2011 +0200
1731
1732 libmenu: Ignore invalid desktop entries
1733
1734 Both "Name" and "Exec" are mandatory keys according to the desktop
1735 entry spec; some .desktop files missing one or the other have been
1736 spotted in the while, so ignore them explicitly.
1737
1738 https://bugzilla.gnome.org/show_bug.cgi?id=662409
1739
1740 M libmenu/desktop-entries.c
1741
1742 commit 2179d84e1d7ce5a5b79f2736bf64a13e7bca1b4d
1743 Author: Vincent Untz <vuntz@gnome.org>
1744 Date: Fri Oct 7 10:43:02 2011 +0200
1745
1746 layout: Put the Other menu at the end
1747
1748 It's really a special menu, which should not be handled with
1749 alphabetical order.
1750
1751 M layout/applications.menu
1752
1753 commit 480edac95baba553b3cfc471817779e50a367871
1754 Author: Vincent Untz <vuntz@gnome.org>
1755 Date: Wed Sep 28 09:24:19 2011 +0200
1756
1757 release: post-release bump to 3.2.1
1758
1759 M configure.ac
1760
1761 commit 2d5915b925791d2d3bd736704a8706553455bd26
1762 Author: Vincent Untz <vuntz@gnome.org>
1763 Date: Wed Sep 28 09:24:11 2011 +0200
1764
1765 release: 3.2.0.1
1766
1767 M NEWS
1768 M configure.ac
1769
1770 commit f1c76629d33a616089a15bd034708d21e8bd9a87
1771 Author: Vincent Untz <vuntz@gnome.org>
1772 Date: Tue Sep 27 12:09:35 2011 +0200
1773
1774 editor: Fix to work with latest pygi
1775
1776 https://bugzilla.gnome.org/show_bug.cgi?id=660112
1777
1778 M simple-editor/GMenuSimpleEditor/main.py
1779 M simple-editor/GMenuSimpleEditor/menufilewriter.py
1780 M simple-editor/GMenuSimpleEditor/menutreemodel.py
1781
1782 commit b87a8c15e95fa64c94343b0bfdc6523e8f6c1bbc
1783 Author: Vincent Untz <vuntz@gnome.org>
1784 Date: Mon Sep 26 11:09:13 2011 +0200
1785
1786 release: post-release bump to 3.2.1
1787
1788 M configure.ac
1789
1790 commit 4b0e41e9b45087402e64d90c2760c8d2101cd53e
1791 Author: Vincent Untz <vuntz@gnome.org>
1792 Date: Mon Sep 26 11:09:08 2011 +0200
1793
1794 release: 3.2.0
1795
1796 M NEWS
1797
1798 commit 36f7db2473f1d6a0c7a1b1e05e056210ac87e80b
1799 Author: Nilamdyuti Goswami <ngoswami@redhat.com>
1800 Date: Sat Sep 24 15:27:52 2011 +0530
1801
1802 Updated Assamese Translations:bugzilla#659595
1803
1804 M po/as.po
1805
1806 commit 1412cf2222ef8e0f33e9bfa234cf7fdbf6aee035
1807 Author: ipraveen <mail2ipn@gmail.com>
1808 Date: Fri Sep 23 16:32:28 2011 +0530
1809
1810 Updated Telugu Translation
1811
1812 M po/te.po
1813
1814 commit 3ecf6975346ab77b111a99c45b0f7a79228b202f
1815 Author: ipraveen <mail2ipn@gmail.com>
1816 Date: Fri Sep 23 16:26:27 2011 +0530
1817
1818 Updated Telugu Translation
1819
1820 M po/te.po
1821
1822 commit 26e1dd8864b18c5dee038a929bc23cfcadc0f6e1
1823 Author: Nilamdyuti Goswami <ngoswami@redhat.com>
1824 Date: Thu Sep 22 20:17:41 2011 +0200
1825
1826 Updated Assamese translation
1827
1828 M po/as.po
1829
1830 commit 612011be0953e15f349946ba48df5e6e8f036282
1831 Author: Vincent Untz <vuntz@gnome.org>
1832 Date: Mon Sep 19 19:34:28 2011 +0200
1833
1834 release: post-release bump to 3.2.0
1835
1836 M configure.ac
1837
1838 commit 5d7dd75fbf694cfd31f04a3f81c80786b1b5841d
1839 Author: Vincent Untz <vuntz@gnome.org>
1840 Date: Mon Sep 19 19:34:21 2011 +0200
1841
1842 release: 3.1.92
1843
1844 M NEWS
1845 M configure.ac
1846
1847 commit 49406208c5b1bfe449ed46c20d0bf16c7a363518
1848 Author: Ihar Hrachyshka <ihar.hrachyshka@gmail.com>
1849 Date: Sun Sep 18 18:22:42 2011 +0300
1850
1851 Updated Belarusian translation (some fixes after manual testing).
1852
1853 M po/be.po
1854
1855 commit c3cc2e1d2dfa4f219c01c8dfee4d14e8aebd3a1e
1856 Author: Jiro Matsuzawa <jmatsuzawa@src.gnome.org>
1857 Date: Sat Sep 17 12:08:06 2011 +0900
1858
1859 Updated Japanese translation
1860
1861 M po/ja.po
1862
1863 commit 8c4aa57ca8ff56f1941899eb3c67babd2b7a3839
1864 Author: dmustieles <daniel.mustieles@gmail.com>
1865 Date: Wed Sep 14 17:37:05 2011 +0200
1866
1867 Updated Spanish translation
1868
1869 M po/es.po
1870
1871 commit ecb47adb12c8ec9a3998efa96656dfedfe002525
1872 Author: Vincent Untz <vuntz@gnome.org>
1873 Date: Mon Aug 29 08:43:26 2011 +0200
1874
1875 libmenu: Fix build failure with --enable-debug
1876
1877 gmenu_tree_entry_get_name() was still being used.
1878
1879 https://bugzilla.gnome.org/show_bug.cgi?id=656714
1880
1881 M libmenu/gmenu-tree.c
1882
1883 commit 20d76fb2f1664aadf0b33c7c91603b47dfe04ef0
1884 Author: Matthias Clasen <mclasen@redhat.com>
1885 Date: Sun Aug 28 19:22:26 2011 -0400
1886
1887 Post-release version bump
1888
1889 M configure.ac
1890
1891 commit 002662a50ab7fbce7398596a53e7270fc6f168e3
1892 Author: Matthias Clasen <mclasen@redhat.com>
1893 Date: Sun Aug 28 19:22:00 2011 -0400
1894
1895 3.1.90
1896
1897 M NEWS
1898
1899 commit 739d3f6ef7d533544f1ec2b4d6e237bfaca99e85
1900 Author: Jasper St. Pierre <jstpierre@mecheye.net>
1901 Date: Sun Aug 21 17:27:50 2011 -0400
1902
1903 libmenu: Don't try to unref potentially NULL pointers
1904
1905 When a DIRECTORY desktop entry fails to load, we'll try to unref
1906 some NULL pointers. This was causing some warnings.
1907
1908 https://bugzilla.gnome.org/show_bug.cgi?id=657042
1909
1910 M libmenu/desktop-entries.c
1911
1912 commit 5fade40dc3ebff3b4c00cdf37e6d7198e39e9bce
1913 Author: Vincent Untz <vuntz@gnome.org>
1914 Date: Fri Aug 12 11:50:59 2011 +0200
1915
1916 release: post-release bump to 3.1.90
1917
1918 M configure.ac
1919
1920 commit 8e0ecffe8a6d4434dab086525f52d05e00edf0e9
1921 Author: Vincent Untz <vuntz@gnome.org>
1922 Date: Fri Aug 12 11:50:52 2011 +0200
1923
1924 release: 3.1.5
1925
1926 M NEWS
1927 M configure.ac
1928
1929 commit 61c87a8e66bfa1d7a823a588b073e3922edc78c3
1930 Author: Vincent Untz <vuntz@gnome.org>
1931 Date: Fri Aug 12 10:44:32 2011 +0200
1932
1933 libmenu: Support XDG_CURRENT_DESKTOP
1934
1935 https://bugzilla.gnome.org/show_bug.cgi?id=653440
1936
1937 M libmenu/desktop-entries.c
1938
1939 commit f0e41152be32a3e07772b53a6c3a0d4dbb0545d9
1940 Author: Colin Walters <walters@verbum.org>
1941 Date: Tue Aug 2 23:13:30 2011 -0400
1942
1943 gmenu_tree_get_entry_by_id: New API
1944
1945 It's useful for gnome-shell if we have an index by desktop file ID to
1946 the tree entry. The cost is very small.
1947
1948 https://bugzilla.gnome.org/show_bug.cgi?id=655868
1949
1950 M libmenu/gmenu-tree.c
1951 M libmenu/gmenu-tree.h
1952
1953 commit 2a17c07927cfc23c9837969ac43f2fbcf9e433d7
1954 Author: Vincent Untz <vuntz@gnome.org>
1955 Date: Sat Jul 23 10:06:35 2011 +0200
1956
1957 build: Require gio-unix-2.0 >= 2.29.15
1958
1959 g_desktop_app_info_get_show_in() was pushed, but after 2.29.14.
1960
1961 M configure.ac
1962
1963 commit 3fae17cc6e86b3e988df481a05a386e005b7637c
1964 Author: Vincent Untz <vuntz@gnome.org>
1965 Date: Fri Jul 22 13:21:17 2011 +0200
1966
1967 editor: Stop editing settings.menu
1968
1969 We dropped that file a while ago. We could replace this with
1970 gnomecc.menu, but gnome-control-center loads the file from the system
1971 configuration and explicitly ignore the user changes there...
1972
1973 M simple-editor/GMenuSimpleEditor/menutreemodel.py
1974
1975 commit 1cb6fe8f97cc5a12933bd77d278a16827e763c13
1976 Author: Vincent Untz <vuntz@gnome.org>
1977 Date: Fri Jul 22 08:55:19 2011 +0200
1978
1979 libmenu: Unset GMenuTree:menu-basename if GMenuTree:menu-path is set
1980
1981 Since GMenuTree:menu-basename has a non-NULL default value, we really
1982 want to unset it if the user sets GMenuTree:menu-path, to avoid any
1983 potential confusion.
1984
1985 We need a constructor to do this magic, as the properties are
1986 construct-only, and doing this anywhere else would be too late.
1987
1988 M libmenu/gmenu-tree.c
1989
1990 commit 02d0f749a1c4f406fa773254c2095e6c4f3d5979
1991 Author: Vincent Untz <vuntz@gnome.org>
1992 Date: Thu Jul 21 18:28:56 2011 +0200
1993
1994 libmenu: Group GMenuTreeFlags flags in a pseudo-consistent way
1995
1996 Since we're breaking ABI, let's go crazy and change the value of flags.
1997 Grouping flags by some sort of meaning (include ones, show ones, misc.
1998 ones) makes the header a bit nicer to read.
1999
2000 M libmenu/gmenu-tree.h
2001
2002 commit 9e2b4abdc8ec63e20f4c2a52b3047f1d767ebd8d
2003 Author: Vincent Untz <vuntz@gnome.org>
2004 Date: Thu Jul 21 18:19:24 2011 +0200
2005
2006 editor: Port to introspection-based gmenu bindings
2007
2008 M simple-editor/GMenuSimpleEditor/maindialog.py
2009 M simple-editor/GMenuSimpleEditor/menutreemodel.py
2010
2011 commit 6f4cf16ec2633ebc62cb91759dbfd01923cd3c73
2012 Author: Vincent Untz <vuntz@gnome.org>
2013 Date: Thu Jul 21 18:09:25 2011 +0200
2014
2015 libmenu, util: Rename gmenu_tree_get_menu_path()
2016
2017 Renamed to gmenu_tree_get_canonical_menu_path(), to avoid any potential
2018 confusion with the menu-path property.
2019
2020 M libmenu/gmenu-tree.c
2021 M libmenu/gmenu-tree.h
2022 M util/test-menu-spec.c
2023
2024 commit 8bc90706c9bfbf0d55c2eaddc859afb4a52a3281
2025 Author: Vincent Untz <vuntz@gnome.org>
2026 Date: Thu Jul 21 18:07:54 2011 +0200
2027
2028 libmenu: Add API to load menu file from full path
2029
2030 Add gmenu_tree_new_for_path() and GMenuTree:menu-path.
2031
2032 M libmenu/gmenu-tree.c
2033 M libmenu/gmenu-tree.h
2034
2035 commit c653bbe8904acca4340f70574357d843e549035c
2036 Author: Vincent Untz <vuntz@gnome.org>
2037 Date: Thu Jul 21 17:22:20 2011 +0200
2038
2039 libmenu, util: Rename GMenuTree:name to GMenuTree:menu-basename
2040
2041 This is a much clearer name for the property, else people might assume
2042 they can use a full path, or something that is not a basename.
2043
2044 Note that a relative path will work, but that really should not be of
2045 much use, hence the choice of basename.
2046
2047 M libmenu/gmenu-tree.c
2048 M libmenu/gmenu-tree.h
2049 M util/gnome-menus-ls.js
2050
2051 commit c45596c3d87fa3e344b9e9a32c51190e12d67d93
2052 Author: Vincent Untz <vuntz@gnome.org>
2053 Date: Thu Jul 21 17:13:18 2011 +0200
2054
2055 libmenu: Fix critical warning when trying to load "" menu
2056
2057 Yes, this is a weird use of the API, but still.
2058
2059 M libmenu/gmenu-tree.c
2060
2061 commit 4c85e0ee58a4e8b11f1e5527909eefd02133a72a
2062 Author: Vincent Untz <vuntz@gnome.org>
2063 Date: Thu Jul 21 17:00:25 2011 +0200
2064
2065 libmenu: Default GMenuTree::name property to "applications.menu"
2066
2067 This is useful for introspection bindings, since users will not
2068 necessarily think of setting the menu name property at construct time.
2069 And it's a saner default than NULL.
2070
2071 M libmenu/gmenu-tree.c
2072
2073 commit d8be0079a48bb7b486cc2b1355ee0cea2596fecb
2074 Author: Vincent Untz <vuntz@gnome.org>
2075 Date: Thu Jul 21 16:55:57 2011 +0200
2076
2077 libmenu: Do not keep internal load_error in GMenuTree
2078
2079 This is not used, and it actually gets corrupted at some point (since
2080 g_propagate_error means we lose the ownership of the GError).
2081
2082 M libmenu/gmenu-tree.c
2083
2084 commit bb3e154f60f81e97178b0cb8457827b29ee20692
2085 Author: Vincent Untz <vuntz@gnome.org>
2086 Date: Thu Jul 21 16:01:46 2011 +0200
2087
2088 build: Bump version to 3.1.4
2089
2090 This is needed so users can know which version to require for the new
2091 API.
2092
2093 M configure.ac
2094
2095 commit 70e9aaacbe0783fe11b7c69310873e0dc638b274
2096 Author: Vincent Untz <vuntz@gnome.org>
2097 Date: Thu Jul 21 15:57:18 2011 +0200
2098
2099 libmenu: Drop GMenuTreeDirectoryRoot
2100
2101 This was only used internally, but with no reason.
2102
2103 M libmenu/gmenu-tree.c
2104
2105 commit 17961198a182d5f68e4773427b1c8f735e0a01cd
2106 Author: Vincent Untz <vuntz@gnome.org>
2107 Date: Thu Jul 21 15:48:35 2011 +0200
2108
2109 libmenu: Make gmenu_tree_directory_get_icon() return a GIcon
2110
2111 It's much better to use GIcon than icon name strings.
2112
2113 M libmenu/desktop-entries.c
2114 M libmenu/desktop-entries.h
2115 M libmenu/gmenu-tree.c
2116 M libmenu/gmenu-tree.h
2117
2118 commit 3c6ad47de8cfc3581fee66107a41ba4b39dd0de0
2119 Author: Vincent Untz <vuntz@gnome.org>
2120 Date: Thu Jul 21 15:26:11 2011 +0200
2121
2122 libmenu: Correctly deal with OnlyShowIn/NotShowIn
2123
2124 We're using new glib API for this.
2125
2126 Also, we do not remove entries because of OnlyShowIn/NotShowIn before
2127 processing the layout, as we might want to add a GMenuTreeFlags value in
2128 the future, to include .desktop files that are excluded because of
2129 OnlyShowIn/NotShowIn.
2130
2131 M configure.ac
2132 M libmenu/desktop-entries.c
2133 M libmenu/desktop-entries.h
2134 M libmenu/gmenu-tree.c
2135
2136 commit 66557ef586adff091c8ce69a2f472da00dcd89ba
2137 Author: Vincent Untz <vuntz@gnome.org>
2138 Date: Mon Jul 4 17:23:32 2011 +0200
2139
2140 build: Drop check for -fno-strict-aliasing support
2141
2142 This was only needed for our previous python bindings.
2143
2144 M configure.ac
2145
2146 commit fc5492ab992fde2b8d378ed91f0b4c6c62cbe69b
2147 Author: Colin Walters <walters@verbum.org>
2148 Date: Tue Jun 14 13:52:06 2011 -0400
2149
2150 libmenu: Dispose of source before context to avoid possible double unref
2151
2152 M libmenu/menu-layout.c
2153
2154 commit 21062503fff272872fdfc5fb468288b1faa85127
2155 Author: Colin Walters <walters@verbum.org>
2156 Date: Tue Jun 14 13:25:47 2011 -0400
2157
2158 libmenu: Make refcounts volatile as g_atomic_* expect
2159
2160 M libmenu/gmenu-tree.c
2161
2162 commit b8b2471e8a3bd0f2d469dea41067721919359c22
2163 Author: Vincent Untz <vuntz@gnome.org>
2164 Date: Tue Jun 14 09:04:31 2011 +0200
2165
2166 libmenu: Correctly look up at NoDisplay for .desktop files
2167
2168 We were using Hidden instead. This requires glib 2.29.9.
2169
2170 M configure.ac
2171 M libmenu/desktop-entries.c
2172
2173 commit 6ba57847943cd74839a306ed6c8926401e370b85
2174 Author: Vincent Untz <vuntz@gnome.org>
2175 Date: Sun Jun 12 12:50:05 2011 +0200
2176
2177 libmenu: Simplify some code
2178
2179 M libmenu/desktop-entries.c
2180
2181 commit 2c47cdbebb15715d91d95aa4babc2de7a993dbd4
2182 Author: Vincent Untz <vuntz@gnome.org>
2183 Date: Sun Jun 12 12:38:30 2011 +0200
2184
2185 libmenu: Fix loading of .directory files
2186
2187 We were always returning FALSE, leaking some data and not displaying a
2188 debug message in case of error (like for .desktop files).
2189
2190 M libmenu/desktop-entries.c
2191
2192 commit 6aff671fbeccb5ff7ec3290d0fd6345fbbb632c8
2193 Author: Vincent Untz <vuntz@gnome.org>
2194 Date: Sun Jun 12 12:34:26 2011 +0200
2195
2196 util: Fix build after gmenu_tree_alias_get_item_type() renaming
2197
2198 M util/test-menu-spec.c
2199
2200 commit 398fd5c145524b7961c9d1dbc105c1cd49d98103
2201 Author: Vincent Untz <vuntz@gnome.org>
2202 Date: Sun Jun 12 12:12:55 2011 +0200
2203
2204 doc: Do not reference non-existing gmenu_tree_iter_get_next_type()
2205
2206 This is gmenu_tree_iter_next().
2207
2208 M libmenu/gmenu-tree.c
2209
2210 commit e71520d5d2ccc4392b046e5feb84349d71a2c69f
2211 Author: Vincent Untz <vuntz@gnome.org>
2212 Date: Sun Jun 12 12:11:31 2011 +0200
2213
2214 libmenu: Handle GenericName for .directory files
2215
2216 While we do not strictly need them right now, this is allowed by the
2217 specification and might be used later.
2218
2219 M libmenu/desktop-entries.c
2220 M libmenu/desktop-entries.h
2221 M libmenu/gmenu-tree.c
2222 M libmenu/gmenu-tree.h
2223
2224 commit f29590a8c3771af47debf75c929170fe59877dce
2225 Author: Vincent Untz <vuntz@gnome.org>
2226 Date: Sun Jun 12 11:53:59 2011 +0200
2227
2228 libmenu: Fix desktop_entry_copy() for .desktop file
2229
2230 We were not dealing with the appinfo field at all.
2231
2232 M libmenu/desktop-entries.c
2233
2234 commit 0d5f1909518f86d7283cde404e9470e6f55a42d2
2235 Author: Vincent Untz <vuntz@gnome.org>
2236 Date: Sun Jun 12 11:41:20 2011 +0200
2237
2238 libmenu: Rename gmenu_tree_alias_get_item_type
2239
2240 Renamed to gmenu_tree_alias_get_aliased_item_type, to avoid any
2241 potential confusion about the typoe of what item this is.
2242
2243 M libmenu/gmenu-tree.c
2244 M libmenu/gmenu-tree.h
2245
2246 commit 4d96af04a6d1d2e211c2005cc9d03d2b0e1a85c0
2247 Author: Vincent Untz <vuntz@gnome.org>
2248 Date: Sun Jun 12 11:33:16 2011 +0200
2249
2250 libmenu: Drop gmenu_tree_entry_get_is_nodisplay() from header
2251
2252 The function got dropped, and having a similar function in
2253 GDesktopAppInfo would be the right thing to do.
2254
2255 M libmenu/gmenu-tree.h
2256
2257 commit f587ca8642070892fd4172ab01735c4a0789dc19
2258 Author: Vincent Untz <vuntz@gnome.org>
2259 Date: Sun Jun 12 11:12:42 2011 +0200
2260
2261 libmenu: Remove unneeded cast
2262
2263 M libmenu/gmenu-tree.c
2264
2265 commit 2167c042bc089ba1e570ac9ba60dcdddb1d1d3c3
2266 Author: Vincent Untz <vuntz@gnome.org>
2267 Date: Sun Jun 12 11:05:52 2011 +0200
2268
2269 libmenu: Rename desktop_entry_desktop_get_icon to desktop_entry_get_icon
2270
2271 The additional desktop in the name was confusing.
2272
2273 M libmenu/desktop-entries.c
2274 M libmenu/desktop-entries.h
2275 M libmenu/gmenu-tree.c
2276
2277 commit 0f7d374006f32559893629fba47fecfaa031d4f8
2278 Author: Vincent Untz <vuntz@gnome.org>
2279 Date: Sun Jun 12 10:53:05 2011 +0200
2280
2281 libmenu: Fix getting the flags property
2282
2283 This was not handled at all.
2284
2285 M libmenu/gmenu-tree.c
2286
2287 commit 0eaed2f94d52a8b03263e5976a4d16104546929c
2288 Author: Vincent Untz <vuntz@gnome.org>
2289 Date: Sun Jun 12 10:39:54 2011 +0200
2290
2291 libmenu: Drop support for "KDE Desktop Entry" group
2292
2293 This was deprecated for years, and with the move to GDesktopAppInfo, we
2294 don't support this group for .desktop files anyway, so it makes no sense
2295 to keep supporting it for .directory files.
2296
2297 M libmenu/desktop-entries.c
2298
2299 commit 536c6d510f53bbf76ff0c4134e935c8e7cf552da
2300 Author: Vincent Untz <vuntz@gnome.org>
2301 Date: Sun Jun 12 10:31:37 2011 +0200
2302
2303 libmenu: Use explicit GMENU_TREE_FLAGS_NONE instead of 0
2304
2305 M libmenu/gmenu-tree.c
2306
2307 commit d5da147d09ca09e593e4a88c11b782ee7479268f
2308 Author: Vincent Untz <vuntz@gnome.org>
2309 Date: Sun Jun 12 09:58:20 2011 +0200
2310
2311 Spaces/tabs fixes
2312
2313 M libmenu/desktop-entries.c
2314 M libmenu/desktop-entries.h
2315 M libmenu/gmenu-tree.c
2316 M libmenu/gmenu-tree.h
2317 M libmenu/menu-layout.c
2318
2319 commit 6920649082e3c0be363094054eecc9f1185a8185
2320 Author: Vincent Untz <vuntz@gnome.org>
2321 Date: Wed Jun 8 17:55:43 2011 +0200
2322
2323 util: Do not call exit() in test tool
2324
2325 A simple return statement is enough.
2326
2327 M util/test-menu-spec.c
2328
2329 commit f88d8c4dd17055932a6b335c008b496327d536b3
2330 Author: Vincent Untz <vuntz@gnome.org>
2331 Date: Wed Jun 8 17:48:58 2011 +0200
2332
2333 build: Fix pkg-config to require gio-unix-2.0 and not glib-2.0
2334
2335 We reference gio/gdesktopappinfo.h in our public header.
2336
2337 M libmenu/libgnome-menu-3.0-uninstalled.pc.in
2338 M libmenu/libgnome-menu-3.0.pc.in
2339
2340 commit cfb20fb779eeed93a0c9571a8340251c8964ef69
2341 Author: Vincent Untz <vuntz@gnome.org>
2342 Date: Wed Jun 8 17:44:16 2011 +0200
2343
2344 gi: Add/Fix annotations to remove introspection warnings
2345
2346 M libmenu/gmenu-tree.c
2347
2348 commit 91a698f7860b3d8d5f29ffcdfcdbadf0cc347bad
2349 Author: Vincent Untz <vuntz@gnome.org>
2350 Date: Wed Jun 8 17:39:42 2011 +0200
2351
2352 build: Version the library name and the pkg-config file
2353
2354 The library is now libgnome-menu-3, the pkg-config file is
2355 libgnome-menu-3.0, the header directory is gnome-menus-3.0 and the
2356 gettext package is gnome-menus-3.0.
2357
2358 This way, it's possible to keep the old libgnome-menu around if some
2359 applications still need it.
2360
2361 M configure.ac
2362 M libmenu/Makefile.am
2363 R080 libmenu/libgnome-menu-uninstalled.pc.in libmenu/libgnome-menu-3.0-uninstalled.pc.in
2364 R072 libmenu/libgnome-menu.pc.in libmenu/libgnome-menu-3.0.pc.in
2365 M util/Makefile.am
2366
2367 commit 669f03a403ca83facf83020e9c37b894c67f1b9b
2368 Author: Vincent Untz <vuntz@gnome.org>
2369 Date: Wed Jun 8 16:19:49 2011 +0200
2370
2371 build: Remove old python-related build stuff
2372
2373 M configure.ac
2374 D m4/python.m4
2375
2376 commit 1cf4482fa3416b98916653db23b40edb26d8646d
2377 Author: Colin Walters <walters@verbum.org>
2378 Date: Thu Apr 21 13:21:57 2011 -0400
2379
2380 Bump to GMenu-4.0.gir; this matches the package version.
2381
2382 M libmenu/Makefile.am
2383
2384 commit 5aa2c53ef25f684a848312455dad396f28c96a90
2385 Author: Colin Walters <walters@verbum.org>
2386 Date: Wed Apr 20 17:48:07 2011 -0400
2387
2388 Replace Python example with JS
2389
2390 M util/Makefile.am
2391 A util/gnome-menus-ls.js
2392 D util/gnome-menus-ls.py
2393
2394 commit f0101da1c3df623f5a0023d9c787535e89476291
2395 Author: Colin Walters <walters@verbum.org>
2396 Date: Wed Apr 20 17:42:39 2011 -0400
2397
2398 Remove GMenuTreeItem from public API
2399
2400 gmenu_tree_directory_get_contents() wasn't actually bindable because
2401 there's no way to express to introspection the inheritance hierarchy
2402 (and to do the runtime type checking necessary via
2403 gmenu_tree_item_get_item_type()).
2404
2405 Therefore, drop it, and instead add explicit functions where each type
2406 is needed. So for gmenu_tree_directory_get_contents(), we instead
2407 add an explicit typesafe iterator object.
2408
2409 The only other instance was gmenu_tree_alias.
2410
2411 Note that gmenu_tree_item_ref() and gmenu_tree_item_unref() remain;
2412 they're C only, and already just took "gpointer".
2413
2414 M libmenu/gmenu-tree.c
2415 M libmenu/gmenu-tree.h
2416 M util/test-menu-spec.c
2417
2418 commit 76895ad01d450696519425d03beab8845230397b
2419 Author: Colin Walters <walters@verbum.org>
2420 Date: Wed Apr 20 17:16:25 2011 -0400
2421
2422 introspection: scan gmenu-tree.c
2423
2424 Now that we're actually adding gtk-doc.
2425
2426 M libmenu/Makefile.am
2427
2428 commit 0877f894a6c74f0dc7522e9cb93cc223097d1494
2429 Author: Colin Walters <walters@verbum.org>
2430 Date: Mon Apr 18 16:17:04 2011 -0400
2431
2432 Document a few functions
2433
2434 M libmenu/gmenu-tree.c
2435
2436 commit bd78be7ad777f70d2390309507162f5dc267ea2f
2437 Author: Colin Walters <walters@verbum.org>
2438 Date: Mon Apr 18 15:36:01 2011 -0400
2439
2440 gmenu_tree_entry_get_parent: New function
2441
2442 Earlier we moved this down to GMenuTreeDirectory, but it turns
2443 out gnome-shell does expect to be able to get the parent of a
2444 GMenuTreeEntry. In the future I want to nuke that code, but
2445 for now just readd this functionality.
2446
2447 M libmenu/gmenu-tree.c
2448 M libmenu/gmenu-tree.h
2449
2450 commit 94b2ea48a5221bd6dc82b8b468bb85ea6d420e57
2451 Author: Colin Walters <walters@verbum.org>
2452 Date: Mon Apr 18 10:10:53 2011 -0400
2453
2454 layout: Use thread-default main context for callbacks
2455
2456 Rather than hardcoding g_idle_add(); this gives us future
2457 flexibility for thread support.
2458
2459 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2460
2461 M libmenu/menu-layout.c
2462
2463 commit 52f9f7017779b6691e2b3f46f6c24e37f219abbf
2464 Author: Colin Walters <walters@verbum.org>
2465 Date: Sun Apr 17 11:22:32 2011 -0400
2466
2467 Remove gmenu_tree_directory_get_tree()
2468
2469 This causes a circular reference internally, and API consumers
2470 can just keep track of it easily enough externally.
2471
2472 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2473
2474 M libmenu/gmenu-tree.c
2475 M libmenu/gmenu-tree.h
2476
2477 commit 5d1e03adc206f52dec82f2b5e8831022321cb1f8
2478 Author: Colin Walters <walters@verbum.org>
2479 Date: Sun Apr 17 10:17:20 2011 -0400
2480
2481 Switch to gslice for most data
2482
2483 This is more efficient for the small items we have here.
2484
2485 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2486
2487 M libmenu/gmenu-tree.c
2488
2489 commit 4f47a654d91cc3b08d46ef518a42c7f484f01be3
2490 Author: Colin Walters <walters@verbum.org>
2491 Date: Sun Apr 17 09:57:47 2011 -0400
2492
2493 Rename gmenu_tree_get_menu_file() to gmenu_tree_get_menu_path()
2494
2495 Document it and clean it up.
2496
2497 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2498
2499 M libmenu/gmenu-tree.c
2500 M libmenu/gmenu-tree.h
2501 M util/test-menu-spec.c
2502
2503 commit 13920c91868f77a964b4498b00072563608f583e
2504 Author: Colin Walters <walters@verbum.org>
2505 Date: Sun Apr 17 09:51:58 2011 -0400
2506
2507 Further propagate GError for gmenu_tree_load_sync()
2508
2509 We had some GError use internally; clean things up so we propagate
2510 it more consistently to the top of gmenu_tree_load_sync().
2511
2512 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2513
2514 M libmenu/gmenu-tree.c
2515
2516 commit 8ecb1aa5d762d25f787002dd001e92e404efabe9
2517 Author: Colin Walters <walters@verbum.org>
2518 Date: Sun Apr 17 09:31:34 2011 -0400
2519
2520 Add explicit gmenu_tree_load_sync()
2521
2522 Rather than having _get_root_directory() be lazy, require users
2523 to explicitly load via this function (or in the future, an
2524 async variant).
2525
2526 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2527
2528 M libmenu/gmenu-tree.c
2529 M libmenu/gmenu-tree.h
2530 M util/test-menu-spec.c
2531
2532 commit a2bda1462b6b46b1c8e47a19977f15c294226ef9
2533 Author: Colin Walters <walters@verbum.org>
2534 Date: Sun Apr 17 08:58:19 2011 -0400
2535
2536 Lower gmenu_tree_item_get_parent to gmenu_tree_directory_get_parent
2537
2538 Introspection doesn't know about the GMenuTreeItem "subclassing",
2539 so we would have to duplicate the _get_parent method on all
2540 subclasses - but in practice it only seems to be used on directories,
2541 so just lower it there.
2542
2543 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2544
2545 M libmenu/gmenu-tree.c
2546 M libmenu/gmenu-tree.h
2547 M util/test-menu-spec.c
2548
2549 commit 14c163c8a8d35fe2daa9c0973495fb4462a3ddec
2550 Author: Colin Walters <walters@verbum.org>
2551 Date: Sun Apr 17 08:46:14 2011 -0400
2552
2553 GMenuTreeItem: Register boxed types, drop user data
2554
2555 This was a hack for the static Python bindings, no longer necessary
2556 after we register proper boxed types for the structures.
2557
2558 Convert the refcount to an atomic integer too; the _unref may be
2559 called from a garbage collector thread in bindings.
2560
2561 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2562
2563 M libmenu/gmenu-tree.c
2564 M libmenu/gmenu-tree.h
2565
2566 commit 8eaf04da66be9437c518ac21e27cbbb67abcd3f5
2567 Author: Colin Walters <walters@verbum.org>
2568 Date: Sun Apr 17 08:44:02 2011 -0400
2569
2570 Rename gmenu_tree_item_get_type() to _get_item_type()
2571
2572 The _get_type() namespace suffix is reserved for GType.
2573
2574 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2575
2576 M libmenu/gmenu-tree.c
2577 M libmenu/gmenu-tree.h
2578 M util/test-menu-spec.c
2579
2580 commit 474b86aac925d7b45d3bbbaba9111719ca0d9b8a
2581 Author: Colin Walters <walters@verbum.org>
2582 Date: Sun Apr 17 08:40:47 2011 -0400
2583
2584 Replace monitor API with a simple "changed" signal
2585
2586 So much simpler...
2587
2588 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2589
2590 M libmenu/gmenu-tree.c
2591 M libmenu/gmenu-tree.h
2592 M util/test-menu-spec.c
2593
2594 commit 21672a2d0ee94a9588fc15cf0cd4c5ffdfc818c1
2595 Author: Colin Walters <walters@verbum.org>
2596 Date: Sun Apr 17 07:50:19 2011 -0400
2597
2598 Convert to GObject, drop static Python bindings
2599
2600 GMenuTree is now a GObject. Drop the static Python bindings, since
2601 introspection gives us coverage of most of the API now.
2602
2603 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2604
2605 M Makefile.am
2606 M configure.ac
2607 M libmenu/gmenu-tree.c
2608 M libmenu/gmenu-tree.h
2609 M libmenu/menu-monitor.c
2610 D python/Makefile.am
2611 D python/gmenu.c
2612 M util/gnome-menus-ls.py
2613 M util/test-menu-spec.c
2614
2615 commit 0a7e4736d221ada2167a064c8b4aa9710a2e04bf
2616 Author: Colin Walters <walters@verbum.org>
2617 Date: Sun Apr 17 07:32:07 2011 -0400
2618
2619 Drop GMenuTree caching and support for absolute paths
2620
2621 This is work towards converting to GObject; the API to create a tree
2622 is now just gmenu_tree_new().
2623
2624 First, the internal caching makes things very complex for little gain
2625 - gnome-shell only creates one GMenuTree, and gnome-panel could pretty
2626 easily be converted to do so.
2627
2628 In a Google Code Search, I couldn't find anyone using absolute paths
2629 for menus, and looking through the revision history, I don't see
2630 a rationale for it. So, just drop it too.
2631
2632 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2633
2634 M libmenu/gmenu-tree.c
2635 M libmenu/gmenu-tree.h
2636 M python/gmenu.c
2637 M util/test-menu-spec.c
2638
2639 commit 903c144be9527f048fa1389c39872d0f22740b62
2640 Author: Colin Walters <walters@verbum.org>
2641 Date: Sun Apr 17 07:17:22 2011 -0400
2642
2643 GMenuTreeFlags: Register with GType
2644
2645 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2646
2647 M libmenu/gmenu-tree.c
2648 M libmenu/gmenu-tree.h
2649
2650 commit d660a95756c99fe053ffd4fbaf523008868f5a67
2651 Author: Colin Walters <walters@verbum.org>
2652 Date: Sun Apr 17 07:10:01 2011 -0400
2653
2654 Fold sorting into GMenuTreeFlags
2655
2656 There's only two sorts right now, and so we can make one the default
2657 and select the other with the flags.
2658
2659 Drop the ability to set the sort at runtime; this never was compatible
2660 with the current GMenuTree caching, and also I'm trying to move
2661 GMenuTree towards being immutable.
2662
2663 M libmenu/gmenu-tree.c
2664 M libmenu/gmenu-tree.h
2665 M python/gmenu.c
2666
2667 commit 4d00dcf9ce31b248c61dc66430b881ba32e2d611
2668 Author: Colin Walters <walters@verbum.org>
2669 Date: Sat Apr 16 14:09:57 2011 -0400
2670
2671 Rebase DesktopEntry on GDesktopAppInfo
2672
2673 The main motivation for this work is to avoid gnome-shell having
2674 to read all .desktop files *twice* - once from gnome-menus, and
2675 once from gio (when doing MIME assocation etc.)
2676
2677 This patch replaces almost all of the accessors for GMenuTreeEntry
2678 with the simple gmenu_tree_entry_get_app_info().
2679
2680 Note this patch depends on patches from (see bug 647967).
2681
2682 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2683
2684 M configure.ac
2685 M libmenu/Makefile.am
2686 M libmenu/desktop-entries.c
2687 M libmenu/desktop-entries.h
2688 M libmenu/gmenu-tree.c
2689 M libmenu/gmenu-tree.h
2690 M python/Makefile.am
2691 M python/gmenu.c
2692 M util/Makefile.am
2693
2694 commit 8a01f3c268a1515fb4e9ee8c924dae20a0880bb1
2695 Author: Colin Walters <walters@verbum.org>
2696 Date: Sat Apr 16 13:31:21 2011 -0400
2697
2698 desktop-entries.c: Split structure explicitly between .desktop and .directory
2699
2700 This is code cleaup preparatory work for rebasing DesktopEntry on
2701 GDesktopAppInfo.
2702
2703 These two cases are different; make this explicit via structure subclassing
2704 of a common base structure.
2705
2706 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2707
2708 M libmenu/desktop-entries.c
2709
2710 commit 5a936c77ebb7ffddbc4e282662bb2d5f62a8a814
2711 Author: Colin Walters <walters@verbum.org>
2712 Date: Sat Apr 16 11:54:17 2011 -0400
2713
2714 DesktopEntry: Clean up structure, make TryExec evaluation lazy
2715
2716 This is preparatory work for rebasing DesktopEntry on top of
2717 GDesktopAppInfo.
2718
2719 First, it doesn't make sense to represet a random subset of booleans
2720 as flags; just flatten these into a bitfield. Move the refcount to be
2721 a plain guint at top.
2722
2723 Second, previously we were calling g_find_program_in_path when
2724 creating a .desktop file, even if the caller wasn't interested in
2725 whether the TryExec succeeded. Make this lazy.
2726
2727 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2728
2729 M libmenu/desktop-entries.c
2730
2731 commit ea008b417c02e8238f58d2d419d01129c2de9226
2732 Author: Colin Walters <walters@verbum.org>
2733 Date: Sat Apr 16 11:04:26 2011 -0400
2734
2735 DesktopEntry: Make basename const reference
2736
2737 Avoids another malloc block.
2738
2739 https://bugzilla.gnome.org/show_bug.cgi?id=647968
2740
2741 M libmenu/desktop-entries.c
2742
2743 commit 6cf363f9eaf4ba8ea736ca0a9e8d427226f381f6
2744 Author: Friedel Wolff <friedel@translate.org.za>
2745 Date: Thu Jul 7 22:30:06 2011 +0200
2746
2747 Add Zulu (zu) to LINGUAS
2748
2749 M po/LINGUAS
2750
2751 commit 756d19762d1793ebe80df661328a1c7a510b9773
2752 Author: Priscilla Mahlangu <priny@translate.za>
2753 Date: Thu Jul 7 22:28:26 2011 +0200
2754
2755 New translation for Zulu (zu)
2756
2757 A po/zu.po
2758
2759 commit 5c896c6418cbc6111c823cca5401204bd62c16dc
2760 Author: Ihar Hrachyshka <ihar.hrachyshka@gmail.com>
2761 Date: Sat Jun 25 20:33:13 2011 +0300
2762
2763 Updated Belarusian translation.
2764
2765 M po/be.po
2766
2767 commit 9841d10ecf88b816ea18f186b64d4f1798f0c21a
2768 Author: Carles Ferrando <carles.ferrando@gmail.com>
2769 Date: Sun May 29 18:26:56 2011 +0200
2770
2771 [l10n]Updated Catalan (Valencian) translation
2772
2773 M po/ca@valencia.po
2774
2775 commit 7336104da09a01db012c6d7e4d11abc3bac2f4e8
2776 Author: Vincent Untz <vuntz@gnome.org>
2777 Date: Tue Apr 26 09:12:06 2011 +0200
2778
2779 release: post-release bump to 3.0.2
2780
2781 M configure.ac
2782
2783 commit 582b78f2d085130fb63537c8e7b1120ff77c9979
2784 Author: Vincent Untz <vuntz@gnome.org>
2785 Date: Tue Apr 26 09:11:59 2011 +0200
2786
2787 release: 3.0.1
2788
2789 M NEWS
2790 M configure.ac
2791
2792 commit 1febd5e57900e931b9aa53774a8c3caaff4f6a2a
2793 Author: Theppitak Karoonboonyanan <thep@linux.thai.net>
2794 Date: Mon Apr 25 08:41:18 2011 +0700
2795
2796 Updated Thai translation.
2797
2798 M po/th.po
2799
2800 commit df2f20a03a78d23665148c608b4955bb4702c2d9
2801 Author: Anousak Souphavanh <anousak@gmail.com>
2802 Date: Tue Apr 19 12:47:55 2011 +0300
2803
2804 l10n: Added Lao translation for gnome-menus
2805
2806 M po/LINGUAS
2807 A po/lo.po
2808
2809 commit 2a436ad5101f3a05a029ffaded8d60c412686540
2810 Author: Vincent Untz <vuntz@gnome.org>
2811 Date: Mon Apr 4 23:04:28 2011 +0200
2812
2813 release: post-release bump to 3.0.1
2814
2815 M configure.ac
2816
2817 commit 8f288a1114e95adf98f8186cb309e5cacad66105
2818 Author: Vincent Untz <vuntz@gnome.org>
2819 Date: Mon Apr 4 23:04:21 2011 +0200
2820
2821 release: 3.0.0
2822
2823 M NEWS
2824 M configure.ac
2825
2826 commit a153239bdf7126154ada0b7e22bf827860cad330
2827 Author: Jamil Ahmed <itsjamil@gmail.com>
2828 Date: Mon Apr 4 15:25:07 2011 +0600
2829
2830 Updated Bengali translation
2831
2832 M po/bn.po
2833
2834 commit 381752ab7db377bdabcfda08ab90353ce78770f7
2835 Author: Jordi Serratosa <serratosa@gmail.com>
2836 Date: Mon Apr 4 00:14:46 2011 +0200
2837
2838 [l10n]Fixes on Catalan translation
2839
2840 M po/ca.po
2841
2842 commit 61593b848a8f03694a7963a0cb796aa06b82330c
2843 Author: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
2844 Date: Sat Apr 2 10:31:35 2011 +0700
2845
2846 Updated Vietnamese translation
2847
2848 M po/vi.po
2849
2850 commit 5d9e2b6e441b907ca9724ab3fdc02fc32fa12205
2851 Author: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
2852 Date: Sat Apr 2 10:31:18 2011 +0700
2853
2854 po/vi.po: import from Damned Lies
2855
2856 M po/vi.po
2857
2858 commit e8d7b8fb9d128ac39e3e0f1dd6a8e96c7ce18e75
2859 Author: krishnababu k <kkrothap@redhat.ocm>
2860 Date: Fri Apr 1 13:21:53 2011 +0530
2861
2862 Updated Telugu translations done by praveen
2863
2864 M po/te.po
2865
2866 commit 4fb0ed4d0e7a4fb40e56af6d462467bd16d4c512
2867 Author: Vincent Untz <vuntz@gnome.org>
2868 Date: Wed Mar 30 12:53:39 2011 +0530
2869
2870 layout: Show administration tools and old capplets in Other
2871
2872 Right now, administration tools and capplets that used to be in the
2873 Control Center are not accessible in any way. With this patch, they
2874 appear in Other. While this is not perfect, this will make them
2875 accessible until we find the right solution.
2876
2877 To do this, we just exclude .desktop files that match panels from the
2878 Control Center instead of excludings the Settings category, which is too
2879 broad.
2880
2881 https://bugzilla.gnome.org/show_bug.cgi?id=645061
2882
2883 M layout/applications.menu
2884
2885 commit 7c73245fcbd5feaf66df50ee6ca5e8c9132a11aa
2886 Author: Abduxukur Abdurixit <abdurixit@gmail.com>
2887 Date: Sun Mar 27 19:50:01 2011 +0200
2888
2889 Added UG translation
2890
2891 M po/ug.po
2892
2893 commit d2bd20e2e09fa4791da9a9881d77d58f31328ec9
2894 Author: Sense Hofstede <sense@ubuntu.com>
2895 Date: Fri Mar 18 19:52:08 2011 +0100
2896
2897 Updated Frisian translation
2898
2899 M po/fy.po
2900
2901 commit de31041b34a03b1438407a5604a0d416ea791e31
2902 Author: Khaled Hosny <khaledhosny@eglug.org>
2903 Date: Mon Mar 7 08:07:25 2011 +0200
2904
2905 Updated Arabic translation
2906
2907 M po/ar.po
2908
2909 commit 819557fd56cd9a867bc1ed66955b70c823292b71
2910 Author: Vincent Untz <vuntz@gnome.org>
2911 Date: Sun Mar 6 23:38:58 2011 +0100
2912
2913 release: post-release bump to 2.91.92
2914
2915 M configure.ac
2916
2917 commit 764cf265fac8b7ee574d045b44431fe7cfbd4852
2918 Author: Vincent Untz <vuntz@gnome.org>
2919 Date: Sun Mar 6 23:38:51 2011 +0100
2920
2921 release: 2.91.91
2922
2923 M NEWS
2924 M configure.ac
2925
2926 commit 8e256fa080eadccecea4823f688974da34ed3790
2927 Author: Daniel Korostil <ted.korostiled@gmail.com>
2928 Date: Tue Mar 1 17:43:54 2011 +0200
2929
2930 Uploaded Ukranian
2931
2932 M po/uk.po
2933
2934 commit 715a80efbe9d2be0f329979e13073b0ece9241af
2935 Author: Changwoo Ryu <cwryu@debian.org>
2936 Date: Sun Feb 27 17:17:37 2011 +0900
2937
2938 Updated Korean translation
2939
2940 M po/ko.po
2941
2942 commit 2966b4886b300a0bba438242fa5f44ac9f16f1c0
2943 Author: Vincent Untz <vuntz@gnome.org>
2944 Date: Wed Feb 23 02:55:18 2011 +0100
2945
2946 editor: Fix to work with latest pygi
2947
2948 https://bugzilla.gnome.org/show_bug.cgi?id=643019
2949
2950 M simple-editor/GMenuSimpleEditor/maindialog.py
2951 M simple-editor/GMenuSimpleEditor/menufilewriter.py
2952 M simple-editor/GMenuSimpleEditor/menutreemodel.py
2953
2954 commit 8c66c8ad72ab512bc194cf16eadb7bf76696d817
2955 Author: Friedel Wolff <friedel@translate.org.za>
2956 Date: Mon Feb 7 07:59:47 2011 +0200
2957
2958 Updated translation for Afrikaans (af)
2959
2960 M po/af.po
2961
2962 commit 9792ac69297dc003f5ba4d9461e15874ac1a6908
2963 Author: Vincent Untz <vuntz@gnome.org>
2964 Date: Fri Feb 4 12:01:19 2011 +0100
2965
2966 build: Add ${ACLOCAL_FLAGS} to ACLOCAL_AMFLAGS
2967
2968 This is used by gnome-autogen.sh, and we need it when aclocal.m4 is
2969 to be rebuilt by make, to avoid losing some aclocal paths.
2970
2971 Also, don't set ACLOCAL_AMFLAGS in configure.
2972
2973 M Makefile.am
2974 M configure.ac
2975
2976 commit 6223b261d5a46ac170567ca4d5c0a00a07f64d43
2977 Author: Pavol Klačanský <pavol@klacansky.com>
2978 Date: Wed Feb 2 09:39:00 2011 +0100
2979
2980 Updated Slovak translation
2981
2982 M po/sk.po
2983
2984 commit 668835a8d12d1dd5c88e3bc0fdd44eb6a63272e3
2985 Author: Vincent Untz <vuntz@gnome.org>
2986 Date: Wed Feb 2 01:21:58 2011 +0100
2987
2988 release: post-release bump to 2.91.90
2989
2990 M configure.ac
2991
2992 commit c24f0b2ada3123141049ca579d080245a923b88a
2993 Author: Vincent Untz <vuntz@gnome.org>
2994 Date: Wed Feb 2 01:21:07 2011 +0100
2995
2996 release: 2.91.6
2997
2998 M NEWS
2999 M configure.ac
3000
3001 commit 8c2c9b554fdbbd10769c6028a51e230e362b9b4c
3002 Author: Vincent Untz <vuntz@gnome.org>
3003 Date: Wed Feb 2 01:19:36 2011 +0100
3004
3005 build: Fix distcheck (missing file in POTFILES.in)
3006
3007 M po/POTFILES.in
3008
3009 commit b68bcd27f44ce2c494f6e3cd9695890b9c02af04
3010 Author: Vincent Untz <vuntz@gnome.org>
3011 Date: Tue Feb 1 15:18:49 2011 +0100
3012
3013 layout: Drop settings.menu
3014
3015 It doesn't fit in the GNOME 3 world, where we use gnomecc.menu.
3016
3017 D desktop-directories/InternetAndNetwork.directory.in
3018 D desktop-directories/LookAndFeel.directory.in
3019 M desktop-directories/Makefile.am
3020 D desktop-directories/Settings-System.directory.in
3021 D desktop-directories/Settings.directory.in
3022 D desktop-directories/X-GNOME-Menu-System.directory.in
3023 M layout/Makefile.am
3024 D layout/settings.menu
3025 M po/POTFILES.in
3026
3027 commit 9ff2b267f4ae08ec38a2eb49c9de7f6778708d22
3028 Author: Fran Diéguez <fran.dieguez@mabishu.com>
3029 Date: Thu Jan 27 03:38:30 2011 +0100
3030
3031 QA of Galician translations
3032
3033 M po/gl.po
3034
3035 commit 81dcfb9938e49083eddf545ec4ad919885a041c8
3036 Author: Gheyret T.Kenji <gheyret@gmail.com>
3037 Date: Thu Dec 23 19:02:04 2010 +0100
3038
3039 Added UG translation
3040
3041 M po/ug.po
3042
3043 commit 340f2aa06c57705e9c3c7dcc62abccfa583ed2c8
3044 Author: Mattias Põldaru <mahfiaz gmail com>
3045 Date: Mon Dec 20 14:06:53 2010 +0200
3046
3047 [l10n] Updated Estonian translation
3048
3049 M po/et.po
3050
3051 commit 31c1309040172956c84a6123460e7254221ac2eb
3052 Author: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
3053 Date: Sun Dec 19 12:56:53 2010 +0700
3054
3055 po/vi.po: import some translations from Ubuntu/Maverick
3056
3057 M po/vi.po
3058
3059 commit faf02643fb23eb2ca0ef08b75cd77e1cae0237ab
3060 Author: Gheyret T.Kenji <gheyret@gmail.com>
3061 Date: Sat Nov 20 11:41:03 2010 +0100
3062
3063 Added UG translation
3064
3065 M po/ug.po
3066
3067 commit f921bd15a02ee98d8ad2a5301135f096e0a0621f
3068 Author: Mahyar Moghimi <mahyar.moqimi@gmail.com>
3069 Date: Fri Nov 19 16:16:58 2010 +0330
3070
3071 Updating Persian Translation
3072
3073 M po/fa.po
3074
3075 commit eb15998cee9614522a4a6018249aa449a5b9ead0
3076 Author: Gheyret T.Kenji <gheyret@gmail.com>
3077 Date: Sat Nov 13 22:20:28 2010 +0100
3078
3079 Added UG translation
3080
3081 M po/ug.po
3082
3083 commit 5062b41c9ede06f1dfaa030a72201d7cb24e28d4
3084 Author: Carles Ferrando <carles.ferrando@gmail.com>
3085 Date: Fri Oct 29 01:09:57 2010 +0100
3086
3087 Updated Catalan (Valencian) translation
3088
3089 M po/ca@valencia.po
3090
3091 commit cfaef75741125ab692e4154dcf9ceda8b554c26d
3092 Author: Vincent Untz <vuntz@gnome.org>
3093 Date: Sat Oct 9 11:48:56 2010 +0200
3094
3095 build: Update git.mk from pango
3096
3097 M git.mk
3098
3099 commit 9cbd519531eaacb61cc686c13dce94cc5efb2fb2
3100 Author: Vincent Untz <vuntz@gnome.org>
3101 Date: Wed Oct 6 16:44:29 2010 +0200
3102
3103 editor: Remove useless import
3104
3105 M simple-editor/GMenuSimpleEditor/maindialog.py
3106
3107 commit 6a776912dd1c963644b0291dbf8726ef1ec116ee
3108 Author: Vincent Untz <vuntz@gnome.org>
3109 Date: Wed Oct 6 16:42:06 2010 +0200
3110
3111 introspection: Tell g-ir-scanner what are the prefixes
3112
3113 This means we now require gobject-introspection 0.9.5.
3114
3115 M configure.ac
3116 M libmenu/Makefile.am
3117
3118 commit 5092245c4fe1449bee93b8923e370d7db339f40f
3119 Author: Vincent Untz <vuntz@gnome.org>
3120 Date: Wed Oct 6 15:45:31 2010 +0200
3121
3122 libmenu: Do not send multiple notifications for one file change
3123
3124 We emit notifications in the idle loop, which enables us to compress
3125 multiple notifications: this way, we can check if there is already a
3126 notification for a directory.
3127
3128 We do this at the CachedDir level because it enables us to get one
3129 notification for a file rename, for example. But we also need it at the
3130 MenuLayout level to be sure to compress notifications accross multiple
3131 directories. It could be argued that we only need the latter, but I like
3132 it this way :-)
3133
3134 https://bugzilla.gnome.org/show_bug.cgi?id=172046
3135
3136 M libmenu/entry-directories.c
3137 M libmenu/menu-layout.c
3138
3139 commit 2e394806107ccbf0a3b822ab299042ea441c721e
3140 Author: Vincent Untz <vuntz@gnome.org>
3141 Date: Wed Oct 6 12:01:53 2010 +0200
3142
3143 editor: Forgot to remove "import pygtk" :-)
3144
3145 M simple-editor/GMenuSimpleEditor/main.py
3146
3147 commit adb08fee8c3677e539b9d1ca8fab9be1c4bf14b9
3148 Author: Vincent Untz <vuntz@gnome.org>
3149 Date: Wed Oct 6 11:58:31 2010 +0200
3150
3151 editor: Remove has_separator property
3152
3153 It's gone in new GTK+.
3154
3155 M simple-editor/gmenu-simple-editor.ui
3156
3157 commit cd550ce70f391ba3511800a1e100213bc3ff6c45
3158 Author: Vincent Untz <vuntz@gnome.org>
3159 Date: Wed Oct 6 11:56:48 2010 +0200
3160
3161 editor: port to pygobject-based introspection bindings
3162
3163 https://bugzilla.gnome.org/show_bug.cgi?id=626256
3164
3165 M simple-editor/GMenuSimpleEditor/main.py
3166 M simple-editor/GMenuSimpleEditor/maindialog.py
3167 M simple-editor/GMenuSimpleEditor/menufilewriter.py
3168 M simple-editor/GMenuSimpleEditor/menutreemodel.py
3169
3170 commit ce014ee2884f134bcd9764043a58eb32294062ed
3171 Author: Kikongo Translation Team <lundombe01@zaya-dio.com>
3172 Date: Mon Sep 27 13:27:43 2010 +0200
3173
3174 Added Kikongo translation
3175
3176 M po/LINGUAS
3177 A po/kg.po
3178
3179 commit 15056451271dc5faf31ee2161cdb5696773275c5
3180 Author: Vincent Untz <vuntz@gnome.org>
3181 Date: Mon Sep 27 12:58:48 2010 +0200
3182
3183 release: post-release bump to 2.30.5
3184
3185 M configure.ac
3186
3187 commit 9c269a6850ca8c97edc1e41419d8f7778574c84f
3188 Author: Vincent Untz <vuntz@gnome.org>
3189 Date: Mon Sep 27 12:58:40 2010 +0200
3190
3191 release: 2.30.4
3192
3193 M NEWS
3194 M configure.ac
3195
3196 commit 90a58de1f3a7381b26206be39624a3d40d8ffb07
3197 Author: Vincent Untz <vuntz@gnome.org>
3198 Date: Mon Sep 27 12:29:45 2010 +0200
3199
3200 misc: Rename --enable-deprecations to --enable-deprecation-flags
3201
3202 This is a better name for this configure option, since it's really about
3203 enabling the use of the deprecation flags, not allowing the use of
3204 deprecated API.
3205
3206 M configure.ac
3207
3208 commit f1da5abb7c1783134cfa485b323aedb4f5bc2465
3209 Author: Vincent Untz <vuntz@gnome.org>
3210 Date: Mon Sep 27 12:04:53 2010 +0200
3211
3212 introspection: Associate .gir with pkg-config file
3213
3214 M libmenu/Makefile.am
3215
3216 commit fbd3e2c9f8fb8de4c975367f30b84121478da7d2
3217 Author: Vincent Untz <vuntz@gnome.org>
3218 Date: Thu Sep 23 19:40:47 2010 +0200
3219
3220 libmenu: Clear cache of desktop entries set when files are added/removed
3221
3222 When installing or removing an application, we get a notification of
3223 change. However, the menu tree still contained the same content.
3224
3225 We were simply not clearing the cache containing the list of desktop
3226 entries when a file got added or removed, meaning that we always got the
3227 same list of desktop entries.
3228
3229 (Note that it didn't affect gnome-panel, it's unclear why)
3230
3231 https://bugzilla.gnome.org/show_bug.cgi?id=630410
3232
3233 M libmenu/entry-directories.c
3234
3235 commit 54af0ebf8863955c0641670343ac57900092f104
3236 Author: Torstein Winterseth <kvikende@fsfe.org>
3237 Date: Wed Sep 22 14:32:00 2010 +0200
3238
3239 Updated Norwegian Nynorsk translation.
3240
3241 M po/nn.po
3242
3243 commit b8874d1d36c9b7d448b4958af5b71f2fa3606984
3244 Author: Vincent Untz <vuntz@gnome.org>
3245 Date: Mon Sep 20 17:39:28 2010 +0200
3246
3247 build: Update all Makefile.am to more recent standards
3248
3249 M Makefile.am
3250 M desktop-directories/Makefile.am
3251 M layout/Makefile.am
3252 M libmenu/Makefile.am
3253 M python/Makefile.am
3254 M simple-editor/GMenuSimpleEditor/Makefile.am
3255 M simple-editor/Makefile.am
3256 M util/Makefile.am
3257
3258 commit 7060e3c48a391c0fa96e57879575e333c3a54e67
3259 Author: Vincent Untz <vuntz@gnome.org>
3260 Date: Mon Sep 20 17:37:50 2010 +0200
3261
3262 misc: Update instructions for commit messages
3263
3264 We're switching to "tag:" instead of "[tag]".
3265
3266 M ChangeLog
3267
3268 commit 22642577f7324349b3681bc58e32004820236066
3269 Author: Takayuki KUSANO <AE5T-KSN@asahi-net.or.jp>
3270 Date: Mon Sep 20 18:06:25 2010 +0900
3271
3272 Updated Japanese translation
3273
3274 M po/ja.po
3275
3276 commit feeb6b4d64f2d3e5e0f284aa3e1bfd625a627b1f
3277 Author: Daniel Martinez <entaltoaragon@gmail.com>
3278 Date: Sat Sep 18 20:45:19 2010 +0200
3279
3280 Updated Aragonese translation
3281
3282 M po/an.po
3283
3284 commit 6010094c40eb65965622141cb09fec3a0c8b31d3
3285 Author: Baurzhan Muftakhidinov <baurthefirst@gmail.com>
3286 Date: Wed Sep 15 12:55:46 2010 +0600
3287
3288 Updated Kazakh translation
3289
3290 M po/kk.po
3291
3292 commit fe36b5b5371404ea96a46ac6d6b4a003f54d9828
3293 Author: Vincent Untz <vuntz@gnome.org>
3294 Date: Tue Sep 14 14:40:07 2010 +0200
3295
3296 release: post-release bump to 2.30.4
3297
3298 M configure.ac
3299
3300 commit 721b241e2c5449003238d333d03063fdfe7b3aa1
3301 Author: Vincent Untz <vuntz@gnome.org>
3302 Date: Tue Sep 14 14:39:56 2010 +0200
3303
3304 release: 2.30.3
3305
3306 M NEWS
3307 M configure.ac
3308
3309 commit 328cff2e2951e817a6e999b3a3a8b563f07cd778
3310 Author: Vincent Untz <vuntz@gnome.org>
3311 Date: Wed Sep 8 19:23:00 2010 +0200
3312
3313 [misc] Update AUTHORS, HACKING, MAINTAINERS, README
3314
3315 Nothing new, but make sure this is up-to-date.
3316
3317 Also tweak a bit autogen.sh and Makefile.am to make them look similar in
3318 all my modules.
3319
3320 M AUTHORS
3321 M HACKING
3322 M MAINTAINERS
3323 M Makefile.am
3324 M README
3325 M autogen.sh
3326 M gnome-menus.doap
3327
3328 commit 4cfd7db5d1ac221bc1397854a04dc40a654f1188
3329 Author: Vincent Untz <vuntz@gnome.org>
3330 Date: Wed Sep 8 17:48:37 2010 +0200
3331
3332 [misc] Update license files to latest text
3333
3334 Note that this doesn't change the license. The license text was updated
3335 for the latest FSF address, for example.
3336
3337 M COPYING
3338 M COPYING.LIB
3339
3340 commit bd02ce500383aa04914fda89900af93cdb33809d
3341 Author: Vincent Untz <vuntz@gnome.org>
3342 Date: Wed Sep 8 17:09:44 2010 +0200
3343
3344 [build] Rename configure.in to configure.ac
3345
3346 M autogen.sh
3347 R100 configure.in configure.ac
3348
3349 commit 8b14b78f279bad26580387b69a3274ba1197f66b
3350 Author: Dirgita <dirgitadevina@yahoo.co.id>
3351 Date: Wed Aug 4 10:36:10 2010 +0700
3352
3353 Updated Indonesian translation
3354
3355 M po/id.po
3356
3357 commit d524140a28c92719bdcc81df207c38c3c96052c0
3358 Author: Pablo Castellano <pablog@src.gnome.org>
3359 Date: Tue Aug 3 19:57:18 2010 +0200
3360
3361 Update git.mk from pango
3362
3363 M git.mk
3364
3365 commit d0cbd37e93980bae0781704765c56009913b0204
3366 Author: Gheyret Tohti <gheyret@yahoo.com>
3367 Date: Tue Aug 3 13:54:06 2010 +0200
3368
3369 Updated Uyghur translation
3370
3371 M po/ug.po
3372
3373 commit 784b664c760d655ae306d2d5d718231a1d09333f
3374 Author: Fran Diéguez <fran.dieguez@mabishu.com>
3375 Date: Tue Jul 20 12:10:56 2010 +0200
3376
3377 Updated Galician translations
3378
3379 M po/gl.po
3380
3381 commit 1100aed1ec5863ebfa80e51ca7f07e2b964459dd
3382 Author: Sense Hofstede <qense@ubuntu.com>
3383 Date: Sun Jul 18 00:34:23 2010 +0200
3384
3385 Fix forgotten copyright notices in the Frisian translations.
3386
3387 M po/fy.po
3388
3389 commit e29c70ada73e7f029423361da318685d517cbd2b
3390 Author: Sense Hofstede <qense@ubuntu.com>
3391 Date: Sun Jul 18 00:31:14 2010 +0200
3392
3393 Updated Frisian translation and added it to the LINGUAS file
3394
3395 M po/LINGUAS
3396 A po/fy.po
3397
3398 commit ce67a8b1603bb878131c23b625cf48b44ea8c477
3399 Author: Reuben Potts <reuben03@live.com>
3400 Date: Wed Jul 7 13:51:56 2010 +0200
3401
3402 Added Manx translation
3403
3404 M po/LINGUAS
3405 A po/gv.po
3406
3407 commit 5e30e13fd5923befda0372473f64a81b98b35f92
3408 Author: Baurzhan Muftakhidinov <baurthefirst@gmail.com>
3409 Date: Fri Jul 2 14:40:12 2010 +0300
3410
3411 Updated Kazakh translation for gdm
3412
3413 M po/kk.po
3414
3415 commit 2054996af842e6df1726e58d81a569f03e30d75f
3416 Author: Vincent Untz <vuntz@gnome.org>
3417 Date: Wed Jun 30 12:36:26 2010 +0200
3418
3419 [editor] Better fix for XDG_MENU_PREFIX support in editor
3420
3421 The previous fix was partly wrong, because the menu file referenced in
3422 the created user menu file was not using the prefix.
3423
3424 So instead of letting the library handle XDG_MENU_PREFIX, we handle it
3425 ourselves everywhere.
3426
3427 M simple-editor/GMenuSimpleEditor/menufilewriter.py
3428 M simple-editor/GMenuSimpleEditor/menutreemodel.py
3429
3430 commit ac425c5ac9e386fd171801496503d591d5c5ade2
3431 Author: Vincent Untz <vuntz@gnome.org>
3432 Date: Wed Jun 30 12:31:33 2010 +0200
3433
3434 [editor] Respect XDG_MENU_PREFIX when writing user menu file
3435
3436 https://bugzilla.gnome.org/show_bug.cgi?id=623197
3437
3438 M simple-editor/GMenuSimpleEditor/menufilewriter.py
3439
3440 commit e8a7198465e090f3e35fc1ac328800909574801f
3441 Author: Kristjan Schmidt <kristjan.schmidt@googlemail.com>
3442 Date: Wed Jun 23 17:15:51 2010 +0200
3443
3444 Updated Esperanto translation
3445
3446 M po/eo.po
3447
3448 commit 0e25cc961e1814cb2b6e6e2b9172a649f5542a50
3449 Author: Vincent Untz <vuntz@gnome.org>
3450 Date: Tue Jun 22 03:57:14 2010 +0200
3451
3452 [release] post-release bump to 2.30.3
3453
3454 M configure.in
3455
3456 commit 3ec065b74712c99d1cdde955e864f203afccb41b
3457 Author: Vincent Untz <vuntz@gnome.org>
3458 Date: Tue Jun 22 03:57:06 2010 +0200
3459
3460 [release] 2.30.2
3461
3462 M NEWS
3463 M README
3464 M configure.in
3465
3466 commit 320453668ea791f1d7c9f11d84363bfbae63fb25
3467 Author: Yaakov Selkowitz <yselkowitz@users.sourceforge.net>
3468 Date: Tue Jun 15 23:00:19 2010 -0500
3469
3470 [build] Do not dist gir_DATA
3471
3472 GIR files contain a shared-library attribute which varies per platform,
3473 and therefore must not be disted; see bug 621611 for rationale.
3474
3475 https://bugzilla.gnome.org/show_bug.cgi?id=621724
3476
3477 M libmenu/Makefile.am
3478
3479 commit 612aeb7cd40f57757a733d0805a52f6a6a000152
3480 Author: Fran Diéguez <fran.dieguez@mabishu.com>
3481 Date: Sun Jun 13 19:32:21 2010 +0200
3482
3483 Updated Galician translations
3484
3485 M po/gl.po
3486
3487 commit 7e1332e718118f16ab451a622d40ec3afde79c2b
3488 Author: Matej Urbančič <mateju@svn.gnome.org>
3489 Date: Fri May 21 22:24:04 2010 +0200
3490
3491 Updated Slovenian translation
3492
3493 M po/sl.po
3494
3495 commit f93cca66736b876b8739fb900b51c82e22c7aead
3496 Author: Thomas Thurman <tthurman@gnome.org>
3497 Date: Sun May 16 18:04:30 2010 -0400
3498
3499 Updated Shavian translation
3500
3501 M po/en@shaw.po
3502
3503 commit 0928101b34ac6bc6e7da5c8bd52d39af43a9f38f
3504 Author: Thomas Thurman <tthurman@gnome.org>
3505 Date: Wed May 12 18:41:06 2010 -0400
3506
3507 Updated Shavian transliteration
3508
3509 M po/en@shaw.po
3510
3511 commit a66c03d1771a15ffe054e9e64ff5183bda951bcf
3512 Author: Peteris Krisjanis <pecisk@gmail.com>
3513 Date: Sat Apr 24 22:25:22 2010 +0300
3514
3515 Updated Latvian translation.
3516
3517 M po/lv.po
3518
3519 commit 498f225b6f9a4a146a534bacdc8b8b665cf71d95
3520 Author: Daniel Martinez <entaltoaragon@gmail.com>
3521 Date: Sat Apr 24 11:21:36 2010 +0200
3522
3523 Added Aragonese translation
3524
3525 M po/LINGUAS
3526 A po/an.po
3527
3528 commit 5b8ae6e132b31a0a0f14021ef6cf2fc2794bc87e
3529 Author: Carles Ferrando <carles.ferrando@gmail.com>
3530 Date: Wed Apr 21 23:31:15 2010 +0200
3531
3532 Updated Catalan (Valencian) translation
3533
3534 M po/ca@valencia.po
3535
3536 commit 7408013978ba989af175f2ca17526fec4eea49c6
3537 Author: Shankar Prasad <svenkate@svenkate.pnq.redhat.com>
3538 Date: Wed Apr 21 11:50:02 2010 +0530
3539
3540 Updated Kannada translations
3541
3542 M po/kn.po
3543
3544 commit 20d4732caf167f54c376d34184ca7fdcf7d834b7
3545 Author: Jordi Serratosa <jordis.lists@gmail.com>
3546 Date: Fri Apr 2 17:41:22 2010 +0200
3547
3548 Fixes to Catalan translation
3549
3550 M po/ca.po
3551
3552 commit a08bbc660cd320245ea4f3ccab4c33261e83dc7b
3553 Author: Christian Kirbach <Christian.Kirbach@googlemail.com>
3554 Date: Wed Mar 31 20:11:39 2010 +0200
3555
3556 Updated German translation
3557
3558 M po/de.po
3559
3560 commit ffbddbf28289a4ac72b284e3cc6f4db04cf0b43e
3561 Author: Wouter Bolsterlee <wbolster@gnome.org>
3562 Date: Tue Mar 30 18:01:20 2010 +0200
3563
3564 Dutch translation updated by Wouter Bolsterlee
3565
3566 M po/nl.po
3567
3568 commit 8d9f3db359acde4395645a0ea95c48567e5d79f4
3569 Author: Reşat SABIQ <tilde.birlik@gmail.com>
3570 Date: Tue Mar 30 00:46:58 2010 -0500
3571
3572 Updated Crimean Tatar (Crimean Turkish) translation
3573
3574 M po/crh.po
3575
3576 commit fe6b717216cb93b05abc547fd11c550d9d4f4f4e
3577 Author: Vincent Untz <vuntz@gnome.org>
3578 Date: Tue Mar 30 02:02:15 2010 +0200
3579
3580 [release] post-release bump to 2.30.1
3581
3582 M configure.in
3583
3584 commit ada97a6e81d2f282796938269da7280dc5a8c83f
3585 Author: Vincent Untz <vuntz@gnome.org>
3586 Date: Tue Mar 30 02:02:06 2010 +0200
3587
3588 [release] 2.30.0
3589
3590 M NEWS
3591 M README
3592 M configure.in
3593
3594 commit 2d0068dc1859b3adf21cfcc47a23aa00fc7d4502
3595 Author: Kostas Papadimas <pkst@gnome.org>
3596 Date: Sun Mar 28 10:01:00 2010 +0300
3597
3598 Updated Greek translation
3599
3600 M po/el.po
3601
3602 commit b7d6ee4f3f252b57a85787043fa3506d1640fa05
3603 Author: Inaki Larranaga Murgoitio <dooteo@zundan.com>
3604 Date: Tue Mar 23 15:10:22 2010 +0100
3605
3606 Updated Basque language
3607
3608 M po/eu.po
3609
3610 commit e74ebd36d267033054e6bf15851da0f88d12eb3a
3611 Author: Badral Sanligiin <badral@openmn.org>
3612 Date: Tue Mar 23 02:57:26 2010 +0100
3613
3614 Updated Mongolian translation
3615
3616 M po/mn.po
3617
3618 commit 1bec0581f480c3e8b485be1453de389083feff5c
3619 Author: David Planella <david.planella@gmail.com>
3620 Date: Wed Mar 17 08:24:02 2010 +0100
3621
3622 Updated Catalan translation as per fixes discussed on the translation mailing list
3623
3624 M po/ca.po
3625
3626 commit 1253c82ed7ba774cbad7794c06d31249bf591c37
3627 Author: Vincent Untz <vuntz@gnome.org>
3628 Date: Mon Mar 15 09:55:46 2010 +0100
3629
3630 [libmenu] Never ignore Menuname nodes from DefaultLayout
3631
3632 If a Menuname node applies to a subdirectory that appears because of
3633 inlining, we generally want to ignore it. However, if this Menuname node
3634 comes from a DefaultLayout, then it should really be applied.
3635
3636 M libmenu/gmenu-tree.c
3637
3638 commit 7dba2d5302b539e24131e669e2db26f3c16e1547
3639 Author: Vincent Untz <vuntz@gnome.org>
3640 Date: Mon Mar 15 09:27:44 2010 +0100
3641
3642 [libmenu] Fix layout processing for Menuname nodes
3643
3644 Because of the scope of a variable, no submenu were matching Menuname
3645 nodes in the layouts, making all the Menuname nodes non-working.
3646
3647 https://bugzilla.gnome.org/show_bug.cgi?id=612585
3648
3649 M libmenu/gmenu-tree.c
3650
3651 commit c48304179db938f7336223f99ead95dda6044f7a
3652 Author: Changwoo Ryu <cwryu@debian.org>
3653 Date: Sat Mar 13 23:55:09 2010 +0900
3654
3655 Updated Korean translation
3656
3657 M po/ko.po
3658
3659 commit 87f03667154f5c77548506218c57020e37d04c81
3660 Author: Vincent Untz <vuntz@gnome.org>
3661 Date: Tue Mar 9 14:06:21 2010 +0100
3662
3663 [build] Add configure summary
3664
3665 M configure.in
3666
3667 commit 9fbaa3d7f76ffb4076216d231acf39da0331e0bc
3668 Author: Vincent Untz <vuntz@gnome.org>
3669 Date: Mon Mar 8 14:49:09 2010 +0100
3670
3671 [release] post-release bump to 2.30.0
3672
3673 M configure.in
3674
3675 commit 076f8c42ce3e46c3b5cb761a3cd65c6f1e0a55a2
3676 Author: Vincent Untz <vuntz@gnome.org>
3677 Date: Mon Mar 8 14:49:01 2010 +0100
3678
3679 [release] 2.29.92
3680
3681 M NEWS
3682 M README
3683 M configure.in
3684
3685 commit d50ff15f43bc2311fbd06bd9664ae9571fd550ea
3686 Author: Vincent Untz <vuntz@gnome.org>
3687 Date: Mon Mar 8 14:43:18 2010 +0100
3688
3689 [libmenu] Add gobject-introspection support
3690
3691 https://bugzilla.gnome.org/show_bug.cgi?id=598406
3692
3693 M Makefile.am
3694 M autogen.sh
3695 M configure.in
3696 M libmenu/Makefile.am
3697
3698 commit d3135a15585afcb8d597fae05beadfaf1c743238
3699 Author: Vincent Untz <vuntz@gnome.org>
3700 Date: Thu Mar 4 17:55:40 2010 +0100
3701
3702 [misc] Do not call bindtextdomain() and friends in test-menu-spec.c
3703
3704 We decided to not make this application translatable in commit
3705 25c10ed7, so we don't need those calls.
3706
3707 M util/test-menu-spec.c
3708
3709 commit 57e825050cf8eafb970c8ddd2dc9c66885d25ce3
3710 Author: Nikos Charonitakis <nikosx@gmail.com>
3711 Date: Thu Feb 25 00:52:48 2010 +0200
3712
3713 Updated Greek translation
3714
3715 M po/el.po
3716
3717 commit 274a633ddb053a70e26278308a6fb52ef933e608
3718 Author: Pavol Klačanský <pavol@klacansky.com>
3719 Date: Tue Feb 23 17:55:32 2010 +0100
3720
3721 Updated Slovak translation
3722
3723 M po/sk.po
3724
3725 commit 41d1ce2320c26dce1677e0624c36119ec88e6823
3726 Author: Vincent Untz <vuntz@gnome.org>
3727 Date: Mon Feb 22 20:18:20 2010 +0100
3728
3729 [release] post-release bump to 2.29.92
3730
3731 M configure.in
3732
3733 commit 5eaf95e2c9e7755332a7c586eef34726244290dd
3734 Author: Vincent Untz <vuntz@gnome.org>
3735 Date: Mon Feb 22 20:18:05 2010 +0100
3736
3737 [release] 2.29.91
3738
3739 M NEWS
3740 M README
3741 M configure.in
3742
3743 commit ca24481c06829c7382d20ac7ee2283e22b908451
3744 Author: Vincent Untz <vuntz@gnome.org>
3745 Date: Mon Feb 22 12:11:57 2010 +0100
3746
3747 [misc] Add translator comment for Personal
3748
3749 https://bugzilla.gnome.org/show_bug.cgi?id=610661
3750
3751 M desktop-directories/Personal.directory.in
3752
3753 commit 25c10ed70c8125516537b3b731dff1c559bcab94
3754 Author: Vincent Untz <vuntz@gnome.org>
3755 Date: Mon Feb 22 12:06:48 2010 +0100
3756
3757 [misc] Do not make the string test-menu-spec.c translatable
3758
3759 This is only a test program that doesn't get installed, so there's no
3760 point in making translators work on the strings there.
3761
3762 https://bugzilla.gnome.org/show_bug.cgi?id=609441
3763
3764 M po/POTFILES.in
3765 A po/POTFILES.skip
3766 M util/test-menu-spec.c
3767
3768 commit 8f52b3c312da587fb29590559cb50370787f6116
3769 Author: Fran Diéguez <frandieguez@ubuntu.com>
3770 Date: Mon Feb 15 18:44:37 2010 +0100
3771
3772 Updated Galician Translation
3773
3774 M po/gl.po
3775
3776 commit ef1383eda9480c96ac2e5d958342c98ab1be41cb
3777 Author: Torstein Adolf Winterseth <kvikende@yahoo.no>
3778 Date: Thu Jan 28 12:10:06 2010 +0100
3779
3780 Updated Norwegian Nynorsk translation
3781
3782 M po/nn.po
3783
3784 commit a0a398d77aa2ea28d267f106880e0df1417d8fab
3785 Author: Vincent Untz <vuntz@gnome.org>
3786 Date: Wed Jan 27 16:47:53 2010 +0100
3787
3788 [release] post-release bump to 2.29.90
3789
3790 M configure.in
3791
3792 commit 2c37a7cbdd9d6f7e9f1ccfb0d2979da897e9668e
3793 Author: Vincent Untz <vuntz@gnome.org>
3794 Date: Wed Jan 27 16:47:41 2010 +0100
3795
3796 [release] 2.29.6
3797
3798 M NEWS
3799 M README
3800 M configure.in
3801
3802 commit a462be862680ee3051eef3ca238f5bc405640e16
3803 Author: Jamil Ahmed <itsjamil@gmail.com>
3804 Date: Sun Jan 24 23:15:18 2010 +0600
3805
3806 Updated Bengali translation
3807
3808 M po/bn.po
3809
3810 commit fdafd8c07a65e65fdf6f2e7d358b7eb2b14d01eb
3811 Author: Aron Xu <aronxu@gnome.org>
3812 Date: Sun Jan 24 15:34:41 2010 +0800
3813
3814 Update Simplified Chinese translation.
3815
3816 M po/zh_CN.po
3817
3818 commit 89309a8bb1e76209e147180a3fe2fd15307d6012
3819 Author: Vincent Untz <vuntz@gnome.org>
3820 Date: Fri Jan 15 00:02:07 2010 +0100
3821
3822 [libmenu] Do not count non-inlining submenus as inlining with header
3823
3824 When computing if a submenu should be inlined, we use the number of
3825 items in the subsubmenus. It was always assuming that inlining with a
3826 header would be used, because of misuse of the will_inline_header field.
3827
3828 M libmenu/gmenu-tree.c
3829
3830 commit 4b51a77fcbc1a2c3e94945c4b92a65559f7e5996
3831 Author: Vincent Untz <vuntz@gnome.org>
3832 Date: Thu Jan 14 23:30:47 2010 +0100
3833
3834 [libmenu] Support inline alias of an inline alias
3835
3836 M libmenu/gmenu-tree.c
3837
3838 commit cd1d0a199110ca94266c3e8b928e61d12cd2aac3
3839 Author: Vincent Untz <vuntz@gnome.org>
3840 Date: Thu Jan 14 23:26:49 2010 +0100
3841
3842 [libmenu] Add real support for inline aliases during layout processing
3843
3844 It looks like the inline aliases were not really tested before...
3845
3846 All the processing of the layout info (merge_subdir_by_name,
3847 merge_entry_by_id, merge_subdirs, merge_entries,
3848 merge_subdirs_and_entries) was completely ignoring the fact that some
3849 subdirs or entries might be aliases, and therefore need a different
3850 processing.
3851
3852 This is now done correctly, and we have a merge_alias() function to do
3853 the right thing when an alias is detected.
3854
3855 Also, this simplifies quite a bit the sorting of subdirs or entries
3856 since we always need to check the types of the items we compare, so we
3857 can get rid of gmenu_tree_directory_compare and
3858 gmenu_tree_entry_compare.
3859
3860 M libmenu/gmenu-tree.c
3861
3862 commit cda451cec61345d357cbc26e5a5dfd102f476ea5
3863 Author: Vincent Untz <vuntz@gnome.org>
3864 Date: Thu Jan 14 21:29:42 2010 +0100
3865
3866 [libmenu] Fix miscalulation for inlining when inline_header = true
3867
3868 When inline_header = true for a subdirectory, the length of the content
3869 of the parent directory was miscomputed: instead of adding the length of
3870 the subdirectory, it was just replacing the current length with the
3871 length of the subdirectory.
3872
3873 Also fix a typo in a comment
3874
3875 M libmenu/gmenu-tree.c
3876
3877 commit b6dc7a2443ef3d8fef1a1175532124882e95d03b
3878 Author: krishnababu k <kkrothap@redhat.ocm>
3879 Date: Wed Dec 2 14:36:36 2009 +0530
3880
3881 Updated telugu translation
3882
3883 M po/te.po
3884
3885 commit 696867378e99b410c1803412c7f894c99b386f64
3886 Author: Thomas Thurman <tthurman@gnome.org>
3887 Date: Sat Oct 31 22:26:02 2009 -0400
3888
3889 Shavian translation
3890
3891 M po/LINGUAS
3892 A po/en@shaw.po
3893
3894 commit 4c76410de345e06b879c85fa015659286f42c240
3895 Author: Nils-Christoph Fiedler <linux@medienkompanie.de>
3896 Date: Thu Oct 22 19:47:54 2009 +0200
3897
3898 Updated Low German translation
3899
3900 M po/nds.po
3901
3902 commit 5f2c53bd3fbd4b39707609955f445c96eb9b5fb3
3903 Author: Reşat SABIQ <tilde.birlik@gmail.com>
3904 Date: Wed Oct 21 00:29:46 2009 -0500
3905
3906 Update for Crimean Tatar (Crimean Turkish) translation
3907
3908 M po/crh.po
3909
3910 commit bde6cabd440da28d25983e762ed6b71849da918f
3911 Author: Leonid Kanter <leon@asplinux.ru>
3912 Date: Tue Oct 20 12:06:06 2009 +0300
3913
3914 Updated Russian translation
3915
3916 M po/ru.po
3917
3918 commit 72f4ede1ab9bbcfa29affda98e1fcfecdfcf87ef
3919 Author: Gil Forcada <gforcada@gnome.org>
3920 Date: Tue Oct 6 17:36:50 2009 +0200
3921
3922 Updated Catalan translation
3923
3924 M po/ca.po
3925
3926 commit e1c00f83d903581233e7e15655916b4079b3e500
3927 Author: Sveinn í Felli <sveinki@nett.is>
3928 Date: Fri Oct 2 06:17:30 2009 +0000
3929
3930 Updated Icelandic translation
3931
3932 M po/is.po
3933
3934 commit 5aff8025f08164699721084857a5321ed2a45abe
3935 Author: Vincent Untz <vuntz@gnome.org>
3936 Date: Thu Oct 1 13:25:58 2009 +0200
3937
3938 [release] post-release bump to 2.28.1
3939
3940 M configure.in
3941
3942 commit 1d899c5b7bafb9ea01e0f407df7acb2b0d6432f4
3943 Author: Vincent Untz <vuntz@gnome.org>
3944 Date: Thu Oct 1 13:25:50 2009 +0200
3945
3946 [release] 2.28.0.1
3947
3948 M NEWS
3949 M README
3950 M configure.in
3951
3952 commit 9d8e52a9a3e495f1e8e50a61f89510a03a4d31c9
3953 Author: Vincent Untz <vuntz@gnome.org>
3954 Date: Thu Oct 1 13:21:45 2009 +0200
3955
3956 [build] Link the python binding to libpython
3957
3958 Thanks to Frederic Crozat <fcrozat@mandriva.com> for spotting this.
3959
3960 D acinclude.m4
3961 A m4/python.m4
3962 M python/Makefile.am
3963
3964 commit 117273842932a6fb24143926513474c28e3595b4
3965 Author: Frederic Crozat <fcrozat@mandriva.com>
3966 Date: Tue Sep 29 15:35:10 2009 +0200
3967
3968 [libmenu] Make sure to use a value when sorting items during a merge
3969
3970 The name variable was not assigned any value...
3971
3972 M libmenu/gmenu-tree.c
3973
3974 commit 5713f8e66b0fe3791a57555ebd12ddb076176cd9
3975 Author: Vincent Untz <vuntz@gnome.org>
3976 Date: Mon Sep 21 18:18:27 2009 +0200
3977
3978 [release] post-release bump to 2.28.1
3979
3980 M configure.in
3981
3982 commit ddc11bf8f9d279cc2578c074ccb2c85a837e4ba7
3983 Author: Vincent Untz <vuntz@gnome.org>
3984 Date: Mon Sep 21 18:18:18 2009 +0200
3985
3986 [release] 2.28.0
3987
3988 M NEWS
3989 M README
3990 M configure.in
3991
3992 commit 87d43c2dd053ae3cb16e53341db2cb60ae348cf9
3993 Author: Amitakhya Phukan <aphukan@fedoraproject.org>
3994 Date: Mon Sep 21 11:30:34 2009 +0530
3995
3996 Updating Assamese translations
3997
3998 M po/as.po
3999
4000 commit a44cc5ae04856e05300dc189cab1a9e0ea0d7697
4001 Author: Amitakhya Phukan <aphukan@fedoraproject.org>
4002 Date: Mon Sep 21 11:29:14 2009 +0530
4003
4004 Updating Assamese translations
4005
4006 M po/as.po
4007
4008 commit e6a5afde9de461ccbc45556ca7720d0b9e1c63a8
4009 Author: Shankar Prasad <svenkate@redhat.com>
4010 Date: Mon Sep 21 10:18:37 2009 +0530
4011
4012 Updated Kannada(kn) translation
4013
4014 M po/kn.po
4015
4016 commit dc324804a5105c78f5fded62232d7f83976dec2d
4017 Author: Petr Kovar <pknbe@volny.cz>
4018 Date: Mon Sep 21 04:22:31 2009 +0200
4019
4020 Updated Czech translation
4021
4022 M po/cs.po
4023
4024 commit c7045e1d8937e7730cde99e87dce31ccb3474d76
4025 Author: Rajesh Ranjan <rranjan@rranjan.csb>
4026 Date: Sun Sep 20 12:35:22 2009 +0530
4027
4028 maithili update, translated by Sangeeta Kumari
4029
4030 M po/mai.po
4031
4032 commit 3e5f8caa3f659f4a3ea7730ea79af98c639379ac
4033 Author: Daniel Nylander <po@danielnylander.se>
4034 Date: Sat Sep 19 10:25:52 2009 +0200
4035
4036 Updated Swedish translation
4037
4038 M po/sv.po
4039
4040 commit 8af7dc29b7ccf039422d70c38f3927d1e493e726
4041 Author: Luca Ferretti <elle.uca@libero.it>
4042 Date: Fri Sep 18 15:08:11 2009 +0200
4043
4044 Added Italian translation
4045
4046 M po/it.po
4047
4048 commit b46ad5eb16a78883c09ebddcf09634368052ac72
4049 Author: krishnababu k <kkrothap@redhat.ocm>
4050 Date: Thu Sep 17 21:43:06 2009 +0530
4051
4052 Updated Telugu Translation
4053
4054 M po/te.po
4055
4056 commit 9f35303a5dbb43c2f024d001de95dd58402e4519
4057 Author: Maxim V. Dziumanenko <dziumanenko@gmail.com>
4058 Date: Thu Sep 17 13:56:16 2009 +0300
4059
4060 Added Ukrainian translation
4061
4062 M po/uk.po
4063
4064 commit 9f656630180c4328bfe95e325c4e2afbf61a22d4
4065 Author: Niels-Christoph Fiedler <linux@medienkompanie.de>
4066 Date: Wed Sep 16 18:33:04 2009 +0200
4067
4068 Updated German translation
4069
4070 M po/nds.po
4071
4072 commit 3989f3da21c0b72c2161c4476620e992ca533a02
4073 Author: Adi Roiban <adi@roiban.ro>
4074 Date: Wed Sep 16 16:01:54 2009 +0300
4075
4076 Updated Romanian translation
4077
4078 M po/ro.po
4079
4080 commit a9f05d49e0e31e5cd7ec6ca6e835b3de438f3033
4081 Author: Rajesh Ranjan <rranjan@rranjan.csb>
4082 Date: Wed Sep 16 13:47:43 2009 +0530
4083
4084 hindi update by Rajesh Ranjan
4085
4086 M po/hi.po
4087
4088 commit 19d5f458acc6001cee1446caa39163f3369af273
4089 Author: Gintautas Miliauskas <gintautas@miliauskas.lt>
4090 Date: Tue Sep 15 02:11:17 2009 +0300
4091
4092 Updated Lithuanian translation.
4093
4094 M po/lt.po
4095
4096 commit 1102f0542b4df782708eb857e638bfd5966aa7dc
4097 Author: Ask H. Larsen <asklarsen@gmail.com>
4098 Date: Sun Sep 13 03:23:56 2009 +0200
4099
4100 Updated Danish translation
4101
4102 M po/da.po
4103
4104 commit da99034888d5191a27e3029cbad6c73c7e52e10a
4105 Author: Philip Withnall <philip@tecnocode.co.uk>
4106 Date: Sat Sep 12 23:35:46 2009 +0100
4107
4108 Updated British English translation
4109
4110 M po/en_GB.po
4111
4112 commit 67c6af706c287319a7d5c73cf86d5209fd89f44f
4113 Author: Chao-Hsiung Liao <j_h_liau@yahoo.com.tw>
4114 Date: Sun Sep 13 06:32:22 2009 +0800
4115
4116 Updated Traditional Chinese translation(Hong Kong and Taiwan)
4117
4118 M po/zh_HK.po
4119 M po/zh_TW.po
4120
4121 commit ebd7a9aaa45fb4b43ac7e5b35e45412ddd55dc9d
4122 Author: Ani <peter.ani@gmail.com>
4123 Date: Sat Sep 12 18:32:13 2009 +0530
4124
4125 Updaeted Malayalam Translations
4126
4127 M po/ml.po
4128
4129 commit 37d761d18917ba9167c2be84c8fd5c6948ff3139
4130 Author: Matej Urbančič <mateju@svn.gnome.org>
4131 Date: Sat Sep 12 10:31:29 2009 +0200
4132
4133 Updated Slovenian translation
4134
4135 M po/sl.po
4136
4137 commit 333ce6b4509b8137c085171b730b92f0d2870c7b
4138 Author: Sandeep Shedmake <sshedmak@redhat.com>
4139 Date: Fri Sep 11 19:35:53 2009 +0530
4140
4141 Updated Marathi Translations
4142
4143 M po/mr.po
4144
4145 commit 068006c9639123b7c7fe47855f5e1801c78c7dd0
4146 Author: A S Alam <aalam@users.sf.net>
4147 Date: Thu Sep 10 06:46:10 2009 +0530
4148
4149 updating for Punjabi
4150
4151 M po/pa.po
4152
4153 commit 7411701b1e806aa8f283ea9868d165ab150ac2c7
4154 Author: Baris Cicek <baris@teamforce.name.tr>
4155 Date: Thu Sep 10 00:25:54 2009 +0300
4156
4157 Updated Turkish translation.
4158
4159 M po/tr.po
4160
4161 commit eb43a84b5a1b55f1743e9d0b9aee036a72962b17
4162 Author: Vincent Untz <vuntz@gnome.org>
4163 Date: Wed Sep 9 02:10:28 2009 +0200
4164
4165 [build] Generate bzip2 tarballs and use foreign automake option
4166
4167 M configure.in
4168
4169 commit 4d35bde7e67f6b59c108ebc8615a342cb93cbd17
4170 Author: Vincent Untz <vuntz@gnome.org>
4171 Date: Wed Sep 9 02:08:49 2009 +0200
4172
4173 [release] post-release bump to 2.28.0
4174
4175 M configure.in
4176
4177 commit 908771796bad88ce49158f951bd7ca40c51b256b
4178 Author: Vincent Untz <vuntz@gnome.org>
4179 Date: Wed Sep 9 02:08:40 2009 +0200
4180
4181 [release] 2.27.92
4182
4183 M NEWS
4184 M README
4185 M configure.in
4186
4187 commit f1a8145230fc24f47f4818f39839aafe5b911c37
4188 Author: Takayuki KUSANO <AE5T-KSN@asahi-net.or.jp>
4189 Date: Tue Sep 8 22:14:00 2009 +0900
4190
4191 Updated Japanese translation
4192
4193 M po/ja.po
4194
4195 commit 0178e1acc5aeca2e05ddfcf67e8f03e8ee82eff6
4196 Author: Vincent Untz <vuntz@gnome.org>
4197 Date: Tue Sep 8 00:20:56 2009 +0200
4198
4199 [misc] Bump version to 2.27.92
4200
4201 M configure.in
4202
4203 commit db81a7f1194e435b538ab6710d752276fb1c7135
4204 Author: Vincent Untz <vuntz@gnome.org>
4205 Date: Mon Sep 7 23:28:56 2009 +0200
4206
4207 [editor] Use display name instead of name
4208
4209 M simple-editor/GMenuSimpleEditor/menutreemodel.py
4210
4211 commit d3fdd0ba4133c66e670561bca5b1bf4dd1a8feea
4212 Author: Vincent Untz <vuntz@gnome.org>
4213 Date: Mon Sep 7 23:13:22 2009 +0200
4214
4215 [libmenu,python] Add gmenu_tree_get_sort_key()/gmenu_tree_set_sort_key()
4216
4217 This is needed for users of gnome-menus that display applications by
4218 strings obtained via get_display_name(), since the order of entry
4219 changes if strings are different.
4220
4221 The default is to keep the sort order by name. At the moment, the only
4222 other option is to use a sort by display name.
4223
4224 Bindings for python are added too.
4225
4226 M libmenu/gmenu-tree.c
4227 M libmenu/gmenu-tree.h
4228 M python/gmenu.c
4229
4230 commit 4b007ffddf4cfd58d508a8c24d49ca8724753bb0
4231 Author: Vincent Untz <vuntz@gnome.org>
4232 Date: Mon Sep 7 23:19:49 2009 +0200
4233
4234 [python] Fix accidental swap between display_name and generic_name
4235
4236 M python/gmenu.c
4237
4238 commit 22b78e8b3cbc5d68638a1729728b682a4ce1d10b
4239 Author: Vincent Untz <vuntz@gnome.org>
4240 Date: Mon Sep 7 19:21:12 2009 +0200
4241
4242 [python] Add bindings for get_generic_name() and get_display_name()
4243
4244 M python/gmenu.c
4245
4246 commit e539c64e99a000b3a89a53bb5a6233a6b16ed96a
4247 Author: Vincent Untz <vuntz@gnome.org>
4248 Date: Mon Sep 7 19:19:41 2009 +0200
4249
4250 [libmenu] Add gmenu_tree_entry_get_display_name() API
4251
4252 This API returns the content of the X-GNOME-FullName key if available,
4253 and fallbacks to Name.
4254
4255 M libmenu/desktop-entries.c
4256 M libmenu/desktop-entries.h
4257 M libmenu/gmenu-tree.c
4258 M libmenu/gmenu-tree.h
4259
4260 commit 9767b9094ef22034be948f289473726e922e1d28
4261 Author: Gabor Kelemen <kelemeng@gnome.hu>
4262 Date: Mon Sep 7 02:30:34 2009 +0200
4263
4264 Hungarian translation updated
4265
4266 M po/hu.po
4267
4268 commit c2e0a3cedf6ceded675e6d6b8368d98dc8e64716
4269 Author: Rodrigo L. M. Flores <rlmflores@src.gnome.org>
4270 Date: Sun Aug 30 19:56:20 2009 -0300
4271
4272 Updated Brazilian Portuguese mailing list address.
4273
4274 M po/pt_BR.po
4275
4276 commit e5c016f25d380332029e4b4d78ebc795fcc06e27
4277 Author: Mario Blättermann <mariobl@gnome.org>
4278 Date: Sun Aug 30 14:54:33 2009 +0200
4279
4280 Updated German translation
4281
4282 M po/de.po
4283
4284 commit aadd320b39ccd5a713b2fc9f5fb85d894141c61f
4285 Author: Miloš Popović <mpopovic@src.gnome.org>
4286 Date: Sat Aug 29 14:43:42 2009 +0200
4287
4288 Updated Serbian translation
4289
4290 M po/sr.po
4291 M po/sr@latin.po
4292
4293 commit 900767c9056455409c0ad674d17959b50b1a0f49
4294 Author: Runa Bhattacharjee <runab@redhat.com>
4295 Date: Fri Aug 28 12:30:47 2009 +0530
4296
4297 Updated Bengali India Translations
4298
4299 M po/bn_IN.po
4300
4301 commit 66913548e742cc1a8c138fccce9302b88fe5eae9
4302 Author: Duarte Loreto <happyguy_pt@hotmail.com>
4303 Date: Thu Aug 27 08:58:37 2009 +0100
4304
4305 Updated Portuguese translation
4306
4307 M po/pt.po
4308
4309 commit 4dbf30628a5f29f8eb0b85c65ffd5b9b0caa5177
4310 Author: Claude Paroz <claude@2xlibre.net>
4311 Date: Wed Aug 26 21:33:09 2009 +0200
4312
4313 Updated French translation
4314
4315 M po/fr.po
4316
4317 commit bcf479be8e2b92c26becafe004a0f1b1c62af679
4318 Author: Inaki Larranaga Murgoitio <dooteo@zundan.com>
4319 Date: Wed Aug 26 18:27:07 2009 +0200
4320
4321 Updated Basque language
4322
4323 M po/eu.po
4324
4325 commit 5ee416dd4c7b0ddb11c7c38581bf293b3b9352dd
4326 Author: Luca Ferretti <elle.uca@libero.it>
4327 Date: Wed Aug 26 09:31:11 2009 +0200
4328
4329 Updated Italian translation
4330
4331 M po/it.po
4332
4333 commit a02a132ed3b18f156bf2c7e965c3075685f72759
4334 Author: Tomasz Dominikowski <dominikowski@gmail.com>
4335 Date: Tue Aug 25 19:26:18 2009 +0200
4336
4337 Updated Polish translation
4338
4339 M po/pl.po
4340
4341 commit 6f55d2289a2a30d03db2bf40c31f79474ff952c0
4342 Author: Frédéric Péters <fpeters@0d.be>
4343 Date: Sat Aug 22 13:44:25 2009 +0200
4344
4345 Remove deprecated Encoding key from desktop file
4346
4347 M simple-editor/gmenu-simple-editor.desktop.in
4348
4349 commit 4771bdb6af1b29f10ba39706cb2b63433d1f0480
4350 Author: Andre Klapper <a9016009@gmx.de>
4351 Date: Mon Aug 24 23:01:03 2009 +0200
4352
4353 Change nds_NFE to nds as discussed on irc
4354
4355 M po/LINGUAS
4356 R100 po/nds_NFE.po po/nds.po
4357
4358 commit 539360b4cfa0c26b884c9811acd15d943a7574a0
4359 Author: Sweta Kothari <swkothar@redhat.com>
4360 Date: Mon Aug 24 15:37:44 2009 +0530
4361
4362 Updated Gujarati Translations
4363
4364 M po/gu.po
4365
4366 commit 28501f2afe901298d74760362f47bb7f67107699
4367 Author: Changwoo Ryu <cwryu@debian.org>
4368 Date: Sun Aug 23 07:27:42 2009 +0900
4369
4370 Update Korean translation
4371
4372 M po/ko.po
4373
4374 commit b2b842dbd5fd8687562d0961ad62391d5f72004f
4375 Author: Mario Blättermann <mariobl@gnome.org>
4376 Date: Sat Aug 22 15:09:52 2009 +0200
4377
4378 Added Low German to LINGUAS
4379
4380 M po/LINGUAS
4381
4382 commit 4694660b8ebac75d9c72ef1de5654bd031a9062e
4383 Author: Mario Blättermann <mariobl@gnome.org>
4384 Date: Sat Aug 22 15:07:10 2009 +0200
4385
4386 Added Low German translation
4387
4388 A po/nds_NFE.po
4389
4390 commit 9dc36cf2b049dbca649c92911f29644b073f031b
4391 Author: Khaled Hosny <khaledhosny@eglug.org>
4392 Date: Thu Aug 20 21:19:07 2009 +0300
4393
4394 Updated Arabic translation
4395
4396 M po/ar.po
4397
4398 commit 895ba3af6f107e61cc6a4414746d94ba3df38a8f
4399 Author: Alexander Shopov <ash@contact.bg>
4400 Date: Fri Aug 14 07:48:02 2009 +0300
4401
4402 Updated Bulgarian translation
4403
4404 M po/bg.po
4405
4406 commit 761e44b26fd2315f8823d3b5f2347346f1ef9eb0
4407 Author: Manoj Kumar Giri <mgiri@mgiri.csb>
4408 Date: Thu Aug 13 20:09:24 2009 +0530
4409
4410 Updated Oriya Translation
4411
4412 M po/or.po
4413
4414 commit c49f4bb691fd51039add261b9a19c5b45bbbd0d5
4415 Author: Antón Méixome <meixome@mancoumun.org>
4416 Date: Wed Aug 12 13:25:27 2009 +0200
4417
4418 Updated Galician translation
4419
4420 M po/gl.po
4421
4422 commit 3ed6cedb4154fa399f48b036088fa703291d28d9
4423 Author: Krix Apolinário <krixapolinario@gmail.com>
4424 Date: Wed Aug 12 00:01:59 2009 -0300
4425
4426 Updated Brazilian Portuguese translation.
4427
4428 M po/pt_BR.po
4429
4430 commit 3f85e31f5fbb7e3e77ed8459af8b18f071ed3767
4431 Author: Kjartan Maraas <kmaraas@gnome.org>
4432 Date: Tue Aug 4 18:15:34 2009 +0200
4433
4434 Updated Norwegian bokmål translation.
4435
4436 M po/nb.po
4437
4438 commit ed5f86151fd103c446535bc8d1ed3d80604e2899
4439 Author: Seán de Búrca <leftmostcat@gmail.com>
4440 Date: Wed Jul 29 17:45:34 2009 -0600
4441
4442 Updated Irish translation
4443
4444 M po/ga.po
4445
4446 commit 6b5cd5918a318712237b5fc511fe122a1229a29b
4447 Author: Denis Arnaud <darnaud@src.gnome.org>
4448 Date: Wed Jul 29 12:13:13 2009 +0200
4449
4450 Updated breton translation
4451
4452 M po/br.po
4453
4454 commit c6f657e60f70bb848b70b9462591a38c973b4010
4455 Author: drtvasudevan <agnihot3@gmail.com>
4456 Date: Wed Jul 29 12:12:26 2009 +0530
4457
4458 Updated Tamil translation
4459
4460 M po/ta.po
4461
4462 commit 458924440a7f69c64272ddeb9fbe1999ac70bbcd
4463 Author: drtvasudevan <agnihot3@gmail.com>
4464 Date: Wed Jul 29 12:10:37 2009 +0530
4465
4466 Updated Tamil translation
4467
4468 M po/ta.po
4469
4470 commit 3d9f5ce3d34839e2342692a849838c9e7503c588
4471 Author: Denis Arnaud <darnaud@src.gnome.org>
4472 Date: Wed Jul 29 07:09:51 2009 +0200
4473
4474 Updated breton translation
4475
4476 M po/br.po
4477
4478 commit ea7245e45238b380a8ba11ee8dabc67775e49c6f
4479 Author: Vincent Untz <vuntz@gnome.org>
4480 Date: Wed Jul 29 00:08:38 2009 +0200
4481
4482 [misc] Update commit guidelines for referencing bugs
4483
4484 M ChangeLog
4485
4486 commit 374cdd09e33122d6a7f06eec9748130b3c85f9fe
4487 Author: Vincent Untz <vuntz@gnome.org>
4488 Date: Tue Jul 28 18:07:17 2009 +0200
4489
4490 [release] post-release bump to 2.27.90
4491
4492 M configure.in
4493
4494 commit e399908c171a0add6ab6febd405b5121b3772fd2
4495 Author: Vincent Untz <vuntz@gnome.org>
4496 Date: Tue Jul 28 18:07:07 2009 +0200
4497
4498 [release] 2.27.5
4499
4500 M NEWS
4501 M README
4502 M configure.in
4503
4504 commit 2bb16693175f59d28c8a798d50ffb9b462c916f3
4505 Author: Denis Arnaud <darnaud@src.gnome.org>
4506 Date: Tue Jul 28 14:10:04 2009 +0200
4507
4508 Updated breton translation
4509
4510 M po/br.po
4511
4512 commit b9cbf41d7c93ad8f18a5b9162dbd85a9e2774efa
4513 Author: Aron Xu <aronxu@gnome.org>
4514 Date: Sun Jul 26 18:25:28 2009 +0800
4515
4516 Updated Simplified Chinese translation.
4517
4518 M po/zh_CN.po
4519
4520 commit 0aed3c9a813e79ad57ec335e2aa410beb5d23991
4521 Author: Theppitak Karoonboonyanan <thep@linux.thai.net>
4522 Date: Thu Jul 23 15:13:40 2009 +0700
4523
4524 Updated Thai translation.
4525
4526 M po/th.po
4527
4528 commit 83e66d9b0487b0be48ea935801d500e9857a4ae5
4529 Author: Vincent Untz <vuntz@gnome.org>
4530 Date: Tue Jul 21 19:35:18 2009 +0200
4531
4532 [build] Set m4/ as macro dir
4533
4534 M Makefile.am
4535 M configure.in
4536
4537 commit 34c1fcb2306df9ca22634a82e75865eda1031769
4538 Author: Vincent Untz <vuntz@gnome.org>
4539 Date: Tue Jul 21 18:53:35 2009 +0200
4540
4541 [build] Use silent-rules instead of shave for quiet build
4542
4543 M configure.in
4544 D m4/shave.m4
4545 D shave-libtool.in
4546 D shave.in
4547 M simple-editor/GMenuSimpleEditor/Makefile.am
4548 M simple-editor/Makefile.am
4549
4550 commit 8ca7e0223b9f0e935889c146bfb5378bfeadf0ea
4551 Author: Ilkka Tuohela <hile@iki.fi>
4552 Date: Sun Jul 19 10:39:57 2009 +0300
4553
4554 Updated Finnish translation
4555
4556 M po/fi.po
4557
4558 commit b8fa21a37017f3fe5e333a616434c5b489e7f333
4559 Author: Vincent Untz <vuntz@gnome.org>
4560 Date: Wed Jul 15 16:52:26 2009 +0200
4561
4562 [release] post-release bump to 2.27.5
4563
4564 M configure.in
4565
4566 commit c17916822f13accf78dfc4d1f3ef8bc398e398f5
4567 Author: Vincent Untz <vuntz@gnome.org>
4568 Date: Wed Jul 15 16:52:15 2009 +0200
4569
4570 [release] 2.27.4
4571
4572 M NEWS
4573 M README
4574 M configure.in
4575
4576 commit d49ae45dc30c3fbafded146e5fbccbe23215316b
4577 Author: Daniel Nylander <po@danielnylander.se>
4578 Date: Tue Jul 7 19:37:51 2009 +0200
4579
4580 Updated Swedish translation
4581
4582 M po/sv.po
4583
4584 commit 0b7478a5de36192e3bc31576c7e8114a3a6dbed3
4585 Author: Yaron Shahrabani <sh.yaron@gmail.com>
4586 Date: Sat Jul 4 22:39:12 2009 +0300
4587
4588 Updated Hebrew translation
4589
4590 M po/he.po
4591
4592 commit 8bddaf2076e094043189a20c36bf66b8c3813921
4593 Author: Vincent Untz <vuntz@gnome.org>
4594 Date: Sat Jun 27 04:18:02 2009 +0200
4595
4596 [libmenu] Sort inlined items unless inline_header is used
4597
4598 We also strip duplicate entries in a menu when we do such a sort.
4599
4600 This is actually not a trivial task since it means we have to correctly
4601 preprocess all inline data before using the layout -- else, we'd try to
4602 sort items while a layout has been used, which is too late (since you
4603 can specify that you want mixed entries and directories, eg; or even
4604 entries or directories in a specified order).
4605
4606 We make sure to evaluate the inline data at the deepest first, so we can
4607 correctly propagate as much inline items as possible to the top.
4608
4609 http://bugzilla.gnome.org/show_bug.cgi?id=490483
4610
4611 M libmenu/gmenu-tree.c
4612
4613 commit 0b1f053e07d1d4c081a58303cfe7ed6b583ed966
4614 Author: Jorge Gonzalez <jorgegonz@svn.gnome.org>
4615 Date: Sat Jun 27 14:58:46 2009 +0200
4616
4617 Updated Spanish translation
4618
4619 M po/es.po
4620
4621 commit 7a6b5b5a292357357f4458820b6e8f851472c592
4622 Author: Vincent Untz <vuntz@gnome.org>
4623 Date: Fri Jun 26 23:42:59 2009 +0200
4624
4625 [libmenu] Do not always inherit parent DefaultLayout attributes
4626
4627 We should not use the parent DefaultLayout attributes when there no
4628 DefaultLayout children for the child but there are DefaultLayout
4629 attributes for the child.
4630
4631 Ie, if the child has <DefaultLayout inline="true"/>, then it used to
4632 keep inheriting the parent DefaultLayout attributes, and then possibly
4633 lose the inline="true".
4634
4635 M libmenu/gmenu-tree.c
4636
4637 commit 89ab14f5fd34d18ef1591baf1000f5197344cd92
4638 Author: Vincent Untz <vuntz@gnome.org>
4639 Date: Fri Jun 26 23:19:47 2009 +0200
4640
4641 [libmenu] Fix DefaultLayout attributes not being inherited
4642
4643 The submenus were not inheriting the DefaultLayout attributes (eg,
4644 all the inline parameters) of their parent when there was no child to
4645 the DefaultLayout of their parent.
4646
4647 Concretely, this was ignored for submenus:
4648 <DefaultLayout inline="true"/>
4649 while this should have behaved like:
4650 <DefaultLayout inline="true"><Merge type="all"/></DefaultLayout>
4651
4652 M libmenu/gmenu-tree.c
4653
4654 commit 6f948049c368cad02b713332430b98c5daa11b5e
4655 Author: Mattias Põldaru <mahfiaz gmail com>
4656 Date: Thu Jun 25 14:29:03 2009 +0300
4657
4658 Updating Estonian translation
4659
4660 M po/et.po
4661
4662 commit 80f5477a91e11630403d60b701658c7ae242c7d7
4663 Author: Claude Paroz <claude@2xlibre.net>
4664 Date: Sun Jun 21 19:35:00 2009 +0200
4665
4666 Add translator comment
4667
4668 M simple-editor/GMenuSimpleEditor/main.py
4669
4670 commit fc7e3ebf3266a47de4dc96a2fc938e01848eb53d
4671 Author: Claude Paroz <claude@2xlibre.net>
4672 Date: Sun Jun 21 19:28:21 2009 +0200
4673
4674 Fix POTFILES.in
4675
4676 M po/POTFILES.in
4677
4678 commit dca731f53b3686436cc94cf2704035419c03b7e8
4679 Author: Robert Staudinger <robsta@gnome.org>
4680 Date: Thu May 7 17:28:08 2009 +0200
4681
4682 [libmenu] Add API to access GenericName
4683
4684 http://bugzilla.gnome.org/show_bug.cgi?id=581887
4685
4686 M libmenu/desktop-entries.c
4687 M libmenu/desktop-entries.h
4688 M libmenu/gmenu-tree.c
4689 M libmenu/gmenu-tree.h
4690
4691 commit d504fabf22e5303abd631e5a868fb5f45f4c7b05
4692 Author: Pedro Fragoso <ember@ubuntu.com>
4693 Date: Sun Jun 21 01:17:26 2009 +0200
4694
4695 [editor] Port to GtkBuilder
4696
4697 (with a few tweaks by me to make it work)
4698
4699 http://bugzilla.gnome.org/show_bug.cgi?id=580158
4700
4701 M po/POTFILES.in
4702 M simple-editor/GMenuSimpleEditor/config.py.in
4703 M simple-editor/GMenuSimpleEditor/main.py
4704 M simple-editor/GMenuSimpleEditor/maindialog.py
4705 M simple-editor/Makefile.am
4706 D simple-editor/gmenu-simple-editor.glade
4707 A simple-editor/gmenu-simple-editor.ui
4708
4709 commit fecc2847cf75048a6fe41c7423ac1e705a32b12b
4710 Author: Vincent Untz <vuntz@gnome.org>
4711 Date: Sun Jun 21 01:09:40 2009 +0200
4712
4713 [editor] Add --help and --version arguments
4714
4715 http://bugzilla.gnome.org/show_bug.cgi?id=552989
4716
4717 M simple-editor/GMenuSimpleEditor/main.py
4718
4719 commit db6d999b0ada473a3124a14dd83ed7ccb2375474
4720 Author: Seán de Búrca <leftmostcat@gmail.com>
4721 Date: Sun May 24 21:21:58 2009 +0100
4722
4723 Updated Irish translation
4724
4725 M po/ga.po
4726
4727 commit 0aa50a649aa76ea95f2cda225a87448e8a16b69c
4728 Author: Inash Zubair <inash@leptone.com>
4729 Date: Sat May 23 13:32:29 2009 +0200
4730
4731 Added Divehi translation
4732
4733 M po/LINGUAS
4734 A po/dv.po
4735
4736 commit 569e4835258906ca51b6b32e062cb3f860e5aeac
4737 Author: Michael Meeks <michael.meeks@novell.com>
4738 Date: Thu May 21 03:55:59 2009 +0200
4739
4740 [libmenu] Add a cache for listing the desktop files
4741
4742 When processing a layout, we get the list of desktop files from a set of
4743 directories. It turns out that we nearly always use the same set of
4744 directories, so adding a one-entry cache makes it possible to avoid
4745 computing things again and again.
4746
4747 I changed Michael's patch a bit, mainly to empty the cache when the last
4748 GMenuTree is unref'ed.
4749
4750 Closes: bgo#498749
4751
4752 M libmenu/entry-directories.c
4753 M libmenu/entry-directories.h
4754 M libmenu/gmenu-tree.c
4755
4756 commit 020085ed0a4086509ede57eb4fb480bbdbb5c79a
4757 Author: Vincent Untz <vuntz@gnome.org>
4758 Date: Sat May 2 02:24:07 2009 +0200
4759
4760 Update commit messages guidelines
4761
4762 M ChangeLog
4763
4764 commit 4900c5713c5caf69d4d476023fb5638fb9d60a17
4765 Author: Vincent Untz <vuntz@gnome.org>
4766 Date: Mon Apr 27 00:20:04 2009 +0200
4767
4768 Use git.mk from pango to autogenerate .gitignore files
4769
4770 M Makefile.am
4771 M desktop-directories/Makefile.am
4772 A git.mk
4773 M layout/Makefile.am
4774 M libmenu/Makefile.am
4775 M python/Makefile.am
4776 M simple-editor/GMenuSimpleEditor/Makefile.am
4777 M simple-editor/Makefile.am
4778 M util/Makefile.am
4779
4780 commit c11452a831ab1ecf15ccb516492740980bda7c18
4781 Author: Vincent Untz <vuntz@gnome.org>
4782 Date: Mon Apr 27 01:10:53 2009 +0200
4783
4784 Remove .cvsignore files
4785
4786 D .cvsignore
4787 D desktop-directories/.cvsignore
4788 D layout/.cvsignore
4789 D libmenu/.cvsignore
4790 D po/.cvsignore
4791 D python/.cvsignore
4792 D simple-editor/.cvsignore
4793 D simple-editor/GMenuSimpleEditor/.cvsignore
4794 D util/.cvsignore
4795
4796 commit 5fa8af519c166de0a0abd4d9cf0e396540270b4f
4797 Author: Vincent Untz <vuntz@gnome.org>
4798 Date: Mon Apr 27 00:13:56 2009 +0200
4799
4800 Use shave to improve build log readability
4801
4802 See http://git.lespiau.name/cgit/shave/tree/README for more details.
4803
4804 M configure.in
4805 A m4/shave.m4
4806 A shave-libtool.in
4807 A shave.in
4808 M simple-editor/GMenuSimpleEditor/Makefile.am
4809 M simple-editor/Makefile.am
4810
4811 commit 22e86da4dfd79d8f2084bce3537607fa2b9bd40e
4812 Author: Vincent Untz <vuntz@gnome.org>
4813 Date: Sun Apr 26 12:26:11 2009 +0200
4814
4815 Fix doap file to have mail address as URL (mailto:)
4816
4817 M gnome-menus.doap
4818
4819 commit 1e3078b3ef1f58c7f6e8878ff0836d312c3e57dc
4820 Author: Vincent Untz <vuntz@gnome.org>
4821 Date: Fri Apr 24 03:25:12 2009 +0200
4822
4823 Make autogen.sh more modern.
4824
4825 M autogen.sh
4826
4827 commit 2f4ef97ac8153a1460b2a1404c4f622712b3db81
4828 Author: Vincent Untz <vuntz@gnome.org>
4829 Date: Wed Apr 22 17:28:55 2009 +0200
4830
4831 Add doap file
4832
4833 A gnome-menus.doap
4834
4835 commit e5c0da200efb4a82d8478d45c62959b848085d77
4836 Author: Vincent Untz <vuntz@gnome.org>
4837 Date: Wed Apr 22 17:27:40 2009 +0200
4838
4839 ename ChangeLog files to ChangeLog.pre-git
4840
4841 M ChangeLog
4842 C100 ChangeLog ChangeLog.pre-git
4843 R100 po/ChangeLog po/ChangeLog.pre-git
4844
4845 commit 71593a96a06add451182f6b2bf848daaae3d37a0
4846 Author: Miquel Esplà <miquelespla@gmail.com>
4847 Date: Tue Apr 21 15:05:10 2009 +0200
4848
4849 Added Valencian-Catalan translation
4850
4851 M po/LINGUAS
4852 A po/ca@valencia.po
4853
4854 commit 44190fe0edccfe82eeed523af572bcda3cb55dab
4855 Author: Vincent Untz <vuntz@gnome.org>
4856 Date: Mon Apr 13 22:05:39 2009 +0000
4857
4858 post-release bump to 2.26.2
4859
4860 2009-04-14 Vincent Untz <vuntz@gnome.org>
4861
4862 * configure.in: post-release bump to 2.26.2
4863
4864 svn path=/trunk/; revision=1017
4865
4866 M ChangeLog
4867 M configure.in
4868 M po/ChangeLog
0 Hacking on gnome-menus
1 ======================
2
3 + The development occurs in git:
4
5 http://git.gnome.org/browse/gnome-menus
6
7 For information on how to access GNOME git please read:
8
9 http://live.gnome.org/Git
10
11 + Please send patches as bug reports in GNOME Bugzilla:
12
13 https://bugzilla.gnome.org/ (product gnome-menus)
14
15 Your patch should be in unified diff form (the -u option to GNU
16 diff). See also:
17
18 http://live.gnome.org/GnomeLove/SubmittingPatches
19
20 + Please try and send a patch against a recent version of this package.
21 Patches against git master are most preferable.
22
23 + Don't commit any but the most trivial patches without approval.
24
25 + Exceptions to this are:
26
27 - Translators may commit basic i18n related patches to the build
28 setup.
29 - Build sheriff are welcome - in accordance with the relevant build
30 sheriff constraints.
0 Currently active maintainers
1 ----------------------------
2
3 Jasper St. Pierre
4 E-mail: jstpierre@mecheye.net
5 Userid: jstpierre
6
7 Non-active maintainers, who have a good understanding of the code
8 -----------------------------------------------------------------
9
10 #Mark McLoughlin
11 #E-mail: mark@skynet.ie
12
13 #Vincent Untz
14 #E-mail: vuntz@gnome.org
15 #Userid: vuntz
0 SUBDIRS = libmenu
1
2 ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
3
4 DISTCHECK_CONFIGURE_FLAGS = --enable-introspection
5
6 EXTRA_DIST = \
7 HACKING \
8 MAINTAINERS
9
10 MAINTAINERCLEANFILES = \
11 $(srcdir)/INSTALL \
12 $(srcdir)/aclocal.m4 \
13 $(srcdir)/config.guess \
14 $(srcdir)/config.h.in \
15 $(srcdir)/config.sub \
16 $(srcdir)/depcomp \
17 $(srcdir)/install-sh \
18 $(srcdir)/ltmain.sh \
19 $(srcdir)/missing \
20 $(srcdir)/mkinstalldirs \
21 $(srcdir)/py-compile \
22 `find "$(srcdir)" -type f -name Makefile.in -print` \
23 $(srcdir)/configure \
24 $(srcdir)/m4/intltool.m4 \
25 $(srcdir)/m4/libtool.m4 \
26 $(srcdir)/m4/ltoptions.m4 \
27 $(srcdir)/m4/ltsugar.m4 \
28 $(srcdir)/m4/ltversion.m4 \
29 $(srcdir)/m4/lt~obsolete.m4
30
+1790
-0
NEWS less more
0 =============
1 Version 3.8.0
2 =============
3
4 * Add more java things to sundry
5 * Add im-chooser, power statistics and personal file
6 sharing to sundry
7 * Remove special-casing for Fedora vendor prefixdd
8 * Translation updates
9
10 ==============
11 Version 3.7.90
12 ==============
13
14 Bugs Fixed:
15 * Fix error reporting
16 * Memory leak fixes (William John McCann)
17 * Remove the simple editor (William John McCann)
18 * New menu layout for gnome-shell tweaks (William John McCann,
19 Florian Muellner, Jasper St. Pierre)
20 * Remove old gnome-control-center files (Jasper St. Pierre)
21
22 Translation updates:
23 * Alexandre Franke
24 * Fabio Tomat
25 * Inaki Larranaga Murgoitio
26 * Piotr Drąg
27 * William Jon McCann
28
29 =============
30 Version 3.6.2
31 =============
32
33 * 688972 call menu_layout_load() with non_prefixed_name parameter
34 * Translation updates (Arabic, Kannada, Uyghur)
35
36 =============
37 Version 3.6.1
38 =============
39
40 * Translation updates (Catalan, Catalan (Valencian), Ukrainian,
41 Aragonese, Slovak, Uzbek)
42
43 =============
44 Version 3.6.0
45 =============
46
47 * Translation updates
48
49 ==============
50 Version 3.5.92
51 ==============
52
53 libmenu
54
55 * Add proper header reference to GMenu-3.0.gir (Rico Tzschichholz)
56
57 Translators
58
59 * Nilamdyuti Goswami (as)
60 * Petr Kovar (cs)
61 * Peter Bach (da)
62 * Bruce Cowan (en_GB)
63 * Arash Mousavi (fa)
64 * Jiri Grönroos (fi)
65 * Claude Paroz (fr)
66 * Leandro Regueiro (gl)
67 * Gabor Kelemen (hu)
68 * Dirgita (id)
69 * Milo Casagrande (it)
70 * Changwoo Ryu (ko)
71 * Aurimas Černius (lt)
72 * Sandeep Shedmake (mr)
73 * A S Alam (pa)
74 * Piotr Drąg (pl)
75 * Duarte Loreto (pt)
76 * Daniel Nylander (sv)
77 * Dr.T.Vasudevan (ta)
78 * Theppitak Karoonboonyanan (th)
79 * Muhammet Kara (tr)
80 * Nguyễn Thái Ngọc Duy (vi)
81 * Chrovex Fan (zh_CN)
82
83 =============
84 Version 3.5.5
85 =============
86
87 Translations Updates:
88
89 * Traditional Chinese (Chao-Hsiung Liao)
90 * German (Tobias Endrigkeit)
91 * Kazakh (Baurzhan Muftakhidinov)
92 * Silesian (Przemysław Buczkowski)
93 * Gujarati (Sweta Kothari)
94 * Serbian (Мирослав Николић)
95
96 =============
97 Version 3.5.4
98 =============
99
100 Translations Updates
101
102 * Belarusian (Alexander Shopov)
103
104 =============
105 Version 3.5.3
106 =============
107
108 libmenu
109
110 * Fix scanner warnings (Colin Walters)
111 * Add gmenu_tree_iter_get_separator (Jasper St. Pierre)
112 * Add a recursive NoDisplay getter (Jasper St. Pierre)
113 * Add a way to get a GMenuTree from a GMenuTreeItem (Jasper St. Pierre)
114 * Add a way to get the parent item for a GMenuTreeItem (Jasper St. Pierre)
115
116 Translations Updates
117
118 * Belarusian (Ihar Hrachyshka)
119 * Greek (Tom Tryfonidis)
120 * Assamese (Nilamdyuti Goswami)
121 * Telugu (Sasi Bhushan Boddepalli)
122
123 =============
124 Version 3.5.2
125 =============
126
127 Layout
128
129 * Add a separate category for Web Applications (Giovanni Campagna)
130
131 libmenu
132
133 * Add a new GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED flag (Vincent Untz)
134
135 Translations
136
137 * Bulgarian
138 * Kashubian
139 * Spanish
140 * Galician
141 * Slovenian
142 * Hebrew
143 * Russian
144 * Norwegian bokmål
145
146 =============
147 Version 3.4.0
148 =============
149
150 Translators
151
152 * Morn Met (km)
153 * Bahodir Mansurov (uz@cyrillic)
154
155 =============
156 Version 3.3.5
157 =============
158
159 Translators
160
161 * Jiro Matsuzawa (ja)
162 * Kjartan Maraas (nb)
163 * Danishka Navin (si)
164 * Andiswa Mvanyashe (xh)
165
166 =============
167 Version 3.3.1
168 =============
169
170 libmenu
171
172 * Ignore desktop entries with no Name or Exec key (Florian Müllner)
173
174 Layout
175
176 * Put the Other menu at the end (Vincent)
177
178 ===============
179 Version 3.2.0.1
180 ===============
181
182 Menu Editor
183
184 * Work with latest pygobject (Vincent)
185
186 =============
187 Version 3.2.0
188 =============
189
190 Translators
191
192 * Nilamdyuti Goswami (as)
193 * Praveen Illa (te)
194
195 ==============
196 Version 3.1.92
197 ==============
198
199 libmenu
200
201 * Fix build failure with --enable-debug (Vincent)
202
203 Translators
204
205 * Ігар Грачышка (be)
206 * Daniel Mustieles (es)
207 * Jiro Matsuzawa (ja)
208
209 ==============
210 Version 3.1.90
211 ==============
212
213 libmenu
214
215 * Don't try to unref potentially NULL pointers (Jasper St. Pierre)
216
217 =============
218 Version 3.1.5
219 =============
220
221 This version of gnome-menus comes with a significant API and ABI break, to
222 modernize the code. As a result, the name of the library and the pkg-config
223 filename have changed, so that this new version is parallel-installable with
224 previous ones.
225
226 As of now, there is no guide to migrate to the new API, but it is rather
227 straight-forward as changes were mostly done to improve the experience for
228 introspection-based bindings. The examples shipped in util/ are a good basis.
229
230 libmenu
231
232 * Rebase internal representation of .desktop files on top of
233 GDesktopAppInfo (Colin Walters)
234 * Make GMenuTree a GObject (Colin Walters)
235 * Use GSlice for various data (Colin Walters)
236 * Use thread-default main context for callbacks for future flexibility
237 for thread support (Colin Walters)
238 * Many API changes, see new headers for changes. The most visible one
239 is that gmenu_tree_load_sync() should explicitly be used to load the
240 menu now. (Colin Walters, Vincent)
241 * Drop support for "KDE Desktop Entry" group (Vincent)
242 * Return GIcon instead of char * for icon-related API (Vincent)
243 * Various fixes and cleanups to merge Colin's branch (Vincent, Colin
244 Walters)
245 * Add gmenu_tree_get_entry_by_id() API (Colin Walters)
246 * Support XDG_CURRENT_DESKTOP (Vincent)
247
248 Menu Editor
249
250 * Port to introspection-based bindings (Vincent)
251 * Stop editing settings.menu (Vincent)
252
253 Python
254
255 * Drop static python bindings; introspection-based ones should be used
256 now (Colin Walters)
257
258 Misc
259
260 * Replace example of python code with javascript code (Colin Walters)
261 * Change library name, header directory, pkg-config filename (Vincent)
262 * Require glib 2.29.15 for new API (Vincent)
263
264 Translators
265
266 * Ігар Грачышка (be)
267 * Gil Forcada (ca@valencia)
268 * Priscilla Mahlangu (zu)
269
270 =============
271 Version 3.0.1
272 =============
273
274 Translators
275
276 * Anousak Souphavanh (lo)
277 * Theppitak Karoonboonyanan (th)
278
279 =============
280 Version 3.0.0
281 =============
282
283 Layout
284
285 * Show administration tools and old capplets in Other (Vincent)
286
287 Translators
288
289 * Khaled Hosny (ar)
290 * Zenat Rahnuma (bn)
291 * Gil Forcada (ca)
292 * Sense Hofstede (fy)
293 * Praveen Illa (te)
294 * Sahran (ug)
295 * Clytie Siddall (vi)
296
297 ===============
298 Version 2.91.91
299 ===============
300
301 Menu Editor
302
303 * Fix to work with latest pygi (Vincent)
304
305 Misc
306
307 * Build fix (Vincent)
308
309 Translators
310
311 * F Wolff (af)
312 * Changwoo Ryu (ko)
313 * Pavol Klačanský (sk)
314 * Korostil Daniel (uk)
315
316 ==============
317 Version 2.91.6
318 ==============
319
320 libmenu
321
322 * Do not send multiple notifications for one file change (Vincent)
323
324 Menu Editor
325
326 * Port to pygobject-based introspection bindings (Vincent)
327 * Make editor GTK+ 3 ready (Vincent)
328
329 Misc
330
331 * Improve introspection build (Vincent)
332 * Drop settings.menu (Vincent)
333
334 Translators
335
336 * Gil Forcada (ca@valencia)
337 * Mattias Põldaru (et)
338 * Mahyar Moghimi (fa)
339 * Fran Diéguez (gl)
340 * Kikongo Translation Team (kg)
341 * Sahran (ug)
342 * Clytie Siddall (vi)
343
344 ==============
345 Version 2.30.4
346 ==============
347
348 libmenu
349
350 * Clear cache of desktop entries set when files are added/removed
351 (Vincent)
352
353 Misc
354
355 * Associate .gir with pkg-config file (Vincent)
356 * Rename --enable-deprecations configure option to
357 --enable-deprecation-flags (Vincent)
358
359 Translators
360
361 * Daniel Martinez (an)
362 * Takayuki KUSANO (ja)
363 * Baurzhan Muftakhidinov (kk)
364 * Torstein Winterseth (nn)
365
366 ==============
367 Version 2.30.3
368 ==============
369
370 Menu Editor
371
372 * Respect XDG_MENU_PREFIX when writing user menu file (Vincent)
373
374 Misc
375
376 * Update information in README and other files (Vincent)
377
378 Translators
379
380 * Kristjan SCHMIDT (eo)
381 * Sense Hofstede (fy)
382 * Fran Diéguez (gl)
383 * Reuben Potts (gv)
384 * Dirgita (id)
385 * Baurzhan M. (kk)
386 * Sahran (ug)
387
388 ==============
389 Version 2.30.2
390 ==============
391
392 Misc
393
394 * Do not ship gir files in the tarball (Yaakov Selkowitz)
395
396 Translators
397
398 * Daniel Martinez (an)
399 * Gil Forcada (ca)
400 * Gil Forcada (ca@valencia)
401 * Reşat SABIQ (crh)
402 * Christian Kirbach (de)
403 * Thomas Thurman (en@shaw)
404 * Fran Diéguez (gl)
405 * Shankar Prasad (kn)
406 * Peteris Krisjanis (lv)
407 * Wouter Bolsterlee (nl)
408 * Matej Urbančič (sl)
409
410 ==============
411 Version 2.30.0
412 ==============
413
414 libmenu
415
416 * Fix layout processing for Menuname nodes (Vincent)
417 * Never ignore Menuname nodes from DefaultLayout (Vincent)
418
419 Misc
420
421 * Add configure summary (Vincent)
422
423 Translators
424
425 * Gil Forcada (ca)
426 * Kostas Papadimas (el)
427 * Iñaki Larrañaga Murgoitio (eu)
428 * Changwoo Ryu (ko)
429 * Badral (mn)
430
431 ===============
432 Version 2.29.92
433 ===============
434
435 libmenu
436
437 * Add gobject-introspection support (Vincent)
438
439 Misc
440
441 * Build fix (Vincent)
442
443 Translators
444
445 * Nikos Charonitakis (el)
446 * Pavol Klačanský (sk)
447
448 ===============
449 Version 2.29.91
450 ===============
451
452 Misc
453
454 * Make some non-visible strings non-translatable (Vincent)
455 * Add translator comment (Vincent)
456
457 Translators
458
459 * Fran Diéguez (gl)
460 * Torstein Adolf Winterseth (nn)
461
462 ==============
463 Version 2.29.6
464 ==============
465
466 libmenu
467
468 * Fix miscalculation for inlining when inline_header = true (Vincent)
469 * Add real support for inline aliases during layout processing
470 (Vincent)
471 * Support inline alias of an inline alias (Vincent)
472 * Do not count non-inlining submenus as inlining with header (Vincent)
473
474 Translators
475
476 * Sadia Afroz (bn)
477 * Gil Forcada (ca)
478 * Reşat SABIQ (crh)
479 * Thomas Thurman (en@shaw)
480 * Sveinn í Felli (is)
481 * Nils-Christoph Fiedler (nds)
482 * Dmitry Yacenko (ru)
483 * Krishna Babu K (te)
484 * 甘露(Gan Lu) (zh_CN)
485
486 ================
487 Version 2.28.0.1
488 ================
489
490 libmenu
491
492 * Fix sorting of menu items during merge to actually work (and not
493 crash) (Frédéric Crozat)
494
495 Python
496
497 * Link the python module to libpython (Frédéric Crozat, Vincent)
498
499 ==============
500 Version 2.28.0
501 ==============
502
503 Translators
504
505 * Amitakhya Phukan (as)
506 * Petr Kovar (cs)
507 * Peter Bach (da)
508 * Philip Withnall (en_GB)
509 * Rajesh Ranjan (hi)
510 * Luca Ferretti (it)
511 * Shankar Prasad (kn)
512 * Gintautas Miliauskas (lt)
513 * Rajesh Ranjan (mai)
514 * Peter Ani
515 * Sandeep Shedmake (mr)
516 * Nils-Christoph Fiedler (nds)
517 * A S Alam (pa)
518 * Adi Roiban (ro)
519 * Matej Urbančič (sl)
520 * Daniel Nylander (sv)
521 * Krishna Babu K (te)
522 * Baris Cicek (tr)
523 * Maxim Dziumanenko (uk)
524 * Chao-Hsiung Liao (zh_HK)
525 * Chao-Hsiung Liao (zh_TW)
526
527 ===============
528 Version 2.27.92
529 ===============
530
531 This releases features new API that applications can use to display the full
532 name contained in .desktop files that is now in the X-GNOME-FullName key. If an
533 application chooses to use this, then it should set the sort key so that
534 .desktop files are correctly sorted.
535
536 libmenu
537
538 * Add gmenu_tree_entry_get_display_name() API (Vincent)
539 This will return X-GNOME-FullName if available, and fallback to Name.
540 * Add gmenu_tree_get_sort_key()/gmenu_tree_set_sort_key() (Vincent)
541 The default sort key is still Name. Users of
542 gmenu_tree_entry_get_display_name() should use
543 gmenu_tree_set_sort_key().
544
545 Python
546
547 * Bind new API (Vincent)
548 * Add missing bindings for gmenu_tree_entry_get_generic_name() (Vincent)
549
550 Menu Editor
551
552 * Remove deprecated Encoding key from desktop file (Frédéric Péters)
553 * Use display name instead of name (Vincent)
554
555 Translators
556
557 * Khaled Hosny (ar)
558 * Alexander Shopov (bg)
559 * Runa Bhattacharjee (bn_IN)
560 * Denis (br)
561 * Mario Blättermann (de)
562 * Iñaki Larrañaga Murgoitio (eu)
563 * Claude Paroz (fr)
564 * Seán de Búrca (ga)
565 * Anton Meixome (gl)
566 * Sweta Kothari (gu)
567 * Gabor Kelemen (hu)
568 * Francesco Marletta (it)
569 * Takayuki KUSANO (ja)
570 * Changwoo Ryu (ko)
571 * Kjartan Maraas (nb)
572 * Mario Blättermann (nds)
573 * Manoj Kumar Giri (or)
574 * Tomasz Dominikowski (pl)
575 * Duarte Loreto (pt)
576 * Krix Apolinário (pt_BR)
577 * Горан Ракић (sr)
578 * Goran Rakić (sr@latin)
579 * Dr.T.Vasudevan (ta)
580 * Theppitak Karoonboonyanan (th)
581 * 甘露 (Lu Gan) (zh_CN)
582
583 ==============
584 Version 2.27.5
585 ==============
586
587 Misc
588
589 * Use silent-rules with automake 1.11 (Vincent)
590
591 Translators
592
593 * Ilkka Tuohela (fi)
594
595 ==============
596 Version 2.27.4
597 ==============
598
599 libmenu
600
601 * Improve performance by using a cache to not compute the same thing
602 again and again (Michael Meeks, Vincent)
603 * Add API to access GenericName (Robert Staudinger)
604 * Fix DefaultLayout attributes not being inherited (Vincent)
605 * Do not always inherit parent DefaultLayout attributes (Vincent)
606 * Sort inlined items unless inline_header is used (Vincent)
607
608 Menu Editor
609
610 * Add --help and --version arguments (Vincent)
611 * Port to GtkBuilder (Pedro Fragoso, Vincent)
612
613 Misc
614
615 * Use shave to improve build log readability (Vincent)
616
617 Translators
618
619 * Jordi Mallach (ca@valencia)
620 * Huxain (dv)
621 * Jorge González (es)
622 * Mattias Põldaru (et)
623 * Seán de Búrca (ga)
624 * Yaron Shahrabani (he)
625 * Daniel Nylander (sv)
626
627 ==============
628 Version 2.26.1
629 ==============
630
631 Translators
632
633 * Khaled Hosny (ar)
634 * Daniel Nylander (sv)
635
636 ==============
637 Version 2.26.0
638 ==============
639
640 Translators
641
642 * Reşat SABIQ (crh)
643 * Suso Baleato (gl)
644 * Rajesh Ranjan (hi)
645 * Francesco Marletta (it)
646 * Manoj Kumar Giri (or)
647
648 ===============
649 Version 2.25.91
650 ===============
651
652 Translators
653
654 * Changwoo Ryu (ko)
655 * Raivis Dejus (lv)
656 * Sandeep Shedmake (mr)
657 * Горан Ракић (sr)
658 * Daniel Nylander (sv)
659 * Woodman Tuen (zh_HK)
660 * Woodman Tuen (zh_TW)
661
662 ==============
663 Version 2.25.5
664 ==============
665
666 Misc
667
668 * Use gnome-common macro to define DEPRECATED build variables (Vincent)
669
670 Translators
671
672 * Reşat SABIQ (crh)
673 * Saudat Mohammed (ha)
674 * Sylvester Onye (ig)
675 * Fajuyitan, Sunday Ayo (yo)
676
677 ==============
678 Version 2.25.2
679 ==============
680
681 Fixes
682
683 * Fix a critical warning in the python binding for monitoring a file
684 (Vincent)
685
686 Misc
687
688 * Ship a gnome-menus-ls.py script that is an example of python bindings
689 and that can be used as a replacement for gnome-menu-spec-test
690 (Vincent)
691
692 ==============
693 Version 2.24.2
694 ==============
695
696 Translators
697
698 * Mikel González (ast)
699
700 ==============
701 Version 2.24.1
702 ==============
703
704 Translators
705
706 * Khaled Hosny (ar)
707 * Rostislav "zbrox" Raykov (bg)
708 * Margulan Moldabekov (kk)
709
710 ==============
711 Version 2.24.0
712 ==============
713
714 Translators
715
716 * F Wolff (af)
717 * Khaled Hosny (ar)
718 * Roozbeh Pournader (fa)
719 * Rajesh Ranjan (hi)
720 * Gabor Kelemen (hu)
721 * Francesco Marletta (it)
722 * Shankar Prasad (kn)
723 * Leonardo Ferreira Fontenelle (pt_BR)
724 * Mișu Moldovan (ro)
725
726 ===============
727 Version 2.23.92
728 ===============
729
730 Translators
731
732 * Seán de Búrca (ga)
733 * Krešo Kunjas (hr)
734 * Praveen Arimbrathodiyil (ml)
735 * Leonardo Ferreira Fontenelle (pt_BR)
736
737 ===============
738 Version 2.23.91
739 ===============
740
741 Translators
742
743 * Khaled Hosny (ar)
744 * Reuven Gonen (he)
745 * Shankar Prasad (kn)
746
747 ==============
748 Version 2.23.6
749 ==============
750
751 Layout
752
753 * Fix the icon for the accessibility menu (Matthias Clasen)
754
755 Translators
756
757 * Khaled Hosny (ar)
758 * Fabrício Godoy (pt_BR)
759
760 ==============
761 Version 2.23.5
762 ==============
763
764 Translators
765
766 * Yannig Marchegay (Kokoyaya) (oc)
767 * Wadim Dziedzic (pl)
768 * Zabeeh Khan (ps)
769 * Laurent Dhima (sq)
770
771 ==============
772 Version 2.23.4
773 ==============
774
775 Misc
776
777 * Require intltool 0.40.0 (Vincent)
778
779 Translators
780
781 * 甘露(Lu Gan) (zh_CN)
782
783 ==============
784 Version 2.23.3
785 ==============
786
787 Features
788
789 * Implement handling of $XDG_MENU_PREFIX from the xdg menu
790 specification (Vincent)
791
792 Fixes
793
794 * Fix the values of <DefaultLayout> (ie, show_empty, inline,
795 inline_limit, etc.) not being inherited by submenus when the
796 <DefaultLayout> node is after the <Menu> node in the .menu file
797 (Vincent)
798 * Fix a bug where the fallback on the filename in <MergeFile> couldn't
799 be used (Vincent)
800
801 Translators
802
803 * Djihed Afifi (ar)
804 * Anna Ármansdóttir (is)
805 * Manoj Kumar Giri (or)
806
807 ==============
808 Version 2.23.1
809 ==============
810
811 Features
812
813 * Do not show separators at the beginning/end of a menu, or after
814 another separator, but add an option to show them (Vincent)
815
816 Fixes
817
818 * Call g_type_init() because GIO needs it and we use GIO for file
819 monitoring (Vincent)
820 * Fix a crash when a file notification is emitted with a non-ascii
821 filename (Vincent)
822 * Remove entries from the excluded set if they are included after they
823 were excluded (eg:
824 <Exclude>something</Exclude><Include>something</Include>) (Vincent)
825 * Implicitly add <Merge> nodes to <Layout> and <DefaultLayout> that are
826 missing some (Vincent)
827 * Correctly order the move operations to execute so that moving
828 something and moving it back again works as undo (Vincent)
829 * Simplify some code (William Jon McCann, Vincent)
830 * Plug leak (Vincent)
831
832 Layout
833
834 * Do not show accessibility items in the accessories submenu
835 (Josselin Mouette)
836 * Merge menus and files at the end of the layout of settings.menu
837 * Explicitly do not include gnomecc.menu in the preferences menu
838 instead of explicitly excluding it, so that alacarte doesn't know it
839 was excluded (Vincent)
840 * Rename many .directory files so they use fd.o Categories as name
841 (Vincent)
842 * Remove preferences.menu and directly include things in settings.menu
843 (Vincent)
844 * Update a few icons used in .directory files according to the icon
845 naming spec (Vincent)
846 * Remove the accessibility submenu from Preferences, since it's empty
847 now anyway (Vincent)
848
849 Misc
850
851 * Remove shebangs from non-executable Python scripts.
852
853 Translators
854
855 * Žygimantas Beručka (lt)
856 * Kjartan Maraas (nb)
857
858 ==============
859 Version 2.22.1
860 ==============
861
862 Misc
863
864 * Remove shebangs from non-executable Python scripts.
865
866 Translators
867
868 * David Lodge (en_GB)
869 * Massimo Furlani (fur)
870 * Eskild Hustvedt (nn)
871
872 ==============
873 Version 2.22.0
874 ==============
875
876 Translators
877
878 * Petr Kovar (cs)
879 * nikosCharonitakis (el)
880 * Changwoo Ryu (ko)
881 * Žygimantas Beručka (lt)
882 * Sandeep Shedmake (mr)
883 * Woodman Tuen (zh_HK)
884 * Woodman Tuen (zh_TW)
885
886 ===============
887 Version 2.21.92
888 ===============
889
890 Translators
891
892 * Massimo Furlani (fur)
893 * Sangeeta Kumari (mai)
894 * Nabin Gautam (ne)
895
896
897 ===============
898 Version 2.21.91
899 ===============
900
901 Fixes
902
903 * Remove the various monitor backends, and unconditionnaly use gio
904 (Vincent)
905
906 Misc
907
908 * Do not install gnome-menu-spec-test, it's useless for the user
909 (Vincent)
910 * Add a hard dependency on gio (Vincent)
911
912 Translators
913
914
915 * Woodman Tuen (zh_HK)
916 * Woodman Tuen (zh_TW)
917
918 ===============
919 Version 2.21.90
920 ===============
921
922 Misc
923
924 * When using gio, require version 2.15.2 (Saleem Abdulrasool)
925
926 Translators
927
928
929 * Djihed Afifi (ar)
930 * Alexander Nyakhaychyk (be)
931 * Michael Terry (io)
932
933 ==============
934 Version 2.21.5
935 ==============
936
937 Fixes
938
939 * Fix for API changes in gio (Sebastian Bacher, Wouter Bolsterlee,
940 Vincent)
941
942 Translators
943
944
945 * F Wolff (af)
946 * Iñaki Larrañaga Murgoitio (eu)
947 * Erdal Ronahi (ku)
948 * Kjartan Maraas (nn)
949 * Yannig Marchegay (Kokoyaya) (oc)
950
951 ==============
952 Version 2.21.3
953 ==============
954
955 Features
956
957 * Fix for api change in gio (Kjartan Maraas)
958 * Prevent a major memory leak (Sebastian Droge)
959
960 Translators
961
962
963 * Petr Kovar (cs)
964 * Seán de Búrca (ga)
965 * Danishka Navin (si)
966
967 ==============
968 Version 2.21.2
969 ==============
970
971 Features
972
973 * Add a new GIO-based monitor backend. It is now the preferred one.
974 The --with-monitor-backend configure flag can be used to select the
975 backend to build. (Sebastian Dröge)
976
977 Translators
978
979 * Anas Husseini (ar)
980 * Peter Bach (da)
981 * Jorge González (es)
982 * Seán de Búrca (ga)
983 * Ignacio Casal Quinteiro (gl)
984 * Huda Toriq (id)
985 * Matej Urbančič (sl)
986
987 ==============
988 Version 2.20.1
989 ==============
990
991 Translators
992
993 * Peter Bach (da)
994
995 ==============
996 Version 2.20.0
997 ==============
998
999 Translators
1000
1001 * Anas Husseini (ar)
1002 * Amitakhya Phukan (as)
1003 * Jordi Mallach (ca)
1004 * Peter Bach (da)
1005 * Simos Xenitellis (el)
1006 * Francesco Marletta (it)
1007 * Shankar Prasad (kn)
1008 * Žygimantas Beručka (lt)
1009 * Alexandru Szasz (ro)
1010 * Nickolay V. Shmyrev (ru)
1011 * Peter Tuhársky (sk)
1012 * Горан Ракић (sr)
1013 * Maxim Dziumanenko (uk)
1014
1015 ===============
1016 Version 2.19.92
1017 ===============
1018
1019 Fixes
1020
1021 * Fix potential crash (Rob Bradford)
1022
1023 Translators
1024
1025 * Rostislav "zbrox" Raykov (bg)
1026 * Stéphane Raimbault (fr)
1027 * Arangel Angov (mk)
1028 * Tomasz Dominikowski (pl)
1029 * Leonardo Ferreira Fontenelle (pt_BR)
1030 * Duarte Loreto (pt)
1031 * Funda Wang (zh_CN)
1032
1033 ===============
1034 Version 2.19.90
1035 ===============
1036
1037 Translators
1038
1039 * Ani Peter (ml)
1040 * Inaki Larranaga Murgoitio (eu)
1041 * Ankit Patel (gu)
1042 * Ilkka Tuohela (fi)
1043 * Yair Hershkovitz (he)
1044 * Sean Burke (ga)
1045
1046 ==============
1047 Version 2.19.6
1048 ==============
1049
1050 Translators
1051
1052 * Ihar Hrachyshka (be@latin)
1053 * Runa Bhattacharjee (bn_IN)
1054 * Hendrik Richter (de)
1055 * Ilkka Tuohela (fi)
1056 * Gabor Kelemen (hu)
1057 * Eunju Kim (ko)
1058 * Raivis Dejus (lv)
1059 * Wouter Bolsterlee (nl)
1060 * Bharat Kumar (te)
1061 * Nurali Abdurahmonov (uz@cyrillic)
1062
1063 ==============
1064 Version 2.19.5
1065 ==============
1066
1067 Fixes
1068
1069 * Use python-config to get python includes (Sebastien Bacher)
1070 * Don't show screensavers in the menus (Ray Strode)
1071
1072 Translators
1073
1074 * Tshewang Norbu (dz)
1075 * Takeshi AIHANA (ja)
1076 * Tomasz Dominikowski (pl)
1077 * Danishka Navin (si)
1078 * Daniel Nylander (sv)
1079 * Dr.T.Vasudevan (ta)
1080 * Bharat Kumar (te)
1081 * Nurali Abdurahmonov (uz)
1082 * Clytie Siddall (vi)
1083
1084 ==============
1085 Version 2.19.4
1086 ==============
1087
1088 Fixes
1089
1090 * Fix crashes in python bindings (Colin Walters)
1091 * Fix crash in inotify backend when ~/.config/menus is created
1092 (Vincent)
1093
1094 Translators
1095
1096 * Alexander Nyakhaychyk (be)
1097
1098 ==============
1099 Version 2.19.3
1100 ==============
1101
1102 Fixes
1103
1104 * Use G_DIR_SEPARATOR instead of '/' (Vincent)
1105 * Fix small leak (William KJon McCann)
1106
1107 Translators
1108
1109 * David Lodge (en_GB)
1110 * Jorge González (es)
1111 * Ivar Smolin (et)
1112 * Theppitak Karoonboonyanan (th)
1113
1114 ==============
1115 Version 2.19.2
1116 ==============
1117
1118 Menu Layout
1119
1120 * Fix "system-wide" typo (Vincent)
1121 * Put Preferences before Administration in the System menu (Vincent)
1122 * Use icons from the icon naming spec (Luca Ferreti, Matthias Clasen,
1123 Vincent)
1124 * Use Universal Access instead of Accessibility (Calum Benson, Vincent)
1125 * Use System instead of Desktop since the menu got renamed
1126 (Sébastien Bacher)
1127 * Do not require the Application category in the Other submenu (Vincent)
1128
1129 Menu Editor
1130
1131 * Fix a crash when unselecting the current menu (Vincent)
1132 * Require pygtk at runtime (Vincent)
1133 * Use the python executable found by configure (Vincent)
1134
1135 Misc
1136
1137 * Require automake 1.9
1138
1139 Translators
1140
1141 * Ihar Hrachyshka (be@latin)
1142 * norbu (dz)
1143 * Jorge González (es)
1144 * Iñaki Larrañaga Murgoitio (eu)
1145 * Ignacio Casal Quinteiro (gl)
1146 * Francesco Marletta (it)
1147 * Raivis Dejus (lv)
1148 * Kjartan Maraas (nb)
1149 * Yannig MARCHEGAY (Kokoyaya) (oc)
1150 * Matej Urbančič (sl)
1151 * Daniel Nylander (sv)
1152
1153 ==============
1154 Version 2.18.0
1155 ==============
1156
1157 Translators
1158
1159 * Alaksandar Navicki (be@latin)
1160 * Peter Bach (da)
1161 * Simos Xenitellis (el)
1162 * Ankit Patel (gu)
1163 * Žygimantas Beručka (lt)
1164 * wadim dziedzic (pl)
1165 * Elian Myftiu (sq)
1166 * Горан Ракић (sr)
1167
1168 ===============
1169 Version 2.17.92
1170 ===============
1171
1172 Fixes
1173
1174 * Show the system menu directories by default (as it was in 2.16)
1175 (Denis Washington)
1176
1177 Translators
1178
1179 * Peter Bach (da)
1180 * Takeshi AIHANA (ja)
1181 * Duarte Loreto (pt)
1182 * Clytie Siddall (vi)
1183 * Funda Wang (zh_CN)
1184 * Woodman Tuen (zh_HK)
1185 * Woodman Tuen (zh_TW)
1186
1187 ===============
1188 Version 2.17.91
1189 ===============
1190
1191 Features
1192
1193 * Rework the layout so that it's easy to have the old layout. The
1194 control center will ship its own menu file to have the layout for the
1195 shell. (Denis Washington)
1196
1197 Translators
1198
1199 * Khaled Hosny (ar)
1200 * Ihar Hrachyshka (be)
1201 * Alaksandar Navicki (be@latin)
1202 * Rostislav "zbrox" Raykov (bg)
1203 * Runa Bhattacharjee (bn_IN)
1204 * Mahay Alam Khan (bn)
1205 * Jordi Mallach (ca)
1206 * Jakub Friedl (cs)
1207 * Rhys Jones (cy)
1208 * Hendrik Richter (de)
1209 * David Lodge (en_GB)
1210 * Ivar Smolin (et)
1211 * Ilkka Tuohela (fi)
1212 * Robert-André Mauchin (fr)
1213 * Reuven Gonen (he)
1214 * Rajesh Ranjan (hi)
1215 * Gabor Kelemen (hu)
1216 * Young-Ho Cha (ko)
1217 * Žygimantas Beručka (lt)
1218 * Jovan Naumovski (mk)
1219 * Badral (mn)
1220 * Kjartan Maraas (nb)
1221 * Wouter Bolsterlee (nl)
1222 * Og Maciel (pt_BR)
1223 * Leonid Kanter (ru)
1224 * Steve Murphy (rw)
1225 * Daniel Nylander (sv)
1226 * Theppitak Karoonboonyanan (th)
1227 * Clytie Siddall (vi)
1228
1229 ==============
1230 Version 2.17.5
1231 ==============
1232
1233 Features
1234
1235 * New menu layout for the control center capplets (Denis Washington)
1236
1237 Translators
1238
1239 * Khaled Hosny (ar)
1240 * Ihar Hrachyshka (be)
1241
1242 ==============
1243 Version 2.17.2
1244 ==============
1245
1246 Features
1247
1248 * Flesh out inotify support (use --enable-inotify) (Mark)
1249
1250 Fixes
1251
1252 * Don't load incorrectly encoded .desktop files (Mark)
1253 * Fix compile warning (Mark)
1254
1255 Translators
1256
1257 * Khaled Hosny (ar)
1258 * Guillaume Savaton (eo)
1259
1260 ==============
1261 Version 2.16.1
1262 ==============
1263
1264 Translators
1265
1266 * David Lodge (en_GB)
1267 * Francesco Marletta (it)
1268
1269 ==============
1270 Version 2.16.0
1271 ==============
1272
1273 Translators
1274
1275 * Gabor Kelemen (hu)
1276 * Jovan Naumovski (mk)
1277 * Badral (mn)
1278 * Rahul Bhalerao (mr)
1279 * Matic Žgur (sl)
1280 * Onur Can Çakmak (tr)
1281
1282 ===============
1283 Version 2.15.91
1284 ===============
1285
1286 Translators
1287
1288 * Runa Bhattacharjee (bn_IN)
1289 * Francisco Javier F. Serrador (es)
1290 * Arangel Angov (mk)
1291 * Matic Žgur (sl)
1292
1293 ===============
1294 Version 2.15.90
1295 ===============
1296
1297 Translators
1298
1299 * Ani Peter (ml)
1300 * Subhransu Behera (or)
1301 * Theppitak Karoonboonyanan (th)
1302
1303 ================
1304 Version 2.15.4.1
1305 ================
1306
1307 Fixes
1308
1309 * Correctly update LT_VERSION (Vincent)
1310
1311 ==============
1312 Version 2.15.4
1313 ==============
1314
1315 Features
1316
1317 * Add new API to know if an application should be launched in a
1318 terminal and to know the path to the desktop file (Travis Watkins)
1319 * Complete python bindings for the "No Display" flag (Travis Watkins)
1320
1321 Menu Editor
1322
1323 * Allow specifying alternate menu files as command line arguments
1324 (William Jon McCann)
1325
1326 Misc
1327
1328 * Use po/LINGUAS (Wouter Bolsterlee)
1329 * Require intltool 0.35.0 (Vincent Untz)
1330
1331 Translators
1332
1333 * Runa Bhattacharjee (bn_IN)
1334 * Matheus Grandi (gn)
1335 * Swapnil Hajare (mr)
1336
1337 ==============
1338 Version 2.14.0
1339 ==============
1340
1341 Features
1342
1343 * Start inotify support (not compiled by default) (Mark McLoughlin)
1344
1345 Fixes
1346
1347 * Small fix for the python bindings (Mark McLoughlin)
1348 * Fix infinite loop (Mark McLoughlin)
1349
1350 Translators
1351
1352 * Ales Nyakhaychyk (be)
1353 * Jérémy Le Floc'h (br)
1354 * Petr Tomeš (cs)
1355 * Rhys Jones (cy)
1356 * Ole Laursen (da)
1357 * Hendrik Richter (de)
1358 * Pema Geyleg (dz)
1359 * Kostas Papadimas (el)
1360 * Reuven Gonen (he)
1361 * Rajesh Ranjan (hi)
1362 * Takeshi AIHANA (ja)
1363 * Vladimer Sichinava (ka)
1364 * Erdal Ronahi (ku)
1365 * Žygimantas Beručka (lt)
1366 * Raivis Dejus (lv)
1367 * Thierry Randrianiriana (mg)
1368 * Wouter Bolsterlee (nl)
1369 * Kjartan Maraas (nn)
1370 * GNOME PL Team (pl)
1371 * Evandro Fernandes Giovanini (pt_BR)
1372 * Duarte Loreto (pt)
1373 * Sebastian Ivan (ro)
1374 * Leonid Kanter (ru)
1375 * Elian Myftiu (sq)
1376 * Слободан Д. Средојевић (sr)
1377 * Maxim Dziumanenko (uk)
1378 * Funda Wang (zh_CN)
1379 * Woodman Tuen (zh_HK)
1380 * Woodman Tuen (zh_TW)
1381
1382 ==============
1383 Version 2.13.5
1384 ==============
1385
1386 Features
1387
1388 * Add "include NoDisplay" flag (Mark McLoughlin)
1389
1390 Fixes
1391
1392 * Fix issue where menu wouldn't fully reload after lots of
1393 file change events (Mark McLoughlin, Frederic Crozat)
1394 * Remove some unused code (Kjartan Maraas)
1395 * Fix incorrect escaping of C format string (The Written Word)
1396
1397 Translators
1398
1399 * Rostislav Raykov (bg)
1400 * Mahay Alam Khan (bn)
1401 * Jordi Mallach (ca)
1402 * Miloslav Trmac (cs)
1403 * Adam Weinberger (en_CA)
1404 * Francisco Javier F. Serrador (es)
1405 * Priit Laes (et)
1406 * Iñaki Larrañaga (eu)
1407 * Ilkka Tuohela (fi)
1408 * Christophe Merlet (RedFox) (fr)
1409 * Ignacio Casal Quinteiro (gl)
1410 * Ankit Patel (gu)
1411 * Gabor Kelemen (hu)
1412 * Francesco Marletta (it)
1413 * Erdal Ronahî (ku)
1414 * Timur Jamakeev (ky)
1415 * Kjartan Maraas (nb, no)
1416 * Tino Meinen (nl)
1417 * Marcel Telka (sk)
1418 * Christian Rose (sv)
1419 * Y.Kiran Chandra (te)
1420 * Theppitak Karoonboonyanan (th)
1421 * Abduxukur Abdurixit (ug)
1422 * Clytie Siddall (vi)
1423 * Woodman Tuen (zh_HK, zh_TW)
1424
1425 ==============
1426 Version 2.12.0
1427 ==============
1428
1429 Fixes
1430
1431 * Fix FAM crasher in gmenu-simple-editor (Ed Catmur)
1432
1433 Translators
1434
1435 * Rhys Jones (cy)
1436 * Vincent Untz (fr)
1437 * Ignacio Casal Quinteiro (gl)
1438 * Norayr Chilingaryan (hy)
1439 * Žygimantas Beručka (lt)
1440 * Duarte Loreto (pt)
1441 * Leonid Kanter (ru)
1442 * Elian Myftiu (sq)
1443 * Данило Шеган (sr)
1444 * Onur Can Cakmak (tr)
1445 * Clytie Siddall (vi)
1446
1447 ===============
1448 Version 2.11.92
1449 ===============
1450
1451 Fixes
1452
1453 * Fix memory corruption crasher handling notifies (Mark)
1454 * Fix python syntax warning (Mark)
1455 * Fix build when FAM isn't found (Elijah Newren)
1456 * Fix crasher when a <AppDir> references a subdir of another <AppDir> (Mark)
1457 * Fix duplicate entries after updating (Mark)
1458 * Fix infinite loop (Frederic Crozat)
1459 * Make <LegacyDir> with prefix work again (Chris Lahey, Mark)
1460
1461 Translators
1462
1463 * Rostislav "zbrox" Raykov (bg)
1464 * Jordi Mallach (ca)
1465 * Hendrik Brandt (de)
1466 * Nikos Charonitakis (el)
1467 * Roozbeh Pournader (fa)
1468 * ahmad riza h nst (id)
1469 * Takeshi AIHANA (ja)
1470 * Young-Ho Cha (ko)
1471 * GNOME PL Team (pl)
1472 * Sebastian Ivan (ro)
1473 * Maxim Dziumanenko (uk)
1474 * Clytie Siddall (vi)
1475
1476 ===============
1477 Version 2.11.91
1478 ===============
1479
1480 Fixes
1481
1482 * Install .desktop file for editor (Dennis Cranston, Mark)
1483 * Fix the window icon in the editor (Jaap A. Haitsma, Mark)
1484 * Allow running editor in different prefix from python (Mark)
1485
1486 Translators
1487
1488 * Miloslav Trmac (cs)
1489 * Hendrik Brandt (de)
1490 * Adam Weinberger (en_CA)
1491 * Francisco Javier F. Serrador (es)
1492 * Priit Laes (et)
1493 * Ilkka Tuohela (fi)
1494 * Ankit Patel (gu)
1495 * Reuven Gonen (he)
1496 * Gabor Kelemen (hu)
1497 * Takeshi AIHANA (ja)
1498 * Kjartan Maraas (nb)
1499 * Tino Meinen (nl)
1500 * Kjartan Maraas (no)
1501 * Afonso Celso Medina (pt_BR)
1502 * Marcel Telka (sk)
1503 * Theppitak Karoonboonyanan (th)
1504 * Clytie Siddall (vi)
1505 * Funda Wang (zh_CN)
1506 * Woodman Tuen (zh_TW)
1507
1508 ===============
1509 Version 2.11.90
1510 ===============
1511
1512 Fixes
1513
1514 * Fix issue with handling of filename encodings (Mark)
1515 * Only try to include ".directory" for <LegacyDir> if it exists (Mark)
1516 * Re-name the Edutainment sub-menu to Education (Mark)
1517 * Fix spec compliance issue with <Merge> tag handling (Mark)
1518 * Remove some unused code (Mark)
1519 * Plug some leaks (Mark)
1520
1521 Menu Editor
1522
1523 * HIGify menu editor (Dennis Cranston)
1524 * Make "Desktop" menu appear correctly (Mark)
1525
1526 Misc
1527
1528 * Allow building against uninstalled library (Brian Cameron)
1529
1530 Translators
1531
1532 * Ales Nyakhaychyk (be)
1533 * Rostislav "zbrox" Raykov (bg)
1534 * Miloslav Trmac (cs)
1535 * Martin Willemoes Hansen (da)
1536 * Hendrik Brandt (de)
1537 * Nikos Charonitakis (el)
1538 * Adam Weinberger (en_CA)
1539 * Francisco Javier F. Serrador (es)
1540 * Priit Laes (et)
1541 * Iñaki Larrañaga (eu)
1542 * Ilkka Tuohela (fi)
1543 * Ignacio Casal Quinteiro (gl)
1544 * Ankit Patel (gu)
1545 * Yuval Tanny (he)
1546 * Swapnil Hajare (mr)
1547 * Terance Edward Sola (nb)
1548 * Ganesh Ghimire (ne)
1549 * Tino Meinen (nl)
1550 * Terance Edward Sola (no)
1551 * Marcel Telka (sk)
1552 * Elian Myftiu (sq)
1553 * Данило Шеган (sr)
1554 * Theppitak Karoonboonyanan (th)
1555 * Onur Can Cakmak (tr)
1556 * Clytie Siddall (vi)
1557 * Funda Wang (zh_CN)
1558 * Woodman Tuen (zh_TW)
1559
1560 ================
1561 Version 2.11.1.1
1562 ================
1563
1564 Fixes
1565
1566 * Fix crasher bug in libgnome-menu triggered by editor (Mark)
1567 * Make the editor create $XDG_CONFIG_HOME/menus if it doesn't exist (Mark)
1568
1569 ==============
1570 Version 2.11.1
1571 ==============
1572
1573 Features
1574
1575 * Simple menu editor (Mark)
1576 * Python bindings (Mark)
1577 * Support for <Layout> and <DefaultLayout> (Mark, Frederic Crozat)
1578 * Use FAM directly for monitoring rather than gnome-vfs (Mark)
1579 * Add API for retaining empty sub-menus and excluded items in
1580 the GMenuTree (Mark, Christian Neumair)
1581 * Add gmenu_tree_directory_get_menu_id() API (Mark)
1582 * Add gmenu_tree_directory_get_tree() and gmenu_tree_get_menu_file()
1583 API (Mark)
1584 * Namespace the API - i.e. MenuTree -> GMenuTree (Mark)
1585
1586 Fixes
1587
1588 * Plug major memory leak when the menu is reloaded (Mark)
1589 * Fix "recursive <MergeFile> inclusion" crash (Mark)
1590 * Fix problem where you could end up with identical items in
1591 the same menu (Mark)
1592 * Fix issue where you could end up with more than one menu
1593 with the same name (Mark)
1594 * Update for changes to <Move> behaviour in spec (Mark)
1595 * Fix off-by-one errors shown up in valgrind (Mark)
1596 * Remove <LegacyDir>s from default menu (Mark)
1597
1598 Translators
1599
1600 * Vladimir "Kaladan" Petkov (bg)
1601 * Miloslav Trmac (cs)
1602 * Hendrik Brandt (de)
1603 * Adam Weinberger (en_CA)
1604 * David Lodge (en_GB)
1605 * Francisco Javier F. Serrador (es)
1606 * Priit Laes (et)
1607 * Iñaki Larrañaga (eu)
1608 * Takeshi AIHANA (ja)
1609 * Steve Murphy (rw)
1610 * Canonical Ltd (xh)
1611
1612 ==============
1613 Version 2.10.1
1614 ==============
1615
1616 Fixes
1617
1618 * Add support for new "type" argument to <MergeFile> (Mark)
1619 * Monitor <MergeDir>s for changes (Mark)
1620 * Make user desktop entries override system ones (Mark)
1621 * Make .directory files in <LegacyDir>s be pulled in (Mark)
1622 * Fix weirdess with [KDE Desktop Entry] files (Mark)
1623 * Fix <LegacyDir>s which don't contain any entries in the toplevel (Mark)
1624 * Make sure items in <LegacyDir>s as allocated (Mark)
1625 * Make <LegacyDir>s with a prefix work correctly (Mark)
1626
1627 Translators
1628
1629 * Adam Weinberger (en_CA)
1630 * Daniel van Eeden (nl)
1631
1632 ==============
1633 Version 2.10.0
1634 ==============
1635
1636 Fixes
1637
1638 * Fix 64-bit crasher (Jeremy Katz)
1639
1640 Translators
1641
1642 * Dafydd Harries (cy)
1643 * Farzaneh Sarafraz (fa)
1644 * Rajesh Ranjan (hi)
1645 * Žygimantas Beručka (lt)
1646 * Данило Шеган (sr)
1647 * Woodman Tuen (zh_TW)
1648
1649 ==============
1650 Version 2.9.92
1651 ==============
1652
1653 Fixes
1654
1655 * Fix issue with file monitoring and subdirs of <AppDir> (Mark)
1656 * Fix bug with the <Not> directive (Mark)
1657 * Make gnome-menu-spec-test work with menu-spec test framework again (Mark)
1658
1659 Translators
1660
1661 * Arafat Medini (ar)
1662 * Jordi Mallach (ca)
1663 * Martin Willemoes Hansen (da)
1664 * Nikos Charonitakis (el)
1665 * David Lodge (en_GB)
1666 * Ankit Patel (gu)
1667 * Laszlo Dvornik (hu)
1668 * ahmad riza h nst (id)
1669 * Francesco Marletta (it)
1670 * Takeshi AIHANA (ja)
1671 * Sang-Gju Kim (ko)
1672 * Rajeev Shrestha (ne)
1673 * Daniel van Eeden (nl)
1674 * GNOME PL Team (pl)
1675 * Duarte Loreto (pt)
1676 * Dan Damian (ro)
1677 * Leonid Kanter (ru)
1678 * Elian Myftiu (sq)
1679
1680 ==============
1681 Version 2.9.90
1682 ==============
1683
1684 Fixes
1685
1686 * Do not include the Core category in the Other menu (Vincent Untz)
1687
1688 Translators
1689
1690 * Vladimir "Kaladan" Petkov (bg)
1691 * Francisco Javier F. Serrador (es)
1692 * Priit Laes (et)
1693 * Tommi Vainikainen (fi)
1694 * Baptiste Mille-Mathias (fr)
1695 * Žygimantas Beručka (lt)
1696 * Kjartan Maraas (nb)
1697 * Kjartan Maraas (nn)
1698 * Raphael Higino (pt_BR)
1699 * Marcel Telka (sk)
1700 * Christian Rose (sv)
1701 * Theppitak Karoonboonyanan (th)
1702 * Maxim Dziumanenko (uk)
1703
1704 ===============
1705 Version 2.9.4.1
1706 ===============
1707
1708 Features
1709
1710 * Add menu_tree_entry_get_exec() (Richard Hult)
1711
1712 Translators
1713
1714 * Miloslav Trmac (cs)
1715 * Kjartan Maraas (nb)
1716
1717 =============
1718 Version 2.9.4
1719 =============
1720
1721 Fixes
1722
1723 * New menus layout (Vincent Untz)
1724 * Reload menus correctly when they are deleted/updated (Frederic Crozat)
1725 * Ref the return value from menu_tree_entry_get_parent() (Mark)
1726
1727 Translators
1728
1729 * Hendrik Brandt (de)
1730 * Adam Weinberger (en_CA)
1731 * Baptiste Mille-Mathias (fr)
1732 * Arangel Angov (mk)
1733
1734 =============
1735 Version 2.9.3
1736 =============
1737
1738 Fixes
1739
1740 * Find the right icon path in desktop files (Frederic Crozat)
1741 * Handle root path correctly (Mark)
1742 * Always remove file monitors (Mark)
1743 * Plug leak (Vincent Untz)
1744 * Implement behaviour defined in version 0.9 of the spec: entries that
1745 match an <Include> rule and an <Exclude> rule are marked as
1746 "allocated" (Mark)
1747
1748 Translators
1749
1750 * Vladimir "Kaladan" Petkov (bg)
1751 * David Nielsen (da)
1752 * Hendrik Brandt (de)
1753 * Simos Xenitellis (el)
1754 * Iñaki Larrañaga (eu)
1755 * Tommi Vainikainen (fi)
1756 * Gabor Kelemen (hu)
1757 * Žygimantas Beručka (lt)
1758 * Duarte Loreto (pt)
1759 * Dmitry G. Mastrukov (ru)
1760 * Marcel Telka (sk)
1761 * Данило Шеган (sr)
1762 * Christian Rose (sv)
1763 * Theppitak Karoonboonyanan (th)
1764
1765 =============
1766 Version 2.9.2
1767 =============
1768
1769 Fixes
1770
1771 * Fix a bunch of leaks (Frederic Crozat)
1772 * Fix problem where menu entries appear in random places (Mark)
1773 * Don't go into an infinite loop if $XDG_CONFIG_DIRS is set wrong (Mark)
1774 * Put the user config/data dirs before the system dirs (Mark)
1775 * Allow removing monitors from handlers (Mark)
1776
1777 Translators
1778
1779 * Miloslav Trmac (cs)
1780 * Hendrik Brandt (de)
1781 * Adam Weinberger (en_CA)
1782 * Francisco Javier F. Serrador (es)
1783 * Satoru SATOH (ja)
1784 * Hasbullah Bin Pit (ms)
1785 * Kjartan Maraas (nb)
1786 * Daniel van Eeden (nl)
1787 * Raphael Higino (pt_BR)
1788 * Funda Wang (zh_CN)
1789
0 gnome-menus
1 ===========
2
3 gnome-menus contains the libgnome-menu library, the layout configuration
4 files for the GNOME menu, as well as a simple menu editor.
5
6 The libgnome-menu library implements the "Desktop Menu Specification"
7 from freedesktop.org:
8
9 http://freedesktop.org/wiki/Specifications/menu-spec
10 http://specifications.freedesktop.org/menu-spec/menu-spec-latest.html
11
12 You may download updates to the package from:
13
14 http://download.gnome.org/sources/gnome-menus/
15
16 To discuss gnome-menus, you may use the desktop-devel-list mailing list:
17
18 http://mail.gnome.org/mailman/listinfo/desktop-devel-list
19
20
21 Installation
22 ============
23
24 See the file 'INSTALL'. If you are not using a released version of
25 gnome-menus (for example, if you checked out the code from git), you
26 first need to run './autogen.sh'.
27
28
29 How to report bugs
30 ==================
31
32 Bugs should be reported to the GNOME bug tracking system:
33
34 https://bugzilla.gnome.org/ (product gnome-menus)
35
36 You will need to create an account for yourself.
37
38 Please read the following page on how to prepare a useful bug report:
39
40 https://bugzilla.gnome.org/page.cgi?id=bug-writing.html
41
42 Please read the HACKING file for information on where to send changes or
43 bugfixes for this package.
0 #!/bin/sh
1 # Run this to generate all the initial makefiles, etc.
2 test -n "$srcdir" || srcdir=$(dirname "$0")
3 test -n "$srcdir" || srcdir=.
4
5 olddir=$(pwd)
6
7 cd $srcdir
8
9 (test -f configure.ac) || {
10 echo "*** ERROR: Directory '$srcdir' does not look like the top-level project directory ***"
11 exit 1
12 }
13
14 # shellcheck disable=SC2016
15 PKG_NAME=$(autoconf --trace 'AC_INIT:$1' configure.ac)
16
17 if [ "$#" = 0 -a "x$NOCONFIGURE" = "x" ]; then
18 echo "*** WARNING: I am going to run 'configure' with no arguments." >&2
19 echo "*** If you wish to pass any to it, please specify them on the" >&2
20 echo "*** '$0' command line." >&2
21 echo "" >&2
22 fi
23
24 mkdir -p m4
25
26 glib-gettextize --force --copy || exit 1
27 intltoolize --force --copy --automake || exit 1
28 autoreconf --verbose --force --install || exit 1
29
30 cd "$olddir"
31 if [ "$NOCONFIGURE" = "" ]; then
32 $srcdir/configure "$@" || exit 1
33
34 if [ "$1" = "--help" ]; then exit 0 else
35 echo "Now type 'make' to compile $PKG_NAME" || exit 1
36 fi
37 else
38 echo "Skipping configure process."
39 fi
0 AC_PREREQ(2.62)
1
2 AC_INIT([cinnamon-menus], [3.4.0])
3 AC_CONFIG_SRCDIR(libmenu/gmenu-tree.h)
4
5 m4_ifdef([AX_IS_RELEASE], [AX_IS_RELEASE([always])])
6
7 AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
8 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
9 AC_CONFIG_MACRO_DIR([m4])
10 AC_SUBST([ACLOCAL_AMFLAGS], ["-I $ac_macro_dir \${ACLOCAL_FLAGS}"])
11 AC_CONFIG_HEADERS(config.h)
12
13 AM_MAINTAINER_MODE
14
15 # Before making a release, the LT_VERSION string should be modified.
16 # The string is of the form C:R:A.
17 # - If interfaces have been changed or added, but binary compatibility has
18 # been preserved, change to C+1:0:A+1
19 # - If binary compatibility has been broken (eg removed or changed interfaces)
20 # change to C+1:0:0
21 # - If the interface is the same as the previous version, change to C:R+1:A
22
23 LIB_MENU_LT_VERSION=0:1:0
24 AC_SUBST(LIB_MENU_LT_VERSION)
25
26 AC_PROG_CC
27 AC_STDC_HEADERS
28 AC_ARG_PROGRAM
29 AC_LIBTOOL_WIN32_DLL
30 AM_PROG_LIBTOOL
31
32 PKG_CHECK_MODULES(GIO_UNIX, gio-unix-2.0 >= 2.29.15)
33 AC_SUBST(GIO_UNIX_CFLAGS)
34 AC_SUBST(GIO_UNIX_LIBS)
35
36 m4_ifdef([AX_COMPILER_FLAGS],
37 [AX_COMPILER_FLAGS([WARN_CFLAGS],[WARN_LDFLAGS])])
38
39 AC_ARG_ENABLE(deprecation_flags,
40 [AC_HELP_STRING([--enable-deprecation-flags],
41 [use *_DISABLE_DEPRECATED flags @<:@default=no@:>@])],,
42 [enable_deprecation_flags=no])
43
44 if test "x$enable_deprecation_flags" = "xyes"; then
45 DISABLE_DEPRECATED_CFLAGS=$DISABLE_DEPRECATED
46 AC_SUBST(DISABLE_DEPRECATED_CFLAGS)
47 fi
48
49 AC_ARG_ENABLE(debug,
50 [AS_HELP_STRING([--enable-debug=@<:@no/minimum/yes@:>@],
51 [turn on debugging @<:@default=debug_default@:>@])],,
52 [enable_debug=debug_default])
53 if test "x$enable_debug" = "xyes"; then
54 DEBUG_CFLAGS="-DG_ENABLE_DEBUG"
55 else
56 if test "x$enable_debug" = "xno"; then
57 DEBUG_CFLAGS="-DG_DISABLE_ASSERT -DG_DISABLE_CHECKS -DG_DISABLE_CAST_CHECKS"
58 else
59 DEBUG_CFLAGS="-DG_ENABLE_DEBUG -DG_DISABLE_CAST_CHECKS"
60 fi
61 fi
62 AC_SUBST(DEBUG_CFLAGS)
63
64 GOBJECT_INTROSPECTION_CHECK([0.9.5])
65
66 AC_OUTPUT([
67 Makefile
68 libmenu/Makefile
69 libmenu/libcinnamon-menu-3.0.pc
70 libmenu/libcinnamon-menu-3.0-uninstalled.pc
71 ])
72
73 dnl ---------------------------------------------------------------------------
74 dnl - Show summary
75 dnl ---------------------------------------------------------------------------
76
77 echo "
78 cinnamon-menus $VERSION
79 `echo cinnamon-menus $VERSION | sed "s/./=/g"`
80
81 prefix: ${prefix}
82 exec_prefix: ${exec_prefix}
83 libdir: ${libdir}
84 bindir: ${bindir}
85 sbindir: ${sbindir}
86 sysconfdir: ${sysconfdir}
87 localstatedir: ${localstatedir}
88 datadir: ${datadir}
89 source code location: ${srcdir}
90 compiler: ${CC}
91 cflags: ${CFLAGS}
92 Maintainer mode: ${USE_MAINTAINER_MODE}
93 Use *_DISABLE_DEPRECATED: ${enable_deprecation_flags}
94
95 Turn on debugging: ${enable_debug}
96 Build introspection support: ${found_introspection}
97
98 "
0 lib_LTLIBRARIES = libcinnamon-menu-3.la
1
2 AM_CPPFLAGS = \
3 $(GIO_UNIX_CFLAGS) \
4 $(WARN_CFLAGS) \
5 -DGMENU_I_KNOW_THIS_IS_UNSTABLE \
6 $(DISABLE_DEPRECATED_CFLAGS) \
7 $(DEBUG_CFLAGS)
8
9 AM_CFLAGS = $(WARN_CFLAGS)
10
11 libcinnamon_menu_3_includedir = $(includedir)/cinnamon-menus-3.0
12 libcinnamon_menu_3_include_HEADERS = \
13 gmenu-tree.h
14
15 libcinnamon_menu_3_sources = \
16 canonicalize.c \
17 desktop-entries.c \
18 entry-directories.c \
19 gmenu-tree.c \
20 menu-layout.c \
21 menu-monitor.c \
22 menu-util.c
23
24 libcinnamon_menu_3_la_SOURCES = \
25 $(libcinnamon_menu_3_sources) \
26 canonicalize.h \
27 desktop-entries.h \
28 entry-directories.h \
29 gmenu-tree.h \
30 menu-layout.h \
31 menu-monitor.h \
32 menu-util.h
33
34 libcinnamon_menu_3_la_LIBADD = \
35 $(GIO_UNIX_LIBS)
36
37 libcinnamon_menu_3_la_LDFLAGS = \
38 $(WARN_LDFLAGS) \
39 -version-info $(LIB_MENU_LT_VERSION) \
40 -no-undefined \
41 -export-symbols-regex gmenu_tree
42
43 pkgconfigdir = $(libdir)/pkgconfig
44 pkgconfig_DATA = libcinnamon-menu-3.0.pc
45
46 EXTRA_DIST = \
47 libcinnamon-menu-3.0.pc.in \
48 libcinnamon-menu-3.0-uninstalled.pc.in
49
50 CLEANFILES =
51
52 # Introspection
53 -include $(INTROSPECTION_MAKEFILE)
54 INTROSPECTION_GIRS =
55 INTROSPECTION_SCANNER_ARGS = --warn-all --add-include-path=$(srcdir)
56 INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
57
58 if HAVE_INTROSPECTION
59 introspection_sources = $(libcinnamon_menu_3_include_HEADERS) gmenu-tree.c
60
61 CMenu-3.0.gir: libcinnamon-menu-3.la
62 CMenu_3_0_gir_INCLUDES = Gio-2.0
63 CMenu_3_0_gir_CFLAGS = $(AM_CPPFLAGS)
64 CMenu_3_0_gir_LIBS = libcinnamon-menu-3.la
65 CMenu_3_0_gir_SCANNERFLAGS = $(WARN_SCANNERFLAGS) --identifier-prefix=GMenu --symbol-prefix=gmenu --pkg-export=libcinnamon-menu-3.0 --c-include=gmenu-tree.h
66 CMenu_3_0_gir_FILES = $(addprefix $(srcdir)/,$(introspection_sources))
67 INTROSPECTION_GIRS += CMenu-3.0.gir
68
69 girdir = $(INTROSPECTION_GIRDIR)
70 gir_DATA = $(INTROSPECTION_GIRS)
71
72 typelibdir = $(INTROSPECTION_TYPELIBDIR)
73 typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
74
75 CLEANFILES += $(gir_DATA) $(typelib_DATA)
76 endif
77
78 -include $(top_srcdir)/git.mk
0 /* Return the canonical absolute name of a given file.
1 Copyright (C) 1996-2001, 2002 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 Copyright (C) 2002 Red Hat, Inc. (trivial port to GLib)
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
20
21 #include <config.h>
22
23 #include "canonicalize.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <limits.h>
29 #include <sys/param.h>
30 #include <sys/stat.h>
31 #include <errno.h>
32 #include <stddef.h>
33
34 /* Return the canonical absolute name of file NAME. A canonical name
35 does not contain any `.', `..' components nor any repeated path
36 separators ('/') or symlinks. All path components must exist. If
37 RESOLVED is null, the result is malloc'd; otherwise, if the
38 canonical name is PATH_MAX chars or more, returns null with `errno'
39 set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars,
40 returns the name in RESOLVED. If the name cannot be resolved and
41 RESOLVED is non-NULL, it contains the path of the first component
42 that cannot be resolved. If the path can be resolved, RESOLVED
43 holds the same value as the value returned. */
44
45 static char*
46 menu_realpath (const char *name, char *resolved)
47 {
48 char *rpath, *dest, *extra_buf = NULL;
49 const char *start, *end, *rpath_limit;
50 long int path_max;
51 int num_links = 0;
52
53 if (name == NULL)
54 {
55 /* As per Single Unix Specification V2 we must return an error if
56 either parameter is a null pointer. We extend this to allow
57 the RESOLVED parameter to be NULL in case the we are expected to
58 allocate the room for the return value. */
59 errno = EINVAL;
60 return NULL;
61 }
62
63 if (name[0] == '\0')
64 {
65 /* As per Single Unix Specification V2 we must return an error if
66 the name argument points to an empty string. */
67 errno = ENOENT;
68 return NULL;
69 }
70
71 #ifdef PATH_MAX
72 path_max = PATH_MAX;
73 #else
74 path_max = pathconf (name, _PC_PATH_MAX);
75 if (path_max <= 0)
76 path_max = 1024;
77 #endif
78
79 rpath = resolved ? g_alloca (path_max) : g_malloc (path_max);
80 rpath_limit = rpath + path_max;
81
82 if (name[0] != G_DIR_SEPARATOR)
83 {
84 if (!getcwd (rpath, path_max))
85 {
86 rpath[0] = '\0';
87 goto error;
88 }
89 dest = strchr (rpath, '\0');
90 }
91 else
92 {
93 rpath[0] = G_DIR_SEPARATOR;
94 dest = rpath + 1;
95 }
96
97 for (start = end = name; *start; start = end)
98 {
99 struct stat st;
100 int n;
101
102 /* Skip sequence of multiple path-separators. */
103 while (*start == G_DIR_SEPARATOR)
104 ++start;
105
106 /* Find end of path component. */
107 for (end = start; *end && *end != G_DIR_SEPARATOR; ++end)
108 /* Nothing. */;
109
110 if (end - start == 0)
111 break;
112 else if (end - start == 1 && start[0] == '.')
113 /* nothing */;
114 else if (end - start == 2 && start[0] == '.' && start[1] == '.')
115 {
116 /* Back up to previous component, ignore if at root already. */
117 if (dest > rpath + 1)
118 while ((--dest)[-1] != G_DIR_SEPARATOR);
119 }
120 else
121 {
122 size_t new_size;
123
124 if (dest[-1] != G_DIR_SEPARATOR)
125 *dest++ = G_DIR_SEPARATOR;
126
127 if (dest + (end - start) >= rpath_limit)
128 {
129 ptrdiff_t dest_offset = dest - rpath;
130 char *new_rpath;
131
132 if (resolved)
133 {
134 #ifdef ENAMETOOLONG
135 errno = ENAMETOOLONG;
136 #else
137 /* Uh... just pick something */
138 errno = EINVAL;
139 #endif
140 if (dest > rpath + 1)
141 dest--;
142 *dest = '\0';
143 goto error;
144 }
145 new_size = rpath_limit - rpath;
146 if (end - start + 1 > path_max)
147 new_size += end - start + 1;
148 else
149 new_size += path_max;
150 new_rpath = (char *) realloc (rpath, new_size);
151 if (new_rpath == NULL)
152 goto error;
153 rpath = new_rpath;
154 rpath_limit = rpath + new_size;
155
156 dest = rpath + dest_offset;
157 }
158
159 memcpy (dest, start, end - start);
160 dest = dest + (end - start);
161 *dest = '\0';
162
163 if (stat (rpath, &st) < 0)
164 goto error;
165
166 if (S_ISLNK (st.st_mode))
167 {
168 char *buf = alloca (path_max);
169 size_t len;
170
171 if (++num_links > MAXSYMLINKS)
172 {
173 errno = ELOOP;
174 goto error;
175 }
176
177 n = readlink (rpath, buf, path_max);
178 if (n < 0)
179 goto error;
180 buf[n] = '\0';
181
182 if (!extra_buf)
183 extra_buf = g_alloca (path_max);
184
185 len = strlen (end);
186 if ((long int) (n + len) >= path_max)
187 {
188 #ifdef ENAMETOOLONG
189 errno = ENAMETOOLONG;
190 #else
191 /* Uh... just pick something */
192 errno = EINVAL;
193 #endif
194 goto error;
195 }
196
197 /* Careful here, end may be a pointer into extra_buf... */
198 g_memmove (&extra_buf[n], end, len + 1);
199 name = end = memcpy (extra_buf, buf, n);
200
201 if (buf[0] == G_DIR_SEPARATOR)
202 dest = rpath + 1; /* It's an absolute symlink */
203 else
204 /* Back up to previous component, ignore if at root already: */
205 if (dest > rpath + 1)
206 while ((--dest)[-1] != G_DIR_SEPARATOR);
207 }
208 }
209 }
210 if (dest > rpath + 1 && dest[-1] == G_DIR_SEPARATOR)
211 --dest;
212 *dest = '\0';
213
214 return resolved ? memcpy (resolved, rpath, dest - rpath + 1) : rpath;
215
216 error:
217 if (resolved)
218 strcpy (resolved, rpath);
219 else
220 g_free (rpath);
221 return NULL;
222 }
223
224 char *
225 menu_canonicalize_file_name (const char *name,
226 gboolean allow_missing_basename)
227 {
228 char *retval;
229
230 retval = menu_realpath (name, NULL);
231
232 /* We could avoid some system calls by using the second
233 * argument to realpath() instead of doing realpath
234 * all over again, but who cares really. we'll see if
235 * it's ever in a profile.
236 */
237 if (allow_missing_basename && retval == NULL)
238 {
239 char *dirname;
240 char *canonical_dirname;
241 dirname = g_path_get_dirname (name);
242 canonical_dirname = menu_realpath (dirname, NULL);
243 g_free (dirname);
244 if (canonical_dirname)
245 {
246 char *basename;
247 basename = g_path_get_basename (name);
248 retval = g_build_filename (canonical_dirname, basename, NULL);
249 g_free (basename);
250 g_free (canonical_dirname);
251 }
252 }
253
254 return retval;
255 }
0 /* Return the canonical absolute name of a given file.
1 Copyright (C) 1996-2001, 2002 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 Copyright (C) 2002 Red Hat, Inc. (trivial port to GLib)
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
20
21 #ifndef G_CANONICALIZE_H
22 #define G_CANONICALIZE_H
23
24 #include <glib.h>
25
26 G_BEGIN_DECLS
27
28 char* menu_canonicalize_file_name (const char *name,
29 gboolean allow_missing_basename);
30
31 G_END_DECLS
32
33 #endif /* G_CANONICALIZE_H */
0 /*
1 * Copyright (C) 2002 - 2004 Red Hat, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 02111-1307, USA.
17 */
18
19 #include <config.h>
20
21 #include "desktop-entries.h"
22 #include <gio/gdesktopappinfo.h>
23
24 #include <string.h>
25
26 #include "menu-util.h"
27
28 #define DESKTOP_ENTRY_GROUP "Desktop Entry"
29
30 struct DesktopEntry
31 {
32 guint refcount;
33
34 char *path;
35 const char *basename;
36
37 guint type : 2;
38 guint reserved : 30;
39 };
40
41 typedef struct
42 {
43 DesktopEntry base;
44
45 GDesktopAppInfo *appinfo;
46 GQuark *categories;
47 guint showin : 1;
48 } DesktopEntryDesktop;
49
50 typedef struct
51 {
52 DesktopEntry base;
53
54 char *name;
55 char *generic_name;
56 char *comment;
57 GIcon *icon;
58
59 guint nodisplay : 1;
60 guint hidden : 1;
61 guint showin : 1;
62 } DesktopEntryDirectory;
63
64 struct DesktopEntrySet
65 {
66 int refcount;
67 GHashTable *hash;
68 };
69
70 /*
71 * Desktop entries
72 */
73
74 /**
75 * unix_basename_from_path:
76 * @path: Path string
77 *
78 * Returns: A constant pointer into the basename of @path
79 */
80 static const char *
81 unix_basename_from_path (const char *path)
82 {
83 const char *basename = g_strrstr (path, "/");
84 if (basename)
85 return basename + 1;
86 else
87 return path;
88 }
89
90 static const char *
91 get_current_desktop (void)
92 {
93 static char *current_desktop = NULL;
94
95 /* Support XDG_CURRENT_DESKTOP environment variable; this can be used
96 * to abuse gnome-menus in non-GNOME desktops. */
97 if (!current_desktop)
98 {
99 const char *desktop;
100
101 desktop = g_getenv ("XDG_CURRENT_DESKTOP");
102
103 /* Note: if XDG_CURRENT_DESKTOP is set but empty, do as if it
104 * was not set */
105 if (!desktop || desktop[0] == '\0')
106 current_desktop = g_strdup ("GNOME");
107 else
108 current_desktop = g_strdup (desktop);
109 }
110
111 /* Using "*" means skipping desktop-related checks */
112 if (g_strcmp0 (current_desktop, "*") == 0)
113 return NULL;
114
115 return current_desktop;
116 }
117
118 static GIcon *
119 key_file_get_icon (GKeyFile *key_file)
120 {
121 GIcon *icon = NULL;
122 gchar *icon_name;
123
124 icon_name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP,
125 "Icon", NULL, NULL);
126 if (!icon_name)
127 return NULL;
128
129 if (g_path_is_absolute (icon_name)) {
130 GFile *file;
131
132 file = g_file_new_for_path (icon_name);
133 icon = g_file_icon_new (file);
134 g_object_unref (file);
135 } else {
136 char *p;
137
138 /* Work around a common mistake in desktop files */
139 if ((p = strrchr (icon_name, '.')) != NULL &&
140 (strcmp (p, ".png") == 0 ||
141 strcmp (p, ".xpm") == 0 ||
142 strcmp (p, ".svg") == 0))
143 *p = 0;
144
145 icon = g_themed_icon_new (icon_name);
146 }
147
148 g_free (icon_name);
149
150 return icon;
151 }
152
153 static gboolean
154 key_file_get_show_in (GKeyFile *key_file)
155 {
156 const gchar *current_desktop;
157 gchar **strv;
158 gboolean show_in = TRUE;
159 int i;
160 gchar *exec;
161
162 current_desktop = get_current_desktop ();
163 if (!current_desktop)
164 return TRUE;
165
166 exec = g_key_file_get_string (key_file,
167 DESKTOP_ENTRY_GROUP,
168 "Exec",
169 NULL);
170
171 if (exec) {
172 if (g_str_has_prefix (exec, "gnome-control-center")) {
173 g_free (exec);
174 return FALSE;
175 }
176 g_free (exec);
177 }
178
179 strv = g_key_file_get_string_list (key_file,
180 DESKTOP_ENTRY_GROUP,
181 "OnlyShowIn",
182 NULL,
183 NULL);
184
185 if (strv)
186 {
187 show_in = FALSE;
188 for (i = 0; strv[i]; i++)
189 {
190 if (!strcmp (strv[i], "GNOME") || !strcmp (strv[i], "X-Cinnamon"))
191 {
192 show_in = TRUE;
193 break;
194 }
195 }
196 }
197 else
198 {
199 strv = g_key_file_get_string_list (key_file,
200 DESKTOP_ENTRY_GROUP,
201 "NotShowIn",
202 NULL,
203 NULL);
204 if (strv)
205 {
206 show_in = TRUE;
207 for (i = 0; strv[i]; i++)
208 {
209 if (!strcmp (strv[i], current_desktop))
210 {
211 show_in = FALSE;
212 }
213 }
214 }
215 }
216 g_strfreev (strv);
217
218 return show_in;
219 }
220
221 static gboolean
222 desktop_entry_load_directory (DesktopEntry *entry,
223 GKeyFile *key_file,
224 GError **error)
225 {
226 DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*)entry;
227 char *type_str;
228
229 type_str = g_key_file_get_string (key_file, DESKTOP_ENTRY_GROUP, "Type", error);
230 if (!type_str)
231 return FALSE;
232
233 if (strcmp (type_str, "Directory") != 0)
234 {
235 g_set_error (error,
236 G_KEY_FILE_ERROR,
237 G_KEY_FILE_ERROR_INVALID_VALUE,
238 "\"%s\" does not contain the correct \"Type\" value\n", entry->path);
239 g_free (type_str);
240 return FALSE;
241 }
242
243 g_free (type_str);
244
245 entry_directory->name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "Name", NULL, error);
246 if (entry_directory->name == NULL)
247 return FALSE;
248
249 entry_directory->generic_name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "GenericName", NULL, NULL);
250 entry_directory->comment = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "Comment", NULL, NULL);
251 entry_directory->icon = key_file_get_icon (key_file);
252 entry_directory->nodisplay = g_key_file_get_boolean (key_file,
253 DESKTOP_ENTRY_GROUP,
254 "NoDisplay",
255 NULL);
256 entry_directory->hidden = g_key_file_get_boolean (key_file,
257 DESKTOP_ENTRY_GROUP,
258 "Hidden",
259 NULL);
260 entry_directory->showin = key_file_get_show_in (key_file);
261
262 return TRUE;
263 }
264
265 static DesktopEntryResultCode
266 desktop_entry_load (DesktopEntry *entry)
267 {
268 if (strstr (entry->path, "/menu-xdg/"))
269 return DESKTOP_ENTRY_LOAD_FAIL_OTHER;
270 if (entry->type == DESKTOP_ENTRY_DESKTOP)
271 {
272 GKeyFile *key_file = NULL;
273 DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop*)entry;
274 const char *categories_str;
275
276 entry_desktop->appinfo = g_desktop_app_info_new_from_filename (entry->path);
277 if (!entry_desktop->appinfo ||
278 !g_app_info_get_name (G_APP_INFO (entry_desktop->appinfo)) ||
279 !g_app_info_get_executable (G_APP_INFO (entry_desktop->appinfo)))
280 {
281 menu_verbose ("Failed to load \"%s\"\n", entry->path);
282 return DESKTOP_ENTRY_LOAD_FAIL_APPINFO;
283 }
284
285 categories_str = g_desktop_app_info_get_categories (entry_desktop->appinfo);
286 if (categories_str)
287 {
288 char **categories;
289 int i;
290
291 categories = g_strsplit (categories_str, ";", -1);
292 entry_desktop->categories = g_new0 (GQuark, g_strv_length (categories) + 1);
293
294 for (i = 0; categories[i]; i++)
295 entry_desktop->categories[i] = g_quark_from_string (categories[i]);
296
297 g_strfreev (categories);
298 }
299
300 key_file = g_key_file_new ();
301
302 if (!g_key_file_load_from_file (key_file, entry->path, 0, NULL))
303 entry_desktop->showin = TRUE;
304 else
305 entry_desktop->showin = key_file_get_show_in (key_file);
306
307 g_key_file_free (key_file);
308
309 return DESKTOP_ENTRY_LOAD_SUCCESS;
310 }
311 else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
312 {
313 GKeyFile *key_file = NULL;
314 GError *error = NULL;
315 DesktopEntryResultCode rescode = DESKTOP_ENTRY_LOAD_SUCCESS;
316
317 key_file = g_key_file_new ();
318
319 if (!g_key_file_load_from_file (key_file, entry->path, 0, &error))
320 {
321 rescode = DESKTOP_ENTRY_LOAD_FAIL_OTHER;
322 goto out;
323 }
324
325 if (!desktop_entry_load_directory (entry, key_file, &error))
326 {
327 rescode = DESKTOP_ENTRY_LOAD_FAIL_OTHER;
328 goto out;
329 }
330
331 rescode = DESKTOP_ENTRY_LOAD_SUCCESS;
332
333 out:
334 g_key_file_free (key_file);
335
336 if (rescode == DESKTOP_ENTRY_LOAD_FAIL_OTHER)
337 {
338 if (error)
339 {
340 menu_verbose ("Failed to load \"%s\": %s\n", entry->path, error->message);
341 g_error_free (error);
342 }
343 else
344 menu_verbose ("Failed to load \"%s\"\n", entry->path);
345 }
346
347 return rescode;
348 }
349 else
350 g_assert_not_reached ();
351
352 return DESKTOP_ENTRY_LOAD_FAIL_OTHER;
353 }
354
355 DesktopEntry *
356 desktop_entry_new (const char *path,
357 DesktopEntryResultCode *res_code)
358 {
359 DesktopEntryType type;
360 DesktopEntry *retval;
361 DesktopEntryResultCode code;
362
363 menu_verbose ("Loading desktop entry \"%s\"\n", path);
364
365 if (g_str_has_suffix (path, ".desktop"))
366 {
367 type = DESKTOP_ENTRY_DESKTOP;
368 retval = (DesktopEntry*)g_new0 (DesktopEntryDesktop, 1);
369 }
370 else if (g_str_has_suffix (path, ".directory"))
371 {
372 type = DESKTOP_ENTRY_DIRECTORY;
373 retval = (DesktopEntry*)g_new0 (DesktopEntryDirectory, 1);
374 }
375 else
376 {
377 menu_verbose ("Unknown desktop entry suffix in \"%s\"\n",
378 path);
379 *res_code = DESKTOP_ENTRY_LOAD_FAIL_OTHER;
380 return NULL;
381 }
382
383 retval->refcount = 1;
384 retval->type = type;
385 retval->path = g_strdup (path);
386 retval->basename = unix_basename_from_path (retval->path);
387
388 code = desktop_entry_load (retval);
389 *res_code = code;
390
391 if (code < DESKTOP_ENTRY_LOAD_SUCCESS)
392 {
393 desktop_entry_unref (retval);
394 return NULL;
395 }
396
397 return retval;
398 }
399
400 DesktopEntry *
401 desktop_entry_reload (DesktopEntry *entry)
402 {
403 g_return_val_if_fail (entry != NULL, NULL);
404
405 menu_verbose ("Re-loading desktop entry \"%s\"\n", entry->path);
406
407 if (entry->type == DESKTOP_ENTRY_DESKTOP)
408 {
409 DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop *) entry;
410
411 g_object_unref (entry_desktop->appinfo);
412 entry_desktop->appinfo = NULL;
413
414 g_free (entry_desktop->categories);
415 entry_desktop->categories = NULL;
416 }
417 else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
418 {
419 DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*) entry;
420
421 g_free (entry_directory->name);
422 entry_directory->name = NULL;
423
424 g_free (entry_directory->comment);
425 entry_directory->comment = NULL;
426
427 g_object_unref (entry_directory->icon);
428 entry_directory->icon = NULL;
429 }
430 else
431 g_assert_not_reached ();
432
433 if (desktop_entry_load (entry) < DESKTOP_ENTRY_LOAD_SUCCESS)
434 {
435 desktop_entry_unref (entry);
436 return NULL;
437 }
438
439 return entry;
440 }
441
442 DesktopEntry *
443 desktop_entry_ref (DesktopEntry *entry)
444 {
445 g_return_val_if_fail (entry != NULL, NULL);
446 g_return_val_if_fail (entry->refcount > 0, NULL);
447
448 g_atomic_int_inc (&entry->refcount);
449
450 return entry;
451 }
452
453 DesktopEntry *
454 desktop_entry_copy (DesktopEntry *entry)
455 {
456 DesktopEntry *retval;
457
458 menu_verbose ("Copying desktop entry \"%s\"\n",
459 entry->basename);
460
461 if (entry->type == DESKTOP_ENTRY_DESKTOP)
462 retval = (DesktopEntry*)g_new0 (DesktopEntryDesktop, 1);
463 else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
464 retval = (DesktopEntry*)g_new0 (DesktopEntryDirectory, 1);
465 else
466 g_assert_not_reached ();
467
468 retval->refcount = 1;
469 retval->type = entry->type;
470 retval->path = g_strdup (entry->path);
471 retval->basename = unix_basename_from_path (retval->path);
472
473 if (retval->type == DESKTOP_ENTRY_DESKTOP)
474 {
475 DesktopEntryDesktop *desktop_entry = (DesktopEntryDesktop*) entry;
476 DesktopEntryDesktop *retval_desktop_entry = (DesktopEntryDesktop*) retval;
477 int i;
478
479 retval_desktop_entry->appinfo = g_object_ref (desktop_entry->appinfo);
480
481 if (desktop_entry->categories != NULL)
482 {
483 i = 0;
484 for (; desktop_entry->categories[i]; i++);
485
486 retval_desktop_entry->categories = g_new0 (GQuark, i + 1);
487
488 i = 0;
489 for (; desktop_entry->categories[i]; i++)
490 retval_desktop_entry->categories[i] = desktop_entry->categories[i];
491 }
492 else
493 retval_desktop_entry->categories = NULL;
494 }
495 else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
496 {
497 DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*)entry;
498 DesktopEntryDirectory *retval_directory = (DesktopEntryDirectory*)retval;
499
500 retval_directory->name = g_strdup (entry_directory->name);
501 retval_directory->comment = g_strdup (entry_directory->comment);
502 retval_directory->icon = g_object_ref (entry_directory->icon);
503 retval_directory->nodisplay = entry_directory->nodisplay;
504 retval_directory->hidden = entry_directory->hidden;
505 retval_directory->showin = entry_directory->showin;
506 }
507
508 return retval;
509 }
510
511 void
512 desktop_entry_unref (DesktopEntry *entry)
513 {
514 g_return_if_fail (entry != NULL);
515 g_return_if_fail (entry->refcount > 0);
516
517 entry->refcount -= 1;
518 if (entry->refcount != 0)
519 return;
520
521 g_free (entry->path);
522 entry->path = NULL;
523
524 if (entry->type == DESKTOP_ENTRY_DESKTOP)
525 {
526 DesktopEntryDesktop *desktop_entry = (DesktopEntryDesktop*) entry;
527 g_free (desktop_entry->categories);
528 if (desktop_entry->appinfo)
529 g_object_unref (desktop_entry->appinfo);
530 }
531 else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
532 {
533 DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*) entry;
534
535 g_free (entry_directory->name);
536 entry_directory->name = NULL;
537
538 g_free (entry_directory->comment);
539 entry_directory->comment = NULL;
540
541 if (entry_directory->icon != NULL)
542 {
543 g_object_unref (entry_directory->icon);
544 entry_directory->icon = NULL;
545 }
546 }
547 else
548 g_assert_not_reached ();
549
550 g_free (entry);
551 }
552
553 DesktopEntryType
554 desktop_entry_get_type (DesktopEntry *entry)
555 {
556 return entry->type;
557 }
558
559 const char *
560 desktop_entry_get_path (DesktopEntry *entry)
561 {
562 return entry->path;
563 }
564
565 const char *
566 desktop_entry_get_basename (DesktopEntry *entry)
567 {
568 return entry->basename;
569 }
570
571 const char *
572 desktop_entry_get_name (DesktopEntry *entry)
573 {
574 if (entry->type == DESKTOP_ENTRY_DESKTOP)
575 return g_app_info_get_name (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo));
576 return ((DesktopEntryDirectory*)entry)->name;
577 }
578
579 const char *
580 desktop_entry_get_generic_name (DesktopEntry *entry)
581 {
582 if (entry->type == DESKTOP_ENTRY_DESKTOP)
583 return g_desktop_app_info_get_generic_name (((DesktopEntryDesktop*)entry)->appinfo);
584 return ((DesktopEntryDirectory*)entry)->generic_name;
585 }
586
587 const char *
588 desktop_entry_get_comment (DesktopEntry *entry)
589 {
590 if (entry->type == DESKTOP_ENTRY_DESKTOP)
591 return g_app_info_get_description (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo));
592 return ((DesktopEntryDirectory*)entry)->comment;
593 }
594
595 GIcon *
596 desktop_entry_get_icon (DesktopEntry *entry)
597 {
598 if (entry->type == DESKTOP_ENTRY_DESKTOP)
599 return g_app_info_get_icon (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo));
600 return ((DesktopEntryDirectory*)entry)->icon;
601 }
602
603 gboolean
604 desktop_entry_get_no_display (DesktopEntry *entry)
605 {
606 if (entry->type == DESKTOP_ENTRY_DESKTOP)
607 return g_desktop_app_info_get_nodisplay (((DesktopEntryDesktop*)entry)->appinfo);
608 return ((DesktopEntryDirectory*)entry)->nodisplay;
609 }
610
611 gboolean
612 desktop_entry_get_hidden (DesktopEntry *entry)
613 {
614 if (entry->type == DESKTOP_ENTRY_DESKTOP)
615 return g_desktop_app_info_get_is_hidden (((DesktopEntryDesktop*)entry)->appinfo);
616 return ((DesktopEntryDirectory*)entry)->hidden;
617 }
618
619 gboolean
620 desktop_entry_get_show_in (DesktopEntry *entry)
621 {
622 if (entry->type == DESKTOP_ENTRY_DESKTOP)
623 {
624 const char *current_desktop = get_current_desktop ();
625
626 if (current_desktop == NULL)
627 return TRUE;
628 else {
629 return ((DesktopEntryDesktop *)entry)->showin;
630 }
631 }
632 return ((DesktopEntryDirectory*)entry)->showin;
633 }
634
635
636 GDesktopAppInfo *
637 desktop_entry_get_app_info (DesktopEntry *entry)
638 {
639 g_return_val_if_fail (entry->type == DESKTOP_ENTRY_DESKTOP, NULL);
640 return ((DesktopEntryDesktop*)entry)->appinfo;
641 }
642
643 gboolean
644 desktop_entry_has_categories (DesktopEntry *entry)
645 {
646 DesktopEntryDesktop *desktop_entry;
647 if (entry->type != DESKTOP_ENTRY_DESKTOP)
648 return FALSE;
649
650 desktop_entry = (DesktopEntryDesktop*) entry;
651 return (desktop_entry->categories != NULL && desktop_entry->categories[0] != 0);
652 }
653
654 gboolean
655 desktop_entry_has_category (DesktopEntry *entry,
656 const char *category)
657 {
658 GQuark quark;
659 int i;
660 DesktopEntryDesktop *desktop_entry;
661
662 if (entry->type != DESKTOP_ENTRY_DESKTOP)
663 return FALSE;
664
665 desktop_entry = (DesktopEntryDesktop*) entry;
666
667 if (desktop_entry->categories == NULL)
668 return FALSE;
669
670 if (!(quark = g_quark_try_string (category)))
671 return FALSE;
672
673 for (i = 0; desktop_entry->categories[i]; i++)
674 {
675 if (quark == desktop_entry->categories[i])
676 return TRUE;
677 }
678
679 return FALSE;
680 }
681
682 void
683 desktop_entry_add_legacy_category (DesktopEntry *entry)
684 {
685 GQuark *categories;
686 int i;
687 DesktopEntryDesktop *desktop_entry;
688
689 g_return_if_fail (entry->type == DESKTOP_ENTRY_DESKTOP);
690
691 desktop_entry = (DesktopEntryDesktop*) entry;
692
693 menu_verbose ("Adding Legacy category to \"%s\"\n",
694 entry->basename);
695
696 if (desktop_entry->categories != NULL)
697 {
698 i = 0;
699 for (; desktop_entry->categories[i]; i++);
700
701 categories = g_new0 (GQuark, i + 2);
702
703 i = 0;
704 for (; desktop_entry->categories[i]; i++)
705 categories[i] = desktop_entry->categories[i];
706 }
707 else
708 {
709 categories = g_new0 (GQuark, 2);
710 i = 0;
711 }
712
713 categories[i] = g_quark_from_string ("Legacy");
714
715 g_free (desktop_entry->categories);
716 desktop_entry->categories = categories;
717 }
718
719 /*
720 * Entry sets
721 */
722
723 DesktopEntrySet *
724 desktop_entry_set_new (void)
725 {
726 DesktopEntrySet *set;
727
728 set = g_new0 (DesktopEntrySet, 1);
729 set->refcount = 1;
730
731 menu_verbose (" New entry set %p\n", set);
732
733 return set;
734 }
735
736 DesktopEntrySet *
737 desktop_entry_set_ref (DesktopEntrySet *set)
738 {
739 g_return_val_if_fail (set != NULL, NULL);
740 g_return_val_if_fail (set->refcount > 0, NULL);
741
742 g_atomic_int_inc (&set->refcount);
743
744 return set;
745 }
746
747 void
748 desktop_entry_set_unref (DesktopEntrySet *set)
749 {
750 gboolean is_zero;
751
752 g_return_if_fail (set != NULL);
753 g_return_if_fail (set->refcount > 0);
754
755 is_zero = g_atomic_int_dec_and_test (&set->refcount);
756 if (is_zero)
757 {
758 menu_verbose (" Deleting entry set %p\n", set);
759
760 if (set->hash)
761 g_hash_table_destroy (set->hash);
762 set->hash = NULL;
763
764 g_free (set);
765 }
766 }
767
768 void
769 desktop_entry_set_add_entry (DesktopEntrySet *set,
770 DesktopEntry *entry,
771 const char *file_id)
772 {
773 menu_verbose (" Adding to set %p entry %s\n", set, file_id);
774
775 if (set->hash == NULL)
776 {
777 set->hash = g_hash_table_new_full (g_str_hash,
778 g_str_equal,
779 g_free,
780 (GDestroyNotify) desktop_entry_unref);
781 }
782
783 g_hash_table_replace (set->hash,
784 g_strdup (file_id),
785 desktop_entry_ref (entry));
786 }
787
788 DesktopEntry *
789 desktop_entry_set_lookup (DesktopEntrySet *set,
790 const char *file_id)
791 {
792 if (set->hash == NULL)
793 return NULL;
794
795 return g_hash_table_lookup (set->hash, file_id);
796 }
797
798 typedef struct
799 {
800 DesktopEntrySetForeachFunc func;
801 gpointer user_data;
802 } EntryHashForeachData;
803
804 static void
805 entry_hash_foreach (const char *file_id,
806 DesktopEntry *entry,
807 EntryHashForeachData *fd)
808 {
809 fd->func (file_id, entry, fd->user_data);
810 }
811
812 void
813 desktop_entry_set_foreach (DesktopEntrySet *set,
814 DesktopEntrySetForeachFunc func,
815 gpointer user_data)
816 {
817 g_return_if_fail (set != NULL);
818 g_return_if_fail (func != NULL);
819
820 if (set->hash != NULL)
821 {
822 EntryHashForeachData fd;
823
824 fd.func = func;
825 fd.user_data = user_data;
826
827 g_hash_table_foreach (set->hash,
828 (GHFunc) entry_hash_foreach,
829 &fd);
830 }
831 }
832
833 static void
834 desktop_entry_set_clear (DesktopEntrySet *set)
835 {
836 menu_verbose (" Clearing set %p\n", set);
837
838 if (set->hash != NULL)
839 {
840 g_hash_table_destroy (set->hash);
841 set->hash = NULL;
842 }
843 }
844
845 int
846 desktop_entry_set_get_count (DesktopEntrySet *set)
847 {
848 if (set->hash == NULL)
849 return 0;
850
851 return g_hash_table_size (set->hash);
852 }
853
854 static void
855 union_foreach (const char *file_id,
856 DesktopEntry *entry,
857 DesktopEntrySet *set)
858 {
859 /* we are iterating over "with" adding anything not
860 * already in "set". We unconditionally overwrite
861 * the stuff in "set" because we can assume
862 * two entries with the same name are equivalent.
863 */
864 desktop_entry_set_add_entry (set, entry, file_id);
865 }
866
867 void
868 desktop_entry_set_union (DesktopEntrySet *set,
869 DesktopEntrySet *with)
870 {
871 menu_verbose (" Union of %p and %p\n", set, with);
872
873 if (desktop_entry_set_get_count (with) == 0)
874 return; /* A fast simple case */
875
876 g_hash_table_foreach (with->hash,
877 (GHFunc) union_foreach,
878 set);
879 }
880
881 typedef struct
882 {
883 DesktopEntrySet *set;
884 DesktopEntrySet *with;
885 } IntersectData;
886
887 static gboolean
888 intersect_foreach_remove (const char *file_id,
889 DesktopEntry *entry,
890 IntersectData *id)
891 {
892 /* Remove everything in "set" which is not in "with" */
893
894 if (g_hash_table_lookup (id->with->hash, file_id) != NULL)
895 return FALSE;
896
897 menu_verbose (" Removing from %p entry %s\n", id->set, file_id);
898
899 return TRUE; /* return TRUE to remove */
900 }
901
902 void
903 desktop_entry_set_intersection (DesktopEntrySet *set,
904 DesktopEntrySet *with)
905 {
906 IntersectData id;
907
908 menu_verbose (" Intersection of %p and %p\n", set, with);
909
910 if (desktop_entry_set_get_count (set) == 0 ||
911 desktop_entry_set_get_count (with) == 0)
912 {
913 /* A fast simple case */
914 desktop_entry_set_clear (set);
915 return;
916 }
917
918 id.set = set;
919 id.with = with;
920
921 g_hash_table_foreach_remove (set->hash,
922 (GHRFunc) intersect_foreach_remove,
923 &id);
924 }
925
926 typedef struct
927 {
928 DesktopEntrySet *set;
929 DesktopEntrySet *other;
930 } SubtractData;
931
932 static gboolean
933 subtract_foreach_remove (const char *file_id,
934 DesktopEntry *entry,
935 SubtractData *sd)
936 {
937 /* Remove everything in "set" which is not in "other" */
938
939 if (g_hash_table_lookup (sd->other->hash, file_id) == NULL)
940 return FALSE;
941
942 menu_verbose (" Removing from %p entry %s\n", sd->set, file_id);
943
944 return TRUE; /* return TRUE to remove */
945 }
946
947 void
948 desktop_entry_set_subtract (DesktopEntrySet *set,
949 DesktopEntrySet *other)
950 {
951 SubtractData sd;
952
953 menu_verbose (" Subtract from %p set %p\n", set, other);
954
955 if (desktop_entry_set_get_count (set) == 0 ||
956 desktop_entry_set_get_count (other) == 0)
957 return; /* A fast simple case */
958
959 sd.set = set;
960 sd.other = other;
961
962 g_hash_table_foreach_remove (set->hash,
963 (GHRFunc) subtract_foreach_remove,
964 &sd);
965 }
966
967 void
968 desktop_entry_set_swap_contents (DesktopEntrySet *a,
969 DesktopEntrySet *b)
970 {
971 GHashTable *tmp;
972
973 menu_verbose (" Swap contents of %p and %p\n", a, b);
974
975 tmp = a->hash;
976 a->hash = b->hash;
977 b->hash = tmp;
978 }
0 /*
1 * Copyright (C) 2002 - 2004 Red Hat, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 02111-1307, USA.
17 */
18
19 #ifndef __DESKTOP_ENTRIES_H__
20 #define __DESKTOP_ENTRIES_H__
21
22 #include <gio/gdesktopappinfo.h>
23
24 G_BEGIN_DECLS
25
26 typedef enum
27 {
28 DESKTOP_ENTRY_INVALID = 0,
29 DESKTOP_ENTRY_DESKTOP,
30 DESKTOP_ENTRY_DIRECTORY
31 } DesktopEntryType;
32
33 typedef enum
34 {
35 DESKTOP_ENTRY_LOAD_FAIL_OTHER = 0,
36 DESKTOP_ENTRY_LOAD_FAIL_APPINFO,
37 DESKTOP_ENTRY_LOAD_SUCCESS
38 } DesktopEntryResultCode;
39
40 typedef struct DesktopEntry DesktopEntry;
41
42 DesktopEntry *desktop_entry_new (const char *path,
43 DesktopEntryResultCode *res_code);
44
45 DesktopEntry *desktop_entry_ref (DesktopEntry *entry);
46 DesktopEntry *desktop_entry_copy (DesktopEntry *entry);
47 DesktopEntry *desktop_entry_reload (DesktopEntry *entry);
48 void desktop_entry_unref (DesktopEntry *entry);
49
50 DesktopEntryType desktop_entry_get_type (DesktopEntry *entry);
51 const char *desktop_entry_get_path (DesktopEntry *entry);
52 const char *desktop_entry_get_basename (DesktopEntry *entry);
53 const char *desktop_entry_get_name (DesktopEntry *entry);
54 const char *desktop_entry_get_generic_name (DesktopEntry *entry);
55 const char *desktop_entry_get_comment (DesktopEntry *entry);
56 GIcon *desktop_entry_get_icon (DesktopEntry *entry);
57 gboolean desktop_entry_get_hidden (DesktopEntry *entry);
58 gboolean desktop_entry_get_no_display (DesktopEntry *entry);
59 gboolean desktop_entry_get_show_in (DesktopEntry *entry);
60
61 /* Only valid for DESKTOP_ENTRY_DESKTOP */
62 GDesktopAppInfo *desktop_entry_get_app_info (DesktopEntry *entry);
63 gboolean desktop_entry_has_categories (DesktopEntry *entry);
64 gboolean desktop_entry_has_category (DesktopEntry *entry,
65 const char *category);
66
67 void desktop_entry_add_legacy_category (DesktopEntry *src);
68
69
70 typedef struct DesktopEntrySet DesktopEntrySet;
71
72 DesktopEntrySet *desktop_entry_set_new (void);
73 DesktopEntrySet *desktop_entry_set_ref (DesktopEntrySet *set);
74 void desktop_entry_set_unref (DesktopEntrySet *set);
75
76 void desktop_entry_set_add_entry (DesktopEntrySet *set,
77 DesktopEntry *entry,
78 const char *file_id);
79 DesktopEntry* desktop_entry_set_lookup (DesktopEntrySet *set,
80 const char *file_id);
81 int desktop_entry_set_get_count (DesktopEntrySet *set);
82
83 void desktop_entry_set_union (DesktopEntrySet *set,
84 DesktopEntrySet *with);
85 void desktop_entry_set_intersection (DesktopEntrySet *set,
86 DesktopEntrySet *with);
87 void desktop_entry_set_subtract (DesktopEntrySet *set,
88 DesktopEntrySet *other);
89 void desktop_entry_set_swap_contents (DesktopEntrySet *a,
90 DesktopEntrySet *b);
91
92 typedef void (*DesktopEntrySetForeachFunc) (const char *file_id,
93 DesktopEntry *entry,
94 gpointer user_data);
95
96 void desktop_entry_set_foreach (DesktopEntrySet *set,
97 DesktopEntrySetForeachFunc func,
98 gpointer user_data);
99
100 G_END_DECLS
101
102 #endif /* __DESKTOP_ENTRIES_H__ */
0 /*
1 * Copyright (C) 2002 - 2004 Red Hat, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 02111-1307, USA.
17 */
18
19 #include <config.h>
20
21 #include "entry-directories.h"
22
23 #include <string.h>
24 #include <errno.h>
25 #include <sys/types.h>
26 #include <dirent.h>
27
28 #include "menu-util.h"
29 #include "menu-monitor.h"
30 #include "canonicalize.h"
31
32 typedef struct CachedDir CachedDir;
33 typedef struct CachedDirMonitor CachedDirMonitor;
34
35 struct EntryDirectory
36 {
37 CachedDir *dir;
38 char *legacy_prefix;
39
40 guint entry_type : 2;
41 guint is_legacy : 1;
42 volatile gint refcount;
43 };
44
45 struct EntryDirectoryList
46 {
47 volatile int refcount;
48 int length;
49 GList *dirs;
50 };
51
52 struct CachedDir
53 {
54 CachedDir *parent;
55 char *name;
56
57 GSList *entries;
58 GSList *subdirs;
59 GSList *retry_later_desktop_entries;
60
61 MenuMonitor *dir_monitor;
62 GSList *monitors;
63
64 guint have_read_entries : 1;
65 guint deleted : 1;
66
67 GFunc notify;
68 gpointer notify_data;
69
70 volatile gint references;
71 };
72
73 struct CachedDirMonitor
74 {
75 EntryDirectory *ed;
76 EntryDirectoryChangedFunc callback;
77 gpointer user_data;
78 };
79
80 static void cached_dir_add_reference (CachedDir *dir);
81 static void cached_dir_remove_reference (CachedDir *dir);
82 static void cached_dir_free (CachedDir *dir);
83 static gboolean cached_dir_load_entries_recursive (CachedDir *dir,
84 const char *dirname);
85 static void cached_dir_unref (CachedDir *dir);
86 static CachedDir * cached_dir_add_subdir (CachedDir *dir,
87 const char *basename,
88 const char *path);
89 static gboolean cached_dir_remove_subdir (CachedDir *dir,
90 const char *basename);
91
92 static void handle_cached_dir_changed (MenuMonitor *monitor,
93 MenuMonitorEvent event,
94 const char *path,
95 CachedDir *dir);
96
97 /*
98 * Entry directory cache
99 */
100
101 static CachedDir *dir_cache = NULL;
102
103 static void
104 clear_cache (CachedDir *dir,
105 gpointer *cache)
106 {
107 *cache = NULL;
108 }
109
110 static CachedDir *
111 cached_dir_new (const char *name)
112 {
113 CachedDir *dir;
114
115 dir = g_new0 (CachedDir, 1);
116 dir->name = g_strdup (name);
117
118 return dir;
119 }
120
121 static CachedDir *
122 cached_dir_new_full (const char *name,
123 GFunc notify,
124 gpointer notify_data)
125 {
126 CachedDir *dir;
127
128 dir = cached_dir_new (name);
129
130 dir->notify = notify;
131 dir->notify_data = notify_data;
132
133 return dir;
134 }
135
136 static void
137 cached_dir_free (CachedDir *dir)
138 {
139 if (dir->dir_monitor)
140 {
141 menu_monitor_remove_notify (dir->dir_monitor,
142 (MenuMonitorNotifyFunc) handle_cached_dir_changed,
143 dir);
144 menu_monitor_unref (dir->dir_monitor);
145 dir->dir_monitor = NULL;
146 }
147
148 g_slist_foreach (dir->monitors, (GFunc) g_free, NULL);
149 g_slist_free (dir->monitors);
150 dir->monitors = NULL;
151
152 g_slist_foreach (dir->entries,
153 (GFunc) desktop_entry_unref,
154 NULL);
155 g_slist_free (dir->entries);
156 dir->entries = NULL;
157
158 g_slist_foreach (dir->subdirs,
159 (GFunc) cached_dir_unref,
160 NULL);
161 g_slist_free (dir->subdirs);
162 dir->subdirs = NULL;
163
164 g_slist_free_full (dir->retry_later_desktop_entries, g_free);
165 dir->retry_later_desktop_entries = NULL;
166
167 g_free (dir->name);
168 g_free (dir);
169 }
170
171 static CachedDir *
172 cached_dir_ref (CachedDir *dir)
173 {
174 g_atomic_int_inc (&dir->references);
175
176 return dir;
177 }
178
179 static void
180 cached_dir_unref (CachedDir *dir)
181 {
182 gboolean is_zero;
183
184 is_zero = g_atomic_int_dec_and_test (&dir->references);
185 if (is_zero)
186 {
187 CachedDir *parent;
188
189 parent = dir->parent;
190
191 if (parent != NULL)
192 cached_dir_remove_subdir (parent, dir->name);
193
194 if (dir->notify)
195 dir->notify (dir, dir->notify_data);
196
197 cached_dir_free (dir);
198 }
199 }
200
201 static inline CachedDir *
202 find_subdir (CachedDir *dir,
203 const char *subdir)
204 {
205 GSList *tmp;
206
207 tmp = dir->subdirs;
208 while (tmp != NULL)
209 {
210 CachedDir *sub = tmp->data;
211
212 if (strcmp (sub->name, subdir) == 0)
213 return sub;
214
215 tmp = tmp->next;
216 }
217
218 return NULL;
219 }
220
221 static DesktopEntry *
222 find_entry (CachedDir *dir,
223 const char *basename)
224 {
225 GSList *tmp;
226
227 tmp = dir->entries;
228 while (tmp != NULL)
229 {
230 const char *entry_basename;
231
232 entry_basename = desktop_entry_get_basename (tmp->data);
233 if (strcmp (entry_basename, basename) == 0)
234 {
235 return tmp->data;
236 }
237
238 tmp = tmp->next;
239 }
240
241 return NULL;
242 }
243
244 static DesktopEntry *
245 cached_dir_find_relative_path (CachedDir *dir,
246 const char *relative_path)
247 {
248 DesktopEntry *retval = NULL;
249 char **split;
250 int i;
251
252 split = g_strsplit (relative_path, "/", -1);
253
254 i = 0;
255 while (split[i] != NULL)
256 {
257 if (split[i + 1] != NULL)
258 {
259 if ((dir = find_subdir (dir, split[i])) == NULL)
260 break;
261 }
262 else
263 {
264 retval = find_entry (dir, split[i]);
265 break;
266 }
267
268 ++i;
269 }
270
271 g_strfreev (split);
272
273 return retval;
274 }
275
276 static CachedDir *
277 cached_dir_lookup (const char *canonical)
278 {
279 CachedDir *dir;
280 char **split;
281 int i;
282
283 if (dir_cache == NULL)
284 dir_cache = cached_dir_new_full ("/",
285 (GFunc) clear_cache,
286 &dir_cache);
287 dir = dir_cache;
288
289 g_assert (canonical != NULL && canonical[0] == G_DIR_SEPARATOR);
290
291 menu_verbose ("Looking up cached dir \"%s\"\n", canonical);
292
293 split = g_strsplit (canonical + 1, "/", -1);
294
295 i = 0;
296 while (split[i] != NULL)
297 {
298 CachedDir *subdir;
299
300 subdir = cached_dir_add_subdir (dir, split[i], NULL);
301
302 dir = subdir;
303
304 ++i;
305 }
306
307 g_strfreev (split);
308
309 g_assert (dir != NULL);
310
311 return dir;
312 }
313
314 static gboolean
315 cached_dir_add_entry (CachedDir *dir,
316 const char *basename,
317 const char *path)
318 {
319 DesktopEntry *entry;
320 DesktopEntryResultCode code;
321
322 entry = desktop_entry_new (path, &code);
323 if (entry == NULL)
324 {
325 if (code == DESKTOP_ENTRY_LOAD_FAIL_APPINFO)
326 {
327 menu_verbose ("Adding %s to the retry list (mimeinfo.cache maybe isn't done getting updated yet\n", path);
328
329 dir->retry_later_desktop_entries = g_slist_prepend (dir->retry_later_desktop_entries, g_strdup (path));
330 }
331
332 return FALSE;
333 }
334
335 dir->entries = g_slist_prepend (dir->entries, entry);
336
337 return TRUE;
338 }
339
340 static gboolean
341 cached_dir_update_entry (CachedDir *dir,
342 const char *basename,
343 const char *path)
344 {
345 GSList *tmp;
346
347 tmp = dir->entries;
348 while (tmp != NULL)
349 {
350 const char *entry_basename;
351 entry_basename = desktop_entry_get_basename (tmp->data);
352 if (strcmp (entry_basename, basename) == 0)
353 {
354 if (!desktop_entry_reload (tmp->data))
355 {
356 dir->entries = g_slist_delete_link (dir->entries, tmp);
357 }
358
359 return TRUE;
360 }
361
362 tmp = tmp->next;
363 }
364
365 return cached_dir_add_entry (dir, basename, path);
366 }
367
368 static gboolean
369 cached_dir_remove_entry (CachedDir *dir,
370 const char *basename)
371 {
372 GSList *tmp;
373
374 tmp = dir->entries;
375 while (tmp != NULL)
376 {
377 const char *entry_basename;
378 entry_basename = desktop_entry_get_basename (tmp->data);
379
380 if (strcmp (entry_basename, basename) == 0)
381 {
382 desktop_entry_unref (tmp->data);
383 dir->entries = g_slist_delete_link (dir->entries, tmp);
384 return TRUE;
385 }
386
387 tmp = tmp->next;
388 }
389
390 return FALSE;
391 }
392
393 static CachedDir *
394 cached_dir_add_subdir (CachedDir *dir,
395 const char *basename,
396 const char *path)
397 {
398 CachedDir *subdir;
399
400 subdir = find_subdir (dir, basename);
401
402 if (subdir != NULL)
403 {
404 subdir->deleted = FALSE;
405 return subdir;
406 }
407
408 subdir = cached_dir_new (basename);
409
410 if (path != NULL && !cached_dir_load_entries_recursive (subdir, path))
411 {
412 cached_dir_free (subdir);
413 return NULL;
414 }
415
416 menu_verbose ("Caching dir \"%s\"\n", basename);
417
418 subdir->parent = dir;
419 dir->subdirs = g_slist_prepend (dir->subdirs, cached_dir_ref (subdir));
420
421 return subdir;
422 }
423
424 static gboolean
425 cached_dir_remove_subdir (CachedDir *dir,
426 const char *basename)
427 {
428 CachedDir *subdir;
429
430 subdir = find_subdir (dir, basename);
431
432 if (subdir != NULL)
433 {
434 subdir->deleted = TRUE;
435
436 if (subdir->references == 0)
437 {
438 cached_dir_unref (subdir);
439 dir->subdirs = g_slist_remove (dir->subdirs, subdir);
440 }
441
442 return TRUE;
443 }
444
445 return FALSE;
446 }
447
448 static guint monitors_idle_handler = 0;
449 static GSList *pending_monitors_dirs = NULL;
450
451 static void
452 cached_dir_invoke_monitors (CachedDir *dir)
453 {
454 GSList *tmp;
455
456 tmp = dir->monitors;
457 while (tmp != NULL)
458 {
459 CachedDirMonitor *monitor = tmp->data;
460 GSList *next = tmp->next;
461
462 monitor->callback (monitor->ed, monitor->user_data);
463
464 tmp = next;
465 }
466
467 /* we explicitly don't invoke monitors of the parent since an
468 * event has been queued for it too */
469 }
470
471 static gboolean
472 emit_monitors_in_idle (void)
473 {
474 GSList *monitors_to_emit;
475 GSList *tmp;
476
477 monitors_to_emit = pending_monitors_dirs;
478
479 pending_monitors_dirs = NULL;
480 monitors_idle_handler = 0;
481
482 tmp = monitors_to_emit;
483 while (tmp != NULL)
484 {
485 CachedDir *dir = tmp->data;
486
487 cached_dir_invoke_monitors (dir);
488 cached_dir_remove_reference (dir);
489
490 tmp = tmp->next;
491 }
492
493 g_slist_free (monitors_to_emit);
494
495 return FALSE;
496 }
497
498 static void
499 cached_dir_queue_monitor_event (CachedDir *dir)
500 {
501 GSList *tmp;
502
503 tmp = pending_monitors_dirs;
504 while (tmp != NULL)
505 {
506 CachedDir *d = tmp->data;
507 GSList *next = tmp->next;
508
509 if (dir->parent == d->parent &&
510 g_strcmp0 (dir->name, d->name) == 0)
511 break;
512
513 tmp = next;
514 }
515
516 /* not found, so let's queue it */
517 if (tmp == NULL)
518 {
519 cached_dir_add_reference (dir);
520 pending_monitors_dirs = g_slist_append (pending_monitors_dirs, dir);
521 }
522
523 if (dir->parent)
524 {
525 cached_dir_queue_monitor_event (dir->parent);
526 }
527
528 if (monitors_idle_handler == 0)
529 {
530 monitors_idle_handler = g_idle_add ((GSourceFunc) emit_monitors_in_idle, NULL);
531 }
532 }
533
534 static void
535 handle_cached_dir_changed (MenuMonitor *monitor,
536 MenuMonitorEvent event,
537 const char *path,
538 CachedDir *dir)
539 {
540 gboolean handled = FALSE;
541 gboolean retry_changes = FALSE;
542
543 char *basename;
544 char *dirname;
545
546 menu_verbose ("'%s' notified of '%s' %s - invalidating cache\n",
547 dir->name,
548 path,
549 event == MENU_MONITOR_EVENT_CREATED ? ("created") :
550 event == MENU_MONITOR_EVENT_DELETED ? ("deleted") : ("changed"));
551
552 dirname = g_path_get_dirname (path);
553 basename = g_path_get_basename (path);
554
555 dir = cached_dir_lookup (dirname);
556
557 if (g_str_has_suffix (basename, ".desktop") ||
558 g_str_has_suffix (basename, ".directory"))
559 {
560 switch (event)
561 {
562 case MENU_MONITOR_EVENT_CREATED:
563 case MENU_MONITOR_EVENT_CHANGED:
564 handled = cached_dir_update_entry (dir, basename, path);
565 break;
566
567 case MENU_MONITOR_EVENT_DELETED:
568 handled = cached_dir_remove_entry (dir, basename);
569 break;
570
571 default:
572 g_assert_not_reached ();
573 break;
574 }
575 }
576 else if (g_strcmp0 (basename, "mimeinfo.cache") == 0)
577 {
578 /* The observed file notifies when a new desktop file is added
579 * (but fails to load) go something like:
580 *
581 * NOTIFY: foo.desktop
582 * NOTIFY: mimeinfo.cache.tempfile
583 * NOTIFY: mimeinfo.cache.tempfile
584 * NOTIFY: mimeinfo.cache
585 *
586 * Additionally, the failure is not upon trying to read the file,
587 * but attempting to get its GAppInfo (g_desktop_app_info_new_from_filename()
588 * in desktop-entries.c ln 277). If you jigger desktop_entry_load() around
589 * and read the file as a keyfile *first*, it succeeds. If you then try
590 * to run g_desktop_app_info_new_from_keyfile(), *then* it fails.
591 *
592 * The theory here is there is a race condition where app info (which includes
593 * mimetype stuff) is unavailable because mimeinfo.cache is updated immediately
594 * after the app is installed.
595 *
596 * What we do here is, when a desktop fails to load, we add it to a temporary
597 * list. We wait until mimeinfo.cache changes, then retry that desktop file,
598 * which succeeds this second time.
599 *
600 * Note: An alternative fix (presented more as a proof than a suggestion) is to
601 * change line 151 in menu-monitor.c to use g_timeout_add_seconds, and delay
602 * for one second. This also avoids the issue (but it remains a race condition).
603 */
604
605 GSList *iter;
606
607 menu_verbose ("mimeinfo changed, checking for failed entries\n");
608
609 for (iter = dir->retry_later_desktop_entries; iter != NULL; iter = iter->next)
610 {
611 const gchar *retry_path = iter->data;
612
613 menu_verbose ("retrying %s\n", retry_path);
614
615 char *retry_basename = g_path_get_basename (retry_path);
616
617 if (cached_dir_update_entry (dir, retry_basename, retry_path))
618 retry_changes = TRUE;
619
620 g_free (retry_basename);
621 }
622
623 g_slist_free_full (dir->retry_later_desktop_entries, g_free);
624 dir->retry_later_desktop_entries = NULL;
625
626 handled = retry_changes;
627 }
628 else /* Try recursing */
629 {
630 switch (event)
631 {
632 case MENU_MONITOR_EVENT_CREATED:
633 handled = cached_dir_add_subdir (dir, basename, path) != NULL;
634 break;
635
636 case MENU_MONITOR_EVENT_CHANGED:
637 break;
638
639 case MENU_MONITOR_EVENT_DELETED:
640 handled = cached_dir_remove_subdir (dir, basename);
641 break;
642
643 default:
644 g_assert_not_reached ();
645 break;
646 }
647 }
648
649 g_free (basename);
650 g_free (dirname);
651
652 if (handled)
653 {
654 /* CHANGED events don't change the set of desktop entries, unless it's the mimeinfo.cache file changing */
655 if (retry_changes || (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED))
656 {
657 _entry_directory_list_empty_desktop_cache ();
658 }
659
660 cached_dir_queue_monitor_event (dir);
661 }
662 }
663
664 static void
665 cached_dir_ensure_monitor (CachedDir *dir,
666 const char *dirname)
667 {
668 if (dir->dir_monitor == NULL)
669 {
670 dir->dir_monitor = menu_get_directory_monitor (dirname);
671 menu_monitor_add_notify (dir->dir_monitor,
672 (MenuMonitorNotifyFunc) handle_cached_dir_changed,
673 dir);
674 }
675 }
676
677 static gboolean
678 cached_dir_load_entries_recursive (CachedDir *dir,
679 const char *dirname)
680 {
681 DIR *dp;
682 struct dirent *dent;
683 GString *fullpath;
684 gsize fullpath_len;
685
686 g_assert (dir != NULL);
687
688 if (dir->have_read_entries)
689 return TRUE;
690
691 menu_verbose ("Attempting to read entries from %s (full path %s)\n",
692 dir->name, dirname);
693
694 dp = opendir (dirname);
695 if (dp == NULL)
696 {
697 menu_verbose ("Unable to list directory \"%s\"\n",
698 dirname);
699 return FALSE;
700 }
701
702 cached_dir_ensure_monitor (dir, dirname);
703
704 fullpath = g_string_new (dirname);
705 if (fullpath->str[fullpath->len - 1] != G_DIR_SEPARATOR)
706 g_string_append_c (fullpath, G_DIR_SEPARATOR);
707
708 fullpath_len = fullpath->len;
709
710 while ((dent = readdir (dp)) != NULL)
711 {
712 /* ignore . and .. */
713 if (dent->d_name[0] == '.' &&
714 (dent->d_name[1] == '\0' ||
715 (dent->d_name[1] == '.' &&
716 dent->d_name[2] == '\0')))
717 continue;
718
719 g_string_append (fullpath, dent->d_name);
720
721 if (g_str_has_suffix (dent->d_name, ".desktop") ||
722 g_str_has_suffix (dent->d_name, ".directory"))
723 {
724 cached_dir_add_entry (dir, dent->d_name, fullpath->str);
725 }
726 else /* Try recursing */
727 {
728 cached_dir_add_subdir (dir, dent->d_name, fullpath->str);
729 }
730
731 g_string_truncate (fullpath, fullpath_len);
732 }
733
734 closedir (dp);
735
736 g_string_free (fullpath, TRUE);
737
738 dir->have_read_entries = TRUE;
739
740 return TRUE;
741 }
742
743 static void
744 cached_dir_add_monitor (CachedDir *dir,
745 EntryDirectory *ed,
746 EntryDirectoryChangedFunc callback,
747 gpointer user_data)
748 {
749 CachedDirMonitor *monitor;
750 GSList *tmp;
751
752 tmp = dir->monitors;
753 while (tmp != NULL)
754 {
755 monitor = tmp->data;
756
757 if (monitor->ed == ed &&
758 monitor->callback == callback &&
759 monitor->user_data == user_data)
760 break;
761
762 tmp = tmp->next;
763 }
764
765 if (tmp == NULL)
766 {
767 monitor = g_new0 (CachedDirMonitor, 1);
768 monitor->ed = ed;
769 monitor->callback = callback;
770 monitor->user_data = user_data;
771
772 dir->monitors = g_slist_append (dir->monitors, monitor);
773 }
774 }
775
776 static void
777 cached_dir_remove_monitor (CachedDir *dir,
778 EntryDirectory *ed,
779 EntryDirectoryChangedFunc callback,
780 gpointer user_data)
781 {
782 GSList *tmp;
783
784 tmp = dir->monitors;
785 while (tmp != NULL)
786 {
787 CachedDirMonitor *monitor = tmp->data;
788 GSList *next = tmp->next;
789
790 if (monitor->ed == ed &&
791 monitor->callback == callback &&
792 monitor->user_data == user_data)
793 {
794 dir->monitors = g_slist_delete_link (dir->monitors, tmp);
795 g_free (monitor);
796 }
797
798 tmp = next;
799 }
800 }
801
802 static void
803 cached_dir_add_reference (CachedDir *dir)
804 {
805 cached_dir_ref (dir);
806
807 if (dir->parent != NULL)
808 {
809 cached_dir_add_reference (dir->parent);
810 }
811 }
812
813 static void
814 cached_dir_remove_reference (CachedDir *dir)
815 {
816 CachedDir *parent;
817
818 parent = dir->parent;
819
820 cached_dir_unref (dir);
821
822 if (parent != NULL)
823 {
824 cached_dir_remove_reference (parent);
825 }
826 }
827
828 /*
829 * Entry directories
830 */
831
832 static EntryDirectory *
833 entry_directory_new_full (DesktopEntryType entry_type,
834 const char *path,
835 gboolean is_legacy,
836 const char *legacy_prefix)
837 {
838 EntryDirectory *ed;
839 char *canonical;
840
841 menu_verbose ("Loading entry directory \"%s\" (legacy %s)\n",
842 path,
843 is_legacy ? "<yes>" : "<no>");
844
845 canonical = menu_canonicalize_file_name (path, FALSE);
846 if (canonical == NULL)
847 {
848 menu_verbose ("Failed to canonicalize \"%s\": %s\n",
849 path, g_strerror (errno));
850 return NULL;
851 }
852
853 ed = g_new0 (EntryDirectory, 1);
854
855 ed->dir = cached_dir_lookup (canonical);
856 g_assert (ed->dir != NULL);
857
858 cached_dir_add_reference (ed->dir);
859 cached_dir_load_entries_recursive (ed->dir, canonical);
860
861 ed->legacy_prefix = g_strdup (legacy_prefix);
862 ed->entry_type = entry_type;
863 ed->is_legacy = is_legacy != FALSE;
864 ed->refcount = 1;
865
866 g_free (canonical);
867
868 return ed;
869 }
870
871 EntryDirectory *
872 entry_directory_new (DesktopEntryType entry_type,
873 const char *path)
874 {
875 return entry_directory_new_full (entry_type, path, FALSE, NULL);
876 }
877
878 EntryDirectory *
879 entry_directory_new_legacy (DesktopEntryType entry_type,
880 const char *path,
881 const char *legacy_prefix)
882 {
883 return entry_directory_new_full (entry_type, path, TRUE, legacy_prefix);
884 }
885
886 EntryDirectory *
887 entry_directory_ref (EntryDirectory *ed)
888 {
889 g_return_val_if_fail (ed != NULL, NULL);
890 g_return_val_if_fail (ed->refcount > 0, NULL);
891
892 g_atomic_int_inc (&ed->refcount);
893
894 return ed;
895 }
896
897 void
898 entry_directory_unref (EntryDirectory *ed)
899 {
900 gboolean is_zero;
901
902 g_return_if_fail (ed != NULL);
903 g_return_if_fail (ed->refcount > 0);
904
905 is_zero = g_atomic_int_dec_and_test (&ed->refcount);
906 if (is_zero)
907 {
908 cached_dir_remove_reference (ed->dir);
909
910 ed->dir = NULL;
911 ed->entry_type = DESKTOP_ENTRY_INVALID;
912 ed->is_legacy = FALSE;
913
914 g_free (ed->legacy_prefix);
915 ed->legacy_prefix = NULL;
916
917 g_free (ed);
918 }
919 }
920
921 static void
922 entry_directory_add_monitor (EntryDirectory *ed,
923 EntryDirectoryChangedFunc callback,
924 gpointer user_data)
925 {
926 cached_dir_add_monitor (ed->dir, ed, callback, user_data);
927 }
928
929 static void
930 entry_directory_remove_monitor (EntryDirectory *ed,
931 EntryDirectoryChangedFunc callback,
932 gpointer user_data)
933 {
934 cached_dir_remove_monitor (ed->dir, ed, callback, user_data);
935 }
936
937 static DesktopEntry *
938 entry_directory_get_directory (EntryDirectory *ed,
939 const char *relative_path)
940 {
941 DesktopEntry *entry;
942
943 if (ed->entry_type != DESKTOP_ENTRY_DIRECTORY)
944 return NULL;
945
946 entry = cached_dir_find_relative_path (ed->dir, relative_path);
947 if (entry == NULL || desktop_entry_get_type (entry) != DESKTOP_ENTRY_DIRECTORY)
948 return NULL;
949
950 return desktop_entry_ref (entry);
951 }
952
953 static char *
954 get_desktop_file_id_from_path (EntryDirectory *ed,
955 DesktopEntryType entry_type,
956 const char *relative_path)
957 {
958 char *retval;
959
960 retval = NULL;
961
962 if (entry_type == DESKTOP_ENTRY_DESKTOP)
963 {
964 if (!ed->is_legacy)
965 {
966 retval = g_strdelimit (g_strdup (relative_path), "/", '-');
967 }
968 else
969 {
970 char *basename;
971
972 basename = g_path_get_basename (relative_path);
973
974 if (ed->legacy_prefix)
975 {
976 retval = g_strjoin ("-", ed->legacy_prefix, basename, NULL);
977 g_free (basename);
978 }
979 else
980 {
981 retval = basename;
982 }
983 }
984 }
985 else
986 {
987 retval = g_strdup (relative_path);
988 }
989
990 return retval;
991 }
992
993 typedef gboolean (* EntryDirectoryForeachFunc) (EntryDirectory *ed,
994 DesktopEntry *entry,
995 const char *file_id,
996 DesktopEntrySet *set,
997 gpointer user_data);
998
999 static gboolean
1000 entry_directory_foreach_recursive (EntryDirectory *ed,
1001 CachedDir *cd,
1002 GString *relative_path,
1003 EntryDirectoryForeachFunc func,
1004 DesktopEntrySet *set,
1005 gpointer user_data)
1006 {
1007 GSList *tmp;
1008 int relative_path_len;
1009
1010 if (cd->deleted)
1011 return TRUE;
1012
1013 relative_path_len = relative_path->len;
1014
1015 tmp = cd->entries;
1016 while (tmp != NULL)
1017 {
1018 DesktopEntry *entry = tmp->data;
1019
1020 if (desktop_entry_get_type (entry) == ed->entry_type)
1021 {
1022 gboolean ret;
1023 char *file_id;
1024 const char *basename;
1025
1026 basename = desktop_entry_get_basename (entry);
1027 g_string_append (relative_path, basename);
1028
1029 file_id = get_desktop_file_id_from_path (ed,
1030 ed->entry_type,
1031 relative_path->str);
1032
1033 ret = func (ed, entry, file_id, set, user_data);
1034
1035 g_free (file_id);
1036
1037 g_string_truncate (relative_path, relative_path_len);
1038
1039 if (!ret)
1040 return FALSE;
1041 }
1042
1043 tmp = tmp->next;
1044 }
1045
1046 tmp = cd->subdirs;
1047 while (tmp != NULL)
1048 {
1049 CachedDir *subdir = tmp->data;
1050
1051 g_string_append (relative_path, subdir->name);
1052 g_string_append_c (relative_path, G_DIR_SEPARATOR);
1053
1054 if (!entry_directory_foreach_recursive (ed,
1055 subdir,
1056 relative_path,
1057 func,
1058 set,
1059 user_data))
1060 return FALSE;
1061
1062 g_string_truncate (relative_path, relative_path_len);
1063
1064 tmp = tmp->next;
1065 }
1066
1067 return TRUE;
1068 }
1069
1070 static void
1071 entry_directory_foreach (EntryDirectory *ed,
1072 EntryDirectoryForeachFunc func,
1073 DesktopEntrySet *set,
1074 gpointer user_data)
1075 {
1076 GString *path;
1077
1078 path = g_string_new (NULL);
1079
1080 entry_directory_foreach_recursive (ed,
1081 ed->dir,
1082 path,
1083 func,
1084 set,
1085 user_data);
1086
1087 g_string_free (path, TRUE);
1088 }
1089
1090 void
1091 entry_directory_get_flat_contents (EntryDirectory *ed,
1092 DesktopEntrySet *desktop_entries,
1093 DesktopEntrySet *directory_entries,
1094 GSList **subdirs)
1095 {
1096 GSList *tmp;
1097
1098 if (subdirs)
1099 *subdirs = NULL;
1100
1101 tmp = ed->dir->entries;
1102 while (tmp != NULL)
1103 {
1104 DesktopEntry *entry = tmp->data;
1105 const char *basename;
1106
1107 basename = desktop_entry_get_path (entry);
1108
1109 if (desktop_entries &&
1110 desktop_entry_get_type (entry) == DESKTOP_ENTRY_DESKTOP)
1111 {
1112 char *file_id;
1113
1114 file_id = get_desktop_file_id_from_path (ed,
1115 DESKTOP_ENTRY_DESKTOP,
1116 basename);
1117
1118 desktop_entry_set_add_entry (desktop_entries,
1119 entry,
1120 file_id);
1121
1122 g_free (file_id);
1123 }
1124
1125 if (directory_entries &&
1126 desktop_entry_get_type (entry) == DESKTOP_ENTRY_DIRECTORY)
1127 {
1128 desktop_entry_set_add_entry (directory_entries,
1129 entry,
1130 basename);
1131 }
1132
1133 tmp = tmp->next;
1134 }
1135
1136 if (subdirs)
1137 {
1138 tmp = ed->dir->subdirs;
1139 while (tmp != NULL)
1140 {
1141 CachedDir *cd = tmp->data;
1142
1143 if (!cd->deleted)
1144 {
1145 *subdirs = g_slist_prepend (*subdirs, g_strdup (cd->name));
1146 }
1147
1148 tmp = tmp->next;
1149 }
1150 }
1151
1152 if (subdirs)
1153 *subdirs = g_slist_reverse (*subdirs);
1154 }
1155
1156 /*
1157 * Entry directory lists
1158 */
1159
1160 EntryDirectoryList *
1161 entry_directory_list_new (void)
1162 {
1163 EntryDirectoryList *list;
1164
1165 list = g_new0 (EntryDirectoryList, 1);
1166
1167 list->refcount = 1;
1168 list->dirs = NULL;
1169 list->length = 0;
1170
1171 return list;
1172 }
1173
1174 EntryDirectoryList *
1175 entry_directory_list_ref (EntryDirectoryList *list)
1176 {
1177 g_return_val_if_fail (list != NULL, NULL);
1178 g_return_val_if_fail (list->refcount > 0, NULL);
1179
1180 g_atomic_int_inc (&list->refcount);
1181
1182 return list;
1183 }
1184
1185 void
1186 entry_directory_list_unref (EntryDirectoryList *list)
1187 {
1188 gboolean is_zero;
1189
1190 g_return_if_fail (list != NULL);
1191 g_return_if_fail (list->refcount > 0);
1192
1193 is_zero = g_atomic_int_dec_and_test (&list->refcount);
1194 if (is_zero)
1195 {
1196 g_list_foreach (list->dirs, (GFunc) entry_directory_unref, NULL);
1197 g_list_free (list->dirs);
1198 list->dirs = NULL;
1199 list->length = 0;
1200 g_free (list);
1201 }
1202 }
1203
1204 void
1205 entry_directory_list_prepend (EntryDirectoryList *list,
1206 EntryDirectory *ed)
1207 {
1208 list->length += 1;
1209 list->dirs = g_list_prepend (list->dirs,
1210 entry_directory_ref (ed));
1211 }
1212
1213 int
1214 entry_directory_list_get_length (EntryDirectoryList *list)
1215 {
1216 return list->length;
1217 }
1218
1219 void
1220 entry_directory_list_append_list (EntryDirectoryList *list,
1221 EntryDirectoryList *to_append)
1222 {
1223 GList *tmp;
1224 GList *new_dirs = NULL;
1225
1226 if (to_append->length == 0)
1227 return;
1228
1229 tmp = to_append->dirs;
1230 while (tmp != NULL)
1231 {
1232 list->length += 1;
1233 new_dirs = g_list_prepend (new_dirs,
1234 entry_directory_ref (tmp->data));
1235
1236 tmp = tmp->next;
1237 }
1238
1239 new_dirs = g_list_reverse (new_dirs);
1240 list->dirs = g_list_concat (list->dirs, new_dirs);
1241 }
1242
1243 DesktopEntry *
1244 entry_directory_list_get_directory (EntryDirectoryList *list,
1245 const char *relative_path)
1246 {
1247 DesktopEntry *retval = NULL;
1248 GList *tmp;
1249
1250 tmp = list->dirs;
1251 while (tmp != NULL)
1252 {
1253 if ((retval = entry_directory_get_directory (tmp->data, relative_path)) != NULL)
1254 break;
1255
1256 tmp = tmp->next;
1257 }
1258
1259 return retval;
1260 }
1261
1262 gboolean
1263 _entry_directory_list_compare (const EntryDirectoryList *a,
1264 const EntryDirectoryList *b)
1265 {
1266 GList *al, *bl;
1267
1268 if (a == NULL && b == NULL)
1269 return TRUE;
1270
1271 if ((a == NULL || b == NULL))
1272 return FALSE;
1273
1274 if (a->length != b->length)
1275 return FALSE;
1276
1277 al = a->dirs; bl = b->dirs;
1278 while (al && bl && al->data == bl->data)
1279 {
1280 al = al->next;
1281 bl = bl->next;
1282 }
1283
1284 return (al == NULL && bl == NULL);
1285 }
1286
1287 static gboolean
1288 get_all_func (EntryDirectory *ed,
1289 DesktopEntry *entry,
1290 const char *file_id,
1291 DesktopEntrySet *set,
1292 gpointer user_data)
1293 {
1294 if (ed->is_legacy && !desktop_entry_has_categories (entry))
1295 {
1296 entry = desktop_entry_copy (entry);
1297 desktop_entry_add_legacy_category (entry);
1298 }
1299 else
1300 {
1301 entry = desktop_entry_ref (entry);
1302 }
1303
1304 desktop_entry_set_add_entry (set, entry, file_id);
1305 desktop_entry_unref (entry);
1306
1307 return TRUE;
1308 }
1309
1310 static DesktopEntrySet *entry_directory_last_set = NULL;
1311 static EntryDirectoryList *entry_directory_last_list = NULL;
1312
1313 void
1314 _entry_directory_list_empty_desktop_cache (void)
1315 {
1316 if (entry_directory_last_set != NULL)
1317 desktop_entry_set_unref (entry_directory_last_set);
1318 entry_directory_last_set = NULL;
1319
1320 if (entry_directory_last_list != NULL)
1321 entry_directory_list_unref (entry_directory_last_list);
1322 entry_directory_last_list = NULL;
1323 }
1324
1325 DesktopEntrySet *
1326 _entry_directory_list_get_all_desktops (EntryDirectoryList *list)
1327 {
1328 GList *tmp;
1329 DesktopEntrySet *set;
1330
1331 /* The only tricky thing here is that desktop files later
1332 * in the search list with the same relative path
1333 * are "hidden" by desktop files earlier in the path,
1334 * so we have to do the earlier files first causing
1335 * the later files to replace the earlier files
1336 * in the DesktopEntrySet
1337 *
1338 * We go from the end of the list so we can just
1339 * g_hash_table_replace and not have to do two
1340 * hash lookups (check for existing entry, then insert new
1341 * entry)
1342 */
1343
1344 /* This method is -extremely- slow, so we have a simple
1345 one-entry cache here */
1346 if (_entry_directory_list_compare (list, entry_directory_last_list))
1347 {
1348 menu_verbose (" Hit desktop list (%p) cache\n", list);
1349 return desktop_entry_set_ref (entry_directory_last_set);
1350 }
1351
1352 if (entry_directory_last_set != NULL)
1353 desktop_entry_set_unref (entry_directory_last_set);
1354 if (entry_directory_last_list != NULL)
1355 entry_directory_list_unref (entry_directory_last_list);
1356
1357 set = desktop_entry_set_new ();
1358 menu_verbose (" Storing all of list %p in set %p\n",
1359 list, set);
1360
1361 tmp = g_list_last (list->dirs);
1362 while (tmp != NULL)
1363 {
1364 entry_directory_foreach (tmp->data, get_all_func, set, NULL);
1365
1366 tmp = tmp->prev;
1367 }
1368
1369 entry_directory_last_list = entry_directory_list_ref (list);
1370 entry_directory_last_set = desktop_entry_set_ref (set);
1371
1372 return set;
1373 }
1374
1375 void
1376 entry_directory_list_add_monitors (EntryDirectoryList *list,
1377 EntryDirectoryChangedFunc callback,
1378 gpointer user_data)
1379 {
1380 GList *tmp;
1381
1382 tmp = list->dirs;
1383 while (tmp != NULL)
1384 {
1385 entry_directory_add_monitor (tmp->data, callback, user_data);
1386 tmp = tmp->next;
1387 }
1388 }
1389
1390 void
1391 entry_directory_list_remove_monitors (EntryDirectoryList *list,
1392 EntryDirectoryChangedFunc callback,
1393 gpointer user_data)
1394 {
1395 GList *tmp;
1396
1397 tmp = list->dirs;
1398 while (tmp != NULL)
1399 {
1400 entry_directory_remove_monitor (tmp->data, callback, user_data);
1401 tmp = tmp->next;
1402 }
1403 }
0 /*
1 * Copyright (C) 2002 - 2004 Red Hat, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 02111-1307, USA.
17 */
18
19 #ifndef __ENTRY_DIRECTORIES_H__
20 #define __ENTRY_DIRECTORIES_H__
21
22 #include <glib.h>
23 #include "desktop-entries.h"
24
25 G_BEGIN_DECLS
26
27 typedef struct EntryDirectory EntryDirectory;
28
29 typedef void (*EntryDirectoryChangedFunc) (EntryDirectory *ed,
30 gpointer user_data);
31
32 EntryDirectory *entry_directory_new (DesktopEntryType entry_type,
33 const char *path);
34 EntryDirectory *entry_directory_new_legacy (DesktopEntryType entry_type,
35 const char *path,
36 const char *legacy_prefix);
37
38 EntryDirectory *entry_directory_ref (EntryDirectory *ed);
39 void entry_directory_unref (EntryDirectory *ed);
40
41 void entry_directory_get_flat_contents (EntryDirectory *ed,
42 DesktopEntrySet *desktop_entries,
43 DesktopEntrySet *directory_entries,
44 GSList **subdirs);
45
46
47 typedef struct EntryDirectoryList EntryDirectoryList;
48
49 EntryDirectoryList *entry_directory_list_new (void);
50 EntryDirectoryList *entry_directory_list_ref (EntryDirectoryList *list);
51 void entry_directory_list_unref (EntryDirectoryList *list);
52
53 int entry_directory_list_get_length (EntryDirectoryList *list);
54 gboolean _entry_directory_list_compare (const EntryDirectoryList *a,
55 const EntryDirectoryList *b);
56
57 void entry_directory_list_prepend (EntryDirectoryList *list,
58 EntryDirectory *ed);
59 void entry_directory_list_append_list (EntryDirectoryList *list,
60 EntryDirectoryList *to_append);
61
62 void entry_directory_list_add_monitors (EntryDirectoryList *list,
63 EntryDirectoryChangedFunc callback,
64 gpointer user_data);
65 void entry_directory_list_remove_monitors (EntryDirectoryList *list,
66 EntryDirectoryChangedFunc callback,
67 gpointer user_data);
68
69 DesktopEntry* entry_directory_list_get_directory (EntryDirectoryList *list,
70 const char *relative_path);
71
72 DesktopEntrySet *_entry_directory_list_get_all_desktops (EntryDirectoryList *list);
73 void _entry_directory_list_empty_desktop_cache (void);
74
75 G_END_DECLS
76
77 #endif /* __ENTRY_DIRECTORIES_H__ */
0 /* -*- mode:c; c-file-style: "gnu"; indent-tabs-mode: nil -*-
1 * Copyright (C) 2003, 2004, 2011 Red Hat, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 02111-1307, USA.
17 */
18
19 #include <config.h>
20
21 #include "gmenu-tree.h"
22
23 #include <string.h>
24 #include <errno.h>
25
26 #include "menu-layout.h"
27 #include "menu-monitor.h"
28 #include "menu-util.h"
29 #include "canonicalize.h"
30
31 /* private */
32 typedef struct GMenuTreeItem GMenuTreeItem;
33 #define GMENU_TREE_ITEM(i) ((GMenuTreeItem *)(i))
34 #define GMENU_TREE_DIRECTORY(i) ((GMenuTreeDirectory *)(i))
35 #define GMENU_TREE_ENTRY(i) ((GMenuTreeEntry *)(i))
36 #define GMENU_TREE_SEPARATOR(i) ((GMenuTreeSeparator *)(i))
37 #define GMENU_TREE_HEADER(i) ((GMenuTreeHeader *)(i))
38 #define GMENU_TREE_ALIAS(i) ((GMenuTreeAlias *)(i))
39
40 enum {
41 PROP_0,
42
43 PROP_MENU_BASENAME,
44 PROP_MENU_PATH,
45 PROP_FLAGS
46 };
47
48 /* Signals */
49 enum
50 {
51 CHANGED,
52 LAST_SIGNAL
53 };
54
55 static guint gmenu_tree_signals [LAST_SIGNAL] = { 0 };
56
57 struct _GMenuTree
58 {
59 GObject parent_instance;
60
61 char *basename;
62 char *non_prefixed_basename;
63 char *path;
64 char *canonical_path;
65
66 GMenuTreeFlags flags;
67
68 GSList *menu_file_monitors;
69
70 MenuLayoutNode *layout;
71 GMenuTreeDirectory *root;
72 GHashTable *entries_by_id;
73
74 guint canonical : 1;
75 guint loaded : 1;
76 };
77
78 G_DEFINE_TYPE (GMenuTree, gmenu_tree, G_TYPE_OBJECT)
79
80 struct GMenuTreeItem
81 {
82 volatile gint refcount;
83
84 GMenuTreeItemType type;
85
86 GMenuTreeDirectory *parent;
87 GMenuTree *tree;
88 };
89
90 struct GMenuTreeIter
91 {
92 volatile gint refcount;
93
94 GMenuTreeItem *item;
95 GSList *contents;
96 GSList *contents_iter;
97 };
98
99 struct GMenuTreeDirectory
100 {
101 GMenuTreeItem item;
102
103 DesktopEntry *directory_entry;
104 char *name;
105
106 GSList *entries;
107 GSList *subdirs;
108
109 MenuLayoutValues default_layout_values;
110 GSList *default_layout_info;
111 GSList *layout_info;
112 GSList *contents;
113
114 guint only_unallocated : 1;
115 guint is_nodisplay : 1;
116 guint layout_pending_separator : 1;
117 guint preprocessed : 1;
118
119 /* 16 bits should be more than enough; G_MAXUINT16 means no inline header */
120 guint will_inline_header : 16;
121 };
122
123 struct GMenuTreeEntry
124 {
125 GMenuTreeItem item;
126
127 DesktopEntry *desktop_entry;
128 char *desktop_file_id;
129
130 guint is_excluded : 1;
131 guint is_unallocated : 1;
132 };
133
134 struct GMenuTreeSeparator
135 {
136 GMenuTreeItem item;
137 };
138
139 struct GMenuTreeHeader
140 {
141 GMenuTreeItem item;
142
143 GMenuTreeDirectory *directory;
144 };
145
146 struct GMenuTreeAlias
147 {
148 GMenuTreeItem item;
149
150 GMenuTreeDirectory *directory;
151 GMenuTreeItem *aliased_item;
152 };
153
154 static gboolean gmenu_tree_load_layout (GMenuTree *tree,
155 GError **error);
156 static void gmenu_tree_force_reload (GMenuTree *tree);
157 static gboolean gmenu_tree_build_from_layout (GMenuTree *tree,
158 GError **error);
159 static void gmenu_tree_force_rebuild (GMenuTree *tree);
160 static void gmenu_tree_resolve_files (GMenuTree *tree,
161 GHashTable *loaded_menu_files,
162 MenuLayoutNode *layout);
163 static void gmenu_tree_force_recanonicalize (GMenuTree *tree);
164 static void gmenu_tree_invoke_monitors (GMenuTree *tree);
165
166 static void gmenu_tree_item_unref_and_unset_parent (gpointer itemp);
167
168 typedef enum
169 {
170 MENU_FILE_MONITOR_INVALID = 0,
171 MENU_FILE_MONITOR_FILE,
172 MENU_FILE_MONITOR_NONEXISTENT_FILE,
173 MENU_FILE_MONITOR_DIRECTORY
174 } MenuFileMonitorType;
175
176 typedef struct
177 {
178 MenuFileMonitorType type;
179 MenuMonitor *monitor;
180 } MenuFileMonitor;
181
182 static void
183 handle_nonexistent_menu_file_changed (MenuMonitor *monitor,
184 MenuMonitorEvent event,
185 const char *path,
186 GMenuTree *tree)
187 {
188 if (event == MENU_MONITOR_EVENT_CHANGED ||
189 event == MENU_MONITOR_EVENT_CREATED)
190 {
191 menu_verbose ("\"%s\" %s, marking tree for recanonicalization\n",
192 path,
193 event == MENU_MONITOR_EVENT_CREATED ? "created" : "changed");
194
195 gmenu_tree_force_recanonicalize (tree);
196 gmenu_tree_invoke_monitors (tree);
197 }
198 }
199
200 static void
201 handle_menu_file_changed (MenuMonitor *monitor,
202 MenuMonitorEvent event,
203 const char *path,
204 GMenuTree *tree)
205 {
206 menu_verbose ("\"%s\" %s, marking tree for recanicalization\n",
207 path,
208 event == MENU_MONITOR_EVENT_CREATED ? "created" :
209 event == MENU_MONITOR_EVENT_CHANGED ? "changed" : "deleted");
210
211 gmenu_tree_force_recanonicalize (tree);
212 gmenu_tree_invoke_monitors (tree);
213 }
214
215 static void
216 handle_menu_file_directory_changed (MenuMonitor *monitor,
217 MenuMonitorEvent event,
218 const char *path,
219 GMenuTree *tree)
220 {
221 if (!g_str_has_suffix (path, ".menu"))
222 return;
223
224 menu_verbose ("\"%s\" %s, marking tree for recanicalization\n",
225 path,
226 event == MENU_MONITOR_EVENT_CREATED ? "created" :
227 event == MENU_MONITOR_EVENT_CHANGED ? "changed" : "deleted");
228
229 gmenu_tree_force_recanonicalize (tree);
230 gmenu_tree_invoke_monitors (tree);
231 }
232
233 static void
234 gmenu_tree_add_menu_file_monitor (GMenuTree *tree,
235 const char *path,
236 MenuFileMonitorType type)
237 {
238 MenuFileMonitor *monitor;
239
240 monitor = g_slice_new0 (MenuFileMonitor);
241
242 monitor->type = type;
243
244 switch (type)
245 {
246 case MENU_FILE_MONITOR_FILE:
247 menu_verbose ("Adding a menu file monitor for \"%s\"\n", path);
248
249 monitor->monitor = menu_get_file_monitor (path);
250 menu_monitor_add_notify (monitor->monitor,
251 (MenuMonitorNotifyFunc) handle_menu_file_changed,
252 tree);
253 break;
254
255 case MENU_FILE_MONITOR_NONEXISTENT_FILE:
256 menu_verbose ("Adding a menu file monitor for non-existent \"%s\"\n", path);
257
258 monitor->monitor = menu_get_file_monitor (path);
259 menu_monitor_add_notify (monitor->monitor,
260 (MenuMonitorNotifyFunc) handle_nonexistent_menu_file_changed,
261 tree);
262 break;
263
264 case MENU_FILE_MONITOR_DIRECTORY:
265 menu_verbose ("Adding a menu directory monitor for \"%s\"\n", path);
266
267 monitor->monitor = menu_get_directory_monitor (path);
268 menu_monitor_add_notify (monitor->monitor,
269 (MenuMonitorNotifyFunc) handle_menu_file_directory_changed,
270 tree);
271 break;
272
273 default:
274 g_assert_not_reached ();
275 break;
276 }
277
278 tree->menu_file_monitors = g_slist_prepend (tree->menu_file_monitors, monitor);
279 }
280
281 static void
282 remove_menu_file_monitor (MenuFileMonitor *monitor,
283 GMenuTree *tree)
284 {
285 switch (monitor->type)
286 {
287 case MENU_FILE_MONITOR_FILE:
288 menu_monitor_remove_notify (monitor->monitor,
289 (MenuMonitorNotifyFunc) handle_menu_file_changed,
290 tree);
291 break;
292
293 case MENU_FILE_MONITOR_NONEXISTENT_FILE:
294 menu_monitor_remove_notify (monitor->monitor,
295 (MenuMonitorNotifyFunc) handle_nonexistent_menu_file_changed,
296 tree);
297 break;
298
299 case MENU_FILE_MONITOR_DIRECTORY:
300 menu_monitor_remove_notify (monitor->monitor,
301 (MenuMonitorNotifyFunc) handle_menu_file_directory_changed,
302 tree);
303 break;
304
305 default:
306 g_assert_not_reached ();
307 break;
308 }
309
310 menu_monitor_unref (monitor->monitor);
311 monitor->monitor = NULL;
312
313 monitor->type = MENU_FILE_MONITOR_INVALID;
314
315 g_slice_free (MenuFileMonitor, monitor);
316 }
317
318 static void
319 gmenu_tree_remove_menu_file_monitors (GMenuTree *tree)
320 {
321 menu_verbose ("Removing all menu file monitors\n");
322
323 g_slist_foreach (tree->menu_file_monitors,
324 (GFunc) remove_menu_file_monitor,
325 tree);
326 g_slist_free (tree->menu_file_monitors);
327 tree->menu_file_monitors = NULL;
328 }
329
330 static gboolean
331 canonicalize_path (GMenuTree *tree,
332 const char *path)
333 {
334 tree->canonical_path = menu_canonicalize_file_name (path, FALSE);
335 if (tree->canonical_path)
336 {
337 tree->canonical = TRUE;
338 gmenu_tree_add_menu_file_monitor (tree,
339 tree->canonical_path,
340 MENU_FILE_MONITOR_FILE);
341 }
342 else
343 {
344 gmenu_tree_add_menu_file_monitor (tree,
345 path,
346 MENU_FILE_MONITOR_NONEXISTENT_FILE);
347 }
348
349 return tree->canonical;
350 }
351
352 static gboolean
353 canonicalize_basename_with_config_dir (GMenuTree *tree,
354 const char *basename,
355 const char *config_dir)
356 {
357 gboolean ret;
358 char *path;
359
360 path = g_build_filename (config_dir, "menus", basename, NULL);
361 ret = canonicalize_path (tree, path);
362 g_free (path);
363
364 return ret;
365 }
366
367 static void
368 canonicalize_basename (GMenuTree *tree,
369 const char *basename)
370 {
371 if (!canonicalize_basename_with_config_dir (tree,
372 basename,
373 g_get_user_config_dir ()))
374 {
375 const char * const *system_config_dirs;
376 int i;
377
378 system_config_dirs = g_get_system_config_dirs ();
379
380 i = 0;
381 while (system_config_dirs[i] != NULL)
382 {
383 if (canonicalize_basename_with_config_dir (tree,
384 basename,
385 system_config_dirs[i]))
386 break;
387
388 ++i;
389 }
390 }
391 }
392
393 static char *
394 prefix_menu_name (const char *orig_name)
395 {
396 char *prefix;
397 prefix = (char *) g_getenv ("XDG_MENU_PREFIX");
398 if (prefix == NULL)
399 prefix = "gnome-";
400 return g_strconcat (prefix, orig_name, NULL);
401 }
402
403 static gboolean
404 gmenu_tree_canonicalize_path (GMenuTree *tree,
405 GError **error)
406 {
407 const char *menu_file = NULL;
408
409 if (tree->canonical)
410 return TRUE;
411
412 g_assert (tree->canonical_path == NULL);
413
414 gmenu_tree_remove_menu_file_monitors (tree);
415
416 if (tree->path)
417 {
418 menu_file = tree->path;
419 canonicalize_path (tree, tree->path);
420 }
421 else
422 {
423 const gchar *xdg_menu_prefix;
424
425 menu_file = tree->basename;
426 xdg_menu_prefix = g_getenv ("XDG_MENU_PREFIX");
427
428 if (xdg_menu_prefix == NULL)
429 xdg_menu_prefix = "gnome-";
430
431 if (xdg_menu_prefix != NULL)
432 {
433 gchar *prefixed_basename;
434
435 prefixed_basename = g_strdup_printf ("%sapplications.menu",
436 xdg_menu_prefix);
437
438 /* Some gnome-menus using applications just use "applications.menu"
439 * as the basename and expect gnome-menus to prefix it. Others (e.g.
440 * Alacarte) explicitly use "${XDG_MENU_PREFIX}applications.menu" as
441 * the basename, because they want to save changes to the right files
442 * in ~. In both cases, we want to use "applications-merged" as the
443 * merge directory (as required by the fd.o menu spec), so we save
444 * the non-prefixed basename and use it later when calling
445 * menu_layout_load().
446 */
447 if (!g_strcmp0 (tree->basename, "applications.menu") ||
448 !g_strcmp0 (tree->basename, prefixed_basename))
449 {
450 canonicalize_basename (tree, prefixed_basename);
451 g_free (tree->non_prefixed_basename);
452 tree->non_prefixed_basename = g_strdup ("applications.menu");
453 }
454 g_free (prefixed_basename);
455 }
456
457 if (!tree->canonical)
458 canonicalize_basename (tree, tree->basename);
459 }
460
461 if (tree->canonical)
462 {
463 menu_verbose ("Successfully looked up menu_file for \"%s\": %s\n",
464 menu_file, tree->canonical_path);
465 return TRUE;
466 }
467 else
468 {
469 g_set_error (error,
470 G_IO_ERROR,
471 G_IO_ERROR_FAILED,
472 "Failed to look up menu_file for \"%s\"\n",
473 menu_file);
474 return FALSE;
475 }
476 }
477
478 static void
479 gmenu_tree_force_recanonicalize (GMenuTree *tree)
480 {
481 gmenu_tree_remove_menu_file_monitors (tree);
482
483 if (tree->canonical)
484 {
485 gmenu_tree_force_reload (tree);
486
487 g_free (tree->canonical_path);
488 tree->canonical_path = NULL;
489
490 tree->canonical = FALSE;
491 }
492 }
493
494 /**
495 * gmenu_tree_new:
496 * @menu_basename: Basename of menu file
497 * @flags: Flags controlling menu content
498 *
499 * Returns: (transfer full): A new #GMenuTree instance
500 */
501 GMenuTree *
502 gmenu_tree_new (const char *menu_basename,
503 GMenuTreeFlags flags)
504 {
505 g_return_val_if_fail (menu_basename != NULL, NULL);
506
507 return g_object_new (GMENU_TYPE_TREE,
508 "menu-basename", menu_basename,
509 "flags", flags,
510 NULL);
511 }
512
513 /**
514 * gmenu_tree_new_fo_path:
515 * @menu_path: Path of menu file
516 * @flags: Flags controlling menu content
517 *
518 * Returns: (transfer full): A new #GMenuTree instance
519 */
520 GMenuTree *
521 gmenu_tree_new_for_path (const char *menu_path,
522 GMenuTreeFlags flags)
523 {
524 g_return_val_if_fail (menu_path != NULL, NULL);
525
526 return g_object_new (GMENU_TYPE_TREE,
527 "menu-path", menu_path,
528 "flags", flags,
529 NULL);
530 }
531
532 static GObject *
533 gmenu_tree_constructor (GType type,
534 guint n_construct_properties,
535 GObjectConstructParam *construct_properties)
536 {
537 GObject *obj;
538 GMenuTree *self;
539
540 obj = G_OBJECT_CLASS (gmenu_tree_parent_class)->constructor (type,
541 n_construct_properties,
542 construct_properties);
543
544 /* If GMenuTree:menu-path is set, then we should make sure that
545 * GMenuTree:menu-basename is unset (especially as it has a default
546 * value). This has to be done here, in the constructor, since the
547 * properties are construct-only. */
548
549 self = GMENU_TREE (obj);
550
551 if (self->path != NULL)
552 g_object_set (self, "menu-basename", NULL, NULL);
553
554 return obj;
555 }
556
557 static void
558 gmenu_tree_set_property (GObject *object,
559 guint prop_id,
560 const GValue *value,
561 GParamSpec *pspec)
562 {
563 GMenuTree *self = GMENU_TREE (object);
564
565 switch (prop_id)
566 {
567 case PROP_MENU_BASENAME:
568 self->basename = g_value_dup_string (value);
569 break;
570
571 case PROP_MENU_PATH:
572 self->path = g_value_dup_string (value);
573 break;
574
575 case PROP_FLAGS:
576 self->flags = g_value_get_flags (value);
577 break;
578
579 default:
580 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
581 break;
582 }
583 }
584
585 static void
586 gmenu_tree_get_property (GObject *object,
587 guint prop_id,
588 GValue *value,
589 GParamSpec *pspec)
590 {
591 GMenuTree *self = GMENU_TREE (object);
592
593 switch (prop_id)
594 {
595 case PROP_MENU_BASENAME:
596 g_value_set_string (value, self->basename);
597 break;
598 case PROP_MENU_PATH:
599 g_value_set_string (value, self->path);
600 break;
601 case PROP_FLAGS:
602 g_value_set_flags (value, self->flags);
603 break;
604 default:
605 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
606 break;
607 }
608 }
609
610 static void
611 gmenu_tree_finalize (GObject *object)
612 {
613 GMenuTree *tree = GMENU_TREE (object);
614
615 gmenu_tree_force_recanonicalize (tree);
616
617 if (tree->basename != NULL)
618 g_free (tree->basename);
619 tree->basename = NULL;
620
621 g_free (tree->non_prefixed_basename);
622 tree->non_prefixed_basename = NULL;
623
624 if (tree->path != NULL)
625 g_free (tree->path);
626 tree->path = NULL;
627
628 if (tree->canonical_path != NULL)
629 g_free (tree->canonical_path);
630 tree->canonical_path = NULL;
631
632 g_hash_table_destroy (tree->entries_by_id);
633 tree->entries_by_id = NULL;
634
635 G_OBJECT_CLASS (gmenu_tree_parent_class)->finalize (object);
636 }
637
638 static void
639 gmenu_tree_init (GMenuTree *self)
640 {
641 self->entries_by_id = g_hash_table_new (g_str_hash, g_str_equal);
642 }
643
644 static void
645 gmenu_tree_class_init (GMenuTreeClass *klass)
646 {
647 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
648
649 gobject_class->constructor = gmenu_tree_constructor;
650 gobject_class->get_property = gmenu_tree_get_property;
651 gobject_class->set_property = gmenu_tree_set_property;
652 gobject_class->finalize = gmenu_tree_finalize;
653
654 /**
655 * GMenuTree:menu-basename:
656 *
657 * The name of the menu file; must be a basename or a relative path. The file
658 * will be looked up in $XDG_CONFIG_DIRS/menus/. See the Desktop Menu
659 * specification.
660 */
661 g_object_class_install_property (gobject_class,
662 PROP_MENU_BASENAME,
663 g_param_spec_string ("menu-basename", "", "",
664 "applications.menu",
665 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
666 /**
667 * GMenuTree:menu-path:
668 *
669 * The full path of the menu file. If set, GMenuTree:menu-basename will get
670 * ignored.
671 */
672 g_object_class_install_property (gobject_class,
673 PROP_MENU_PATH,
674 g_param_spec_string ("menu-path", "", "",
675 NULL,
676 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
677 /**
678 * GMenuTree:flags:
679 *
680 * Flags controlling the content of the menu.
681 */
682 g_object_class_install_property (gobject_class,
683 PROP_FLAGS,
684 g_param_spec_flags ("flags", "", "",
685 GMENU_TYPE_TREE_FLAGS,
686 GMENU_TREE_FLAGS_NONE,
687 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
688
689 /**
690 * GMenuTree:changed:
691 *
692 * This signal is emitted when applications are added, removed, or
693 * upgraded. But note the new data will only be visible after
694 * gmenu_tree_load_sync() or a variant thereof is invoked.
695 */
696 gmenu_tree_signals[CHANGED] =
697 g_signal_new ("changed",
698 G_TYPE_FROM_CLASS (klass),
699 G_SIGNAL_RUN_LAST,
700 0,
701 NULL, NULL,
702 g_cclosure_marshal_VOID__VOID,
703 G_TYPE_NONE, 0);
704 }
705
706 /**
707 * gmenu_tree_get_canonical_menu_path:
708 * @tree: a #GMenuTree
709 *
710 * This function is only available if the tree has been loaded via
711 * gmenu_tree_load_sync() or a variant thereof.
712 *
713 * Returns: The absolute and canonicalized path to the loaded menu file
714 */
715 const char *
716 gmenu_tree_get_canonical_menu_path (GMenuTree *tree)
717 {
718 g_return_val_if_fail (GMENU_IS_TREE (tree), NULL);
719 g_return_val_if_fail (tree->loaded, NULL);
720
721 return tree->canonical_path;
722 }
723
724 /**
725 * gmenu_tree_load_sync:
726 * @tree: a #GMenuTree
727 * @error: a #GError
728 *
729 * Synchronously load the menu contents. This function
730 * performs a significant amount of blocking I/O if the
731 * tree has not been loaded yet.
732 *
733 * Returns: %TRUE on success, %FALSE on error
734 */
735 gboolean
736 gmenu_tree_load_sync (GMenuTree *tree,
737 GError **error)
738 {
739 GError *local_error = NULL;
740
741 if (tree->loaded)
742 return TRUE;
743
744 if (!gmenu_tree_build_from_layout (tree, &local_error))
745 {
746 if (local_error)
747 g_propagate_error (error, local_error);
748 return FALSE;
749 }
750
751 tree->loaded = TRUE;
752
753 return TRUE;
754 }
755
756 /**
757 * gmenu_tree_get_root_directory:
758 * @tree: a #GMenuTree
759 *
760 * Get the root directory; you must have loaded the tree first (at
761 * least once) via gmenu_tree_load_sync() or a variant thereof.
762 *
763 * Returns: (transfer full): Root of the tree
764 */
765 GMenuTreeDirectory *
766 gmenu_tree_get_root_directory (GMenuTree *tree)
767 {
768 g_return_val_if_fail (tree != NULL, NULL);
769 g_return_val_if_fail (tree->loaded, NULL);
770
771 return gmenu_tree_item_ref (tree->root);
772 }
773
774 static GMenuTreeDirectory *
775 find_path (GMenuTreeDirectory *directory,
776 const char *path)
777 {
778 const char *name;
779 char *slash;
780 char *freeme;
781 GSList *tmp;
782
783 while (path[0] == G_DIR_SEPARATOR) path++;
784
785 if (path[0] == '\0')
786 return directory;
787
788 freeme = NULL;
789 slash = strchr (path, G_DIR_SEPARATOR);
790 if (slash)
791 {
792 name = freeme = g_strndup (path, slash - path);
793 path = slash + 1;
794 }
795 else
796 {
797 name = path;
798 path = NULL;
799 }
800
801 tmp = directory->contents;
802 while (tmp != NULL)
803 {
804 GMenuTreeItem *item = tmp->data;
805
806 if (item->type != GMENU_TREE_ITEM_DIRECTORY)
807 {
808 tmp = tmp->next;
809 continue;
810 }
811
812 if (!strcmp (name, GMENU_TREE_DIRECTORY (item)->name))
813 {
814 g_free (freeme);
815
816 if (path)
817 return find_path (GMENU_TREE_DIRECTORY (item), path);
818 else
819 return GMENU_TREE_DIRECTORY (item);
820 }
821
822 tmp = tmp->next;
823 }
824
825 g_free (freeme);
826
827 return NULL;
828 }
829
830 GMenuTreeDirectory *
831 gmenu_tree_get_directory_from_path (GMenuTree *tree,
832 const char *path)
833 {
834 GMenuTreeDirectory *root;
835 GMenuTreeDirectory *directory;
836
837 g_return_val_if_fail (tree != NULL, NULL);
838 g_return_val_if_fail (path != NULL, NULL);
839
840 if (path[0] != G_DIR_SEPARATOR)
841 return NULL;
842
843 if (!(root = gmenu_tree_get_root_directory (tree)))
844 return NULL;
845
846 directory = find_path (root, path);
847
848 gmenu_tree_item_unref (root);
849
850 return directory ? gmenu_tree_item_ref (directory) : NULL;
851 }
852
853 /**
854 * gmenu_tree_get_entry_by_id:
855 * @tree: a #GMenuTree
856 * @id: a desktop file ID
857 *
858 * Look up the entry corresponding to the given "desktop file id".
859 *
860 * Returns: (transfer full): A newly referenced #GMenuTreeEntry, or %NULL if none
861 */
862 GMenuTreeEntry *
863 gmenu_tree_get_entry_by_id (GMenuTree *tree,
864 const char *id)
865 {
866 GMenuTreeEntry *entry;
867
868 g_return_val_if_fail (tree->loaded, NULL);
869
870 entry = g_hash_table_lookup (tree->entries_by_id, id);
871 if (entry != NULL)
872 gmenu_tree_item_ref (entry);
873
874 return entry;
875 }
876
877 static void
878 gmenu_tree_invoke_monitors (GMenuTree *tree)
879 {
880 g_signal_emit (tree, gmenu_tree_signals[CHANGED], 0);
881 }
882
883 static GMenuTreeDirectory *
884 get_parent (GMenuTreeItem *item)
885 {
886 g_return_val_if_fail (item != NULL, NULL);
887 return item->parent ? gmenu_tree_item_ref (item->parent) : NULL;
888 }
889
890 /**
891 * gmenu_tree_directory_get_parent:
892 * @directory: a #GMenuTreeDirectory
893 *
894 * Returns: (transfer full): The parent directory, or %NULL if none
895 */
896 GMenuTreeDirectory *
897 gmenu_tree_directory_get_parent (GMenuTreeDirectory *directory)
898 {
899 return get_parent ((GMenuTreeItem *)directory);
900 }
901
902 /**
903 * gmenu_tree_entry_get_parent:
904 * @entry: a #GMenuTreeEntry
905 *
906 * Returns: (transfer full): The parent directory, or %NULL if none
907 */
908 GMenuTreeDirectory *
909 gmenu_tree_entry_get_parent (GMenuTreeEntry *entry)
910 {
911 return get_parent ((GMenuTreeItem *)entry);
912 }
913
914 /**
915 * gmenu_tree_alias_get_parent:
916 * @alias: a #GMenuTreeAlias
917 *
918 * Returns: (transfer full): The parent directory, or %NULL if none
919 */
920 GMenuTreeDirectory *
921 gmenu_tree_alias_get_parent (GMenuTreeAlias *alias)
922 {
923 return get_parent ((GMenuTreeItem *)alias);
924 }
925
926 /**
927 * gmenu_tree_header_get_parent:
928 * @header: a #GMenuTreeHeader
929 *
930 * Returns: (transfer full): The parent directory, or %NULL if none
931 */
932 GMenuTreeDirectory *
933 gmenu_tree_header_get_parent (GMenuTreeHeader *header)
934 {
935 return get_parent ((GMenuTreeItem *)header);
936 }
937
938 /**
939 * gmenu_tree_separator_get_parent:
940 * @separator: a #GMenuTreeSeparator
941 *
942 * Returns: (transfer full): The parent directory, or %NULL if none
943 */
944 GMenuTreeDirectory *
945 gmenu_tree_separator_get_parent (GMenuTreeSeparator *separator)
946 {
947 return get_parent ((GMenuTreeItem *)separator);
948 }
949
950 static void
951 gmenu_tree_item_set_parent (GMenuTreeItem *item,
952 GMenuTreeDirectory *parent)
953 {
954 g_return_if_fail (item != NULL);
955
956 item->parent = parent;
957 }
958
959 /**
960 * gmenu_tree_iter_ref: (skip)
961 * @iter: iter
962 *
963 * Increment the reference count of @iter
964 */
965 GMenuTreeIter *
966 gmenu_tree_iter_ref (GMenuTreeIter *iter)
967 {
968 g_atomic_int_inc (&iter->refcount);
969 return iter;
970 }
971
972 /**
973 * gmenu_tree_iter_unref: (skip)
974 * @iter: iter
975 *
976 * Decrement the reference count of @iter
977 */
978 void
979 gmenu_tree_iter_unref (GMenuTreeIter *iter)
980 {
981 if (!g_atomic_int_dec_and_test (&iter->refcount))
982 return;
983
984 g_slist_foreach (iter->contents, (GFunc)gmenu_tree_item_unref, NULL);
985 g_slist_free (iter->contents);
986
987 g_slice_free (GMenuTreeIter, iter);
988 }
989
990 /**
991 * gmenu_tree_directory_iter:
992 * @directory: directory
993 *
994 * Returns: (transfer full): A new iterator over the directory contents
995 */
996 GMenuTreeIter *
997 gmenu_tree_directory_iter (GMenuTreeDirectory *directory)
998 {
999 GMenuTreeIter *iter;
1000
1001 g_return_val_if_fail (directory != NULL, NULL);
1002
1003 iter = g_slice_new0 (GMenuTreeIter);
1004 iter->refcount = 1;
1005
1006 iter->contents = g_slist_copy (directory->contents);
1007 iter->contents_iter = iter->contents;
1008 g_slist_foreach (iter->contents, (GFunc) gmenu_tree_item_ref, NULL);
1009
1010 return iter;
1011 }
1012
1013 /**
1014 * gmenu_tree_iter_next:
1015 * @iter: iter
1016 *
1017 * Change the iterator to the next item, and return its type. If
1018 * there are no more items, %GMENU_TREE_ITEM_INVALID is returned.
1019 *
1020 * Returns: The type of the next item that can be retrived from the iterator
1021 */
1022 GMenuTreeItemType
1023 gmenu_tree_iter_next (GMenuTreeIter *iter)
1024 {
1025 g_return_val_if_fail (iter != NULL, GMENU_TREE_ITEM_INVALID);
1026
1027 if (iter->contents_iter)
1028 {
1029 iter->item = iter->contents_iter->data;
1030 iter->contents_iter = iter->contents_iter->next;
1031 return iter->item->type;
1032 }
1033 else
1034 return GMENU_TREE_ITEM_INVALID;
1035 }
1036
1037 /**
1038 * gmenu_tree_iter_get_directory:
1039 * @iter: iter
1040 *
1041 * This method may only be called if gmenu_tree_iter_next()
1042 * returned GMENU_TREE_ITEM_DIRECTORY.
1043 *
1044 * Returns: (transfer full): A directory
1045 */
1046 GMenuTreeDirectory *
1047 gmenu_tree_iter_get_directory (GMenuTreeIter *iter)
1048 {
1049 g_return_val_if_fail (iter != NULL, NULL);
1050 g_return_val_if_fail (iter->item != NULL, NULL);
1051 g_return_val_if_fail (iter->item->type == GMENU_TREE_ITEM_DIRECTORY, NULL);
1052
1053 return (GMenuTreeDirectory*)gmenu_tree_item_ref (iter->item);
1054 }
1055
1056 /**
1057 * gmenu_tree_iter_get_entry:
1058 * @iter: iter
1059 *
1060 * This method may only be called if gmenu_tree_iter_next()
1061 * returned GMENU_TREE_ITEM_ENTRY.
1062 *
1063 * Returns: (transfer full): An entry
1064 */
1065 GMenuTreeEntry *
1066 gmenu_tree_iter_get_entry (GMenuTreeIter *iter)
1067 {
1068 g_return_val_if_fail (iter != NULL, NULL);
1069 g_return_val_if_fail (iter->item != NULL, NULL);
1070 g_return_val_if_fail (iter->item->type == GMENU_TREE_ITEM_ENTRY, NULL);
1071
1072 return (GMenuTreeEntry*)gmenu_tree_item_ref (iter->item);
1073 }
1074
1075 /**
1076 * gmenu_tree_iter_get_header:
1077 * @iter: iter
1078 *
1079 * This method may only be called if gmenu_tree_iter_next()
1080 * returned GMENU_TREE_ITEM_HEADER.
1081 *
1082 * Returns: (transfer full): A header
1083 */
1084 GMenuTreeHeader *
1085 gmenu_tree_iter_get_header (GMenuTreeIter *iter)
1086 {
1087 g_return_val_if_fail (iter != NULL, NULL);
1088 g_return_val_if_fail (iter->item != NULL, NULL);
1089 g_return_val_if_fail (iter->item->type == GMENU_TREE_ITEM_HEADER, NULL);
1090
1091 return (GMenuTreeHeader*)gmenu_tree_item_ref (iter->item);
1092 }
1093
1094 /**
1095 * gmenu_tree_iter_get_alias:
1096 * @iter: iter
1097 *
1098 * This method may only be called if gmenu_tree_iter_next()
1099 * returned GMENU_TREE_ITEM_ALIAS.
1100 *
1101 * Returns: (transfer full): An alias
1102 */
1103 GMenuTreeAlias *
1104 gmenu_tree_iter_get_alias (GMenuTreeIter *iter)
1105 {
1106 g_return_val_if_fail (iter != NULL, NULL);
1107 g_return_val_if_fail (iter->item != NULL, NULL);
1108 g_return_val_if_fail (iter->item->type == GMENU_TREE_ITEM_ALIAS, NULL);
1109
1110 return (GMenuTreeAlias*)gmenu_tree_item_ref (iter->item);
1111 }
1112
1113 /**
1114 * gmenu_tree_iter_get_separator:
1115 * @iter: iter
1116 *
1117 * This method may only be called if gmenu_tree_iter_next()
1118 * returned #GMENU_TREE_ITEM_SEPARATOR.
1119 *
1120 * Returns: (transfer full): A separator
1121 */
1122 GMenuTreeSeparator *
1123 gmenu_tree_iter_get_separator (GMenuTreeIter *iter)
1124 {
1125 g_return_val_if_fail (iter != NULL, NULL);
1126 g_return_val_if_fail (iter->item != NULL, NULL);
1127 g_return_val_if_fail (iter->item->type == GMENU_TREE_ITEM_SEPARATOR, NULL);
1128
1129 return (GMenuTreeSeparator*)gmenu_tree_item_ref (iter->item);
1130 }
1131
1132 const char *
1133 gmenu_tree_directory_get_name (GMenuTreeDirectory *directory)
1134 {
1135 g_return_val_if_fail (directory != NULL, NULL);
1136
1137 if (!directory->directory_entry)
1138 return directory->name;
1139
1140 return desktop_entry_get_name (directory->directory_entry);
1141 }
1142
1143 const char *
1144 gmenu_tree_directory_get_generic_name (GMenuTreeDirectory *directory)
1145 {
1146 g_return_val_if_fail (directory != NULL, NULL);
1147
1148 if (!directory->directory_entry)
1149 return NULL;
1150
1151 return desktop_entry_get_generic_name (directory->directory_entry);
1152 }
1153
1154 const char *
1155 gmenu_tree_directory_get_comment (GMenuTreeDirectory *directory)
1156 {
1157 g_return_val_if_fail (directory != NULL, NULL);
1158
1159 if (!directory->directory_entry)
1160 return NULL;
1161
1162 return desktop_entry_get_comment (directory->directory_entry);
1163 }
1164
1165 /**
1166 * gmenu_tree_directory_get_icon:
1167 * @directory: a #GMenuTreeDirectory
1168 *
1169 * Gets the icon for the directory.
1170 *
1171 * Returns: (transfer none): The #GIcon for this directory
1172 */
1173 GIcon *
1174 gmenu_tree_directory_get_icon (GMenuTreeDirectory *directory)
1175 {
1176 g_return_val_if_fail (directory != NULL, NULL);
1177
1178 if (!directory->directory_entry)
1179 return NULL;
1180
1181 return desktop_entry_get_icon (directory->directory_entry);
1182 }
1183
1184 const char *
1185 gmenu_tree_directory_get_desktop_file_path (GMenuTreeDirectory *directory)
1186 {
1187 g_return_val_if_fail (directory != NULL, NULL);
1188
1189 if (!directory->directory_entry)
1190 return NULL;
1191
1192 return desktop_entry_get_path (directory->directory_entry);
1193 }
1194
1195 const char *
1196 gmenu_tree_directory_get_menu_id (GMenuTreeDirectory *directory)
1197 {
1198 g_return_val_if_fail (directory != NULL, NULL);
1199
1200 return directory->name;
1201 }
1202
1203 gboolean
1204 gmenu_tree_directory_get_is_nodisplay (GMenuTreeDirectory *directory)
1205 {
1206 g_return_val_if_fail (directory != NULL, FALSE);
1207
1208 return directory->is_nodisplay;
1209 }
1210
1211 /**
1212 * gmenu_tree_directory_get_tree:
1213 * @directory: A #GMenuTreeDirectory
1214 *
1215 * Grab the tree associated with a #GMenuTreeItem.
1216 *
1217 * Returns: (transfer full): The #GMenuTree
1218 */
1219 GMenuTree *
1220 gmenu_tree_directory_get_tree (GMenuTreeDirectory *directory)
1221 {
1222 g_return_val_if_fail (directory != NULL, NULL);
1223
1224 return g_object_ref (directory->item.tree);
1225 }
1226
1227 static void
1228 append_directory_path (GMenuTreeDirectory *directory,
1229 GString *path)
1230 {
1231
1232 if (!directory->item.parent)
1233 {
1234 g_string_append_c (path, G_DIR_SEPARATOR);
1235 return;
1236 }
1237
1238 append_directory_path (directory->item.parent, path);
1239
1240 g_string_append (path, directory->name);
1241 g_string_append_c (path, G_DIR_SEPARATOR);
1242 }
1243
1244 char *
1245 gmenu_tree_directory_make_path (GMenuTreeDirectory *directory,
1246 GMenuTreeEntry *entry)
1247 {
1248 GString *path;
1249
1250 g_return_val_if_fail (directory != NULL, NULL);
1251
1252 path = g_string_new (NULL);
1253
1254 append_directory_path (directory, path);
1255
1256 if (entry != NULL)
1257 {
1258 const char *basename;
1259
1260 basename = desktop_entry_get_basename (entry->desktop_entry);
1261 g_string_append (path, basename);
1262 }
1263
1264 return g_string_free (path, FALSE);
1265 }
1266
1267 /**
1268 * gmenu_tree_entry_get_app_info:
1269 * @entry: a #GMenuTreeEntry
1270 *
1271 * Returns: (transfer none): The #GDesktopAppInfo for this entry
1272 */
1273 GDesktopAppInfo *
1274 gmenu_tree_entry_get_app_info (GMenuTreeEntry *entry)
1275 {
1276 g_return_val_if_fail (entry != NULL, NULL);
1277
1278 return desktop_entry_get_app_info (entry->desktop_entry);
1279 }
1280
1281 const char *
1282 gmenu_tree_entry_get_desktop_file_path (GMenuTreeEntry *entry)
1283 {
1284 g_return_val_if_fail (entry != NULL, NULL);
1285
1286 return desktop_entry_get_path (entry->desktop_entry);
1287 }
1288
1289 const char *
1290 gmenu_tree_entry_get_desktop_file_id (GMenuTreeEntry *entry)
1291 {
1292 g_return_val_if_fail (entry != NULL, FALSE);
1293
1294 return entry->desktop_file_id;
1295 }
1296
1297 gboolean
1298 gmenu_tree_entry_get_is_nodisplay_recurse (GMenuTreeEntry *entry)
1299 {
1300 GMenuTreeDirectory *directory;
1301 GDesktopAppInfo *app_info;
1302
1303 g_return_val_if_fail (entry != NULL, FALSE);
1304
1305 app_info = gmenu_tree_entry_get_app_info (entry);
1306
1307 if (g_desktop_app_info_get_nodisplay (app_info))
1308 return TRUE;
1309
1310 directory = entry->item.parent;
1311 while (directory != NULL)
1312 {
1313 if (directory->is_nodisplay)
1314 return TRUE;
1315
1316 directory = directory->item.parent;
1317 }
1318
1319 return FALSE;
1320 }
1321
1322 gboolean
1323 gmenu_tree_entry_get_is_excluded (GMenuTreeEntry *entry)
1324 {
1325 g_return_val_if_fail (entry != NULL, FALSE);
1326
1327 return entry->is_excluded;
1328 }
1329
1330 gboolean
1331 gmenu_tree_entry_get_is_unallocated (GMenuTreeEntry *entry)
1332 {
1333 g_return_val_if_fail (entry != NULL, FALSE);
1334
1335 return entry->is_unallocated;
1336 }
1337
1338 /**
1339 * gmenu_tree_entry_get_tree:
1340 * @entry: A #GMenuTreeEntry
1341 *
1342 * Grab the tree associated with a #GMenuTreeEntry.
1343 *
1344 * Returns: (transfer full): The #GMenuTree
1345 */
1346 GMenuTree *
1347 gmenu_tree_entry_get_tree (GMenuTreeEntry *entry)
1348 {
1349 g_return_val_if_fail (entry != NULL, NULL);
1350
1351 return g_object_ref (entry->item.tree);
1352 }
1353
1354 GMenuTreeDirectory *
1355 gmenu_tree_header_get_directory (GMenuTreeHeader *header)
1356 {
1357 g_return_val_if_fail (header != NULL, NULL);
1358
1359 return gmenu_tree_item_ref (header->directory);
1360 }
1361
1362 /**
1363 * gmenu_tree_header_get_tree:
1364 * @header: A #GMenuTreeHeader
1365 *
1366 * Grab the tree associated with a #GMenuTreeHeader.
1367 *
1368 * Returns: (transfer full): The #GMenuTree
1369 */
1370 GMenuTree *
1371 gmenu_tree_header_get_tree (GMenuTreeHeader *header)
1372 {
1373 g_return_val_if_fail (header != NULL, NULL);
1374
1375 return g_object_ref (header->item.tree);
1376 }
1377
1378 GMenuTreeItemType
1379 gmenu_tree_alias_get_aliased_item_type (GMenuTreeAlias *alias)
1380 {
1381 g_return_val_if_fail (alias != NULL, GMENU_TREE_ITEM_INVALID);
1382
1383 g_assert (alias->aliased_item != NULL);
1384 return alias->aliased_item->type;
1385 }
1386
1387 GMenuTreeDirectory *
1388 gmenu_tree_alias_get_directory (GMenuTreeAlias *alias)
1389 {
1390 g_return_val_if_fail (alias != NULL, NULL);
1391
1392 return gmenu_tree_item_ref (alias->directory);
1393 }
1394
1395 /**
1396 * gmenu_tree_alias_get_tree:
1397 * @alias: A #GMenuTreeAlias
1398 *
1399 * Grab the tree associated with a #GMenuTreeAlias.
1400 *
1401 * Returns: (transfer full): The #GMenuTree
1402 */
1403 GMenuTree *
1404 gmenu_tree_alias_get_tree (GMenuTreeAlias *alias)
1405 {
1406 g_return_val_if_fail (alias != NULL, NULL);
1407
1408 return g_object_ref (alias->item.tree);
1409 }
1410
1411 /**
1412 * gmenu_tree_separator_get_tree:
1413 * @separator: A #GMenuTreeSeparator
1414 *
1415 * Grab the tree associated with a #GMenuTreeSeparator.
1416 *
1417 * Returns: (transfer full): The #GMenuTree
1418 */
1419 GMenuTree *
1420 gmenu_tree_separator_get_tree (GMenuTreeSeparator *separator)
1421 {
1422 g_return_val_if_fail (separator != NULL, NULL);
1423
1424 return g_object_ref (separator->item.tree);
1425 }
1426
1427 /**
1428 * gmenu_tree_alias_get_aliased_directory:
1429 * @alias: alias
1430 *
1431 * Returns: (transfer full): The aliased directory entry
1432 */
1433 GMenuTreeDirectory *
1434 gmenu_tree_alias_get_aliased_directory (GMenuTreeAlias *alias)
1435 {
1436 g_return_val_if_fail (alias != NULL, NULL);
1437 g_return_val_if_fail (alias->aliased_item->type == GMENU_TREE_ITEM_DIRECTORY, NULL);
1438
1439 return (GMenuTreeDirectory *) gmenu_tree_item_ref (alias->aliased_item);
1440 }
1441
1442 /**
1443 * gmenu_tree_alias_get_aliased_entry:
1444 * @alias: alias
1445 *
1446 * Returns: (transfer full): The aliased entry
1447 */
1448 GMenuTreeEntry *
1449 gmenu_tree_alias_get_aliased_entry (GMenuTreeAlias *alias)
1450 {
1451 g_return_val_if_fail (alias != NULL, NULL);
1452 g_return_val_if_fail (alias->aliased_item->type == GMENU_TREE_ITEM_ENTRY, NULL);
1453
1454 return (GMenuTreeEntry *) gmenu_tree_item_ref (alias->aliased_item);
1455 }
1456
1457 static GMenuTreeDirectory *
1458 gmenu_tree_directory_new (GMenuTree *tree,
1459 GMenuTreeDirectory *parent,
1460 const char *name)
1461 {
1462 GMenuTreeDirectory *retval;
1463
1464 retval = g_slice_new0 (GMenuTreeDirectory);
1465
1466 retval->item.type = GMENU_TREE_ITEM_DIRECTORY;
1467 retval->item.parent = parent;
1468 retval->item.refcount = 1;
1469 retval->item.tree = tree;
1470
1471 retval->name = g_strdup (name);
1472 retval->directory_entry = NULL;
1473 retval->entries = NULL;
1474 retval->subdirs = NULL;
1475 retval->default_layout_info = NULL;
1476 retval->layout_info = NULL;
1477 retval->contents = NULL;
1478 retval->only_unallocated = FALSE;
1479 retval->is_nodisplay = FALSE;
1480 retval->layout_pending_separator = FALSE;
1481 retval->preprocessed = FALSE;
1482 retval->will_inline_header = G_MAXUINT16;
1483
1484 retval->default_layout_values.mask = MENU_LAYOUT_VALUES_NONE;
1485 retval->default_layout_values.show_empty = FALSE;
1486 retval->default_layout_values.inline_menus = FALSE;
1487 retval->default_layout_values.inline_limit = 4;
1488 retval->default_layout_values.inline_header = FALSE;
1489 retval->default_layout_values.inline_alias = FALSE;
1490
1491 return retval;
1492 }
1493
1494 static void
1495 gmenu_tree_directory_finalize (GMenuTreeDirectory *directory)
1496 {
1497 g_assert (directory->item.refcount == 0);
1498
1499 g_slist_foreach (directory->contents,
1500 (GFunc) gmenu_tree_item_unref_and_unset_parent,
1501 NULL);
1502 g_slist_free (directory->contents);
1503 directory->contents = NULL;
1504
1505 g_slist_foreach (directory->default_layout_info,
1506 (GFunc) menu_layout_node_unref,
1507 NULL);
1508 g_slist_free (directory->default_layout_info);
1509 directory->default_layout_info = NULL;
1510
1511 g_slist_foreach (directory->layout_info,
1512 (GFunc) menu_layout_node_unref,
1513 NULL);
1514 g_slist_free (directory->layout_info);
1515 directory->layout_info = NULL;
1516
1517 g_slist_foreach (directory->subdirs,
1518 (GFunc) gmenu_tree_item_unref_and_unset_parent,
1519 NULL);
1520 g_slist_free (directory->subdirs);
1521 directory->subdirs = NULL;
1522
1523 g_slist_foreach (directory->entries,
1524 (GFunc) gmenu_tree_item_unref_and_unset_parent,
1525 NULL);
1526 g_slist_free (directory->entries);
1527 directory->entries = NULL;
1528
1529 if (directory->directory_entry)
1530 desktop_entry_unref (directory->directory_entry);
1531 directory->directory_entry = NULL;
1532
1533 g_free (directory->name);
1534 directory->name = NULL;
1535
1536 g_slice_free (GMenuTreeDirectory, directory);
1537 }
1538
1539 static GMenuTreeSeparator *
1540 gmenu_tree_separator_new (GMenuTreeDirectory *parent)
1541 {
1542 GMenuTreeSeparator *retval;
1543
1544 retval = g_slice_new0 (GMenuTreeSeparator);
1545
1546 retval->item.type = GMENU_TREE_ITEM_SEPARATOR;
1547 retval->item.parent = parent;
1548 retval->item.refcount = 1;
1549 retval->item.tree = parent->item.tree;
1550
1551 return retval;
1552 }
1553
1554 static void
1555 gmenu_tree_separator_finalize (GMenuTreeSeparator *separator)
1556 {
1557 g_assert (separator->item.refcount == 0);
1558
1559 g_slice_free (GMenuTreeSeparator, separator);
1560 }
1561
1562 static GMenuTreeHeader *
1563 gmenu_tree_header_new (GMenuTreeDirectory *parent,
1564 GMenuTreeDirectory *directory)
1565 {
1566 GMenuTreeHeader *retval;
1567
1568 retval = g_slice_new0 (GMenuTreeHeader);
1569
1570 retval->item.type = GMENU_TREE_ITEM_HEADER;
1571 retval->item.parent = parent;
1572 retval->item.refcount = 1;
1573 retval->item.tree = parent->item.tree;
1574
1575 retval->directory = gmenu_tree_item_ref (directory);
1576
1577 gmenu_tree_item_set_parent (GMENU_TREE_ITEM (retval->directory), NULL);
1578
1579 return retval;
1580 }
1581
1582 static void
1583 gmenu_tree_header_finalize (GMenuTreeHeader *header)
1584 {
1585 g_assert (header->item.refcount == 0);
1586
1587 if (header->directory != NULL)
1588 gmenu_tree_item_unref (header->directory);
1589 header->directory = NULL;
1590
1591 g_slice_free (GMenuTreeHeader, header);
1592 }
1593
1594 static GMenuTreeAlias *
1595 gmenu_tree_alias_new (GMenuTreeDirectory *parent,
1596 GMenuTreeDirectory *directory,
1597 GMenuTreeItem *item)
1598 {
1599 GMenuTreeAlias *retval;
1600
1601 retval = g_slice_new0 (GMenuTreeAlias);
1602
1603 retval->item.type = GMENU_TREE_ITEM_ALIAS;
1604 retval->item.parent = parent;
1605 retval->item.refcount = 1;
1606 retval->item.tree = parent->item.tree;
1607
1608 retval->directory = gmenu_tree_item_ref (directory);
1609 if (item->type != GMENU_TREE_ITEM_ALIAS)
1610 retval->aliased_item = gmenu_tree_item_ref (item);
1611 else
1612 {
1613 GMenuTreeAlias *alias = GMENU_TREE_ALIAS (item);
1614 retval->aliased_item = gmenu_tree_item_ref (alias->aliased_item);
1615 }
1616
1617 gmenu_tree_item_set_parent (GMENU_TREE_ITEM (retval->directory), NULL);
1618 gmenu_tree_item_set_parent (retval->aliased_item, NULL);
1619
1620 return retval;
1621 }
1622
1623 static void
1624 gmenu_tree_alias_finalize (GMenuTreeAlias *alias)
1625 {
1626 g_assert (alias->item.refcount == 0);
1627
1628 if (alias->directory != NULL)
1629 gmenu_tree_item_unref (alias->directory);
1630 alias->directory = NULL;
1631
1632 if (alias->aliased_item != NULL)
1633 gmenu_tree_item_unref (alias->aliased_item);
1634 alias->aliased_item = NULL;
1635
1636 g_slice_free (GMenuTreeAlias, alias);
1637 }
1638
1639 static GMenuTreeEntry *
1640 gmenu_tree_entry_new (GMenuTreeDirectory *parent,
1641 DesktopEntry *desktop_entry,
1642 const char *desktop_file_id,
1643 gboolean is_excluded,
1644 gboolean is_unallocated)
1645 {
1646 GMenuTreeEntry *retval;
1647
1648 retval = g_slice_new0 (GMenuTreeEntry);
1649
1650 retval->item.type = GMENU_TREE_ITEM_ENTRY;
1651 retval->item.parent = parent;
1652 retval->item.refcount = 1;
1653 retval->item.tree = parent->item.tree;
1654
1655 retval->desktop_entry = desktop_entry_ref (desktop_entry);
1656 retval->desktop_file_id = g_strdup (desktop_file_id);
1657 retval->is_excluded = is_excluded != FALSE;
1658 retval->is_unallocated = is_unallocated != FALSE;
1659
1660 return retval;
1661 }
1662
1663 static void
1664 gmenu_tree_entry_finalize (GMenuTreeEntry *entry)
1665 {
1666 g_assert (entry->item.refcount == 0);
1667
1668 g_free (entry->desktop_file_id);
1669 entry->desktop_file_id = NULL;
1670
1671 if (entry->desktop_entry)
1672 desktop_entry_unref (entry->desktop_entry);
1673 entry->desktop_entry = NULL;
1674
1675 g_slice_free (GMenuTreeEntry, entry);
1676 }
1677
1678 static int
1679 gmenu_tree_entry_compare_by_id (GMenuTreeItem *a,
1680 GMenuTreeItem *b)
1681 {
1682 if (a->type == GMENU_TREE_ITEM_ALIAS)
1683 a = GMENU_TREE_ALIAS (a)->aliased_item;
1684
1685 if (b->type == GMENU_TREE_ITEM_ALIAS)
1686 b = GMENU_TREE_ALIAS (b)->aliased_item;
1687
1688 return strcmp (GMENU_TREE_ENTRY (a)->desktop_file_id,
1689 GMENU_TREE_ENTRY (b)->desktop_file_id);
1690 }
1691
1692 /**
1693 * gmenu_tree_item_ref:
1694 * @item: a #GMenuTreeItem
1695 *
1696 * Returns: (transfer full): The same @item, or %NULL if @item is not a valid #GMenuTreeItem
1697 */
1698 gpointer
1699 gmenu_tree_item_ref (gpointer itemp)
1700 {
1701 GMenuTreeItem *item;
1702
1703 item = (GMenuTreeItem *) itemp;
1704
1705 g_return_val_if_fail (item != NULL, NULL);
1706 g_return_val_if_fail (item->refcount > 0, NULL);
1707
1708 g_atomic_int_inc (&item->refcount);
1709
1710 return item;
1711 }
1712
1713 void
1714 gmenu_tree_item_unref (gpointer itemp)
1715 {
1716 GMenuTreeItem *item;
1717
1718 item = (GMenuTreeItem *) itemp;
1719
1720 g_return_if_fail (item != NULL);
1721 g_return_if_fail (item->refcount > 0);
1722
1723 if (g_atomic_int_dec_and_test (&(item->refcount)))
1724 {
1725 switch (item->type)
1726 {
1727 case GMENU_TREE_ITEM_DIRECTORY:
1728 gmenu_tree_directory_finalize (GMENU_TREE_DIRECTORY (item));
1729 break;
1730
1731 case GMENU_TREE_ITEM_ENTRY:
1732 gmenu_tree_entry_finalize (GMENU_TREE_ENTRY (item));
1733 break;
1734
1735 case GMENU_TREE_ITEM_SEPARATOR:
1736 gmenu_tree_separator_finalize (GMENU_TREE_SEPARATOR (item));
1737 break;
1738
1739 case GMENU_TREE_ITEM_HEADER:
1740 gmenu_tree_header_finalize (GMENU_TREE_HEADER (item));
1741 break;
1742
1743 case GMENU_TREE_ITEM_ALIAS:
1744 gmenu_tree_alias_finalize (GMENU_TREE_ALIAS (item));
1745 break;
1746
1747 default:
1748 g_assert_not_reached ();
1749 break;
1750 }
1751 }
1752 }
1753
1754 static void
1755 gmenu_tree_item_unref_and_unset_parent (gpointer itemp)
1756 {
1757 GMenuTreeItem *item;
1758
1759 item = (GMenuTreeItem *) itemp;
1760
1761 g_return_if_fail (item != NULL);
1762
1763 gmenu_tree_item_set_parent (item, NULL);
1764 gmenu_tree_item_unref (item);
1765 }
1766
1767 static inline const char *
1768 gmenu_tree_item_compare_get_name_helper (GMenuTreeItem *item,
1769 GMenuTreeFlags flags)
1770 {
1771 const char *name;
1772
1773 name = NULL;
1774
1775 switch (item->type)
1776 {
1777 case GMENU_TREE_ITEM_DIRECTORY:
1778 if (GMENU_TREE_DIRECTORY (item)->directory_entry)
1779 name = desktop_entry_get_name (GMENU_TREE_DIRECTORY (item)->directory_entry);
1780 else
1781 name = GMENU_TREE_DIRECTORY (item)->name;
1782 break;
1783
1784 case GMENU_TREE_ITEM_ENTRY:
1785 if (flags & GMENU_TREE_FLAGS_SORT_DISPLAY_NAME)
1786 name = g_app_info_get_display_name (G_APP_INFO (gmenu_tree_entry_get_app_info (GMENU_TREE_ENTRY (item))));
1787 else
1788 name = desktop_entry_get_name (GMENU_TREE_ENTRY (item)->desktop_entry);
1789 break;
1790
1791 case GMENU_TREE_ITEM_ALIAS:
1792 {
1793 GMenuTreeItem *dir;
1794 dir = GMENU_TREE_ITEM (GMENU_TREE_ALIAS (item)->directory);
1795 name = gmenu_tree_item_compare_get_name_helper (dir, flags);
1796 }
1797 break;
1798
1799 case GMENU_TREE_ITEM_SEPARATOR:
1800 case GMENU_TREE_ITEM_HEADER:
1801 default:
1802 g_assert_not_reached ();
1803 break;
1804 }
1805
1806 return name;
1807 }
1808
1809 static int
1810 gmenu_tree_item_compare (GMenuTreeItem *a,
1811 GMenuTreeItem *b,
1812 gpointer flags_p)
1813 {
1814 const char *name_a;
1815 const char *name_b;
1816 GMenuTreeFlags flags;
1817
1818 flags = GPOINTER_TO_INT (flags_p);
1819
1820 name_a = gmenu_tree_item_compare_get_name_helper (a, flags);
1821 name_b = gmenu_tree_item_compare_get_name_helper (b, flags);
1822
1823 return g_utf8_collate (name_a, name_b);
1824 }
1825
1826 static MenuLayoutNode *
1827 find_menu_child (MenuLayoutNode *layout)
1828 {
1829 MenuLayoutNode *child;
1830
1831 child = menu_layout_node_get_children (layout);
1832 while (child && menu_layout_node_get_type (child) != MENU_LAYOUT_NODE_MENU)
1833 child = menu_layout_node_get_next (child);
1834
1835 return child;
1836 }
1837
1838 static void
1839 merge_resolved_children (GMenuTree *tree,
1840 GHashTable *loaded_menu_files,
1841 MenuLayoutNode *where,
1842 MenuLayoutNode *from)
1843 {
1844 MenuLayoutNode *insert_after;
1845 MenuLayoutNode *menu_child;
1846 MenuLayoutNode *from_child;
1847
1848 gmenu_tree_resolve_files (tree, loaded_menu_files, from);
1849
1850 insert_after = where;
1851 g_assert (menu_layout_node_get_type (insert_after) != MENU_LAYOUT_NODE_ROOT);
1852 g_assert (menu_layout_node_get_parent (insert_after) != NULL);
1853
1854 /* skip root node */
1855 menu_child = find_menu_child (from);
1856 g_assert (menu_child != NULL);
1857 g_assert (menu_layout_node_get_type (menu_child) == MENU_LAYOUT_NODE_MENU);
1858
1859 /* merge children of toplevel <Menu> */
1860 from_child = menu_layout_node_get_children (menu_child);
1861 while (from_child != NULL)
1862 {
1863 MenuLayoutNode *next;
1864
1865 next = menu_layout_node_get_next (from_child);
1866
1867 menu_verbose ("Merging ");
1868 menu_debug_print_layout (from_child, FALSE);
1869 menu_verbose (" after ");
1870 menu_debug_print_layout (insert_after, FALSE);
1871
1872 switch (menu_layout_node_get_type (from_child))
1873 {
1874 case MENU_LAYOUT_NODE_NAME:
1875 menu_layout_node_unlink (from_child); /* delete this */
1876 break;
1877
1878 default:
1879 menu_layout_node_steal (from_child);
1880 menu_layout_node_insert_after (insert_after, from_child);
1881 menu_layout_node_unref (from_child);
1882
1883 insert_after = from_child;
1884 break;
1885 }
1886
1887 from_child = next;
1888 }
1889 }
1890
1891 static gboolean
1892 load_merge_file (GMenuTree *tree,
1893 GHashTable *loaded_menu_files,
1894 const char *filename,
1895 gboolean is_canonical,
1896 gboolean add_monitor,
1897 MenuLayoutNode *where)
1898 {
1899 MenuLayoutNode *to_merge;
1900 const char *canonical;
1901 char *freeme;
1902 gboolean retval;
1903
1904 freeme = NULL;
1905 retval = FALSE;
1906
1907 if (!is_canonical)
1908 {
1909 canonical = freeme = menu_canonicalize_file_name (filename, FALSE);
1910 if (canonical == NULL)
1911 {
1912 if (add_monitor)
1913 gmenu_tree_add_menu_file_monitor (tree,
1914 filename,
1915 MENU_FILE_MONITOR_NONEXISTENT_FILE);
1916
1917 menu_verbose ("Failed to canonicalize merge file path \"%s\": %s\n",
1918 filename, g_strerror (errno));
1919 goto out;
1920 }
1921 }
1922 else
1923 {
1924 canonical = filename;
1925 }
1926
1927 if (g_hash_table_lookup (loaded_menu_files, canonical) != NULL)
1928 {
1929 g_warning ("Not loading \"%s\": recursive loop detected in .menu files",
1930 canonical);
1931 retval = TRUE;
1932 goto out;
1933 }
1934
1935 menu_verbose ("Merging file \"%s\"\n", canonical);
1936
1937 to_merge = menu_layout_load (canonical, tree->non_prefixed_basename, NULL);
1938 if (to_merge == NULL)
1939 {
1940 menu_verbose ("No menu for file \"%s\" found when merging\n",
1941 canonical);
1942 goto out;
1943 }
1944
1945 retval = TRUE;
1946
1947 g_hash_table_insert (loaded_menu_files, (char *) canonical, GUINT_TO_POINTER (TRUE));
1948
1949 if (add_monitor)
1950 gmenu_tree_add_menu_file_monitor (tree,
1951 canonical,
1952 MENU_FILE_MONITOR_FILE);
1953
1954 merge_resolved_children (tree, loaded_menu_files, where, to_merge);
1955
1956 g_hash_table_remove (loaded_menu_files, canonical);
1957
1958 menu_layout_node_unref (to_merge);
1959
1960 out:
1961 if (freeme)
1962 g_free (freeme);
1963
1964 return retval;
1965 }
1966
1967 static gboolean
1968 load_merge_file_with_config_dir (GMenuTree *tree,
1969 GHashTable *loaded_menu_files,
1970 const char *menu_file,
1971 const char *config_dir,
1972 MenuLayoutNode *where)
1973 {
1974 char *merge_file;
1975 gboolean loaded;
1976
1977 loaded = FALSE;
1978
1979 merge_file = g_build_filename (config_dir, "menus", menu_file, NULL);
1980
1981 if (load_merge_file (tree, loaded_menu_files, merge_file, FALSE, TRUE, where))
1982 loaded = TRUE;
1983
1984 g_free (merge_file);
1985
1986 return loaded;
1987 }
1988
1989 static gboolean
1990 compare_basedir_to_config_dir (const char *canonical_basedir,
1991 const char *config_dir)
1992 {
1993 char *dirname;
1994 char *canonical_menus_dir;
1995 gboolean retval;
1996
1997 menu_verbose ("Checking to see if basedir '%s' is in '%s'\n",
1998 canonical_basedir, config_dir);
1999
2000 dirname = g_build_filename (config_dir, "menus", NULL);
2001
2002 retval = FALSE;
2003
2004 canonical_menus_dir = menu_canonicalize_file_name (dirname, FALSE);
2005 if (canonical_menus_dir != NULL &&
2006 strcmp (canonical_basedir, canonical_menus_dir) == 0)
2007 {
2008 retval = TRUE;
2009 }
2010
2011 g_free (canonical_menus_dir);
2012 g_free (dirname);
2013
2014 return retval;
2015 }
2016
2017 static gboolean
2018 load_parent_merge_file_from_basename (GMenuTree *tree,
2019 GHashTable *loaded_menu_files,
2020 MenuLayoutNode *layout,
2021 const char *menu_file,
2022 const char *canonical_basedir)
2023 {
2024 gboolean found_basedir;
2025 const char * const *system_config_dirs;
2026 int i;
2027
2028 /* We're not interested in menu files that are in directories which are not a
2029 * parent of the base directory of this menu file */
2030 found_basedir = compare_basedir_to_config_dir (canonical_basedir,
2031 g_get_user_config_dir ());
2032
2033 system_config_dirs = g_get_system_config_dirs ();
2034
2035 i = 0;
2036 while (system_config_dirs[i] != NULL)
2037 {
2038 if (!found_basedir)
2039 {
2040 found_basedir = compare_basedir_to_config_dir (canonical_basedir,
2041 system_config_dirs[i]);
2042 }
2043 else
2044 {
2045 menu_verbose ("Looking for parent menu file '%s' in '%s'\n",
2046 menu_file, system_config_dirs[i]);
2047
2048 if (load_merge_file_with_config_dir (tree,
2049 loaded_menu_files,
2050 menu_file,
2051 system_config_dirs[i],
2052 layout))
2053 {
2054 break;
2055 }
2056 }
2057
2058 ++i;
2059 }
2060
2061 return system_config_dirs[i] != NULL;
2062 }
2063
2064 static gboolean
2065 load_parent_merge_file (GMenuTree *tree,
2066 GHashTable *loaded_menu_files,
2067 MenuLayoutNode *layout)
2068 {
2069 MenuLayoutNode *root;
2070 const char *basedir;
2071 const char *menu_name;
2072 char *canonical_basedir;
2073 char *menu_file;
2074 gboolean found;
2075
2076 root = menu_layout_node_get_root (layout);
2077
2078 basedir = menu_layout_node_root_get_basedir (root);
2079 menu_name = menu_layout_node_root_get_name (root);
2080
2081 canonical_basedir = menu_canonicalize_file_name (basedir, FALSE);
2082 if (canonical_basedir == NULL)
2083 {
2084 menu_verbose ("Menu basedir '%s' no longer exists, not merging parent\n",
2085 basedir);
2086 return FALSE;
2087 }
2088
2089 found = FALSE;
2090 menu_file = g_strconcat (menu_name, ".menu", NULL);
2091
2092 if (strcmp (menu_file, "applications.menu") == 0)
2093 {
2094 char *prefixed_basename;
2095 prefixed_basename = prefix_menu_name (menu_file);
2096 found = load_parent_merge_file_from_basename (tree, loaded_menu_files,
2097 layout, prefixed_basename,
2098 canonical_basedir);
2099 g_free (prefixed_basename);
2100 }
2101
2102 if (!found)
2103 {
2104 found = load_parent_merge_file_from_basename (tree, loaded_menu_files,
2105 layout, menu_file,
2106 canonical_basedir);
2107 }
2108
2109 g_free (menu_file);
2110 g_free (canonical_basedir);
2111
2112 return found;
2113 }
2114
2115 static void
2116 load_merge_dir (GMenuTree *tree,
2117 GHashTable *loaded_menu_files,
2118 const char *dirname,
2119 MenuLayoutNode *where)
2120 {
2121 GDir *dir;
2122 const char *menu_file;
2123
2124 menu_verbose ("Loading merge dir \"%s\"\n", dirname);
2125
2126 gmenu_tree_add_menu_file_monitor (tree,
2127 dirname,
2128 MENU_FILE_MONITOR_DIRECTORY);
2129
2130 if ((dir = g_dir_open (dirname, 0, NULL)) == NULL)
2131 return;
2132
2133 while ((menu_file = g_dir_read_name (dir)))
2134 {
2135 if (g_str_has_suffix (menu_file, ".menu"))
2136 {
2137 char *full_path;
2138
2139 full_path = g_build_filename (dirname, menu_file, NULL);
2140
2141 load_merge_file (tree, loaded_menu_files, full_path, TRUE, FALSE, where);
2142
2143 g_free (full_path);
2144 }
2145 }
2146
2147 g_dir_close (dir);
2148 }
2149
2150 static void
2151 load_merge_dir_with_config_dir (GMenuTree *tree,
2152 GHashTable *loaded_menu_files,
2153 const char *config_dir,
2154 const char *dirname,
2155 MenuLayoutNode *where)
2156 {
2157 char *path;
2158
2159 path = g_build_filename (config_dir, "menus", dirname, NULL);
2160
2161 load_merge_dir (tree, loaded_menu_files, path, where);
2162
2163 g_free (path);
2164 }
2165
2166 static void
2167 resolve_merge_file (GMenuTree *tree,
2168 GHashTable *loaded_menu_files,
2169 MenuLayoutNode *layout)
2170 {
2171 char *filename;
2172
2173 if (menu_layout_node_merge_file_get_type (layout) == MENU_MERGE_FILE_TYPE_PARENT)
2174 {
2175 if (load_parent_merge_file (tree, loaded_menu_files, layout))
2176 return;
2177 }
2178
2179 filename = menu_layout_node_get_content_as_path (layout);
2180 if (filename == NULL)
2181 {
2182 menu_verbose ("didn't get node content as a path, not merging file\n");
2183 }
2184 else
2185 {
2186 load_merge_file (tree, loaded_menu_files, filename, FALSE, TRUE, layout);
2187
2188 g_free (filename);
2189 }
2190
2191 /* remove the now-replaced node */
2192 menu_layout_node_unlink (layout);
2193 }
2194
2195 static void
2196 resolve_merge_dir (GMenuTree *tree,
2197 GHashTable *loaded_menu_files,
2198 MenuLayoutNode *layout)
2199 {
2200 char *path;
2201
2202 path = menu_layout_node_get_content_as_path (layout);
2203 if (path == NULL)
2204 {
2205 menu_verbose ("didn't get layout node content as a path, not merging dir\n");
2206 }
2207 else
2208 {
2209 load_merge_dir (tree, loaded_menu_files, path, layout);
2210
2211 g_free (path);
2212 }
2213
2214 /* remove the now-replaced node */
2215 menu_layout_node_unlink (layout);
2216 }
2217
2218 static MenuLayoutNode *
2219 add_app_dir (GMenuTree *tree,
2220 MenuLayoutNode *before,
2221 const char *data_dir)
2222 {
2223 MenuLayoutNode *tmp;
2224 char *dirname;
2225
2226 tmp = menu_layout_node_new (MENU_LAYOUT_NODE_APP_DIR);
2227 dirname = g_build_filename (data_dir, "applications", NULL);
2228 menu_layout_node_set_content (tmp, dirname);
2229 menu_layout_node_insert_before (before, tmp);
2230 menu_layout_node_unref (before);
2231
2232 menu_verbose ("Adding <AppDir>%s</AppDir> in <DefaultAppDirs/>\n",
2233 dirname);
2234
2235 g_free (dirname);
2236
2237 return tmp;
2238 }
2239
2240 static void
2241 resolve_default_app_dirs (GMenuTree *tree,
2242 MenuLayoutNode *layout)
2243 {
2244 MenuLayoutNode *before;
2245 const char * const *system_data_dirs;
2246 int i;
2247
2248 system_data_dirs = g_get_system_data_dirs ();
2249
2250 before = add_app_dir (tree,
2251 menu_layout_node_ref (layout),
2252 g_get_user_data_dir ());
2253
2254 i = 0;
2255 while (system_data_dirs[i] != NULL)
2256 {
2257 before = add_app_dir (tree, before, system_data_dirs[i]);
2258
2259 ++i;
2260 }
2261
2262 menu_layout_node_unref (before);
2263
2264 /* remove the now-replaced node */
2265 menu_layout_node_unlink (layout);
2266 }
2267
2268 static MenuLayoutNode *
2269 add_directory_dir (GMenuTree *tree,
2270 MenuLayoutNode *before,
2271 const char *data_dir)
2272 {
2273 MenuLayoutNode *tmp;
2274 char *dirname;
2275
2276 tmp = menu_layout_node_new (MENU_LAYOUT_NODE_DIRECTORY_DIR);
2277 dirname = g_build_filename (data_dir, "desktop-directories", NULL);
2278 menu_layout_node_set_content (tmp, dirname);
2279 menu_layout_node_insert_before (before, tmp);
2280 menu_layout_node_unref (before);
2281
2282 menu_verbose ("Adding <DirectoryDir>%s</DirectoryDir> in <DefaultDirectoryDirs/>\n",
2283 dirname);
2284
2285 g_free (dirname);
2286
2287 return tmp;
2288 }
2289
2290 static void
2291 resolve_default_directory_dirs (GMenuTree *tree,
2292 MenuLayoutNode *layout)
2293 {
2294 MenuLayoutNode *before;
2295 const char * const *system_data_dirs;
2296 int i;
2297
2298 system_data_dirs = g_get_system_data_dirs ();
2299
2300 before = add_directory_dir (tree,
2301 menu_layout_node_ref (layout),
2302 g_get_user_data_dir ());
2303
2304 i = 0;
2305 while (system_data_dirs[i] != NULL)
2306 {
2307 before = add_directory_dir (tree, before, system_data_dirs[i]);
2308
2309 ++i;
2310 }
2311
2312 menu_layout_node_unref (before);
2313
2314 /* remove the now-replaced node */
2315 menu_layout_node_unlink (layout);
2316 }
2317
2318 static void
2319 resolve_default_merge_dirs (GMenuTree *tree,
2320 GHashTable *loaded_menu_files,
2321 MenuLayoutNode *layout)
2322 {
2323 MenuLayoutNode *root;
2324 const char *menu_name;
2325 char *merge_name;
2326 const char * const *system_config_dirs;
2327 int i;
2328
2329 root = menu_layout_node_get_root (layout);
2330 menu_name = menu_layout_node_root_get_name (root);
2331
2332 merge_name = g_strconcat (menu_name, "-merged", NULL);
2333
2334 system_config_dirs = g_get_system_config_dirs ();
2335
2336 /* Merge in reverse order */
2337 i = 0;
2338 while (system_config_dirs[i] != NULL) i++;
2339 while (i > 0)
2340 {
2341 i--;
2342 load_merge_dir_with_config_dir (tree,
2343 loaded_menu_files,
2344 system_config_dirs[i],
2345 merge_name,
2346 layout);
2347 }
2348
2349 load_merge_dir_with_config_dir (tree,
2350 loaded_menu_files,
2351 g_get_user_config_dir (),
2352 merge_name,
2353 layout);
2354
2355 g_free (merge_name);
2356
2357 /* remove the now-replaced node */
2358 menu_layout_node_unlink (layout);
2359 }
2360
2361 static void
2362 add_filename_include (const char *desktop_file_id,
2363 DesktopEntry *entry,
2364 MenuLayoutNode *include)
2365 {
2366 if (!desktop_entry_has_categories (entry))
2367 {
2368 MenuLayoutNode *node;
2369
2370 node = menu_layout_node_new (MENU_LAYOUT_NODE_FILENAME);
2371 menu_layout_node_set_content (node, desktop_file_id);
2372
2373 menu_layout_node_append_child (include, node);
2374 menu_layout_node_unref (node);
2375 }
2376 }
2377
2378 static void
2379 is_dot_directory (const char *basename,
2380 DesktopEntry *entry,
2381 gboolean *has_dot_directory)
2382 {
2383 if (!strcmp (basename, ".directory"))
2384 *has_dot_directory = TRUE;
2385 }
2386
2387 static gboolean
2388 add_menu_for_legacy_dir (MenuLayoutNode *parent,
2389 const char *legacy_dir,
2390 const char *relative_path,
2391 const char *legacy_prefix,
2392 const char *menu_name)
2393 {
2394 EntryDirectory *ed;
2395 DesktopEntrySet *desktop_entries;
2396 DesktopEntrySet *directory_entries;
2397 GSList *subdirs;
2398 gboolean menu_added;
2399 gboolean has_dot_directory;
2400
2401 ed = entry_directory_new_legacy (DESKTOP_ENTRY_INVALID, legacy_dir, legacy_prefix);
2402 if (!ed)
2403 return FALSE;
2404
2405 subdirs = NULL;
2406 desktop_entries = desktop_entry_set_new ();
2407 directory_entries = desktop_entry_set_new ();
2408
2409 entry_directory_get_flat_contents (ed,
2410 desktop_entries,
2411 directory_entries,
2412 &subdirs);
2413 entry_directory_unref (ed);
2414
2415 has_dot_directory = FALSE;
2416 desktop_entry_set_foreach (directory_entries,
2417 (DesktopEntrySetForeachFunc) is_dot_directory,
2418 &has_dot_directory);
2419 desktop_entry_set_unref (directory_entries);
2420
2421 menu_added = FALSE;
2422 if (desktop_entry_set_get_count (desktop_entries) > 0 || subdirs)
2423 {
2424 MenuLayoutNode *menu;
2425 MenuLayoutNode *node;
2426 GString *subdir_path;
2427 GString *subdir_relative;
2428 GSList *tmp;
2429 int legacy_dir_len;
2430 int relative_path_len;
2431
2432 menu = menu_layout_node_new (MENU_LAYOUT_NODE_MENU);
2433 menu_layout_node_append_child (parent, menu);
2434
2435 menu_added = TRUE;
2436
2437 g_assert (menu_name != NULL);
2438
2439 node = menu_layout_node_new (MENU_LAYOUT_NODE_NAME);
2440 menu_layout_node_set_content (node, menu_name);
2441 menu_layout_node_append_child (menu, node);
2442 menu_layout_node_unref (node);
2443
2444 if (has_dot_directory)
2445 {
2446 node = menu_layout_node_new (MENU_LAYOUT_NODE_DIRECTORY);
2447 if (relative_path != NULL)
2448 {
2449 char *directory_entry_path;
2450
2451 directory_entry_path = g_strdup_printf ("%s/.directory", relative_path);
2452 menu_layout_node_set_content (node, directory_entry_path);
2453 g_free (directory_entry_path);
2454 }
2455 else
2456 {
2457 menu_layout_node_set_content (node, ".directory");
2458 }
2459 menu_layout_node_append_child (menu, node);
2460 menu_layout_node_unref (node);
2461 }
2462
2463 if (desktop_entry_set_get_count (desktop_entries) > 0)
2464 {
2465 MenuLayoutNode *include;
2466
2467 include = menu_layout_node_new (MENU_LAYOUT_NODE_INCLUDE);
2468 menu_layout_node_append_child (menu, include);
2469
2470 desktop_entry_set_foreach (desktop_entries,
2471 (DesktopEntrySetForeachFunc) add_filename_include,
2472 include);
2473
2474 menu_layout_node_unref (include);
2475 }
2476
2477 subdir_path = g_string_new (legacy_dir);
2478 legacy_dir_len = strlen (legacy_dir);
2479
2480 subdir_relative = g_string_new (relative_path);
2481 relative_path_len = relative_path ? strlen (relative_path) : 0;
2482
2483 tmp = subdirs;
2484 while (tmp != NULL)
2485 {
2486 const char *subdir = tmp->data;
2487
2488 g_string_append_c (subdir_path, G_DIR_SEPARATOR);
2489 g_string_append (subdir_path, subdir);
2490
2491 if (relative_path_len)
2492 {
2493 g_string_append_c (subdir_relative, G_DIR_SEPARATOR);
2494 }
2495 g_string_append (subdir_relative, subdir);
2496
2497 add_menu_for_legacy_dir (menu,
2498 subdir_path->str,
2499 subdir_relative->str,
2500 legacy_prefix,
2501 subdir);
2502
2503 g_string_truncate (subdir_relative, relative_path_len);
2504 g_string_truncate (subdir_path, legacy_dir_len);
2505
2506 tmp = tmp->next;
2507 }
2508
2509 g_string_free (subdir_path, TRUE);
2510 g_string_free (subdir_relative, TRUE);
2511
2512 menu_layout_node_unref (menu);
2513 }
2514
2515 desktop_entry_set_unref (desktop_entries);
2516
2517 g_slist_foreach (subdirs, (GFunc) g_free, NULL);
2518 g_slist_free (subdirs);
2519
2520 return menu_added;
2521 }
2522
2523 static void
2524 resolve_legacy_dir (GMenuTree *tree,
2525 GHashTable *loaded_menu_files,
2526 MenuLayoutNode *legacy)
2527 {
2528 MenuLayoutNode *to_merge;
2529 MenuLayoutNode *menu;
2530
2531 to_merge = menu_layout_node_new (MENU_LAYOUT_NODE_ROOT);
2532
2533 menu = menu_layout_node_get_parent (legacy);
2534 g_assert (menu_layout_node_get_type (menu) == MENU_LAYOUT_NODE_MENU);
2535
2536 if (add_menu_for_legacy_dir (to_merge,
2537 menu_layout_node_get_content (legacy),
2538 NULL,
2539 menu_layout_node_legacy_dir_get_prefix (legacy),
2540 menu_layout_node_menu_get_name (menu)))
2541 {
2542 merge_resolved_children (tree, loaded_menu_files, legacy, to_merge);
2543 }
2544
2545 menu_layout_node_unref (to_merge);
2546 }
2547
2548 static MenuLayoutNode *
2549 add_legacy_dir (GMenuTree *tree,
2550 GHashTable *loaded_menu_files,
2551 MenuLayoutNode *before,
2552 const char *data_dir)
2553 {
2554 MenuLayoutNode *legacy;
2555 char *dirname;
2556
2557 dirname = g_build_filename (data_dir, "applnk", NULL);
2558
2559 legacy = menu_layout_node_new (MENU_LAYOUT_NODE_LEGACY_DIR);
2560 menu_layout_node_set_content (legacy, dirname);
2561 menu_layout_node_legacy_dir_set_prefix (legacy, "kde");
2562 menu_layout_node_insert_before (before, legacy);
2563 menu_layout_node_unref (before);
2564
2565 menu_verbose ("Adding <LegacyDir>%s</LegacyDir> in <KDELegacyDirs/>\n",
2566 dirname);
2567
2568 resolve_legacy_dir (tree, loaded_menu_files, legacy);
2569
2570 g_free (dirname);
2571
2572 return legacy;
2573 }
2574
2575 static void
2576 resolve_kde_legacy_dirs (GMenuTree *tree,
2577 GHashTable *loaded_menu_files,
2578 MenuLayoutNode *layout)
2579 {
2580 MenuLayoutNode *before;
2581 const char * const *system_data_dirs;
2582 int i;
2583
2584 system_data_dirs = g_get_system_data_dirs ();
2585
2586 before = add_legacy_dir (tree,
2587 loaded_menu_files,
2588 menu_layout_node_ref (layout),
2589 g_get_user_data_dir ());
2590
2591 i = 0;
2592 while (system_data_dirs[i] != NULL)
2593 {
2594 before = add_legacy_dir (tree, loaded_menu_files, before, system_data_dirs[i]);
2595
2596 ++i;
2597 }
2598
2599 menu_layout_node_unref (before);
2600
2601 /* remove the now-replaced node */
2602 menu_layout_node_unlink (layout);
2603 }
2604
2605 static void
2606 gmenu_tree_resolve_files (GMenuTree *tree,
2607 GHashTable *loaded_menu_files,
2608 MenuLayoutNode *layout)
2609 {
2610 MenuLayoutNode *child;
2611
2612 menu_verbose ("Resolving files in: ");
2613 menu_debug_print_layout (layout, TRUE);
2614
2615 switch (menu_layout_node_get_type (layout))
2616 {
2617 case MENU_LAYOUT_NODE_MERGE_FILE:
2618 resolve_merge_file (tree, loaded_menu_files, layout);
2619 break;
2620
2621 case MENU_LAYOUT_NODE_MERGE_DIR:
2622 resolve_merge_dir (tree, loaded_menu_files, layout);
2623 break;
2624
2625 case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS:
2626 resolve_default_app_dirs (tree, layout);
2627 break;
2628
2629 case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS:
2630 resolve_default_directory_dirs (tree, layout);
2631 break;
2632
2633 case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS:
2634 resolve_default_merge_dirs (tree, loaded_menu_files, layout);
2635 break;
2636
2637 case MENU_LAYOUT_NODE_LEGACY_DIR:
2638 resolve_legacy_dir (tree, loaded_menu_files, layout);
2639 break;
2640
2641 case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS:
2642 resolve_kde_legacy_dirs (tree, loaded_menu_files, layout);
2643 break;
2644
2645 case MENU_LAYOUT_NODE_PASSTHROUGH:
2646 /* Just get rid of these, we don't need the memory usage */
2647 menu_layout_node_unlink (layout);
2648 break;
2649
2650 default:
2651 /* Recurse */
2652 child = menu_layout_node_get_children (layout);
2653 while (child != NULL)
2654 {
2655 MenuLayoutNode *next = menu_layout_node_get_next (child);
2656
2657 gmenu_tree_resolve_files (tree, loaded_menu_files, child);
2658
2659 child = next;
2660 }
2661 break;
2662 }
2663 }
2664
2665 static void
2666 move_children (MenuLayoutNode *from,
2667 MenuLayoutNode *to)
2668 {
2669 MenuLayoutNode *from_child;
2670 MenuLayoutNode *insert_before;
2671
2672 insert_before = menu_layout_node_get_children (to);
2673 from_child = menu_layout_node_get_children (from);
2674
2675 while (from_child != NULL)
2676 {
2677 MenuLayoutNode *next;
2678
2679 next = menu_layout_node_get_next (from_child);
2680
2681 menu_layout_node_steal (from_child);
2682
2683 if (menu_layout_node_get_type (from_child) == MENU_LAYOUT_NODE_NAME)
2684 {
2685 ; /* just drop the Name in the old <Menu> */
2686 }
2687 else if (insert_before)
2688 {
2689 menu_layout_node_insert_before (insert_before, from_child);
2690 g_assert (menu_layout_node_get_next (from_child) == insert_before);
2691 }
2692 else
2693 {
2694 menu_layout_node_append_child (to, from_child);
2695 }
2696
2697 menu_layout_node_unref (from_child);
2698
2699 from_child = next;
2700 }
2701 }
2702
2703 static int
2704 null_safe_strcmp (const char *a,
2705 const char *b)
2706 {
2707 if (a == NULL && b == NULL)
2708 return 0;
2709 else if (a == NULL)
2710 return -1;
2711 else if (b == NULL)
2712 return 1;
2713 else
2714 return strcmp (a, b);
2715 }
2716
2717 static int
2718 node_compare_func (const void *a,
2719 const void *b)
2720 {
2721 MenuLayoutNode *node_a = (MenuLayoutNode*) a;
2722 MenuLayoutNode *node_b = (MenuLayoutNode*) b;
2723 MenuLayoutNodeType t_a = menu_layout_node_get_type (node_a);
2724 MenuLayoutNodeType t_b = menu_layout_node_get_type (node_b);
2725
2726 if (t_a < t_b)
2727 return -1;
2728 else if (t_a > t_b)
2729 return 1;
2730 else
2731 {
2732 const char *c_a = menu_layout_node_get_content (node_a);
2733 const char *c_b = menu_layout_node_get_content (node_b);
2734
2735 return null_safe_strcmp (c_a, c_b);
2736 }
2737 }
2738
2739 static int
2740 node_menu_compare_func (const void *a,
2741 const void *b)
2742 {
2743 MenuLayoutNode *node_a = (MenuLayoutNode*) a;
2744 MenuLayoutNode *node_b = (MenuLayoutNode*) b;
2745 MenuLayoutNode *parent_a = menu_layout_node_get_parent (node_a);
2746 MenuLayoutNode *parent_b = menu_layout_node_get_parent (node_b);
2747
2748 if (parent_a < parent_b)
2749 return -1;
2750 else if (parent_a > parent_b)
2751 return 1;
2752 else
2753 return null_safe_strcmp (menu_layout_node_menu_get_name (node_a),
2754 menu_layout_node_menu_get_name (node_b));
2755 }
2756
2757 static void
2758 gmenu_tree_strip_duplicate_children (GMenuTree *tree,
2759 MenuLayoutNode *layout)
2760 {
2761 MenuLayoutNode *child;
2762 GSList *simple_nodes;
2763 GSList *menu_layout_nodes;
2764 GSList *prev;
2765 GSList *tmp;
2766
2767 /* to strip dups, we find all the child nodes where
2768 * we want to kill dups, sort them,
2769 * then nuke the adjacent nodes that are equal
2770 */
2771
2772 simple_nodes = NULL;
2773 menu_layout_nodes = NULL;
2774
2775 child = menu_layout_node_get_children (layout);
2776 while (child != NULL)
2777 {
2778 switch (menu_layout_node_get_type (child))
2779 {
2780 /* These are dups if their content is the same */
2781 case MENU_LAYOUT_NODE_APP_DIR:
2782 case MENU_LAYOUT_NODE_DIRECTORY_DIR:
2783 case MENU_LAYOUT_NODE_DIRECTORY:
2784 simple_nodes = g_slist_prepend (simple_nodes, child);
2785 break;
2786
2787 /* These have to be merged in a more complicated way,
2788 * and then recursed
2789 */
2790 case MENU_LAYOUT_NODE_MENU:
2791 menu_layout_nodes = g_slist_prepend (menu_layout_nodes, child);
2792 break;
2793
2794 default:
2795 break;
2796 }
2797
2798 child = menu_layout_node_get_next (child);
2799 }
2800
2801 /* Note that the lists are all backward. So we want to keep
2802 * the items that are earlier in the list, because they were
2803 * later in the file
2804 */
2805
2806 /* stable sort the simple nodes */
2807 simple_nodes = g_slist_sort (simple_nodes,
2808 node_compare_func);
2809
2810 prev = NULL;
2811 tmp = simple_nodes;
2812 while (tmp != NULL)
2813 {
2814 GSList *next = tmp->next;
2815
2816 if (prev)
2817 {
2818 MenuLayoutNode *p = prev->data;
2819 MenuLayoutNode *n = tmp->data;
2820
2821 if (node_compare_func (p, n) == 0)
2822 {
2823 /* nuke it! */
2824 menu_layout_node_unlink (n);
2825 simple_nodes = g_slist_delete_link (simple_nodes, tmp);
2826 tmp = prev;
2827 }
2828 }
2829
2830 prev = tmp;
2831 tmp = next;
2832 }
2833
2834 g_slist_free (simple_nodes);
2835 simple_nodes = NULL;
2836
2837 /* stable sort the menu nodes (the sort includes the
2838 * parents of the nodes in the comparison). Remember
2839 * the list is backward.
2840 */
2841 menu_layout_nodes = g_slist_sort (menu_layout_nodes,
2842 node_menu_compare_func);
2843
2844 prev = NULL;
2845 tmp = menu_layout_nodes;
2846 while (tmp != NULL)
2847 {
2848 GSList *next = tmp->next;
2849
2850 if (prev)
2851 {
2852 MenuLayoutNode *p = prev->data;
2853 MenuLayoutNode *n = tmp->data;
2854
2855 if (node_menu_compare_func (p, n) == 0)
2856 {
2857 /* Move children of first menu to the start of second
2858 * menu and nuke the first menu
2859 */
2860 move_children (n, p);
2861 menu_layout_node_unlink (n);
2862 menu_layout_nodes = g_slist_delete_link (menu_layout_nodes, tmp);
2863 tmp = prev;
2864 }
2865 }
2866
2867 prev = tmp;
2868 tmp = next;
2869 }
2870
2871 g_slist_free (menu_layout_nodes);
2872 menu_layout_nodes = NULL;
2873
2874 /* Recursively clean up all children */
2875 child = menu_layout_node_get_children (layout);
2876 while (child != NULL)
2877 {
2878 if (menu_layout_node_get_type (child) == MENU_LAYOUT_NODE_MENU)
2879 gmenu_tree_strip_duplicate_children (tree, child);
2880
2881 child = menu_layout_node_get_next (child);
2882 }
2883 }
2884
2885 static MenuLayoutNode *
2886 find_submenu (MenuLayoutNode *layout,
2887 const char *path,
2888 gboolean create_if_not_found)
2889 {
2890 MenuLayoutNode *child;
2891 const char *slash;
2892 const char *next_path;
2893 char *name;
2894
2895 menu_verbose (" (splitting \"%s\")\n", path);
2896
2897 if (path[0] == '\0' || path[0] == G_DIR_SEPARATOR)
2898 return NULL;
2899
2900 slash = strchr (path, G_DIR_SEPARATOR);
2901 if (slash != NULL)
2902 {
2903 name = g_strndup (path, slash - path);
2904 next_path = slash + 1;
2905 if (*next_path == '\0')
2906 next_path = NULL;
2907 }
2908 else
2909 {
2910 name = g_strdup (path);
2911 next_path = NULL;
2912 }
2913
2914 child = menu_layout_node_get_children (layout);
2915 while (child != NULL)
2916 {
2917 switch (menu_layout_node_get_type (child))
2918 {
2919 case MENU_LAYOUT_NODE_MENU:
2920 {
2921 if (strcmp (name, menu_layout_node_menu_get_name (child)) == 0)
2922 {
2923 menu_verbose ("MenuNode %p found for path component \"%s\"\n",
2924 child, name);
2925
2926 g_free (name);
2927
2928 if (!next_path)
2929 {
2930 menu_verbose (" Found menu node %p parent is %p\n",
2931 child, layout);
2932 return child;
2933 }
2934
2935 return find_submenu (child, next_path, create_if_not_found);
2936 }
2937 }
2938 break;
2939
2940 default:
2941 break;
2942 }
2943
2944 child = menu_layout_node_get_next (child);
2945 }
2946
2947 if (create_if_not_found)
2948 {
2949 MenuLayoutNode *name_node;
2950
2951 child = menu_layout_node_new (MENU_LAYOUT_NODE_MENU);
2952 menu_layout_node_append_child (layout, child);
2953
2954 name_node = menu_layout_node_new (MENU_LAYOUT_NODE_NAME);
2955 menu_layout_node_set_content (name_node, name);
2956 menu_layout_node_append_child (child, name_node);
2957 menu_layout_node_unref (name_node);
2958
2959 menu_verbose (" Created menu node %p parent is %p\n",
2960 child, layout);
2961
2962 menu_layout_node_unref (child);
2963 g_free (name);
2964
2965 if (!next_path)
2966 return child;
2967
2968 return find_submenu (child, next_path, create_if_not_found);
2969 }
2970 else
2971 {
2972 g_free (name);
2973 return NULL;
2974 }
2975 }
2976
2977 /* To call this you first have to strip duplicate children once,
2978 * otherwise when you move a menu Foo to Bar then you may only
2979 * move one of Foo, not all the merged Foo.
2980 */
2981 static void
2982 gmenu_tree_execute_moves (GMenuTree *tree,
2983 MenuLayoutNode *layout,
2984 gboolean *need_remove_dups_p)
2985 {
2986 MenuLayoutNode *child;
2987 gboolean need_remove_dups;
2988 GSList *move_nodes;
2989 GSList *tmp;
2990
2991 need_remove_dups = FALSE;
2992
2993 move_nodes = NULL;
2994
2995 child = menu_layout_node_get_children (layout);
2996 while (child != NULL)
2997 {
2998 switch (menu_layout_node_get_type (child))
2999 {
3000 case MENU_LAYOUT_NODE_MENU:
3001 /* Recurse - we recurse first and process the current node
3002 * second, as the spec dictates.
3003 */
3004 gmenu_tree_execute_moves (tree, child, &need_remove_dups);
3005 break;
3006
3007 case MENU_LAYOUT_NODE_MOVE:
3008 move_nodes = g_slist_prepend (move_nodes, child);
3009 break;
3010
3011 default:
3012 break;
3013 }
3014
3015 child = menu_layout_node_get_next (child);
3016 }
3017
3018 /* We need to execute the move operations in the order that they appear */
3019 move_nodes = g_slist_reverse (move_nodes);
3020
3021 tmp = move_nodes;
3022 while (tmp != NULL)
3023 {
3024 MenuLayoutNode *move_node = tmp->data;
3025 MenuLayoutNode *old_node;
3026 GSList *next = tmp->next;
3027 const char *old;
3028 const char *new;
3029
3030 old = menu_layout_node_move_get_old (move_node);
3031 new = menu_layout_node_move_get_new (move_node);
3032 g_assert (old != NULL && new != NULL);
3033
3034 menu_verbose ("executing <Move> old = \"%s\" new = \"%s\"\n",
3035 old, new);
3036
3037 old_node = find_submenu (layout, old, FALSE);
3038 if (old_node != NULL)
3039 {
3040 MenuLayoutNode *new_node;
3041
3042 /* here we can create duplicates anywhere below the
3043 * node
3044 */
3045 need_remove_dups = TRUE;
3046
3047 /* look up new node creating it and its parents if
3048 * required
3049 */
3050 new_node = find_submenu (layout, new, TRUE);
3051 g_assert (new_node != NULL);
3052
3053 move_children (old_node, new_node);
3054
3055 menu_layout_node_unlink (old_node);
3056 }
3057
3058 menu_layout_node_unlink (move_node);
3059
3060 tmp = next;
3061 }
3062
3063 g_slist_free (move_nodes);
3064
3065 /* This oddness is to ensure we only remove dups once,
3066 * at the root, instead of recursing the tree over
3067 * and over.
3068 */
3069 if (need_remove_dups_p)
3070 *need_remove_dups_p = need_remove_dups;
3071 else if (need_remove_dups)
3072 gmenu_tree_strip_duplicate_children (tree, layout);
3073 }
3074
3075 static gboolean
3076 gmenu_tree_load_layout (GMenuTree *tree,
3077 GError **error)
3078 {
3079 GHashTable *loaded_menu_files;
3080
3081 if (tree->layout)
3082 return TRUE;
3083
3084 if (!gmenu_tree_canonicalize_path (tree, error))
3085 return FALSE;
3086
3087 menu_verbose ("Loading menu layout from \"%s\"\n",
3088 tree->canonical_path);
3089
3090 tree->layout = menu_layout_load (tree->canonical_path,
3091 tree->non_prefixed_basename,
3092 error);
3093 if (!tree->layout)
3094 return FALSE;
3095
3096 loaded_menu_files = g_hash_table_new (g_str_hash, g_str_equal);
3097 g_hash_table_insert (loaded_menu_files, tree->canonical_path, GUINT_TO_POINTER (TRUE));
3098 gmenu_tree_resolve_files (tree, loaded_menu_files, tree->layout);
3099 g_hash_table_destroy (loaded_menu_files);
3100
3101 gmenu_tree_strip_duplicate_children (tree, tree->layout);
3102 gmenu_tree_execute_moves (tree, tree->layout, NULL);
3103
3104 return TRUE;
3105 }
3106
3107 static void
3108 gmenu_tree_force_reload (GMenuTree *tree)
3109 {
3110 gmenu_tree_force_rebuild (tree);
3111
3112 if (tree->layout)
3113 menu_layout_node_unref (tree->layout);
3114 tree->layout = NULL;
3115 }
3116
3117 typedef struct
3118 {
3119 DesktopEntrySet *set;
3120 const char *category;
3121 } GetByCategoryForeachData;
3122
3123 static void
3124 get_by_category_foreach (const char *file_id,
3125 DesktopEntry *entry,
3126 GetByCategoryForeachData *data)
3127 {
3128 if (desktop_entry_has_category (entry, data->category))
3129 desktop_entry_set_add_entry (data->set, entry, file_id);
3130 }
3131
3132 static void
3133 get_by_category (DesktopEntrySet *entry_pool,
3134 DesktopEntrySet *set,
3135 const char *category)
3136 {
3137 GetByCategoryForeachData data;
3138
3139 data.set = set;
3140 data.category = category;
3141
3142 desktop_entry_set_foreach (entry_pool,
3143 (DesktopEntrySetForeachFunc) get_by_category_foreach,
3144 &data);
3145 }
3146
3147 static DesktopEntrySet *
3148 process_include_rules (MenuLayoutNode *layout,
3149 DesktopEntrySet *entry_pool)
3150 {
3151 DesktopEntrySet *set = NULL;
3152
3153 switch (menu_layout_node_get_type (layout))
3154 {
3155 case MENU_LAYOUT_NODE_AND:
3156 {
3157 MenuLayoutNode *child;
3158
3159 menu_verbose ("Processing <And>\n");
3160
3161 child = menu_layout_node_get_children (layout);
3162 while (child != NULL)
3163 {
3164 DesktopEntrySet *child_set;
3165
3166 child_set = process_include_rules (child, entry_pool);
3167
3168 if (set == NULL)
3169 {
3170 set = child_set;
3171 }
3172 else
3173 {
3174 desktop_entry_set_intersection (set, child_set);
3175 desktop_entry_set_unref (child_set);
3176 }
3177
3178 /* as soon as we get empty results, we can bail,
3179 * because it's an AND
3180 */
3181 if (desktop_entry_set_get_count (set) == 0)
3182 break;
3183
3184 child = menu_layout_node_get_next (child);
3185 }
3186 menu_verbose ("Processed <And>\n");
3187 }
3188 break;
3189
3190 case MENU_LAYOUT_NODE_OR:
3191 {
3192 MenuLayoutNode *child;
3193
3194 menu_verbose ("Processing <Or>\n");
3195
3196 child = menu_layout_node_get_children (layout);
3197 while (child != NULL)
3198 {
3199 DesktopEntrySet *child_set;
3200
3201 child_set = process_include_rules (child, entry_pool);
3202
3203 if (set == NULL)
3204 {
3205 set = child_set;
3206 }
3207 else
3208 {
3209 desktop_entry_set_union (set, child_set);
3210 desktop_entry_set_unref (child_set);
3211 }
3212
3213 child = menu_layout_node_get_next (child);
3214 }
3215 menu_verbose ("Processed <Or>\n");
3216 }
3217 break;
3218
3219 case MENU_LAYOUT_NODE_NOT:
3220 {
3221 /* First get the OR of all the rules */
3222 MenuLayoutNode *child;
3223
3224 menu_verbose ("Processing <Not>\n");
3225
3226 child = menu_layout_node_get_children (layout);
3227 while (child != NULL)
3228 {
3229 DesktopEntrySet *child_set;
3230
3231 child_set = process_include_rules (child, entry_pool);
3232
3233 if (set == NULL)
3234 {
3235 set = child_set;
3236 }
3237 else
3238 {
3239 desktop_entry_set_union (set, child_set);
3240 desktop_entry_set_unref (child_set);
3241 }
3242
3243 child = menu_layout_node_get_next (child);
3244 }
3245
3246 if (set != NULL)
3247 {
3248 DesktopEntrySet *inverted;
3249
3250 /* Now invert the result */
3251 inverted = desktop_entry_set_new ();
3252 desktop_entry_set_union (inverted, entry_pool);
3253 desktop_entry_set_subtract (inverted, set);
3254 desktop_entry_set_unref (set);
3255 set = inverted;
3256 }
3257 menu_verbose ("Processed <Not>\n");
3258 }
3259 break;
3260
3261 case MENU_LAYOUT_NODE_ALL:
3262 menu_verbose ("Processing <All>\n");
3263 set = desktop_entry_set_new ();
3264 desktop_entry_set_union (set, entry_pool);
3265 menu_verbose ("Processed <All>\n");
3266 break;
3267
3268 case MENU_LAYOUT_NODE_FILENAME:
3269 {
3270 DesktopEntry *entry;
3271
3272 menu_verbose ("Processing <Filename>%s</Filename>\n",
3273 menu_layout_node_get_content (layout));
3274
3275 entry = desktop_entry_set_lookup (entry_pool,
3276 menu_layout_node_get_content (layout));
3277 if (entry != NULL)
3278 {
3279 set = desktop_entry_set_new ();
3280 desktop_entry_set_add_entry (set,
3281 entry,
3282 menu_layout_node_get_content (layout));
3283 }
3284 menu_verbose ("Processed <Filename>%s</Filename>\n",
3285 menu_layout_node_get_content (layout));
3286 }
3287 break;
3288
3289 case MENU_LAYOUT_NODE_CATEGORY:
3290 menu_verbose ("Processing <Category>%s</Category>\n",
3291 menu_layout_node_get_content (layout));
3292 set = desktop_entry_set_new ();
3293 get_by_category (entry_pool, set, menu_layout_node_get_content (layout));
3294 menu_verbose ("Processed <Category>%s</Category>\n",
3295 menu_layout_node_get_content (layout));
3296 break;
3297
3298 default:
3299 break;
3300 }
3301
3302 if (set == NULL)
3303 set = desktop_entry_set_new (); /* create an empty set */
3304
3305 menu_verbose ("Matched %d entries\n", desktop_entry_set_get_count (set));
3306
3307 return set;
3308 }
3309
3310 static void
3311 collect_layout_info (MenuLayoutNode *layout,
3312 GSList **layout_info)
3313 {
3314 MenuLayoutNode *iter;
3315
3316 g_slist_foreach (*layout_info,
3317 (GFunc) menu_layout_node_unref,
3318 NULL);
3319 g_slist_free (*layout_info);
3320 *layout_info = NULL;
3321
3322 iter = menu_layout_node_get_children (layout);
3323 while (iter != NULL)
3324 {
3325 switch (menu_layout_node_get_type (iter))
3326 {
3327 case MENU_LAYOUT_NODE_MENUNAME:
3328 case MENU_LAYOUT_NODE_FILENAME:
3329 case MENU_LAYOUT_NODE_SEPARATOR:
3330 case MENU_LAYOUT_NODE_MERGE:
3331 *layout_info = g_slist_prepend (*layout_info,
3332 menu_layout_node_ref (iter));
3333 break;
3334
3335 default:
3336 break;
3337 }
3338
3339 iter = menu_layout_node_get_next (iter);
3340 }
3341
3342 *layout_info = g_slist_reverse (*layout_info);
3343 }
3344
3345 static void
3346 entries_listify_foreach (const char *desktop_file_id,
3347 DesktopEntry *desktop_entry,
3348 GMenuTreeDirectory *directory)
3349 {
3350 directory->entries =
3351 g_slist_prepend (directory->entries,
3352 gmenu_tree_entry_new (directory,
3353 desktop_entry,
3354 desktop_file_id,
3355 FALSE,
3356 FALSE));
3357 }
3358
3359 static void
3360 excluded_entries_listify_foreach (const char *desktop_file_id,
3361 DesktopEntry *desktop_entry,
3362 GMenuTreeDirectory *directory)
3363 {
3364 directory->entries =
3365 g_slist_prepend (directory->entries,
3366 gmenu_tree_entry_new (directory,
3367 desktop_entry,
3368 desktop_file_id,
3369 TRUE,
3370 FALSE));
3371 }
3372
3373 static void
3374 unallocated_entries_listify_foreach (const char *desktop_file_id,
3375 DesktopEntry *desktop_entry,
3376 GMenuTreeDirectory *directory)
3377 {
3378 directory->entries =
3379 g_slist_prepend (directory->entries,
3380 gmenu_tree_entry_new (directory,
3381 desktop_entry,
3382 desktop_file_id,
3383 FALSE,
3384 TRUE));
3385 }
3386
3387 static void
3388 set_default_layout_values (GMenuTreeDirectory *parent,
3389 GMenuTreeDirectory *child)
3390 {
3391 GSList *tmp;
3392
3393 /* if the child has a defined default layout, we don't want to override its
3394 * values. The parent might have a non-defined layout info (ie, no child of
3395 * the DefaultLayout node) but it doesn't meant the default layout values
3396 * (ie, DefaultLayout attributes) aren't different from the global defaults.
3397 */
3398 if (child->default_layout_info != NULL ||
3399 child->default_layout_values.mask != MENU_LAYOUT_VALUES_NONE)
3400 return;
3401
3402 child->default_layout_values = parent->default_layout_values;
3403
3404 tmp = child->subdirs;
3405 while (tmp != NULL)
3406 {
3407 GMenuTreeDirectory *subdir = tmp->data;
3408
3409 set_default_layout_values (child, subdir);
3410
3411 tmp = tmp->next;
3412 }
3413 }
3414
3415 static GMenuTreeDirectory *
3416 process_layout (GMenuTree *tree,
3417 GMenuTreeDirectory *parent,
3418 MenuLayoutNode *layout,
3419 DesktopEntrySet *allocated)
3420 {
3421 MenuLayoutNode *layout_iter;
3422 GMenuTreeDirectory *directory;
3423 DesktopEntrySet *entry_pool;
3424 DesktopEntrySet *entries;
3425 DesktopEntrySet *allocated_set;
3426 DesktopEntrySet *excluded_set;
3427 gboolean deleted;
3428 gboolean only_unallocated;
3429 GSList *tmp;
3430
3431 g_assert (menu_layout_node_get_type (layout) == MENU_LAYOUT_NODE_MENU);
3432 g_assert (menu_layout_node_menu_get_name (layout) != NULL);
3433
3434 directory = gmenu_tree_directory_new (tree, parent,
3435 menu_layout_node_menu_get_name (layout));
3436
3437 menu_verbose ("=== Menu name = %s ===\n", directory->name);
3438
3439
3440 deleted = FALSE;
3441 only_unallocated = FALSE;
3442
3443 entries = desktop_entry_set_new ();
3444 allocated_set = desktop_entry_set_new ();
3445
3446 if (tree->flags & GMENU_TREE_FLAGS_INCLUDE_EXCLUDED)
3447 excluded_set = desktop_entry_set_new ();
3448 else
3449 excluded_set = NULL;
3450
3451 entry_pool = _entry_directory_list_get_all_desktops (menu_layout_node_menu_get_app_dirs (layout));
3452
3453 layout_iter = menu_layout_node_get_children (layout);
3454 while (layout_iter != NULL)
3455 {
3456 switch (menu_layout_node_get_type (layout_iter))
3457 {
3458 case MENU_LAYOUT_NODE_MENU:
3459 /* recurse */
3460 {
3461 GMenuTreeDirectory *child_dir;
3462
3463 menu_verbose ("Processing <Menu>\n");
3464
3465 child_dir = process_layout (tree,
3466 directory,
3467 layout_iter,
3468 allocated);
3469 if (child_dir)
3470 directory->subdirs = g_slist_prepend (directory->subdirs,
3471 child_dir);
3472
3473 menu_verbose ("Processed <Menu>\n");
3474 }
3475 break;
3476
3477 case MENU_LAYOUT_NODE_INCLUDE:
3478 {
3479 /* The match rule children of the <Include> are
3480 * independent (logical OR) so we can process each one by
3481 * itself
3482 */
3483 MenuLayoutNode *rule;
3484
3485 menu_verbose ("Processing <Include> (%d entries)\n",
3486 desktop_entry_set_get_count (entries));
3487
3488 rule = menu_layout_node_get_children (layout_iter);
3489 while (rule != NULL)
3490 {
3491 DesktopEntrySet *rule_set;
3492
3493 rule_set = process_include_rules (rule, entry_pool);
3494 if (rule_set != NULL)
3495 {
3496 desktop_entry_set_union (entries, rule_set);
3497 desktop_entry_set_union (allocated_set, rule_set);
3498 if (excluded_set != NULL)
3499 desktop_entry_set_subtract (excluded_set, rule_set);
3500 desktop_entry_set_unref (rule_set);
3501 }
3502
3503 rule = menu_layout_node_get_next (rule);
3504 }
3505
3506 menu_verbose ("Processed <Include> (%d entries)\n",
3507 desktop_entry_set_get_count (entries));
3508 }
3509 break;
3510
3511 case MENU_LAYOUT_NODE_EXCLUDE:
3512 {
3513 /* The match rule children of the <Exclude> are
3514 * independent (logical OR) so we can process each one by
3515 * itself
3516 */
3517 MenuLayoutNode *rule;
3518
3519 menu_verbose ("Processing <Exclude> (%d entries)\n",
3520 desktop_entry_set_get_count (entries));
3521
3522 rule = menu_layout_node_get_children (layout_iter);
3523 while (rule != NULL)
3524 {
3525 DesktopEntrySet *rule_set;
3526
3527 rule_set = process_include_rules (rule, entry_pool);
3528 if (rule_set != NULL)
3529 {
3530 if (excluded_set != NULL)
3531 desktop_entry_set_union (excluded_set, rule_set);
3532 desktop_entry_set_subtract (entries, rule_set);
3533 desktop_entry_set_unref (rule_set);
3534 }
3535
3536 rule = menu_layout_node_get_next (rule);
3537 }
3538
3539 menu_verbose ("Processed <Exclude> (%d entries)\n",
3540 desktop_entry_set_get_count (entries));
3541 }
3542 break;
3543
3544 case MENU_LAYOUT_NODE_DIRECTORY:
3545 {
3546 DesktopEntry *entry;
3547
3548 menu_verbose ("Processing <Directory>%s</Directory>\n",
3549 menu_layout_node_get_content (layout_iter));
3550
3551 /*
3552 * The last <Directory> to exist wins, so we always try overwriting
3553 */
3554 entry = entry_directory_list_get_directory (menu_layout_node_menu_get_directory_dirs (layout),
3555 menu_layout_node_get_content (layout_iter));
3556
3557 if (entry != NULL)
3558 {
3559 if (!desktop_entry_get_hidden (entry))
3560 {
3561 if (directory->directory_entry)
3562 desktop_entry_unref (directory->directory_entry);
3563 directory->directory_entry = entry; /* pass ref ownership */
3564 }
3565 else
3566 {
3567 desktop_entry_unref (entry);
3568 }
3569 }
3570
3571 menu_verbose ("Processed <Directory> new directory entry = %p (%s)\n",
3572 directory->directory_entry,
3573 directory->directory_entry? desktop_entry_get_path (directory->directory_entry) : "null");
3574 }
3575 break;
3576
3577 case MENU_LAYOUT_NODE_DELETED:
3578 menu_verbose ("Processed <Deleted/>\n");
3579 deleted = TRUE;
3580 break;
3581
3582 case MENU_LAYOUT_NODE_NOT_DELETED:
3583 menu_verbose ("Processed <NotDeleted/>\n");
3584 deleted = FALSE;
3585 break;
3586
3587 case MENU_LAYOUT_NODE_ONLY_UNALLOCATED:
3588 menu_verbose ("Processed <OnlyUnallocated/>\n");
3589 only_unallocated = TRUE;
3590 break;
3591
3592 case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED:
3593 menu_verbose ("Processed <NotOnlyUnallocated/>\n");
3594 only_unallocated = FALSE;
3595 break;
3596
3597 case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
3598 menu_layout_node_default_layout_get_values (layout_iter,
3599 &directory->default_layout_values);
3600 collect_layout_info (layout_iter, &directory->default_layout_info);
3601 menu_verbose ("Processed <DefaultLayout/>\n");
3602 break;
3603
3604 case MENU_LAYOUT_NODE_LAYOUT:
3605 collect_layout_info (layout_iter, &directory->layout_info);
3606 menu_verbose ("Processed <Layout/>\n");
3607 break;
3608
3609 default:
3610 break;
3611 }
3612
3613 layout_iter = menu_layout_node_get_next (layout_iter);
3614 }
3615
3616 desktop_entry_set_unref (entry_pool);
3617
3618 directory->only_unallocated = only_unallocated;
3619
3620 if (!directory->only_unallocated)
3621 desktop_entry_set_union (allocated, allocated_set);
3622
3623 desktop_entry_set_unref (allocated_set);
3624
3625 if (directory->directory_entry)
3626 {
3627 if (desktop_entry_get_no_display (directory->directory_entry))
3628 {
3629 directory->is_nodisplay = TRUE;
3630
3631 if (!(tree->flags & GMENU_TREE_FLAGS_INCLUDE_NODISPLAY))
3632 {
3633 menu_verbose ("Not showing menu %s because NoDisplay=true\n",
3634 desktop_entry_get_name (directory->directory_entry));
3635 deleted = TRUE;
3636 }
3637 }
3638
3639 if (!desktop_entry_get_show_in (directory->directory_entry))
3640 {
3641 menu_verbose ("Not showing menu %s because OnlyShowIn!=$DESKTOP or NotShowIn=$DESKTOP (with $DESKTOP=${XDG_CURRENT_DESKTOP:-GNOME})\n",
3642 desktop_entry_get_name (directory->directory_entry));
3643 deleted = TRUE;
3644 }
3645 }
3646
3647 if (deleted)
3648 {
3649 if (excluded_set != NULL)
3650 desktop_entry_set_unref (excluded_set);
3651 desktop_entry_set_unref (entries);
3652 gmenu_tree_item_unref (directory);
3653 return NULL;
3654 }
3655
3656 desktop_entry_set_foreach (entries,
3657 (DesktopEntrySetForeachFunc) entries_listify_foreach,
3658 directory);
3659 desktop_entry_set_unref (entries);
3660
3661 if (excluded_set != NULL)
3662 {
3663 desktop_entry_set_foreach (excluded_set,
3664 (DesktopEntrySetForeachFunc) excluded_entries_listify_foreach,
3665 directory);
3666 desktop_entry_set_unref (excluded_set);
3667 }
3668
3669 tmp = directory->subdirs;
3670 while (tmp != NULL)
3671 {
3672 GMenuTreeDirectory *subdir = tmp->data;
3673
3674 set_default_layout_values (directory, subdir);
3675
3676 tmp = tmp->next;
3677 }
3678
3679 tmp = directory->entries;
3680 while (tmp != NULL)
3681 {
3682 GMenuTreeEntry *entry = tmp->data;
3683 GSList *next = tmp->next;
3684 gboolean delete = FALSE;
3685
3686 /* If adding a new condition to delete here, it has to be added to
3687 * get_still_unallocated_foreach() too */
3688
3689 if (desktop_entry_get_hidden (entry->desktop_entry))
3690 {
3691 menu_verbose ("Deleting %s because Hidden=true\n",
3692 desktop_entry_get_name (entry->desktop_entry));
3693 delete = TRUE;
3694 }
3695
3696 if (!(tree->flags & GMENU_TREE_FLAGS_INCLUDE_NODISPLAY) &&
3697 desktop_entry_get_no_display (entry->desktop_entry))
3698 {
3699 menu_verbose ("Deleting %s because NoDisplay=true\n",
3700 desktop_entry_get_name (entry->desktop_entry));
3701 delete = TRUE;
3702 }
3703
3704 if (!desktop_entry_get_show_in (entry->desktop_entry))
3705 {
3706 menu_verbose ("Deleting %s because OnlyShowIn!=$DESKTOP or NotShowIn=$DESKTOP (with $DESKTOP=${XDG_CURRENT_DESKTOP:-GNOME})\n",
3707 desktop_entry_get_name (entry->desktop_entry));
3708 delete = TRUE;
3709 }
3710
3711 /* No need to filter out based on TryExec since GDesktopAppInfo cannot
3712 * deal with .desktop files with a failed TryExec. */
3713
3714 if (delete)
3715 {
3716 directory->entries = g_slist_delete_link (directory->entries,
3717 tmp);
3718 gmenu_tree_item_unref_and_unset_parent (entry);
3719 }
3720
3721 tmp = next;
3722 }
3723
3724 g_assert (directory->name != NULL);
3725
3726 return directory;
3727 }
3728
3729 static void
3730 process_only_unallocated (GMenuTree *tree,
3731 GMenuTreeDirectory *directory,
3732 DesktopEntrySet *allocated,
3733 DesktopEntrySet *unallocated_used)
3734 {
3735 GSList *tmp;
3736
3737 /* For any directory marked only_unallocated, we have to remove any
3738 * entries that were in fact allocated.
3739 */
3740
3741 if (directory->only_unallocated)
3742 {
3743 tmp = directory->entries;
3744 while (tmp != NULL)
3745 {
3746 GMenuTreeEntry *entry = tmp->data;
3747 GSList *next = tmp->next;
3748
3749 if (desktop_entry_set_lookup (allocated, entry->desktop_file_id))
3750 {
3751 directory->entries = g_slist_delete_link (directory->entries,
3752 tmp);
3753 gmenu_tree_item_unref_and_unset_parent (entry);
3754 }
3755 else
3756 {
3757 desktop_entry_set_add_entry (unallocated_used, entry->desktop_entry, entry->desktop_file_id);
3758 }
3759
3760 tmp = next;
3761 }
3762 }
3763
3764 tmp = directory->subdirs;
3765 while (tmp != NULL)
3766 {
3767 GMenuTreeDirectory *subdir = tmp->data;
3768
3769 process_only_unallocated (tree, subdir, allocated, unallocated_used);
3770
3771 tmp = tmp->next;
3772 }
3773 }
3774
3775 typedef struct
3776 {
3777 GMenuTree *tree;
3778 DesktopEntrySet *allocated;
3779 DesktopEntrySet *unallocated_used;
3780 DesktopEntrySet *still_unallocated;
3781 } GetStillUnallocatedForeachData;
3782
3783 static void
3784 get_still_unallocated_foreach (const char *file_id,
3785 DesktopEntry *entry,
3786 GetStillUnallocatedForeachData *data)
3787 {
3788 if (desktop_entry_set_lookup (data->allocated, file_id))
3789 return;
3790
3791 if (desktop_entry_set_lookup (data->unallocated_used, file_id))
3792 return;
3793
3794 /* Same rules than at the end of process_layout() */
3795 if (desktop_entry_get_hidden (entry))
3796 return;
3797
3798 if (!(data->tree->flags & GMENU_TREE_FLAGS_INCLUDE_NODISPLAY) &&
3799 desktop_entry_get_no_display (entry))
3800 return;
3801
3802 if (!desktop_entry_get_show_in (entry))
3803 return;
3804
3805 desktop_entry_set_add_entry (data->still_unallocated, entry, file_id);
3806 }
3807
3808 static void preprocess_layout_info (GMenuTree *tree,
3809 GMenuTreeDirectory *directory);
3810
3811 static GSList *
3812 get_layout_info (GMenuTreeDirectory *directory,
3813 gboolean *is_default_layout)
3814 {
3815 GMenuTreeDirectory *iter;
3816
3817 if (directory->layout_info != NULL)
3818 {
3819 if (is_default_layout)
3820 {
3821 *is_default_layout = FALSE;
3822 }
3823 return directory->layout_info;
3824 }
3825
3826 /* Even if there's no layout information at all, the result will be an
3827 * implicit default layout */
3828 if (is_default_layout)
3829 {
3830 *is_default_layout = TRUE;
3831 }
3832
3833 iter = directory;
3834 while (iter != NULL)
3835 {
3836 /* FIXME: this is broken: we might skip real parent in the
3837 * XML structure, that are hidden because of inlining. */
3838 if (iter->default_layout_info != NULL)
3839 {
3840 return iter->default_layout_info;
3841 }
3842
3843 iter = GMENU_TREE_ITEM (iter)->parent;
3844 }
3845
3846 return NULL;
3847 }
3848
3849 static void
3850 get_values_with_defaults (MenuLayoutNode *node,
3851 MenuLayoutValues *layout_values,
3852 MenuLayoutValues *default_layout_values)
3853 {
3854 menu_layout_node_menuname_get_values (node, layout_values);
3855
3856 if (!(layout_values->mask & MENU_LAYOUT_VALUES_SHOW_EMPTY))
3857 layout_values->show_empty = default_layout_values->show_empty;
3858
3859 if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_MENUS))
3860 layout_values->inline_menus = default_layout_values->inline_menus;
3861
3862 if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_LIMIT))
3863 layout_values->inline_limit = default_layout_values->inline_limit;
3864
3865 if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_HEADER))
3866 layout_values->inline_header = default_layout_values->inline_header;
3867
3868 if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_ALIAS))
3869 layout_values->inline_alias = default_layout_values->inline_alias;
3870 }
3871
3872 static guint
3873 get_real_subdirs_len (GMenuTreeDirectory *directory)
3874 {
3875 guint len;
3876 GSList *tmp;
3877
3878 len = 0;
3879
3880 tmp = directory->subdirs;
3881 while (tmp != NULL)
3882 {
3883 GMenuTreeDirectory *subdir = tmp->data;
3884
3885 tmp = tmp->next;
3886
3887 if (subdir->will_inline_header != G_MAXUINT16)
3888 {
3889 len += get_real_subdirs_len (subdir) + g_slist_length (subdir->entries) + 1;
3890 }
3891 else
3892 len += 1;
3893 }
3894
3895 return len;
3896 }
3897
3898 static void
3899 preprocess_layout_info_subdir_helper (GMenuTree *tree,
3900 GMenuTreeDirectory *directory,
3901 GMenuTreeDirectory *subdir,
3902 MenuLayoutValues *layout_values,
3903 gboolean *contents_added,
3904 gboolean *should_remove)
3905 {
3906 preprocess_layout_info (tree, subdir);
3907
3908 *should_remove = FALSE;
3909 *contents_added = FALSE;
3910
3911 if (subdir->subdirs == NULL && subdir->entries == NULL)
3912 {
3913 if (!(tree->flags & GMENU_TREE_FLAGS_SHOW_EMPTY) &&
3914 !layout_values->show_empty)
3915 {
3916 menu_verbose ("Not showing empty menu '%s'\n", subdir->name);
3917 *should_remove = TRUE;
3918 }
3919 }
3920
3921 else if (layout_values->inline_menus)
3922 {
3923 guint real_subdirs_len;
3924
3925 real_subdirs_len = get_real_subdirs_len (subdir);
3926
3927 if (layout_values->inline_alias &&
3928 real_subdirs_len + g_slist_length (subdir->entries) == 1)
3929 {
3930 GMenuTreeAlias *alias;
3931 GMenuTreeItem *item;
3932 GSList *list;
3933
3934 if (subdir->subdirs != NULL)
3935 list = subdir->subdirs;
3936 else
3937 list = subdir->entries;
3938
3939 item = GMENU_TREE_ITEM (list->data);
3940
3941 menu_verbose ("Inline aliasing '%s' to '%s'\n",
3942 item->type == GMENU_TREE_ITEM_ENTRY ?
3943 g_app_info_get_name (G_APP_INFO (gmenu_tree_entry_get_app_info (GMENU_TREE_ENTRY (item)))) :
3944 (item->type == GMENU_TREE_ITEM_DIRECTORY ?
3945 gmenu_tree_directory_get_name (GMENU_TREE_DIRECTORY (item)) :
3946 gmenu_tree_directory_get_name (GMENU_TREE_ALIAS (item)->directory)),
3947 subdir->name);
3948
3949 alias = gmenu_tree_alias_new (directory, subdir, item);
3950
3951 g_slist_foreach (list,
3952 (GFunc) gmenu_tree_item_unref_and_unset_parent,
3953 NULL);
3954 g_slist_free (list);
3955 subdir->subdirs = NULL;
3956 subdir->entries = NULL;
3957
3958 if (item->type == GMENU_TREE_ITEM_DIRECTORY)
3959 directory->subdirs = g_slist_append (directory->subdirs, alias);
3960 else
3961 directory->entries = g_slist_append (directory->entries, alias);
3962
3963 *contents_added = TRUE;
3964 *should_remove = TRUE;
3965 }
3966
3967 else if (layout_values->inline_limit == 0 ||
3968 layout_values->inline_limit >= real_subdirs_len + g_slist_length (subdir->entries))
3969 {
3970 if (layout_values->inline_header)
3971 {
3972 menu_verbose ("Creating inline header with name '%s'\n", subdir->name);
3973 /* we're limited to 16-bits to spare some memory; if the limit is
3974 * higher than that (would be crazy), we just consider it's
3975 * unlimited */
3976 if (layout_values->inline_limit < G_MAXUINT16)
3977 subdir->will_inline_header = layout_values->inline_limit;
3978 else
3979 subdir->will_inline_header = 0;
3980 }
3981 else
3982 {
3983 g_slist_foreach (subdir->subdirs,
3984 (GFunc) gmenu_tree_item_set_parent,
3985 directory);
3986 directory->subdirs = g_slist_concat (directory->subdirs,
3987 subdir->subdirs);
3988 subdir->subdirs = NULL;
3989
3990 g_slist_foreach (subdir->entries,
3991 (GFunc) gmenu_tree_item_set_parent,
3992 directory);
3993 directory->entries = g_slist_concat (directory->entries,
3994 subdir->entries);
3995 subdir->entries = NULL;
3996
3997 *contents_added = TRUE;
3998 *should_remove = TRUE;
3999 }
4000
4001 menu_verbose ("Inlining directory contents of '%s' to '%s'\n",
4002 subdir->name, directory->name);
4003 }
4004 }
4005 }
4006
4007 static void
4008 preprocess_layout_info (GMenuTree *tree,
4009 GMenuTreeDirectory *directory)
4010 {
4011 GSList *tmp;
4012 GSList *layout_info;
4013 gboolean using_default_layout;
4014 GSList *last_subdir;
4015 gboolean strip_duplicates;
4016 gboolean contents_added;
4017 gboolean should_remove;
4018 GSList *subdirs_sentinel;
4019
4020 /* Note: we need to preprocess all menus, even if the layout mask for a menu
4021 * is MENU_LAYOUT_VALUES_NONE: in this case, we need to remove empty menus;
4022 * and the layout mask can be different for a submenu anyway */
4023
4024 menu_verbose ("Processing menu layout inline hints for %s\n", directory->name);
4025 g_assert (!directory->preprocessed);
4026
4027 strip_duplicates = FALSE;
4028 /* we use last_subdir to track the last non-inlined subdirectory */
4029 last_subdir = g_slist_last (directory->subdirs);
4030
4031 /*
4032 * First process subdirectories with explicit layout
4033 */
4034 layout_info = get_layout_info (directory, &using_default_layout);
4035 tmp = layout_info;
4036 /* see comment below about Menuname to understand why we leave the loop if
4037 * last_subdir is NULL */
4038 while (tmp != NULL && last_subdir != NULL)
4039 {
4040 MenuLayoutNode *node = tmp->data;
4041 MenuLayoutValues layout_values;
4042 const char *name;
4043 GMenuTreeDirectory *subdir;
4044 GSList *subdir_l;
4045
4046 tmp = tmp->next;
4047
4048 /* only Menuname nodes are relevant here */
4049 if (menu_layout_node_get_type (node) != MENU_LAYOUT_NODE_MENUNAME)
4050 continue;
4051
4052 get_values_with_defaults (node,
4053 &layout_values,
4054 &directory->default_layout_values);
4055
4056 /* find the subdirectory that is affected by those attributes */
4057 name = menu_layout_node_get_content (node);
4058 subdir = NULL;
4059 subdir_l = directory->subdirs;
4060 while (subdir_l != NULL)
4061 {
4062 subdir = subdir_l->data;
4063
4064 if (!strcmp (subdir->name, name))
4065 break;
4066
4067 subdir = NULL;
4068 subdir_l = subdir_l->next;
4069
4070 /* We do not want to use Menuname on a menu that appeared via
4071 * inlining: without inlining, the Menuname wouldn't have matched
4072 * anything, and we want to keep the same behavior.
4073 * Unless the layout is a default layout, in which case the Menuname
4074 * does match the subdirectory. */
4075 if (!using_default_layout && subdir_l == last_subdir)
4076 {
4077 subdir_l = NULL;
4078 break;
4079 }
4080 }
4081
4082 if (subdir == NULL)
4083 continue;
4084
4085 preprocess_layout_info_subdir_helper (tree, directory,
4086 subdir, &layout_values,
4087 &contents_added, &should_remove);
4088 strip_duplicates = strip_duplicates || contents_added;
4089 if (should_remove)
4090 {
4091 if (last_subdir == subdir_l)
4092 {
4093 /* we need to recompute last_subdir since we'll remove it from
4094 * the list */
4095 GSList *buf;
4096
4097 if (subdir_l == directory->subdirs)
4098 last_subdir = NULL;
4099 else
4100 {
4101 buf = directory->subdirs;
4102 while (buf != NULL && buf->next != subdir_l)
4103 buf = buf->next;
4104 last_subdir = buf;
4105 }
4106 }
4107
4108 directory->subdirs = g_slist_remove (directory->subdirs, subdir);
4109 gmenu_tree_item_unref_and_unset_parent (GMENU_TREE_ITEM (subdir));
4110 }
4111 }
4112
4113 /*
4114 * Now process the subdirectories with no explicit layout
4115 */
4116 /* this is bogus data, but we just need the pointer anyway */
4117 subdirs_sentinel = g_slist_prepend (directory->subdirs, PACKAGE);
4118 directory->subdirs = subdirs_sentinel;
4119
4120 tmp = directory->subdirs;
4121 while (tmp->next != NULL)
4122 {
4123 GMenuTreeDirectory *subdir = tmp->next->data;
4124
4125 if (subdir->preprocessed)
4126 {
4127 tmp = tmp->next;
4128 continue;
4129 }
4130
4131 preprocess_layout_info_subdir_helper (tree, directory,
4132 subdir, &directory->default_layout_values,
4133 &contents_added, &should_remove);
4134 strip_duplicates = strip_duplicates || contents_added;
4135 if (should_remove)
4136 {
4137 tmp = g_slist_delete_link (tmp, tmp->next);
4138 gmenu_tree_item_unref_and_unset_parent (GMENU_TREE_ITEM (subdir));
4139 }
4140 else
4141 tmp = tmp->next;
4142 }
4143
4144 /* remove the sentinel */
4145 directory->subdirs = g_slist_delete_link (directory->subdirs,
4146 directory->subdirs);
4147
4148 /*
4149 * Finally, remove duplicates if needed
4150 */
4151 if (strip_duplicates)
4152 {
4153 /* strip duplicate entries; there should be no duplicate directories */
4154 directory->entries = g_slist_sort (directory->entries,
4155 (GCompareFunc) gmenu_tree_entry_compare_by_id);
4156 tmp = directory->entries;
4157 while (tmp != NULL && tmp->next != NULL)
4158 {
4159 GMenuTreeItem *a = tmp->data;
4160 GMenuTreeItem *b = tmp->next->data;
4161
4162 if (a->type == GMENU_TREE_ITEM_ALIAS)
4163 a = GMENU_TREE_ALIAS (a)->aliased_item;
4164
4165 if (b->type == GMENU_TREE_ITEM_ALIAS)
4166 b = GMENU_TREE_ALIAS (b)->aliased_item;
4167
4168 if (strcmp (GMENU_TREE_ENTRY (a)->desktop_file_id,
4169 GMENU_TREE_ENTRY (b)->desktop_file_id) == 0)
4170 {
4171 tmp = g_slist_delete_link (tmp, tmp->next);
4172 gmenu_tree_item_unref (b);
4173 }
4174 else
4175 tmp = tmp->next;
4176 }
4177 }
4178
4179 directory->preprocessed = TRUE;
4180 }
4181
4182 static void process_layout_info (GMenuTree *tree,
4183 GMenuTreeDirectory *directory);
4184
4185 static void
4186 check_pending_separator (GMenuTreeDirectory *directory)
4187 {
4188 if (directory->layout_pending_separator)
4189 {
4190 menu_verbose ("Adding pending separator in '%s'\n", directory->name);
4191
4192 directory->contents = g_slist_append (directory->contents,
4193 gmenu_tree_separator_new (directory));
4194 directory->layout_pending_separator = FALSE;
4195 }
4196 }
4197
4198 static void
4199 merge_alias (GMenuTree *tree,
4200 GMenuTreeDirectory *directory,
4201 GMenuTreeAlias *alias)
4202 {
4203 menu_verbose ("Merging alias '%s' in directory '%s'\n",
4204 alias->directory->name, directory->name);
4205
4206 if (alias->aliased_item->type == GMENU_TREE_ITEM_DIRECTORY)
4207 {
4208 process_layout_info (tree, GMENU_TREE_DIRECTORY (alias->aliased_item));
4209 }
4210
4211 check_pending_separator (directory);
4212
4213 directory->contents = g_slist_append (directory->contents,
4214 gmenu_tree_item_ref (alias));
4215 }
4216
4217 static void
4218 merge_subdir (GMenuTree *tree,
4219 GMenuTreeDirectory *directory,
4220 GMenuTreeDirectory *subdir)
4221 {
4222 menu_verbose ("Merging subdir '%s' in directory '%s'\n",
4223 subdir->name, directory->name);
4224
4225 process_layout_info (tree, subdir);
4226
4227 check_pending_separator (directory);
4228
4229 if (subdir->will_inline_header == 0 ||
4230 (subdir->will_inline_header != G_MAXUINT16 &&
4231 g_slist_length (subdir->contents) <= subdir->will_inline_header))
4232 {
4233 GMenuTreeHeader *header;
4234
4235 header = gmenu_tree_header_new (directory, subdir);
4236 directory->contents = g_slist_append (directory->contents, header);
4237
4238 g_slist_foreach (subdir->contents,
4239 (GFunc) gmenu_tree_item_set_parent,
4240 directory);
4241 directory->contents = g_slist_concat (directory->contents,
4242 subdir->contents);
4243 subdir->contents = NULL;
4244 subdir->will_inline_header = G_MAXUINT16;
4245
4246 gmenu_tree_item_set_parent (GMENU_TREE_ITEM (subdir), NULL);
4247 }
4248 else
4249 {
4250 directory->contents = g_slist_append (directory->contents,
4251 gmenu_tree_item_ref (subdir));
4252 }
4253 }
4254
4255 static void
4256 merge_subdir_by_name (GMenuTree *tree,
4257 GMenuTreeDirectory *directory,
4258 const char *subdir_name)
4259 {
4260 GSList *tmp;
4261
4262 menu_verbose ("Attempting to merge subdir '%s' in directory '%s'\n",
4263 subdir_name, directory->name);
4264
4265 tmp = directory->subdirs;
4266 while (tmp != NULL)
4267 {
4268 GMenuTreeDirectory *subdir = tmp->data;
4269 GSList *next = tmp->next;
4270
4271 /* if it's an alias, then it cannot be affected by
4272 * the Merge nodes in the layout */
4273 if (GMENU_TREE_ITEM (subdir)->type == GMENU_TREE_ITEM_ALIAS)
4274 continue;
4275
4276 if (!strcmp (subdir->name, subdir_name))
4277 {
4278 directory->subdirs = g_slist_delete_link (directory->subdirs, tmp);
4279 merge_subdir (tree, directory, subdir);
4280 gmenu_tree_item_unref (subdir);
4281 }
4282
4283 tmp = next;
4284 }
4285 }
4286
4287 static void
4288 merge_entry (GMenuTree *tree,
4289 GMenuTreeDirectory *directory,
4290 GMenuTreeEntry *entry)
4291 {
4292 menu_verbose ("Merging entry '%s' in directory '%s'\n",
4293 entry->desktop_file_id, directory->name);
4294
4295 check_pending_separator (directory);
4296 directory->contents = g_slist_append (directory->contents,
4297 gmenu_tree_item_ref (entry));
4298 }
4299
4300 static void
4301 merge_entry_by_id (GMenuTree *tree,
4302 GMenuTreeDirectory *directory,
4303 const char *file_id)
4304 {
4305 GSList *tmp;
4306
4307 menu_verbose ("Attempting to merge entry '%s' in directory '%s'\n",
4308 file_id, directory->name);
4309
4310 tmp = directory->entries;
4311 while (tmp != NULL)
4312 {
4313 GMenuTreeEntry *entry = tmp->data;
4314 GSList *next = tmp->next;
4315
4316 /* if it's an alias, then it cannot be affected by
4317 * the Merge nodes in the layout */
4318 if (GMENU_TREE_ITEM (entry)->type == GMENU_TREE_ITEM_ALIAS)
4319 continue;
4320
4321 if (!strcmp (entry->desktop_file_id, file_id))
4322 {
4323 directory->entries = g_slist_delete_link (directory->entries, tmp);
4324 merge_entry (tree, directory, entry);
4325 gmenu_tree_item_unref (entry);
4326 }
4327
4328 tmp = next;
4329 }
4330 }
4331
4332 static inline gboolean
4333 find_name_in_list (const char *name,
4334 GSList *list)
4335 {
4336 while (list != NULL)
4337 {
4338 if (!strcmp (name, list->data))
4339 return TRUE;
4340
4341 list = list->next;
4342 }
4343
4344 return FALSE;
4345 }
4346
4347 static void
4348 merge_subdirs (GMenuTree *tree,
4349 GMenuTreeDirectory *directory,
4350 GSList *except)
4351 {
4352 GSList *subdirs;
4353 GSList *tmp;
4354
4355 menu_verbose ("Merging subdirs in directory '%s'\n", directory->name);
4356
4357 subdirs = directory->subdirs;
4358 directory->subdirs = NULL;
4359
4360 subdirs = g_slist_sort_with_data (subdirs,
4361 (GCompareDataFunc) gmenu_tree_item_compare,
4362 GINT_TO_POINTER (GMENU_TREE_FLAGS_NONE));
4363
4364 tmp = subdirs;
4365 while (tmp != NULL)
4366 {
4367 GMenuTreeDirectory *subdir = tmp->data;
4368
4369 if (GMENU_TREE_ITEM (subdir)->type == GMENU_TREE_ITEM_ALIAS)
4370 {
4371 merge_alias (tree, directory, GMENU_TREE_ALIAS (subdir));
4372 gmenu_tree_item_unref (subdir);
4373 }
4374 else if (!find_name_in_list (subdir->name, except))
4375 {
4376 merge_subdir (tree, directory, subdir);
4377 gmenu_tree_item_unref (subdir);
4378 }
4379 else
4380 {
4381 menu_verbose ("Not merging directory '%s' yet\n", subdir->name);
4382 directory->subdirs = g_slist_append (directory->subdirs, subdir);
4383 }
4384
4385 tmp = tmp->next;
4386 }
4387
4388 g_slist_free (subdirs);
4389 g_slist_free (except);
4390 }
4391
4392 static void
4393 merge_entries (GMenuTree *tree,
4394 GMenuTreeDirectory *directory,
4395 GSList *except)
4396 {
4397 GSList *entries;
4398 GSList *tmp;
4399
4400 menu_verbose ("Merging entries in directory '%s'\n", directory->name);
4401
4402 entries = directory->entries;
4403 directory->entries = NULL;
4404
4405 entries = g_slist_sort_with_data (entries,
4406 (GCompareDataFunc) gmenu_tree_item_compare,
4407 GINT_TO_POINTER (tree->flags));
4408
4409 tmp = entries;
4410 while (tmp != NULL)
4411 {
4412 GMenuTreeEntry *entry = tmp->data;
4413
4414 if (GMENU_TREE_ITEM (entry)->type == GMENU_TREE_ITEM_ALIAS)
4415 {
4416 merge_alias (tree, directory, GMENU_TREE_ALIAS (entry));
4417 gmenu_tree_item_unref (entry);
4418 }
4419 else if (!find_name_in_list (entry->desktop_file_id, except))
4420 {
4421 merge_entry (tree, directory, entry);
4422 gmenu_tree_item_unref (entry);
4423 }
4424 else
4425 {
4426 menu_verbose ("Not merging entry '%s' yet\n", entry->desktop_file_id);
4427 directory->entries = g_slist_append (directory->entries, entry);
4428 }
4429
4430 tmp = tmp->next;
4431 }
4432
4433 g_slist_free (entries);
4434 g_slist_free (except);
4435 }
4436
4437 static void
4438 merge_subdirs_and_entries (GMenuTree *tree,
4439 GMenuTreeDirectory *directory,
4440 GSList *except_subdirs,
4441 GSList *except_entries)
4442 {
4443 GSList *items;
4444 GSList *tmp;
4445
4446 menu_verbose ("Merging subdirs and entries together in directory %s\n",
4447 directory->name);
4448
4449 items = g_slist_concat (directory->subdirs, directory->entries);
4450
4451 directory->subdirs = NULL;
4452 directory->entries = NULL;
4453
4454 items = g_slist_sort_with_data (items,
4455 (GCompareDataFunc) gmenu_tree_item_compare,
4456 GINT_TO_POINTER (tree->flags));
4457
4458 tmp = items;
4459 while (tmp != NULL)
4460 {
4461 GMenuTreeItem *item = tmp->data;
4462 GMenuTreeItemType type;
4463
4464 type = item->type;
4465
4466 if (type == GMENU_TREE_ITEM_ALIAS)
4467 {
4468 merge_alias (tree, directory, GMENU_TREE_ALIAS (item));
4469 gmenu_tree_item_unref (item);
4470 }
4471 else if (type == GMENU_TREE_ITEM_DIRECTORY)
4472 {
4473 if (!find_name_in_list (GMENU_TREE_DIRECTORY (item)->name, except_subdirs))
4474 {
4475 merge_subdir (tree,
4476 directory,
4477 GMENU_TREE_DIRECTORY (item));
4478 gmenu_tree_item_unref (item);
4479 }
4480 else
4481 {
4482 menu_verbose ("Not merging directory '%s' yet\n",
4483 GMENU_TREE_DIRECTORY (item)->name);
4484 directory->subdirs = g_slist_append (directory->subdirs, item);
4485 }
4486 }
4487 else if (type == GMENU_TREE_ITEM_ENTRY)
4488 {
4489 if (!find_name_in_list (GMENU_TREE_ENTRY (item)->desktop_file_id, except_entries))
4490 {
4491 merge_entry (tree, directory, GMENU_TREE_ENTRY (item));
4492 gmenu_tree_item_unref (item);
4493 }
4494 else
4495 {
4496 menu_verbose ("Not merging entry '%s' yet\n",
4497 GMENU_TREE_ENTRY (item)->desktop_file_id);
4498 directory->entries = g_slist_append (directory->entries, item);
4499 }
4500 }
4501 else
4502 {
4503 g_assert_not_reached ();
4504 }
4505
4506 tmp = tmp->next;
4507 }
4508
4509 g_slist_free (items);
4510 g_slist_free (except_subdirs);
4511 g_slist_free (except_entries);
4512 }
4513
4514 static GSList *
4515 get_subdirs_from_layout_info (GSList *layout_info)
4516 {
4517 GSList *subdirs;
4518 GSList *tmp;
4519
4520 subdirs = NULL;
4521
4522 tmp = layout_info;
4523 while (tmp != NULL)
4524 {
4525 MenuLayoutNode *node = tmp->data;
4526
4527 if (menu_layout_node_get_type (node) == MENU_LAYOUT_NODE_MENUNAME)
4528 {
4529 subdirs = g_slist_append (subdirs,
4530 (char *) menu_layout_node_get_content (node));
4531 }
4532
4533 tmp = tmp->next;
4534 }
4535
4536 return subdirs;
4537 }
4538
4539 static GSList *
4540 get_entries_from_layout_info (GSList *layout_info)
4541 {
4542 GSList *entries;
4543 GSList *tmp;
4544
4545 entries = NULL;
4546
4547 tmp = layout_info;
4548 while (tmp != NULL)
4549 {
4550 MenuLayoutNode *node = tmp->data;
4551
4552 if (menu_layout_node_get_type (node) == MENU_LAYOUT_NODE_FILENAME)
4553 {
4554 entries = g_slist_append (entries,
4555 (char *) menu_layout_node_get_content (node));
4556 }
4557
4558 tmp = tmp->next;
4559 }
4560
4561 return entries;
4562 }
4563
4564 static void
4565 process_layout_info (GMenuTree *tree,
4566 GMenuTreeDirectory *directory)
4567 {
4568 GSList *layout_info;
4569
4570 menu_verbose ("Processing menu layout hints for %s\n", directory->name);
4571
4572 g_slist_foreach (directory->contents,
4573 (GFunc) gmenu_tree_item_unref_and_unset_parent,
4574 NULL);
4575 g_slist_free (directory->contents);
4576 directory->contents = NULL;
4577 directory->layout_pending_separator = FALSE;
4578
4579 layout_info = get_layout_info (directory, NULL);
4580
4581 if (layout_info == NULL)
4582 {
4583 merge_subdirs (tree, directory, NULL);
4584 merge_entries (tree, directory, NULL);
4585 }
4586 else
4587 {
4588 GSList *tmp;
4589
4590 tmp = layout_info;
4591 while (tmp != NULL)
4592 {
4593 MenuLayoutNode *node = tmp->data;
4594
4595 switch (menu_layout_node_get_type (node))
4596 {
4597 case MENU_LAYOUT_NODE_MENUNAME:
4598 merge_subdir_by_name (tree,
4599 directory,
4600 menu_layout_node_get_content (node));
4601 break;
4602
4603 case MENU_LAYOUT_NODE_FILENAME:
4604 merge_entry_by_id (tree,
4605 directory,
4606 menu_layout_node_get_content (node));
4607 break;
4608
4609 case MENU_LAYOUT_NODE_SEPARATOR:
4610 /* Unless explicitly told to show all separators, do not show a
4611 * separator at the beginning of a menu. Note that we don't add
4612 * the separators now, and instead make it pending. This way, we
4613 * won't show two consecutive separators nor will we show a
4614 * separator at the end of a menu. */
4615 if (tree->flags & GMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS)
4616 {
4617 directory->layout_pending_separator = TRUE;
4618 check_pending_separator (directory);
4619 }
4620 else if (directory->contents)
4621 {
4622 menu_verbose ("Adding a potential separator in '%s'\n",
4623 directory->name);
4624
4625 directory->layout_pending_separator = TRUE;
4626 }
4627 else
4628 {
4629 menu_verbose ("Skipping separator at the beginning of '%s'\n",
4630 directory->name);
4631 }
4632 break;
4633
4634 case MENU_LAYOUT_NODE_MERGE:
4635 switch (menu_layout_node_merge_get_type (node))
4636 {
4637 case MENU_LAYOUT_MERGE_NONE:
4638 break;
4639
4640 case MENU_LAYOUT_MERGE_MENUS:
4641 merge_subdirs (tree,
4642 directory,
4643 get_subdirs_from_layout_info (tmp->next));
4644 break;
4645
4646 case MENU_LAYOUT_MERGE_FILES:
4647 merge_entries (tree,
4648 directory,
4649 get_entries_from_layout_info (tmp->next));
4650 break;
4651
4652 case MENU_LAYOUT_MERGE_ALL:
4653 merge_subdirs_and_entries (tree,
4654 directory,
4655 get_subdirs_from_layout_info (tmp->next),
4656 get_entries_from_layout_info (tmp->next));
4657 break;
4658
4659 default:
4660 g_assert_not_reached ();
4661 break;
4662 }
4663 break;
4664
4665 default:
4666 g_assert_not_reached ();
4667 break;
4668 }
4669
4670 tmp = tmp->next;
4671 }
4672 }
4673
4674 g_slist_foreach (directory->subdirs,
4675 (GFunc) gmenu_tree_item_unref,
4676 NULL);
4677 g_slist_free (directory->subdirs);
4678 directory->subdirs = NULL;
4679
4680 g_slist_foreach (directory->entries,
4681 (GFunc) gmenu_tree_item_unref,
4682 NULL);
4683 g_slist_free (directory->entries);
4684 directory->entries = NULL;
4685
4686 g_slist_foreach (directory->default_layout_info,
4687 (GFunc) menu_layout_node_unref,
4688 NULL);
4689 g_slist_free (directory->default_layout_info);
4690 directory->default_layout_info = NULL;
4691
4692 g_slist_foreach (directory->layout_info,
4693 (GFunc) menu_layout_node_unref,
4694 NULL);
4695 g_slist_free (directory->layout_info);
4696 directory->layout_info = NULL;
4697 }
4698
4699 static void
4700 handle_entries_changed (MenuLayoutNode *layout,
4701 GMenuTree *tree)
4702 {
4703 if (tree->layout == layout)
4704 {
4705 gmenu_tree_force_rebuild (tree);
4706 gmenu_tree_invoke_monitors (tree);
4707 }
4708 }
4709
4710 static void
4711 update_entry_index (GMenuTree *tree,
4712 GMenuTreeDirectory *dir)
4713 {
4714 GMenuTreeIter *iter = gmenu_tree_directory_iter (dir);
4715 GMenuTreeItemType next_type;
4716
4717 while ((next_type = gmenu_tree_iter_next (iter)) != GMENU_TREE_ITEM_INVALID)
4718 {
4719 gpointer item = NULL;
4720
4721 switch (next_type)
4722 {
4723 case GMENU_TREE_ITEM_ENTRY:
4724 {
4725 const char *id;
4726
4727 item = gmenu_tree_iter_get_entry (iter);
4728 id = gmenu_tree_entry_get_desktop_file_id (item);
4729 if (id != NULL)
4730 g_hash_table_insert (tree->entries_by_id, (char*)id, item);
4731 }
4732 break;
4733 case GMENU_TREE_ITEM_DIRECTORY:
4734 {
4735 item = gmenu_tree_iter_get_directory (iter);
4736 update_entry_index (tree, (GMenuTreeDirectory*)item);
4737 }
4738 break;
4739 default:
4740 break;
4741 }
4742 if (item != NULL)
4743 gmenu_tree_item_unref (item);
4744 }
4745
4746 gmenu_tree_iter_unref (iter);
4747 }
4748
4749 static gboolean
4750 gmenu_tree_build_from_layout (GMenuTree *tree,
4751 GError **error)
4752 {
4753 DesktopEntrySet *allocated;
4754
4755 if (tree->root)
4756 return TRUE;
4757
4758 if (!gmenu_tree_load_layout (tree, error))
4759 return FALSE;
4760
4761 menu_verbose ("Building menu tree from layout\n");
4762
4763 allocated = desktop_entry_set_new ();
4764
4765 /* create the menu structure */
4766 tree->root = process_layout (tree,
4767 NULL,
4768 find_menu_child (tree->layout),
4769 allocated);
4770 if (tree->root)
4771 {
4772 DesktopEntrySet *unallocated_used;
4773
4774 unallocated_used = desktop_entry_set_new ();
4775
4776 process_only_unallocated (tree, tree->root, allocated, unallocated_used);
4777 if (tree->flags & GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED)
4778 {
4779 DesktopEntrySet *entry_pool;
4780 DesktopEntrySet *still_unallocated;
4781 GetStillUnallocatedForeachData data;
4782
4783 entry_pool = _entry_directory_list_get_all_desktops (menu_layout_node_menu_get_app_dirs (find_menu_child (tree->layout)));
4784 still_unallocated = desktop_entry_set_new ();
4785
4786 data.tree = tree;
4787 data.allocated = allocated;
4788 data.unallocated_used = unallocated_used;
4789 data.still_unallocated = still_unallocated;
4790
4791 desktop_entry_set_foreach (entry_pool,
4792 (DesktopEntrySetForeachFunc) get_still_unallocated_foreach,
4793 &data);
4794
4795 desktop_entry_set_unref (entry_pool);
4796
4797 desktop_entry_set_foreach (still_unallocated,
4798 (DesktopEntrySetForeachFunc) unallocated_entries_listify_foreach,
4799 tree->root);
4800
4801 desktop_entry_set_unref (still_unallocated);
4802 }
4803
4804 desktop_entry_set_unref (unallocated_used);
4805
4806 /* process the layout info part that can move/remove items:
4807 * inline, show_empty, etc. */
4808 preprocess_layout_info (tree, tree->root);
4809 /* populate the menu structure that we got with the items, and order it
4810 * according to the layout info */
4811 process_layout_info (tree, tree->root);
4812
4813 update_entry_index (tree, tree->root);
4814
4815 menu_layout_node_root_add_entries_monitor (tree->layout,
4816 (MenuLayoutNodeEntriesChangedFunc) handle_entries_changed,
4817 tree);
4818 }
4819
4820 desktop_entry_set_unref (allocated);
4821
4822 return TRUE;
4823 }
4824
4825 static void
4826 gmenu_tree_force_rebuild (GMenuTree *tree)
4827 {
4828 if (tree->root)
4829 {
4830 g_hash_table_remove_all (tree->entries_by_id);
4831 gmenu_tree_item_unref (tree->root);
4832 tree->root = NULL;
4833 tree->loaded = FALSE;
4834
4835 g_assert (tree->layout != NULL);
4836
4837 menu_layout_node_root_remove_entries_monitor (tree->layout,
4838 (MenuLayoutNodeEntriesChangedFunc) handle_entries_changed,
4839 tree);
4840 }
4841 }
4842
4843 GType
4844 gmenu_tree_iter_get_type (void)
4845 {
4846 static GType gtype = G_TYPE_INVALID;
4847 if (gtype == G_TYPE_INVALID)
4848 {
4849 gtype = g_boxed_type_register_static ("GMenuTreeIter",
4850 (GBoxedCopyFunc)gmenu_tree_iter_ref,
4851 (GBoxedFreeFunc)gmenu_tree_iter_unref);
4852 }
4853 return gtype;
4854 }
4855
4856 GType
4857 gmenu_tree_directory_get_type (void)
4858 {
4859 static GType gtype = G_TYPE_INVALID;
4860 if (gtype == G_TYPE_INVALID)
4861 {
4862 gtype = g_boxed_type_register_static ("GMenuTreeDirectory",
4863 (GBoxedCopyFunc)gmenu_tree_item_ref,
4864 (GBoxedFreeFunc)gmenu_tree_item_unref);
4865 }
4866 return gtype;
4867 }
4868
4869 GType
4870 gmenu_tree_entry_get_type (void)
4871 {
4872 static GType gtype = G_TYPE_INVALID;
4873 if (gtype == G_TYPE_INVALID)
4874 {
4875 gtype = g_boxed_type_register_static ("GMenuTreeEntry",
4876 (GBoxedCopyFunc)gmenu_tree_item_ref,
4877 (GBoxedFreeFunc)gmenu_tree_item_unref);
4878 }
4879 return gtype;
4880 }
4881
4882 GType
4883 gmenu_tree_separator_get_type (void)
4884 {
4885 static GType gtype = G_TYPE_INVALID;
4886 if (gtype == G_TYPE_INVALID)
4887 {
4888 gtype = g_boxed_type_register_static ("GMenuTreeSeparator",
4889 (GBoxedCopyFunc)gmenu_tree_item_ref,
4890 (GBoxedFreeFunc)gmenu_tree_item_unref);
4891 }
4892 return gtype;
4893 }
4894
4895 GType
4896 gmenu_tree_header_get_type (void)
4897 {
4898 static GType gtype = G_TYPE_INVALID;
4899 if (gtype == G_TYPE_INVALID)
4900 {
4901 gtype = g_boxed_type_register_static ("GMenuTreeHeader",
4902 (GBoxedCopyFunc)gmenu_tree_item_ref,
4903 (GBoxedFreeFunc)gmenu_tree_item_unref);
4904 }
4905 return gtype;
4906 }
4907
4908 GType
4909 gmenu_tree_alias_get_type (void)
4910 {
4911 static GType gtype = G_TYPE_INVALID;
4912 if (gtype == G_TYPE_INVALID)
4913 {
4914 gtype = g_boxed_type_register_static ("GMenuTreeAlias",
4915 (GBoxedCopyFunc)gmenu_tree_item_ref,
4916 (GBoxedFreeFunc)gmenu_tree_item_unref);
4917 }
4918 return gtype;
4919 }
4920
4921 GType
4922 gmenu_tree_flags_get_type (void)
4923 {
4924 static GType enum_type_id = 0;
4925 if (G_UNLIKELY (!enum_type_id))
4926 {
4927 static const GFlagsValue values[] = {
4928 { GMENU_TREE_FLAGS_NONE, "GMENU_TREE_FLAGS_NONE", "none" },
4929 { GMENU_TREE_FLAGS_INCLUDE_EXCLUDED, "GMENU_TREE_FLAGS_INCLUDE_EXCLUDED", "include-excluded" },
4930 { GMENU_TREE_FLAGS_SHOW_EMPTY, "GMENU_TREE_FLAGS_SHOW_EMPTY", "show-empty" },
4931 { GMENU_TREE_FLAGS_INCLUDE_NODISPLAY, "GMENU_TREE_FLAGS_INCLUDE_NODISPLAY", "include-nodisplay" },
4932 { GMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS, "GMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS", "show-all-separators" },
4933 { GMENU_TREE_FLAGS_SORT_DISPLAY_NAME, "GMENU_TREE_FLAGS_SORT_DISPLAY_NAME", "sort-display-name" },
4934 { GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED, "GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED,", "include-unallocated" },
4935 { 0, NULL, NULL }
4936 };
4937 enum_type_id = g_flags_register_static ("GMenuTreeFlags", values);
4938 }
4939 return enum_type_id;
4940 }
0 /*
1 * Copyright (C) 2004, 2011 Red Hat, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 02111-1307, USA.
17 */
18
19 #ifndef __GMENU_TREE_H__
20 #define __GMENU_TREE_H__
21
22 #ifndef GMENU_I_KNOW_THIS_IS_UNSTABLE
23 #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"
24 #endif
25
26 #include <gio/gdesktopappinfo.h>
27
28 G_BEGIN_DECLS
29
30 #define GMENU_TYPE_TREE (gmenu_tree_get_type ())
31 #define GMENU_TREE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GMENU_TYPE_TREE, GMenuTree))
32 #define GMENU_TREE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GMENU_TYPE_TREE, GMenuTreeClass))
33 #define GMENU_IS_TREE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GMENU_TYPE_TREE))
34 #define GMENU_IS_TREE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GMENU_TYPE_TREE))
35 #define GMENU_TREE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DESKTOP_APP_INFO, GMenuTreeClass))
36
37 typedef struct _GMenuTree GMenuTree;
38 typedef struct _GMenuTreeClass GMenuTreeClass;
39
40 struct _GMenuTreeClass
41 {
42 GObjectClass parent_class;
43 };
44
45 GType gmenu_tree_get_type (void) G_GNUC_CONST;
46
47 typedef struct GMenuTreeIter GMenuTreeIter;
48 typedef struct GMenuTreeDirectory GMenuTreeDirectory;
49 typedef struct GMenuTreeEntry GMenuTreeEntry;
50 typedef struct GMenuTreeSeparator GMenuTreeSeparator;
51 typedef struct GMenuTreeHeader GMenuTreeHeader;
52 typedef struct GMenuTreeAlias GMenuTreeAlias;
53
54 typedef enum
55 {
56 GMENU_TREE_ITEM_INVALID = 0,
57 GMENU_TREE_ITEM_DIRECTORY,
58 GMENU_TREE_ITEM_ENTRY,
59 GMENU_TREE_ITEM_SEPARATOR,
60 GMENU_TREE_ITEM_HEADER,
61 GMENU_TREE_ITEM_ALIAS
62 } GMenuTreeItemType;
63
64 GType gmenu_tree_iter_get_type (void);
65
66 /* Explicitly skip item, it's a "hidden" base class */
67 GType gmenu_tree_directory_get_type (void);
68 GType gmenu_tree_entry_get_type (void);
69 GType gmenu_tree_separator_get_type (void);
70 GType gmenu_tree_header_get_type (void);
71 GType gmenu_tree_alias_get_type (void);
72
73 typedef enum
74 {
75 GMENU_TREE_FLAGS_NONE = 0,
76 GMENU_TREE_FLAGS_INCLUDE_EXCLUDED = 1 << 0,
77 GMENU_TREE_FLAGS_INCLUDE_NODISPLAY = 1 << 1,
78 GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED = 1 << 2,
79 /* leave some space for more include flags */
80 GMENU_TREE_FLAGS_SHOW_EMPTY = 1 << 8,
81 GMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS = 1 << 9,
82 /* leave some space for more show flags */
83 GMENU_TREE_FLAGS_SORT_DISPLAY_NAME = 1 << 16
84 } GMenuTreeFlags;
85 GType gmenu_tree_flags_get_type (void);
86 #define GMENU_TYPE_TREE_FLAGS (gmenu_tree_flags_get_type ())
87
88 GMenuTree *gmenu_tree_new (const char *menu_basename,
89 GMenuTreeFlags flags);
90
91 GMenuTree *gmenu_tree_new_for_path (const char *menu_path,
92 GMenuTreeFlags flags);
93
94 gboolean gmenu_tree_load_sync (GMenuTree *tree,
95 GError **error);
96
97 const char *gmenu_tree_get_canonical_menu_path (GMenuTree *tree);
98 GMenuTreeDirectory *gmenu_tree_get_root_directory (GMenuTree *tree);
99 GMenuTreeDirectory *gmenu_tree_get_directory_from_path (GMenuTree *tree,
100 const char *path);
101 GMenuTreeEntry *gmenu_tree_get_entry_by_id (GMenuTree *tree,
102 const char *id);
103
104 gpointer gmenu_tree_item_ref (gpointer item);
105 void gmenu_tree_item_unref (gpointer item);
106
107 GMenuTreeDirectory *gmenu_tree_directory_get_parent (GMenuTreeDirectory *directory);
108 const char *gmenu_tree_directory_get_name (GMenuTreeDirectory *directory);
109 const char *gmenu_tree_directory_get_generic_name (GMenuTreeDirectory *directory);
110 const char *gmenu_tree_directory_get_comment (GMenuTreeDirectory *directory);
111 GIcon *gmenu_tree_directory_get_icon (GMenuTreeDirectory *directory);
112 const char *gmenu_tree_directory_get_desktop_file_path (GMenuTreeDirectory *directory);
113 const char *gmenu_tree_directory_get_menu_id (GMenuTreeDirectory *directory);
114 GMenuTree *gmenu_tree_directory_get_tree (GMenuTreeDirectory *directory);
115
116 gboolean gmenu_tree_directory_get_is_nodisplay (GMenuTreeDirectory *directory);
117
118 GMenuTreeIter *gmenu_tree_directory_iter (GMenuTreeDirectory *directory);
119
120 GMenuTreeIter *gmenu_tree_iter_ref (GMenuTreeIter *iter);
121 void gmenu_tree_iter_unref (GMenuTreeIter *iter);
122
123 GMenuTreeItemType gmenu_tree_iter_next (GMenuTreeIter *iter);
124 GMenuTreeDirectory *gmenu_tree_iter_get_directory (GMenuTreeIter *iter);
125 GMenuTreeEntry *gmenu_tree_iter_get_entry (GMenuTreeIter *iter);
126 GMenuTreeHeader *gmenu_tree_iter_get_header (GMenuTreeIter *iter);
127 GMenuTreeAlias *gmenu_tree_iter_get_alias (GMenuTreeIter *iter);
128 GMenuTreeSeparator *gmenu_tree_iter_get_separator (GMenuTreeIter *iter);
129
130 char *gmenu_tree_directory_make_path (GMenuTreeDirectory *directory,
131 GMenuTreeEntry *entry);
132
133
134 GDesktopAppInfo *gmenu_tree_entry_get_app_info (GMenuTreeEntry *entry);
135 GMenuTreeDirectory *gmenu_tree_entry_get_parent (GMenuTreeEntry *entry);
136 GMenuTree *gmenu_tree_entry_get_tree (GMenuTreeEntry *entry);
137
138 const char *gmenu_tree_entry_get_desktop_file_path (GMenuTreeEntry *entry);
139 const char *gmenu_tree_entry_get_desktop_file_id (GMenuTreeEntry *entry);
140
141 gboolean gmenu_tree_entry_get_is_nodisplay_recurse (GMenuTreeEntry *entry);
142 gboolean gmenu_tree_entry_get_is_excluded (GMenuTreeEntry *entry);
143 gboolean gmenu_tree_entry_get_is_unallocated (GMenuTreeEntry *entry);
144
145 GMenuTreeDirectory *gmenu_tree_header_get_directory (GMenuTreeHeader *header);
146 GMenuTree *gmenu_tree_header_get_tree (GMenuTreeHeader *header);
147 GMenuTreeDirectory *gmenu_tree_header_get_parent (GMenuTreeHeader *header);
148
149 GMenuTreeDirectory *gmenu_tree_alias_get_directory (GMenuTreeAlias *alias);
150 GMenuTreeItemType gmenu_tree_alias_get_aliased_item_type (GMenuTreeAlias *alias);
151 GMenuTreeDirectory *gmenu_tree_alias_get_aliased_directory (GMenuTreeAlias *alias);
152 GMenuTreeEntry *gmenu_tree_alias_get_aliased_entry (GMenuTreeAlias *alias);
153 GMenuTree *gmenu_tree_alias_get_tree (GMenuTreeAlias *alias);
154 GMenuTreeDirectory *gmenu_tree_alias_get_parent (GMenuTreeAlias *alias);
155
156 GMenuTree *gmenu_tree_separator_get_tree (GMenuTreeSeparator *separator);
157 GMenuTreeDirectory *gmenu_tree_separator_get_parent (GMenuTreeSeparator *separator);
158
159 G_END_DECLS
160
161 #endif /* __GMENU_TREE_H__ */
0 prefix=@prefix@
1 exec_prefix=@exec_prefix@
2 libdir=@libdir@
3 includedir=@includedir@
4
5 Name: libcinnamon-menu
6 Description: Desktop Menu Specification Implementation
7 Requires: gio-unix-2.0
8 Version: @VERSION@
9 Libs: ${pc_top_builddir}/${pcfiledir}/libcinnamon-menu-3.la
10 Cflags: -I${pc_top_builddir}/${pcfiledir}
0 prefix=@prefix@
1 exec_prefix=@exec_prefix@
2 libdir=@libdir@
3 includedir=@includedir@
4
5 Name: libcinnamon-menu
6 Description: Desktop Menu Specification Implementation
7 Requires: gio-unix-2.0
8 Version: @VERSION@
9 Libs: -L${libdir} -lcinnamon-menu-3
10 Cflags: -I${includedir}/cinnamon-menus-3.0
0 /* Menu layout in-memory data structure (a custom "DOM tree") */
1
2 /*
3 * Copyright (C) 2002 - 2004 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21 #include <config.h>
22
23 #include "menu-layout.h"
24
25 #include <string.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <errno.h>
30
31 #include "canonicalize.h"
32 #include "entry-directories.h"
33 #include "menu-util.h"
34
35 typedef struct MenuLayoutNodeMenu MenuLayoutNodeMenu;
36 typedef struct MenuLayoutNodeRoot MenuLayoutNodeRoot;
37 typedef struct MenuLayoutNodeLegacyDir MenuLayoutNodeLegacyDir;
38 typedef struct MenuLayoutNodeMergeFile MenuLayoutNodeMergeFile;
39 typedef struct MenuLayoutNodeDefaultLayout MenuLayoutNodeDefaultLayout;
40 typedef struct MenuLayoutNodeMenuname MenuLayoutNodeMenuname;
41 typedef struct MenuLayoutNodeMerge MenuLayoutNodeMerge;
42
43 struct MenuLayoutNode
44 {
45 /* Node lists are circular, for length-one lists
46 * prev/next point back to the node itself.
47 */
48 MenuLayoutNode *prev;
49 MenuLayoutNode *next;
50 MenuLayoutNode *parent;
51 MenuLayoutNode *children;
52
53 char *content;
54
55 guint refcount : 20;
56 guint type : 7;
57 };
58
59 struct MenuLayoutNodeRoot
60 {
61 MenuLayoutNode node;
62
63 char *basedir;
64 char *name;
65
66 GMainContext *main_context;
67
68 GSList *monitors;
69 GSource *monitors_idle_handler;
70 };
71
72 struct MenuLayoutNodeMenu
73 {
74 MenuLayoutNode node;
75
76 MenuLayoutNode *name_node; /* cache of the <Name> node */
77
78 EntryDirectoryList *app_dirs;
79 EntryDirectoryList *dir_dirs;
80 };
81
82 struct MenuLayoutNodeLegacyDir
83 {
84 MenuLayoutNode node;
85
86 char *prefix;
87 };
88
89 struct MenuLayoutNodeMergeFile
90 {
91 MenuLayoutNode node;
92
93 MenuMergeFileType type;
94 };
95
96 struct MenuLayoutNodeDefaultLayout
97 {
98 MenuLayoutNode node;
99
100 MenuLayoutValues layout_values;
101 };
102
103 struct MenuLayoutNodeMenuname
104 {
105 MenuLayoutNode node;
106
107 MenuLayoutValues layout_values;
108 };
109
110 struct MenuLayoutNodeMerge
111 {
112 MenuLayoutNode node;
113
114 MenuLayoutMergeType merge_type;
115 };
116
117 typedef struct
118 {
119 MenuLayoutNodeEntriesChangedFunc callback;
120 gpointer user_data;
121 } MenuLayoutNodeEntriesMonitor;
122
123
124 static inline MenuLayoutNode *
125 node_next (MenuLayoutNode *node)
126 {
127 /* root nodes (no parent) never have siblings */
128 if (node->parent == NULL)
129 return NULL;
130
131 /* circular list */
132 if (node->next == node->parent->children)
133 return NULL;
134
135 return node->next;
136 }
137
138 static gboolean
139 menu_layout_invoke_monitors (MenuLayoutNodeRoot *nr)
140 {
141 GSList *tmp;
142
143 g_assert (nr->node.type == MENU_LAYOUT_NODE_ROOT);
144
145 nr->monitors_idle_handler = NULL;
146
147 tmp = nr->monitors;
148 while (tmp != NULL)
149 {
150 MenuLayoutNodeEntriesMonitor *monitor = tmp->data;
151 GSList *next = tmp->next;
152
153 monitor->callback ((MenuLayoutNode *) nr, monitor->user_data);
154
155 tmp = next;
156 }
157
158 return FALSE;
159 }
160
161 static void
162 handle_entry_directory_changed (EntryDirectory *dir,
163 MenuLayoutNode *node)
164 {
165 MenuLayoutNodeRoot *nr;
166
167 g_assert (node->type == MENU_LAYOUT_NODE_MENU);
168
169 nr = (MenuLayoutNodeRoot *) menu_layout_node_get_root (node);
170
171 if (nr->monitors_idle_handler == NULL)
172 {
173 nr->monitors_idle_handler = g_idle_source_new ();
174 g_source_set_callback (nr->monitors_idle_handler,
175 (GSourceFunc) menu_layout_invoke_monitors, nr, NULL);
176 g_source_attach (nr->monitors_idle_handler, nr->main_context);
177 g_source_unref (nr->monitors_idle_handler);
178 }
179 }
180
181 static void
182 remove_entry_directory_list (MenuLayoutNodeMenu *nm,
183 EntryDirectoryList **dirs)
184 {
185 if (*dirs)
186 {
187 entry_directory_list_remove_monitors (*dirs,
188 (EntryDirectoryChangedFunc) handle_entry_directory_changed,
189 nm);
190 entry_directory_list_unref (*dirs);
191 *dirs = NULL;
192 }
193 }
194
195 MenuLayoutNode *
196 menu_layout_node_ref (MenuLayoutNode *node)
197 {
198 g_return_val_if_fail (node != NULL, NULL);
199
200 node->refcount += 1;
201
202 return node;
203 }
204
205 void
206 menu_layout_node_unref (MenuLayoutNode *node)
207 {
208 g_return_if_fail (node != NULL);
209 g_return_if_fail (node->refcount > 0);
210
211 node->refcount -= 1;
212 if (node->refcount == 0)
213 {
214 MenuLayoutNode *iter;
215
216 iter = node->children;
217 while (iter != NULL)
218 {
219 MenuLayoutNode *next = node_next (iter);
220
221 menu_layout_node_unref (iter);
222
223 iter = next;
224 }
225
226 if (node->type == MENU_LAYOUT_NODE_MENU)
227 {
228 MenuLayoutNodeMenu *nm = (MenuLayoutNodeMenu *) node;
229
230 if (nm->name_node)
231 menu_layout_node_unref (nm->name_node);
232
233 remove_entry_directory_list (nm, &nm->app_dirs);
234 remove_entry_directory_list (nm, &nm->dir_dirs);
235 }
236 else if (node->type == MENU_LAYOUT_NODE_LEGACY_DIR)
237 {
238 MenuLayoutNodeLegacyDir *legacy = (MenuLayoutNodeLegacyDir *) node;
239
240 g_free (legacy->prefix);
241 }
242 else if (node->type == MENU_LAYOUT_NODE_ROOT)
243 {
244 MenuLayoutNodeRoot *nr = (MenuLayoutNodeRoot*) node;
245
246 g_slist_foreach (nr->monitors, (GFunc) g_free, NULL);
247 g_slist_free (nr->monitors);
248
249 if (nr->monitors_idle_handler != NULL)
250 g_source_destroy (nr->monitors_idle_handler);
251 nr->monitors_idle_handler = NULL;
252
253 if (nr->main_context != NULL)
254 g_main_context_unref (nr->main_context);
255 nr->main_context = NULL;
256
257 g_free (nr->basedir);
258 g_free (nr->name);
259 }
260
261 g_free (node->content);
262 g_free (node);
263 }
264 }
265
266 MenuLayoutNode *
267 menu_layout_node_new (MenuLayoutNodeType type)
268 {
269 MenuLayoutNode *node;
270
271 switch (type)
272 {
273 case MENU_LAYOUT_NODE_MENU:
274 node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMenu, 1);
275 break;
276
277 case MENU_LAYOUT_NODE_LEGACY_DIR:
278 node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeLegacyDir, 1);
279 break;
280
281 case MENU_LAYOUT_NODE_ROOT:
282 node = (MenuLayoutNode*) g_new0 (MenuLayoutNodeRoot, 1);
283 break;
284
285 case MENU_LAYOUT_NODE_MERGE_FILE:
286 node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMergeFile, 1);
287 break;
288
289 case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
290 node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeDefaultLayout, 1);
291 break;
292
293 case MENU_LAYOUT_NODE_MENUNAME:
294 node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMenuname, 1);
295 break;
296
297 case MENU_LAYOUT_NODE_MERGE:
298 node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMerge, 1);
299 break;
300
301 default:
302 node = g_new0 (MenuLayoutNode, 1);
303 break;
304 }
305
306 node->type = type;
307
308 node->refcount = 1;
309
310 /* we're in a list of one node */
311 node->next = node;
312 node->prev = node;
313
314 return node;
315 }
316
317 MenuLayoutNode *
318 menu_layout_node_get_next (MenuLayoutNode *node)
319 {
320 return node_next (node);
321 }
322
323 MenuLayoutNode *
324 menu_layout_node_get_parent (MenuLayoutNode *node)
325 {
326 return node->parent;
327 }
328
329 MenuLayoutNode *
330 menu_layout_node_get_children (MenuLayoutNode *node)
331 {
332 return node->children;
333 }
334
335 MenuLayoutNode *
336 menu_layout_node_get_root (MenuLayoutNode *node)
337 {
338 MenuLayoutNode *parent;
339
340 parent = node;
341 while (parent->parent != NULL)
342 parent = parent->parent;
343
344 g_assert (parent->type == MENU_LAYOUT_NODE_ROOT);
345
346 return parent;
347 }
348
349 char *
350 menu_layout_node_get_content_as_path (MenuLayoutNode *node)
351 {
352 if (node->content == NULL)
353 {
354 menu_verbose (" (node has no content to get as a path)\n");
355 return NULL;
356 }
357
358 if (g_path_is_absolute (node->content))
359 {
360 return g_strdup (node->content);
361 }
362 else
363 {
364 MenuLayoutNodeRoot *root;
365
366 root = (MenuLayoutNodeRoot *) menu_layout_node_get_root (node);
367
368 if (root->basedir == NULL)
369 {
370 menu_verbose ("No basedir available, using \"%s\" as-is\n",
371 node->content);
372 return g_strdup (node->content);
373 }
374 else
375 {
376 menu_verbose ("Using basedir \"%s\" filename \"%s\"\n",
377 root->basedir, node->content);
378 return g_build_filename (root->basedir, node->content, NULL);
379 }
380 }
381 }
382
383 #define RETURN_IF_NO_PARENT(node) G_STMT_START { \
384 if ((node)->parent == NULL) \
385 { \
386 g_warning ("To add siblings to a menu node, " \
387 "it must not be the root node, " \
388 "and must be linked in below some root node\n" \
389 "node parent = %p and type = %d", \
390 (node)->parent, (node)->type); \
391 return; \
392 } \
393 } G_STMT_END
394
395 #define RETURN_IF_HAS_ENTRY_DIRS(node) G_STMT_START { \
396 if ((node)->type == MENU_LAYOUT_NODE_MENU && \
397 (((MenuLayoutNodeMenu*)(node))->app_dirs != NULL || \
398 ((MenuLayoutNodeMenu*)(node))->dir_dirs != NULL)) \
399 { \
400 g_warning ("node acquired ->app_dirs or ->dir_dirs " \
401 "while not rooted in a tree\n"); \
402 return; \
403 } \
404 } G_STMT_END \
405
406 void
407 menu_layout_node_insert_before (MenuLayoutNode *node,
408 MenuLayoutNode *new_sibling)
409 {
410 g_return_if_fail (new_sibling != NULL);
411 g_return_if_fail (new_sibling->parent == NULL);
412
413 RETURN_IF_NO_PARENT (node);
414 RETURN_IF_HAS_ENTRY_DIRS (new_sibling);
415
416 new_sibling->next = node;
417 new_sibling->prev = node->prev;
418
419 node->prev = new_sibling;
420 new_sibling->prev->next = new_sibling;
421
422 new_sibling->parent = node->parent;
423
424 if (node == node->parent->children)
425 node->parent->children = new_sibling;
426
427 menu_layout_node_ref (new_sibling);
428 }
429
430 void
431 menu_layout_node_insert_after (MenuLayoutNode *node,
432 MenuLayoutNode *new_sibling)
433 {
434 g_return_if_fail (new_sibling != NULL);
435 g_return_if_fail (new_sibling->parent == NULL);
436
437 RETURN_IF_NO_PARENT (node);
438 RETURN_IF_HAS_ENTRY_DIRS (new_sibling);
439
440 new_sibling->prev = node;
441 new_sibling->next = node->next;
442
443 node->next = new_sibling;
444 new_sibling->next->prev = new_sibling;
445
446 new_sibling->parent = node->parent;
447
448 menu_layout_node_ref (new_sibling);
449 }
450
451 void
452 menu_layout_node_prepend_child (MenuLayoutNode *parent,
453 MenuLayoutNode *new_child)
454 {
455 RETURN_IF_HAS_ENTRY_DIRS (new_child);
456
457 if (parent->children)
458 {
459 menu_layout_node_insert_before (parent->children, new_child);
460 }
461 else
462 {
463 parent->children = menu_layout_node_ref (new_child);
464 new_child->parent = parent;
465 }
466 }
467
468 void
469 menu_layout_node_append_child (MenuLayoutNode *parent,
470 MenuLayoutNode *new_child)
471 {
472 RETURN_IF_HAS_ENTRY_DIRS (new_child);
473
474 if (parent->children)
475 {
476 menu_layout_node_insert_after (parent->children->prev, new_child);
477 }
478 else
479 {
480 parent->children = menu_layout_node_ref (new_child);
481 new_child->parent = parent;
482 }
483 }
484
485 void
486 menu_layout_node_unlink (MenuLayoutNode *node)
487 {
488 g_return_if_fail (node != NULL);
489 g_return_if_fail (node->parent != NULL);
490
491 menu_layout_node_steal (node);
492 menu_layout_node_unref (node);
493 }
494
495 static void
496 recursive_clean_entry_directory_lists (MenuLayoutNode *node,
497 gboolean apps)
498 {
499 EntryDirectoryList **dirs;
500 MenuLayoutNodeMenu *nm;
501 MenuLayoutNode *iter;
502
503 if (node->type != MENU_LAYOUT_NODE_MENU)
504 return;
505
506 nm = (MenuLayoutNodeMenu *) node;
507
508 dirs = apps ? &nm->app_dirs : &nm->dir_dirs;
509
510 if (*dirs == NULL || entry_directory_list_get_length (*dirs) == 0)
511 return; /* child menus continue to have valid lists */
512
513 remove_entry_directory_list (nm, dirs);
514
515 iter = node->children;
516 while (iter != NULL)
517 {
518 if (iter->type == MENU_LAYOUT_NODE_MENU)
519 recursive_clean_entry_directory_lists (iter, apps);
520
521 iter = node_next (iter);
522 }
523 }
524
525 void
526 menu_layout_node_steal (MenuLayoutNode *node)
527 {
528 g_return_if_fail (node != NULL);
529 g_return_if_fail (node->parent != NULL);
530
531 switch (node->type)
532 {
533 case MENU_LAYOUT_NODE_NAME:
534 {
535 MenuLayoutNodeMenu *nm = (MenuLayoutNodeMenu *) node->parent;
536
537 if (nm->name_node == node)
538 {
539 menu_layout_node_unref (nm->name_node);
540 nm->name_node = NULL;
541 }
542 }
543 break;
544
545 case MENU_LAYOUT_NODE_APP_DIR:
546 recursive_clean_entry_directory_lists (node->parent, TRUE);
547 break;
548
549 case MENU_LAYOUT_NODE_DIRECTORY_DIR:
550 recursive_clean_entry_directory_lists (node->parent, FALSE);
551 break;
552
553 default:
554 break;
555 }
556
557 if (node->parent && node->parent->children == node)
558 {
559 if (node->next != node)
560 node->parent->children = node->next;
561 else
562 node->parent->children = NULL;
563 }
564
565 /* these are no-ops for length-one node lists */
566 node->prev->next = node->next;
567 node->next->prev = node->prev;
568
569 node->parent = NULL;
570
571 /* point to ourselves, now we're length one */
572 node->next = node;
573 node->prev = node;
574 }
575
576 MenuLayoutNodeType
577 menu_layout_node_get_type (MenuLayoutNode *node)
578 {
579 return node->type;
580 }
581
582 const char *
583 menu_layout_node_get_content (MenuLayoutNode *node)
584 {
585 return node->content;
586 }
587
588 void
589 menu_layout_node_set_content (MenuLayoutNode *node,
590 const char *content)
591 {
592 if (node->content == content)
593 return;
594
595 g_free (node->content);
596 node->content = g_strdup (content);
597 }
598
599 const char *
600 menu_layout_node_root_get_name (MenuLayoutNode *node)
601 {
602 MenuLayoutNodeRoot *nr;
603
604 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_ROOT, NULL);
605
606 nr = (MenuLayoutNodeRoot*) node;
607
608 return nr->name;
609 }
610
611 const char *
612 menu_layout_node_root_get_basedir (MenuLayoutNode *node)
613 {
614 MenuLayoutNodeRoot *nr;
615
616 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_ROOT, NULL);
617
618 nr = (MenuLayoutNodeRoot*) node;
619
620 return nr->basedir;
621 }
622
623 const char *
624 menu_layout_node_menu_get_name (MenuLayoutNode *node)
625 {
626 MenuLayoutNodeMenu *nm;
627
628 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL);
629
630 nm = (MenuLayoutNodeMenu*) node;
631
632 if (nm->name_node == NULL)
633 {
634 MenuLayoutNode *iter;
635
636 iter = node->children;
637 while (iter != NULL)
638 {
639 if (iter->type == MENU_LAYOUT_NODE_NAME)
640 {
641 nm->name_node = menu_layout_node_ref (iter);
642 break;
643 }
644
645 iter = node_next (iter);
646 }
647 }
648
649 if (nm->name_node == NULL)
650 return NULL;
651
652 return menu_layout_node_get_content (nm->name_node);
653 }
654
655 static void
656 ensure_dir_lists (MenuLayoutNodeMenu *nm)
657 {
658 MenuLayoutNode *node;
659 MenuLayoutNode *iter;
660 EntryDirectoryList *app_dirs;
661 EntryDirectoryList *dir_dirs;
662
663 node = (MenuLayoutNode *) nm;
664
665 if (nm->app_dirs && nm->dir_dirs)
666 return;
667
668 app_dirs = NULL;
669 dir_dirs = NULL;
670
671 if (nm->app_dirs == NULL)
672 {
673 app_dirs = entry_directory_list_new ();
674
675 if (node->parent && node->parent->type == MENU_LAYOUT_NODE_MENU)
676 {
677 EntryDirectoryList *dirs;
678
679 if ((dirs = menu_layout_node_menu_get_app_dirs (node->parent)))
680 entry_directory_list_append_list (app_dirs, dirs);
681 }
682 }
683
684 if (nm->dir_dirs == NULL)
685 {
686 dir_dirs = entry_directory_list_new ();
687
688 if (node->parent && node->parent->type == MENU_LAYOUT_NODE_MENU)
689 {
690 EntryDirectoryList *dirs;
691
692 if ((dirs = menu_layout_node_menu_get_directory_dirs (node->parent)))
693 entry_directory_list_append_list (dir_dirs, dirs);
694 }
695 }
696
697 iter = node->children;
698 while (iter != NULL)
699 {
700 EntryDirectory *ed;
701
702 if (app_dirs != NULL && iter->type == MENU_LAYOUT_NODE_APP_DIR)
703 {
704 char *path;
705
706 path = menu_layout_node_get_content_as_path (iter);
707
708 ed = entry_directory_new (DESKTOP_ENTRY_DESKTOP, path);
709 if (ed != NULL)
710 {
711 entry_directory_list_prepend (app_dirs, ed);
712 entry_directory_unref (ed);
713 }
714
715 g_free (path);
716 }
717
718 if (dir_dirs != NULL && iter->type == MENU_LAYOUT_NODE_DIRECTORY_DIR)
719 {
720 char *path;
721
722 path = menu_layout_node_get_content_as_path (iter);
723
724 ed = entry_directory_new (DESKTOP_ENTRY_DIRECTORY, path);
725 if (ed != NULL)
726 {
727 entry_directory_list_prepend (dir_dirs, ed);
728 entry_directory_unref (ed);
729 }
730
731 g_free (path);
732 }
733
734 if (iter->type == MENU_LAYOUT_NODE_LEGACY_DIR)
735 {
736 MenuLayoutNodeLegacyDir *legacy = (MenuLayoutNodeLegacyDir *) iter;
737 char *path;
738
739 path = menu_layout_node_get_content_as_path (iter);
740
741 if (app_dirs != NULL) /* we're loading app dirs */
742 {
743 ed = entry_directory_new_legacy (DESKTOP_ENTRY_DESKTOP,
744 path,
745 legacy->prefix);
746 if (ed != NULL)
747 {
748 entry_directory_list_prepend (app_dirs, ed);
749 entry_directory_unref (ed);
750 }
751 }
752
753 if (dir_dirs != NULL) /* we're loading dir dirs */
754 {
755 ed = entry_directory_new_legacy (DESKTOP_ENTRY_DIRECTORY,
756 path,
757 legacy->prefix);
758 if (ed != NULL)
759 {
760 entry_directory_list_prepend (dir_dirs, ed);
761 entry_directory_unref (ed);
762 }
763 }
764
765 g_free (path);
766 }
767
768 iter = node_next (iter);
769 }
770
771 if (app_dirs)
772 {
773 g_assert (nm->app_dirs == NULL);
774
775 nm->app_dirs = app_dirs;
776 entry_directory_list_add_monitors (nm->app_dirs,
777 (EntryDirectoryChangedFunc) handle_entry_directory_changed,
778 nm);
779 }
780
781 if (dir_dirs)
782 {
783 g_assert (nm->dir_dirs == NULL);
784
785 nm->dir_dirs = dir_dirs;
786 entry_directory_list_add_monitors (nm->dir_dirs,
787 (EntryDirectoryChangedFunc) handle_entry_directory_changed,
788 nm);
789 }
790 }
791
792 EntryDirectoryList *
793 menu_layout_node_menu_get_app_dirs (MenuLayoutNode *node)
794 {
795 MenuLayoutNodeMenu *nm;
796
797 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL);
798
799 nm = (MenuLayoutNodeMenu *) node;
800
801 ensure_dir_lists (nm);
802
803 return nm->app_dirs;
804 }
805
806 EntryDirectoryList *
807 menu_layout_node_menu_get_directory_dirs (MenuLayoutNode *node)
808 {
809 MenuLayoutNodeMenu *nm;
810
811 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL);
812
813 nm = (MenuLayoutNodeMenu *) node;
814
815 ensure_dir_lists (nm);
816
817 return nm->dir_dirs;
818 }
819
820 const char *
821 menu_layout_node_move_get_old (MenuLayoutNode *node)
822 {
823 MenuLayoutNode *iter;
824
825 iter = node->children;
826 while (iter != NULL)
827 {
828 if (iter->type == MENU_LAYOUT_NODE_OLD)
829 return iter->content;
830
831 iter = node_next (iter);
832 }
833
834 return NULL;
835 }
836
837 const char *
838 menu_layout_node_move_get_new (MenuLayoutNode *node)
839 {
840 MenuLayoutNode *iter;
841
842 iter = node->children;
843 while (iter != NULL)
844 {
845 if (iter->type == MENU_LAYOUT_NODE_NEW)
846 return iter->content;
847
848 iter = node_next (iter);
849 }
850
851 return NULL;
852 }
853
854 const char *
855 menu_layout_node_legacy_dir_get_prefix (MenuLayoutNode *node)
856 {
857 MenuLayoutNodeLegacyDir *legacy;
858
859 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_LEGACY_DIR, NULL);
860
861 legacy = (MenuLayoutNodeLegacyDir *) node;
862
863 return legacy->prefix;
864 }
865
866 void
867 menu_layout_node_legacy_dir_set_prefix (MenuLayoutNode *node,
868 const char *prefix)
869 {
870 MenuLayoutNodeLegacyDir *legacy;
871
872 g_return_if_fail (node->type == MENU_LAYOUT_NODE_LEGACY_DIR);
873
874 legacy = (MenuLayoutNodeLegacyDir *) node;
875
876 g_free (legacy->prefix);
877 legacy->prefix = g_strdup (prefix);
878 }
879
880 MenuMergeFileType
881 menu_layout_node_merge_file_get_type (MenuLayoutNode *node)
882 {
883 MenuLayoutNodeMergeFile *merge_file;
884
885 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MERGE_FILE, FALSE);
886
887 merge_file = (MenuLayoutNodeMergeFile *) node;
888
889 return merge_file->type;
890 }
891
892 void
893 menu_layout_node_merge_file_set_type (MenuLayoutNode *node,
894 MenuMergeFileType type)
895 {
896 MenuLayoutNodeMergeFile *merge_file;
897
898 g_return_if_fail (node->type == MENU_LAYOUT_NODE_MERGE_FILE);
899
900 merge_file = (MenuLayoutNodeMergeFile *) node;
901
902 merge_file->type = type;
903 }
904
905 MenuLayoutMergeType
906 menu_layout_node_merge_get_type (MenuLayoutNode *node)
907 {
908 MenuLayoutNodeMerge *merge;
909
910 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MERGE, 0);
911
912 merge = (MenuLayoutNodeMerge *) node;
913
914 return merge->merge_type;
915 }
916
917 static void
918 menu_layout_node_merge_set_type (MenuLayoutNode *node,
919 const char *merge_type)
920 {
921 MenuLayoutNodeMerge *merge;
922
923 g_return_if_fail (node->type == MENU_LAYOUT_NODE_MERGE);
924
925 merge = (MenuLayoutNodeMerge *) node;
926
927 merge->merge_type = MENU_LAYOUT_MERGE_NONE;
928
929 if (strcmp (merge_type, "menus") == 0)
930 {
931 merge->merge_type = MENU_LAYOUT_MERGE_MENUS;
932 }
933 else if (strcmp (merge_type, "files") == 0)
934 {
935 merge->merge_type = MENU_LAYOUT_MERGE_FILES;
936 }
937 else if (strcmp (merge_type, "all") == 0)
938 {
939 merge->merge_type = MENU_LAYOUT_MERGE_ALL;
940 }
941 }
942
943 void
944 menu_layout_node_default_layout_get_values (MenuLayoutNode *node,
945 MenuLayoutValues *values)
946 {
947 MenuLayoutNodeDefaultLayout *default_layout;
948
949 g_return_if_fail (node->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT);
950 g_return_if_fail (values != NULL);
951
952 default_layout = (MenuLayoutNodeDefaultLayout *) node;
953
954 *values = default_layout->layout_values;
955 }
956
957 void
958 menu_layout_node_menuname_get_values (MenuLayoutNode *node,
959 MenuLayoutValues *values)
960 {
961 MenuLayoutNodeMenuname *menuname;
962
963 g_return_if_fail (node->type == MENU_LAYOUT_NODE_MENUNAME);
964 g_return_if_fail (values != NULL);
965
966 menuname = (MenuLayoutNodeMenuname *) node;
967
968 *values = menuname->layout_values;
969 }
970
971 static void
972 menu_layout_values_set (MenuLayoutValues *values,
973 const char *show_empty,
974 const char *inline_menus,
975 const char *inline_limit,
976 const char *inline_header,
977 const char *inline_alias)
978 {
979 values->mask = MENU_LAYOUT_VALUES_NONE;
980 values->show_empty = FALSE;
981 values->inline_menus = FALSE;
982 values->inline_limit = 4;
983 values->inline_header = FALSE;
984 values->inline_alias = FALSE;
985
986 if (show_empty != NULL)
987 {
988 values->show_empty = strcmp (show_empty, "true") == 0;
989 values->mask |= MENU_LAYOUT_VALUES_SHOW_EMPTY;
990 }
991
992 if (inline_menus != NULL)
993 {
994 values->inline_menus = strcmp (inline_menus, "true") == 0;
995 values->mask |= MENU_LAYOUT_VALUES_INLINE_MENUS;
996 }
997
998 if (inline_limit != NULL)
999 {
1000 char *end;
1001 int limit;
1002
1003 limit = strtol (inline_limit, &end, 10);
1004 if (*end == '\0')
1005 {
1006 values->inline_limit = limit;
1007 values->mask |= MENU_LAYOUT_VALUES_INLINE_LIMIT;
1008 }
1009 }
1010
1011 if (inline_header != NULL)
1012 {
1013 values->inline_header = strcmp (inline_header, "true") == 0;
1014 values->mask |= MENU_LAYOUT_VALUES_INLINE_HEADER;
1015 }
1016
1017 if (inline_alias != NULL)
1018 {
1019 values->inline_alias = strcmp (inline_alias, "true") == 0;
1020 values->mask |= MENU_LAYOUT_VALUES_INLINE_ALIAS;
1021 }
1022 }
1023
1024 static void
1025 menu_layout_node_default_layout_set_values (MenuLayoutNode *node,
1026 const char *show_empty,
1027 const char *inline_menus,
1028 const char *inline_limit,
1029 const char *inline_header,
1030 const char *inline_alias)
1031 {
1032 MenuLayoutNodeDefaultLayout *default_layout;
1033
1034 g_return_if_fail (node->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT);
1035
1036 default_layout = (MenuLayoutNodeDefaultLayout *) node;
1037
1038 menu_layout_values_set (&default_layout->layout_values,
1039 show_empty,
1040 inline_menus,
1041 inline_limit,
1042 inline_header,
1043 inline_alias);
1044 }
1045
1046 static void
1047 menu_layout_node_menuname_set_values (MenuLayoutNode *node,
1048 const char *show_empty,
1049 const char *inline_menus,
1050 const char *inline_limit,
1051 const char *inline_header,
1052 const char *inline_alias)
1053 {
1054 MenuLayoutNodeMenuname *menuname;
1055
1056 g_return_if_fail (node->type == MENU_LAYOUT_NODE_MENUNAME);
1057
1058 menuname = (MenuLayoutNodeMenuname *) node;
1059
1060 menu_layout_values_set (&menuname->layout_values,
1061 show_empty,
1062 inline_menus,
1063 inline_limit,
1064 inline_header,
1065 inline_alias);
1066 }
1067
1068 void
1069 menu_layout_node_root_add_entries_monitor (MenuLayoutNode *node,
1070 MenuLayoutNodeEntriesChangedFunc callback,
1071 gpointer user_data)
1072 {
1073 MenuLayoutNodeEntriesMonitor *monitor;
1074 MenuLayoutNodeRoot *nr;
1075 GSList *tmp;
1076
1077 g_return_if_fail (node->type == MENU_LAYOUT_NODE_ROOT);
1078
1079 nr = (MenuLayoutNodeRoot *) node;
1080
1081 tmp = nr->monitors;
1082 while (tmp != NULL)
1083 {
1084 monitor = tmp->data;
1085
1086 if (monitor->callback == callback &&
1087 monitor->user_data == user_data)
1088 break;
1089
1090 tmp = tmp->next;
1091 }
1092
1093 if (tmp == NULL)
1094 {
1095 monitor = g_new0 (MenuLayoutNodeEntriesMonitor, 1);
1096 monitor->callback = callback;
1097 monitor->user_data = user_data;
1098
1099 nr->monitors = g_slist_append (nr->monitors, monitor);
1100 }
1101 }
1102
1103 void
1104 menu_layout_node_root_remove_entries_monitor (MenuLayoutNode *node,
1105 MenuLayoutNodeEntriesChangedFunc callback,
1106 gpointer user_data)
1107 {
1108 MenuLayoutNodeRoot *nr;
1109 GSList *tmp;
1110
1111 g_return_if_fail (node->type == MENU_LAYOUT_NODE_ROOT);
1112
1113 nr = (MenuLayoutNodeRoot *) node;
1114
1115 tmp = nr->monitors;
1116 while (tmp != NULL)
1117 {
1118 MenuLayoutNodeEntriesMonitor *monitor = tmp->data;
1119 GSList *next = tmp->next;
1120
1121 if (monitor->callback == callback &&
1122 monitor->user_data == user_data)
1123 {
1124 nr->monitors = g_slist_delete_link (nr->monitors, tmp);
1125 g_free (monitor);
1126 }
1127
1128 tmp = next;
1129 }
1130 }
1131
1132
1133 /*
1134 * Menu file parsing
1135 */
1136
1137 typedef struct
1138 {
1139 MenuLayoutNode *root;
1140 MenuLayoutNode *stack_top;
1141 } MenuParser;
1142
1143 static void set_error (GError **err,
1144 GMarkupParseContext *context,
1145 int error_domain,
1146 int error_code,
1147 const char *format,
1148 ...) G_GNUC_PRINTF (5, 6);
1149
1150 static void add_context_to_error (GError **err,
1151 GMarkupParseContext *context);
1152
1153 static void start_element_handler (GMarkupParseContext *context,
1154 const char *element_name,
1155 const char **attribute_names,
1156 const char **attribute_values,
1157 gpointer user_data,
1158 GError **error);
1159 static void end_element_handler (GMarkupParseContext *context,
1160 const char *element_name,
1161 gpointer user_data,
1162 GError **error);
1163 static void text_handler (GMarkupParseContext *context,
1164 const char *text,
1165 gsize text_len,
1166 gpointer user_data,
1167 GError **error);
1168 static void passthrough_handler (GMarkupParseContext *context,
1169 const char *passthrough_text,
1170 gsize text_len,
1171 gpointer user_data,
1172 GError **error);
1173
1174
1175 static GMarkupParser menu_funcs = {
1176 start_element_handler,
1177 end_element_handler,
1178 text_handler,
1179 passthrough_handler,
1180 NULL
1181 };
1182
1183 static void
1184 set_error (GError **err,
1185 GMarkupParseContext *context,
1186 int error_domain,
1187 int error_code,
1188 const char *format,
1189 ...)
1190 {
1191 int line, ch;
1192 va_list args;
1193 char *str;
1194
1195 g_markup_parse_context_get_position (context, &line, &ch);
1196
1197 va_start (args, format);
1198 str = g_strdup_vprintf (format, args);
1199 va_end (args);
1200
1201 g_set_error (err, error_domain, error_code,
1202 "Line %d character %d: %s",
1203 line, ch, str);
1204
1205 g_free (str);
1206 }
1207
1208 static void
1209 add_context_to_error (GError **err,
1210 GMarkupParseContext *context)
1211 {
1212 int line, ch;
1213 char *str;
1214
1215 if (err == NULL || *err == NULL)
1216 return;
1217
1218 g_markup_parse_context_get_position (context, &line, &ch);
1219
1220 str = g_strdup_printf ("Line %d character %d: %s",
1221 line, ch, (*err)->message);
1222 g_free ((*err)->message);
1223 (*err)->message = str;
1224 }
1225
1226 #define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
1227
1228 typedef struct
1229 {
1230 const char *name;
1231 const char **retloc;
1232 } LocateAttr;
1233
1234 static gboolean
1235 locate_attributes (GMarkupParseContext *context,
1236 const char *element_name,
1237 const char **attribute_names,
1238 const char **attribute_values,
1239 GError **error,
1240 const char *first_attribute_name,
1241 const char **first_attribute_retloc,
1242 ...)
1243 {
1244 #define MAX_ATTRS 24
1245 LocateAttr attrs[MAX_ATTRS];
1246 int n_attrs;
1247 va_list args;
1248 const char *name;
1249 const char **retloc;
1250 gboolean retval;
1251 int i;
1252
1253 g_return_val_if_fail (first_attribute_name != NULL, FALSE);
1254 g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
1255
1256 retval = TRUE;
1257
1258 n_attrs = 1;
1259 attrs[0].name = first_attribute_name;
1260 attrs[0].retloc = first_attribute_retloc;
1261 *first_attribute_retloc = NULL;
1262
1263 va_start (args, first_attribute_retloc);
1264
1265 name = va_arg (args, const char *);
1266 retloc = va_arg (args, const char **);
1267
1268 while (name != NULL)
1269 {
1270 g_return_val_if_fail (retloc != NULL, FALSE);
1271
1272 g_assert (n_attrs < MAX_ATTRS);
1273
1274 attrs[n_attrs].name = name;
1275 attrs[n_attrs].retloc = retloc;
1276 n_attrs += 1;
1277 *retloc = NULL;
1278
1279 name = va_arg (args, const char *);
1280 retloc = va_arg (args, const char **);
1281 }
1282
1283 va_end (args);
1284
1285 i = 0;
1286 while (attribute_names[i])
1287 {
1288 int j;
1289
1290 j = 0;
1291 while (j < n_attrs)
1292 {
1293 if (strcmp (attrs[j].name, attribute_names[i]) == 0)
1294 {
1295 retloc = attrs[j].retloc;
1296
1297 if (*retloc != NULL)
1298 {
1299 set_error (error, context,
1300 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1301 "Attribute \"%s\" repeated twice on the same <%s> element",
1302 attrs[j].name, element_name);
1303 retval = FALSE;
1304 goto out;
1305 }
1306
1307 *retloc = attribute_values[i];
1308 break;
1309 }
1310
1311 ++j;
1312 }
1313
1314 if (j == n_attrs)
1315 {
1316 set_error (error, context,
1317 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1318 "Attribute \"%s\" is invalid on <%s> element in this context",
1319 attribute_names[i], element_name);
1320 retval = FALSE;
1321 goto out;
1322 }
1323
1324 ++i;
1325 }
1326
1327 out:
1328 return retval;
1329
1330 #undef MAX_ATTRS
1331 }
1332
1333 static gboolean
1334 check_no_attributes (GMarkupParseContext *context,
1335 const char *element_name,
1336 const char **attribute_names,
1337 const char **attribute_values,
1338 GError **error)
1339 {
1340 if (attribute_names[0] != NULL)
1341 {
1342 set_error (error, context,
1343 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1344 "Attribute \"%s\" is invalid on <%s> element in this context",
1345 attribute_names[0], element_name);
1346 return FALSE;
1347 }
1348
1349 return TRUE;
1350 }
1351
1352 static int
1353 has_child_of_type (MenuLayoutNode *node,
1354 MenuLayoutNodeType type)
1355 {
1356 MenuLayoutNode *iter;
1357
1358 iter = node->children;
1359 while (iter)
1360 {
1361 if (iter->type == type)
1362 return TRUE;
1363
1364 iter = node_next (iter);
1365 }
1366
1367 return FALSE;
1368 }
1369
1370 static void
1371 push_node (MenuParser *parser,
1372 MenuLayoutNodeType type)
1373 {
1374 MenuLayoutNode *node;
1375
1376 node = menu_layout_node_new (type);
1377 menu_layout_node_append_child (parser->stack_top, node);
1378 menu_layout_node_unref (node);
1379
1380 parser->stack_top = node;
1381 }
1382
1383 static void
1384 start_menu_element (MenuParser *parser,
1385 GMarkupParseContext *context,
1386 const char *element_name,
1387 const char **attribute_names,
1388 const char **attribute_values,
1389 GError **error)
1390 {
1391 if (!check_no_attributes (context, element_name,
1392 attribute_names, attribute_values,
1393 error))
1394 return;
1395
1396 if (!(parser->stack_top->type == MENU_LAYOUT_NODE_ROOT ||
1397 parser->stack_top->type == MENU_LAYOUT_NODE_MENU))
1398 {
1399 set_error (error, context,
1400 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1401 "<Menu> element can only appear below other <Menu> elements or at toplevel\n");
1402 }
1403 else
1404 {
1405 push_node (parser, MENU_LAYOUT_NODE_MENU);
1406 }
1407 }
1408
1409 static void
1410 start_menu_child_element (MenuParser *parser,
1411 GMarkupParseContext *context,
1412 const char *element_name,
1413 const char **attribute_names,
1414 const char **attribute_values,
1415 GError **error)
1416 {
1417 if (ELEMENT_IS ("LegacyDir"))
1418 {
1419 const char *prefix;
1420
1421 push_node (parser, MENU_LAYOUT_NODE_LEGACY_DIR);
1422
1423 if (!locate_attributes (context, element_name,
1424 attribute_names, attribute_values,
1425 error,
1426 "prefix", &prefix,
1427 NULL))
1428 return;
1429
1430 menu_layout_node_legacy_dir_set_prefix (parser->stack_top, prefix);
1431 }
1432 else if (ELEMENT_IS ("MergeFile"))
1433 {
1434 const char *type;
1435
1436 push_node (parser, MENU_LAYOUT_NODE_MERGE_FILE);
1437
1438 if (!locate_attributes (context, element_name,
1439 attribute_names, attribute_values,
1440 error,
1441 "type", &type,
1442 NULL))
1443 return;
1444
1445 if (type != NULL && strcmp (type, "parent") == 0)
1446 {
1447 menu_layout_node_merge_file_set_type (parser->stack_top,
1448 MENU_MERGE_FILE_TYPE_PARENT);
1449 }
1450 }
1451 else if (ELEMENT_IS ("DefaultLayout"))
1452 {
1453 const char *show_empty;
1454 const char *inline_menus;
1455 const char *inline_limit;
1456 const char *inline_header;
1457 const char *inline_alias;
1458
1459 push_node (parser, MENU_LAYOUT_NODE_DEFAULT_LAYOUT);
1460
1461 locate_attributes (context, element_name,
1462 attribute_names, attribute_values,
1463 error,
1464 "show_empty", &show_empty,
1465 "inline", &inline_menus,
1466 "inline_limit", &inline_limit,
1467 "inline_header", &inline_header,
1468 "inline_alias", &inline_alias,
1469 NULL);
1470
1471 menu_layout_node_default_layout_set_values (parser->stack_top,
1472 show_empty,
1473 inline_menus,
1474 inline_limit,
1475 inline_header,
1476 inline_alias);
1477 }
1478 else
1479 {
1480 if (!check_no_attributes (context, element_name,
1481 attribute_names, attribute_values,
1482 error))
1483 return;
1484
1485 if (ELEMENT_IS ("AppDir"))
1486 {
1487 push_node (parser, MENU_LAYOUT_NODE_APP_DIR);
1488 }
1489 else if (ELEMENT_IS ("DefaultAppDirs"))
1490 {
1491 push_node (parser, MENU_LAYOUT_NODE_DEFAULT_APP_DIRS);
1492 }
1493 else if (ELEMENT_IS ("DirectoryDir"))
1494 {
1495 push_node (parser, MENU_LAYOUT_NODE_DIRECTORY_DIR);
1496 }
1497 else if (ELEMENT_IS ("DefaultDirectoryDirs"))
1498 {
1499 push_node (parser, MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS);
1500 }
1501 else if (ELEMENT_IS ("DefaultMergeDirs"))
1502 {
1503 push_node (parser, MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS);
1504 }
1505 else if (ELEMENT_IS ("Name"))
1506 {
1507 if (has_child_of_type (parser->stack_top, MENU_LAYOUT_NODE_NAME))
1508 {
1509 set_error (error, context,
1510 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1511 "Multiple <Name> elements in a <Menu> element is not allowed\n");
1512 return;
1513 }
1514
1515 push_node (parser, MENU_LAYOUT_NODE_NAME);
1516 }
1517 else if (ELEMENT_IS ("Directory"))
1518 {
1519 push_node (parser, MENU_LAYOUT_NODE_DIRECTORY);
1520 }
1521 else if (ELEMENT_IS ("OnlyUnallocated"))
1522 {
1523 push_node (parser, MENU_LAYOUT_NODE_ONLY_UNALLOCATED);
1524 }
1525 else if (ELEMENT_IS ("NotOnlyUnallocated"))
1526 {
1527 push_node (parser, MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED);
1528 }
1529 else if (ELEMENT_IS ("Include"))
1530 {
1531 push_node (parser, MENU_LAYOUT_NODE_INCLUDE);
1532 }
1533 else if (ELEMENT_IS ("Exclude"))
1534 {
1535 push_node (parser, MENU_LAYOUT_NODE_EXCLUDE);
1536 }
1537 else if (ELEMENT_IS ("MergeDir"))
1538 {
1539 push_node (parser, MENU_LAYOUT_NODE_MERGE_DIR);
1540 }
1541 else if (ELEMENT_IS ("KDELegacyDirs"))
1542 {
1543 push_node (parser, MENU_LAYOUT_NODE_KDE_LEGACY_DIRS);
1544 }
1545 else if (ELEMENT_IS ("Move"))
1546 {
1547 push_node (parser, MENU_LAYOUT_NODE_MOVE);
1548 }
1549 else if (ELEMENT_IS ("Deleted"))
1550 {
1551 push_node (parser, MENU_LAYOUT_NODE_DELETED);
1552
1553 }
1554 else if (ELEMENT_IS ("NotDeleted"))
1555 {
1556 push_node (parser, MENU_LAYOUT_NODE_NOT_DELETED);
1557 }
1558 else if (ELEMENT_IS ("Layout"))
1559 {
1560 push_node (parser, MENU_LAYOUT_NODE_LAYOUT);
1561 }
1562 else
1563 {
1564 set_error (error, context,
1565 G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1566 "Element <%s> may not appear below <%s>\n",
1567 element_name, "Menu");
1568 }
1569 }
1570 }
1571
1572 static void
1573 start_matching_rule_element (MenuParser *parser,
1574 GMarkupParseContext *context,
1575 const char *element_name,
1576 const char **attribute_names,
1577 const char **attribute_values,
1578 GError **error)
1579 {
1580 if (!check_no_attributes (context, element_name,
1581 attribute_names, attribute_values,
1582 error))
1583 return;
1584
1585
1586 if (ELEMENT_IS ("Filename"))
1587 {
1588 push_node (parser, MENU_LAYOUT_NODE_FILENAME);
1589 }
1590 else if (ELEMENT_IS ("Category"))
1591 {
1592 push_node (parser, MENU_LAYOUT_NODE_CATEGORY);
1593 }
1594 else if (ELEMENT_IS ("All"))
1595 {
1596 push_node (parser, MENU_LAYOUT_NODE_ALL);
1597 }
1598 else if (ELEMENT_IS ("And"))
1599 {
1600 push_node (parser, MENU_LAYOUT_NODE_AND);
1601 }
1602 else if (ELEMENT_IS ("Or"))
1603 {
1604 push_node (parser, MENU_LAYOUT_NODE_OR);
1605 }
1606 else if (ELEMENT_IS ("Not"))
1607 {
1608 push_node (parser, MENU_LAYOUT_NODE_NOT);
1609 }
1610 else
1611 {
1612 set_error (error, context,
1613 G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1614 "Element <%s> may not appear in this context\n",
1615 element_name);
1616 }
1617 }
1618
1619 static void
1620 start_move_child_element (MenuParser *parser,
1621 GMarkupParseContext *context,
1622 const char *element_name,
1623 const char **attribute_names,
1624 const char **attribute_values,
1625 GError **error)
1626 {
1627 if (!check_no_attributes (context, element_name,
1628 attribute_names, attribute_values,
1629 error))
1630 return;
1631
1632 if (ELEMENT_IS ("Old"))
1633 {
1634 push_node (parser, MENU_LAYOUT_NODE_OLD);
1635 }
1636 else if (ELEMENT_IS ("New"))
1637 {
1638 push_node (parser, MENU_LAYOUT_NODE_NEW);
1639 }
1640 else
1641 {
1642 set_error (error, context,
1643 G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1644 "Element <%s> may not appear below <%s>\n",
1645 element_name, "Move");
1646 }
1647 }
1648
1649 static void
1650 start_layout_child_element (MenuParser *parser,
1651 GMarkupParseContext *context,
1652 const char *element_name,
1653 const char **attribute_names,
1654 const char **attribute_values,
1655 GError **error)
1656 {
1657 if (ELEMENT_IS ("Menuname"))
1658 {
1659 const char *show_empty;
1660 const char *inline_menus;
1661 const char *inline_limit;
1662 const char *inline_header;
1663 const char *inline_alias;
1664
1665 push_node (parser, MENU_LAYOUT_NODE_MENUNAME);
1666
1667 locate_attributes (context, element_name,
1668 attribute_names, attribute_values,
1669 error,
1670 "show_empty", &show_empty,
1671 "inline", &inline_menus,
1672 "inline_limit", &inline_limit,
1673 "inline_header", &inline_header,
1674 "inline_alias", &inline_alias,
1675 NULL);
1676
1677 menu_layout_node_menuname_set_values (parser->stack_top,
1678 show_empty,
1679 inline_menus,
1680 inline_limit,
1681 inline_header,
1682 inline_alias);
1683 }
1684 else if (ELEMENT_IS ("Merge"))
1685 {
1686 const char *type;
1687
1688 push_node (parser, MENU_LAYOUT_NODE_MERGE);
1689
1690 locate_attributes (context, element_name,
1691 attribute_names, attribute_values,
1692 error,
1693 "type", &type,
1694 NULL);
1695
1696 menu_layout_node_merge_set_type (parser->stack_top, type);
1697 }
1698 else
1699 {
1700 if (!check_no_attributes (context, element_name,
1701 attribute_names, attribute_values,
1702 error))
1703 return;
1704
1705 if (ELEMENT_IS ("Filename"))
1706 {
1707 push_node (parser, MENU_LAYOUT_NODE_FILENAME);
1708 }
1709 else if (ELEMENT_IS ("Separator"))
1710 {
1711 push_node (parser, MENU_LAYOUT_NODE_SEPARATOR);
1712 }
1713 else
1714 {
1715 set_error (error, context,
1716 G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1717 "Element <%s> may not appear below <%s>\n",
1718 element_name, "Move");
1719 }
1720 }
1721 }
1722
1723 static void
1724 start_element_handler (GMarkupParseContext *context,
1725 const char *element_name,
1726 const char **attribute_names,
1727 const char **attribute_values,
1728 gpointer user_data,
1729 GError **error)
1730 {
1731 MenuParser *parser = user_data;
1732
1733 if (ELEMENT_IS ("Menu"))
1734 {
1735 if (parser->stack_top == parser->root &&
1736 has_child_of_type (parser->root, MENU_LAYOUT_NODE_MENU))
1737 {
1738 set_error (error, context,
1739 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1740 "Multiple root elements in menu file, only one toplevel <Menu> is allowed\n");
1741 return;
1742 }
1743
1744 start_menu_element (parser, context, element_name,
1745 attribute_names, attribute_values,
1746 error);
1747 }
1748 else if (parser->stack_top == parser->root)
1749 {
1750 set_error (error, context,
1751 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1752 "Root element in a menu file must be <Menu>, not <%s>\n",
1753 element_name);
1754 }
1755 else if (parser->stack_top->type == MENU_LAYOUT_NODE_MENU)
1756 {
1757 start_menu_child_element (parser, context, element_name,
1758 attribute_names, attribute_values,
1759 error);
1760 }
1761 else if (parser->stack_top->type == MENU_LAYOUT_NODE_INCLUDE ||
1762 parser->stack_top->type == MENU_LAYOUT_NODE_EXCLUDE ||
1763 parser->stack_top->type == MENU_LAYOUT_NODE_AND ||
1764 parser->stack_top->type == MENU_LAYOUT_NODE_OR ||
1765 parser->stack_top->type == MENU_LAYOUT_NODE_NOT)
1766 {
1767 start_matching_rule_element (parser, context, element_name,
1768 attribute_names, attribute_values,
1769 error);
1770 }
1771 else if (parser->stack_top->type == MENU_LAYOUT_NODE_MOVE)
1772 {
1773 start_move_child_element (parser, context, element_name,
1774 attribute_names, attribute_values,
1775 error);
1776 }
1777 else if (parser->stack_top->type == MENU_LAYOUT_NODE_LAYOUT ||
1778 parser->stack_top->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT)
1779 {
1780 start_layout_child_element (parser, context, element_name,
1781 attribute_names, attribute_values,
1782 error);
1783 }
1784 else
1785 {
1786 set_error (error, context,
1787 G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1788 "Element <%s> may not appear in this context\n",
1789 element_name);
1790 }
1791
1792 add_context_to_error (error, context);
1793 }
1794
1795 /* we want to make sure that the <Layout> or <DefaultLayout> is either empty,
1796 * or contain one <Merge> of type "all", or contain one <Merge> of type "menus"
1797 * and one <Merge> of type "files". If this is not the case, we try to clean up
1798 * things:
1799 * + if there is at least one <Merge> of type "all", then we only keep the
1800 * last <Merge> of type "all" and remove all others <Merge>
1801 * + if there is no <Merge> with type "all", we keep only the last <Merge> of
1802 * type "menus" and the last <Merge> of type "files". If there's no <Merge>
1803 * of type "menus" we append one, and then if there's no <Merge> of type
1804 * "files", we append one. (So menus are before files)
1805 */
1806 static gboolean
1807 fixup_layout_node (GMarkupParseContext *context,
1808 MenuParser *parser,
1809 MenuLayoutNode *node,
1810 GError **error)
1811 {
1812 MenuLayoutNode *child;
1813 MenuLayoutNode *last_all;
1814 MenuLayoutNode *last_menus;
1815 MenuLayoutNode *last_files;
1816 int n_all;
1817 int n_menus;
1818 int n_files;
1819
1820 if (!node->children)
1821 {
1822 return TRUE;
1823 }
1824
1825 last_all = NULL;
1826 last_menus = NULL;
1827 last_files = NULL;
1828 n_all = 0;
1829 n_menus = 0;
1830 n_files = 0;
1831
1832 child = node->children;
1833 while (child != NULL)
1834 {
1835 switch (child->type)
1836 {
1837 case MENU_LAYOUT_NODE_MERGE:
1838 switch (menu_layout_node_merge_get_type (child))
1839 {
1840 case MENU_LAYOUT_MERGE_NONE:
1841 break;
1842
1843 case MENU_LAYOUT_MERGE_MENUS:
1844 last_menus = child;
1845 n_menus++;
1846 break;
1847
1848 case MENU_LAYOUT_MERGE_FILES:
1849 last_files = child;
1850 n_files++;
1851 break;
1852
1853 case MENU_LAYOUT_MERGE_ALL:
1854 last_all = child;
1855 n_all++;
1856 break;
1857
1858 default:
1859 g_assert_not_reached ();
1860 break;
1861 }
1862 break;
1863
1864 default:
1865 break;
1866 }
1867
1868 child = node_next (child);
1869 }
1870
1871 if ((n_all == 1 && n_menus == 0 && n_files == 0) ||
1872 (n_all == 0 && n_menus == 1 && n_files == 1))
1873 {
1874 return TRUE;
1875 }
1876 else if (n_all > 1 || n_menus > 1 || n_files > 1 ||
1877 (n_all == 1 && (n_menus != 0 || n_files != 0)))
1878 {
1879 child = node->children;
1880 while (child != NULL)
1881 {
1882 MenuLayoutNode *next;
1883
1884 next = node_next (child);
1885
1886 switch (child->type)
1887 {
1888 case MENU_LAYOUT_NODE_MERGE:
1889 switch (menu_layout_node_merge_get_type (child))
1890 {
1891 case MENU_LAYOUT_MERGE_NONE:
1892 break;
1893
1894 case MENU_LAYOUT_MERGE_MENUS:
1895 if (n_all || last_menus != child)
1896 {
1897 menu_verbose ("removing duplicated merge menus element\n");
1898 menu_layout_node_unlink (child);
1899 }
1900 break;
1901
1902 case MENU_LAYOUT_MERGE_FILES:
1903 if (n_all || last_files != child)
1904 {
1905 menu_verbose ("removing duplicated merge files element\n");
1906 menu_layout_node_unlink (child);
1907 }
1908 break;
1909
1910 case MENU_LAYOUT_MERGE_ALL:
1911 if (last_all != child)
1912 {
1913 menu_verbose ("removing duplicated merge all element\n");
1914 menu_layout_node_unlink (child);
1915 }
1916 break;
1917
1918 default:
1919 g_assert_not_reached ();
1920 break;
1921 }
1922 break;
1923
1924 default:
1925 break;
1926 }
1927
1928 child = next;
1929 }
1930 }
1931
1932 if (n_all == 0 && n_menus == 0)
1933 {
1934 last_menus = menu_layout_node_new (MENU_LAYOUT_NODE_MERGE);
1935 menu_layout_node_merge_set_type (last_menus, "menus");
1936 menu_verbose ("appending missing merge menus element\n");
1937 menu_layout_node_append_child (node, last_menus);
1938 }
1939
1940 if (n_all == 0 && n_files == 0)
1941 {
1942 last_files = menu_layout_node_new (MENU_LAYOUT_NODE_MERGE);
1943 menu_layout_node_merge_set_type (last_files, "files");
1944 menu_verbose ("appending missing merge files element\n");
1945 menu_layout_node_append_child (node, last_files);
1946 }
1947
1948 return TRUE;
1949 }
1950
1951 /* we want to a) check that we have old-new pairs and b) canonicalize
1952 * such that each <Move> has exactly one old-new pair
1953 */
1954 static gboolean
1955 fixup_move_node (GMarkupParseContext *context,
1956 MenuParser *parser,
1957 MenuLayoutNode *node,
1958 GError **error)
1959 {
1960 MenuLayoutNode *child;
1961 int n_old;
1962 int n_new;
1963
1964 n_old = 0;
1965 n_new = 0;
1966
1967 child = node->children;
1968 while (child != NULL)
1969 {
1970 switch (child->type)
1971 {
1972 case MENU_LAYOUT_NODE_OLD:
1973 if (n_new != n_old)
1974 {
1975 set_error (error, context,
1976 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1977 "<Old>/<New> elements not paired properly\n");
1978 return FALSE;
1979 }
1980
1981 n_old += 1;
1982
1983 break;
1984
1985 case MENU_LAYOUT_NODE_NEW:
1986 n_new += 1;
1987
1988 if (n_new != n_old)
1989 {
1990 set_error (error, context,
1991 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1992 "<Old>/<New> elements not paired properly\n");
1993 return FALSE;
1994 }
1995
1996 break;
1997
1998 default:
1999 g_assert_not_reached ();
2000 break;
2001 }
2002
2003 child = node_next (child);
2004 }
2005
2006 if (n_new == 0 || n_old == 0)
2007 {
2008 set_error (error, context,
2009 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
2010 "<Old>/<New> elements missing under <Move>\n");
2011 return FALSE;
2012 }
2013
2014 g_assert (n_new == n_old);
2015 g_assert ((n_new + n_old) % 2 == 0);
2016
2017 if (n_new > 1)
2018 {
2019 MenuLayoutNode *prev;
2020 MenuLayoutNode *append_after;
2021
2022 /* Need to split the <Move> into multiple <Move> */
2023
2024 n_old = 0;
2025 n_new = 0;
2026 prev = NULL;
2027 append_after = node;
2028
2029 child = node->children;
2030 while (child != NULL)
2031 {
2032 MenuLayoutNode *next;
2033
2034 next = node_next (child);
2035
2036 switch (child->type)
2037 {
2038 case MENU_LAYOUT_NODE_OLD:
2039 n_old += 1;
2040 break;
2041
2042 case MENU_LAYOUT_NODE_NEW:
2043 n_new += 1;
2044 break;
2045
2046 default:
2047 g_assert_not_reached ();
2048 break;
2049 }
2050
2051 if (n_old == n_new &&
2052 n_old > 1)
2053 {
2054 /* Move the just-completed pair */
2055 MenuLayoutNode *new_move;
2056
2057 g_assert (prev != NULL);
2058
2059 new_move = menu_layout_node_new (MENU_LAYOUT_NODE_MOVE);
2060 menu_verbose ("inserting new_move after append_after\n");
2061 menu_layout_node_insert_after (append_after, new_move);
2062 append_after = new_move;
2063
2064 menu_layout_node_steal (prev);
2065 menu_layout_node_steal (child);
2066
2067 menu_verbose ("appending prev to new_move\n");
2068 menu_layout_node_append_child (new_move, prev);
2069 menu_verbose ("appending child to new_move\n");
2070 menu_layout_node_append_child (new_move, child);
2071
2072 menu_verbose ("Created new move element old = %s new = %s\n",
2073 menu_layout_node_move_get_old (new_move),
2074 menu_layout_node_move_get_new (new_move));
2075
2076 menu_layout_node_unref (new_move);
2077 menu_layout_node_unref (prev);
2078 menu_layout_node_unref (child);
2079
2080 prev = NULL;
2081 }
2082 else
2083 {
2084 prev = child;
2085 }
2086
2087 prev = child;
2088 child = next;
2089 }
2090 }
2091
2092 return TRUE;
2093 }
2094
2095 static void
2096 end_element_handler (GMarkupParseContext *context,
2097 const char *element_name,
2098 gpointer user_data,
2099 GError **error)
2100 {
2101 MenuParser *parser = user_data;
2102
2103 g_assert (parser->stack_top != NULL);
2104
2105 switch (parser->stack_top->type)
2106 {
2107 case MENU_LAYOUT_NODE_APP_DIR:
2108 case MENU_LAYOUT_NODE_DIRECTORY_DIR:
2109 case MENU_LAYOUT_NODE_NAME:
2110 case MENU_LAYOUT_NODE_DIRECTORY:
2111 case MENU_LAYOUT_NODE_FILENAME:
2112 case MENU_LAYOUT_NODE_CATEGORY:
2113 case MENU_LAYOUT_NODE_MERGE_DIR:
2114 case MENU_LAYOUT_NODE_LEGACY_DIR:
2115 case MENU_LAYOUT_NODE_OLD:
2116 case MENU_LAYOUT_NODE_NEW:
2117 case MENU_LAYOUT_NODE_MENUNAME:
2118 if (menu_layout_node_get_content (parser->stack_top) == NULL)
2119 {
2120 set_error (error, context,
2121 G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
2122 "Element <%s> is required to contain text and was empty\n",
2123 element_name);
2124 goto out;
2125 }
2126 break;
2127
2128 case MENU_LAYOUT_NODE_MENU:
2129 if (!has_child_of_type (parser->stack_top, MENU_LAYOUT_NODE_NAME))
2130 {
2131 set_error (error, context,
2132 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
2133 "<Menu> elements are required to contain a <Name> element\n");
2134 goto out;
2135 }
2136 break;
2137
2138 case MENU_LAYOUT_NODE_ROOT:
2139 case MENU_LAYOUT_NODE_PASSTHROUGH:
2140 case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS:
2141 case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS:
2142 case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS:
2143 case MENU_LAYOUT_NODE_ONLY_UNALLOCATED:
2144 case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED:
2145 case MENU_LAYOUT_NODE_INCLUDE:
2146 case MENU_LAYOUT_NODE_EXCLUDE:
2147 case MENU_LAYOUT_NODE_ALL:
2148 case MENU_LAYOUT_NODE_AND:
2149 case MENU_LAYOUT_NODE_OR:
2150 case MENU_LAYOUT_NODE_NOT:
2151 case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS:
2152 case MENU_LAYOUT_NODE_DELETED:
2153 case MENU_LAYOUT_NODE_NOT_DELETED:
2154 case MENU_LAYOUT_NODE_SEPARATOR:
2155 case MENU_LAYOUT_NODE_MERGE:
2156 case MENU_LAYOUT_NODE_MERGE_FILE:
2157 break;
2158
2159 case MENU_LAYOUT_NODE_LAYOUT:
2160 case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
2161 if (!fixup_layout_node (context, parser, parser->stack_top, error))
2162 goto out;
2163 break;
2164
2165 case MENU_LAYOUT_NODE_MOVE:
2166 if (!fixup_move_node (context, parser, parser->stack_top, error))
2167 goto out;
2168 break;
2169 }
2170
2171 out:
2172 parser->stack_top = parser->stack_top->parent;
2173 }
2174
2175 static gboolean
2176 all_whitespace (const char *text,
2177 int text_len)
2178 {
2179 const char *p;
2180 const char *end;
2181
2182 p = text;
2183 end = text + text_len;
2184
2185 while (p != end)
2186 {
2187 if (!g_ascii_isspace (*p))
2188 return FALSE;
2189
2190 p = g_utf8_next_char (p);
2191 }
2192
2193 return TRUE;
2194 }
2195
2196 static void
2197 text_handler (GMarkupParseContext *context,
2198 const char *text,
2199 gsize text_len,
2200 gpointer user_data,
2201 GError **error)
2202 {
2203 MenuParser *parser = user_data;
2204
2205 switch (parser->stack_top->type)
2206 {
2207 case MENU_LAYOUT_NODE_APP_DIR:
2208 case MENU_LAYOUT_NODE_DIRECTORY_DIR:
2209 case MENU_LAYOUT_NODE_NAME:
2210 case MENU_LAYOUT_NODE_DIRECTORY:
2211 case MENU_LAYOUT_NODE_FILENAME:
2212 case MENU_LAYOUT_NODE_CATEGORY:
2213 case MENU_LAYOUT_NODE_MERGE_FILE:
2214 case MENU_LAYOUT_NODE_MERGE_DIR:
2215 case MENU_LAYOUT_NODE_LEGACY_DIR:
2216 case MENU_LAYOUT_NODE_OLD:
2217 case MENU_LAYOUT_NODE_NEW:
2218 case MENU_LAYOUT_NODE_MENUNAME:
2219 g_assert (menu_layout_node_get_content (parser->stack_top) == NULL);
2220
2221 menu_layout_node_set_content (parser->stack_top, text);
2222 break;
2223
2224 case MENU_LAYOUT_NODE_ROOT:
2225 case MENU_LAYOUT_NODE_PASSTHROUGH:
2226 case MENU_LAYOUT_NODE_MENU:
2227 case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS:
2228 case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS:
2229 case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS:
2230 case MENU_LAYOUT_NODE_ONLY_UNALLOCATED:
2231 case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED:
2232 case MENU_LAYOUT_NODE_INCLUDE:
2233 case MENU_LAYOUT_NODE_EXCLUDE:
2234 case MENU_LAYOUT_NODE_ALL:
2235 case MENU_LAYOUT_NODE_AND:
2236 case MENU_LAYOUT_NODE_OR:
2237 case MENU_LAYOUT_NODE_NOT:
2238 case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS:
2239 case MENU_LAYOUT_NODE_MOVE:
2240 case MENU_LAYOUT_NODE_DELETED:
2241 case MENU_LAYOUT_NODE_NOT_DELETED:
2242 case MENU_LAYOUT_NODE_LAYOUT:
2243 case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
2244 case MENU_LAYOUT_NODE_SEPARATOR:
2245 case MENU_LAYOUT_NODE_MERGE:
2246 if (!all_whitespace (text, text_len))
2247 {
2248 set_error (error, context,
2249 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
2250 "No text is allowed inside element <%s>",
2251 g_markup_parse_context_get_element (context));
2252 }
2253 break;
2254 }
2255
2256 add_context_to_error (error, context);
2257 }
2258
2259 static void
2260 passthrough_handler (GMarkupParseContext *context,
2261 const char *passthrough_text,
2262 gsize text_len,
2263 gpointer user_data,
2264 GError **error)
2265 {
2266 MenuParser *parser = user_data;
2267 MenuLayoutNode *node;
2268
2269 /* don't push passthrough on the stack, it's not an element */
2270
2271 node = menu_layout_node_new (MENU_LAYOUT_NODE_PASSTHROUGH);
2272 menu_layout_node_set_content (node, passthrough_text);
2273
2274 menu_layout_node_append_child (parser->stack_top, node);
2275 menu_layout_node_unref (node);
2276
2277 add_context_to_error (error, context);
2278 }
2279
2280 static void
2281 menu_parser_init (MenuParser *parser)
2282 {
2283 parser->root = menu_layout_node_new (MENU_LAYOUT_NODE_ROOT);
2284 parser->stack_top = parser->root;
2285 }
2286
2287 static void
2288 menu_parser_free (MenuParser *parser)
2289 {
2290 if (parser->root)
2291 menu_layout_node_unref (parser->root);
2292 }
2293
2294 MenuLayoutNode *
2295 menu_layout_load (const char *filename,
2296 const char *non_prefixed_basename,
2297 GError **err)
2298 {
2299 GMainContext *main_context;
2300 GMarkupParseContext *context;
2301 MenuLayoutNodeRoot *root;
2302 MenuLayoutNode *retval;
2303 MenuParser parser;
2304 GError *error;
2305 GString *str;
2306 char *text;
2307 char *s;
2308 gsize length;
2309
2310 text = NULL;
2311 length = 0;
2312 retval = NULL;
2313 context = NULL;
2314
2315 main_context = g_main_context_get_thread_default ();
2316
2317 menu_verbose ("Loading \"%s\" from disk\n", filename);
2318
2319 if (!g_file_get_contents (filename,
2320 &text,
2321 &length,
2322 err))
2323 {
2324 menu_verbose ("Failed to load \"%s\"\n",
2325 filename);
2326 return NULL;
2327 }
2328
2329 g_assert (text != NULL);
2330
2331 menu_parser_init (&parser);
2332
2333 root = (MenuLayoutNodeRoot *) parser.root;
2334
2335 root->basedir = g_path_get_dirname (filename);
2336 menu_verbose ("Set basedir \"%s\"\n", root->basedir);
2337
2338 if (non_prefixed_basename)
2339 s = g_strdup (non_prefixed_basename);
2340 else
2341 s = g_path_get_basename (filename);
2342 str = g_string_new (s);
2343 if (g_str_has_suffix (str->str, ".menu"))
2344 g_string_truncate (str, str->len - strlen (".menu"));
2345
2346 root->name = str->str;
2347 menu_verbose ("Set menu name \"%s\"\n", root->name);
2348
2349 g_string_free (str, FALSE);
2350 g_free (s);
2351
2352 context = g_markup_parse_context_new (&menu_funcs, 0, &parser, NULL);
2353
2354 error = NULL;
2355 if (!g_markup_parse_context_parse (context,
2356 text,
2357 length,
2358 &error))
2359 goto out;
2360
2361 error = NULL;
2362 g_markup_parse_context_end_parse (context, &error);
2363
2364 root->main_context = main_context ? g_main_context_ref (main_context) : NULL;
2365
2366 out:
2367 if (context)
2368 g_markup_parse_context_free (context);
2369 g_free (text);
2370
2371 if (error)
2372 {
2373 menu_verbose ("Error \"%s\" loading \"%s\"\n",
2374 error->message, filename);
2375 g_propagate_error (err, error);
2376 }
2377 else if (has_child_of_type (parser.root, MENU_LAYOUT_NODE_MENU))
2378 {
2379 menu_verbose ("File loaded OK\n");
2380 retval = parser.root;
2381 parser.root = NULL;
2382 }
2383 else
2384 {
2385 menu_verbose ("Did not have a root element in file\n");
2386 g_set_error (err, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
2387 "Menu file %s did not contain a root <Menu> element",
2388 filename);
2389 }
2390
2391 menu_parser_free (&parser);
2392
2393 return retval;
2394 }
0 /* Menu layout in-memory data structure (a custom "DOM tree") */
1
2 /*
3 * Copyright (C) 2002 - 2004 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21 #ifndef __MENU_LAYOUT_H__
22 #define __MENU_LAYOUT_H__
23
24 #include <glib.h>
25
26 #include "entry-directories.h"
27
28 G_BEGIN_DECLS
29
30 typedef struct MenuLayoutNode MenuLayoutNode;
31
32 typedef enum
33 {
34 MENU_LAYOUT_NODE_ROOT,
35 MENU_LAYOUT_NODE_PASSTHROUGH,
36 MENU_LAYOUT_NODE_MENU,
37 MENU_LAYOUT_NODE_APP_DIR,
38 MENU_LAYOUT_NODE_DEFAULT_APP_DIRS,
39 MENU_LAYOUT_NODE_DIRECTORY_DIR,
40 MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS,
41 MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS,
42 MENU_LAYOUT_NODE_NAME,
43 MENU_LAYOUT_NODE_DIRECTORY,
44 MENU_LAYOUT_NODE_ONLY_UNALLOCATED,
45 MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED,
46 MENU_LAYOUT_NODE_INCLUDE,
47 MENU_LAYOUT_NODE_EXCLUDE,
48 MENU_LAYOUT_NODE_FILENAME,
49 MENU_LAYOUT_NODE_CATEGORY,
50 MENU_LAYOUT_NODE_ALL,
51 MENU_LAYOUT_NODE_AND,
52 MENU_LAYOUT_NODE_OR,
53 MENU_LAYOUT_NODE_NOT,
54 MENU_LAYOUT_NODE_MERGE_FILE,
55 MENU_LAYOUT_NODE_MERGE_DIR,
56 MENU_LAYOUT_NODE_LEGACY_DIR,
57 MENU_LAYOUT_NODE_KDE_LEGACY_DIRS,
58 MENU_LAYOUT_NODE_MOVE,
59 MENU_LAYOUT_NODE_OLD,
60 MENU_LAYOUT_NODE_NEW,
61 MENU_LAYOUT_NODE_DELETED,
62 MENU_LAYOUT_NODE_NOT_DELETED,
63 MENU_LAYOUT_NODE_LAYOUT,
64 MENU_LAYOUT_NODE_DEFAULT_LAYOUT,
65 MENU_LAYOUT_NODE_MENUNAME,
66 MENU_LAYOUT_NODE_SEPARATOR,
67 MENU_LAYOUT_NODE_MERGE
68 } MenuLayoutNodeType;
69
70 typedef enum
71 {
72 MENU_MERGE_FILE_TYPE_PATH = 0,
73 MENU_MERGE_FILE_TYPE_PARENT
74 } MenuMergeFileType;
75
76 typedef enum
77 {
78 MENU_LAYOUT_MERGE_NONE,
79 MENU_LAYOUT_MERGE_MENUS,
80 MENU_LAYOUT_MERGE_FILES,
81 MENU_LAYOUT_MERGE_ALL
82 } MenuLayoutMergeType;
83
84 typedef enum
85 {
86 MENU_LAYOUT_VALUES_NONE = 0,
87 MENU_LAYOUT_VALUES_SHOW_EMPTY = 1 << 0,
88 MENU_LAYOUT_VALUES_INLINE_MENUS = 1 << 1,
89 MENU_LAYOUT_VALUES_INLINE_LIMIT = 1 << 2,
90 MENU_LAYOUT_VALUES_INLINE_HEADER = 1 << 3,
91 MENU_LAYOUT_VALUES_INLINE_ALIAS = 1 << 4
92 } MenuLayoutValuesMask;
93
94 typedef struct
95 {
96 MenuLayoutValuesMask mask;
97
98 guint show_empty : 1;
99 guint inline_menus : 1;
100 guint inline_header : 1;
101 guint inline_alias : 1;
102
103 guint inline_limit;
104 } MenuLayoutValues;
105
106
107 MenuLayoutNode *menu_layout_load (const char *filename,
108 const char *non_prefixed_basename,
109 GError **error);
110
111 MenuLayoutNode *menu_layout_node_new (MenuLayoutNodeType type);
112 MenuLayoutNode *menu_layout_node_ref (MenuLayoutNode *node);
113 void menu_layout_node_unref (MenuLayoutNode *node);
114
115 MenuLayoutNodeType menu_layout_node_get_type (MenuLayoutNode *node);
116
117 MenuLayoutNode *menu_layout_node_get_root (MenuLayoutNode *node);
118 MenuLayoutNode *menu_layout_node_get_parent (MenuLayoutNode *node);
119 MenuLayoutNode *menu_layout_node_get_children (MenuLayoutNode *node);
120 MenuLayoutNode *menu_layout_node_get_next (MenuLayoutNode *node);
121
122 void menu_layout_node_insert_before (MenuLayoutNode *node,
123 MenuLayoutNode *new_sibling);
124 void menu_layout_node_insert_after (MenuLayoutNode *node,
125 MenuLayoutNode *new_sibling);
126 void menu_layout_node_prepend_child (MenuLayoutNode *parent,
127 MenuLayoutNode *new_child);
128 void menu_layout_node_append_child (MenuLayoutNode *parent,
129 MenuLayoutNode *new_child);
130
131 void menu_layout_node_unlink (MenuLayoutNode *node);
132 void menu_layout_node_steal (MenuLayoutNode *node);
133
134 const char *menu_layout_node_get_content (MenuLayoutNode *node);
135 void menu_layout_node_set_content (MenuLayoutNode *node,
136 const char *content);
137
138 char *menu_layout_node_get_content_as_path (MenuLayoutNode *node);
139
140 const char *menu_layout_node_root_get_name (MenuLayoutNode *node);
141 const char *menu_layout_node_root_get_basedir (MenuLayoutNode *node);
142
143 const char *menu_layout_node_menu_get_name (MenuLayoutNode *node);
144 EntryDirectoryList *menu_layout_node_menu_get_app_dirs (MenuLayoutNode *node);
145 EntryDirectoryList *menu_layout_node_menu_get_directory_dirs (MenuLayoutNode *node);
146
147 const char *menu_layout_node_move_get_old (MenuLayoutNode *node);
148 const char *menu_layout_node_move_get_new (MenuLayoutNode *node);
149
150 const char *menu_layout_node_legacy_dir_get_prefix (MenuLayoutNode *node);
151 void menu_layout_node_legacy_dir_set_prefix (MenuLayoutNode *node,
152 const char *prefix);
153
154 MenuMergeFileType menu_layout_node_merge_file_get_type (MenuLayoutNode *node);
155 void menu_layout_node_merge_file_set_type (MenuLayoutNode *node,
156 MenuMergeFileType type);
157
158 MenuLayoutMergeType menu_layout_node_merge_get_type (MenuLayoutNode *node);
159
160 void menu_layout_node_default_layout_get_values (MenuLayoutNode *node,
161 MenuLayoutValues *values);
162 void menu_layout_node_menuname_get_values (MenuLayoutNode *node,
163 MenuLayoutValues *values);
164
165 typedef void (* MenuLayoutNodeEntriesChangedFunc) (MenuLayoutNode *node,
166 gpointer user_data);
167
168 void menu_layout_node_root_add_entries_monitor (MenuLayoutNode *node,
169 MenuLayoutNodeEntriesChangedFunc callback,
170 gpointer user_data);
171 void menu_layout_node_root_remove_entries_monitor (MenuLayoutNode *node,
172 MenuLayoutNodeEntriesChangedFunc callback,
173 gpointer user_data);
174
175 G_END_DECLS
176
177 #endif /* __MENU_LAYOUT_H__ */
0 /*
1 * Copyright (C) 2005 Red Hat, Inc.
2 * Copyright (C) 2006 Mark McLoughlin
3 * Copyright (C) 2007 Sebastian Dröge
4 * Copyright (C) 2008 Vincent Untz
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
21
22 #include <config.h>
23
24 #include "menu-monitor.h"
25
26 #include <gio/gio.h>
27
28 #include "menu-util.h"
29
30 struct MenuMonitor
31 {
32 char *path;
33 guint refcount;
34
35 GSList *notifies;
36
37 GFileMonitor *monitor;
38
39 guint is_directory : 1;
40 };
41
42 typedef struct
43 {
44 MenuMonitor *monitor;
45 MenuMonitorEvent event;
46 char *path;
47 } MenuMonitorEventInfo;
48
49 typedef struct
50 {
51 MenuMonitorNotifyFunc notify_func;
52 gpointer user_data;
53 guint refcount;
54 } MenuMonitorNotify;
55
56 static MenuMonitorNotify *menu_monitor_notify_ref (MenuMonitorNotify *notify);
57 static void menu_monitor_notify_unref (MenuMonitorNotify *notify);
58
59 static GHashTable *monitors_registry = NULL;
60 static guint events_idle_handler = 0;
61 static GSList *pending_events = NULL;
62
63 static void
64 invoke_notifies (MenuMonitor *monitor,
65 MenuMonitorEvent event,
66 const char *path)
67 {
68 GSList *copy;
69 GSList *tmp;
70
71 copy = g_slist_copy (monitor->notifies);
72 g_slist_foreach (copy,
73 (GFunc) menu_monitor_notify_ref,
74 NULL);
75
76 tmp = copy;
77 while (tmp != NULL)
78 {
79 MenuMonitorNotify *notify = tmp->data;
80 GSList *next = tmp->next;
81
82 if (notify->notify_func)
83 {
84 notify->notify_func (monitor, event, path, notify->user_data);
85 }
86
87 menu_monitor_notify_unref (notify);
88
89 tmp = next;
90 }
91
92 g_slist_free (copy);
93 }
94
95 static gboolean
96 emit_events_in_idle (void)
97 {
98 GSList *events_to_emit;
99 GSList *tmp;
100
101 events_to_emit = pending_events;
102
103 pending_events = NULL;
104 events_idle_handler = 0;
105
106 tmp = events_to_emit;
107 while (tmp != NULL)
108 {
109 MenuMonitorEventInfo *event_info = tmp->data;
110
111 menu_monitor_ref (event_info->monitor);
112
113 tmp = tmp->next;
114 }
115
116 tmp = events_to_emit;
117 while (tmp != NULL)
118 {
119 MenuMonitorEventInfo *event_info = tmp->data;
120
121 invoke_notifies (event_info->monitor,
122 event_info->event,
123 event_info->path);
124
125 menu_monitor_unref (event_info->monitor);
126 event_info->monitor = NULL;
127
128 g_free (event_info->path);
129 event_info->path = NULL;
130
131 event_info->event = MENU_MONITOR_EVENT_INVALID;
132
133 g_free (event_info);
134
135 tmp = tmp->next;
136 }
137
138 g_slist_free (events_to_emit);
139
140 return FALSE;
141 }
142
143 static void
144 menu_monitor_queue_event (MenuMonitorEventInfo *event_info)
145 {
146 pending_events = g_slist_append (pending_events, event_info);
147
148 if (events_idle_handler == 0)
149 {
150 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL);
151 }
152 }
153
154 static inline char *
155 get_registry_key (const char *path,
156 gboolean is_directory)
157 {
158 return g_strdup_printf ("%s:%s",
159 path,
160 is_directory ? "<dir>" : "<file>");
161 }
162
163 static gboolean
164 monitor_callback (GFileMonitor *monitor,
165 GFile *child,
166 GFile *other_file,
167 GFileMonitorEvent eflags,
168 gpointer user_data)
169 {
170 MenuMonitorEventInfo *event_info;
171 MenuMonitorEvent event;
172 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
173
174 event = MENU_MONITOR_EVENT_INVALID;
175 switch (eflags)
176 {
177 case G_FILE_MONITOR_EVENT_CHANGED:
178 event = MENU_MONITOR_EVENT_CHANGED;
179 break;
180 case G_FILE_MONITOR_EVENT_CREATED:
181 event = MENU_MONITOR_EVENT_CREATED;
182 break;
183 case G_FILE_MONITOR_EVENT_DELETED:
184 event = MENU_MONITOR_EVENT_DELETED;
185 break;
186 default:
187 return TRUE;
188 }
189
190 event_info = g_new0 (MenuMonitorEventInfo, 1);
191
192 event_info->path = g_file_get_path (child);
193 event_info->event = event;
194 event_info->monitor = menu_monitor;
195
196 menu_monitor_queue_event (event_info);
197
198 return TRUE;
199 }
200
201 static MenuMonitor *
202 register_monitor (const char *path,
203 gboolean is_directory)
204 {
205 MenuMonitor *retval;
206 GFile *file;
207
208 retval = g_new0 (MenuMonitor, 1);
209
210 retval->path = g_strdup (path);
211 retval->refcount = 1;
212 retval->is_directory = is_directory != FALSE;
213
214 file = g_file_new_for_path (retval->path);
215
216 if (file == NULL)
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
219 retval->path);
220 return retval;
221 }
222
223 if (retval->is_directory)
224 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
225 NULL, NULL);
226 else
227 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
228 NULL, NULL);
229
230 g_object_unref (G_OBJECT (file));
231
232 if (retval->monitor == NULL)
233 {
234 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
235 retval->path);
236 return retval;
237 }
238
239 g_signal_connect (retval->monitor, "changed",
240 G_CALLBACK (monitor_callback), retval);
241
242 return retval;
243 }
244
245 static MenuMonitor *
246 lookup_monitor (const char *path,
247 gboolean is_directory)
248 {
249 MenuMonitor *retval;
250 char *registry_key;
251
252 retval = NULL;
253
254 registry_key = get_registry_key (path, is_directory);
255
256 if (monitors_registry == NULL)
257 {
258 monitors_registry = g_hash_table_new_full (g_str_hash,
259 g_str_equal,
260 g_free,
261 NULL);
262 }
263 else
264 {
265 retval = g_hash_table_lookup (monitors_registry, registry_key);
266 }
267
268 if (retval == NULL)
269 {
270 retval = register_monitor (path, is_directory);
271 g_hash_table_insert (monitors_registry, registry_key, retval);
272
273 return retval;
274 }
275 else
276 {
277 g_free (registry_key);
278
279 return menu_monitor_ref (retval);
280 }
281 }
282
283 MenuMonitor *
284 menu_get_file_monitor (const char *path)
285 {
286 g_return_val_if_fail (path != NULL, NULL);
287
288 return lookup_monitor (path, FALSE);
289 }
290
291 MenuMonitor *
292 menu_get_directory_monitor (const char *path)
293 {
294 g_return_val_if_fail (path != NULL, NULL);
295
296 return lookup_monitor (path, TRUE);
297 }
298
299 MenuMonitor *
300 menu_monitor_ref (MenuMonitor *monitor)
301 {
302 g_return_val_if_fail (monitor != NULL, NULL);
303 g_return_val_if_fail (monitor->refcount > 0, NULL);
304
305 monitor->refcount++;
306
307 return monitor;
308 }
309
310 static void
311 menu_monitor_clear_pending_events (MenuMonitor *monitor)
312 {
313 GSList *tmp;
314
315 tmp = pending_events;
316 while (tmp != NULL)
317 {
318 MenuMonitorEventInfo *event_info = tmp->data;
319 GSList *next = tmp->next;
320
321 if (event_info->monitor == monitor)
322 {
323 pending_events = g_slist_delete_link (pending_events, tmp);
324
325 g_free (event_info->path);
326 event_info->path = NULL;
327
328 event_info->monitor = NULL;
329 event_info->event = MENU_MONITOR_EVENT_INVALID;
330
331 g_free (event_info);
332 }
333
334 tmp = next;
335 }
336 }
337
338 void
339 menu_monitor_unref (MenuMonitor *monitor)
340 {
341 char *registry_key;
342
343 g_return_if_fail (monitor != NULL);
344 g_return_if_fail (monitor->refcount > 0);
345
346 if (--monitor->refcount > 0)
347 return;
348
349 registry_key = get_registry_key (monitor->path, monitor->is_directory);
350 g_hash_table_remove (monitors_registry, registry_key);
351 g_free (registry_key);
352
353 if (g_hash_table_size (monitors_registry) == 0)
354 {
355 g_hash_table_destroy (monitors_registry);
356 monitors_registry = NULL;
357 }
358
359 if (monitor->monitor)
360 {
361 g_file_monitor_cancel (monitor->monitor);
362 g_object_unref (monitor->monitor);
363 monitor->monitor = NULL;
364 }
365
366 g_slist_foreach (monitor->notifies, (GFunc) menu_monitor_notify_unref, NULL);
367 g_slist_free (monitor->notifies);
368 monitor->notifies = NULL;
369
370 menu_monitor_clear_pending_events (monitor);
371
372 g_free (monitor->path);
373 monitor->path = NULL;
374
375 g_free (monitor);
376 }
377
378 static MenuMonitorNotify *
379 menu_monitor_notify_ref (MenuMonitorNotify *notify)
380 {
381 g_return_val_if_fail (notify != NULL, NULL);
382 g_return_val_if_fail (notify->refcount > 0, NULL);
383
384 notify->refcount++;
385
386 return notify;
387 }
388
389 static void
390 menu_monitor_notify_unref (MenuMonitorNotify *notify)
391 {
392 g_return_if_fail (notify != NULL);
393 g_return_if_fail (notify->refcount > 0);
394
395 if (--notify->refcount > 0)
396 return;
397
398 g_free (notify);
399 }
400
401 void
402 menu_monitor_add_notify (MenuMonitor *monitor,
403 MenuMonitorNotifyFunc notify_func,
404 gpointer user_data)
405 {
406 MenuMonitorNotify *notify;
407 GSList *tmp;
408
409 g_return_if_fail (monitor != NULL);
410 g_return_if_fail (notify_func != NULL);
411
412 tmp = monitor->notifies;
413 while (tmp != NULL)
414 {
415 notify = tmp->data;
416
417 if (notify->notify_func == notify_func &&
418 notify->user_data == user_data)
419 break;
420
421 tmp = tmp->next;
422 }
423
424 if (tmp == NULL)
425 {
426 notify = g_new0 (MenuMonitorNotify, 1);
427 notify->notify_func = notify_func;
428 notify->user_data = user_data;
429 notify->refcount = 1;
430
431 monitor->notifies = g_slist_append (monitor->notifies, notify);
432 }
433 }
434
435 void
436 menu_monitor_remove_notify (MenuMonitor *monitor,
437 MenuMonitorNotifyFunc notify_func,
438 gpointer user_data)
439 {
440 GSList *tmp;
441
442 tmp = monitor->notifies;
443 while (tmp != NULL)
444 {
445 MenuMonitorNotify *notify = tmp->data;
446 GSList *next = tmp->next;
447
448 if (notify->notify_func == notify_func &&
449 notify->user_data == user_data)
450 {
451 notify->notify_func = NULL;
452 notify->user_data = NULL;
453 menu_monitor_notify_unref (notify);
454
455 monitor->notifies = g_slist_delete_link (monitor->notifies, tmp);
456 }
457
458 tmp = next;
459 }
460 }
0 /*
1 * Copyright (C) 2005 Red Hat, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 02111-1307, USA.
17 */
18
19 #ifndef __MENU_MONITOR_H__
20 #define __MENU_MONITOR_H__
21
22 #include <glib.h>
23
24 G_BEGIN_DECLS
25
26 typedef struct MenuMonitor MenuMonitor;
27
28 typedef enum
29 {
30 MENU_MONITOR_EVENT_INVALID = 0,
31 MENU_MONITOR_EVENT_CREATED = 1,
32 MENU_MONITOR_EVENT_DELETED = 2,
33 MENU_MONITOR_EVENT_CHANGED = 3
34 } MenuMonitorEvent;
35
36 typedef void (*MenuMonitorNotifyFunc) (MenuMonitor *monitor,
37 MenuMonitorEvent event,
38 const char *path,
39 gpointer user_data);
40
41
42 MenuMonitor *menu_get_file_monitor (const char *path);
43 MenuMonitor *menu_get_directory_monitor (const char *path);
44
45 MenuMonitor *menu_monitor_ref (MenuMonitor *monitor);
46 void menu_monitor_unref (MenuMonitor *monitor);
47
48 void menu_monitor_add_notify (MenuMonitor *monitor,
49 MenuMonitorNotifyFunc notify_func,
50 gpointer user_data);
51 void menu_monitor_remove_notify (MenuMonitor *monitor,
52 MenuMonitorNotifyFunc notify_func,
53 gpointer user_data);
54
55 G_END_DECLS
56
57 #endif /* __MENU_MONITOR_H__ */
0 /* Random utility functions for menu code */
1
2 /*
3 * Copyright (C) 2003 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21 #include <config.h>
22
23 #include "menu-util.h"
24
25 #include <stdio.h>
26 #include <stdarg.h>
27
28
29 #ifdef G_ENABLE_DEBUG
30
31 static gboolean verbose = FALSE;
32 static gboolean initted = FALSE;
33
34 static inline gboolean
35 menu_verbose_enabled (void)
36 {
37 if (!initted)
38 {
39 verbose = g_getenv ("MENU_VERBOSE") != NULL;
40 initted = TRUE;
41 }
42
43 return verbose;
44 }
45
46 static int
47 utf8_fputs (const char *str,
48 FILE *f)
49 {
50 char *l;
51 int ret;
52
53 l = g_locale_from_utf8 (str, -1, NULL, NULL, NULL);
54
55 if (l == NULL)
56 ret = fputs (str, f); /* just print it anyway, better than nothing */
57 else
58 ret = fputs (l, f);
59
60 g_free (l);
61
62 return ret;
63 }
64
65 void
66 menu_verbose (const char *format,
67 ...)
68 {
69 va_list args;
70 char *str;
71
72 if (!menu_verbose_enabled ())
73 return;
74
75 va_start (args, format);
76 str = g_strdup_vprintf (format, args);
77 va_end (args);
78
79 utf8_fputs (str, stderr);
80 fflush (stderr);
81
82 g_free (str);
83 }
84
85 static void append_to_string (MenuLayoutNode *node,
86 gboolean onelevel,
87 int depth,
88 GString *str);
89
90 static void
91 append_spaces (GString *str,
92 int depth)
93 {
94 while (depth > 0)
95 {
96 g_string_append_c (str, ' ');
97 --depth;
98 }
99 }
100
101 static void
102 append_children (MenuLayoutNode *node,
103 int depth,
104 GString *str)
105 {
106 MenuLayoutNode *iter;
107
108 iter = menu_layout_node_get_children (node);
109 while (iter != NULL)
110 {
111 append_to_string (iter, FALSE, depth, str);
112
113 iter = menu_layout_node_get_next (iter);
114 }
115 }
116
117 static void
118 append_simple_with_attr (MenuLayoutNode *node,
119 int depth,
120 const char *node_name,
121 const char *attr_name,
122 const char *attr_value,
123 GString *str)
124 {
125 const char *content;
126
127 append_spaces (str, depth);
128
129 if ((content = menu_layout_node_get_content (node)))
130 {
131 char *escaped;
132
133 escaped = g_markup_escape_text (content, -1);
134
135 if (attr_name && attr_value)
136 {
137 char *attr_escaped;
138
139 attr_escaped = g_markup_escape_text (attr_value, -1);
140
141 g_string_append_printf (str, "<%s %s=\"%s\">%s</%s>\n",
142 node_name, attr_name,
143 attr_escaped, escaped, node_name);
144
145 g_free (attr_escaped);
146 }
147 else
148 {
149 g_string_append_printf (str, "<%s>%s</%s>\n",
150 node_name, escaped, node_name);
151 }
152
153 g_free (escaped);
154 }
155 else
156 {
157 if (attr_name && attr_value)
158 {
159 char *attr_escaped;
160
161 attr_escaped = g_markup_escape_text (attr_value, -1);
162
163 g_string_append_printf (str, "<%s %s=\"%s\"/>\n",
164 node_name, attr_name,
165 attr_escaped);
166
167 g_free (attr_escaped);
168 }
169 else
170 {
171 g_string_append_printf (str, "<%s/>\n", node_name);
172 }
173 }
174 }
175
176 static void
177 append_layout (MenuLayoutNode *node,
178 int depth,
179 const char *node_name,
180 MenuLayoutValues *layout_values,
181 GString *str)
182 {
183 const char *content;
184
185 append_spaces (str, depth);
186
187 if ((content = menu_layout_node_get_content (node)))
188 {
189 char *escaped;
190
191 escaped = g_markup_escape_text (content, -1);
192
193 g_string_append_printf (str,
194 "<%s show_empty=\"%s\" inline=\"%s\" inline_header=\"%s\""
195 " inline_alias=\"%s\" inline_limit=\"%d\">%s</%s>\n",
196 node_name,
197 layout_values->show_empty ? "true" : "false",
198 layout_values->inline_menus ? "true" : "false",
199 layout_values->inline_header ? "true" : "false",
200 layout_values->inline_alias ? "true" : "false",
201 layout_values->inline_limit,
202 escaped,
203 node_name);
204
205 g_free (escaped);
206 }
207 else
208 {
209 g_string_append_printf (str,
210 "<%s show_empty=\"%s\" inline=\"%s\" inline_header=\"%s\""
211 " inline_alias=\"%s\" inline_limit=\"%d\"/>\n",
212 node_name,
213 layout_values->show_empty ? "true" : "false",
214 layout_values->inline_menus ? "true" : "false",
215 layout_values->inline_header ? "true" : "false",
216 layout_values->inline_alias ? "true" : "false",
217 layout_values->inline_limit);
218 }
219 }
220
221 static void
222 append_merge (MenuLayoutNode *node,
223 int depth,
224 const char *node_name,
225 MenuLayoutMergeType merge_type,
226 GString *str)
227 {
228 const char *merge_type_str;
229
230 merge_type_str = NULL;
231
232 switch (merge_type)
233 {
234 case MENU_LAYOUT_MERGE_NONE:
235 merge_type_str = "none";
236 break;
237
238 case MENU_LAYOUT_MERGE_MENUS:
239 merge_type_str = "menus";
240 break;
241
242 case MENU_LAYOUT_MERGE_FILES:
243 merge_type_str = "files";
244 break;
245
246 case MENU_LAYOUT_MERGE_ALL:
247 merge_type_str = "all";
248 break;
249
250 default:
251 g_assert_not_reached ();
252 break;
253 }
254
255 append_simple_with_attr (node, depth, node_name, "type", merge_type_str, str);
256 }
257
258 static void
259 append_simple (MenuLayoutNode *node,
260 int depth,
261 const char *node_name,
262 GString *str)
263 {
264 append_simple_with_attr (node, depth, node_name, NULL, NULL, str);
265 }
266
267 static void
268 append_start (MenuLayoutNode *node,
269 int depth,
270 const char *node_name,
271 GString *str)
272 {
273 append_spaces (str, depth);
274
275 g_string_append_printf (str, "<%s>\n", node_name);
276 }
277
278 static void
279 append_end (MenuLayoutNode *node,
280 int depth,
281 const char *node_name,
282 GString *str)
283 {
284 append_spaces (str, depth);
285
286 g_string_append_printf (str, "</%s>\n", node_name);
287 }
288
289 static void
290 append_container (MenuLayoutNode *node,
291 gboolean onelevel,
292 int depth,
293 const char *node_name,
294 GString *str)
295 {
296 append_start (node, depth, node_name, str);
297 if (!onelevel)
298 {
299 append_children (node, depth + 2, str);
300 append_end (node, depth, node_name, str);
301 }
302 }
303
304 static void
305 append_to_string (MenuLayoutNode *node,
306 gboolean onelevel,
307 int depth,
308 GString *str)
309 {
310 MenuLayoutValues layout_values;
311
312 switch (menu_layout_node_get_type (node))
313 {
314 case MENU_LAYOUT_NODE_ROOT:
315 if (!onelevel)
316 append_children (node, depth - 1, str); /* -1 to ignore depth of root */
317 else
318 append_start (node, depth - 1, "Root", str);
319 break;
320
321 case MENU_LAYOUT_NODE_PASSTHROUGH:
322 g_string_append (str,
323 menu_layout_node_get_content (node));
324 g_string_append_c (str, '\n');
325 break;
326
327 case MENU_LAYOUT_NODE_MENU:
328 append_container (node, onelevel, depth, "Menu", str);
329 break;
330
331 case MENU_LAYOUT_NODE_APP_DIR:
332 append_simple (node, depth, "AppDir", str);
333 break;
334
335 case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS:
336 append_simple (node, depth, "DefaultAppDirs", str);
337 break;
338
339 case MENU_LAYOUT_NODE_DIRECTORY_DIR:
340 append_simple (node, depth, "DirectoryDir", str);
341 break;
342
343 case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS:
344 append_simple (node, depth, "DefaultDirectoryDirs", str);
345 break;
346
347 case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS:
348 append_simple (node, depth, "DefaultMergeDirs", str);
349 break;
350
351 case MENU_LAYOUT_NODE_NAME:
352 append_simple (node, depth, "Name", str);
353 break;
354
355 case MENU_LAYOUT_NODE_DIRECTORY:
356 append_simple (node, depth, "Directory", str);
357 break;
358
359 case MENU_LAYOUT_NODE_ONLY_UNALLOCATED:
360 append_simple (node, depth, "OnlyUnallocated", str);
361 break;
362
363 case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED:
364 append_simple (node, depth, "NotOnlyUnallocated", str);
365 break;
366
367 case MENU_LAYOUT_NODE_INCLUDE:
368 append_container (node, onelevel, depth, "Include", str);
369 break;
370
371 case MENU_LAYOUT_NODE_EXCLUDE:
372 append_container (node, onelevel, depth, "Exclude", str);
373 break;
374
375 case MENU_LAYOUT_NODE_FILENAME:
376 append_simple (node, depth, "Filename", str);
377 break;
378
379 case MENU_LAYOUT_NODE_CATEGORY:
380 append_simple (node, depth, "Category", str);
381 break;
382
383 case MENU_LAYOUT_NODE_ALL:
384 append_simple (node, depth, "All", str);
385 break;
386
387 case MENU_LAYOUT_NODE_AND:
388 append_container (node, onelevel, depth, "And", str);
389 break;
390
391 case MENU_LAYOUT_NODE_OR:
392 append_container (node, onelevel, depth, "Or", str);
393 break;
394
395 case MENU_LAYOUT_NODE_NOT:
396 append_container (node, onelevel, depth, "Not", str);
397 break;
398
399 case MENU_LAYOUT_NODE_MERGE_FILE:
400 {
401 MenuMergeFileType type;
402
403 type = menu_layout_node_merge_file_get_type (node);
404
405 append_simple_with_attr (node, depth, "MergeFile",
406 "type", type == MENU_MERGE_FILE_TYPE_PARENT ? "parent" : "path",
407 str);
408 break;
409 }
410
411 case MENU_LAYOUT_NODE_MERGE_DIR:
412 append_simple (node, depth, "MergeDir", str);
413 break;
414
415 case MENU_LAYOUT_NODE_LEGACY_DIR:
416 append_simple_with_attr (node, depth, "LegacyDir",
417 "prefix", menu_layout_node_legacy_dir_get_prefix (node),
418 str);
419 break;
420
421 case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS:
422 append_simple (node, depth, "KDELegacyDirs", str);
423 break;
424
425 case MENU_LAYOUT_NODE_MOVE:
426 append_container (node, onelevel, depth, "Move", str);
427 break;
428
429 case MENU_LAYOUT_NODE_OLD:
430 append_simple (node, depth, "Old", str);
431 break;
432
433 case MENU_LAYOUT_NODE_NEW:
434 append_simple (node, depth, "New", str);
435 break;
436
437 case MENU_LAYOUT_NODE_DELETED:
438 append_simple (node, depth, "Deleted", str);
439 break;
440
441 case MENU_LAYOUT_NODE_NOT_DELETED:
442 append_simple (node, depth, "NotDeleted", str);
443 break;
444
445 case MENU_LAYOUT_NODE_LAYOUT:
446 append_container (node, onelevel, depth, "Layout", str);
447 break;
448
449 case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
450 menu_layout_node_default_layout_get_values (node, &layout_values);
451 append_layout (node, depth, "DefaultLayout", &layout_values, str);
452 break;
453
454 case MENU_LAYOUT_NODE_MENUNAME:
455 menu_layout_node_menuname_get_values (node, &layout_values);
456 append_layout (node, depth, "MenuName", &layout_values, str);
457 break;
458
459 case MENU_LAYOUT_NODE_SEPARATOR:
460 append_simple (node, depth, "Name", str);
461 break;
462
463 case MENU_LAYOUT_NODE_MERGE:
464 append_merge (node,
465 depth,
466 "Merge",
467 menu_layout_node_merge_get_type (node),
468 str);
469 break;
470
471 default:
472 g_assert_not_reached ();
473 break;
474 }
475 }
476
477 void
478 menu_debug_print_layout (MenuLayoutNode *node,
479 gboolean onelevel)
480 {
481 if (menu_verbose_enabled ())
482 {
483 GString *str;
484
485 str = g_string_new (NULL);
486 append_to_string (node, onelevel, 0, str);
487
488 utf8_fputs (str->str, stderr);
489 fflush (stderr);
490
491 g_string_free (str, TRUE);
492 }
493 }
494
495 #endif /* G_ENABLE_DEBUG */
0 /* Random utility functions for menu code */
1
2 /*
3 * Copyright (C) 2003 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21 #ifndef __MENU_UTIL_H__
22 #define __MENU_UTIL_H__
23
24 #include <glib.h>
25
26 #include "menu-layout.h"
27
28 G_BEGIN_DECLS
29
30 #ifdef G_ENABLE_DEBUG
31
32 void menu_verbose (const char *format, ...) G_GNUC_PRINTF (1, 2);
33
34 void menu_debug_print_layout (MenuLayoutNode *node,
35 gboolean onelevel);
36
37 #else /* !defined(G_ENABLE_DEBUG) */
38
39 #ifdef G_HAVE_ISO_VARARGS
40 #define menu_verbose(...)
41 #elif defined(G_HAVE_GNUC_VARARGS)
42 #define menu_verbose(format...)
43 #else
44 #error "Cannot disable verbose mode due to lack of varargs macros"
45 #endif
46
47 #define menu_debug_print_layout(n,o)
48
49 #endif /* G_ENABLE_DEBUG */
50
51 G_END_DECLS
52
53 #endif /* __MENU_UTIL_H__ */
0 #
1 # No modifications of this Makefile should be necessary.
2 #
3 # This file contains the build instructions for installing OMF files. It is
4 # generally called from the makefiles for particular formats of documentation.
5 #
6 # Note that you must configure your package with --localstatedir=/var
7 # so that the scrollkeeper-update command below will update the database
8 # in the standard scrollkeeper directory.
9 #
10 # If it is impossible to configure with --localstatedir=/var, then
11 # modify the definition of scrollkeeper_localstate_dir so that
12 # it points to the correct location. Note that you must still use
13 # $(localstatedir) in this or when people build RPMs it will update
14 # the real database on their system instead of the one under RPM_BUILD_ROOT.
15 #
16 # Note: This make file is not incorporated into xmldocs.make because, in
17 # general, there will be other documents install besides XML documents
18 # and the makefiles for these formats should also include this file.
19 #
20 # About this file:
21 # This file was derived from scrollkeeper_example2, a package
22 # illustrating how to install documentation and OMF files for use with
23 # ScrollKeeper 0.3.x and 0.4.x. For more information, see:
24 # http://scrollkeeper.sourceforge.net/
25 # Version: 0.1.3 (last updated: March 20, 2002)
26 #
27
28 omf_dest_dir=$(datadir)/omf/@PACKAGE@
29 scrollkeeper_localstate_dir = $(localstatedir)/scrollkeeper
30
31 # At some point, it may be wise to change to something like this:
32 # scrollkeeper_localstate_dir = @SCROLLKEEPER_STATEDIR@
33
34 omf: omf_timestamp
35
36 omf_timestamp: $(omffile)
37 -for file in $(omffile); do \
38 absfile=$(srcdir)/$$file; \
39 test -r $$file && absfile=$$file; \
40 scrollkeeper-preinstall $(docdir)/$(docname).xml $$absfile $$file.out; \
41 done; \
42 touch omf_timestamp
43
44 install-data-hook-omf:
45 $(mkinstalldirs) $(DESTDIR)$(omf_dest_dir)
46 for file in $(omffile); do \
47 absfile=$(srcdir)/$$file.out; \
48 test -r $$file.out && absfile=$$file.out; \
49 $(INSTALL_DATA) $$absfile $(DESTDIR)$(omf_dest_dir)/$$file; \
50 done
51 -scrollkeeper-update -p $(DESTDIR)$(scrollkeeper_localstate_dir) -o $(DESTDIR)$(omf_dest_dir)
52
53 uninstall-local-omf:
54 -for file in $(omffile); do \
55 basefile=`basename $$file`; \
56 rm -f $(DESTDIR)$(omf_dest_dir)/$$basefile; \
57 done
58 -rmdir $(DESTDIR)$(omf_dest_dir)
59 -scrollkeeper-update -p $(DESTDIR)$(scrollkeeper_localstate_dir)
60
61 clean-local-omf:
62 -for file in $(omffile); do \
63 rm -f $$file.out; \
64 done
0 #
1 # No modifications of this Makefile should be necessary.
2 #
3 # To use this template:
4 # 1) Define: figdir, docname, lang, omffile, and entities in
5 # your Makefile.am file for each document directory,
6 # although figdir, omffile, and entities may be empty
7 # 2) Make sure the Makefile in (1) also includes
8 # "include $(top_srcdir)/xmldocs.make" and
9 # "dist-hook: app-dist-hook".
10 # 3) Optionally define 'entities' to hold xml entities which
11 # you would also like installed
12 # 4) Figures must go under $(figdir)/ and be in PNG format
13 # 5) You should only have one document per directory
14 # 6) Note that the figure directory, $(figdir)/, should not have its
15 # own Makefile since this Makefile installs those figures.
16 #
17 # example Makefile.am:
18 # figdir = figures
19 # docname = scrollkeeper-manual
20 # lang = C
21 # omffile=scrollkeeper-manual-C.omf
22 # entities = fdl.xml
23 # include $(top_srcdir)/xmldocs.make
24 # dist-hook: app-dist-hook
25 #
26 # About this file:
27 # This file was taken from scrollkeeper_example2, a package illustrating
28 # how to install documentation and OMF files for use with ScrollKeeper
29 # 0.3.x and 0.4.x. For more information, see:
30 # http://scrollkeeper.sourceforge.net/
31 # Version: 0.1.2 (last updated: March 20, 2002)
32 #
33
34
35 # ********** Begin of section some packagers may need to modify **********
36 # This variable (docdir) specifies where the documents should be installed.
37 # This default value should work for most packages.
38 docdir = $(datadir)/gnome/help/$(docname)/$(lang)
39
40 # ********** You should not have to edit below this line **********
41 xml_files = $(entities) $(docname).xml
42
43 EXTRA_DIST = $(xml_files) $(omffile)
44 CLEANFILES = omf_timestamp
45
46 include $(top_srcdir)/omf.make
47
48 all: omf
49
50 $(docname).xml: $(entities)
51 -ourdir=`pwd`; \
52 cd $(srcdir); \
53 cp $(entities) $$ourdir
54
55 app-dist-hook:
56 if test "$(figdir)"; then \
57 $(mkinstalldirs) $(distdir)/$(figdir); \
58 for file in $(srcdir)/$(figdir)/*.png; do \
59 basefile=`echo $$file | sed -e 's,^.*/,,'`; \
60 $(INSTALL_DATA) $$file $(distdir)/$(figdir)/$$basefile; \
61 done \
62 fi
63
64 install-data-local: omf
65 $(mkinstalldirs) $(DESTDIR)$(docdir)
66 for file in $(xml_files); do \
67 cp $(srcdir)/$$file $(DESTDIR)$(docdir); \
68 done
69 if test "$(figdir)"; then \
70 $(mkinstalldirs) $(DESTDIR)$(docdir)/$(figdir); \
71 for file in $(srcdir)/$(figdir)/*.png; do \
72 basefile=`echo $$file | sed -e 's,^.*/,,'`; \
73 $(INSTALL_DATA) $$file $(DESTDIR)$(docdir)/$(figdir)/$$basefile; \
74 done \
75 fi
76
77 install-data-hook: install-data-hook-omf
78
79 uninstall-local: uninstall-local-doc uninstall-local-omf
80
81 uninstall-local-doc:
82 -if test "$(figdir)"; then \
83 for file in $(srcdir)/$(figdir)/*.png; do \
84 basefile=`echo $$file | sed -e 's,^.*/,,'`; \
85 rm -f $(DESTDIR)$(docdir)/$(figdir)/$$basefile; \
86 done; \
87 rmdir $(DESTDIR)$(docdir)/$(figdir); \
88 fi
89 -for file in $(xml_files); do \
90 rm -f $(DESTDIR)$(docdir)/$$file; \
91 done
92 -rmdir $(DESTDIR)$(docdir)
93
94 clean-local: clean-local-doc clean-local-omf
95
96 # for non-srcdir builds, remove the copied entities.
97 clean-local-doc:
98 if test $(srcdir) != .; then \
99 rm -f $(entities); \
100 fi