New upstream version 2.0.0
Norbert Preining
3 years ago
0 | 0 | # Specify filepatterns you want git to ignore. |
1 | 1 | |
2 | debian/build/ | |
2 | obj-x86_64-linux-gnu | |
3 | 3 | debian/tmp/ |
4 | 4 | debian/*.debhelper |
5 | 5 | debian/gir1.2-xapp-1.0/ |
5 | 5 | |
6 | 6 | # API Reference |
7 | 7 | |
8 | https://projects.linuxmint.com/xapps/reference/index.html | |
8 | https://projects.linuxmint.com/xapp/reference/index.html | |
9 | 9 | |
10 | 10 | # xapp-common |
11 | 11 |
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 | ||
0 | 15 | xapp (1.8.9) ulyana; urgency=medium |
1 | 16 | |
2 | 17 | [ Michael Webster ] |
2 | 2 | Priority: optional |
3 | 3 | Maintainer: Clement Lefebvre <root@linuxmint.com> |
4 | 4 | Build-Depends: |
5 | debhelper (>= 10), | |
5 | debhelper-compat (= 12), | |
6 | 6 | dh-python, |
7 | 7 | dpkg-dev (>= 1.15.1), |
8 | 8 | gobject-introspection (>= 0.10.2-1~), |
0 | usr/lib/*/libxapp.so.1* | |
0 | usr/lib/*/libxapp.so.* | |
1 | 1 | usr/libexec/xapps/sn-watcher/* |
2 | usr/lib/*/gtk-3.0/modules | |
3 | /etc/X11 |
0 | 0 | #!/usr/bin/make -f |
1 | 1 | |
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 | |
4 | 3 | |
5 | 4 | %: |
6 | 5 | dh $@ --with=gir,python3 |
7 | 6 | |
8 | 7 | 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 \ | |
13 | 10 | -D docs=true \ |
14 | 11 | -D deprecated_warnings=false |
15 | 12 | |
16 | 13 | override_dh_strip: |
17 | 14 | 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 |
10 | 10 | main_xml: 'xapp-docs.xml', |
11 | 11 | scan_args: ['--rebuild-types'], |
12 | 12 | 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 | ) |
15 | 15 | |
16 | 16 | <chapter> |
17 | 17 | <title>API reference</title> |
18 | <xi:include href="xml/xapp-favorites.xml"/> | |
18 | 19 | <xi:include href="xml/xapp-gtk-window.xml"/> |
19 | 20 | <xi:include href="xml/xapp-icon-chooser-button.xml"/> |
20 | 21 | <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 | #!/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 | ]) |
0 | 0 | glib_min_ver = '>=2.37.3' |
1 | 1 | |
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 | ||
2 | 6 | 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 | |
6 | 10 | libdeps += dependency('gdk-pixbuf-2.0', version: '>=2.22.0', required: true) |
7 | 11 | libdeps += dependency('cairo', required: true) |
8 | 12 | libdeps += dependency('x11', required: true) |
9 | 13 | |
14 | favorite_vfs_sources = [ | |
15 | 'favorite-vfs-file.c', | |
16 | 'favorite-vfs-file-enumerator.c', | |
17 | 'favorite-vfs-file-monitor.c' | |
18 | ] | |
19 | ||
10 | 20 | xapp_headers = [ |
21 | 'xapp-favorites.h', | |
11 | 22 | 'xapp-gtk-window.h', |
12 | 23 | 'xapp-icon-chooser-button.h', |
13 | 24 | 'xapp-icon-chooser-dialog.h', |
20 | 31 | ] |
21 | 32 | |
22 | 33 | xapp_sources = [ |
34 | 'xapp-favorites.c', | |
23 | 35 | 'xapp-glade-catalog.c', |
24 | 36 | 'xapp-gtk-window.c', |
25 | 37 | 'xapp-icon-chooser-button.c', |
82 | 94 | ) |
83 | 95 | |
84 | 96 | 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, | |
86 | 98 | include_directories: [top_inc], |
87 | 99 | version: meson.project_version(), |
88 | 100 | soversion: '1', |
133 | 145 | metadata_dirs: meson.current_source_dir(), |
134 | 146 | install: true |
135 | 147 | ) |
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 (¶m_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 | } |
16 | 16 | #include "xapp-status-icon-monitor.h" |
17 | 17 | #include "xapp-statusicon-interface.h" |
18 | 18 | |
19 | #define MONITOR_PATH "/org/x/StatusIconMonitor" | |
20 | 19 | #define MONITOR_NAME "org.x.StatusIconMonitor" |
21 | 20 | |
22 | 21 | #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 | ||
28 | 27 | #define WATCHER_MAX_RESTARTS 2 |
29 | 28 | |
30 | 29 | enum |
58 | 57 | { |
59 | 58 | GDBusConnection *connection; |
60 | 59 | |
61 | GHashTable *icons; | |
62 | gchar *name; | |
60 | GHashTable *object_managers; | |
63 | 61 | |
64 | 62 | guint owner_id; |
65 | 63 | guint listener_id; |
66 | guint sn_watcher_id; | |
67 | ||
68 | guint sn_watcher_retry_count; | |
69 | ||
70 | 64 | } XAppStatusIconMonitorPrivate; |
71 | 65 | |
72 | 66 | struct _XAppStatusIconMonitor |
76 | 70 | |
77 | 71 | G_DEFINE_TYPE_WITH_PRIVATE (XAppStatusIconMonitor, xapp_status_icon_monitor, G_TYPE_OBJECT) |
78 | 72 | |
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; | |
90 | 81 | |
91 | 82 | g_object_get (object, |
92 | "g-name-owner", &name_owner, | |
93 | "g-name", &proxy_name, | |
83 | "name-owner", &owner, | |
84 | "name", &name, | |
94 | 85 | NULL); |
95 | 86 | |
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 | ||
147 | 139 | GError *error; |
148 | gchar *g_name; | |
140 | gchar *name; | |
149 | 141 | |
150 | 142 | error = NULL; |
151 | 143 | |
152 | proxy = xapp_status_icon_interface_proxy_new_finish (res, | |
153 | &error); | |
144 | obj_mgr = xapp_object_manager_client_new_finish (res, | |
145 | &error); | |
154 | 146 | |
155 | 147 | if (error) |
156 | 148 | { |
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); | |
158 | 150 | g_error_free (error); |
159 | 151 | return; |
160 | 152 | } |
161 | 153 | |
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); | |
200 | 210 | } |
201 | 211 | else |
202 | 212 | { |
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); | |
205 | 217 | } |
206 | 218 | |
207 | 219 | static void |
235 | 247 | /* the '.' at the end so we don't catch ourselves in this */ |
236 | 248 | if (g_str_has_prefix (str, STATUS_ICON_MATCH)) |
237 | 249 | { |
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); | |
240 | 252 | } |
241 | 253 | } |
242 | 254 | |
249 | 261 | { |
250 | 262 | XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self); |
251 | 263 | |
252 | ||
253 | g_debug("XAppStatusIconMonitor: looking for status icons on the bus"); | |
264 | g_debug("XAppStatusIconMonitor: looking for status icon apps on the bus"); | |
254 | 265 | |
255 | 266 | /* If there are no monitors (applets) already running when this is set up, |
256 | 267 | * this won't find anything. The XAppStatusIcons will be in fallback mode, |
289 | 300 | } |
290 | 301 | |
291 | 302 | 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 | |
328 | 303 | name_owner_changed (GDBusConnection *connection, |
329 | 304 | const gchar *sender_name, |
330 | 305 | const gchar *object_path, |
343 | 318 | |
344 | 319 | g_variant_get (parameters, "(&s&s&s)", &name, &old_owner, &new_owner); |
345 | 320 | |
346 | if (old_owner[0] != '\0') | |
347 | { | |
348 | status_icon_name_vanished (self, name); | |
349 | } | |
350 | ||
351 | 321 | if (new_owner[0] != '\0') |
352 | 322 | { |
353 | status_icon_name_appeared (self, name, new_owner); | |
354 | } | |
355 | } | |
356 | ||
323 | add_object_manager_for_name (self, name); | |
324 | } | |
325 | } | |
357 | 326 | |
358 | 327 | static void |
359 | 328 | add_name_listener (XAppStatusIconMonitor *self) |
360 | 329 | { |
361 | 330 | XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self); |
362 | 331 | |
363 | g_debug ("XAppStatusIconMonitor: Adding NameOwnerChanged listener for status icons"); | |
332 | g_debug ("XAppStatusIconMonitor: Adding NameOwnerChanged listener for status icon apps"); | |
364 | 333 | |
365 | 334 | priv->listener_id = g_dbus_connection_signal_subscribe (priv->connection, |
366 | 335 | "org.freedesktop.DBus", |
392 | 361 | { |
393 | 362 | XAppStatusIconMonitor *self = XAPP_STATUS_ICON_MONITOR (user_data); |
394 | 363 | |
395 | g_debug ("XAppStatusIconMonitor: name acquired on dbus"); | |
364 | g_debug ("XAppStatusIconMonitor: Name owned on bus: %s", name); | |
396 | 365 | |
397 | 366 | add_name_listener (self); |
398 | 367 | find_and_add_icons (self); |
407 | 376 | XAppStatusIconMonitor *self = XAPP_STATUS_ICON_MONITOR (user_data); |
408 | 377 | XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self); |
409 | 378 | |
410 | g_debug ("XAppStatusIconMonitor: session bus connection acquired"); | |
379 | g_debug ("XAppStatusIconMonitor: Connected to bus: %s", name); | |
411 | 380 | |
412 | 381 | priv->connection = connection; |
413 | 382 | } |
416 | 385 | connect_to_bus (XAppStatusIconMonitor *self) |
417 | 386 | { |
418 | 387 | XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self); |
419 | ||
388 | gchar *valid_app_name, *owned_name; | |
420 | 389 | static gint unique_id = 0; |
421 | 390 | |
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); | |
427 | 396 | |
428 | 397 | priv->owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, |
429 | owner_name, | |
398 | owned_name, | |
430 | 399 | G_DBUS_CONNECTION_FLAGS_NONE, |
431 | 400 | on_bus_acquired, |
432 | 401 | on_name_acquired, |
434 | 403 | self, |
435 | 404 | NULL); |
436 | 405 | |
437 | g_free(owner_name); | |
406 | g_free(owned_name); | |
438 | 407 | } |
439 | 408 | |
440 | 409 | static void |
442 | 411 | { |
443 | 412 | XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (self); |
444 | 413 | |
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); | |
451 | 416 | |
452 | 417 | connect_to_bus (self); |
453 | 418 | } |
468 | 433 | priv->listener_id = 0; |
469 | 434 | } |
470 | 435 | |
436 | if (priv->object_managers != NULL) | |
437 | { | |
438 | g_hash_table_unref (priv->object_managers); | |
439 | priv->object_managers = NULL; | |
440 | } | |
441 | ||
471 | 442 | if (priv->owner_id > 0) |
472 | 443 | { |
473 | 444 | g_bus_unown_name(priv->owner_id); |
474 | 445 | priv->owner_id = 0; |
475 | 446 | } |
476 | 447 | |
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 | ||
483 | 448 | g_clear_object (&priv->connection); |
484 | 449 | } |
485 | ||
486 | g_free (priv->name); | |
487 | g_clear_pointer (&priv->icons, g_hash_table_unref); | |
488 | 450 | |
489 | 451 | G_OBJECT_CLASS (xapp_status_icon_monitor_parent_class)->dispose (object); |
490 | 452 | } |
538 | 500 | G_TYPE_NONE, 1, XAPP_TYPE_STATUS_ICON_INTERFACE_PROXY); |
539 | 501 | } |
540 | 502 | |
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 | ||
541 | 528 | /** |
542 | 529 | * xapp_status_icon_monitor_list_icons: |
543 | 530 | * @monitor: a #XAppStatusIconMonitor |
552 | 539 | xapp_status_icon_monitor_list_icons (XAppStatusIconMonitor *monitor) |
553 | 540 | { |
554 | 541 | g_return_val_if_fail (XAPP_IS_STATUS_ICON_MONITOR (monitor), NULL); |
542 | GList *ret = NULL; | |
555 | 543 | |
556 | 544 | XAppStatusIconMonitorPrivate *priv = xapp_status_icon_monitor_get_instance_private (monitor); |
557 | 545 | |
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; | |
559 | 551 | } |
560 | 552 | |
561 | 553 | /** |
19 | 19 | #define FDO_DBUS_NAME "org.freedesktop.DBus" |
20 | 20 | #define FDO_DBUS_PATH "/org/freedesktop/DBus" |
21 | 21 | |
22 | #define ICON_PATH "/org/x/StatusIcon" | |
22 | #define ICON_BASE_PATH "/org/x/StatusIcon" | |
23 | #define ICON_SUB_PATH (ICON_BASE_PATH "/Icon") | |
23 | 24 | #define ICON_NAME "org.x.StatusIcon" |
24 | 25 | |
25 | 26 | #define STATUS_ICON_MONITOR_MATCH "org.x.StatusIconMonitor" |
29 | 30 | #define MAX_SANE_ICON_SIZE 96 |
30 | 31 | #define FALLBACK_ICON_SIZE 24 |
31 | 32 | |
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; | |
33 | 40 | |
34 | 41 | enum |
35 | 42 | { |
49 | 56 | PROP_PRIMARY_MENU, |
50 | 57 | PROP_SECONDARY_MENU, |
51 | 58 | PROP_ICON_SIZE, |
59 | PROP_NAME, | |
52 | 60 | N_PROPERTIES |
53 | 61 | }; |
54 | 62 | |
66 | 74 | */ |
67 | 75 | typedef struct |
68 | 76 | { |
69 | XAppStatusIconInterface *skeleton; | |
70 | 77 | GDBusConnection *connection; |
78 | XAppStatusIconInterface *interface_skeleton; | |
79 | XAppObjectSkeleton *object_skeleton; | |
71 | 80 | |
72 | 81 | GCancellable *cancellable; |
73 | 82 | |
85 | 94 | gint icon_size; |
86 | 95 | gchar *metadata; |
87 | 96 | |
88 | guint owner_id; | |
89 | 97 | guint listener_id; |
90 | 98 | |
91 | 99 | gint fail_counter; |
102 | 110 | |
103 | 111 | static void refresh_icon (XAppStatusIcon *self); |
104 | 112 | 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); | |
106 | 114 | |
107 | 115 | static void |
108 | 116 | cancellable_reset (XAppStatusIcon *self) |
265 | 273 | |
266 | 274 | if (icon->priv->state == XAPP_STATUS_ICON_STATE_NATIVE) |
267 | 275 | { |
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); | |
269 | 277 | } |
270 | 278 | |
271 | 279 | g_signal_handlers_disconnect_by_func (widget, primary_menu_unmapped, icon); |
282 | 290 | |
283 | 291 | if (icon->priv->state == XAPP_STATUS_ICON_STATE_NATIVE) |
284 | 292 | { |
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); | |
286 | 294 | } |
287 | 295 | |
288 | 296 | g_signal_handlers_disconnect_by_func (widget, secondary_menu_unmapped, icon); |
327 | 335 | { |
328 | 336 | if (self->priv->state == XAPP_STATUS_ICON_STATE_NATIVE) |
329 | 337 | { |
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); | |
331 | 339 | } |
332 | 340 | |
333 | 341 | g_signal_connect (gtk_widget_get_toplevel (GTK_WIDGET (menu)), |
340 | 348 | { |
341 | 349 | if (self->priv->state == XAPP_STATUS_ICON_STATE_NATIVE) |
342 | 350 | { |
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); | |
344 | 352 | } |
345 | 353 | |
346 | 354 | g_signal_connect (gtk_widget_get_toplevel (GTK_WIDGET (menu)), |
728 | 736 | |
729 | 737 | g_clear_object (&self->priv->gtk_status_icon); |
730 | 738 | |
731 | g_object_set (G_OBJECT (priv->skeleton), | |
739 | g_object_set (G_OBJECT (priv->interface_skeleton), | |
732 | 740 | "name", priv->name, |
733 | 741 | "label", priv->label, |
734 | 742 | "icon-name", priv->icon_name, |
737 | 745 | "metadata", priv->metadata, |
738 | 746 | NULL); |
739 | 747 | |
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)); | |
741 | 749 | } |
742 | 750 | |
743 | 751 | static void |
769 | 777 | { "handle-scroll", handle_scroll_method } |
770 | 778 | }; |
771 | 779 | |
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 | ||
772 | 812 | static gboolean |
773 | 813 | export_icon_interface (XAppStatusIcon *self) |
774 | 814 | { |
775 | GError *error = NULL; | |
776 | 815 | 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 | { | |
780 | 821 | return TRUE; |
781 | 822 | } |
782 | 823 | |
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); | |
802 | 835 | |
803 | 836 | for (i = 0; i < G_N_ELEMENTS (skeleton_signals); i++) { |
804 | 837 | SkeletonSignal sig = skeleton_signals[i]; |
805 | 838 | |
806 | g_signal_connect (self->priv->skeleton, | |
839 | g_signal_connect (self->priv->interface_skeleton, | |
807 | 840 | sig.signal_name, |
808 | 841 | G_CALLBACK (sig.callback), |
809 | 842 | self); |
815 | 848 | static void |
816 | 849 | connect_with_status_applet (XAppStatusIcon *self) |
817 | 850 | { |
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 | } | |
832 | 885 | |
833 | 886 | g_free(owner_name); |
834 | 887 | } |
895 | 948 | |
896 | 949 | g_debug ("XAppStatusIcon: falling back to GtkStatusIcon"); |
897 | 950 | |
898 | tear_down_dbus (self); | |
951 | remove_icon_path_from_bus (self); | |
899 | 952 | |
900 | 953 | // Make sure there wasn't already one |
901 | 954 | g_clear_object (&self->priv->gtk_status_icon); |
970 | 1023 | |
971 | 1024 | if (found && export_icon_interface (self)) |
972 | 1025 | { |
973 | if (self->priv->owner_id > 0) | |
1026 | if (name_owner_id > 0) | |
974 | 1027 | { |
975 | g_debug ("XAppStatusIcon: We already exist on the bus, syncing icon properties"); | |
976 | 1028 | sync_skeleton (self); |
977 | 1029 | } |
978 | 1030 | else |
1064 | 1116 | return; |
1065 | 1117 | } |
1066 | 1118 | |
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); | |
1074 | 1120 | } |
1075 | 1121 | |
1076 | 1122 | static void |
1077 | 1123 | refresh_icon (XAppStatusIcon *self) |
1078 | 1124 | { |
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"); | |
1082 | 1128 | |
1083 | 1129 | cancellable_reset (self); |
1084 | 1130 | |
1110 | 1156 | case PROP_ICON_SIZE: |
1111 | 1157 | XAPP_STATUS_ICON (object)->priv->icon_size = CLAMP (g_value_get_int (value), 0, MAX_SANE_ICON_SIZE); |
1112 | 1158 | 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; | |
1113 | 1171 | default: |
1114 | 1172 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
1115 | 1173 | break; |
1135 | 1193 | case PROP_ICON_SIZE: |
1136 | 1194 | g_value_set_int (value, icon->priv->icon_size); |
1137 | 1195 | break; |
1196 | case PROP_NAME: | |
1197 | g_value_set_string (value, icon->priv->name); | |
1198 | break; | |
1138 | 1199 | default: |
1139 | 1200 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
1140 | 1201 | break; |
1146 | 1207 | { |
1147 | 1208 | self->priv = xapp_status_icon_get_instance_private (self); |
1148 | 1209 | |
1149 | self->priv->name = g_strdup_printf("%s", g_get_application_name()); | |
1210 | self->priv->name = g_strdup (g_get_application_name()); | |
1211 | ||
1150 | 1212 | self->priv->state = XAPP_STATUS_ICON_STATE_NO_SUPPORT; |
1151 | 1213 | self->priv->icon_size = FALLBACK_ICON_SIZE; |
1152 | 1214 | self->priv->icon_name = g_strdup (" "); |
1160 | 1222 | } |
1161 | 1223 | |
1162 | 1224 | static void |
1163 | tear_down_dbus (XAppStatusIcon *self) | |
1225 | remove_icon_path_from_bus (XAppStatusIcon *self) | |
1164 | 1226 | { |
1165 | 1227 | g_return_if_fail (XAPP_IS_STATUS_ICON (self)); |
1166 | 1228 | |
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); | |
1182 | 1241 | } |
1183 | 1242 | } |
1184 | 1243 | |
1208 | 1267 | self->priv->gtk_status_icon = NULL; |
1209 | 1268 | } |
1210 | 1269 | |
1211 | tear_down_dbus (self); | |
1270 | remove_icon_path_from_bus (self); | |
1212 | 1271 | |
1213 | 1272 | if (self->priv->listener_id > 0) |
1214 | 1273 | { |
1215 | 1274 | g_dbus_connection_signal_unsubscribe (self->priv->connection, self->priv->listener_id); |
1275 | self->priv->listener_id = 0; | |
1216 | 1276 | } |
1217 | 1277 | |
1218 | 1278 | g_clear_object (&self->priv->connection); |
1317 | 1377 | G_PARAM_READWRITE)); |
1318 | 1378 | |
1319 | 1379 | /** |
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 | /** | |
1320 | 1397 | * XAppStatusIcon::button-press-event: |
1321 | 1398 | * @icon: The #XAppStatusIcon |
1322 | 1399 | * @x: The absolute x position to use for menu positioning |
1429 | 1506 | return; |
1430 | 1507 | } |
1431 | 1508 | |
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 | ||
1432 | 1518 | g_clear_pointer (&icon->priv->name, g_free); |
1433 | 1519 | icon->priv->name = g_strdup (name); |
1434 | 1520 | |
1435 | 1521 | g_debug ("XAppStatusIcon set_name: %s", name); |
1436 | 1522 | |
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); | |
1440 | 1526 | } |
1441 | 1527 | |
1442 | 1528 | /* Call this directly instead of in the update_fallback_icon() function, |
1473 | 1559 | |
1474 | 1560 | g_debug ("XAppStatusIcon set_icon_name: %s", icon_name); |
1475 | 1561 | |
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); | |
1479 | 1565 | } |
1480 | 1566 | |
1481 | 1567 | update_fallback_icon (icon); |
1496 | 1582 | { |
1497 | 1583 | g_return_val_if_fail (XAPP_IS_STATUS_ICON (icon), FALLBACK_ICON_SIZE); |
1498 | 1584 | |
1499 | if (icon->priv->skeleton == NULL) | |
1585 | if (icon->priv->interface_skeleton == NULL) | |
1500 | 1586 | { |
1501 | 1587 | g_debug ("XAppStatusIcon get_icon_size: %d (fallback)", FALLBACK_ICON_SIZE); |
1502 | 1588 | |
1505 | 1591 | |
1506 | 1592 | gint size; |
1507 | 1593 | |
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); | |
1509 | 1595 | |
1510 | 1596 | g_debug ("XAppStatusIcon get_icon_size: %d", size); |
1511 | 1597 | |
1536 | 1622 | |
1537 | 1623 | g_debug ("XAppStatusIcon set_tooltip_text: %s", tooltip_text); |
1538 | 1624 | |
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); | |
1542 | 1628 | } |
1543 | 1629 | |
1544 | 1630 | update_fallback_icon (icon); |
1568 | 1654 | |
1569 | 1655 | g_debug ("XAppStatusIcon set_label: '%s'", label); |
1570 | 1656 | |
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); | |
1574 | 1660 | } |
1575 | 1661 | } |
1576 | 1662 | |
1597 | 1683 | |
1598 | 1684 | g_debug ("XAppStatusIcon set_visible: %s", visible ? "TRUE" : "FALSE"); |
1599 | 1685 | |
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); | |
1603 | 1689 | } |
1604 | 1690 | |
1605 | 1691 | update_fallback_icon (icon); |
1782 | 1868 | } |
1783 | 1869 | |
1784 | 1870 | /** |
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 | /** | |
1785 | 1888 | * xapp_status_icon_get_state: |
1786 | 1889 | * @icon: an #XAppStatusIcon |
1787 | 1890 | * |
1834 | 1937 | icon->priv->metadata = g_strdup (metadata); |
1835 | 1938 | g_free (old_meta); |
1836 | 1939 | |
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); | |
1840 | 1943 | } |
1841 | 1944 | } |
1842 | 1945 |
46 | 46 | |
47 | 47 | |
48 | 48 | XAppStatusIcon *xapp_status_icon_new (void); |
49 | XAppStatusIcon *xapp_status_icon_new_with_name (const gchar *name); | |
49 | 50 | void xapp_status_icon_set_name (XAppStatusIcon *icon, const gchar *name); |
50 | 51 | void xapp_status_icon_set_icon_name (XAppStatusIcon *icon, const gchar *icon_name); |
51 | 52 | gint xapp_status_icon_get_icon_size (XAppStatusIcon *icon); |
26 | 26 | '--interface-prefix=' + sys.argv[1], |
27 | 27 | '--generate-c-code=' + os.path.join(sys.argv[4], sys.argv[2]), |
28 | 28 | '--c-namespace=XApp', |
29 | '--c-generate-object-manager', | |
29 | 30 | '--annotate', sys.argv[1], 'org.gtk.GDBus.C.Name', sys.argv[3], |
30 | 31 | sys.argv[5] |
31 | 32 | ]) |
0 | 0 | project('xapp', |
1 | 1 | 'c', |
2 | version : '1.8.9' | |
2 | version : '2.0.0' | |
3 | 3 | ) |
4 | 4 | |
5 | 5 | gnome = import('gnome') |
7 | 7 | i18n = import('i18n') |
8 | 8 | |
9 | 9 | 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')]) | |
11 | 11 | libexec_path = join_paths(get_option('prefix'), get_option('libexecdir'), 'xapps', 'sn-watcher') |
12 | 12 | |
13 | 13 | cdata = configuration_data() |
49 | 49 | subdir('icons') |
50 | 50 | subdir('status-applets') |
51 | 51 | subdir('scripts') |
52 | subdir('data') | |
52 | 53 | endif |
53 | 54 | |
54 | 55 | if get_option('status-notifier') and not app_lib_only |
1 | 1 | <schemalist> |
2 | 2 | <schema id="org.x.apps" path="/org/x/apps/"> |
3 | 3 | <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> | |
4 | 16 | </schema> |
5 | 17 | |
6 | 18 | <schema id="org.x.apps.statusicon" path="/org/x/apps/statusicon/"> |
0 | 0 | #!/bin/bash |
1 | 1 | |
2 | 2 | # Author: Weitian Leung <weitianleung@gmail.com> |
3 | # Version: 2.0 | |
3 | # Version: 2.1 | |
4 | 4 | # License: GPL-3.0 |
5 | 5 | # Description: set a picture as xfce4 wallpaper |
6 | 6 | |
7 | wallpaper=$1 | |
7 | # xfce4-desktop requires an absolute path. | |
8 | wallpaper="$(realpath "$1")" | |
8 | 9 | |
9 | 10 | # check image |
10 | 11 | mime_type=`file --mime-type -b "$wallpaper"` |
371 | 371 | self.monitor.connect("icon-added", self.on_icon_added) |
372 | 372 | self.monitor.connect("icon-removed", self.on_icon_removed) |
373 | 373 | |
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 | ||
374 | 381 | def destroy_monitor (self): |
375 | 382 | for key in self.indicators.keys(): |
376 | 383 | self.indicator_box.remove(self.indicators[key]) |
379 | 386 | self.indicators = {} |
380 | 387 | |
381 | 388 | 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) | |
387 | 394 | |
388 | 395 | self.sort_icons() |
389 | 396 | |
390 | 397 | 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]) | |
396 | 403 | |
397 | 404 | self.sort_icons() |
398 | 405 | |
440 | 447 | # for i in icon_list: |
441 | 448 | # print("before: ", i.proxy.props.icon_name, i.proxy.props.name.lower()) |
442 | 449 | |
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()) | |
444 | 451 | icon_list.sort(key=lambda icon: icon.proxy.props.icon_name.lower().endswith("symbolic")) |
445 | 452 | |
446 | 453 | # 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() |
166 | 166 | self.disco_button.set_label("Connect monitor") |
167 | 167 | |
168 | 168 | def on_icon_added(self, monitor, proxy): |
169 | name = proxy.get_name() | |
169 | name = proxy.get_name() + proxy.get_object_path() | |
170 | 170 | |
171 | 171 | self.indicators[name] = StatusWidget(proxy) |
172 | 172 | self.indicator_box.add(self.indicators[name]) |
175 | 175 | self.sort_icons() |
176 | 176 | |
177 | 177 | def on_icon_removed(self, monitor, proxy): |
178 | name = proxy.get_name() | |
178 | name = proxy.get_name() + proxy.get_object_path() | |
179 | 179 | |
180 | 180 | self.indicator_box.remove(self.indicators[name]) |
181 | 181 | self.indicators[name].disconnect_by_func(self.sort_icons) |
189 | 189 | # for i in icon_list: |
190 | 190 | # print("before: ", i.proxy.props.icon_name, i.proxy.props.name.lower()) |
191 | 191 | |
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()) | |
193 | 193 | icon_list.sort(key=lambda icon: icon.proxy.props.icon_name.lower().endswith("symbolic")) |
194 | 194 | |
195 | 195 | # for i in icon_list: |
392 | 392 | { |
393 | 393 | surface = new_props->attention_icon_surface; |
394 | 394 | } |
395 | else | |
396 | if (new_props->icon_surface) | |
397 | { | |
398 | surface = new_props->icon_surface; | |
399 | } | |
395 | 400 | } |
396 | 401 | |
397 | 402 | if (surface != NULL) |
422 | 427 | } |
423 | 428 | |
424 | 429 | 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) | |
428 | 433 | { |
429 | 434 | GtkIconInfo *info; |
430 | 435 | gchar *filename; |
434 | 439 | array[0] = icon_name; |
435 | 440 | array[1] = NULL; |
436 | 441 | |
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 | |
438 | 443 | GtkIconTheme *theme = gtk_icon_theme_get_default (); |
439 | 444 | host_icon_size = get_icon_size (item); |
440 | 445 | |
444 | 449 | lookup_ui_scale (), |
445 | 450 | GTK_ICON_LOOKUP_FORCE_SVG | GTK_ICON_LOOKUP_FORCE_SYMBOLIC); |
446 | 451 | |
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) | |
448 | 461 | { |
449 | 462 | // Make a temp theme based off of the provided path |
450 | 463 | GtkIconTheme *theme = gtk_icon_theme_new (); |
476 | 489 | const gchar *icon_theme_path, |
477 | 490 | const gchar *icon_name) |
478 | 491 | { |
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); | |
481 | 496 | |
482 | 497 | if (icon_name == NULL) |
483 | 498 | { |
487 | 502 | if (g_path_is_absolute (icon_name)) |
488 | 503 | { |
489 | 504 | xapp_status_icon_set_icon_name (item->status_icon, icon_name); |
490 | ||
491 | 505 | return TRUE; |
492 | 506 | } |
493 | 507 | else |
494 | 508 | { |
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); | |
501 | 515 | return TRUE; |
502 | 516 | } |
503 | 517 | } |
511 | 525 | { |
512 | 526 | const gchar *name_to_use = NULL; |
513 | 527 | |
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) | |
515 | 532 | { |
516 | 533 | if (new_props->icon_name) |
517 | 534 | { |
716 | 733 | { |
717 | 734 | if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
718 | 735 | { |
719 | g_critical ("Could get propertyies for %s: %s\n", | |
736 | g_critical ("Could not get properties for %s: %s\n", | |
720 | 737 | g_dbus_proxy_get_name (item->sn_item_proxy), |
721 | 738 | error->message); |
722 | 739 | } |
887 | 904 | update_menu (item, new_props); |
888 | 905 | } |
889 | 906 | |
890 | if (new_props->update_icon) | |
907 | if (new_props->update_icon || new_props->update_status) | |
891 | 908 | { |
892 | 909 | update_icon (item, new_props); |
893 | 910 | } |
307 | 307 | } NewSnProxyData; |
308 | 308 | |
309 | 309 | 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 | |
310 | 322 | sn_item_proxy_new_completed (GObject *source, |
311 | 323 | GAsyncResult *res, |
312 | 324 | gpointer user_data) |
321 | 333 | |
322 | 334 | proxy = sn_item_interface_proxy_new_finish (res, &error); |
323 | 335 | |
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 | ||
324 | 354 | if (error != NULL) |
325 | 355 | { |
326 | 356 | if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
329 | 359 | data->bus_name, error->message); |
330 | 360 | } |
331 | 361 | |
332 | g_hash_table_steal_extended (watcher->items, | |
333 | data->key, | |
334 | &stolen_ptr, | |
335 | NULL); | |
336 | 362 | g_free (stolen_ptr); |
363 | free_sn_proxy_data (data); | |
337 | 364 | |
338 | 365 | g_dbus_method_invocation_take_error (data->invocation, error); |
339 | 366 | return; |
341 | 368 | |
342 | 369 | item = sn_item_new ((GDBusProxy *) proxy, |
343 | 370 | 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); | |
349 | 371 | |
350 | 372 | g_hash_table_insert (watcher->items, |
351 | 373 | stolen_ptr, |
359 | 381 | sn_watcher_interface_complete_register_status_notifier_item (watcher->skeleton, |
360 | 382 | data->invocation); |
361 | 383 | |
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); | |
368 | 385 | } |
369 | 386 | |
370 | 387 | static gboolean |
493 | 510 | } |
494 | 511 | |
495 | 512 | 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 | |
496 | 533 | watcher_startup (GApplication *application) |
497 | 534 | { |
498 | 535 | XAppSnWatcher *watcher = (XAppSnWatcher*) application; |
503 | 540 | xapp_settings = g_settings_new (STATUS_ICON_SCHEMA); |
504 | 541 | |
505 | 542 | 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); | |
507 | 544 | |
508 | 545 | /* This buys us 30 seconds (gapp timeout) - we'll either be re-held immediately |
509 | 546 | * because there's a monitor or exit after the 30 seconds. */ |
7 | 7 | msgstr "" |
8 | 8 | "Project-Id-Version: PACKAGE VERSION\n" |
9 | 9 | "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" | |
11 | 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
12 | 12 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
13 | 13 | "Language-Team: LANGUAGE <LL@li.org>\n" |
15 | 15 | "MIME-Version: 1.0\n" |
16 | 16 | "Content-Type: text/plain; charset=CHARSET\n" |
17 | 17 | "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 "" | |
18 | 31 | |
19 | 32 | #: libxapp/xapp-icon-chooser-button.c:207 |
20 | 33 | #: libxapp/xapp-icon-chooser-dialog.c:639 |
139 | 152 | msgid "Image" |
140 | 153 | msgstr "" |
141 | 154 | |
142 | #: status-applets/mate/mate-xapp-status-applet.py:295 | |
155 | #: status-applets/mate/mate-xapp-status-applet.py:330 | |
143 | 156 | msgid "About" |
144 | 157 | msgstr "" |
145 | 158 | |
146 | #: status-applets/mate/mate-xapp-status-applet.py:315 | |
159 | #: status-applets/mate/mate-xapp-status-applet.py:350 | |
147 | 160 | #: status-applets/mate/org.x.MateXAppStatusApplet.mate-panel-applet.in.in:7 |
148 | 161 | #: status-applets/mate/org.x.MateXAppStatusApplet.mate-panel-applet.in.in:11 |
149 | 162 | msgid "Area where XApp status icons appear" |