Codebase list xapp / upstream/2.0.0
New upstream version 2.0.0 Norbert Preining 3 years ago
41 changed file(s) with 5533 addition(s) and 409 deletion(s). Raw diff Collapse all Expand all
00 # Specify filepatterns you want git to ignore.
11
2 debian/build/
2 obj-x86_64-linux-gnu
33 debian/tmp/
44 debian/*.debhelper
55 debian/gir1.2-xapp-1.0/
55
66 # API Reference
77
8 https://projects.linuxmint.com/xapps/reference/index.html
8 https://projects.linuxmint.com/xapp/reference/index.html
99
1010 # xapp-common
1111
0 # This file is sourced by Xsession(5), not executed.
1
2 if [ -z "$GTK_MODULES" ] ; then
3 GTK_MODULES="xapp-gtk3-module"
4 else
5 GTK_MODULES="$GTK_MODULES:xapp-gtk3-module"
6 fi
7
8 export GTK_MODULES
0 install_data(['80xapp-gtk3-module'],
1 install_dir: join_paths(get_option('sysconfdir'), 'X11', 'Xsession.d')
2 )
0 xapp (2.0.0) ulyssa; urgency=medium
1
2 [ Michael Webster ]
3 * Bump for favorites support
4 * .gitignore: Ignore the new debian build folder.
5 * favorites: expose xapp_favorites_rename (), it was mistakenly made private during some cleanup.
6 * xapp-gtk3-module.c: Don't initialize favorites until they're needed.
7 * xapp-gtk3-module.c: Register the favorites uri scheme immediately, rather than when XAppFavorites is instantiated.
8 * xapp-favorites: Add missing pointer to the DestroyData.
9
10 [ Clement Lefebvre ]
11 * l10n: Update POT
12
13 -- Clement Lefebvre <root@linuxmint.com> Wed, 25 Nov 2020 11:27:06 +0000
14
015 xapp (1.8.9) ulyana; urgency=medium
116
217 [ Michael Webster ]
+0
-1
debian/clean less more
0 debian/build/
+0
-1
debian/compat less more
0 10
22 Priority: optional
33 Maintainer: Clement Lefebvre <root@linuxmint.com>
44 Build-Depends:
5 debhelper (>= 10),
5 debhelper-compat (= 12),
66 dh-python,
77 dpkg-dev (>= 1.15.1),
88 gobject-introspection (>= 0.10.2-1~),
0 usr/lib/*/libxapp.so.1*
0 usr/lib/*/libxapp.so.*
11 usr/libexec/xapps/sn-watcher/*
2 usr/lib/*/gtk-3.0/modules
3 /etc/X11
00 #!/usr/bin/make -f
11
2 # FIXME: much of this can be removed once we move off of xenial/mint 18 base support
3 # meson support is baked in
2 export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed -Wl,-z,now
43
54 %:
65 dh $@ --with=gir,python3
76
87 override_dh_auto_configure:
9 mkdir -p debian/build
10 meson debian/build \
11 --prefix=/usr \
12 --buildtype=plain \
8 dh_auto_configure -- \
9 --libexecdir=/usr/libexec \
1310 -D docs=true \
1411 -D deprecated_warnings=false
1512
1613 override_dh_strip:
1714 dh_strip --dbg-package=libxapp-dbg
18
19 override_dh_auto_build:
20 ninja -C debian/build
21
22 override_dh_auto_test:
23 ninja -C debian/build test
24
25 override_dh_auto_install:
26 DESTDIR=${CURDIR}/debian/tmp \
27 ninja -C debian/build install
1010 main_xml: 'xapp-docs.xml',
1111 scan_args: ['--rebuild-types'],
1212 mkdb_args: ['--xml-mode', '--output-format=xml'],
13 )
13 ignore_headers: ['favorite-vfs-file.h', 'favorite-vfs-file-enumerator.h', 'favorite-vfs-file-monitor.h']
14 )
1515
1616 <chapter>
1717 <title>API reference</title>
18 <xi:include href="xml/xapp-favorites.xml"/>
1819 <xi:include href="xml/xapp-gtk-window.xml"/>
1920 <xi:include href="xml/xapp-icon-chooser-button.xml"/>
2021 <xi:include href="xml/xapp-icon-chooser-dialog.xml"/>
0 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1 <svg
2 xmlns:dc="http://purl.org/dc/elements/1.1/"
3 xmlns:cc="http://creativecommons.org/ns#"
4 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
5 xmlns:svg="http://www.w3.org/2000/svg"
6 xmlns="http://www.w3.org/2000/svg"
7 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
8 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
9 id="svg4060"
10 height="16"
11 width="16"
12 version="1.1"
13 sodipodi:docname="xapp-favorite-symbolic.svg"
14 inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
15 <sodipodi:namedview
16 pagecolor="#ffffff"
17 bordercolor="#666666"
18 borderopacity="1"
19 objecttolerance="10"
20 gridtolerance="10"
21 guidetolerance="10"
22 inkscape:pageopacity="0"
23 inkscape:pageshadow="2"
24 inkscape:window-width="1920"
25 inkscape:window-height="989"
26 id="namedview6"
27 showgrid="true"
28 inkscape:zoom="41.7193"
29 inkscape:cx="0.93745069"
30 inkscape:cy="8.3555532"
31 inkscape:window-x="0"
32 inkscape:window-y="27"
33 inkscape:window-maximized="1"
34 inkscape:current-layer="svg4060">
35 <inkscape:grid
36 type="xygrid"
37 id="grid816" />
38 </sodipodi:namedview>
39 <defs
40 id="defs4062" />
41 <metadata
42 id="metadata4065">
43 <rdf:RDF>
44 <cc:Work
45 rdf:about="">
46 <dc:format>image/svg+xml</dc:format>
47 <dc:type
48 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
49 <dc:title />
50 </cc:Work>
51 </rdf:RDF>
52 </metadata>
53 <path
54 id="path3649"
55 style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.92937016;marker:none;enable-background:accumulate"
56 d="M 9.087891,10.5 5.9983503,8.874203 2.9075508,10.497604 3.4990553,7.0568778 1,4.6190107 4.4551098,4.1183218 6.0014083,0.98823644 7.5452799,4.1195202 11,4.6228883 8.4990549,7.0588163 Z M 11,9.0000004 V 11 H 9.0000002 v 2 H 11 v 2 h 2 v -2 h 2 V 11 H 13 V 9.0000004 Z" />
57 </svg>
0 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1 <svg
2 xmlns:dc="http://purl.org/dc/elements/1.1/"
3 xmlns:cc="http://creativecommons.org/ns#"
4 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
5 xmlns:svg="http://www.w3.org/2000/svg"
6 xmlns="http://www.w3.org/2000/svg"
7 xmlns:xlink="http://www.w3.org/1999/xlink"
8 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
9 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
10 id="svg4060"
11 height="16"
12 width="16"
13 version="1.1"
14 sodipodi:docname="xapp-unfavorite-symbolic.svg"
15 inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
16 <sodipodi:namedview
17 pagecolor="#ffffff"
18 bordercolor="#666666"
19 borderopacity="1"
20 objecttolerance="10"
21 gridtolerance="10"
22 guidetolerance="10"
23 inkscape:pageopacity="0"
24 inkscape:pageshadow="2"
25 inkscape:window-width="1920"
26 inkscape:window-height="989"
27 id="namedview6"
28 showgrid="true"
29 inkscape:zoom="41.7193"
30 inkscape:cx="1.4574888"
31 inkscape:cy="7.7311177"
32 inkscape:window-x="0"
33 inkscape:window-y="27"
34 inkscape:window-maximized="1"
35 inkscape:current-layer="svg4060">
36 <inkscape:grid
37 type="xygrid"
38 id="grid816" />
39 </sodipodi:namedview>
40 <defs
41 id="defs4062">
42 <linearGradient
43 inkscape:collect="always"
44 xlink:href="#linearGradient5261"
45 id="linearGradient5263"
46 x1="1.013729"
47 y1="8"
48 x2="15"
49 y2="8"
50 gradientUnits="userSpaceOnUse"
51 gradientTransform="matrix(1.1111111,0,0,1.1111111,-0.11111111,-0.0555556)" />
52 <linearGradient
53 inkscape:collect="always"
54 id="linearGradient5261">
55 <stop
56 style="stop-color:#bebebe;stop-opacity:1;"
57 offset="0"
58 id="stop5257" />
59 <stop
60 style="stop-color:#bebebe;stop-opacity:0;"
61 offset="1"
62 id="stop5259" />
63 </linearGradient>
64 </defs>
65 <metadata
66 id="metadata4065">
67 <rdf:RDF>
68 <cc:Work
69 rdf:about="">
70 <dc:format>image/svg+xml</dc:format>
71 <dc:type
72 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
73 <dc:title />
74 </cc:Work>
75 </rdf:RDF>
76 </metadata>
77 <path
78 id="path836"
79 style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:url(#linearGradient5263);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.20698357;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
80 d="M 6.00217,0.98828111 4.4548611,4.1176211 1,4.6189233 3.5,7.0559889 2.9075522,10.49783 5.99783,8.8745656 9.0881078,10.5 8.5,7.05816 11,4.6232633 7.5451389,4.1197911 Z M 11,11 v 0 H 9.0000002 v 2 H 11 v 0 h 2 v 0 h 2 v -2 h -2 v 0 z" />
81 </svg>
0 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1 <svg
2 xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
3 xmlns:dc="http://purl.org/dc/elements/1.1/"
4 xmlns:cc="http://creativecommons.org/ns#"
5 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6 xmlns:svg="http://www.w3.org/2000/svg"
7 xmlns="http://www.w3.org/2000/svg"
8 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
9 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
10 inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
11 sodipodi:docname="xapp-favorites-symbolic.svg"
12 id="svg4"
13 version="1.1"
14 width="48"
15 height="48">
16 <metadata
17 id="metadata10">
18 <rdf:RDF>
19 <cc:Work
20 rdf:about="">
21 <dc:format>image/svg+xml</dc:format>
22 <dc:type
23 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
24 <dc:title></dc:title>
25 </cc:Work>
26 </rdf:RDF>
27 </metadata>
28 <defs
29 id="defs8">
30 <linearGradient
31 id="linearGradient7722"
32 osb:paint="gradient">
33 <stop
34 style="stop-color:#000000;stop-opacity:1;"
35 offset="0"
36 id="stop7718" />
37 <stop
38 style="stop-color:#000000;stop-opacity:0;"
39 offset="1"
40 id="stop7720" />
41 </linearGradient>
42 <filter
43 style="color-interpolation-filters:sRGB;"
44 inkscape:label="Feather"
45 id="filter1726">
46 <feGaussianBlur
47 stdDeviation="5"
48 result="blur"
49 id="feGaussianBlur1718" />
50 <feComposite
51 in="SourceGraphic"
52 in2="blur"
53 operator="atop"
54 result="composite1"
55 id="feComposite1720" />
56 <feComposite
57 in2="composite1"
58 operator="in"
59 result="composite2"
60 id="feComposite1722" />
61 <feComposite
62 in2="composite2"
63 operator="in"
64 result="composite3"
65 id="feComposite1724" />
66 </filter>
67 </defs>
68 <sodipodi:namedview
69 pagecolor="#ffffff"
70 bordercolor="#666666"
71 borderopacity="1"
72 objecttolerance="10"
73 gridtolerance="10"
74 guidetolerance="10"
75 inkscape:pageopacity="0"
76 inkscape:pageshadow="2"
77 inkscape:window-width="1920"
78 inkscape:window-height="989"
79 id="namedview6"
80 showgrid="false"
81 inkscape:zoom="12.525139"
82 inkscape:cx="27.539377"
83 inkscape:cy="20.700947"
84 inkscape:window-x="0"
85 inkscape:window-y="27"
86 inkscape:window-maximized="1"
87 inkscape:current-layer="svg4" />
88 <path
89 d="M 23.953096,2.4741988 17.470049,15.628515 3,17.78943 13.429644,28.03116 10.9867,42.5 23.953096,35.641717 36.91799,42.5 34.476548,28.03116 45,17.78943 30.435543,15.628515 Z"
90 id="path2"
91 style="fill:#bebebe;fill-opacity:1;stroke:none;stroke-width:1.12517142;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0;paint-order:normal"
92 inkscape:connector-curvature="0"
93 sodipodi:nodetypes="ccccccccccc" />
94 </svg>
0 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1 <svg
2 xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
3 xmlns:dc="http://purl.org/dc/elements/1.1/"
4 xmlns:cc="http://creativecommons.org/ns#"
5 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6 xmlns:svg="http://www.w3.org/2000/svg"
7 xmlns="http://www.w3.org/2000/svg"
8 xmlns:xlink="http://www.w3.org/1999/xlink"
9 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
10 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11 inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
12 sodipodi:docname="xapp-favorites.svg"
13 id="svg4"
14 version="1.1"
15 width="96"
16 height="96"
17 style="enable-background:new">
18 <metadata
19 id="metadata10">
20 <rdf:RDF>
21 <cc:Work
22 rdf:about="">
23 <dc:format>image/svg+xml</dc:format>
24 <dc:type
25 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
26 <dc:title></dc:title>
27 </cc:Work>
28 </rdf:RDF>
29 </metadata>
30 <defs
31 id="defs8">
32 <linearGradient
33 x1="48"
34 y1="90"
35 x2="48"
36 gradientUnits="userSpaceOnUse"
37 y2="5.9877"
38 id="linearGradient6452">
39 <stop
40 id="stop6448"
41 offset="0"
42 stop-color="#b4b4b4" />
43 <stop
44 id="stop6450"
45 offset="1"
46 stop-color="#e6e6e6" />
47 </linearGradient>
48 <linearGradient
49 id="linearGradient4984"
50 osb:paint="solid">
51 <stop
52 style="stop-color:#ffffff;stop-opacity:1;"
53 offset="0"
54 id="stop4982" />
55 </linearGradient>
56 <linearGradient
57 id="linearGradient7722"
58 osb:paint="gradient">
59 <stop
60 style="stop-color:#000000;stop-opacity:1;"
61 offset="0"
62 id="stop7718" />
63 <stop
64 style="stop-color:#000000;stop-opacity:0;"
65 offset="1"
66 id="stop7720" />
67 </linearGradient>
68 <filter
69 style="color-interpolation-filters:sRGB"
70 inkscape:label="Feather"
71 id="filter1726">
72 <feGaussianBlur
73 stdDeviation="5"
74 result="blur"
75 id="feGaussianBlur1718" />
76 <feComposite
77 in="SourceGraphic"
78 in2="blur"
79 operator="atop"
80 result="composite1"
81 id="feComposite1720" />
82 <feComposite
83 in2="composite1"
84 operator="in"
85 result="composite2"
86 id="feComposite1722" />
87 <feComposite
88 in2="composite2"
89 operator="in"
90 result="composite3"
91 id="feComposite1724" />
92 </filter>
93 <filter
94 style="color-interpolation-filters:sRGB"
95 inkscape:label="Drop Shadow"
96 id="filter1795">
97 <feFlood
98 flood-opacity="0.498039"
99 flood-color="rgb(0,0,0)"
100 result="flood"
101 id="feFlood1785" />
102 <feComposite
103 in="flood"
104 in2="SourceGraphic"
105 operator="in"
106 result="composite1"
107 id="feComposite1787" />
108 <feGaussianBlur
109 in="composite1"
110 stdDeviation="1"
111 result="blur"
112 id="feGaussianBlur1789" />
113 <feOffset
114 dx="1"
115 dy="1"
116 result="offset"
117 id="feOffset1791" />
118 <feComposite
119 in="SourceGraphic"
120 in2="offset"
121 operator="over"
122 result="composite2"
123 id="feComposite1793" />
124 </filter>
125 <filter
126 style="color-interpolation-filters:sRGB"
127 inkscape:label="Drop Shadow"
128 id="filter1819">
129 <feFlood
130 flood-opacity="0.498039"
131 flood-color="rgb(0,0,0)"
132 result="flood"
133 id="feFlood1809" />
134 <feComposite
135 in="flood"
136 in2="SourceGraphic"
137 operator="in"
138 result="composite1"
139 id="feComposite1811" />
140 <feGaussianBlur
141 in="composite1"
142 stdDeviation="1"
143 result="blur"
144 id="feGaussianBlur1813" />
145 <feOffset
146 dx="1"
147 dy="1"
148 result="offset"
149 id="feOffset1815" />
150 <feComposite
151 in="SourceGraphic"
152 in2="offset"
153 operator="over"
154 result="composite2"
155 id="feComposite1817" />
156 </filter>
157 <filter
158 style="color-interpolation-filters:sRGB"
159 inkscape:label="Drop Shadow"
160 id="filter1720">
161 <feFlood
162 flood-opacity="0.498039"
163 flood-color="rgb(0,0,0)"
164 result="flood"
165 id="feFlood1710" />
166 <feComposite
167 in="flood"
168 in2="SourceGraphic"
169 operator="in"
170 result="composite1"
171 id="feComposite1712" />
172 <feGaussianBlur
173 in="composite1"
174 stdDeviation="1"
175 result="blur"
176 id="feGaussianBlur1714" />
177 <feOffset
178 dx="1"
179 dy="1"
180 result="offset"
181 id="feOffset1716" />
182 <feComposite
183 in="SourceGraphic"
184 in2="offset"
185 operator="over"
186 result="composite2"
187 id="feComposite1718" />
188 </filter>
189 <filter
190 style="color-interpolation-filters:sRGB"
191 inkscape:label="Drop Shadow"
192 id="filter1740">
193 <feFlood
194 flood-opacity="0.498039"
195 flood-color="rgb(0,0,0)"
196 result="flood"
197 id="feFlood1730" />
198 <feComposite
199 in="flood"
200 in2="SourceGraphic"
201 operator="in"
202 result="composite1"
203 id="feComposite1732" />
204 <feGaussianBlur
205 in="composite1"
206 stdDeviation="1"
207 result="blur"
208 id="feGaussianBlur1734" />
209 <feOffset
210 dx="1"
211 dy="1"
212 result="offset"
213 id="feOffset1736" />
214 <feComposite
215 in="SourceGraphic"
216 in2="offset"
217 operator="over"
218 result="composite2"
219 id="feComposite1738" />
220 </filter>
221 <linearGradient
222 id="g"
223 y2="90.238998"
224 xlink:href="#a"
225 gradientUnits="userSpaceOnUse"
226 x2="32.250999"
227 gradientTransform="matrix(1.0238,0,0,-1.0119,-1.1429,98.071)"
228 y1="6.1317"
229 x1="32.250999" />
230 <linearGradient
231 id="a"
232 y2="7.0165"
233 gradientUnits="userSpaceOnUse"
234 x2="45.448002"
235 gradientTransform="matrix(1.0059,0,0,0.99417,100,0)"
236 y1="92.540001"
237 x1="45.448002">
238 <stop
239 offset="0"
240 id="stop3979" />
241 <stop
242 stop-opacity=".58824"
243 offset="1"
244 id="stop3981" />
245 </linearGradient>
246 <linearGradient
247 id="h"
248 y2="5.9995999"
249 xlink:href="#a"
250 gradientUnits="userSpaceOnUse"
251 x2="32.250999"
252 gradientTransform="matrix(1.0238,0,0,1.0119,-1.1429,0.929)"
253 y1="90"
254 x1="32.250999" />
255 <linearGradient
256 id="i"
257 y2="6"
258 xlink:href="#a"
259 gradientUnits="userSpaceOnUse"
260 x2="32.250999"
261 gradientTransform="translate(0,1)"
262 y1="90"
263 x1="32.250999" />
264 <radialGradient
265 id="e"
266 gradientUnits="userSpaceOnUse"
267 cy="78.287003"
268 cx="47.098"
269 gradientTransform="matrix(1.383,-1.035e-8,0,1.3124,-18.038997,-24.219)"
270 r="38.957001">
271 <stop
272 stop-color="#42394b"
273 offset="0"
274 id="stop4006" />
275 <stop
276 offset="1"
277 id="stop4008" />
278 </radialGradient>
279 <filter
280 style="color-interpolation-filters:sRGB"
281 id="o"
282 height="1.0908"
283 width="1.3035001"
284 y="-0.045378"
285 x="-0.15174">
286 <feGaussianBlur
287 stdDeviation="1.1526305"
288 id="feGaussianBlur3973" />
289 </filter>
290 <linearGradient
291 id="b"
292 y2="52.101002"
293 xlink:href="#d"
294 gradientUnits="userSpaceOnUse"
295 x2="19.941999"
296 gradientTransform="matrix(0,-0.65823,0.65616,0,-10.186,94.127)"
297 y1="52.101002"
298 x1="80.710999" />
299 <linearGradient
300 id="d"
301 stop-color="#fff">
302 <stop
303 offset="0"
304 id="stop3968" />
305 <stop
306 stop-opacity="0"
307 offset="1"
308 id="stop3970" />
309 </linearGradient>
310 <filter
311 style="color-interpolation-filters:sRGB"
312 id="p"
313 height="1.3025"
314 width="1.0888"
315 y="-0.15126"
316 x="-0.044411998">
317 <feGaussianBlur
318 stdDeviation="1.1526305"
319 id="feGaussianBlur3976" />
320 </filter>
321 <linearGradient
322 id="c"
323 y2="52.101002"
324 xlink:href="#d"
325 gradientUnits="userSpaceOnUse"
326 x2="19.941999"
327 gradientTransform="matrix(0.65823,0,0,0.65616,2.8732,25.814)"
328 y1="52.101002"
329 x1="80.710999" />
330 <linearGradient
331 id="j"
332 y2="5.9877"
333 gradientUnits="userSpaceOnUse"
334 x2="48"
335 y1="90"
336 x1="48">
337 <stop
338 stop-color="#b4b4b4"
339 offset="0"
340 id="stop3999" />
341 <stop
342 stop-color="#e6e6e6"
343 offset="1"
344 id="stop4001" />
345 </linearGradient>
346 <linearGradient
347 id="m"
348 stop-color="#fff"
349 y2="63.893002"
350 gradientUnits="userSpaceOnUse"
351 x2="36.356998"
352 y1="6"
353 x1="36.356998">
354 <stop
355 offset="0"
356 id="stop3994" />
357 <stop
358 stop-opacity="0"
359 offset="1"
360 id="stop3996" />
361 </linearGradient>
362 <linearGradient
363 id="n"
364 y2="83.294998"
365 gradientUnits="userSpaceOnUse"
366 x2="43.179001"
367 gradientTransform="matrix(1.0294,0,0,1.0294,-1.4118,-1.4118)"
368 y1="13"
369 x1="43.179001">
370 <stop
371 stop-color="#919191"
372 offset="0"
373 id="stop3989" />
374 <stop
375 stop-color="#fdfdfd"
376 offset="1"
377 id="stop3991" />
378 </linearGradient>
379 <radialGradient
380 id="f"
381 stop-color="#fff"
382 gradientUnits="userSpaceOnUse"
383 cy="90.171997"
384 cx="48"
385 gradientTransform="matrix(1.1573,0,0,0.99591,-7.551,0.19713)"
386 r="42">
387 <stop
388 offset="0"
389 id="stop3984" />
390 <stop
391 stop-opacity="0"
392 offset="1"
393 id="stop3986" />
394 </radialGradient>
395 <linearGradient
396 inkscape:collect="always"
397 xlink:href="#a"
398 id="linearGradient4912"
399 gradientUnits="userSpaceOnUse"
400 gradientTransform="matrix(1.0238,0,0,-1.0119,-1.1429,98.071)"
401 x1="32.250999"
402 y1="6.1317"
403 x2="32.250999"
404 y2="90.238998" />
405 <linearGradient
406 inkscape:collect="always"
407 xlink:href="#a"
408 id="linearGradient4914"
409 gradientUnits="userSpaceOnUse"
410 gradientTransform="matrix(1.0238,0,0,-1.0119,-1.1429,98.071)"
411 x1="32.250999"
412 y1="6.1317"
413 x2="32.250999"
414 y2="90.238998" />
415 <linearGradient
416 inkscape:collect="always"
417 xlink:href="#a"
418 id="linearGradient4916"
419 gradientUnits="userSpaceOnUse"
420 gradientTransform="matrix(1.0238,0,0,-1.0119,-1.1429,98.071)"
421 x1="32.250999"
422 y1="6.1317"
423 x2="32.250999"
424 y2="90.238998" />
425 <radialGradient
426 inkscape:collect="always"
427 xlink:href="#linearGradient6452"
428 id="radialGradient6446"
429 cx="24.000002"
430 cy="23.915552"
431 fx="24.000002"
432 fy="23.915552"
433 r="23.37389"
434 gradientTransform="matrix(0.50559953,-0.01484073,0.01430171,0.48723605,11.523578,12.619211)"
435 gradientUnits="userSpaceOnUse" />
436 </defs>
437 <sodipodi:namedview
438 pagecolor="#ffffff"
439 bordercolor="#666666"
440 borderopacity="1"
441 objecttolerance="10"
442 gridtolerance="10"
443 guidetolerance="10"
444 inkscape:pageopacity="0"
445 inkscape:pageshadow="2"
446 inkscape:window-width="1920"
447 inkscape:window-height="989"
448 id="namedview6"
449 showgrid="false"
450 inkscape:zoom="4.4283054"
451 inkscape:cx="46.062124"
452 inkscape:cy="35.733194"
453 inkscape:window-x="0"
454 inkscape:window-y="27"
455 inkscape:window-maximized="1"
456 inkscape:current-layer="svg4" />
457 <ellipse
458 style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.95995587;stroke-opacity:1;filter:url(#filter1819)"
459 id="path1698"
460 cx="-62.545948"
461 cy="154.09407"
462 rx="21"
463 ry="20.501823" />
464 <rect
465 style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-opacity:1;filter:url(#filter1740)"
466 id="rect1728"
467 width="40"
468 height="40"
469 x="-129.49153"
470 y="121.61999"
471 rx="6"
472 ry="6" />
473 <path
474 d="M 93.79561,48.452021 87.457596,61.312062 73.31126,63.424634 83.50758,73.437241 81.119287,87.5824 93.79561,80.877544 106.47046,87.5824 104.08364,73.437241 114.37167,63.424634 100.13303,61.312062 Z"
475 id="path2-3"
476 style="fill:none;fill-opacity:0.94117647;stroke:#000000;stroke-width:2.93992543;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0"
477 inkscape:connector-curvature="0"
478 sodipodi:nodetypes="ccccccccccc" />
479 <g
480 style="fill:url(#g)"
481 id="g4022">
482 <path
483 style="opacity:0.07999998;fill:url(#linearGradient4912)"
484 inkscape:connector-curvature="0"
485 d="M 12,95.031 C 6.4889,95.031 1.969,90.511 1.969,85 V 14 C 1.969,8.4889 6.4892,3.969 12,3.969 h 72 c 5.5111,0 10.031,4.5202 10.031,10.031 v 71 c 0,5.5111 -4.5202,10.031 -10.031,10.031 z"
486 id="path4016" />
487 <path
488 style="opacity:0.1;fill:url(#linearGradient4914)"
489 inkscape:connector-curvature="0"
490 d="m 12,94.031 c -4.9716,0 -9.0312,-4.0596 -9.0312,-9.0312 v -71 C 2.9688,9.028 7.0284,4.9684 12,4.9684 h 72 c 4.9716,0 9.0312,4.0596 9.0312,9.0312 v 71 c 0,4.9716 -4.0596,9.0312 -9.0312,9.0312 H 12 Z"
491 id="path4018" />
492 <path
493 style="opacity:0.2;fill:url(#linearGradient4916)"
494 inkscape:connector-curvature="0"
495 d="M 12,93 C 7.5909,93 4,89.409 4,85 V 14 C 4,9.5909 7.5909,6 12,6 h 72 c 4.4091,0 8,3.5909 8,8 v 71 c 0,4.4091 -3.5909,8 -8,8 z"
496 id="path4020" />
497 </g>
498 <rect
499 style="opacity:0.3;fill:url(#h)"
500 rx="7"
501 ry="7"
502 height="85"
503 width="86"
504 y="6.9999986"
505 x="4.9999986"
506 id="rect4024" />
507 <rect
508 style="opacity:0.45;fill:url(#i)"
509 rx="6"
510 ry="6"
511 height="84"
512 width="84"
513 y="6.9999986"
514 x="5.9999986"
515 id="rect4026" />
516 <path
517 inkscape:connector-curvature="0"
518 style="opacity:0.15"
519 d="m 19,13 c -3.2894,0 -6,2.7106 -6,6 v 58 c 0,3.2894 2.7106,6 6,6 h 58 c 3.2894,0 6,-2.7106 6,-6 V 19 c 0,-3.2894 -2.7106,-6 -6,-6 z m 0,4 h 58 c 1.1426,0 2,0.85741 2,2 v 58 c 0,1.1426 -0.85741,2 -2,2 H 19 c -1.1426,0 -2,-0.85741 -2,-2 V 19 c 0,-1.1426 0.85741,-2 2,-2 z"
520 id="path4058" />
521 <path
522 inkscape:connector-curvature="0"
523 style="opacity:0.3"
524 d="m 17,14 c -1.662,0 -3,1.338 -3,3 v 62 c 0,1.662 1.338,3 3,3 h 62 c 1.662,0 3,-1.338 3,-3 V 17 c 0,-1.662 -1.338,-3 -3,-3 z m 0,1.9375 h 62 c 0.61816,0 1.0625,0.44434 1.0625,1.0625 v 62 c 0,0.61816 -0.44434,1.0625 -1.0625,1.0625 H 17 c -0.61816,0 -1.0625,-0.44434 -1.0625,-1.0625 V 17 c 0,-0.61816 0.44434,-1.0625 1.0625,-1.0625 z"
525 id="path4060" />
526 <path
527 inkscape:connector-curvature="0"
528 style="opacity:0.6"
529 d="m 17,14 c -1.662,0 -3,1.338 -3,3 v 62 c 0,1.662 1.338,3 3,3 h 62 c 1.662,0 3,-1.338 3,-3 V 17 c 0,-1.662 -1.338,-3 -3,-3 z m 0,0.96875 h 62 c 1.1401,0 2.0312,0.89117 2.0312,2.0312 v 62 c 0,1.1401 -0.89117,2.0312 -2.0312,2.0312 H 17 c -1.1401,0 -2.0312,-0.89117 -2.0312,-2.0312 v -62 c 0,-1.1401 0.89117,-2.0312 2.0312,-2.0312 z"
530 id="path4062" />
531 <path
532 inkscape:connector-curvature="0"
533 style="fill:url(#j)"
534 d="M 12,6 C 8.676,6 6,8.676 6,12 v 72 c 0,3.324 2.676,6 6,6 h 72 c 3.324,0 6,-2.676 6,-6 V 12 C 90,8.676 87.324,6 84,6 Z m 5,7 h 62 c 2.216,0 4,1.784 4,4 v 62 c 10e-7,2.216 -1.784,4 -4,4 H 17 c -2.216,10e-7 -4,-1.784 -4,-4 V 17 c 0,-2.216 1.784,-4 4,-4 z"
535 id="path4064" />
536 <path
537 inkscape:connector-curvature="0"
538 style="opacity:0.5;fill:url(#m)"
539 d="M 12,6 C 8.676,6 6,8.676 6,12 v 72 c 0,0.33472 0.04135,0.6507 0.09375,0.96875 0.0487,0.2956 0.09704,0.59692 0.1875,0.875 0.0099,0.03038 0.02089,0.0636 0.03125,0.09375 0.09887,0.28777 0.23488,0.54745 0.375,0.8125 0.14459,0.27351 0.31562,0.53562 0.5,0.78125 0.18438,0.24564 0.37378,0.47347 0.59375,0.6875 0.43995,0.42806 0.94291,0.81453 1.5,1.0938 0.27854,0.13961 0.5734698,0.24695 0.875,0.34375 -0.2562002,-0.10022 -0.48671,-0.23627 -0.71875,-0.375 -0.0074,-0.0044 -0.02387,0.0045 -0.03125,0 -0.0319,-0.019 -0.0622,-0.042 -0.0937,-0.062 -0.1204,-0.077 -0.231,-0.164 -0.3437,-0.25 -0.1062,-0.081 -0.2133,-0.161 -0.3126,-0.25 -0.1778,-0.162 -0.3473,-0.346 -0.4999,-0.531 -0.1075,-0.131 -0.2183,-0.266 -0.3124,-0.407 -0.0251,-0.038 -0.0385,-0.086 -0.0626,-0.125 -0.0647,-0.103 -0.1302,-0.204 -0.1874,-0.312 -0.1011,-0.195 -0.2057,-0.416 -0.2813,-0.625 -0.008,-0.022 -0.0236,-0.041 -0.0313,-0.063 -0.0318,-0.092 -0.0358,-0.187 -0.0624,-0.281 -0.0304,-0.107 -0.0704,-0.203 -0.0938,-0.313 -0.0729,-0.341 -0.125,-0.698 -0.125,-1.062 v -72 c 0,-2.782 2.2182,-5 5,-5 h 72 c 2.7818,0 5,2.2182 5,5 v 72 c 0,0.3643 -0.05212,0.72099 -0.125,1.0625 -0.04415,0.20689 -0.08838,0.39766 -0.15625,0.59375 -0.0077,0.02195 -0.0233,0.04069 -0.03125,0.0625 -0.06274,0.17374 -0.13838,0.36745 -0.21875,0.53125 -0.04158,0.0828 -0.07904,0.16995 -0.125,0.25 -0.0546,0.09721 -0.12677,0.18835 -0.1875,0.28125 -0.09411,0.14096 -0.20492,0.276 -0.3125,0.40625 -0.14317,0.17445 -0.30314,0.347 -0.46875,0.5 -0.01117,0.0102 -0.01998,0.02115 -0.03125,0.03125 -0.13839,0.12556 -0.28509,0.23444 -0.4375,0.34375 -0.10257,0.07315 -0.20432,0.15336 -0.3125,0.21875 -0.0074,0.0045 -0.02384,-0.0044 -0.03125,0 -0.23204,0.13873 -0.46255,0.27478 -0.71875,0.375 0.30153,-0.0968 0.59646,-0.20414 0.875,-0.34375 0.55709,-0.27922 1.0601,-0.66569 1.5,-1.0938 0.21997,-0.21403 0.40937,-0.44186 0.59375,-0.6875 0.18438,-0.24564 0.35541,-0.50774 0.5,-0.78125 0.14012,-0.26505 0.27614,-0.52473 0.375,-0.8125 0.01041,-0.03078 0.02133,-0.06274 0.03125,-0.09375 0.09046,-0.27808 0.1388,-0.5794 0.1875,-0.875 0.053,-0.318 0.094,-0.634 0.094,-0.969 V 12 c 0,-3.324 -2.676,-6 -6,-6 h -72 z"
540 id="path4066" />
541 <path
542 inkscape:connector-curvature="0"
543 style="fill:url(#n)"
544 d="m 17,12 c -2.7527,0 -5,2.2473 -5,5 v 62 c 0,2.7527 2.2473,5 5,5 h 62 c 2.7527,0 5,-2.2473 5,-5 V 17 c 0,-2.7527 -2.2473,-5 -5,-5 z m 0,2 h 62 c 1.6793,0 3,1.3207 3,3 v 62 c 10e-7,1.6793 -1.3207,3 -3,3 H 17 c -1.6793,0 -3,-1.3207 -3,-3 V 17 c 0,-1.6793 1.3207,-3 3,-3 z"
545 id="path4068" />
546 <path
547 inkscape:connector-curvature="0"
548 style="opacity:0.4;fill:url(#f)"
549 d="M 12,90 C 8.676,90 6,87.324 6,84 V 12 c 0,-0.33472 0.04135,-0.6507 0.09375,-0.96875 0.0487,-0.2956 0.09704,-0.59692 0.1875,-0.875 0.0099,-0.03 0.0209,-0.063 0.0313,-0.094 0.0989,-0.2873 0.2349,-0.547 0.375,-0.812 0.1446,-0.2735 0.3156,-0.5356 0.5,-0.7812 0.1844,-0.2457 0.3738,-0.4735 0.5937,-0.6876 0.44,-0.428 0.943,-0.8145 1.5,-1.0937 0.2786,-0.1396 0.5734998,-0.2469 0.8748,-0.3437 -0.2560002,0.1002 -0.4865,0.2362 -0.7185,0.375 -0.0074,0.0044 -0.02387,-0.0045 -0.03125,0 -0.03193,0.0193 -0.06229,0.04251 -0.09375,0.0625 -0.1204,0.0767 -0.23102,0.16351 -0.34375,0.25 -0.10617,0.0808 -0.21328,0.16111 -0.3125,0.25 -0.1779,0.1614 -0.3474,0.3453 -0.5,0.5312 -0.1075,0.1303 -0.2183,0.2653 -0.3124,0.4063 -0.0251,0.0383 -0.0385,0.0858 -0.0626,0.125 -0.0647,0.103 -0.1302,0.2045 -0.1874,0.3124 -0.1011,0.1948 -0.2057,0.4158 -0.2813,0.625 -0.008,0.0219 -0.0236,0.0406 -0.0313,0.0626 -0.0318,0.0919 -0.0358,0.1868 -0.0624,0.2812 -0.0304,0.1066 -0.0704,0.203 -0.0938,0.3125 -0.0729,0.3415 -0.125,0.6985 -0.125,1.0625 v 72 c 0,2.7818 2.2182,5 5,5 h 72 c 2.7818,0 5,-2.2182 5,-5 v -72 c 0,-0.364 -0.052,-0.721 -0.125,-1.0625 -0.044,-0.2069 -0.088,-0.3977 -0.156,-0.5937 -0.008,-0.022 -0.024,-0.0407 -0.031,-0.0626 -0.063,-0.1737 -0.139,-0.3674 -0.219,-0.5312 -0.042,-0.0828 -0.079,-0.17 -0.125,-0.25 -0.055,-0.0972 -0.127,-0.1884 -0.188,-0.2812 -0.094,-0.141 -0.205,-0.276 -0.312,-0.4063 -0.143,-0.1745 -0.303,-0.347 -0.469,-0.5 -0.011,-0.0102 -0.02,-0.0211 -0.031,-0.0313 -0.139,-0.1255 -0.285,-0.2344 -0.438,-0.3437 -0.102,-0.0731 -0.204,-0.1534 -0.312,-0.2187 -0.0074,-0.0045 -0.02384,0.0044 -0.03125,0 -0.23204,-0.13873 -0.46255,-0.27478 -0.71875,-0.375 0.30153,0.0968 0.59646,0.20414 0.875,0.34375 0.55709,0.27922 1.0601,0.66569 1.5,1.0938 0.21997,0.21403 0.40937,0.44186 0.59375,0.6875 0.18438,0.24564 0.35541,0.50774 0.5,0.78125 0.14012,0.26505 0.27614,0.52473 0.375,0.8125 0.01041,0.03078 0.02133,0.06274 0.03125,0.09375 0.09046,0.27808 0.1388,0.5794 0.1875,0.875 0.053,0.318 0.094,0.634 0.094,0.969 v 72 c 0,3.324 -2.676,6 -6,6 h -72 z"
550 id="path4070" />
551 <path
552 d="M 23.956495,5.3528801 17.94324,17.553973 4.521759,19.558298 l 9.673863,9.499563 -2.265917,13.420364 12.02679,-6.3613 12.025397,6.3613 -2.264523,-13.420364 9.760873,-9.499563 -13.509048,-2.004325 z"
553 id="path2"
554 style="fill:url(#radialGradient6446);fill-opacity:1;stroke:#000000;stroke-width:1.0436362;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0;paint-order:normal;filter:url(#filter1795)"
555 inkscape:connector-curvature="0"
556 sodipodi:nodetypes="ccccccccccc"
557 transform="matrix(1.4973973,0,0,1.4973973,12.062463,10.33408)" />
558 </svg>
0 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1 <svg
2 xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
3 xmlns:dc="http://purl.org/dc/elements/1.1/"
4 xmlns:cc="http://creativecommons.org/ns#"
5 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6 xmlns:svg="http://www.w3.org/2000/svg"
7 xmlns="http://www.w3.org/2000/svg"
8 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
9 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
10 inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
11 sodipodi:docname="emblem-xapp-favorite.svg"
12 id="svg4"
13 version="1.1"
14 width="48"
15 height="48"
16 enable-background="new">
17 <metadata
18 id="metadata10">
19 <rdf:RDF>
20 <cc:Work
21 rdf:about="">
22 <dc:format>image/svg+xml</dc:format>
23 <dc:type
24 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
25 <dc:title />
26 </cc:Work>
27 </rdf:RDF>
28 </metadata>
29 <defs
30 id="defs8">
31 <linearGradient
32 id="linearGradient7722"
33 osb:paint="gradient">
34 <stop
35 style="stop-color:#000000;stop-opacity:1;"
36 offset="0"
37 id="stop7718" />
38 <stop
39 style="stop-color:#000000;stop-opacity:0;"
40 offset="1"
41 id="stop7720" />
42 </linearGradient>
43 <filter
44 style="color-interpolation-filters:sRGB;"
45 inkscape:label="Feather"
46 id="filter1726">
47 <feGaussianBlur
48 stdDeviation="5"
49 result="blur"
50 id="feGaussianBlur1718" />
51 <feComposite
52 in="SourceGraphic"
53 in2="blur"
54 operator="atop"
55 result="composite1"
56 id="feComposite1720" />
57 <feComposite
58 in2="composite1"
59 operator="in"
60 result="composite2"
61 id="feComposite1722" />
62 <feComposite
63 in2="composite2"
64 operator="in"
65 result="composite3"
66 id="feComposite1724" />
67 </filter>
68 <filter
69 inkscape:collect="always"
70 style="color-interpolation-filters:sRGB"
71 id="filter1694"
72 x="-0.02929493"
73 width="1.0585899"
74 y="-0.030739846"
75 height="1.0614797">
76 <feGaussianBlur
77 inkscape:collect="always"
78 stdDeviation="0.54683814"
79 id="feGaussianBlur1696" />
80 </filter>
81 <filter
82 style="color-interpolation-filters:sRGB;"
83 inkscape:label="Drop Shadow"
84 id="filter1795">
85 <feFlood
86 flood-opacity="0.498039"
87 flood-color="rgb(0,0,0)"
88 result="flood"
89 id="feFlood1785" />
90 <feComposite
91 in="flood"
92 in2="SourceGraphic"
93 operator="in"
94 result="composite1"
95 id="feComposite1787" />
96 <feGaussianBlur
97 in="composite1"
98 stdDeviation="1"
99 result="blur"
100 id="feGaussianBlur1789" />
101 <feOffset
102 dx="1"
103 dy="1"
104 result="offset"
105 id="feOffset1791" />
106 <feComposite
107 in="SourceGraphic"
108 in2="offset"
109 operator="over"
110 result="composite2"
111 id="feComposite1793" />
112 </filter>
113 <filter
114 style="color-interpolation-filters:sRGB;"
115 inkscape:label="Drop Shadow"
116 id="filter1819">
117 <feFlood
118 flood-opacity="0.498039"
119 flood-color="rgb(0,0,0)"
120 result="flood"
121 id="feFlood1809" />
122 <feComposite
123 in="flood"
124 in2="SourceGraphic"
125 operator="in"
126 result="composite1"
127 id="feComposite1811" />
128 <feGaussianBlur
129 in="composite1"
130 stdDeviation="1"
131 result="blur"
132 id="feGaussianBlur1813" />
133 <feOffset
134 dx="1"
135 dy="1"
136 result="offset"
137 id="feOffset1815" />
138 <feComposite
139 in="SourceGraphic"
140 in2="offset"
141 operator="over"
142 result="composite2"
143 id="feComposite1817" />
144 </filter>
145 </defs>
146 <sodipodi:namedview
147 pagecolor="#ffffff"
148 bordercolor="#666666"
149 borderopacity="1"
150 objecttolerance="10"
151 gridtolerance="10"
152 guidetolerance="10"
153 inkscape:pageopacity="0"
154 inkscape:pageshadow="2"
155 inkscape:window-width="1920"
156 inkscape:window-height="989"
157 id="namedview6"
158 showgrid="false"
159 inkscape:zoom="12.525139"
160 inkscape:cx="10.514129"
161 inkscape:cy="19.752735"
162 inkscape:window-x="0"
163 inkscape:window-y="27"
164 inkscape:window-maximized="1"
165 inkscape:current-layer="svg4" />
166 <ellipse
167 style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.95995587;stroke-opacity:1;filter:url(#filter1819)"
168 id="path1698"
169 cx="24"
170 cy="24.498177"
171 rx="21"
172 ry="20.501823" />
173 <path
174 d="m -38.085272,-1.7272848 -6.915242,14.0312568 -15.434703,2.304974 11.124942,10.924498 -2.605805,15.433419 13.830808,-7.315495 13.829207,7.315495 -2.604201,-15.433419 11.225004,-10.924498 -15.535406,-2.304974 z"
175 id="path2-2"
176 style="fill:#cccccc;fill-opacity:1;stroke:#000000;stroke-width:1.20018172;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0;paint-order:normal;filter:url(#filter1694)"
177 inkscape:connector-curvature="0"
178 sodipodi:nodetypes="ccccccccccc" />
179 <path
180 d="M 23.956495,5.3528801 17.94324,17.553973 4.521759,19.558298 l 9.673863,9.499563 -2.265917,13.420364 12.02679,-6.3613 12.025397,6.3613 -2.264523,-13.420364 9.760873,-9.499563 -13.509048,-2.004325 z"
181 id="path2"
182 style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.0436362;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0;paint-order:normal;filter:url(#filter1795)"
183 inkscape:connector-curvature="0"
184 sodipodi:nodetypes="ccccccccccc"
185 transform="matrix(0.90919255,0,0,0.90919255,2.1793771,1.0885881)" />
186 <path
187 d="M 8.0836672,-3.5187526 1.7456531,9.3412879 -12.400683,11.45386 -2.2043633,21.466467 -4.5926558,35.611626 8.0836672,28.90677 20.758521,35.611626 18.371698,21.466467 28.659726,11.45386 14.421094,9.3412879 Z"
188 id="path2-3"
189 style="fill:none;fill-opacity:0.94117647;stroke:#000000;stroke-width:2.93992543;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0"
190 inkscape:connector-curvature="0"
191 sodipodi:nodetypes="ccccccccccc" />
192 </svg>
0 #include "xapp-favorites.h"
1 #include "favorite-vfs-file-enumerator.h"
2 #include "favorite-vfs-file.h"
3
4 typedef struct
5 {
6 GFile *file;
7
8 GList *uris;
9 gchar *attributes;
10 GFileQueryInfoFlags flags;
11
12 GList *current_pos;
13 } FavoriteVfsFileEnumeratorPrivate;
14
15 struct _FavoriteVfsFileEnumerator
16 {
17 GObject parent_instance;
18 FavoriteVfsFileEnumeratorPrivate *priv;
19 };
20
21 G_DEFINE_TYPE_WITH_PRIVATE(FavoriteVfsFileEnumerator,
22 favorite_vfs_file_enumerator,
23 G_TYPE_FILE_ENUMERATOR)
24
25 static GFileInfo *
26 next_file (GFileEnumerator *enumerator,
27 GCancellable *cancellable,
28 GError **error)
29 {
30 FavoriteVfsFileEnumerator *self = FAVORITE_VFS_FILE_ENUMERATOR (enumerator);
31 FavoriteVfsFileEnumeratorPrivate *priv = favorite_vfs_file_enumerator_get_instance_private (self);
32 GFileInfo *info;
33
34 if (cancellable)
35 {
36 if (g_cancellable_is_cancelled (cancellable))
37 {
38 *error = g_error_new (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Enumerate canceled");
39 return NULL;
40 }
41 }
42
43 info = NULL;
44
45 while (priv->current_pos != NULL && info == NULL)
46 {
47 GFile *file;
48 gchar *uri;
49
50 uri = path_to_fav_uri ((const gchar *) priv->current_pos->data);
51 if (!xapp_favorites_find_by_display_name (xapp_favorites_get_default (), (gchar *) priv->current_pos->data))
52 {
53 *error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "File not found");
54
55 g_warn_if_reached ();
56 }
57 else
58 {
59 file = g_file_new_for_uri (uri);
60
61 info = g_file_query_info (file,
62 priv->attributes,
63 priv->flags,
64 cancellable,
65 error);
66 }
67
68 g_free (uri);
69 g_object_unref (file);
70 }
71
72 if (priv->current_pos)
73 {
74 priv->current_pos = priv->current_pos->next;
75 }
76
77 return info;
78 }
79
80 static void
81 next_async_op_free (GList *files)
82 {
83 g_list_free_full (files, g_object_unref);
84 }
85
86 static void
87 next_files_async_thread (GTask *task,
88 gpointer source_object,
89 gpointer task_data,
90 GCancellable *cancellable)
91 {
92 FavoriteVfsFileEnumerator *self = FAVORITE_VFS_FILE_ENUMERATOR (source_object);
93 GList *ret;
94 GError *error = NULL;
95 gint i, n_requested;
96
97 n_requested = GPOINTER_TO_INT (g_task_get_task_data (task));
98
99 ret = NULL;
100
101 for (i = 0; i < n_requested; i++)
102 {
103 GFileInfo *info = NULL;
104 // g_clear_error (&error);
105
106 if (g_cancellable_set_error_if_cancelled (cancellable, &error))
107 {
108 g_task_return_error (task, error);
109 return;
110 }
111 else
112 {
113 info = next_file (G_FILE_ENUMERATOR (self), cancellable, &error);
114 }
115
116 if (info != NULL)
117 {
118 ret = g_list_prepend (ret, info);
119 }
120 else
121 {
122 if (error)
123 {
124 g_critical ("ERROR: %s\n", error->message);
125 }
126 break;
127 }
128 }
129
130 if (error)
131 {
132 g_task_return_error (task, error);
133 }
134 else
135 {
136 ret = g_list_reverse (ret);
137 g_task_return_pointer (task, ret, (GDestroyNotify) next_async_op_free);
138 }
139 }
140
141 static void
142 next_files_async (GFileEnumerator *enumerator,
143 gint num_files,
144 gint io_priority,
145 GCancellable *cancellable,
146 GAsyncReadyCallback callback,
147 gpointer user_data)
148 {
149 // FavoriteVfsFileEnumerator *self = FAVORITE_VFS_FILE_ENUMERATOR (enumerator);
150
151 GTask *task;
152 task = g_task_new (enumerator, cancellable, callback, user_data);
153 g_task_set_priority (task, io_priority);
154 g_task_set_task_data (task, GINT_TO_POINTER (num_files), NULL);
155
156 g_task_run_in_thread (task, next_files_async_thread);
157 g_object_unref (task);
158 }
159
160 static GList *
161 next_files_finished (GFileEnumerator *enumerator,
162 GAsyncResult *result,
163 GError **error)
164 {
165 g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL);
166 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
167
168 return (GList *) g_task_propagate_pointer (G_TASK (result), error);
169 }
170
171 static gboolean
172 close_fn (GFileEnumerator *enumerator,
173 GCancellable *cancellable,
174 GError **error)
175 {
176 // FavoriteVfsFileEnumerator *self = FAVORITE_VFS_FILE_ENUMERATOR (enumerator);
177
178 return TRUE;
179 }
180
181 static void
182 favorite_vfs_file_enumerator_init (FavoriteVfsFileEnumerator *self)
183 {
184 }
185
186 static void
187 favorite_vfs_file_enumerator_dispose (GObject *object)
188 {
189 G_OBJECT_CLASS (favorite_vfs_file_enumerator_parent_class)->dispose (object);
190 }
191
192 static void
193 favorite_vfs_file_enumerator_finalize (GObject *object)
194 {
195 FavoriteVfsFileEnumerator *self = FAVORITE_VFS_FILE_ENUMERATOR(object);
196 FavoriteVfsFileEnumeratorPrivate *priv = favorite_vfs_file_enumerator_get_instance_private(self);
197
198 g_list_free_full (priv->uris, (GDestroyNotify) g_free);
199 g_free (priv->attributes);
200 g_object_unref (priv->file);
201
202 G_OBJECT_CLASS (favorite_vfs_file_enumerator_parent_class)->finalize (object);
203 }
204
205 static void
206 favorite_vfs_file_enumerator_class_init (FavoriteVfsFileEnumeratorClass *klass)
207 {
208 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
209 GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
210
211 gobject_class->dispose = favorite_vfs_file_enumerator_dispose;
212 gobject_class->finalize = favorite_vfs_file_enumerator_finalize;
213
214 enumerator_class->next_file = next_file;
215 enumerator_class->next_files_async = next_files_async;
216 enumerator_class->next_files_finish = next_files_finished;
217 enumerator_class->close_fn = close_fn;
218 }
219
220 GFileEnumerator *
221 favorite_vfs_file_enumerator_new (GFile *file,
222 const gchar *attributes,
223 GFileQueryInfoFlags flags,
224 GList *uris)
225 {
226 FavoriteVfsFileEnumerator *enumerator = g_object_new (FAVORITE_TYPE_VFS_FILE_ENUMERATOR, NULL);
227 FavoriteVfsFileEnumeratorPrivate *priv = favorite_vfs_file_enumerator_get_instance_private(enumerator);
228
229 priv->uris = g_list_copy_deep (uris, (GCopyFunc) g_strdup, NULL);
230 priv->current_pos = priv->uris;
231
232 priv->file = g_object_ref (file);
233 priv->attributes = g_strdup (attributes);
234 priv->flags = flags;
235
236 return G_FILE_ENUMERATOR (enumerator);
237 }
238
0 #ifndef FAVORITE_VFS_FILE_ENUMERATOR_H
1 #define FAVORITE_VFS_FILE_ENUMERATOR_H
2
3 #include <glib-object.h>
4 #include <gio/gio.h>
5
6 G_BEGIN_DECLS
7
8 #define FAVORITE_TYPE_VFS_FILE_ENUMERATOR favorite_vfs_file_enumerator_get_type()
9
10 G_DECLARE_FINAL_TYPE (FavoriteVfsFileEnumerator, favorite_vfs_file_enumerator, \
11 FAVORITE, VFS_FILE_ENUMERATOR, \
12 GFileEnumerator)
13
14 GFileEnumerator *
15 favorite_vfs_file_enumerator_new (GFile *file,
16 const gchar *attributes,
17 GFileQueryInfoFlags flags,
18 GList *favorites);
19
20 G_END_DECLS
21
22 #endif // FAVORITE_VFS_FILE_ENUMERATOR_H
0 #include "xapp-favorites.h"
1 #include "favorite-vfs-file.h"
2 #include "favorite-vfs-file-monitor.h"
3
4 typedef struct
5 {
6 gulong changed_handler_id;
7 GHashTable *file_monitors;
8 GList *infos;
9
10 GVolumeMonitor *mount_mon;
11 } FavoriteVfsFileMonitorPrivate;
12
13 struct _FavoriteVfsFileMonitor
14 {
15 GObject parent_instance;
16 FavoriteVfsFileMonitorPrivate *priv;
17 };
18
19 G_DEFINE_TYPE_WITH_PRIVATE(FavoriteVfsFileMonitor, \
20 favorite_vfs_file_monitor, \
21 G_TYPE_FILE_MONITOR)
22
23 GFile *_favorite_vfs_file_new_for_info (XAppFavoriteInfo *info);
24
25 // static void
26 // rename_favorite (GFile *old_file,
27 // GFile *new_file)
28 // {
29 // gchar *old_file_uri, *new_file_uri;
30
31 // old_file_uri = g_file_get_uri (old_file);
32 // new_file_uri = g_file_get_uri (new_file);
33
34 // _xapp_favorites_rename (xapp_favorites_get_default (),
35 // old_file_uri,
36 // new_file_uri);
37
38 // g_free (old_file_uri);
39 // g_free (new_file_uri);
40 // }
41
42 static void
43 favorite_real_file_changed (GFileMonitor *rfmonitor,
44 GFile *file,
45 GFile *other_file,
46 GFileMonitorEvent event_type,
47 gpointer user_data)
48 {
49 // Disabled
50 return;
51
52 // g_return_if_fail (FAVORITE_IS_VFS_FILE_MONITOR (user_data));
53 // FavoriteVfsFileMonitor *monitor = FAVORITE_VFS_FILE_MONITOR (user_data);
54 // FavoriteVfsFileMonitorPrivate *priv = favorite_vfs_file_monitor_get_instance_private (monitor);
55
56 // g_debug ("real file changed: %s: %d", g_file_get_uri (file), event_type);
57
58 // switch (event_type)
59 // {
60 // case G_FILE_MONITOR_EVENT_MOVED_OUT:
61 // break;
62 // {
63 // gchar *uri = g_file_get_uri (file);
64
65 // g_debug ("Deleted: %s\n", uri);
66
67 // xapp_favorites_remove (xapp_favorites_get_default (), uri);
68 // g_free (uri);
69 // }
70
71 // break;
72 // case G_FILE_MONITOR_EVENT_RENAMED:
73 // break;
74 // {
75 // gchar *uri = g_file_get_uri (file);
76
77 // g_debug ("Renamed: %s\n", uri);
78
79 // rename_favorite (file, other_file);
80 // }
81 // break;
82 // case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
83 // case G_FILE_MONITOR_EVENT_CHANGED:
84 // {
85 // gchar *uri = g_file_get_uri (file);
86
87 // GList *iter;
88
89 // for (iter = priv->infos; iter != NULL; iter = iter->next)
90 // {
91 // XAppFavoriteInfo *info = (XAppFavoriteInfo *) iter->data;
92
93 // if (g_strcmp0 (uri, info->uri) == 0)
94 // {
95 // GFile *fav_file;
96 // gchar *uri;
97
98 // uri = path_to_fav_uri (info->display_name);
99 // fav_file = g_file_new_for_uri (uri);
100
101 // g_debug ("Changed: %s", uri);
102 // g_free (uri);
103
104 // g_file_monitor_emit_event (G_FILE_MONITOR (monitor),
105 // fav_file,
106 // NULL,
107 // G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED);
108 // g_file_monitor_emit_event (G_FILE_MONITOR (monitor),
109 // fav_file,
110 // NULL,
111 // G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT);
112 // g_object_unref (fav_file);
113
114 // break;
115 // }
116 // }
117 // }
118 // break;
119 // case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
120 // case G_FILE_MONITOR_EVENT_CREATED:
121 // // case G_FILE_MONITOR_EVENT_CHANGED:
122 // case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
123 // case G_FILE_MONITOR_EVENT_UNMOUNTED:
124 // case G_FILE_MONITOR_EVENT_MOVED:
125 // case G_FILE_MONITOR_EVENT_MOVED_IN:
126 // case G_FILE_MONITOR_EVENT_DELETED:
127 // break;
128 // default:
129 // g_warn_if_reached ();
130 // }
131 }
132
133 static void
134 unmonitor_files (FavoriteVfsFileMonitor *monitor)
135 {
136 /* Disabled. See below */
137 return;
138
139 FavoriteVfsFileMonitorPrivate *priv = favorite_vfs_file_monitor_get_instance_private (monitor);
140
141 if (priv->file_monitors != NULL)
142 {
143 g_hash_table_destroy (priv->file_monitors);
144 priv->file_monitors = NULL;
145 }
146 }
147
148 static void
149 monitor_files (FavoriteVfsFileMonitor *monitor)
150 {
151
152 /* Disabled - this isn't necessary right now but could be expanded to help
153 * support less integrated apps. */
154 return;
155
156 FavoriteVfsFileMonitorPrivate *priv = favorite_vfs_file_monitor_get_instance_private (monitor);
157 GList *iter;
158
159 priv->file_monitors = g_hash_table_new_full (g_str_hash, g_str_equal,
160 g_free, (GDestroyNotify) g_object_unref);
161
162 for (iter = priv->infos; iter != NULL; iter = iter->next)
163 {
164 XAppFavoriteInfo *info = (XAppFavoriteInfo *) iter->data;
165 GFileMonitor *real_monitor;
166 GFile *real_file;
167 GError *error;
168
169 g_debug ("Monitoring real file: %s\n", info->uri);
170
171 error = NULL;
172 real_file = g_file_new_for_uri (info->uri);
173 real_monitor = g_file_monitor (real_file,
174 G_FILE_MONITOR_WATCH_MOVES,
175 NULL,
176 &error);
177 g_object_unref (real_file);
178
179 if (real_monitor == NULL)
180 {
181 if (error != NULL)
182 {
183 g_warning ("Unable to add file monitor for '%s': %s", info->uri, error->message);
184 g_error_free (error);
185 }
186
187 continue;
188 }
189
190 g_hash_table_insert (priv->file_monitors,
191 (gpointer) g_strdup (info->uri),
192 (gpointer) real_monitor);
193
194 g_signal_connect (real_monitor,
195 "changed",
196 G_CALLBACK (favorite_real_file_changed),
197 monitor);
198 }
199 }
200
201 static gint
202 find_info_by_uri (gconstpointer ptr_a,
203 gconstpointer ptr_b)
204 {
205 XAppFavoriteInfo *info = (XAppFavoriteInfo *) ptr_a;
206 const gchar *uri = (gchar *) ptr_b;
207
208 return g_strcmp0 (info->uri, uri);
209 }
210
211 static void
212 favorites_changed (XAppFavorites *favorites,
213 gpointer user_data)
214 {
215 g_return_if_fail (XAPP_IS_FAVORITES (favorites));
216 g_return_if_fail (FAVORITE_IS_VFS_FILE_MONITOR (user_data));
217
218 FavoriteVfsFileMonitor *monitor = FAVORITE_VFS_FILE_MONITOR (user_data);
219 FavoriteVfsFileMonitorPrivate *priv = favorite_vfs_file_monitor_get_instance_private (monitor);
220 GList *added, *removed;
221 GList *iter, *new_infos;
222
223 if (g_file_monitor_is_cancelled (G_FILE_MONITOR (monitor)))
224 {
225 return;
226 }
227
228 added = removed = NULL;
229
230 new_infos = xapp_favorites_get_favorites (favorites, NULL);
231
232 for (iter = priv->infos; iter != NULL; iter = iter->next)
233 {
234 XAppFavoriteInfo *old_info = (XAppFavoriteInfo *) iter->data;
235 GList *res = g_list_find_custom (new_infos,
236 (gpointer) old_info->uri,
237 (GCompareFunc) find_info_by_uri);
238 if (res == NULL)
239 {
240 removed = g_list_prepend (removed, old_info);
241 }
242 }
243
244 for (iter = new_infos; iter != NULL; iter = iter->next)
245 {
246 XAppFavoriteInfo *new_info = (XAppFavoriteInfo *) iter->data;
247 GList *res = g_list_find_custom (priv->infos,
248 (gpointer) new_info->uri,
249 (GCompareFunc) find_info_by_uri);
250 if (res == NULL)
251 {
252 added = g_list_prepend (added, new_info);
253 }
254 }
255
256 for (iter = added; iter != NULL; iter = iter->next)
257 {
258 XAppFavoriteInfo *added_info = (XAppFavoriteInfo *) iter->data;
259
260 GFile *file = _favorite_vfs_file_new_for_info (added_info);
261
262 g_file_monitor_emit_event (G_FILE_MONITOR (monitor),
263 file,
264 NULL,
265 G_FILE_MONITOR_EVENT_CREATED);
266 g_file_monitor_emit_event (G_FILE_MONITOR (monitor),
267 file,
268 NULL,
269 G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT);
270 g_object_unref (file);
271 }
272
273 for (iter = removed; iter != NULL; iter = iter->next)
274 {
275 XAppFavoriteInfo *removed_info = (XAppFavoriteInfo *) iter->data;
276
277 GFile *file = _favorite_vfs_file_new_for_info (removed_info);
278
279 g_file_monitor_emit_event (G_FILE_MONITOR (monitor),
280 file,
281 NULL,
282 G_FILE_MONITOR_EVENT_DELETED);
283 g_file_monitor_emit_event (G_FILE_MONITOR (monitor),
284 file,
285 NULL,
286 G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT);
287 g_object_unref (file);
288 }
289
290 GList *tmp = priv->infos;
291 priv->infos = new_infos;
292
293 g_list_free_full (tmp, (GDestroyNotify) xapp_favorite_info_free);
294
295 //FIXME: add/remove individually
296 unmonitor_files (monitor);
297 monitor_files (monitor);
298 }
299
300 static void
301 mounts_changed (GVolumeMonitor *mount_mon,
302 GMount *mount,
303 gpointer user_data)
304 {
305 g_return_if_fail (FAVORITE_IS_VFS_FILE_MONITOR (user_data));
306
307 FavoriteVfsFileMonitor *monitor = FAVORITE_VFS_FILE_MONITOR (user_data);
308 FavoriteVfsFileMonitorPrivate *priv = favorite_vfs_file_monitor_get_instance_private (monitor);
309
310 GFile *root;
311 GList *iter, *mount_favorites;
312
313 root = g_mount_get_root (mount);
314 mount_favorites = NULL;
315
316 // Find any favorites that are descendent from root.
317
318 for (iter = priv->infos; iter != NULL; iter = iter->next)
319 {
320 XAppFavoriteInfo *info = (XAppFavoriteInfo *) iter->data;
321 GFile *fav_file = g_file_new_for_uri (info->uri);
322 gchar *relpath;
323
324 relpath = g_file_get_relative_path (root, fav_file);
325
326 if (relpath != NULL)
327 {
328 mount_favorites = g_list_prepend (mount_favorites, info);
329 }
330
331 g_free (relpath);
332 g_object_unref (fav_file);
333 }
334
335 if (mount_favorites != NULL)
336 {
337 for (iter = mount_favorites; iter != NULL; iter = iter->next) {
338 XAppFavoriteInfo *info = (XAppFavoriteInfo *) iter->data;
339 GFile *fav_file;
340 gchar *uri;
341
342 uri = path_to_fav_uri (info->display_name);
343 fav_file = g_file_new_for_uri (uri);
344
345 g_file_monitor_emit_event (G_FILE_MONITOR (monitor),
346 fav_file,
347 NULL,
348 G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED);
349 g_file_monitor_emit_event (G_FILE_MONITOR (monitor),
350 fav_file,
351 NULL,
352 G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT);
353
354 g_free (uri);
355 g_object_unref (fav_file);
356 }
357
358 g_list_free (mount_favorites);
359 }
360
361 g_object_unref (root);
362
363 unmonitor_files (monitor);
364 monitor_files (monitor);
365 }
366
367 static gboolean
368 favorite_vfs_file_monitor_cancel (GFileMonitor* gfilemon)
369 {
370 FavoriteVfsFileMonitor *monitor = FAVORITE_VFS_FILE_MONITOR (gfilemon);
371 FavoriteVfsFileMonitorPrivate *priv = favorite_vfs_file_monitor_get_instance_private (monitor);
372
373 if (priv->changed_handler_id > 0)
374 {
375 g_signal_handler_disconnect (xapp_favorites_get_default (), priv->changed_handler_id);
376 }
377
378 return TRUE;
379 }
380
381 static void
382 favorite_vfs_file_monitor_init (FavoriteVfsFileMonitor *monitor)
383 {
384 FavoriteVfsFileMonitorPrivate *priv = favorite_vfs_file_monitor_get_instance_private (monitor);
385
386 priv->mount_mon = g_volume_monitor_get ();
387 g_signal_connect (priv->mount_mon,
388 "mount-added",
389 G_CALLBACK (mounts_changed),
390 monitor);
391 g_signal_connect (priv->mount_mon,
392 "mount-removed",
393 G_CALLBACK (mounts_changed),
394 monitor);
395
396 priv->infos = xapp_favorites_get_favorites (xapp_favorites_get_default (), NULL);
397 priv->changed_handler_id = g_signal_connect (xapp_favorites_get_default (),
398 "changed",
399 G_CALLBACK (favorites_changed),
400 monitor);
401
402 monitor_files (monitor);
403 }
404
405 static void
406 favorite_vfs_file_monitor_dispose (GObject *object)
407 {
408 FavoriteVfsFileMonitor *monitor = FAVORITE_VFS_FILE_MONITOR(object);
409 FavoriteVfsFileMonitorPrivate *priv = favorite_vfs_file_monitor_get_instance_private(monitor);
410
411 unmonitor_files (monitor);
412
413 g_signal_handlers_disconnect_by_func (priv->mount_mon, mounts_changed, monitor);
414 g_clear_object (&priv->mount_mon);
415
416 if (priv->infos != NULL)
417 {
418 g_list_free_full (priv->infos, (GDestroyNotify) xapp_favorite_info_free);
419 priv->infos = NULL;
420 }
421
422 G_OBJECT_CLASS (favorite_vfs_file_monitor_parent_class)->dispose (object);
423 }
424
425 static void
426 favorite_vfs_file_monitor_finalize (GObject *object)
427 {
428 // FavoriteVfsFileMonitor *self = FAVORITE_VFS_FILE_MONITOR(object);
429 // FavoriteVfsFileMonitorPrivate *priv = favorite_vfs_file_monitor_get_instance_private(self);
430
431 G_OBJECT_CLASS (favorite_vfs_file_monitor_parent_class)->finalize (object);
432 }
433
434 static void
435 favorite_vfs_file_monitor_class_init (FavoriteVfsFileMonitorClass *klass)
436 {
437 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
438 GFileMonitorClass *monitor_class = G_FILE_MONITOR_CLASS (klass);
439
440 gobject_class->dispose = favorite_vfs_file_monitor_dispose;
441 gobject_class->finalize = favorite_vfs_file_monitor_finalize;
442
443 monitor_class->cancel = favorite_vfs_file_monitor_cancel;
444 }
445
446 GFileMonitor *
447 favorite_vfs_file_monitor_new (void)
448 {
449 return G_FILE_MONITOR (g_object_new (FAVORITE_TYPE_VFS_FILE_MONITOR, NULL));
450 }
451
0 #ifndef __FAVORITE_VFS_FILE_MONITOR_H__
1 #define __FAVORITE_VFS_FILE_MONITOR_H__
2
3 #include <gio/gio.h>
4 #include <glib-object.h>
5
6 G_BEGIN_DECLS
7
8 #define FAVORITE_TYPE_VFS_FILE_MONITOR (favorite_vfs_file_monitor_get_type ())
9
10 G_DECLARE_FINAL_TYPE (FavoriteVfsFileMonitor, favorite_vfs_file_monitor,
11 FAVORITE, VFS_FILE_MONITOR, GFileMonitor)
12
13 GFileMonitor *favorite_vfs_file_monitor_new (void);
14
15 G_END_DECLS
16
17 #endif /* __FAVORITE_VFS_FILE_MONITOR_H__ */
0 #include <config.h>
1 #include <glib/gi18n-lib.h>
2
3 #include "favorite-vfs-file.h"
4 #include "favorite-vfs-file-enumerator.h"
5 #include "favorite-vfs-file-monitor.h"
6
7 #define FAVORITES_SCHEMA "org.x.apps.favorites"
8 #define FAVORITE_DCONF_METADATA_KEY "root-metadata"
9
10 static GSettings *settings = NULL;
11
12 typedef struct
13 {
14 gchar *uri; // favorites://foo
15
16 XAppFavoriteInfo *info;
17 } FavoriteVfsFilePrivate;
18
19 struct _FavoriteVfsFile
20 {
21 GObject parent_instance;
22 FavoriteVfsFilePrivate *priv;
23 };
24
25 GList *_xapp_favorites_get_display_names (XAppFavorites *favorites);
26 static void favorite_vfs_file_gfile_iface_init (GFileIface *iface);
27
28 gchar *
29 path_to_fav_uri (const gchar *path)
30 {
31 g_return_val_if_fail (path != NULL, NULL);
32
33 return g_strconcat (ROOT_URI, path, NULL);
34 }
35
36 gchar *
37 fav_uri_to_display_name (const gchar *uri)
38 {
39 g_return_val_if_fail (uri != NULL, NULL);
40 g_return_val_if_fail (g_str_has_prefix (uri, ROOT_URI), NULL);
41
42 const gchar *ptr;
43
44 ptr = uri + strlen (ROOT_URI);
45
46 if (ptr[0] == '/')
47 {
48 ptr++;
49 }
50
51 return g_strdup (ptr);
52 }
53
54 G_DEFINE_TYPE_EXTENDED (FavoriteVfsFile,
55 favorite_vfs_file,
56 G_TYPE_OBJECT,
57 0,
58 G_ADD_PRIVATE (FavoriteVfsFile)
59 G_IMPLEMENT_INTERFACE (G_TYPE_FILE, favorite_vfs_file_gfile_iface_init))
60
61 static gboolean
62 is_root_file (FavoriteVfsFile *file)
63 {
64 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (file);
65
66 return g_strcmp0 (priv->uri, ROOT_URI) == 0;
67 }
68
69 static GFile *
70 file_dup (GFile *file)
71 {
72 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
73
74 return favorite_vfs_file_new_for_uri (priv->uri);
75 }
76
77 static guint
78 file_hash (GFile *file)
79 {
80 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
81
82 return g_str_hash (priv->uri);
83 }
84
85 static gboolean
86 file_equal (GFile *file1,
87 GFile *file2)
88 {
89 FavoriteVfsFilePrivate *priv1 = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file1));
90 FavoriteVfsFilePrivate *priv2 = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file2));
91
92 return g_strcmp0 (priv1->uri, priv2->uri) == 0;
93 }
94
95 static gboolean
96 file_is_native (GFile *file)
97 {
98 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
99
100 if (priv->info != NULL && priv->info->uri != NULL)
101 {
102 GFile *real_file;
103 gboolean is_really_native;
104
105 real_file = g_file_new_for_uri (priv->info->uri);
106 is_really_native = g_file_is_native (real_file);
107
108 g_object_unref (real_file);
109 return is_really_native;
110 }
111
112 return FALSE;
113 }
114
115 static gboolean
116 file_has_uri_scheme (GFile *file,
117 const gchar *uri_scheme)
118 {
119 return g_strcmp0 (uri_scheme, URI_SCHEME) == 0;
120 }
121
122 static gchar *
123 file_get_uri_scheme (GFile *file)
124 {
125 return g_strdup (URI_SCHEME);
126 }
127
128 static gchar *
129 file_get_basename (GFile *file)
130 {
131 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
132
133 if (priv->info == NULL)
134 {
135 return g_strdup ("/");
136 }
137
138 return g_strdup (priv->info->display_name);
139 }
140
141 static gchar *
142 file_get_path (GFile *file)
143 {
144 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
145
146 if (file_is_native (file))
147 {
148 GFile *real_file;
149 gchar *ret;
150
151 real_file = g_file_new_for_uri (priv->info->uri);
152
153 // file can't be native without an info, so we don't need to check for null here
154 ret = g_file_get_path (real_file);
155 g_object_unref (real_file);
156
157 return ret;
158 }
159
160 // GtkFileChooser checks for a path (and not null) before allowing a shortcut to
161 // be added using gtk_file_chooser_add_shortcut_folder_uri(). Even though this / doesn't
162 // make much sense for favorites:/// it allows it to be added to the file chooser without
163 // being forced to override its 'local-only' property.
164 if (is_root_file (FAVORITE_VFS_FILE (file)))
165 {
166 return g_strdup ("/");
167 }
168
169 return NULL;
170 }
171
172 static gchar *
173 file_get_uri (GFile *file)
174 {
175 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
176
177 return g_strdup (priv->uri);
178 }
179
180 static gchar *
181 file_get_parse_name (GFile *file)
182 {
183 return file_get_uri (file);
184 }
185
186 static GFile *
187 file_get_parent (GFile *file)
188 {
189 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
190
191 // We're only ever one level deep.
192 if (priv->info != NULL)
193 {
194 return g_file_new_for_uri (ROOT_URI);
195 }
196
197 return NULL;
198 }
199
200 static gboolean
201 file_prefix_matches (GFile *parent,
202 GFile *descendant)
203 {
204 g_autofree gchar *puri = NULL;
205 g_autofree gchar *duri = NULL;
206 gchar *ptr = NULL;
207
208 puri = g_file_get_uri (parent);
209 duri = g_file_get_uri (descendant);
210
211 ptr = g_strstr_len (puri, -1, duri);
212
213 if ((ptr == puri) && ptr[strlen (duri) + 1] == '/')
214 {
215 return TRUE;
216 }
217
218 return FALSE;
219 }
220
221 static gchar *
222 file_get_relative_path (GFile *parent,
223 GFile *descendant)
224 {
225 g_autofree gchar *puri = NULL;
226 g_autofree gchar *duri = NULL;
227 g_autofree gchar *rpath = NULL;
228 gchar *ptr = NULL;
229
230 puri = g_file_get_uri (parent);
231 duri = g_file_get_uri (descendant);
232
233 ptr = g_strstr_len (puri, -1, duri);
234
235 if ((ptr == puri) && ptr[strlen (duri) + 1] == '/')
236 {
237 rpath = g_strdup (puri + strlen (duri) + 1);
238
239 return rpath;
240 }
241
242 return NULL;
243 }
244
245 static GFile *
246 file_resolve_relative_path (GFile *file,
247 const gchar *relative_path)
248 {
249 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
250 GFile *relative_file;
251 gchar *uri;
252
253 if (g_path_is_absolute (relative_path))
254 {
255 return g_file_new_for_path (relative_path);
256 }
257
258 if (priv->info != NULL && priv->info->uri != NULL)
259 {
260 GFile *real_file;
261 real_file = g_file_new_for_uri (priv->info->uri);
262
263 relative_file = g_file_resolve_relative_path (real_file,
264 relative_path);
265
266 g_object_unref (real_file);
267 return relative_file;
268 }
269
270 if (g_strcmp0 (relative_path, ".") == 0)
271 {
272 relative_file = file_dup (file);
273
274 return relative_file;
275 }
276
277 uri = path_to_fav_uri (relative_path);
278
279 relative_file = g_file_new_for_uri (uri);
280 g_free (uri);
281
282 return relative_file;
283 }
284
285 static GFile *
286 file_get_child_for_display_name (GFile *file,
287 const char *display_name,
288 GError **error)
289 {
290 return g_file_get_child (file, display_name);
291 }
292
293 static GFile *
294 file_set_display_name (GFile *file,
295 const gchar *display_name,
296 GCancellable *cancellable,
297 GError **error)
298 {
299 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
300
301 if (priv->info != NULL && priv->info->uri != NULL)
302 {
303 GFile *real_file;
304 GFile *ret;
305
306 real_file = g_file_new_for_uri (priv->info->uri);
307 ret = g_file_set_display_name (real_file,
308 display_name,
309 cancellable,
310 error);
311 g_object_unref (real_file);
312
313 return ret;
314 }
315
316 g_set_error_literal (error, G_IO_ERROR,
317 G_IO_ERROR_NOT_SUPPORTED,
318 "Can't rename file");
319
320 return NULL;
321 }
322
323 static GFileEnumerator *
324 file_enumerate_children(GFile *file,
325 const char *attributes,
326 GFileQueryInfoFlags flags,
327 GCancellable *cancellable,
328 GError **error)
329 {
330 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
331 GFileEnumerator *enumerator;
332
333 if (priv->info != NULL && priv->info->uri != NULL)
334 {
335 GFile *real_file;
336 real_file = g_file_new_for_uri (priv->info->uri);
337
338 enumerator = g_file_enumerate_children (real_file,
339 attributes,
340 flags,
341 cancellable,
342 error);
343
344 g_object_unref (real_file);
345 return enumerator;
346 }
347
348 GList *uris;
349
350 uris = _xapp_favorites_get_display_names (xapp_favorites_get_default ());
351 enumerator = favorite_vfs_file_enumerator_new (file,
352 attributes,
353 flags,
354 uris);
355
356 g_list_free (uris);
357
358 return enumerator;
359 }
360
361 static GFileInfo *
362 file_query_info (GFile *file,
363 const char *attributes,
364 GFileQueryInfoFlags flags,
365 GCancellable *cancellable,
366 GError **error)
367 {
368 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
369 GFileInfo *info;
370 GIcon *icon;
371
372 if (priv->info != NULL)
373 {
374 if (!priv->info->uri)
375 {
376 if (error != NULL)
377 {
378 *error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "File not found");
379 }
380 return NULL;
381 }
382
383 GFile *real_file = g_file_new_for_uri (priv->info->uri);
384
385 info = g_file_query_info (real_file, attributes, flags, cancellable, error);
386
387 if (info != NULL)
388 {
389 gchar *local_path;
390
391 g_file_info_set_display_name (info, priv->info->display_name);
392 g_file_info_set_name (info, priv->info->display_name);
393 g_file_info_set_is_symlink (info, TRUE);
394
395 local_path = g_file_get_path (real_file);
396
397 if (local_path != NULL)
398 {
399 g_file_info_set_symlink_target (info, local_path);
400 g_free (local_path);
401 }
402 else
403 {
404 g_file_info_set_symlink_target (info, priv->info->uri);
405 }
406
407 // Recent sets this also. If it's set, this uri is used to display the "location"
408 // for the file (the directory in which real file resides).
409 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, priv->info->uri);
410
411 g_file_info_set_attribute_string (info, FAVORITE_AVAILABLE_METADATA_KEY, META_TRUE);
412 }
413 else
414 {
415 g_clear_error (error);
416 gchar *content_type;
417
418 info = g_file_info_new ();
419
420 g_file_info_set_display_name (info, priv->info->display_name);
421 g_file_info_set_name (info, priv->info->display_name);
422 g_file_info_set_is_symlink (info, TRUE);
423
424 g_file_info_set_symlink_target (info, priv->info->uri);
425
426 /* Prevent showing a 'thumbnailing' icon */
427 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED, TRUE);
428
429 /* This will keep the sort position the same for missing or unmounted files */
430 g_file_info_set_attribute_string (info, FAVORITE_METADATA_KEY, META_TRUE);
431 g_file_info_set_attribute_string (info, FAVORITE_AVAILABLE_METADATA_KEY, META_FALSE);
432 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, priv->info->uri);
433
434 content_type = g_content_type_from_mime_type (priv->info->cached_mimetype);
435
436 icon = g_content_type_get_icon (content_type);
437 g_file_info_set_icon (info, icon);
438 g_object_unref (icon);
439
440 icon = g_content_type_get_symbolic_icon (content_type);
441 g_file_info_set_symbolic_icon (info, icon);
442 g_object_unref (icon);
443
444 g_free (content_type);
445 }
446
447 g_object_unref (real_file);
448
449 return info;
450 }
451
452 if (is_root_file (FAVORITE_VFS_FILE (file)))
453 {
454 GFileAttributeMatcher *matcher = g_file_attribute_matcher_new (attributes);
455
456 info = g_file_info_new ();
457
458 if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_NAME))
459 g_file_info_set_name (info, "/");
460
461 if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME))
462 g_file_info_set_display_name (info, _("Favorites"));
463
464 if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_TYPE))
465 g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
466
467 if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_ICON))
468 {
469 icon = g_themed_icon_new ("xapp-favorites");
470 g_file_info_set_icon (info, icon);
471
472 g_object_unref (icon);
473 }
474
475 if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_SYMBOLIC_ICON))
476 {
477 icon = g_themed_icon_new ("xapp-favorites-symbolic");
478 g_file_info_set_symbolic_icon (info, icon);
479 g_object_unref (icon);
480 }
481
482 if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_GVFS_BACKEND))
483 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_GVFS_BACKEND, "favorites");
484
485 if (g_file_attribute_matcher_matches (matcher, FAVORITE_AVAILABLE_METADATA_KEY))
486 g_file_info_set_attribute_string (info, FAVORITE_AVAILABLE_METADATA_KEY, META_TRUE);
487
488 if (g_file_attribute_matcher_enumerate_namespace (matcher, "metadata"))
489 {
490 gchar **entries = g_settings_get_strv (settings, FAVORITE_DCONF_METADATA_KEY);
491
492 if (entries != NULL)
493 {
494 gint i;
495
496 for (i = 0; entries[i] != NULL; i++)
497 {
498 gchar **t_n_v;
499
500 t_n_v = g_strsplit (entries[i], "==", 3);
501
502 if (g_strv_length (t_n_v) == 3)
503 {
504 if (g_strcmp0 (t_n_v[0], "string") == 0)
505 {
506 g_file_info_set_attribute_string (info, t_n_v[1], t_n_v[2]);
507 }
508 else
509 if (g_strcmp0 (t_n_v[0], "strv") == 0)
510 {
511 gchar **members = g_strsplit (t_n_v[2], "|", -1);
512
513 g_file_info_set_attribute_stringv (info, t_n_v[1], members);
514
515 g_strfreev (members);
516 }
517 }
518
519 g_strfreev (t_n_v);
520 }
521 }
522
523 g_strfreev (entries);
524 }
525
526 g_file_attribute_matcher_unref (matcher);
527 }
528
529 return info;
530 }
531
532 GFileInfo *
533 file_query_filesystem_info (GFile *file,
534 const char *attributes,
535 GCancellable *cancellable,
536 GError **error)
537 {
538 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
539
540 if (priv->info != NULL && priv->info->uri != NULL)
541 {
542 GFileInfo *info;
543 GFile *real_file;
544 real_file = g_file_new_for_uri (priv->info->uri);
545
546 info = g_file_query_filesystem_info (real_file,
547 attributes,
548 cancellable,
549 error);
550
551 g_object_unref (real_file);
552 return info;
553 }
554
555 GFileInfo *info;
556 GFileAttributeMatcher *matcher;
557
558 info = g_file_info_new ();
559
560 matcher = g_file_attribute_matcher_new (attributes);
561
562 if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE))
563 {
564 g_file_info_set_attribute_string (info,
565 G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "favorites");
566 }
567
568 if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY))
569 {
570 g_file_info_set_attribute_boolean (info,
571 G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
572 }
573
574 g_file_attribute_matcher_unref (matcher);
575
576 return info;
577 }
578
579 GMount *
580 file_find_enclosing_mount (GFile *file,
581 GCancellable *cancellable,
582 GError **error)
583 {
584 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
585
586 if (priv->info != NULL && priv->info->uri != NULL)
587 {
588 GMount *mount;
589 GFile *real_file;
590 real_file = g_file_new_for_uri (priv->info->uri);
591
592 mount = g_file_find_enclosing_mount (real_file,
593 cancellable,
594 error);
595
596 g_object_unref (real_file);
597 return mount;
598 }
599
600 g_set_error_literal (error, G_IO_ERROR,
601 G_IO_ERROR_NOT_SUPPORTED,
602 "Can't find favorite file enclosing mount");
603
604 return NULL;
605 }
606
607 GFileAttributeInfoList *
608 file_query_settable_attributes (GFile *file,
609 GCancellable *cancellable,
610 GError **error)
611 {
612 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
613
614 if (priv->info != NULL && priv->info->uri != NULL)
615 {
616 GFileAttributeInfoList *list;
617 GFile *real_file;
618 real_file = g_file_new_for_uri (priv->info->uri);
619
620 list = g_file_query_settable_attributes (real_file,
621 cancellable,
622 error);
623
624 g_object_unref (real_file);
625 return list;
626 }
627
628 return g_file_attribute_info_list_new ();
629 }
630
631 GFileAttributeInfoList *
632 file_query_writable_namespaces (GFile *file,
633 GCancellable *cancellable,
634 GError **error)
635 {
636 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
637 GFileAttributeInfoList *list;
638
639 if (priv->info != NULL && priv->info->uri != NULL)
640 {
641 GFile *real_file;
642 real_file = g_file_new_for_uri (priv->info->uri);
643
644 list = g_file_query_writable_namespaces (real_file,
645 cancellable,
646 error);
647
648 g_object_unref (real_file);
649 return list;
650 }
651
652 list = g_file_attribute_info_list_new ();
653
654 g_file_attribute_info_list_add (list,
655 "metadata",
656 G_FILE_ATTRIBUTE_TYPE_STRING,
657 G_FILE_ATTRIBUTE_INFO_NONE);
658
659 return list;
660 }
661
662 static void
663 remove_root_metadata (const gchar *attr_name)
664 {
665 GPtrArray *new_array;
666 gchar **old_metadata, **new_metadata;
667 gint i;
668
669 old_metadata = g_settings_get_strv (settings, FAVORITE_DCONF_METADATA_KEY);
670
671 if (old_metadata == NULL)
672 {
673 return;
674 }
675
676 new_array = g_ptr_array_new ();
677
678 for (i = 0; old_metadata[i] != NULL; i++)
679 {
680 gchar **t_n_v;
681
682 t_n_v = g_strsplit (old_metadata[i], "==", 3);
683
684 if (g_strcmp0 (t_n_v[1], attr_name) != 0)
685 {
686 g_ptr_array_add (new_array, g_strdup (old_metadata[i]));
687 }
688
689 g_strfreev (t_n_v);
690 }
691
692 g_ptr_array_add (new_array, NULL);
693 g_strfreev (old_metadata);
694
695 new_metadata = (gchar **) g_ptr_array_free (new_array, FALSE);
696
697 g_settings_set_strv (settings, FAVORITE_DCONF_METADATA_KEY, (const gchar * const *) new_metadata);
698 g_strfreev (new_metadata);
699 }
700
701 static void
702 set_or_update_root_metadata (const gchar *attr_name,
703 const gpointer value,
704 GFileAttributeType type)
705 {
706 GPtrArray *new_array;
707 gchar **old_metadata, **new_metadata;
708 gint i;
709 gchar *entry;
710 gboolean exists;
711
712 old_metadata = g_settings_get_strv (settings, FAVORITE_DCONF_METADATA_KEY);
713
714 if (old_metadata == NULL)
715 {
716 return;
717 }
718
719 switch (type)
720 {
721 case G_FILE_ATTRIBUTE_TYPE_STRING:
722 {
723 entry = g_strdup_printf ("string==%s==%s", attr_name, (gchar *) value);
724 break;
725 }
726 case G_FILE_ATTRIBUTE_TYPE_STRINGV:
727 {
728 gchar *val_strv = g_strjoinv ("|", (gchar **) value);
729 entry = g_strdup_printf ("strv==%s==%s", attr_name, val_strv);
730 g_free (val_strv);
731 break;
732 }
733 default:
734 break;
735 }
736
737 exists = FALSE;
738 new_array = g_ptr_array_new ();
739
740 for (i = 0; old_metadata[i] != NULL; i++)
741 {
742 gchar **t_n_v;
743
744 t_n_v = g_strsplit (old_metadata[i], "==", 3);
745
746 if (g_strcmp0 (t_n_v[1], attr_name) == 0)
747 {
748 g_ptr_array_add (new_array, entry);
749 exists = TRUE;
750 }
751 else
752 {
753 g_ptr_array_add (new_array, g_strdup (old_metadata[i]));
754 }
755
756 g_strfreev (t_n_v);
757 }
758
759 if (!exists)
760 {
761 g_ptr_array_add (new_array, entry);
762 }
763
764 g_ptr_array_add (new_array, NULL);
765 g_strfreev (old_metadata);
766
767 new_metadata = (gchar **) g_ptr_array_free (new_array, FALSE);
768
769 g_settings_set_strv (settings, FAVORITE_DCONF_METADATA_KEY, (const gchar * const *) new_metadata);
770 g_strfreev (new_metadata);
771 }
772
773 gboolean
774 file_set_attribute (GFile *file,
775 const gchar *attribute,
776 GFileAttributeType type,
777 gpointer value_p,
778 GFileQueryInfoFlags flags,
779 GCancellable *cancellable,
780 GError **error)
781 {
782 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
783 gboolean ret;
784
785 if (priv->info != NULL && priv->info->uri != NULL)
786 {
787 GFile *real_file;
788 gboolean ret;
789 real_file = g_file_new_for_uri (priv->info->uri);
790
791 ret = g_file_set_attribute (real_file,
792 attribute,
793 type,
794 value_p,
795 flags,
796 cancellable,
797 error);
798
799 g_object_unref (real_file);
800 return ret;
801 }
802
803 ret = FALSE;
804
805 if (!is_root_file (FAVORITE_VFS_FILE (file)))
806 {
807 g_set_error (error, G_IO_ERROR,
808 G_IO_ERROR_NOT_SUPPORTED,
809 "Can't set attributes for %s - only the root (favorites:///) is supported.", priv->uri);
810 }
811 else
812 {
813 if (g_str_has_prefix (attribute, "metadata"))
814 {
815 if (type == G_FILE_ATTRIBUTE_TYPE_INVALID || value_p == NULL || ((char *) value_p)[0] == '\0')
816 {
817 // unset metadata
818 remove_root_metadata (attribute);
819 ret = TRUE;
820 }
821 else
822 {
823 if (type == G_FILE_ATTRIBUTE_TYPE_STRING || type == G_FILE_ATTRIBUTE_TYPE_STRINGV)
824 {
825 set_or_update_root_metadata (attribute, (gchar *) value_p, type);
826 ret = TRUE;
827 }
828 else
829 {
830 g_set_error (error, G_IO_ERROR,
831 G_IO_ERROR_NOT_SUPPORTED,
832 "Can't set attribute '%s' for favorites:/// file "
833 "(only string-type metadata are allowed).", attribute);
834 }
835 }
836 }
837 else
838 {
839 g_set_error (error, G_IO_ERROR,
840 G_IO_ERROR_NOT_SUPPORTED,
841 "Can't set attribute '%s' for favorites:/// file "
842 "(only 'metadata' namespace is allowed).", attribute);
843 }
844 }
845
846 return ret;
847 }
848
849 static gboolean
850 file_set_attributes_from_info (GFile *file,
851 GFileInfo *info,
852 GFileQueryInfoFlags flags,
853 GCancellable *cancellable,
854 GError **error)
855 {
856 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
857 gboolean res;
858
859 if (priv->info != NULL && priv->info->uri != NULL)
860 {
861 GFile *real_file;
862 gboolean ret = FALSE;
863 real_file = g_file_new_for_uri (priv->info->uri);
864
865 ret = g_file_set_attributes_from_info (real_file, info, flags, cancellable, error);
866
867 g_object_unref (real_file);
868 return ret;
869 }
870
871 res = TRUE;
872
873 if (g_file_info_has_namespace (info, "metadata"))
874 {
875 GFileAttributeType type;
876 gchar **attributes;
877 gpointer value_p;
878 gint i;
879
880 attributes = g_file_info_list_attributes (info, "metadata");
881
882 for (i = 0; attributes[i] != NULL; i++)
883 {
884 if (g_file_info_get_attribute_data (info, attributes[i], &type, &value_p, NULL))
885 {
886 if (!file_set_attribute (file,
887 attributes[i],
888 type,
889 value_p,
890 flags,
891 cancellable,
892 error))
893 {
894 g_file_info_set_attribute_status (info, attributes[i], G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING);
895 error = NULL; // from gvfs gdaemonvfs.c - ignore subsequent errors iterating thru attribute list.
896 res = FALSE;
897 }
898 else
899 {
900 g_file_info_set_attribute_status (info, attributes[i], G_FILE_ATTRIBUTE_STATUS_SET);
901 }
902 }
903 }
904
905 g_strfreev (attributes);
906 }
907
908 return res;
909 }
910
911 static GFileInputStream *
912 file_read_fn (GFile *file,
913 GCancellable *cancellable,
914 GError **error)
915 {
916 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
917
918 if (priv->info != NULL && priv->info->uri != NULL)
919 {
920 GFile *real_file = g_file_new_for_uri (priv->info->uri);
921
922 GFileInputStream *stream;
923
924 stream = g_file_read (real_file, cancellable, error);
925
926 g_object_unref (real_file);
927 return stream;
928 }
929
930 g_set_error_literal (error, G_IO_ERROR,
931 G_IO_ERROR_NOT_SUPPORTED,
932 _("Operation not supported"));
933
934 return NULL;
935 }
936
937 GFileOutputStream *
938 file_append_to (GFile *file,
939 GFileCreateFlags flags,
940 GCancellable *cancellable,
941 GError **error)
942 {
943 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
944
945 if (priv->info != NULL && priv->info->uri != NULL)
946 {
947 GFile *real_file = g_file_new_for_uri (priv->info->uri);
948
949 GFileOutputStream *stream;
950
951 stream = g_file_append_to (real_file,
952 flags,
953 cancellable,
954 error);
955
956 g_object_unref (real_file);
957 return stream;
958 }
959
960 g_set_error_literal (error, G_IO_ERROR,
961 G_IO_ERROR_NOT_SUPPORTED,
962 _("Operation not supported"));
963 return NULL;
964 }
965
966 GFileOutputStream *
967 file_create (GFile *file,
968 GFileCreateFlags flags,
969 GCancellable *cancellable,
970 GError **error)
971 {
972 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
973
974 if (priv->info != NULL && priv->info->uri != NULL)
975 {
976 GFile *real_file = g_file_new_for_uri (priv->info->uri);
977
978 GFileOutputStream *stream;
979
980 stream = g_file_create (real_file,
981 flags,
982 cancellable,
983 error);
984
985 g_object_unref (real_file);
986 return stream;
987 }
988
989 g_set_error_literal (error, G_IO_ERROR,
990 G_IO_ERROR_NOT_SUPPORTED,
991 _("Operation not supported"));
992
993 return NULL;
994 }
995
996 static GFileOutputStream *
997 file_replace (GFile *file,
998 const char *etag,
999 gboolean make_backup,
1000 GFileCreateFlags flags,
1001 GCancellable *cancellable,
1002 GError **error)
1003 {
1004 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1005
1006 if (priv->info != NULL && priv->info->uri != NULL)
1007 {
1008 GFile *real_file = g_file_new_for_uri (priv->info->uri);
1009
1010 GFileOutputStream *stream;
1011
1012 stream = g_file_replace (real_file,
1013 etag,
1014 make_backup,
1015 flags,
1016 cancellable,
1017 error);
1018
1019 g_object_unref (real_file);
1020 return stream;
1021 }
1022
1023 g_set_error_literal (error, G_IO_ERROR,
1024 G_IO_ERROR_NOT_SUPPORTED,
1025 _("Operation not supported"));
1026
1027 return NULL;
1028 }
1029
1030 static GFileIOStream *
1031 file_open_readwrite (GFile *file,
1032 GCancellable *cancellable,
1033 GError **error)
1034 {
1035 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1036
1037 if (priv->info != NULL && priv->info->uri != NULL)
1038 {
1039 GFileIOStream *res;
1040 GFile *real_file = g_file_new_for_uri (priv->info->uri);
1041
1042 res = g_file_open_readwrite (real_file,
1043 cancellable,
1044 error);
1045
1046 g_object_unref (real_file);
1047 return res;
1048 }
1049
1050 g_set_error_literal (error, G_IO_ERROR,
1051 G_IO_ERROR_NOT_SUPPORTED,
1052 _("Operation not supported"));
1053
1054 return NULL;
1055 }
1056
1057 static GFileIOStream *
1058 file_create_readwrite (GFile *file,
1059 GFileCreateFlags flags,
1060 GCancellable *cancellable,
1061 GError **error)
1062 {
1063 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1064
1065 if (priv->info != NULL && priv->info->uri != NULL)
1066 {
1067 GFileIOStream *res;
1068 GFile *real_file = g_file_new_for_uri (priv->info->uri);
1069
1070 res = g_file_create_readwrite (real_file,
1071 flags,
1072 cancellable,
1073 error);
1074
1075 g_object_unref (real_file);
1076 return res;
1077 }
1078
1079 g_set_error_literal (error, G_IO_ERROR,
1080 G_IO_ERROR_NOT_SUPPORTED,
1081 _("Operation not supported"));
1082
1083 return NULL;
1084 }
1085
1086 static GFileIOStream *
1087 file_replace_readwrite (GFile *file,
1088 const char *etag,
1089 gboolean make_backup,
1090 GFileCreateFlags flags,
1091 GCancellable *cancellable,
1092 GError **error)
1093 {
1094 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1095
1096 if (priv->info != NULL && priv->info->uri != NULL)
1097 {
1098 GFileIOStream *res;
1099 GFile *real_file = g_file_new_for_uri (priv->info->uri);
1100
1101 res = g_file_replace_readwrite (real_file,
1102 etag,
1103 make_backup,
1104 flags,
1105 cancellable,
1106 error);
1107
1108 g_object_unref (real_file);
1109 return res;
1110 }
1111
1112 g_set_error_literal (error, G_IO_ERROR,
1113 G_IO_ERROR_NOT_SUPPORTED,
1114 _("Operation not supported"));
1115
1116 return NULL;
1117 }
1118
1119 static gboolean
1120 file_delete (GFile *file,
1121 GCancellable *cancellable,
1122 GError **error)
1123 {
1124 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1125
1126 if (priv->info != NULL && priv->info->uri != NULL)
1127 {
1128 gboolean res;
1129 GFile *real_file = g_file_new_for_uri (priv->info->uri);
1130
1131 res = g_file_delete (real_file,
1132 cancellable,
1133 error);
1134
1135 g_object_unref (real_file);
1136 return res;
1137 }
1138
1139 g_set_error_literal (error, G_IO_ERROR,
1140 G_IO_ERROR_NOT_SUPPORTED,
1141 _("Operation not supported"));
1142
1143 return FALSE;
1144 }
1145
1146 static gboolean
1147 file_trash (GFile *file,
1148 GCancellable *cancellable,
1149 GError **error)
1150 {
1151 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1152
1153 if (priv->info != NULL && priv->info->uri != NULL)
1154 {
1155 gboolean res;
1156 GFile *real_file = g_file_new_for_uri (priv->info->uri);
1157
1158 res = g_file_trash (real_file,
1159 cancellable,
1160 error);
1161 g_object_unref (real_file);
1162
1163 return res;
1164 }
1165
1166 g_set_error_literal (error, G_IO_ERROR,
1167 G_IO_ERROR_NOT_SUPPORTED,
1168 "Not supported");
1169
1170 return FALSE;
1171 }
1172
1173 gboolean
1174 file_move (GFile *source,
1175 GFile *destination,
1176 GFileCopyFlags flags,
1177 GCancellable *cancellable,
1178 GFileProgressCallback progress_callback,
1179 gpointer progress_callback_data,
1180 GError **error)
1181 {
1182 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (source));
1183
1184 if (priv->info != NULL && priv->info->uri != NULL)
1185 {
1186 gboolean res;
1187 GFile *real_file = g_file_new_for_uri (priv->info->uri);
1188
1189 res = g_file_move (real_file,
1190 destination,
1191 flags,
1192 cancellable,
1193 progress_callback,
1194 progress_callback_data,
1195 error);
1196
1197 g_object_unref (real_file);
1198 return res;
1199 }
1200
1201 g_set_error_literal (error, G_IO_ERROR,
1202 G_IO_ERROR_NOT_SUPPORTED,
1203 _("Operation not supported"));
1204
1205 return FALSE;
1206 }
1207
1208 static GFileMonitor *
1209 file_monitor_dir (GFile *file,
1210 GFileMonitorFlags flags,
1211 GCancellable *cancellable,
1212 GError **error)
1213 {
1214 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1215
1216 if (priv->info != NULL && priv->info->uri != NULL)
1217 {
1218 GFile *real_file;
1219 GFileMonitor *monitor;
1220
1221 real_file = g_file_new_for_uri (priv->info->uri);
1222 monitor = g_file_monitor_directory (real_file,
1223 flags,
1224 cancellable,
1225 error);
1226 g_object_unref (real_file);
1227
1228 return monitor;
1229 }
1230 else
1231 if (is_root_file (FAVORITE_VFS_FILE (file)))
1232 {
1233 return favorite_vfs_file_monitor_new ();
1234 }
1235
1236 g_set_error_literal (error, G_IO_ERROR,
1237 G_IO_ERROR_NOT_SUPPORTED,
1238 _("Operation not supported"));
1239
1240 return NULL;
1241 }
1242
1243 static GFileMonitor *
1244 file_monitor_file (GFile *file,
1245 GFileMonitorFlags flags,
1246 GCancellable *cancellable,
1247 GError **error)
1248 {
1249 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1250
1251 if (priv->info != NULL && priv->info->uri != NULL)
1252 {
1253 GFile *real_file;
1254 GFileMonitor *monitor;
1255
1256 real_file = g_file_new_for_uri (priv->info->uri);
1257 monitor = g_file_monitor_file (real_file,
1258 flags,
1259 cancellable,
1260 error);
1261 g_object_unref (real_file);
1262
1263 return monitor;
1264 }
1265
1266 g_set_error_literal (error, G_IO_ERROR,
1267 G_IO_ERROR_NOT_SUPPORTED,
1268 _("Operation not supported"));
1269
1270 return NULL;
1271 }
1272
1273 gboolean
1274 file_measure_disk_usage (GFile *file,
1275 GFileMeasureFlags flags,
1276 GCancellable *cancellable,
1277 GFileMeasureProgressCallback progress_callback,
1278 gpointer progress_data,
1279 guint64 *disk_usage,
1280 guint64 *num_dirs,
1281 guint64 *num_files,
1282 GError **error)
1283 {
1284 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1285
1286 if (priv->info != NULL && priv->info->uri != NULL)
1287 {
1288 gboolean res;
1289 GFile *real_file = g_file_new_for_uri (priv->info->uri);
1290
1291 res = g_file_measure_disk_usage (real_file,
1292 flags,
1293 cancellable,
1294 progress_callback,
1295 progress_data,
1296 disk_usage,
1297 num_dirs,
1298 num_files,
1299 error);
1300
1301 g_object_unref (real_file);
1302 return res;
1303 }
1304
1305 g_set_error_literal (error, G_IO_ERROR,
1306 G_IO_ERROR_NOT_SUPPORTED,
1307 _("Operation not supported"));
1308
1309 return FALSE;
1310 }
1311
1312 static void favorite_vfs_file_gfile_iface_init (GFileIface *iface)
1313 {
1314 iface->dup = file_dup;
1315 iface->hash = file_hash;
1316 iface->equal = file_equal;
1317 iface->is_native = file_is_native;
1318 iface->has_uri_scheme = file_has_uri_scheme;
1319 iface->get_uri_scheme = file_get_uri_scheme;
1320 iface->get_basename = file_get_basename;
1321 iface->get_path = file_get_path;
1322 iface->get_uri = file_get_uri;
1323 iface->get_parse_name = file_get_parse_name;
1324 iface->get_parent = file_get_parent;
1325 iface->prefix_matches = file_prefix_matches;
1326 iface->get_relative_path = file_get_relative_path;
1327 iface->resolve_relative_path = file_resolve_relative_path;
1328 iface->get_child_for_display_name = file_get_child_for_display_name;
1329 iface->set_display_name = file_set_display_name;
1330 iface->enumerate_children = file_enumerate_children;
1331 iface->query_info = file_query_info;
1332 iface->query_filesystem_info = file_query_filesystem_info;
1333 iface->find_enclosing_mount = file_find_enclosing_mount;
1334 iface->query_settable_attributes = file_query_settable_attributes;
1335 iface->query_writable_namespaces = file_query_writable_namespaces;
1336 iface->set_attribute = file_set_attribute;
1337 iface->set_attributes_from_info = file_set_attributes_from_info;
1338 iface->read_fn = file_read_fn;
1339 iface->append_to = file_append_to;
1340 iface->create = file_create;
1341 iface->replace = file_replace;
1342 iface->open_readwrite = file_open_readwrite;
1343 iface->create_readwrite = file_create_readwrite;
1344 iface->replace_readwrite = file_replace_readwrite;
1345 iface->delete_file = file_delete;
1346 iface->trash = file_trash;
1347 // iface->make_directory = file_make_directory; ### Don't support
1348 // iface->make_symbolic_link = file_make_symbolic_link; ### Don't support
1349 iface->move = file_move;
1350 iface->monitor_dir = file_monitor_dir;
1351 iface->monitor_file = file_monitor_file;
1352 iface->measure_disk_usage = file_measure_disk_usage;
1353 iface->supports_thread_contexts = TRUE;
1354 }
1355
1356 static void favorite_vfs_file_dispose (GObject *object)
1357 {
1358 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (object));
1359
1360 if (priv->info != NULL)
1361 {
1362 xapp_favorite_info_free (priv->info);
1363 priv->info = NULL;
1364 }
1365
1366 g_clear_pointer (&priv->uri, g_free);
1367
1368 G_OBJECT_CLASS (favorite_vfs_file_parent_class)->dispose (object);
1369 }
1370
1371 static void
1372 ensure_metadata_store (FavoriteVfsFile *file)
1373 {
1374 if (is_root_file (file))
1375 {
1376 if (settings == NULL)
1377 {
1378 settings = g_settings_new (FAVORITES_SCHEMA);
1379 g_object_add_weak_pointer (G_OBJECT (settings), (gpointer) &settings);
1380 }
1381 else
1382 {
1383 g_object_ref (settings);
1384 }
1385 }
1386 }
1387
1388 static void favorite_vfs_file_class_init (FavoriteVfsFileClass *klass)
1389 {
1390 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1391
1392 gobject_class->dispose = favorite_vfs_file_dispose;
1393 }
1394
1395 static void favorite_vfs_file_init (FavoriteVfsFile *self)
1396 {
1397 }
1398
1399 GFile *_favorite_vfs_file_new_for_info (XAppFavoriteInfo *info)
1400 {
1401 FavoriteVfsFile *new_file;
1402
1403 new_file = g_object_new (FAVORITE_TYPE_VFS_FILE, NULL);
1404
1405 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (new_file));
1406
1407 priv->uri = path_to_fav_uri (info->display_name);
1408 priv->info = xapp_favorite_info_copy (info);
1409 ensure_metadata_store (new_file);
1410
1411 return G_FILE (new_file);
1412 }
1413
1414 gchar *favorite_vfs_file_get_real_uri (GFile *file)
1415 {
1416 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (file));
1417
1418 if (priv->info != NULL && priv->info->uri != NULL)
1419 {
1420 return g_strdup (priv->info->uri);
1421 }
1422
1423 return NULL;
1424 }
1425
1426 GFile *favorite_vfs_file_new_for_uri (const char *uri)
1427 {
1428 FavoriteVfsFile *new_file;
1429 g_autofree gchar *basename = NULL;
1430
1431 new_file = g_object_new (FAVORITE_TYPE_VFS_FILE, NULL);
1432
1433 g_debug ("FavoriteVfsFile new for uri: %s", uri);
1434
1435 FavoriteVfsFilePrivate *priv = favorite_vfs_file_get_instance_private (FAVORITE_VFS_FILE (new_file));
1436
1437 priv->uri = g_strdup (uri);
1438 ensure_metadata_store (new_file);
1439
1440 if (g_strcmp0 (uri, ROOT_URI) == 0)
1441 {
1442 priv->info = NULL;
1443 }
1444 else
1445 {
1446 gchar *display_name;
1447
1448 display_name = fav_uri_to_display_name (uri);
1449 XAppFavoriteInfo *info = xapp_favorites_find_by_display_name (xapp_favorites_get_default (),
1450 display_name);
1451
1452 if (info != NULL)
1453 {
1454 priv->info = xapp_favorite_info_copy (info);
1455 }
1456 else
1457 {
1458 info = g_slice_new0 (XAppFavoriteInfo);
1459 info->uri = g_strdup (NULL);
1460 info->display_name = g_strdup (display_name);
1461 info->cached_mimetype = NULL;
1462
1463 priv->info = info;
1464 }
1465
1466 g_free (display_name);
1467 }
1468
1469 return G_FILE (new_file);
1470 }
1471
1472 GFile *favorite_vfs_file_new (void)
1473 {
1474 return favorite_vfs_file_new_for_uri (ROOT_URI);
1475 }
1476
1477 static GFile *
1478 favorite_vfs_lookup (GVfs *vfs,
1479 const char *identifier,
1480 gpointer user_data)
1481 {
1482 if (g_str_has_prefix (identifier, ROOT_URI))
1483 {
1484 return favorite_vfs_file_new_for_uri (identifier);
1485 }
1486
1487 return NULL;
1488 }
1489
1490 void
1491 init_favorite_vfs (void)
1492 {
1493 GVfs *vfs;
1494 vfs = g_vfs_get_default ();
1495
1496 g_vfs_register_uri_scheme (vfs, "favorites",
1497 favorite_vfs_lookup, NULL, NULL,
1498 favorite_vfs_lookup, NULL, NULL);
1499 }
1500
0 #ifndef FAVORITE_VFS_FILE_H
1 #define FAVORITE_VFS_FILE_H
2
3 #include "xapp-favorites.h"
4 #include <glib-object.h>
5 #include <gio/gio.h>
6
7 G_BEGIN_DECLS
8
9 #define FAVORITE_TYPE_VFS_FILE (favorite_vfs_file_get_type ())
10
11 G_DECLARE_FINAL_TYPE (FavoriteVfsFile, favorite_vfs_file, \
12 FAVORITE, VFS_FILE, GObject)
13
14 // Initializer for favorites:/// - called when the XAppFavorites singleton is created
15 void init_favorite_vfs (void);
16
17 GFile *favorite_vfs_file_new_for_uri (const char *uri);
18 gchar *favorite_vfs_file_get_real_uri (GFile *file);
19
20 #define URI_SCHEME "favorites"
21 #define ROOT_URI ("favorites:///")
22
23 #define FAVORITE_METADATA_KEY "metadata::xapp-favorite"
24 #define FAVORITE_AVAILABLE_METADATA_KEY "metadata::xapp-favorite-available"
25
26 #define META_TRUE "true"
27 #define META_FALSE "false"
28
29 gchar *path_to_fav_uri (const gchar *path);
30 gchar *fav_uri_to_display_name (const gchar *uri);
31
32 G_END_DECLS
33
34 #endif // FAVORITE_VFS_FILE_H
+0
-32
libxapp/g-codegen.py less more
0 #!/usr/bin/env python3
1
2 '''
3 FIXME
4
5 This script is used only to call gdbus-codegen and simulate the
6 generation of the source code and header as different targets.
7
8 Both are generated implicitly, so meson is not able to know how
9 many files are generated, so it does generate only one opaque
10 target that represents the two files.
11
12 originally from:
13 https://gitlab.gnome.org/GNOME/gnome-settings-daemon/commit/5924d72931a030b24554116a48140a661a99652b
14
15 Please see:
16 https://bugzilla.gnome.org/show_bug.cgi?id=791015
17 https://github.com/mesonbuild/meson/pull/2930
18 '''
19
20 import subprocess
21 import sys
22 import os
23
24 subprocess.call([
25 'gdbus-codegen',
26 '--interface-prefix=' + sys.argv[1],
27 '--generate-c-code=' + os.path.join(sys.argv[4], sys.argv[2]),
28 '--c-namespace=XApp',
29 '--annotate', sys.argv[1], 'org.gtk.GDBus.C.Name', sys.argv[3],
30 sys.argv[5]
31 ])
00 glib_min_ver = '>=2.37.3'
11
2 gio_dep = dependency('gio-2.0', version: glib_min_ver, required: true)
3 glib_dep = dependency('glib-2.0', version: glib_min_ver, required: true)
4 gtk3_dep = dependency('gtk+-3.0', version: '>=3.3.16', required: true)
5
26 libdeps = []
3 libdeps += dependency('gio-2.0', version: glib_min_ver, required: true)
4 libdeps += dependency('glib-2.0', version: glib_min_ver, required: true)
5 libdeps += dependency('gtk+-3.0', version: '>=3.3.16', required: true)
7 libdeps += gio_dep
8 libdeps += glib_dep
9 libdeps += gtk3_dep
610 libdeps += dependency('gdk-pixbuf-2.0', version: '>=2.22.0', required: true)
711 libdeps += dependency('cairo', required: true)
812 libdeps += dependency('x11', required: true)
913
14 favorite_vfs_sources = [
15 'favorite-vfs-file.c',
16 'favorite-vfs-file-enumerator.c',
17 'favorite-vfs-file-monitor.c'
18 ]
19
1020 xapp_headers = [
21 'xapp-favorites.h',
1122 'xapp-gtk-window.h',
1223 'xapp-icon-chooser-button.h',
1324 'xapp-icon-chooser-dialog.h',
2031 ]
2132
2233 xapp_sources = [
34 'xapp-favorites.c',
2335 'xapp-glade-catalog.c',
2436 'xapp-gtk-window.c',
2537 'xapp-icon-chooser-button.c',
8294 )
8395
8496 libxapp = library('xapp',
85 sources : xapp_headers + xapp_sources + xapp_enums + dbus_headers,
97 sources : xapp_headers + xapp_sources + xapp_enums + dbus_headers + favorite_vfs_sources,
8698 include_directories: [top_inc],
8799 version: meson.project_version(),
88100 soversion: '1',
133145 metadata_dirs: meson.current_source_dir(),
134146 install: true
135147 )
148
149 gtk3_module = shared_module(
150 'xapp-gtk3-module', ['xapp-gtk3-module.c'],
151 include_directories: [top_inc],
152 dependencies: [gtk3_dep, libxapp_dep],
153 install: true,
154 install_dir: join_paths(gtk3_dep.get_pkgconfig_variable('libdir'),'gtk-3.0','modules')
155 )
0
1 #include <config.h>
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <utime.h>
12
13 #include <gtk/gtk.h>
14 #include <glib/gi18n-lib.h>
15 #include <gio/gdesktopappinfo.h>
16 #include <glib/gstdio.h>
17
18 #include "xapp-favorites.h"
19 #include "favorite-vfs-file.h"
20
21 #define FAVORITES_SCHEMA "org.x.apps.favorites"
22 #define FAVORITES_KEY "list"
23 #define SETTINGS_DELIMITER "::"
24 #define MAX_DISPLAY_URI_LENGTH 20
25
26 G_DEFINE_BOXED_TYPE (XAppFavoriteInfo, xapp_favorite_info, xapp_favorite_info_copy, xapp_favorite_info_free);
27 /**
28 * SECTION:xapp-favorites
29 * @Short_description: Keeps track of favorite files.
30 * @Title: XAppFavorites
31 *
32 * The XAppFavorites class allows applications display frequently-used files and
33 * provide a safe mechanism for launching them.
34 *
35 * A list of #XAppFavoriteInfos can be retrieved in full, or only for specific mimetypes.
36 *
37 * XAppFavorites are new for 2.0
38 */
39
40 /**
41 * xapp_favorite_info_copy:
42 * @info: The #XAppFavoriteInfo to duplicate.
43 *
44 * Makes an exact copy of an existing #XAppFavoriteInfo.
45 *
46 * Returns: (transfer full): a new #XAppFavoriteInfo. Free using #xapp_favorite_info_free.
47 *
48 * Since 2.0
49 */
50 XAppFavorites *global_favorites;
51
52 XAppFavoriteInfo *
53 xapp_favorite_info_copy (const XAppFavoriteInfo *info)
54 {
55 // g_debug ("XAppFavoriteInfo: copy");
56 g_return_val_if_fail (info != NULL, NULL);
57
58 XAppFavoriteInfo *_info = g_slice_dup (XAppFavoriteInfo, info);
59 _info->uri = g_strdup (info->uri);
60 _info->display_name = g_strdup (info->display_name);
61 _info->cached_mimetype = g_strdup (info->cached_mimetype);
62
63 return _info;
64 }
65
66 /**
67 * xapp_favorite_info_free:
68 * @info: The #XAppFavoriteInfo to free.
69 *
70 * Destroys the #XAppFavoriteInfo.
71 *
72 * Since 2.0
73 */
74 void
75 xapp_favorite_info_free (XAppFavoriteInfo *info)
76 {
77 g_debug ("XAppFavoriteInfo free (%s)", info->uri);
78 g_return_if_fail (info != NULL);
79
80 g_free (info->uri);
81 g_free (info->display_name);
82 g_free (info->cached_mimetype);
83 g_slice_free (XAppFavoriteInfo, info);
84 }
85
86 typedef struct
87 {
88 GHashTable *infos;
89 GHashTable *menus;
90
91 GSettings *settings;
92
93 gulong settings_listener_id;
94 guint changed_timer_id;
95 } XAppFavoritesPrivate;
96
97 struct _XAppFavorites
98 {
99 GObject parent_instance;
100 };
101
102 G_DEFINE_TYPE_WITH_PRIVATE (XAppFavorites, xapp_favorites, G_TYPE_OBJECT)
103
104 enum
105 {
106 CHANGED,
107 LAST_SIGNAL
108 };
109
110 static guint signals[LAST_SIGNAL] = {0, };
111
112 static void finish_add_favorite (XAppFavorites *favorites,
113 const gchar *uri,
114 const gchar *mimetype,
115 gboolean from_saved);
116
117 static gboolean
118 changed_callback (gpointer data)
119 {
120 g_return_val_if_fail (XAPP_IS_FAVORITES (data), G_SOURCE_REMOVE);
121 XAppFavorites *favorites = XAPP_FAVORITES (data);
122
123 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
124 g_debug ("XAppFavorites: list updated, emitting changed signal");
125
126 priv->changed_timer_id = 0;
127 g_signal_emit (favorites, signals[CHANGED], 0);
128
129 return G_SOURCE_REMOVE;
130 }
131
132 static void
133 queue_changed (XAppFavorites *favorites)
134 {
135 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
136
137 if (priv->changed_timer_id > 0)
138 {
139 g_source_remove (priv->changed_timer_id);
140 }
141
142 priv->changed_timer_id = g_idle_add ((GSourceFunc) changed_callback, favorites);
143 }
144
145 static void
146 sync_metadata_callback (GObject *source,
147 GAsyncResult *res,
148 gpointer user_data)
149 {
150 // Disabled
151 return;
152
153 // GFile *file;
154 // GError *error;
155
156 // file = G_FILE (source);
157 // error = NULL;
158
159 // if (!g_file_set_attributes_finish (file,
160 // res,
161 // NULL,
162 // &error))
163 // {
164 // if (error != NULL)
165 // {
166 // if (error->code != G_IO_ERROR_NOT_FOUND)
167 // {
168 // g_warning ("Could not update file metadata for favorite file '%s': %s", g_file_get_uri (file), error->message);
169 // }
170
171 // g_error_free (error);
172 // }
173 // }
174 // else
175 // {
176 // if (g_file_is_native (file))
177 // {
178 // // I can't think of any other way to touch a file so a file monitor might notice
179 // // the attribute change. It shouldn't be too much trouble since most times add/remove
180 // // will be done in the file manager (where the update can be triggered internally).
181
182 // gchar *local_path = g_file_get_path (file);
183 // g_utime (local_path, NULL);
184 // g_free (local_path);
185 // }
186 // }
187 }
188
189 static void
190 sync_file_metadata (XAppFavorites *favorites,
191 const gchar *uri,
192 gboolean is_favorite)
193 {
194 /* Disabled - this is less than optimal, and is implemented instead in
195 * nemo, currently. This could be changed later to help support other browsers.
196 * Also, this only works with local files. */
197 return;
198
199 /* borrowed from nemo-vfs-file.c */
200 GFileInfo *info;
201 GFile *file;
202
203 g_debug ("Sync metadata: %s - Favorite? %d", uri, is_favorite);
204
205 info = g_file_info_new ();
206
207 if (is_favorite) {
208 g_file_info_set_attribute_string (info, FAVORITE_METADATA_KEY, META_TRUE);
209 } else {
210 /* Unset the key */
211 g_file_info_set_attribute (info, FAVORITE_METADATA_KEY, G_FILE_ATTRIBUTE_TYPE_INVALID, NULL);
212 }
213
214 file = g_file_new_for_uri (uri);
215
216 g_file_set_attributes_async (file,
217 info,
218 0,
219 G_PRIORITY_DEFAULT,
220 NULL,
221 sync_metadata_callback,
222 favorites);
223
224 g_object_unref (file);
225 g_object_unref (info);
226 }
227
228 static void
229 store_favorites (XAppFavorites *favorites)
230 {
231 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
232 GList *iter, *keys;
233 GPtrArray *array;
234 gchar **new_settings;
235
236 array = g_ptr_array_new ();
237
238 keys = g_hash_table_get_keys (priv->infos);
239
240 for (iter = keys; iter != NULL; iter = iter->next)
241 {
242 XAppFavoriteInfo *info = (XAppFavoriteInfo *) g_hash_table_lookup (priv->infos, iter->data);
243 gchar *entry;
244
245 entry = g_strjoin (SETTINGS_DELIMITER,
246 info->uri,
247 info->cached_mimetype,
248 NULL);
249
250 g_ptr_array_add (array, entry);
251 }
252
253 g_ptr_array_add (array, NULL);
254
255 g_list_free (keys);
256
257 new_settings = (gchar **) g_ptr_array_free (array, FALSE);
258
259 g_signal_handler_block (priv->settings, priv->settings_listener_id);
260 g_settings_set_strv (priv->settings, FAVORITES_KEY, (const gchar* const*) new_settings);
261 g_signal_handler_unblock (priv->settings, priv->settings_listener_id);
262
263 g_debug ("XAppFavorites: store_favorites: favorites saved");
264
265 g_free (new_settings);
266 }
267
268 static void
269 load_favorites (XAppFavorites *favorites,
270 gboolean signal_changed)
271 {
272 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
273 gchar **raw_list;
274 gint i;
275
276 if (priv->infos != NULL)
277 {
278 g_hash_table_destroy (priv->infos);
279 }
280
281 priv->infos = g_hash_table_new_full (g_str_hash, g_str_equal,
282 g_free, (GDestroyNotify) xapp_favorite_info_free);
283
284 raw_list = g_settings_get_strv (priv->settings, FAVORITES_KEY);
285
286 if (!raw_list)
287 {
288 // no favorites
289 return;
290 }
291
292 for (i = 0; i < g_strv_length (raw_list); i++)
293 {
294 gchar **entry = g_strsplit (raw_list[i], SETTINGS_DELIMITER, 2);
295
296 finish_add_favorite (favorites,
297 entry[0], // uri
298 entry[1], // cached_mimetype
299 TRUE);
300
301 g_strfreev (entry);
302 }
303
304 g_strfreev (raw_list);
305
306 g_debug ("XAppFavorites: load_favorite: favorites loaded (%d)", i);
307
308 if (signal_changed)
309 {
310 queue_changed (favorites);
311 }
312 }
313
314 static void
315 rename_favorite (XAppFavorites *favorites,
316 const gchar *old_uri,
317 const gchar *new_uri)
318 {
319 XAppFavoriteInfo *info;
320 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
321 gchar *final_new_uri = NULL;
322
323 if (g_str_has_prefix (old_uri, ROOT_URI))
324 {
325 // Renaming occurred inside of favorites:/// we need to identify by
326 // display name.
327
328 const gchar *old_display_name = old_uri + strlen (ROOT_URI);
329 const gchar *new_display_name = new_uri + strlen (ROOT_URI);
330
331 info = xapp_favorites_find_by_display_name (favorites, old_display_name);
332
333 if (info)
334 {
335 GFile *real_file, *parent, *renamed_file;
336
337 real_file = g_file_new_for_uri (info->uri);
338 parent = g_file_get_parent (real_file);
339
340 renamed_file = g_file_get_child_for_display_name (parent,
341 new_display_name,
342 NULL);
343
344 if (renamed_file != NULL)
345 {
346 final_new_uri = g_file_get_uri (renamed_file);
347 }
348
349 g_object_unref (real_file);
350 g_object_unref (parent);
351 g_clear_object (&renamed_file);
352 }
353 }
354 else
355 {
356 info = g_hash_table_lookup (priv->infos, old_uri);
357 final_new_uri = g_strdup (new_uri);
358 }
359
360 if (info != NULL && final_new_uri != NULL)
361 {
362 gchar *mimetype = g_strdup (info->cached_mimetype);
363
364 sync_file_metadata (favorites, info->uri, FALSE);
365
366 g_hash_table_remove (priv->infos,
367 (gconstpointer) info->uri);
368
369 finish_add_favorite (favorites,
370 final_new_uri,
371 mimetype,
372 FALSE);
373
374 sync_file_metadata (favorites, final_new_uri, TRUE);
375
376 g_free (mimetype);
377 }
378
379 g_free (final_new_uri);
380 }
381
382 static void
383 remove_favorite (XAppFavorites *favorites,
384 const gchar *uri)
385 {
386 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
387 gchar *real_uri;
388
389 if (g_str_has_prefix (uri, "favorites"))
390 {
391 GFile *file = g_file_new_for_uri (uri);
392 real_uri = favorite_vfs_file_get_real_uri (file);
393
394 g_object_unref (file);
395 }
396 else
397 {
398 real_uri = g_strdup (uri);
399 }
400
401 g_debug ("XAppFavorites: remove favorite: %s", real_uri);
402
403 // It may be orphaned for some reason.. even if it's not in gsettings, still try
404 // to remove the favorite attribute.
405 sync_file_metadata (favorites, real_uri, FALSE);
406
407 if (!g_hash_table_remove (priv->infos, real_uri))
408 {
409 g_debug ("XAppFavorites: remove_favorite: could not find favorite for uri '%s'", real_uri);
410 g_free (real_uri);
411 return;
412 }
413
414 g_free (real_uri);
415
416 store_favorites (favorites);
417 queue_changed (favorites);
418 }
419
420 static void
421 deduplicate_display_names (XAppFavorites *favorites,
422 GHashTable *infos)
423 {
424 GList *fav_uris, *ptr;
425 GHashTable *lists_of_keys_by_basename = g_hash_table_new_full (g_str_hash, g_str_equal,
426 g_free, NULL);
427 GHashTableIter iter;
428
429 fav_uris = g_hash_table_get_keys (infos);
430
431 for (ptr = fav_uris; ptr != NULL; ptr = ptr->next)
432 {
433 GList *uris;
434 const gchar *uri = (gchar *) ptr->data;
435 gchar *original_display_name = g_path_get_basename (uri);
436
437 if (g_hash_table_contains (lists_of_keys_by_basename, original_display_name))
438 {
439 uris = g_hash_table_lookup (lists_of_keys_by_basename, original_display_name);
440
441 // this could be prepend, but then the value in the table would have to be replaced
442 uris = g_list_append ((GList *) uris, g_strdup (uri));
443 }
444 else
445 {
446 uris = g_list_prepend (NULL, g_strdup (uri));
447 g_hash_table_insert (lists_of_keys_by_basename,
448 g_strdup (original_display_name),
449 uris);
450 }
451
452 g_free (original_display_name);
453 }
454
455 g_list_free (fav_uris);
456
457 gpointer key, value;
458
459 g_hash_table_iter_init (&iter, lists_of_keys_by_basename);
460
461 while (g_hash_table_iter_next (&iter, &key, &value))
462 {
463 GList *same_names_list, *uri_ptr;
464 const gchar *common_display_name;
465
466 if (((GList *) value)->next == NULL)
467 {
468 // Single member of current common name list;
469 g_list_free_full ((GList *) value, g_free);
470 continue;
471 }
472 // Now we know we have a list of uris that would have identical display names
473 // Add a part of the uri after each to distinguish them.
474 common_display_name = (const gchar *) key;
475 same_names_list = (GList *) value;
476
477 for (uri_ptr = same_names_list; uri_ptr != NULL; uri_ptr = uri_ptr->next)
478 {
479 XAppFavoriteInfo *info;
480 GFile *uri_file, *home_file, *parent_file;
481 GString *new_display_string;
482 const gchar *current_uri;
483
484 current_uri = (const gchar *) uri_ptr->data;
485
486 uri_file = g_file_new_for_uri (current_uri);
487 parent_file = g_file_get_parent (uri_file);
488 home_file = g_file_new_for_path (g_get_home_dir());
489
490 new_display_string = g_string_new (common_display_name);
491 g_string_append (new_display_string, " (");
492
493 // How much effort should we put into duplicate naming? Keeping it
494 // simple like this won't work all the time.
495 gchar *parent_basename = g_file_get_basename (parent_file);
496 g_string_append (new_display_string, parent_basename);
497 g_free (parent_basename);
498
499 // TODO: ellipsized deduplication paths?
500
501 // if (g_file_has_prefix (parent_file, home_file))
502 // {
503 // gchar *home_rpath = g_file_get_relative_path (home_file, parent_file);
504 // gchar *home_basename = g_file_get_basename (home_file);
505
506 // if (strlen (home_rpath) < MAX_DISPLAY_URI_LENGTH)
507 // {
508 // g_string_append (new_display_string, home_basename);
509 // g_string_append (new_display_string, "/");
510 // g_string_append (new_display_string, home_rpath);
511 // }
512 // else
513 // {
514 // gchar *parent_basename = g_file_get_basename (parent_file);
515
516 // g_string_append (new_display_string, home_basename);
517 // g_string_append (new_display_string, "/.../");
518 // g_string_append (new_display_string, parent_basename);
519
520 // g_free (parent_basename);
521 // }
522
523 // g_free (home_rpath);
524 // g_free (home_basename);
525 // }
526 // else
527 // {
528 // GString *tmp_string = g_string_new (NULL);
529
530 // if (g_file_is_native (parent_file))
531 // {
532 // g_string_append (tmp_string, g_file_peek_path (parent_file));
533 // }
534 // else
535 // {
536 // g_string_append (tmp_string, current_uri);
537 // }
538
539 // if (tmp_string->len > MAX_DISPLAY_URI_LENGTH)
540 // {
541 // gint diff;
542 // gint replace_pos;
543
544 // diff = tmp_string->len - MAX_DISPLAY_URI_LENGTH;
545 // replace_pos = (tmp_string->len / 2) - (diff / 2) - 2;
546
547 // g_string_erase (tmp_string,
548 // replace_pos,
549 // diff);
550 // g_string_insert (tmp_string,
551 // replace_pos,
552 // "...");
553 // }
554
555 // g_string_append (new_display_string, tmp_string->str);
556 // g_string_free (tmp_string, TRUE);
557 // }
558
559 g_object_unref (uri_file);
560 g_object_unref (home_file);
561 g_object_unref (parent_file);
562
563 g_string_append (new_display_string, ")");
564
565 // Look up the info from our master table
566 info = g_hash_table_lookup (infos, current_uri);
567 g_free (info->display_name);
568
569 info->display_name = g_string_free (new_display_string, FALSE);
570 }
571
572 g_list_free_full (same_names_list, g_free);
573 }
574
575 // We freed the individual lists just above, only the keys will need
576 // freed here.
577 g_hash_table_destroy (lists_of_keys_by_basename);
578 }
579
580 static void
581 finish_add_favorite (XAppFavorites *favorites,
582 const gchar *uri,
583 const gchar *cached_mimetype,
584 gboolean from_saved)
585 {
586 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
587 XAppFavoriteInfo *info;
588 gchar *unescaped_uri;
589
590 // Check if it's there again, in case it was added while we were getting mimetype.
591 if (g_hash_table_contains (priv->infos, uri))
592 {
593 g_debug ("XAppFavorites: favorite for '%s' exists, ignoring", uri);
594 return;
595 }
596
597 info = g_slice_new0 (XAppFavoriteInfo);
598 info->uri = g_strdup (uri);
599
600 unescaped_uri = g_uri_unescape_string (uri, NULL);
601 info->display_name = g_path_get_basename (unescaped_uri);
602 g_free (unescaped_uri);
603
604 info->cached_mimetype = g_strdup (cached_mimetype);
605
606 g_hash_table_insert (priv->infos, (gpointer) g_strdup (uri), (gpointer) info);
607
608 g_debug ("XAppFavorites: added favorite: %s", uri);
609
610 deduplicate_display_names (favorites, priv->infos);
611
612 if (from_saved)
613 {
614 return;
615 }
616
617 store_favorites (favorites);
618 queue_changed (favorites);
619 }
620
621 static void
622 on_content_type_info_received (GObject *source,
623 GAsyncResult *res,
624 gpointer user_data)
625 {
626 XAppFavorites *favorites = XAPP_FAVORITES (user_data);
627 GFile *file;
628 GFileInfo *file_info;
629 GError *error;
630 gchar *cached_mimetype, *uri;
631
632 file = G_FILE (source);
633 uri = g_file_get_uri (file);
634 error = NULL;
635 cached_mimetype = NULL;
636
637 file_info = g_file_query_info_finish (file, res, &error);
638
639 if (error)
640 {
641 g_debug ("XAppFavorites: problem trying to figure out content type for uri '%s': %s",
642 uri, error->message);
643 g_error_free (error);
644 }
645
646 if (file_info)
647 {
648 cached_mimetype = g_strdup (g_file_info_get_content_type (file_info));
649
650 if (cached_mimetype == NULL)
651 {
652 cached_mimetype = g_strdup ("application/unknown");
653 }
654
655 finish_add_favorite (favorites,
656 uri,
657 cached_mimetype,
658 FALSE);
659
660 sync_file_metadata (favorites, uri, TRUE);
661 }
662
663 g_free (uri);
664 g_free (cached_mimetype);
665 g_clear_object (&file_info);
666 g_object_unref (file);
667 }
668
669 static void
670 add_favorite (XAppFavorites *favorites,
671 const gchar *uri)
672 {
673 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
674 GFile *file;
675
676 if (g_hash_table_contains (priv->infos, uri))
677 {
678 g_debug ("XAppFavorites: favorite for '%s' exists, ignoring", uri);
679 return;
680 }
681
682 file = g_file_new_for_uri (uri);
683
684 g_file_query_info_async (file,
685 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
686 G_FILE_QUERY_INFO_NONE,
687 G_PRIORITY_LOW,
688 NULL,
689 on_content_type_info_received,
690 favorites);
691 }
692
693 static void
694 on_settings_list_changed (GSettings *settings,
695 gchar *key,
696 gpointer user_data)
697 {
698 XAppFavorites *favorites = XAPP_FAVORITES (user_data);
699
700 load_favorites (favorites, TRUE);
701 }
702
703 static void
704 xapp_favorites_init (XAppFavorites *favorites)
705 {
706 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
707
708 g_debug ("XAppFavorites: init:");
709
710 priv->settings = g_settings_new (FAVORITES_SCHEMA);
711 priv->settings_listener_id = g_signal_connect (priv->settings,
712 "changed::" FAVORITES_KEY,
713 G_CALLBACK (on_settings_list_changed),
714 favorites);
715
716 load_favorites (favorites, FALSE);
717 }
718
719 static void
720 xapp_favorites_dispose (GObject *object)
721 {
722 XAppFavorites *favorites = XAPP_FAVORITES (object);
723 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
724
725 g_debug ("XAppFavorites dispose (%p)", object);
726
727 g_clear_object (&priv->settings);
728 g_clear_pointer (&priv->infos, g_hash_table_destroy);
729
730 G_OBJECT_CLASS (xapp_favorites_parent_class)->dispose (object);
731 }
732
733 static void
734 xapp_favorites_finalize (GObject *object)
735 {
736 g_debug ("XAppFavorites finalize (%p)", object);
737
738 G_OBJECT_CLASS (xapp_favorites_parent_class)->finalize (object);
739 }
740
741 static void
742 xapp_favorites_class_init (XAppFavoritesClass *klass)
743 {
744 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
745
746 gobject_class->dispose = xapp_favorites_dispose;
747 gobject_class->finalize = xapp_favorites_finalize;
748
749 /**
750 * XAppFavorites::changed:
751
752 * Notifies when the favorites list has changed.
753 */
754 signals [CHANGED] =
755 g_signal_new ("changed",
756 XAPP_TYPE_FAVORITES,
757 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
758 0,
759 NULL, NULL, NULL,
760 G_TYPE_NONE, 0);
761 }
762
763 /**
764 * xapp_favorites_get_default:
765 *
766 * Returns the #XAppFavorites instance.
767 *
768 * Returns: (transfer none): the XAppFavorites instance for the process. Do not free.
769 *
770 * Since: 2.0
771 */
772 XAppFavorites *
773 xapp_favorites_get_default (void)
774 {
775 if (global_favorites == NULL)
776 {
777 global_favorites = g_object_new (XAPP_TYPE_FAVORITES, NULL);
778 }
779
780 return global_favorites;
781 }
782
783 typedef struct {
784 GList *items;
785 const gchar **mimetypes;
786 } MatchData;
787
788 void
789 match_mimetypes (gpointer key,
790 gpointer value,
791 gpointer user_data)
792 {
793 MatchData *data = (MatchData *) user_data;
794 const XAppFavoriteInfo *info = (XAppFavoriteInfo *) value;
795
796 if (data->mimetypes == NULL)
797 {
798 data->items = g_list_prepend (data->items, xapp_favorite_info_copy (info));
799 return;
800 }
801
802 gint i;
803
804 for (i = 0; i < g_strv_length ((gchar **) data->mimetypes); i++)
805 {
806 if (g_content_type_is_mime_type (info->cached_mimetype, data->mimetypes[i]))
807 {
808 data->items = g_list_prepend (data->items, xapp_favorite_info_copy (info));
809 return;
810 }
811 }
812 }
813
814 /**
815 * xapp_favorites_get_favorites:
816 * @favorites: The #XAppFavorites
817 * @mimetypes: (nullable): The mimetypes to filter by for results
818 *
819 * Gets a list of all favorites. If mimetype is not %NULL, the list will
820 * contain only favorites with that mimetype.
821 *
822 * Returns: (element-type XAppFavoriteInfo) (transfer full): a list of #XAppFavoriteInfos.
823 Free the list with #g_list_free, free elements with #xapp_favorite_info_free.
824 *
825 * Since: 2.0
826 */
827 GList *
828 xapp_favorites_get_favorites (XAppFavorites *favorites,
829 const gchar **mimetypes)
830 {
831 g_return_val_if_fail (XAPP_IS_FAVORITES (favorites), NULL);
832 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
833 GList *ret = NULL;
834 MatchData data;
835
836 data.items = NULL;
837 data.mimetypes = mimetypes;
838 g_hash_table_foreach (priv->infos,
839 (GHFunc) match_mimetypes,
840 &data);
841
842 ret = g_list_reverse (data.items);
843
844 gchar *typestring = mimetypes ? g_strjoinv (", ", (gchar **) mimetypes) : NULL;
845 g_debug ("XAppFavorites: get_favorites returning list for mimetype '%s' (%d items)",
846 typestring, g_list_length (ret));
847 g_free (typestring);
848
849 return ret;
850 }
851
852 /**
853 * xapp_favorites_get_n_favorites:
854 * @favorites: The #XAppFavorites
855 *
856 * Returns: The number of favorite files
857
858 * Since: 2.0
859 */
860 gint
861 xapp_favorites_get_n_favorites (XAppFavorites *favorites)
862 {
863 g_return_val_if_fail (XAPP_IS_FAVORITES (favorites), 0);
864 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
865 gint n;
866
867 n = g_hash_table_size (priv->infos);
868
869 g_debug ("XAppFavorites: get_n_favorites returning number of items: %d.", n);
870
871 return n;
872 }
873
874 static gboolean
875 lookup_display_name (gpointer key,
876 gpointer value,
877 gpointer user_data)
878 {
879 XAppFavoriteInfo *info = (XAppFavoriteInfo *) value;
880
881 if (g_strcmp0 (info->display_name, (const gchar *) user_data) == 0)
882 {
883 return TRUE;
884 }
885
886 return FALSE;
887 }
888
889 /**
890 * xapp_favorites_find_by_display_name:
891 * @favorites: The #XAppFavorites
892 * @display_name: (not nullable): The display name to lookup info for.
893 *
894 * Looks for an XAppFavoriteInfo that corresponds to @display_name.
895 *
896 * Returns: (transfer none): an XAppFavoriteInfo or NULL if one was not found. This is owned
897 * by the favorites manager and should not be freed.
898 *
899 * Since: 2.0
900 */
901 XAppFavoriteInfo *
902 xapp_favorites_find_by_display_name (XAppFavorites *favorites,
903 const gchar *display_name)
904 {
905 g_return_val_if_fail (XAPP_IS_FAVORITES (favorites), NULL);
906 g_return_val_if_fail (display_name != NULL, NULL);
907
908 XAppFavoriteInfo *info;
909 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
910
911 info = g_hash_table_find (priv->infos,
912 (GHRFunc) lookup_display_name,
913 (gpointer) display_name);
914
915 if (info != NULL)
916 {
917 return info;
918 }
919
920 return NULL;
921 }
922
923 /**
924 * xapp_favorites_find_by_uri:
925 * @favorites: The #XAppFavorites
926 * @uri: (not nullable): The uri to lookup info for.
927 *
928 * Looks for an XAppFavoriteInfo that corresponds to @uri.
929 *
930 * Returns: (transfer none): an XAppFavoriteInfo or NULL if one was not found. This is owned
931 * by the favorites manager and should not be freed.
932 *
933 * Since: 2.0
934 */
935 XAppFavoriteInfo *
936 xapp_favorites_find_by_uri (XAppFavorites *favorites,
937 const gchar *uri)
938 {
939 g_return_val_if_fail (XAPP_IS_FAVORITES (favorites), NULL);
940 g_return_val_if_fail (uri != NULL, NULL);
941
942 XAppFavoriteInfo *info;
943 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
944
945 info = g_hash_table_lookup (priv->infos, uri);
946
947 if (info != NULL)
948 {
949 return (XAppFavoriteInfo *) info;
950 }
951
952 return NULL;
953 }
954
955 /**
956 * xapp_favorites_add:
957 * @favorites: The #XAppFavorites
958 * @uri: The uri the favorite is for
959 *
960 * Adds a new favorite. If the uri already exists, this does nothing.
961 *
962 * Since: 2.0
963 */
964 void
965 xapp_favorites_add (XAppFavorites *favorites,
966 const gchar *uri)
967 {
968 g_return_if_fail (XAPP_IS_FAVORITES (favorites));
969 g_return_if_fail (uri != NULL);
970
971 add_favorite (favorites, uri);
972 }
973
974 /**
975 * xapp_favorites_remove:
976 * @favorites: The #XAppFavorites
977 * @uri: The uri for the favorite being removed
978 *
979 * Removes a favorite from the list.
980 *
981 * Since: 2.0
982 */
983 void
984 xapp_favorites_remove (XAppFavorites *favorites,
985 const gchar *uri)
986 {
987 g_return_if_fail (XAPP_IS_FAVORITES (favorites));
988 g_return_if_fail (uri != NULL);
989
990 remove_favorite (favorites, uri);
991 }
992
993 static void
994 launch_uri_callback (GObject *source,
995 GAsyncResult *res,
996 gpointer user_data)
997 {
998 gchar *uri = (gchar *) user_data;
999 GError *error;
1000
1001 error = NULL;
1002
1003 if (!g_app_info_launch_default_for_uri_finish (res, &error))
1004 {
1005 if (error)
1006 {
1007 g_debug ("XAppFavorites: launch: error opening uri '%s': %s", uri, error->message);
1008 g_error_free (error);
1009 }
1010 }
1011
1012 g_free (uri);
1013 }
1014
1015 /**
1016 * xapp_favorites_launch:
1017 * @favorites: The #XAppFavorites
1018 * @uri: The uri for the favorite to launch
1019 * @timestamp: The timestamp from an event or 0
1020 *
1021 * Opens a favorite in its default app.
1022 *
1023 * Since: 2.0
1024 */
1025 void
1026 xapp_favorites_launch (XAppFavorites *favorites,
1027 const gchar *uri,
1028 guint32 timestamp)
1029 {
1030 GdkDisplay *display;
1031 GdkAppLaunchContext *launch_context;
1032
1033 display = gdk_display_get_default ();
1034 launch_context = gdk_display_get_app_launch_context (display);
1035 gdk_app_launch_context_set_timestamp (launch_context, timestamp);
1036
1037 g_app_info_launch_default_for_uri_async (uri,
1038 G_APP_LAUNCH_CONTEXT (launch_context),
1039 NULL,
1040 launch_uri_callback,
1041 g_strdup (uri));
1042
1043 g_object_unref (launch_context);
1044 }
1045
1046 /**
1047 * xapp_favorites_rename:
1048 * @old_uri: the old favorite's uri.
1049 * @new_uri: The new uri.
1050 *
1051 * Removes old_uri and adds new_uri. This is mainly for file managers to use as
1052 * a convenience instead of add/remove, and guarantees the result, without having to
1053 * worry about multiple dbus calls (gsettings).
1054 *
1055 * Since: 2.0
1056 */
1057 void
1058 xapp_favorites_rename (XAppFavorites *favorites,
1059 const gchar *old_uri,
1060 const gchar *new_uri)
1061 {
1062 g_return_if_fail (XAPP_IS_FAVORITES (favorites));
1063 g_return_if_fail (old_uri != NULL && new_uri != NULL);
1064
1065 rename_favorite (favorites, old_uri, new_uri);
1066 }
1067
1068 typedef struct {
1069 XAppFavorites *favorites;
1070 guint update_id;
1071
1072 GDestroyNotify destroy_func;
1073 gpointer user_data;
1074 } DestroyData;
1075
1076 typedef struct {
1077 XAppFavorites *favorites;
1078 XAppFavoritesItemSelectedCallback callback;
1079 gchar *uri;
1080 gpointer user_data;
1081 } ItemCallbackData;
1082
1083 static void
1084 cb_data_destroy_notify (gpointer callback_data,
1085 GObject *object)
1086 {
1087 DestroyData *dd = (DestroyData *) callback_data;
1088
1089 if (dd->update_id > 0)
1090 {
1091 g_signal_handler_disconnect (dd->favorites, dd->update_id);
1092 }
1093
1094 dd->destroy_func (dd->user_data);
1095
1096 g_slice_free (DestroyData, dd);
1097 }
1098
1099 static void
1100 free_item_callback_data (gpointer callback_data,
1101 GClosure *closure)
1102 {
1103 ItemCallbackData *data = (ItemCallbackData *) callback_data;
1104 g_free (data->uri);
1105 g_slice_free (ItemCallbackData, data);
1106 }
1107
1108 static void
1109 item_activated (GObject *item,
1110 gpointer user_data)
1111 {
1112 ItemCallbackData *data = (ItemCallbackData *) user_data;
1113
1114 data->callback (data->favorites,
1115 data->uri,
1116 data->user_data);
1117 }
1118
1119 static void
1120 remove_menu_item (GtkWidget *item,
1121 gpointer user_data)
1122 {
1123 gtk_container_remove (GTK_CONTAINER (user_data), item);
1124 }
1125
1126 static void
1127 populate_menu (XAppFavorites *favorites,
1128 GtkMenu *menu)
1129 {
1130 GList *fav_list, *ptr;
1131 GtkWidget *item;
1132 XAppFavoritesItemSelectedCallback callback;
1133 gpointer user_data;
1134 const gchar **mimetypes;
1135
1136 gtk_container_foreach (GTK_CONTAINER (menu), (GtkCallback) remove_menu_item, menu);
1137
1138 mimetypes = (const gchar **) g_object_get_data (G_OBJECT (menu), "mimetypes");
1139 callback = g_object_get_data (G_OBJECT (menu), "activate-cb");
1140 user_data = g_object_get_data (G_OBJECT (menu), "user-data");
1141
1142 fav_list = xapp_favorites_get_favorites (favorites, mimetypes);
1143
1144 if (fav_list == NULL)
1145 {
1146 return;
1147 }
1148
1149 for (ptr = fav_list; ptr != NULL; ptr = ptr->next)
1150 {
1151 XAppFavoriteInfo *info = (XAppFavoriteInfo *) ptr->data;
1152 ItemCallbackData *data;
1153
1154 if (mimetypes != NULL)
1155 {
1156 item = gtk_menu_item_new_with_label (info->display_name);
1157 }
1158 else
1159 {
1160 GtkWidget *image;
1161 GIcon *icon;
1162
1163 icon = g_content_type_get_symbolic_icon (info->cached_mimetype);
1164 image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_MENU);
1165 g_object_unref (icon);
1166
1167 item = gtk_image_menu_item_new_with_label (info->display_name);
1168 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1169 }
1170
1171 data = g_slice_new0 (ItemCallbackData);
1172 data->favorites = favorites;
1173 data->uri = g_strdup (info->uri);
1174 data->callback = callback;
1175 data->user_data = user_data;
1176
1177 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1178
1179 g_signal_connect_data (item,
1180 "activate", G_CALLBACK (item_activated),
1181 data, (GClosureNotify) free_item_callback_data, 0);
1182 }
1183
1184 gtk_widget_show_all (GTK_WIDGET (menu));
1185 }
1186
1187 static void
1188 refresh_menu_items (XAppFavorites *favorites,
1189 gpointer user_data)
1190 {
1191 g_return_if_fail (XAPP_IS_FAVORITES (favorites));
1192 g_return_if_fail (GTK_IS_MENU (user_data));
1193
1194 GtkMenu *menu = GTK_MENU (user_data);
1195
1196 populate_menu (favorites, menu);
1197 }
1198
1199 /**
1200 * xapp_favorites_create_menu:
1201 * @favorites: The #XAppFavorites instance.
1202 * @mimetypes: (nullable): The mimetypes to filter for, or NULL to include all favorites.
1203 * @callback: (scope notified): (closure user_data): The callback to use when a menu item has been selected.
1204 * @user_data: (closure): The data to pass to the callback
1205 * @func: Destroy function for user_data
1206 *
1207 * Generates a GtkMenu widget populated with favorites. The callback will be called when
1208 * a menu item has been activated, and will include the uri of the respective item.
1209 *
1210 * Returns: (transfer full): a new #GtkMenu populated with a list of favorites, or NULL
1211 if there are no favorites.
1212 *
1213 * Since: 2.0
1214 */
1215 GtkWidget *
1216 xapp_favorites_create_menu (XAppFavorites *favorites,
1217 const gchar **mimetypes,
1218 XAppFavoritesItemSelectedCallback callback,
1219 gpointer user_data,
1220 GDestroyNotify func)
1221 {
1222 g_return_val_if_fail (XAPP_IS_FAVORITES (favorites), NULL);
1223 GtkWidget *menu;
1224
1225 menu = gtk_menu_new ();
1226
1227 g_object_set_data_full (G_OBJECT (menu),
1228 "mimetype", g_strdupv ((gchar **) mimetypes),
1229 (GDestroyNotify) g_strfreev);
1230
1231 g_object_set_data (G_OBJECT (menu),
1232 "activate-cb", callback);
1233
1234 g_object_set_data (G_OBJECT (menu),
1235 "user-data", user_data);
1236
1237 populate_menu (favorites, GTK_MENU (menu));
1238
1239 DestroyData *dd = g_slice_new0 (DestroyData);
1240 dd->destroy_func = func;
1241 dd->user_data = user_data;
1242 dd->favorites = favorites;
1243 dd->update_id = g_signal_connect (favorites,
1244 "changed",
1245 G_CALLBACK (refresh_menu_items),
1246 menu);
1247
1248 g_object_weak_ref (G_OBJECT (menu), (GWeakNotify) cb_data_destroy_notify, dd);
1249
1250 return menu;
1251 }
1252
1253 static GList *
1254 populate_action_list (XAppFavorites *favorites,
1255 const gchar **mimetypes)
1256 {
1257 GList *fav_list, *ptr;
1258 GList *actions;
1259 GtkAction *action;
1260 gint i;
1261
1262 fav_list = xapp_favorites_get_favorites (favorites, mimetypes);
1263
1264 if (fav_list == NULL)
1265 {
1266 return NULL;
1267 }
1268
1269 actions = NULL;
1270
1271 for (ptr = fav_list, i = 0; ptr != NULL; ptr = ptr->next, i++)
1272 {
1273 XAppFavoriteInfo *info = (XAppFavoriteInfo *) ptr->data;
1274
1275 if (mimetypes != NULL)
1276 {
1277 action = g_object_new (GTK_TYPE_ACTION,
1278 "name", info->uri,
1279 "label", info->display_name,
1280 NULL);
1281 }
1282 else
1283 {
1284 GIcon *icon;
1285 icon = g_content_type_get_symbolic_icon (info->cached_mimetype);
1286
1287 action = g_object_new (GTK_TYPE_ACTION,
1288 "name", info->uri,
1289 "label", info->display_name,
1290 "gicon", icon,
1291 NULL);
1292
1293 g_free (icon);
1294 }
1295
1296 actions = g_list_prepend (actions, action);
1297 }
1298
1299 actions = g_list_reverse (actions);
1300
1301 return actions;
1302 }
1303
1304 /**
1305 * xapp_favorites_create_actions:
1306 * @favorites: The #XAppFavorites instance.
1307 * @mimetypes: (nullable): The mimetypes to filter for, or NULL to include all favorites.
1308 *
1309 * Generates a list of favorite GtkActions.
1310 *
1311 * Returns: (element-type Gtk.Action) (transfer full): a new #GtkActionGroup populated with a list of favorites, or NULL
1312 if there are no favorites.
1313
1314 * Since: 2.0
1315 */
1316 GList *
1317 xapp_favorites_create_actions (XAppFavorites *favorites,
1318 const gchar **mimetypes)
1319 {
1320 g_return_val_if_fail (XAPP_IS_FAVORITES (favorites), NULL);
1321 GList *actions;
1322
1323 actions = populate_action_list (favorites,
1324 mimetypes);
1325
1326 return actions;
1327 }
1328
1329 /* Used by favorite_vfs_file */
1330 GList *
1331 _xapp_favorites_get_display_names (XAppFavorites *favorites)
1332 {
1333 g_return_val_if_fail (XAPP_IS_FAVORITES (favorites), NULL);
1334 XAppFavoritesPrivate *priv = xapp_favorites_get_instance_private (favorites);
1335 GHashTableIter iter;
1336 GList *ret;
1337 gpointer key, value;
1338
1339 ret = NULL;
1340 g_hash_table_iter_init (&iter, priv->infos);
1341
1342 while (g_hash_table_iter_next (&iter, &key, &value))
1343 {
1344 XAppFavoriteInfo *info = (XAppFavoriteInfo *) value;
1345 ret = g_list_prepend (ret, info->display_name);
1346 }
1347
1348 ret = g_list_reverse (ret);
1349 return ret;
1350 }
1351
0 #ifndef __XAPP_FAVORITES_H__
1 #define __XAPP_FAVORITES_H__
2
3 #include <stdio.h>
4 #include <gtk/gtk.h>
5
6 #include <glib-object.h>
7
8 G_BEGIN_DECLS
9
10 #define XAPP_TYPE_FAVORITE_INFO (xapp_favorite_info_get_type ())
11 typedef struct _XAppFavoriteInfo XAppFavoriteInfo;
12
13 #define XAPP_TYPE_FAVORITES (xapp_favorites_get_type ())
14
15 G_DECLARE_FINAL_TYPE (XAppFavorites, xapp_favorites, XAPP, FAVORITES, GObject)
16
17 XAppFavorites *xapp_favorites_get_default (void);
18 GList *xapp_favorites_get_favorites (XAppFavorites *favorites,
19 const gchar **mimetypes);
20 gint xapp_favorites_get_n_favorites (XAppFavorites *favorites);
21 XAppFavoriteInfo *xapp_favorites_find_by_display_name (XAppFavorites *favorites,
22 const gchar *display_name);
23 XAppFavoriteInfo *xapp_favorites_find_by_uri (XAppFavorites *favorites,
24 const gchar *uri);
25 void xapp_favorites_add (XAppFavorites *favorites,
26 const gchar *uri);
27 void xapp_favorites_remove (XAppFavorites *favorites,
28 const gchar *uri);
29 void xapp_favorites_launch (XAppFavorites *favorites,
30 const gchar *uri,
31 guint32 timestamp);
32 void xapp_favorites_rename (XAppFavorites *favorites,
33 const gchar *old_uri,
34 const gchar *new_uri);
35
36 /**
37 * XAppFavoriteInfo:
38 * @uri: The uri to the favorite file.
39 * @display_name: The name for use when displaying the item. This may not exactly match
40 * the filename if there are files with the same name but in different folders.
41 * @cached_mimetype: The mimetype calculated for the uri when it was added to favorites.
42 *
43 * Information related to a single favorite file.
44 */
45 struct _XAppFavoriteInfo
46 {
47 gchar *uri;
48 gchar *display_name;
49 gchar *cached_mimetype;
50 };
51
52 GType xapp_favorite_info_get_type (void) G_GNUC_CONST;
53 XAppFavoriteInfo *xapp_favorite_info_copy (const XAppFavoriteInfo *info);
54 void xapp_favorite_info_free (XAppFavoriteInfo *info);
55
56
57 /* XAppFavoritesMenu */
58
59 /**
60 * FavoritesItemSelectedCallback
61 * @favorites: the #XAppFavorites instance
62 * @uri: the uri of the favorite that was selected
63 * @user_data: Callback data
64 *
65 * Callback when an item has been selected from the favorites
66 * #GtkMenu.
67 */
68 typedef void (* XAppFavoritesItemSelectedCallback) (XAppFavorites *favorites,
69 const gchar *uri,
70 gpointer user_data);
71
72 GtkWidget *xapp_favorites_create_menu (XAppFavorites *favorites,
73 const gchar **mimetypes,
74 XAppFavoritesItemSelectedCallback callback,
75 gpointer user_data,
76 GDestroyNotify func);
77 GList *xapp_favorites_create_actions (XAppFavorites *favorites,
78 const gchar **mimetypes);
79
80 G_END_DECLS
81
82 #endif /* __XAPP_FAVORITES_H__ */
0 #include <config.h>
1 #include <gtk/gtk.h>
2
3 #include "xapp-favorites.h"
4 #include "favorite-vfs-file.h"
5
6 /* Gtk module justification:
7 *
8 * The sole purpose of this module currently is to add a 'Favorites'
9 * shortcut to GtkFileChooser dialogs.
10 *
11 * In gtk_module_init, the XAppFavorites singleton is initialized, and
12 * the 'favorites' uri scheme is added to the default vfs. Ordinarily
13 * non-file:// schemes aren't supported in these dialogs unless their
14 * 'local-only' property is set to FALSE. Since favorites are shortcuts
15 * to locally-available files, we lie to the chooser setup by returning
16 * "/" instead of NULL when g_file_get_path ("favorites:///") is called.
17 */
18
19
20 /* Make sure GCC doesn't warn us about a missing prototype for this
21 * exported function */
22 void gtk_module_init (gint *argc, gchar ***argv[]);
23
24 static gboolean
25 selection_changed_cb (GSignalInvocationHint *ihint,
26 guint n_param_values,
27 const GValue *param_values,
28 gpointer data)
29 {
30 GtkFileChooser *chooser = GTK_FILE_CHOOSER (g_value_get_object (&param_values[0]));
31 GSList *list, *i;
32 gboolean already_applied = FALSE;
33 list = gtk_file_chooser_list_shortcut_folder_uris (chooser);
34
35 for (i = list; i != NULL; i = i->next)
36 {
37 if (g_strcmp0 ((gchar *) i->data, "favorites:///") == 0)
38 {
39 already_applied = TRUE;
40 break;
41 }
42 }
43
44 g_slist_free_full (list, g_free);
45
46 if (!already_applied)
47 {
48 xapp_favorites_get_default ();
49 gtk_file_chooser_add_shortcut_folder_uri (chooser, "favorites:///", NULL);
50 }
51
52 return TRUE;
53 }
54
55 static void
56 add_chooser_hook (GType type)
57 {
58 GTypeClass *type_class;
59 guint sigid;
60
61 type_class = g_type_class_ref (type);
62
63 sigid = g_signal_lookup ("selection-changed", type);
64 g_signal_add_emission_hook (sigid, 0, selection_changed_cb, NULL, NULL);
65
66 g_type_class_unref (type_class);
67 }
68
69 G_MODULE_EXPORT void gtk_module_init (gint *argc, gchar ***argv[]) {
70 // This won't instantiate XAppFavorites but will register the uri so
71 // it can be used by apps (like pix which doesn't use the favorites api,
72 // but just adds favorites:/// to its sidebar.)
73 init_favorite_vfs ();
74
75 add_chooser_hook (GTK_TYPE_FILE_CHOOSER_WIDGET);
76 add_chooser_hook (GTK_TYPE_FILE_CHOOSER_DIALOG);
77 add_chooser_hook (GTK_TYPE_FILE_CHOOSER_BUTTON);
78 }
79
80 G_MODULE_EXPORT gchar* g_module_check_init (GModule *module);
81
82 G_MODULE_EXPORT gchar* g_module_check_init (GModule *module) {
83 g_module_make_resident(module);
84 return NULL;
85 }
1616 #include "xapp-status-icon-monitor.h"
1717 #include "xapp-statusicon-interface.h"
1818
19 #define MONITOR_PATH "/org/x/StatusIconMonitor"
2019 #define MONITOR_NAME "org.x.StatusIconMonitor"
2120
2221 #define STATUS_ICON_MATCH "org.x.StatusIcon."
23
24 #define STATUS_ICON_ID_FORMAT "org.x.StatusIcon.PID-%d-%d"
25 #define STATUS_ICON_PATH_PREFIX "/org/x/StatusIcon/"
26
27 #define STATUS_NOTIFIER_WATCHER_NAME "org.x.StatusNotifierWatcher"
22 #define STATUS_ICON_INTERFACE "org.x.StatusIcon"
23
24 #define STATUS_ICON_PATH "/org/x/StatusIcon"
25 #define STATUS_ICON_PATH_PREFIX STATUS_ICON_PATH "/"
26
2827 #define WATCHER_MAX_RESTARTS 2
2928
3029 enum
5857 {
5958 GDBusConnection *connection;
6059
61 GHashTable *icons;
62 gchar *name;
60 GHashTable *object_managers;
6361
6462 guint owner_id;
6563 guint listener_id;
66 guint sn_watcher_id;
67
68 guint sn_watcher_retry_count;
69
7064 } XAppStatusIconMonitorPrivate;
7165
7266 struct _XAppStatusIconMonitor
7670
7771 G_DEFINE_TYPE_WITH_PRIVATE (XAppStatusIconMonitor, xapp_status_icon_monitor, G_TYPE_OBJECT)
7872
79 static void remove_icon (XAppStatusIconMonitor *self, const gchar *name);
80
81 static void
82 on_proxy_name_owner_changed (GObject *object,
83 GParamSpec *pspec,
84 gpointer user_data)
85 {
86 XAppStatusIconMonitor *self = XAPP_STATUS_ICON_MONITOR (user_data);
87
88 gchar *name_owner = NULL;
89 gchar *proxy_name = NULL;
73 static void
74 on_object_manager_name_owner_changed (GObject *object,
75 GParamSpec *pspec,
76 gpointer user_data)
77 {
78 XAppStatusIconMonitor *self = XAPP_STATUS_ICON_MONITOR (user_data);
79 XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self);
80 gchar *name, *owner;
9081
9182 g_object_get (object,
92 "g-name-owner", &name_owner,
93 "g-name", &proxy_name,
83 "name-owner", &owner,
84 "name", &name,
9485 NULL);
9586
96 g_debug("XAppStatusIconMonitor: proxy name owner changed - name owner '%s' is now '%s')", proxy_name, name_owner);
97
98 if (name_owner == NULL)
99 {
100 remove_icon (self, proxy_name);
101 }
102
103 g_free (name_owner);
104 g_free (proxy_name);
105 }
106
107 static void
108 remove_icon (XAppStatusIconMonitor *self,
109 const gchar *name)
110 {
111 XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self);
112 XAppStatusIconInterface *proxy;
113
114 proxy = g_hash_table_lookup (priv->icons, name);
115
116 if (proxy)
117 {
118 g_object_ref (proxy);
119
120 g_signal_handlers_disconnect_by_func (proxy,
121 on_proxy_name_owner_changed,
122 self);
123
124 if (g_hash_table_remove (priv->icons, name))
125 {
126 g_debug("XAppStatusIconMonitor: removing icon: '%s'", name);
127
128 g_signal_emit (self, signals[ICON_REMOVED], 0, proxy);
129 }
130 else
131 {
132 g_assert_not_reached ();
133 }
134
135 g_object_unref (proxy);
136 }
137 }
138
139 static void
140 new_status_icon_proxy_complete (GObject *object,
141 GAsyncResult *res,
142 gpointer user_data)
143 {
144 XAppStatusIconMonitor *self = XAPP_STATUS_ICON_MONITOR (user_data);
145 XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self);
146 XAppStatusIconInterface *proxy;
87 g_debug("XAppStatusIconMonitor: app name owner changed - name '%s' is now %s)",
88 name, owner != NULL ? "owned" : "unowned");
89
90 if (owner == NULL)
91 {
92 g_hash_table_remove (priv->object_managers, name);
93 }
94
95 g_free (owner);
96 g_free (name);
97 }
98
99 static void
100 object_manager_object_added (XAppObjectManagerClient *manager,
101 GDBusObject *object,
102 gpointer user_data)
103 {
104 XAppStatusIconMonitor *self = XAPP_STATUS_ICON_MONITOR (user_data);
105 GDBusInterface *proxy;
106
107 proxy = g_dbus_object_get_interface (object, STATUS_ICON_INTERFACE);
108
109 g_signal_emit (self, signals[ICON_ADDED], 0, XAPP_STATUS_ICON_INTERFACE_PROXY (proxy));
110
111 g_object_unref (proxy);
112 }
113
114 static void
115 object_manager_object_removed (XAppObjectManagerClient *manager,
116 GDBusObject *object,
117 gpointer user_data)
118 {
119 XAppStatusIconMonitor *self = XAPP_STATUS_ICON_MONITOR (user_data);
120 GDBusInterface *proxy;
121
122 proxy = g_dbus_object_get_interface (object, STATUS_ICON_INTERFACE);
123
124 g_signal_emit (self, signals[ICON_REMOVED], 0, XAPP_STATUS_ICON_INTERFACE_PROXY (proxy));
125
126 g_object_unref (proxy);
127 }
128
129 static void
130 new_object_manager_created (GObject *object,
131 GAsyncResult *res,
132 gpointer user_data)
133 {
134 XAppStatusIconMonitor *self = XAPP_STATUS_ICON_MONITOR (user_data);
135 XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self);
136 GDBusObjectManager *obj_mgr;
137 GList *objects = NULL, *iter;
138
147139 GError *error;
148 gchar *g_name;
140 gchar *name;
149141
150142 error = NULL;
151143
152 proxy = xapp_status_icon_interface_proxy_new_finish (res,
153 &error);
144 obj_mgr = xapp_object_manager_client_new_finish (res,
145 &error);
154146
155147 if (error)
156148 {
157 g_warning ("Couldn't add status icon: %s", error->message);
149 g_warning ("Couldn't create object manager for bus name: %s", error->message);
158150 g_error_free (error);
159151 return;
160152 }
161153
162 g_signal_connect_object (proxy,
163 "notify::g-name-owner",
164 G_CALLBACK (on_proxy_name_owner_changed),
165 self,
166 0);
167
168 g_object_get (proxy, "g-name", &g_name, NULL);
169
170 g_hash_table_insert (priv->icons,
171 g_name,
172 proxy);
173
174 g_signal_emit (self, signals[ICON_ADDED], 0, proxy);
175 }
176
177 static void
178 add_icon (XAppStatusIconMonitor *self,
179 const gchar *name)
180 {
181 XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self);
182 gchar *unique_path;
183 gint pid, id;
184
185 if (sscanf (name, STATUS_ICON_ID_FORMAT, &pid, &id) == 2)
186 {
187 unique_path = g_strdup_printf (STATUS_ICON_PATH_PREFIX "%d", id);
188
189 g_debug("XAppStatusIconMonitor: adding icon: '%s' with path '%s'", name, unique_path);
190
191 xapp_status_icon_interface_proxy_new (priv->connection,
192 G_DBUS_PROXY_FLAGS_NONE,
193 name,
194 unique_path,
195 NULL,
196 new_status_icon_proxy_complete,
197 self);
198
199 g_free (unique_path);
154 g_object_get (obj_mgr, "name", &name, NULL);
155
156 g_debug("XAppStatusIconMonitor: Object manager added for new bus name: '%s'", name);
157
158 g_signal_connect (obj_mgr,
159 "notify::name-owner",
160 G_CALLBACK (on_object_manager_name_owner_changed),
161 self);
162
163 g_signal_connect (obj_mgr,
164 "object-added",
165 G_CALLBACK (object_manager_object_added),
166 self);
167
168 g_signal_connect (obj_mgr,
169 "object-removed",
170 G_CALLBACK (object_manager_object_removed),
171 self);
172
173 g_hash_table_insert (priv->object_managers,
174 name,
175 obj_mgr);
176
177 objects = g_dbus_object_manager_get_objects (obj_mgr);
178
179 for (iter = objects; iter != NULL; iter = iter->next)
180 {
181 GDBusObject *object = G_DBUS_OBJECT (iter->data);
182 GDBusInterface *proxy = g_dbus_object_get_interface (object, STATUS_ICON_INTERFACE);
183
184 g_signal_emit (self, signals[ICON_ADDED], 0, proxy);
185
186 g_object_unref (proxy);
187 }
188
189 g_list_free_full (objects, g_object_unref);
190 }
191
192 static void
193 add_object_manager_for_name (XAppStatusIconMonitor *self,
194 const gchar *name)
195 {
196 XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self);
197 gchar **name_parts = NULL;
198
199 name_parts = g_strsplit (name, ".", -1);
200
201 if (g_strv_length (name_parts) == 4)
202 {
203 xapp_object_manager_client_new (priv->connection,
204 G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
205 name,
206 STATUS_ICON_PATH,
207 NULL,
208 new_object_manager_created,
209 self);
200210 }
201211 else
202212 {
203 g_debug ("XAppStatusIconMonitor: adding icon failed, name '%s' is invalid", name);
204 }
213 g_debug ("XAppStatusIconMonitor: adding object manager failed, bus name '%s' is invalid", name);
214 }
215
216 g_strfreev (name_parts);
205217 }
206218
207219 static void
235247 /* the '.' at the end so we don't catch ourselves in this */
236248 if (g_str_has_prefix (str, STATUS_ICON_MATCH))
237249 {
238 g_debug ("XAppStatusIconMonitor: found icon: %s", str);
239 add_icon (self, str);
250 g_debug ("XAppStatusIconMonitor: found new status icon app: %s", str);
251 add_object_manager_for_name (self, str);
240252 }
241253 }
242254
249261 {
250262 XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self);
251263
252
253 g_debug("XAppStatusIconMonitor: looking for status icons on the bus");
264 g_debug("XAppStatusIconMonitor: looking for status icon apps on the bus");
254265
255266 /* If there are no monitors (applets) already running when this is set up,
256267 * this won't find anything. The XAppStatusIcons will be in fallback mode,
289300 }
290301
291302 static void
292 status_icon_name_appeared (XAppStatusIconMonitor *self,
293 const gchar *name,
294 const gchar *owner)
295 {
296 XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self);
297
298 if (!g_str_has_prefix (name, STATUS_ICON_MATCH))
299 {
300 return;
301 }
302
303 if (g_hash_table_contains (priv->icons, name))
304 {
305 return;
306 }
307
308 g_debug ("XAppStatusIconMonitor: new icon appeared: %s", name);
309
310 add_icon (self, name);
311 }
312
313 static void
314 status_icon_name_vanished (XAppStatusIconMonitor *self,
315 const gchar *name)
316 {
317 if (!g_str_has_prefix (name, STATUS_ICON_MATCH))
318 {
319 return;
320 }
321
322 g_debug ("XAppStatusIconMonitor: icon presence vanished: %s", name);
323
324 remove_icon (self, name);
325 }
326
327 static void
328303 name_owner_changed (GDBusConnection *connection,
329304 const gchar *sender_name,
330305 const gchar *object_path,
343318
344319 g_variant_get (parameters, "(&s&s&s)", &name, &old_owner, &new_owner);
345320
346 if (old_owner[0] != '\0')
347 {
348 status_icon_name_vanished (self, name);
349 }
350
351321 if (new_owner[0] != '\0')
352322 {
353 status_icon_name_appeared (self, name, new_owner);
354 }
355 }
356
323 add_object_manager_for_name (self, name);
324 }
325 }
357326
358327 static void
359328 add_name_listener (XAppStatusIconMonitor *self)
360329 {
361330 XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self);
362331
363 g_debug ("XAppStatusIconMonitor: Adding NameOwnerChanged listener for status icons");
332 g_debug ("XAppStatusIconMonitor: Adding NameOwnerChanged listener for status icon apps");
364333
365334 priv->listener_id = g_dbus_connection_signal_subscribe (priv->connection,
366335 "org.freedesktop.DBus",
392361 {
393362 XAppStatusIconMonitor *self = XAPP_STATUS_ICON_MONITOR (user_data);
394363
395 g_debug ("XAppStatusIconMonitor: name acquired on dbus");
364 g_debug ("XAppStatusIconMonitor: Name owned on bus: %s", name);
396365
397366 add_name_listener (self);
398367 find_and_add_icons (self);
407376 XAppStatusIconMonitor *self = XAPP_STATUS_ICON_MONITOR (user_data);
408377 XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self);
409378
410 g_debug ("XAppStatusIconMonitor: session bus connection acquired");
379 g_debug ("XAppStatusIconMonitor: Connected to bus: %s", name);
411380
412381 priv->connection = connection;
413382 }
416385 connect_to_bus (XAppStatusIconMonitor *self)
417386 {
418387 XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self);
419
388 gchar *valid_app_name, *owned_name;
420389 static gint unique_id = 0;
421390
422 char *owner_name = g_strdup_printf("%s.PID-%d-%d", MONITOR_NAME, getpid (), unique_id);
423
424 unique_id++;
425
426 g_debug ("XAppStatusIconMonitor: Attempting to acquire presence on dbus as %s", owner_name);
391 valid_app_name = g_strdelimit (g_strdup (g_get_application_name ()), ".-,=+~`/", '_');
392 owned_name = g_strdup_printf ("%s.%s_%d", MONITOR_NAME, valid_app_name, unique_id++);
393 g_free (valid_app_name);
394
395 g_debug ("XAppStatusIconMonitor: Attempting to own name on bus: %s", owned_name);
427396
428397 priv->owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
429 owner_name,
398 owned_name,
430399 G_DBUS_CONNECTION_FLAGS_NONE,
431400 on_bus_acquired,
432401 on_name_acquired,
434403 self,
435404 NULL);
436405
437 g_free(owner_name);
406 g_free(owned_name);
438407 }
439408
440409 static void
442411 {
443412 XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self);
444413
445 priv->name = g_strdup_printf("%s", g_get_application_name());
446
447 priv->icons = g_hash_table_new_full (g_str_hash, g_str_equal,
448 g_free, g_object_unref);
449
450 priv->sn_watcher_retry_count = 0;
414 priv->object_managers = g_hash_table_new_full (g_str_hash, g_str_equal,
415 g_free, g_object_unref);
451416
452417 connect_to_bus (self);
453418 }
468433 priv->listener_id = 0;
469434 }
470435
436 if (priv->object_managers != NULL)
437 {
438 g_hash_table_unref (priv->object_managers);
439 priv->object_managers = NULL;
440 }
441
471442 if (priv->owner_id > 0)
472443 {
473444 g_bus_unown_name(priv->owner_id);
474445 priv->owner_id = 0;
475446 }
476447
477 if (priv->sn_watcher_id > 0)
478 {
479 g_bus_unwatch_name (priv->sn_watcher_id);
480 priv->sn_watcher_id = 0;
481 }
482
483448 g_clear_object (&priv->connection);
484449 }
485
486 g_free (priv->name);
487 g_clear_pointer (&priv->icons, g_hash_table_unref);
488450
489451 G_OBJECT_CLASS (xapp_status_icon_monitor_parent_class)->dispose (object);
490452 }
538500 G_TYPE_NONE, 1, XAPP_TYPE_STATUS_ICON_INTERFACE_PROXY);
539501 }
540502
503 static void
504 gather_objects_foreach_func (gpointer key,
505 gpointer value,
506 gpointer user_data)
507 {
508 GDBusObjectManager *obj_mgr = G_DBUS_OBJECT_MANAGER (value);
509 GList **ret = (GList **) user_data;
510
511 GList *objects = NULL, *iter;
512
513 objects = g_dbus_object_manager_get_objects (obj_mgr);
514
515 for (iter = objects; iter != NULL; iter = iter->next)
516 {
517 GDBusObject *object = G_DBUS_OBJECT (iter->data);
518 GDBusInterface *proxy = g_dbus_object_get_interface (object, STATUS_ICON_INTERFACE);
519
520 *ret = g_list_prepend (*ret, proxy);
521
522 g_object_unref (proxy);
523 }
524
525 g_list_free_full (objects, g_object_unref);
526 }
527
541528 /**
542529 * xapp_status_icon_monitor_list_icons:
543530 * @monitor: a #XAppStatusIconMonitor
552539 xapp_status_icon_monitor_list_icons (XAppStatusIconMonitor *monitor)
553540 {
554541 g_return_val_if_fail (XAPP_IS_STATUS_ICON_MONITOR (monitor), NULL);
542 GList *ret = NULL;
555543
556544 XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (monitor);
557545
558 return g_hash_table_get_values (priv->icons);
546 g_hash_table_foreach (priv->object_managers,
547 (GHFunc) gather_objects_foreach_func,
548 &ret);
549
550 return ret;
559551 }
560552
561553 /**
1919 #define FDO_DBUS_NAME "org.freedesktop.DBus"
2020 #define FDO_DBUS_PATH "/org/freedesktop/DBus"
2121
22 #define ICON_PATH "/org/x/StatusIcon"
22 #define ICON_BASE_PATH "/org/x/StatusIcon"
23 #define ICON_SUB_PATH (ICON_BASE_PATH "/Icon")
2324 #define ICON_NAME "org.x.StatusIcon"
2425
2526 #define STATUS_ICON_MONITOR_MATCH "org.x.StatusIconMonitor"
2930 #define MAX_SANE_ICON_SIZE 96
3031 #define FALLBACK_ICON_SIZE 24
3132
32 static gint unique_id = 0;
33 // This gets reffed and unreffed according to individual icon presence.
34 // For the first icon, it gets created when exporting the icon's inteface.
35 // For each additional icon, it gets reffed again. On destruction, the
36 // opposite occurs - unrefs, and the final unref calls a weak notify
37 // function, which clears this pointer and unown's the process's bus name.
38 static GDBusObjectManagerServer *obj_server = NULL;
39 static guint name_owner_id = 0;
3340
3441 enum
3542 {
4956 PROP_PRIMARY_MENU,
5057 PROP_SECONDARY_MENU,
5158 PROP_ICON_SIZE,
59 PROP_NAME,
5260 N_PROPERTIES
5361 };
5462
6674 */
6775 typedef struct
6876 {
69 XAppStatusIconInterface *skeleton;
7077 GDBusConnection *connection;
78 XAppStatusIconInterface *interface_skeleton;
79 XAppObjectSkeleton *object_skeleton;
7180
7281 GCancellable *cancellable;
7382
8594 gint icon_size;
8695 gchar *metadata;
8796
88 guint owner_id;
8997 guint listener_id;
9098
9199 gint fail_counter;
102110
103111 static void refresh_icon (XAppStatusIcon *self);
104112 static void use_gtk_status_icon (XAppStatusIcon *self);
105 static void tear_down_dbus (XAppStatusIcon *self);
113 static void remove_icon_path_from_bus (XAppStatusIcon *self);
106114
107115 static void
108116 cancellable_reset (XAppStatusIcon *self)
265273
266274 if (icon->priv->state == XAPP_STATUS_ICON_STATE_NATIVE)
267275 {
268 xapp_status_icon_interface_set_primary_menu_is_open (icon->priv->skeleton, FALSE);
276 xapp_status_icon_interface_set_primary_menu_is_open (icon->priv->interface_skeleton, FALSE);
269277 }
270278
271279 g_signal_handlers_disconnect_by_func (widget, primary_menu_unmapped, icon);
282290
283291 if (icon->priv->state == XAPP_STATUS_ICON_STATE_NATIVE)
284292 {
285 xapp_status_icon_interface_set_secondary_menu_is_open (icon->priv->skeleton, FALSE);
293 xapp_status_icon_interface_set_secondary_menu_is_open (icon->priv->interface_skeleton, FALSE);
286294 }
287295
288296 g_signal_handlers_disconnect_by_func (widget, secondary_menu_unmapped, icon);
327335 {
328336 if (self->priv->state == XAPP_STATUS_ICON_STATE_NATIVE)
329337 {
330 xapp_status_icon_interface_set_primary_menu_is_open (self->priv->skeleton, TRUE);
338 xapp_status_icon_interface_set_primary_menu_is_open (self->priv->interface_skeleton, TRUE);
331339 }
332340
333341 g_signal_connect (gtk_widget_get_toplevel (GTK_WIDGET (menu)),
340348 {
341349 if (self->priv->state == XAPP_STATUS_ICON_STATE_NATIVE)
342350 {
343 xapp_status_icon_interface_set_secondary_menu_is_open (self->priv->skeleton, TRUE);
351 xapp_status_icon_interface_set_secondary_menu_is_open (self->priv->interface_skeleton, TRUE);
344352 }
345353
346354 g_signal_connect (gtk_widget_get_toplevel (GTK_WIDGET (menu)),
728736
729737 g_clear_object (&self->priv->gtk_status_icon);
730738
731 g_object_set (G_OBJECT (priv->skeleton),
739 g_object_set (G_OBJECT (priv->interface_skeleton),
732740 "name", priv->name,
733741 "label", priv->label,
734742 "icon-name", priv->icon_name,
737745 "metadata", priv->metadata,
738746 NULL);
739747
740 g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (priv->skeleton));
748 g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (priv->interface_skeleton));
741749 }
742750
743751 static void
769777 { "handle-scroll", handle_scroll_method }
770778 };
771779
780 static void
781 obj_server_finalized (gpointer data,
782 GObject *object)
783 {
784 g_debug ("XAppStatusIcon: Final icon removed, clearing object manager (%s)", g_get_application_name ());
785
786 if (name_owner_id > 0)
787 {
788 g_bus_unown_name(name_owner_id);
789 name_owner_id = 0;
790 }
791
792 obj_server = NULL;
793 }
794
795 static void
796 ensure_object_manager (XAppStatusIcon *self)
797 {
798 if (obj_server == NULL)
799 {
800 g_debug ("XAppStatusIcon: New object manager for (%s)", g_get_application_name ());
801
802 obj_server = g_dbus_object_manager_server_new (ICON_BASE_PATH);
803 g_dbus_object_manager_server_set_connection (obj_server, self->priv->connection);
804 g_object_weak_ref (G_OBJECT (obj_server),(GWeakNotify) obj_server_finalized, self);
805 }
806 else
807 {
808 g_object_ref (obj_server);
809 }
810 }
811
772812 static gboolean
773813 export_icon_interface (XAppStatusIcon *self)
774814 {
775 GError *error = NULL;
776815 gint i;
777 gchar *unique_path;
778
779 if (self->priv->skeleton) {
816
817 ensure_object_manager (self);
818
819 if (self->priv->interface_skeleton)
820 {
780821 return TRUE;
781822 }
782823
783 self->priv->skeleton = xapp_status_icon_interface_skeleton_new ();
784
785 unique_path = g_strdup_printf (ICON_PATH "/%d", unique_id);
786
787 g_debug ("XAppStatusIcon: exporting StatusIcon dbus interface to %s", unique_path);
788
789 g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self->priv->skeleton),
790 self->priv->connection,
791 unique_path,
792 &error);
793
794 g_free (unique_path);
795
796 if (error != NULL) {
797 g_critical ("XAppStatusIcon: could not export StatusIcon interface: %s", error->message);
798 g_error_free (error);
799
800 return FALSE;
801 }
824 self->priv->object_skeleton = xapp_object_skeleton_new (ICON_SUB_PATH);
825 self->priv->interface_skeleton = xapp_status_icon_interface_skeleton_new ();
826
827 xapp_object_skeleton_set_status_icon_interface (self->priv->object_skeleton,
828 self->priv->interface_skeleton);
829
830 g_dbus_object_manager_server_export_uniquely (obj_server,
831 G_DBUS_OBJECT_SKELETON (self->priv->object_skeleton));
832
833 g_object_unref (self->priv->object_skeleton);
834 g_object_unref (self->priv->interface_skeleton);
802835
803836 for (i = 0; i < G_N_ELEMENTS (skeleton_signals); i++) {
804837 SkeletonSignal sig = skeleton_signals[i];
805838
806 g_signal_connect (self->priv->skeleton,
839 g_signal_connect (self->priv->interface_skeleton,
807840 sig.signal_name,
808841 G_CALLBACK (sig.callback),
809842 self);
815848 static void
816849 connect_with_status_applet (XAppStatusIcon *self)
817850 {
818
819 char *owner_name = g_strdup_printf("%s.PID-%d-%d", ICON_NAME, getpid (), unique_id);
820
821 unique_id++;
822
823 g_debug ("XAppStatusIcon: Attempting to acquire presence on dbus as %s", owner_name);
824
825 self->priv->owner_id = g_bus_own_name_on_connection (self->priv->connection,
826 owner_name,
827 G_DBUS_CONNECTION_FLAGS_NONE,
828 on_name_acquired,
829 on_name_lost,
830 self,
831 NULL);
851 gchar **name_parts = NULL;
852 gchar *owner_name;
853
854 name_parts = g_strsplit (self->priv->name, ".", -1);
855
856 if (g_dbus_is_name (self->priv->name) &&
857 g_str_has_prefix (self->priv->name, ICON_NAME) &&
858 g_strv_length (name_parts) == 4)
859 {
860 owner_name = g_strdup (self->priv->name);
861 }
862 else
863 {
864 gchar *valid_app_name = g_strdelimit (g_strdup (g_get_application_name ()), " .-,=+~`/", '_');
865
866 owner_name = g_strdup_printf ("%s.%s",
867 ICON_NAME,
868 valid_app_name);
869 g_free (valid_app_name);
870 }
871
872 g_strfreev (name_parts);
873
874 if (name_owner_id == 0)
875 {
876 g_debug ("XAppStatusIcon: Attempting to own name on bus '%s'", owner_name);
877 name_owner_id = g_bus_own_name_on_connection (self->priv->connection,
878 owner_name,
879 G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE,
880 on_name_acquired,
881 on_name_lost,
882 self,
883 NULL);
884 }
832885
833886 g_free(owner_name);
834887 }
895948
896949 g_debug ("XAppStatusIcon: falling back to GtkStatusIcon");
897950
898 tear_down_dbus (self);
951 remove_icon_path_from_bus (self);
899952
900953 // Make sure there wasn't already one
901954 g_clear_object (&self->priv->gtk_status_icon);
9701023
9711024 if (found && export_icon_interface (self))
9721025 {
973 if (self->priv->owner_id > 0)
1026 if (name_owner_id > 0)
9741027 {
975 g_debug ("XAppStatusIcon: We already exist on the bus, syncing icon properties");
9761028 sync_skeleton (self);
9771029 }
9781030 else
10641116 return;
10651117 }
10661118
1067 if (self->priv->connection)
1068 {
1069 complete_icon_setup (self);
1070 return;
1071 }
1072
1073 use_gtk_status_icon (self);
1119 complete_icon_setup (self);
10741120 }
10751121
10761122 static void
10771123 refresh_icon (XAppStatusIcon *self)
10781124 {
1079 if (!self->priv->connection)
1080 {
1081 g_debug ("XAppStatusIcon: Trying to acquire session bus connection");
1125 if (self->priv->connection == NULL)
1126 {
1127 g_debug ("XAppStatusIcon: Connecting to session bus");
10821128
10831129 cancellable_reset (self);
10841130
11101156 case PROP_ICON_SIZE:
11111157 XAPP_STATUS_ICON (object)->priv->icon_size = CLAMP (g_value_get_int (value), 0, MAX_SANE_ICON_SIZE);
11121158 break;
1159 case PROP_NAME:
1160 {
1161 const gchar *name = g_value_get_string (value);
1162 // Can't be null. We set to g_get_application_name() by default.
1163 if (name == NULL || name[0] == '\0')
1164 {
1165 break;
1166 }
1167 }
1168
1169 xapp_status_icon_set_name (XAPP_STATUS_ICON (object), g_value_get_string (value));
1170 break;
11131171 default:
11141172 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
11151173 break;
11351193 case PROP_ICON_SIZE:
11361194 g_value_set_int (value, icon->priv->icon_size);
11371195 break;
1196 case PROP_NAME:
1197 g_value_set_string (value, icon->priv->name);
1198 break;
11381199 default:
11391200 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
11401201 break;
11461207 {
11471208 self->priv = xapp_status_icon_get_instance_private (self);
11481209
1149 self->priv->name = g_strdup_printf("%s", g_get_application_name());
1210 self->priv->name = g_strdup (g_get_application_name());
1211
11501212 self->priv->state = XAPP_STATUS_ICON_STATE_NO_SUPPORT;
11511213 self->priv->icon_size = FALLBACK_ICON_SIZE;
11521214 self->priv->icon_name = g_strdup (" ");
11601222 }
11611223
11621224 static void
1163 tear_down_dbus (XAppStatusIcon *self)
1225 remove_icon_path_from_bus (XAppStatusIcon *self)
11641226 {
11651227 g_return_if_fail (XAPP_IS_STATUS_ICON (self));
11661228
1167 if (self->priv->owner_id > 0)
1168 {
1169 g_debug ("XAppStatusIcon: removing dbus presence (%p)", self);
1170
1171 g_bus_unown_name(self->priv->owner_id);
1172 self->priv->owner_id = 0;
1173 }
1174
1175 if (self->priv->skeleton)
1176 {
1177 g_debug ("XAppStatusIcon: removing dbus interface (%p)", self);
1178
1179 g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self->priv->skeleton));
1180 g_object_unref (self->priv->skeleton);
1181 self->priv->skeleton = NULL;
1229 if (self->priv->object_skeleton)
1230 {
1231 const gchar *path;
1232 path = g_dbus_object_get_object_path (G_DBUS_OBJECT (self->priv->object_skeleton));
1233
1234 g_debug ("XAppStatusIcon: removing interface at path '%s'", path);
1235
1236 g_dbus_object_manager_server_unexport (obj_server, path);
1237 self->priv->interface_skeleton = NULL;
1238 self->priv->object_skeleton = NULL;
1239
1240 g_object_unref (obj_server);
11821241 }
11831242 }
11841243
12081267 self->priv->gtk_status_icon = NULL;
12091268 }
12101269
1211 tear_down_dbus (self);
1270 remove_icon_path_from_bus (self);
12121271
12131272 if (self->priv->listener_id > 0)
12141273 {
12151274 g_dbus_connection_signal_unsubscribe (self->priv->connection, self->priv->listener_id);
1275 self->priv->listener_id = 0;
12161276 }
12171277
12181278 g_clear_object (&self->priv->connection);
13171377 G_PARAM_READWRITE));
13181378
13191379 /**
1380 * XAppStatusIcon:name:
1381 *
1382 * The name of the icon for sorting purposes. If this is in the form of 'org.x.StatusIcon.foo`
1383 * and set immediately upon creation of the icon, it will also attempt to own this dbus name;
1384 * this can be useful in sandboxed environments where a well-defined name is required. If
1385 * additional icons are created, only the name given to the initial one will be used for dbus,
1386 * though different names can still affect the sort order. This is set to the value of
1387 * g_get_application_name() if no other name is provided.
1388 */
1389 g_object_class_install_property (gobject_class, PROP_NAME,
1390 g_param_spec_string ("name",
1391 "The name of the icon for sorting purposes.",
1392 NULL,
1393 NULL,
1394 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
1395
1396 /**
13201397 * XAppStatusIcon::button-press-event:
13211398 * @icon: The #XAppStatusIcon
13221399 * @x: The absolute x position to use for menu positioning
14291506 return;
14301507 }
14311508
1509 if (name == NULL || name[0] == '\0')
1510 {
1511 // name can't be null. We set to g_get_application_name() at startup,
1512 // and the set_property handler silently ignores nulls, but if this
1513 // is explicit, warn about it.
1514 g_warning ("Can't set icon the name to null or empty string");
1515 return;
1516 }
1517
14321518 g_clear_pointer (&icon->priv->name, g_free);
14331519 icon->priv->name = g_strdup (name);
14341520
14351521 g_debug ("XAppStatusIcon set_name: %s", name);
14361522
1437 if (icon->priv->skeleton)
1438 {
1439 xapp_status_icon_interface_set_name (icon->priv->skeleton, name);
1523 if (icon->priv->interface_skeleton)
1524 {
1525 xapp_status_icon_interface_set_name (icon->priv->interface_skeleton, name);
14401526 }
14411527
14421528 /* Call this directly instead of in the update_fallback_icon() function,
14731559
14741560 g_debug ("XAppStatusIcon set_icon_name: %s", icon_name);
14751561
1476 if (icon->priv->skeleton)
1477 {
1478 xapp_status_icon_interface_set_icon_name (icon->priv->skeleton, icon_name);
1562 if (icon->priv->interface_skeleton)
1563 {
1564 xapp_status_icon_interface_set_icon_name (icon->priv->interface_skeleton, icon_name);
14791565 }
14801566
14811567 update_fallback_icon (icon);
14961582 {
14971583 g_return_val_if_fail (XAPP_IS_STATUS_ICON (icon), FALLBACK_ICON_SIZE);
14981584
1499 if (icon->priv->skeleton == NULL)
1585 if (icon->priv->interface_skeleton == NULL)
15001586 {
15011587 g_debug ("XAppStatusIcon get_icon_size: %d (fallback)", FALLBACK_ICON_SIZE);
15021588
15051591
15061592 gint size;
15071593
1508 size = xapp_status_icon_interface_get_icon_size (icon->priv->skeleton);
1594 size = xapp_status_icon_interface_get_icon_size (icon->priv->interface_skeleton);
15091595
15101596 g_debug ("XAppStatusIcon get_icon_size: %d", size);
15111597
15361622
15371623 g_debug ("XAppStatusIcon set_tooltip_text: %s", tooltip_text);
15381624
1539 if (icon->priv->skeleton)
1540 {
1541 xapp_status_icon_interface_set_tooltip_text (icon->priv->skeleton, tooltip_text);
1625 if (icon->priv->interface_skeleton)
1626 {
1627 xapp_status_icon_interface_set_tooltip_text (icon->priv->interface_skeleton, tooltip_text);
15421628 }
15431629
15441630 update_fallback_icon (icon);
15681654
15691655 g_debug ("XAppStatusIcon set_label: '%s'", label);
15701656
1571 if (icon->priv->skeleton)
1572 {
1573 xapp_status_icon_interface_set_label (icon->priv->skeleton, label);
1657 if (icon->priv->interface_skeleton)
1658 {
1659 xapp_status_icon_interface_set_label (icon->priv->interface_skeleton, label);
15741660 }
15751661 }
15761662
15971683
15981684 g_debug ("XAppStatusIcon set_visible: %s", visible ? "TRUE" : "FALSE");
15991685
1600 if (icon->priv->skeleton)
1601 {
1602 xapp_status_icon_interface_set_visible (icon->priv->skeleton, visible);
1686 if (icon->priv->interface_skeleton)
1687 {
1688 xapp_status_icon_interface_set_visible (icon->priv->interface_skeleton, visible);
16031689 }
16041690
16051691 update_fallback_icon (icon);
17821868 }
17831869
17841870 /**
1871 * xapp_status_icon_new_with_name:
1872 *
1873 * Creates a new #XAppStatusIcon instance and sets its name to %name.
1874 *
1875 * Returns: (transfer full): a new #XAppStatusIcon. Use g_object_unref when finished.
1876 *
1877 * Since: 1.6
1878 */
1879 XAppStatusIcon *
1880 xapp_status_icon_new_with_name (const gchar *name)
1881 {
1882 return g_object_new (XAPP_TYPE_STATUS_ICON,
1883 "name", name,
1884 NULL);
1885 }
1886
1887 /**
17851888 * xapp_status_icon_get_state:
17861889 * @icon: an #XAppStatusIcon
17871890 *
18341937 icon->priv->metadata = g_strdup (metadata);
18351938 g_free (old_meta);
18361939
1837 if (icon->priv->skeleton)
1838 {
1839 xapp_status_icon_interface_set_metadata (icon->priv->skeleton, metadata);
1940 if (icon->priv->interface_skeleton)
1941 {
1942 xapp_status_icon_interface_set_metadata (icon->priv->interface_skeleton, metadata);
18401943 }
18411944 }
18421945
4646
4747
4848 XAppStatusIcon *xapp_status_icon_new (void);
49 XAppStatusIcon *xapp_status_icon_new_with_name (const gchar *name);
4950 void xapp_status_icon_set_name (XAppStatusIcon *icon, const gchar *name);
5051 void xapp_status_icon_set_icon_name (XAppStatusIcon *icon, const gchar *icon_name);
5152 gint xapp_status_icon_get_icon_size (XAppStatusIcon *icon);
2626 '--interface-prefix=' + sys.argv[1],
2727 '--generate-c-code=' + os.path.join(sys.argv[4], sys.argv[2]),
2828 '--c-namespace=XApp',
29 '--c-generate-object-manager',
2930 '--annotate', sys.argv[1], 'org.gtk.GDBus.C.Name', sys.argv[3],
3031 sys.argv[5]
3132 ])
00 project('xapp',
11 'c',
2 version : '1.8.9'
2 version : '2.0.0'
33 )
44
55 gnome = import('gnome')
77 i18n = import('i18n')
88
99 dbus_services_dir = dependency('dbus-1').get_pkgconfig_variable('session_bus_services_dir',
10 define_variable: ['prefix', get_option('prefix')])
10 define_variable: ['datadir', get_option('datadir')])
1111 libexec_path = join_paths(get_option('prefix'), get_option('libexecdir'), 'xapps', 'sn-watcher')
1212
1313 cdata = configuration_data()
4949 subdir('icons')
5050 subdir('status-applets')
5151 subdir('scripts')
52 subdir('data')
5253 endif
5354
5455 if get_option('status-notifier') and not app_lib_only
11 <schemalist>
22 <schema id="org.x.apps" path="/org/x/apps/">
33 <child name="status-icon" schema="org.x.apps.statusicon"/>
4 <child name="favorites" schema="org.x.apps.favorites"/>
5 </schema>
6
7 <schema id="org.x.apps.favorites" path="/org/x/apps/favorites/" gettext-domain="xapps">
8 <key name="list" type="as">
9 <default>[]</default>
10 <summary>List of favorites, stored in display order, with the format of uri::mimetype</summary>
11 </key>
12 <key name="root-metadata" type="as">
13 <default>[]</default>
14 <summary>List of gvfs metadata for the favorites:/// root (for remembering sort order in nemo, etc).</summary>
15 </key>
416 </schema>
517
618 <schema id="org.x.apps.statusicon" path="/org/x/apps/statusicon/">
00 #!/bin/bash
11
22 # Author: Weitian Leung <weitianleung@gmail.com>
3 # Version: 2.0
3 # Version: 2.1
44 # License: GPL-3.0
55 # Description: set a picture as xfce4 wallpaper
66
7 wallpaper=$1
7 # xfce4-desktop requires an absolute path.
8 wallpaper="$(realpath "$1")"
89
910 # check image
1011 mime_type=`file --mime-type -b "$wallpaper"`
371371 self.monitor.connect("icon-added", self.on_icon_added)
372372 self.monitor.connect("icon-removed", self.on_icon_removed)
373373
374 def make_key(self, proxy):
375 name = proxy.get_name()
376 path = proxy.get_object_path()
377
378 # print("Key: %s" % (name+path))
379 return name + path
380
374381 def destroy_monitor (self):
375382 for key in self.indicators.keys():
376383 self.indicator_box.remove(self.indicators[key])
379386 self.indicators = {}
380387
381388 def on_icon_added(self, monitor, proxy):
382 name = proxy.get_name()
383
384 self.indicators[name] = StatusWidget(proxy, self.applet.get_orient(), self.applet.get_size())
385 self.indicator_box.add(self.indicators[name])
386 self.indicators[name].connect("re-sort", self.sort_icons)
389 key = self.make_key(proxy)
390
391 self.indicators[key] = StatusWidget(proxy, self.applet.get_orient(), self.applet.get_size())
392 self.indicator_box.add(self.indicators[key])
393 self.indicators[key].connect("re-sort", self.sort_icons)
387394
388395 self.sort_icons()
389396
390397 def on_icon_removed(self, monitor, proxy):
391 name = proxy.get_name()
392
393 self.indicator_box.remove(self.indicators[name])
394 self.indicators[name].disconnect_by_func(self.sort_icons)
395 del(self.indicators[name])
398 key = self.make_key(proxy)
399
400 self.indicator_box.remove(self.indicators[key])
401 self.indicators[key].disconnect_by_func(self.sort_icons)
402 del(self.indicators[key])
396403
397404 self.sort_icons()
398405
440447 # for i in icon_list:
441448 # print("before: ", i.proxy.props.icon_name, i.proxy.props.name.lower())
442449
443 icon_list.sort(key=lambda icon: icon.proxy.props.name.lower())
450 icon_list.sort(key=lambda icon: icon.proxy.props.name.replace("org.x.StatusIcon.", "").lower())
444451 icon_list.sort(key=lambda icon: icon.proxy.props.icon_name.lower().endswith("symbolic"))
445452
446453 # for i in icon_list:
0 #!/usr/bin/python3
1 import gi
2 gi.require_version('Gtk', '3.0')
3 gi.require_version('XApp', '1.0')
4 from gi.repository import Gio, GLib, GObject, Gtk, XApp, Pango
5 import sys
6 import signal
7
8 signal.signal(signal.SIGINT, signal.SIG_DFL)
9
10
11 DBUS_NAME = "org.x.StatusIcon"
12 DBUS_PATH = "/org/x/StatusIcon"
13
14 class ListItem(Gtk.ListBoxRow):
15 def __init__(self, favinfo, mgr):
16 super(Gtk.ListBoxRow, self).__init__()
17 self.favinfo = favinfo
18 self.mgr = mgr
19
20 self.box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
21 self.add(self.box)
22
23 label = Gtk.Label(label=self.favinfo.display_name, xalign=0)
24 self.box.pack_start(label, False, False, 6)
25
26 delete = Gtk.Button.new_from_icon_name("edit-delete-symbolic", Gtk.IconSize.BUTTON)
27 delete.set_relief(Gtk.ReliefStyle.NONE)
28 self.box.pack_end(delete, False, False, 6)
29 delete.connect("clicked", self.on_delete_clicked)
30
31 self.show_all()
32
33 def on_delete_clicked(self, widget, data=None):
34 self.mgr.remove(self.favinfo.uri)
35
36 class FavoriteList(GObject.Object):
37
38 def __init__(self):
39 super(FavoriteList, self).__init__()
40 self.window = None
41 self.manager = XApp.Favorites.get_default()
42 self.manager.connect("changed", self.on_manager_changed)
43
44 self.window = Gtk.Window()
45 self.window.set_default_size(600, 400)
46 self.window.connect("destroy", self.on_window_destroy)
47 self.window_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
48 self.main_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,
49 margin=6,
50 spacing=0)
51
52 bar = Gtk.MenuBar()
53 menu_item = Gtk.MenuItem.new_with_label("Favorites")
54 favorites = self.manager.create_menu(None, self.favorite_menu_item_activated)
55 menu_item.set_submenu(favorites)
56 bar.append(menu_item)
57
58 self.window_box.pack_start(bar, False, False, 0)
59 self.window_box.pack_start(self.main_box, True, True, 0)
60
61 # list stuff
62 sw_frame = Gtk.Frame()
63 sw = Gtk.ScrolledWindow(hadjustment=None, vadjustment=None)
64 sw_frame.add(sw)
65
66 self.list_box = Gtk.ListBox(activate_on_single_click=False)
67 self.list_box.connect("selected-rows-changed", self.on_selection_changed)
68 self.list_box.connect("row-activated", self.on_row_activated)
69 sw.add(self.list_box)
70
71 self.main_box.pack_start(sw_frame, True, True, 6)
72 self.window.add(self.window_box)
73
74 # controls
75
76 control_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
77 self.add_button = Gtk.Button.new_from_icon_name("list-add-symbolic", Gtk.IconSize.BUTTON)
78 self.add_button.connect("clicked", self.on_add_clicked)
79
80 control_box.pack_start(self.add_button, False, False, 6)
81
82 self.main_box.pack_start(control_box, False, False, 6)
83
84 self.window.show_all()
85
86 self.selected_uri = None
87 self.loading = False
88
89 self.load_favorites()
90
91 def favorite_menu_item_activated(self, manager, uri, data=None):
92 print("activated", uri)
93 self.manager.launch(uri, Gtk.get_current_event_time())
94
95 def load_favorites(self):
96 self.loading = True # this will preserve the last selection
97
98 for child in self.list_box.get_children():
99 self.list_box.remove(child)
100
101 previously_selected = None
102
103 favorites = self.manager.get_favorites(None)
104
105 for info in favorites:
106 row = ListItem(info, self.manager)
107
108 self.list_box.insert(row, -1)
109
110 if self.selected_uri == info.uri:
111 previously_selected = row
112
113 self.loading = False
114
115 if previously_selected:
116 self.list_box.select_row(previously_selected)
117
118 def on_row_activated(self, box, row, data=None):
119 self.manager.launch(row.favinfo.uri, Gtk.get_current_event_time())
120
121 def on_selection_changed(self, box, data=None):
122 if self.loading:
123 return
124
125 row = self.list_box.get_selected_row()
126
127 if row:
128 self.selected_uri = row.favinfo.uri
129 else:
130 self.selected_uri = None
131
132 def on_add_clicked(self, widget, data=None):
133 dialog = Gtk.FileChooserDialog(title="Add file to favorites",
134 parent=self.window,
135 action=Gtk.FileChooserAction.OPEN)
136
137 dialog.add_buttons("Add", Gtk.ResponseType.OK,
138 "Cancel", Gtk.ResponseType.CANCEL)
139
140 # dialog.add_shortcut_folder_uri ("favorites:///")
141
142 res = dialog.run()
143 if res == Gtk.ResponseType.OK:
144 uri = dialog.get_uri()
145
146 self.manager.add(uri)
147
148 dialog.destroy()
149
150 def on_manager_changed(self, manager, data=None):
151 self.load_favorites()
152
153 def on_window_destroy(self, widget, data=None):
154 Gtk.main_quit()
155
156 if __name__ == '__main__':
157 test = FavoriteList()
158 Gtk.main()
166166 self.disco_button.set_label("Connect monitor")
167167
168168 def on_icon_added(self, monitor, proxy):
169 name = proxy.get_name()
169 name = proxy.get_name() + proxy.get_object_path()
170170
171171 self.indicators[name] = StatusWidget(proxy)
172172 self.indicator_box.add(self.indicators[name])
175175 self.sort_icons()
176176
177177 def on_icon_removed(self, monitor, proxy):
178 name = proxy.get_name()
178 name = proxy.get_name() + proxy.get_object_path()
179179
180180 self.indicator_box.remove(self.indicators[name])
181181 self.indicators[name].disconnect_by_func(self.sort_icons)
189189 # for i in icon_list:
190190 # print("before: ", i.proxy.props.icon_name, i.proxy.props.name.lower())
191191
192 icon_list.sort(key=lambda icon: icon.proxy.props.name.lower())
192 icon_list.sort(key=lambda icon: icon.proxy.props.name.replace("org.x.StatusIcon.", "").lower())
193193 icon_list.sort(key=lambda icon: icon.proxy.props.icon_name.lower().endswith("symbolic"))
194194
195195 # for i in icon_list:
392392 {
393393 surface = new_props->attention_icon_surface;
394394 }
395 else
396 if (new_props->icon_surface)
397 {
398 surface = new_props->icon_surface;
399 }
395400 }
396401
397402 if (surface != NULL)
422427 }
423428
424429 static gchar *
425 get_icon_filename_from_theme (SnItem *item,
426 const gchar *theme_path,
427 const gchar *icon_name)
430 get_name_or_path_from_theme (SnItem *item,
431 const gchar *theme_path,
432 const gchar *icon_name)
428433 {
429434 GtkIconInfo *info;
430435 gchar *filename;
434439 array[0] = icon_name;
435440 array[1] = NULL;
436441
437 // We have a theme path, but try the system theme first
442 // We may have a theme path, but try the system theme first
438443 GtkIconTheme *theme = gtk_icon_theme_get_default ();
439444 host_icon_size = get_icon_size (item);
440445
444449 lookup_ui_scale (),
445450 GTK_ICON_LOOKUP_FORCE_SVG | GTK_ICON_LOOKUP_FORCE_SYMBOLIC);
446451
447 if (info == NULL)
452 if (info != NULL)
453 {
454 // If the icon is found in the system theme, we can just pass along the icon name
455 // as is, this way symbolics work properly.
456 g_object_unref (info);
457 return g_strdup (icon_name);
458 }
459
460 if (theme_path != NULL)
448461 {
449462 // Make a temp theme based off of the provided path
450463 GtkIconTheme *theme = gtk_icon_theme_new ();
476489 const gchar *icon_theme_path,
477490 const gchar *icon_name)
478491 {
479 g_debug ("Checking for icon name for %s",
480 item->sortable_name);
492 g_debug ("Checking for icon name for %s - theme path: '%s', icon name: '%s'",
493 item->sortable_name,
494 icon_theme_path,
495 icon_name);
481496
482497 if (icon_name == NULL)
483498 {
487502 if (g_path_is_absolute (icon_name))
488503 {
489504 xapp_status_icon_set_icon_name (item->status_icon, icon_name);
490
491505 return TRUE;
492506 }
493507 else
494508 {
495 gchar *filename = get_icon_filename_from_theme (item, icon_theme_path, icon_name);
496
497 if (filename != NULL)
498 {
499 xapp_status_icon_set_icon_name (item->status_icon, filename);
500 g_free (filename);
509 gchar *used_name = get_name_or_path_from_theme (item, icon_theme_path, icon_name);
510
511 if (used_name != NULL)
512 {
513 xapp_status_icon_set_icon_name (item->status_icon, used_name);
514 g_free (used_name);
501515 return TRUE;
502516 }
503517 }
511525 {
512526 const gchar *name_to_use = NULL;
513527
514 if (item->status == STATUS_ACTIVE)
528 // Set an icon here, even if we're passive (hidden) - eventually only the
529 // status property might change, but we wouldn't have an icon then (unless
530 // the app sets the icon at the same time).
531 if (item->status == STATUS_ACTIVE || item->status == STATUS_PASSIVE)
515532 {
516533 if (new_props->icon_name)
517534 {
716733 {
717734 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
718735 {
719 g_critical ("Could get propertyies for %s: %s\n",
736 g_critical ("Could not get properties for %s: %s\n",
720737 g_dbus_proxy_get_name (item->sn_item_proxy),
721738 error->message);
722739 }
887904 update_menu (item, new_props);
888905 }
889906
890 if (new_props->update_icon)
907 if (new_props->update_icon || new_props->update_status)
891908 {
892909 update_icon (item, new_props);
893910 }
307307 } NewSnProxyData;
308308
309309 static void
310 free_sn_proxy_data (NewSnProxyData *data)
311 {
312 g_free (data->key);
313 g_free (data->path);
314 g_free (data->bus_name);
315 g_free (data->service);
316 g_object_unref (data->invocation);
317
318 g_slice_free (NewSnProxyData, data);
319 }
320
321 static void
310322 sn_item_proxy_new_completed (GObject *source,
311323 GAsyncResult *res,
312324 gpointer user_data)
321333
322334 proxy = sn_item_interface_proxy_new_finish (res, &error);
323335
336 g_hash_table_steal_extended (watcher->items,
337 data->key,
338 &stolen_ptr,
339 NULL);
340
341 if ((gchar *) stolen_ptr == NULL)
342 {
343 g_dbus_method_invocation_return_error (data->invocation,
344 G_DBUS_ERROR,
345 G_DBUS_ERROR_FAILED,
346 "New StatusNotifierItem disappeared before "
347 "its registration was complete.");
348
349 free_sn_proxy_data (data);
350 g_clear_object (&proxy);
351 return;
352 }
353
324354 if (error != NULL)
325355 {
326356 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
329359 data->bus_name, error->message);
330360 }
331361
332 g_hash_table_steal_extended (watcher->items,
333 data->key,
334 &stolen_ptr,
335 NULL);
336362 g_free (stolen_ptr);
363 free_sn_proxy_data (data);
337364
338365 g_dbus_method_invocation_take_error (data->invocation, error);
339366 return;
341368
342369 item = sn_item_new ((GDBusProxy *) proxy,
343370 g_str_has_prefix (data->path, APPINDICATOR_PATH_PREFIX));
344
345 g_hash_table_steal_extended (watcher->items,
346 data->key,
347 &stolen_ptr,
348 NULL);
349371
350372 g_hash_table_insert (watcher->items,
351373 stolen_ptr,
359381 sn_watcher_interface_complete_register_status_notifier_item (watcher->skeleton,
360382 data->invocation);
361383
362 g_free (data->key);
363 g_free (data->path);
364 g_free (data->bus_name);
365 g_free (data->service);
366 g_object_unref (data->invocation);
367 g_slice_free (NewSnProxyData, data);
384 free_sn_proxy_data (data);
368385 }
369386
370387 static gboolean
493510 }
494511
495512 static void
513 unref_proxy (gpointer data)
514 {
515 // if g_hash_table_remove is called from handle_sn_item_name_owner_lost
516 // *before* sn_item_proxy_new_completed is complete, the key will be
517 // pointing to a NULL value, so avoid trying to free it.
518
519 if (data == NULL)
520 {
521 return;
522 }
523
524 SnItem *item = SN_ITEM (data);
525
526 if (item)
527 {
528 g_object_unref (item);
529 }
530 }
531
532 static void
496533 watcher_startup (GApplication *application)
497534 {
498535 XAppSnWatcher *watcher = (XAppSnWatcher*) application;
503540 xapp_settings = g_settings_new (STATUS_ICON_SCHEMA);
504541
505542 watcher->items = g_hash_table_new_full (g_str_hash, g_str_equal,
506 g_free, g_object_unref);
543 g_free, (GDestroyNotify) unref_proxy);
507544
508545 /* This buys us 30 seconds (gapp timeout) - we'll either be re-held immediately
509546 * because there's a monitor or exit after the 30 seconds. */
77 msgstr ""
88 "Project-Id-Version: PACKAGE VERSION\n"
99 "Report-Msgid-Bugs-To: \n"
10 "POT-Creation-Date: 2020-04-23 15:54+0100\n"
10 "POT-Creation-Date: 2020-11-25 11:16+0000\n"
1111 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1212 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313 "Language-Team: LANGUAGE <LL@li.org>\n"
1515 "MIME-Version: 1.0\n"
1616 "Content-Type: text/plain; charset=CHARSET\n"
1717 "Content-Transfer-Encoding: 8bit\n"
18
19 #: libxapp/favorite-vfs-file.c:463
20 msgid "Favorites"
21 msgstr ""
22
23 #: libxapp/favorite-vfs-file.c:933 libxapp/favorite-vfs-file.c:963
24 #: libxapp/favorite-vfs-file.c:992 libxapp/favorite-vfs-file.c:1026
25 #: libxapp/favorite-vfs-file.c:1053 libxapp/favorite-vfs-file.c:1082
26 #: libxapp/favorite-vfs-file.c:1115 libxapp/favorite-vfs-file.c:1142
27 #: libxapp/favorite-vfs-file.c:1204 libxapp/favorite-vfs-file.c:1239
28 #: libxapp/favorite-vfs-file.c:1269 libxapp/favorite-vfs-file.c:1308
29 msgid "Operation not supported"
30 msgstr ""
1831
1932 #: libxapp/xapp-icon-chooser-button.c:207
2033 #: libxapp/xapp-icon-chooser-dialog.c:639
139152 msgid "Image"
140153 msgstr ""
141154
142 #: status-applets/mate/mate-xapp-status-applet.py:295
155 #: status-applets/mate/mate-xapp-status-applet.py:330
143156 msgid "About"
144157 msgstr ""
145158
146 #: status-applets/mate/mate-xapp-status-applet.py:315
159 #: status-applets/mate/mate-xapp-status-applet.py:350
147160 #: status-applets/mate/org.x.MateXAppStatusApplet.mate-panel-applet.in.in:7
148161 #: status-applets/mate/org.x.MateXAppStatusApplet.mate-panel-applet.in.in:11
149162 msgid "Area where XApp status icons appear"