New upstream version 1.4.2
Norbert Preining
5 years ago
0 | xapp (1.4.2) tessa; urgency=medium | |
1 | ||
2 | [ Michael Webster ] | |
3 | * XAppGtkWindow: make derivable type. | |
4 | * xapp-icon-chooser-dialog: use XAppGtkWindow as a parent class, make the default window size slightly taller to prevent a category scroll bar under typical situations, remove the skip-taskbar hint in the test script. | |
5 | * xapp-gtk-window.c: Fix previous commit, initialize priv. | |
6 | * xapp-icon-chooser-dialog.c: Give the window the title 'Choose an icon'. | |
7 | * various improvements (#61) | |
8 | ||
9 | [ Clement Lefebvre ] | |
10 | * l10n: Update POT | |
11 | ||
12 | [ Michael Webster ] | |
13 | * xapp-icon-chooser-dialog.c: Only display icons that belong to contexts. | |
14 | ||
15 | -- Clement Lefebvre <root@linuxmint.com> Mon, 19 Nov 2018 10:03:21 +0000 | |
16 | ||
17 | xapp (1.4.1) tessa; urgency=medium | |
18 | ||
19 | [ Stephen Collins ] | |
20 | * icon chooser dialog: Prevent the dialog from being destroyed when the window close button is clicked. This was causing an issue where the dialog will not reopen in some cases. (#56) | |
21 | * icon chooser button: fix image not updating when the icon property is set (#57) | |
22 | * icon chooser dialog: initialize the default value for the allow_paths property. it was causing file paths to not work when the property wasn't set, even though the browse button was showing (#59) | |
23 | * icon chooser button: prevent string pointer issues when calling icon_… (#58) | |
24 | * icon chooser button: enforce image size when using paths | |
25 | ||
26 | [ Michael Webster ] | |
27 | * xapp-icon-chooser-button.h: add missing public method and fix annotation for it. | |
28 | ||
29 | -- Clement Lefebvre <root@linuxmint.com> Mon, 12 Nov 2018 15:13:01 +0000 | |
30 | ||
31 | xapp (1.4.0) tessa; urgency=medium | |
32 | ||
33 | [ JosephMcc ] | |
34 | * Add an XAppStackSidebar widget (#45) | |
35 | * stack-sidear: Simplify a bit | |
36 | * stack-sidebar: Allow using in Glade | |
37 | ||
38 | [ Michael Webster ] | |
39 | * xapp-stack-sidebar.c: Set our internal children visible during construction. | |
40 | ||
41 | [ JosephMcc ] | |
42 | * icons: Add new icons for the nemo preferences window | |
43 | * prefs-window: Use XAppStackSidebar | |
44 | ||
45 | [ Stephen Collins ] | |
46 | * Add an icon chooser dialog (#51) | |
47 | ||
48 | [ JosephMcc ] | |
49 | * icon-chooser: Do a bit of restyling (#52) | |
50 | ||
51 | [ Fabio Fantoni ] | |
52 | * Improve debian packaging (#42) | |
53 | ||
54 | [ Stephen Collins ] | |
55 | * fix segfault in icon chooser dialog and ensure a category is always selected (#54) | |
56 | * Add an icon chooser button. Displays an image of the icon and opens the icon chooser dialog on click. (#53) | |
57 | ||
58 | [ JosephMcc ] | |
59 | * prefs-window: Restyle to better match other dialogs (#55) | |
60 | ||
61 | [ Michael Webster ] | |
62 | * xapp-icon-chooser-dialog: include the generated enums header in the .c file so it does not break C application compilation. | |
63 | * xapp-icon-chooser-dialog: Add xapp_icon_chooser_dialog_add_button(). | |
64 | ||
65 | [ Clement Lefebvre ] | |
66 | * l10n: Add makepot and de-couple POT from build system | |
67 | ||
68 | -- Clement Lefebvre <root@linuxmint.com> Tue, 30 Oct 2018 15:26:24 +0000 | |
69 | ||
0 | 70 | xapp (1.2.2) tara; urgency=medium |
1 | 71 | |
2 | 72 | [ Fabio Fantoni ] |
5 | 5 | debhelper (>= 10), |
6 | 6 | dh-python, |
7 | 7 | dpkg-dev (>= 1.15.1), |
8 | gnome-doc-utils (>= 0.3.2), | |
9 | gnome-pkg-tools (>= 0.10), | |
10 | 8 | gobject-introspection (>= 0.10.2-1~), |
11 | 9 | gtk-doc-tools (>= 1.4), |
12 | 10 | intltool (>= 0.40.6), |
22 | 20 | python-gi-dev, |
23 | 21 | python3, |
24 | 22 | valac, |
25 | yelp-tools, | |
26 | 23 | Standards-Version: 3.9.6 |
27 | 24 | |
28 | 25 | Package: gir1.2-xapp-1.0 |
52 | 49 | Architecture: any |
53 | 50 | Depends: |
54 | 51 | gir1.2-xapp-1.0 (= ${binary:Version}), |
52 | libgnomekbd-dev, | |
55 | 53 | libgtk-3-dev (>= 3.3.16), |
56 | 54 | libxapp1 (= ${binary:Version}), |
57 | 55 | libxkbfile-dev, |
2 | 2 | usr/lib/*/pkgconfig/xapp.pc |
3 | 3 | usr/share/gir-1.0/XApp-1.0.gir |
4 | 4 | usr/share/glade/catalogs/xapp-glade-catalog.xml |
5 | usr/share/vala/vapi/xapp.deps | |
5 | 6 | usr/share/vala/vapi/xapp.vapi |
15 | 15 | |
16 | 16 | <chapter> |
17 | 17 | <title>API reference</title> |
18 | <xi:include href="xml/xapp-gtk-window.xml"/> | |
19 | <xi:include href="xml/xapp-icon-chooser-button.xml"/> | |
20 | <xi:include href="xml/xapp-icon-chooser-dialog.xml"/> | |
18 | 21 | <xi:include href="xml/xapp-kbd-layout-controller.xml"/> |
19 | 22 | <xi:include href="xml/xapp-monitor-blanker.xml"/> |
20 | <xi:include href="xml/xapp-gtk-window.xml"/> | |
21 | 23 | <xi:include href="xml/xapp-preferences-window.xml"/> |
24 | <xi:include href="xml/xapp-stack-sidebar.xml"/> | |
22 | 25 | |
23 | 26 | </chapter> |
24 | 27 | <chapter id="object-tree"> |
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 | width="16" | |
10 | height="16" | |
11 | viewBox="0 0 16 16" | |
12 | version="1.1" | |
13 | id="svg7" | |
14 | sodipodi:docname="nemo-prefs-behavior.svg" | |
15 | inkscape:version="0.92.3 (2405546, 2018-03-11)"> | |
16 | <metadata | |
17 | id="metadata11"> | |
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 | <sodipodi:namedview | |
29 | pagecolor="#ffffff" | |
30 | bordercolor="#666666" | |
31 | borderopacity="1" | |
32 | objecttolerance="10" | |
33 | gridtolerance="10" | |
34 | guidetolerance="10" | |
35 | inkscape:pageopacity="0" | |
36 | inkscape:pageshadow="2" | |
37 | inkscape:window-width="1177" | |
38 | inkscape:window-height="839" | |
39 | id="namedview9" | |
40 | showgrid="false" | |
41 | inkscape:zoom="14.75" | |
42 | inkscape:cx="7.8983051" | |
43 | inkscape:cy="8" | |
44 | inkscape:window-x="0" | |
45 | inkscape:window-y="0" | |
46 | inkscape:window-maximized="0" | |
47 | inkscape:current-layer="svg7" /> | |
48 | <defs | |
49 | id="defs3"> | |
50 | <style | |
51 | id="current-color-scheme" | |
52 | type="text/css"> | |
53 | .ColorScheme-Text { color:#6e6e6e; } .ColorScheme-Highlight { color:#5294e2; } | |
54 | </style> | |
55 | </defs> | |
56 | <path | |
57 | style="fill:#bebebe;fill-opacity:1" | |
58 | class="ColorScheme-Text" | |
59 | d="M 6.25 1 L 6.0957031 2.84375 A 5.5 5.5 0 0 0 4.4882812 3.7734375 L 2.8125 2.984375 L 1.0625 6.015625 L 2.5839844 7.0722656 A 5.5 5.5 0 0 0 2.5 8 A 5.5 5.5 0 0 0 2.5800781 8.9316406 L 1.0625 9.984375 L 2.8125 13.015625 L 4.484375 12.228516 A 5.5 5.5 0 0 0 6.0957031 13.152344 L 6.2460938 15.001953 L 9.7460938 15.001953 L 9.9003906 13.158203 A 5.5 5.5 0 0 0 11.507812 12.228516 L 13.183594 13.017578 L 14.933594 9.9863281 L 13.412109 8.9296875 A 5.5 5.5 0 0 0 13.496094 8.0019531 A 5.5 5.5 0 0 0 13.416016 7.0703125 L 14.933594 6.0175781 L 13.183594 2.9863281 L 11.511719 3.7734375 A 5.5 5.5 0 0 0 9.9003906 2.8496094 L 9.75 1 L 6.25 1 z M 8 6 A 2 2 0 0 1 10 8 A 2 2 0 0 1 8 10 A 2 2 0 0 1 6 8 A 2 2 0 0 1 8 6 z" | |
60 | id="path5" /> | |
61 | </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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |
8 | xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |
9 | width="16" | |
10 | height="16" | |
11 | viewBox="0 0 16 16" | |
12 | version="1.1" | |
13 | id="svg7" | |
14 | sodipodi:docname="nemo-prefs-display-symbolic.svg" | |
15 | inkscape:version="0.92.3 (2405546, 2018-03-11)"> | |
16 | <metadata | |
17 | id="metadata11"> | |
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 | <sodipodi:namedview | |
29 | pagecolor="#ffffff" | |
30 | bordercolor="#666666" | |
31 | borderopacity="1" | |
32 | objecttolerance="10" | |
33 | gridtolerance="10" | |
34 | guidetolerance="10" | |
35 | inkscape:pageopacity="0" | |
36 | inkscape:pageshadow="2" | |
37 | inkscape:window-width="779" | |
38 | inkscape:window-height="480" | |
39 | id="namedview9" | |
40 | showgrid="false" | |
41 | inkscape:zoom="14.75" | |
42 | inkscape:cx="8" | |
43 | inkscape:cy="8" | |
44 | inkscape:window-x="0" | |
45 | inkscape:window-y="0" | |
46 | inkscape:window-maximized="0" | |
47 | inkscape:current-layer="svg7" /> | |
48 | <defs | |
49 | id="defs3"> | |
50 | <style | |
51 | id="current-color-scheme" | |
52 | type="text/css"> | |
53 | .ColorScheme-Text { color:#6e6e6e; } .ColorScheme-Highlight { color:#5294e2; } | |
54 | </style> | |
55 | </defs> | |
56 | <path | |
57 | style="fill:#bebebe;fill-opacity:1" | |
58 | class="ColorScheme-Text" | |
59 | d="M 8 3.0039062 C 6.4492 3.0190063 4.8879094 3.3732319 3.5371094 4.1386719 C 2.9987094 4.4892919 2.3523344 4.9421175 1.8652344 5.3984375 C 1.0987444 6.1488575 0.4427 7.0244062 0 8.0039062 C 1.2149 10.683506 3.8859187 12.6474 6.8242188 12.9375 C 8.7516188 13.15561 10.768591 12.822631 12.462891 11.869141 C 13.001291 11.518521 13.647666 11.065695 14.134766 10.609375 C 14.901256 9.858955 15.5573 8.9834063 16 8.0039062 C 14.785 5.3245062 12.114181 3.3601125 9.1757812 3.0703125 C 8.7859013 3.0248425 8.39251 3.0038963 8 3.0039062 z M 8 5.0019531 L 8 5.0039062 C 9.607 4.9683062 11.0303 6.4057062 11 8.0039062 C 11.0515 9.7703063 9.2909813 11.294844 7.5507812 10.964844 C 5.7931812 10.758504 4.5587188 8.7851344 5.1367188 7.1152344 C 5.5058788 5.8858344 6.7125 4.9866531 8 5.0019531 z M 8 7.0039062 A 1 1 0 0 0 7 8.0039062 A 1 1 0 0 0 8 9.0039062 A 1 1 0 0 0 9 8.0039062 A 1 1 0 0 0 8 7.0039062 z" | |
60 | id="path5" /> | |
61 | </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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |
8 | xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |
9 | width="16" | |
10 | height="16" | |
11 | viewBox="0 0 16 16" | |
12 | version="1.1" | |
13 | id="svg7" | |
14 | sodipodi:docname="nemo-prefs-[;ugins-symbolic.svg" | |
15 | inkscape:version="0.92.3 (2405546, 2018-03-11)"> | |
16 | <metadata | |
17 | id="metadata11"> | |
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 | <sodipodi:namedview | |
29 | pagecolor="#ffffff" | |
30 | bordercolor="#666666" | |
31 | borderopacity="1" | |
32 | objecttolerance="10" | |
33 | gridtolerance="10" | |
34 | guidetolerance="10" | |
35 | inkscape:pageopacity="0" | |
36 | inkscape:pageshadow="2" | |
37 | inkscape:window-width="779" | |
38 | inkscape:window-height="480" | |
39 | id="namedview9" | |
40 | showgrid="false" | |
41 | inkscape:zoom="14.75" | |
42 | inkscape:cx="8" | |
43 | inkscape:cy="8" | |
44 | inkscape:window-x="0" | |
45 | inkscape:window-y="0" | |
46 | inkscape:window-maximized="0" | |
47 | inkscape:current-layer="svg7" /> | |
48 | <defs | |
49 | id="defs3"> | |
50 | <style | |
51 | id="current-color-scheme" | |
52 | type="text/css"> | |
53 | .ColorScheme-Text { color:#6e6e6e; } .ColorScheme-Highlight { color:#5294e2; } | |
54 | </style> | |
55 | </defs> | |
56 | <path | |
57 | style="fill:#bebebe;fill-opacity:1" | |
58 | class="ColorScheme-Text" | |
59 | d="M 6.5 1 C 5.6977 1 5 1.6468188 5 2.4492188 L 5 4 L 1.9648438 4 C 1.4299438 4 1 4.3264281 1 4.8613281 L 1 8 L 2.4492188 8 C 3.2515187 8 4 8.6977 4 9.5 C 4 10.302 3.2515188 11 2.4492188 11 L 1 11 L 1 14.033203 C 1 14.568203 1.4299437 15 1.9648438 15 L 5 15 L 5 14.033203 C 5 13.231203 5.0175 12.041 6.5 12 C 7.9825 11.959 8 13.291203 8 14.033203 L 8 15 L 11.138672 15 C 11.673672 15 12 14.568203 12 14.033203 L 12 11 L 13.552734 11 C 14.354734 11 15 10.302 15 9.5 C 15 8.6977 14.345734 8.1233 13.552734 8 L 12 8 L 12 4.8613281 C 12 4.3264281 11.673672 4 11.138672 4 L 8 4 L 8 2.4492188 C 8 1.6468187 7.3023 1 6.5 1 z" | |
60 | id="path5" /> | |
61 | </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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |
8 | xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |
9 | width="16" | |
10 | height="16" | |
11 | viewBox="0 0 16 16" | |
12 | version="1.1" | |
13 | id="svg7" | |
14 | sodipodi:docname="nemo-prefs-preview.svg" | |
15 | inkscape:version="0.92.3 (2405546, 2018-03-11)"> | |
16 | <metadata | |
17 | id="metadata11"> | |
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 | <sodipodi:namedview | |
29 | pagecolor="#ffffff" | |
30 | bordercolor="#666666" | |
31 | borderopacity="1" | |
32 | objecttolerance="10" | |
33 | gridtolerance="10" | |
34 | guidetolerance="10" | |
35 | inkscape:pageopacity="0" | |
36 | inkscape:pageshadow="2" | |
37 | inkscape:window-width="1920" | |
38 | inkscape:window-height="1025" | |
39 | id="namedview9" | |
40 | showgrid="false" | |
41 | inkscape:zoom="14.75" | |
42 | inkscape:cx="7.8983051" | |
43 | inkscape:cy="8" | |
44 | inkscape:window-x="0" | |
45 | inkscape:window-y="0" | |
46 | inkscape:window-maximized="1" | |
47 | inkscape:current-layer="svg7" /> | |
48 | <defs | |
49 | id="defs3"> | |
50 | <style | |
51 | id="current-color-scheme" | |
52 | type="text/css"> | |
53 | .ColorScheme-Text { color:#6e6e6e; } .ColorScheme-Highlight { color:#5294e2; } | |
54 | </style> | |
55 | </defs> | |
56 | <path | |
57 | style="fill:#bebebe;fill-opacity:1" | |
58 | class="ColorScheme-Text" | |
59 | d="M 2 2 C 1 2 1 3 1 3 L 1 13 C 1 14 2 14 2 14 L 14 14 C 14 14 15 14 15 13 L 15 3 C 15 2 14 2 14 2 L 2 2 z M 5.5 6.5625 L 8.8125 10.3125 L 10.9375 8.25 L 13 10.3125 L 13 12 L 3 12 L 3 9.5 L 5.5 6.5625 z" | |
60 | id="path5" /> | |
61 | </svg> |
0 | <?xml version="1.0" encoding="UTF-8"?> | |
1 | <!-- Created with Inkscape (http://www.inkscape.org/) --> | |
2 | <svg id="svg7384" width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> | |
3 | <metadata id="metadata90"> | |
4 | <rdf:RDF> | |
5 | <cc:Work rdf:about=""> | |
6 | <dc:format>image/svg+xml</dc:format> | |
7 | <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> | |
8 | <dc:title>Gnome Symbolic Icon Theme</dc:title> | |
9 | </cc:Work> | |
10 | </rdf:RDF> | |
11 | </metadata> | |
12 | <title id="title9167">Gnome Symbolic Icon Theme</title> | |
13 | <g id="layer12" transform="translate(-141 -867)"> | |
14 | <rect id="rect12858" transform="rotate(90)" x="873" y="-157" width="4" height="4" rx=".38462" ry=".50229" style="color:#000000;enable-background:new;fill:#bebebe"/> | |
15 | <rect id="rect12860" transform="rotate(90)" x="873" y="-151" width="4" height="4" rx=".38462" ry=".50229" style="color:#000000;enable-background:new;fill:#bebebe"/> | |
16 | <rect id="rect12862" transform="rotate(90)" x="873" y="-145" width="4" height="4" rx=".38462" ry=".50229" style="color:#000000;enable-background:new;fill:#bebebe"/> | |
17 | </g> | |
18 | </svg> |
10 | 10 | |
11 | 11 | xapp_headers = [ |
12 | 12 | 'xapp-gtk-window.h', |
13 | 'xapp-icon-chooser-button.h', | |
14 | 'xapp-icon-chooser-dialog.h', | |
13 | 15 | 'xapp-kbd-layout-controller.h', |
14 | 16 | 'xapp-monitor-blanker.h', |
15 | 'xapp-preferences-window.h' | |
17 | 'xapp-preferences-window.h', | |
18 | 'xapp-stack-sidebar.h', | |
16 | 19 | ] |
17 | 20 | |
18 | 21 | xapp_sources = [ |
19 | 22 | 'xapp-glade-catalog.c', |
20 | 23 | 'xapp-gtk-window.c', |
24 | 'xapp-icon-chooser-button.c', | |
25 | 'xapp-icon-chooser-dialog.c', | |
21 | 26 | 'xapp-kbd-layout-controller.c', |
22 | 27 | 'xapp-monitor-blanker.c', |
23 | 28 | 'xapp-preferences-window.c', |
29 | 'xapp-stack-sidebar.c', | |
24 | 30 | ] |
25 | 31 | |
32 | xapp_enums = gnome.mkenums('xapp-enums', | |
33 | sources : xapp_headers, | |
34 | c_template : 'xapp-enums.c.template', | |
35 | h_template : 'xapp-enums.h.template', | |
36 | identifier_prefix : 'XApp', | |
37 | symbol_prefix : 'xapp' | |
38 | ) | |
39 | ||
26 | 40 | libxapp = library('xapp', |
27 | sources : xapp_headers + xapp_sources, | |
41 | sources : xapp_headers + xapp_sources + xapp_enums, | |
28 | 42 | include_directories: [top_inc], |
29 | 43 | version: meson.project_version(), |
30 | 44 | soversion: '1', |
31 | 45 | dependencies: libdeps, |
32 | 46 | c_args: ['-Wno-declaration-after-statement'], |
33 | link_args: [ '-Wl,-Bsymbolic', '-Wl,-z,relro', '-Wl,-z,now', ], | |
47 | link_args: [ '-Wl,-Bsymbolic', '-Wl,-z,relro', '-Wl,-z,now', '-lm'], | |
34 | 48 | install: true |
35 | 49 | ) |
36 | 50 | |
74 | 88 | sources: gir[0], |
75 | 89 | metadata_dirs: meson.current_source_dir(), |
76 | 90 | install: true |
77 | )⏎ | |
91 | ) |
0 | /*** BEGIN file-header ***/ | |
1 | ||
2 | #include "config.h" | |
3 | #include "xapp-enums.h" | |
4 | #include "xapp-icon-chooser-dialog.h" | |
5 | ||
6 | #define C_ENUM(v) ((gint) v) | |
7 | #define C_FLAGS(v) ((guint) v) | |
8 | ||
9 | /*** END file-header ***/ | |
10 | ||
11 | /*** BEGIN file-production ***/ | |
12 | ||
13 | /* enumerations from "@basename@" */ | |
14 | ||
15 | /*** END file-production ***/ | |
16 | ||
17 | /*** BEGIN value-header ***/ | |
18 | GType | |
19 | @enum_name@_get_type (void) | |
20 | { | |
21 | static volatile gsize gtype_id = 0; | |
22 | static const G@Type@Value values[] = { | |
23 | /*** END value-header ***/ | |
24 | ||
25 | /*** BEGIN value-production ***/ | |
26 | { C_ENUM(@VALUENAME@), "@VALUENAME@", "@valuenick@" }, | |
27 | /*** END value-production ***/ | |
28 | ||
29 | /*** BEGIN value-tail ***/ | |
30 | { 0, NULL, NULL } | |
31 | }; | |
32 | if (g_once_init_enter (>ype_id)) { | |
33 | GType new_type = g_@type@_register_static ("@EnumName@", values); | |
34 | g_once_init_leave (>ype_id, new_type); | |
35 | } | |
36 | return (GType) gtype_id; | |
37 | } | |
38 | ||
39 | /*** END value-tail ***/ | |
40 | ||
41 | /*** BEGIN file-tail ***/ | |
42 | ||
43 | /*** END file-tail ***/ |
0 | /*** BEGIN file-header ***/ | |
1 | #ifndef __XAPP_ENUMS_H__ | |
2 | #define __XAPP_ENUMS_H__ | |
3 | ||
4 | #include <glib-object.h> | |
5 | ||
6 | G_BEGIN_DECLS | |
7 | /*** END file-header ***/ | |
8 | ||
9 | /*** BEGIN file-production ***/ | |
10 | ||
11 | /* enumerations from "@basename@" */ | |
12 | /*** END file-production ***/ | |
13 | ||
14 | /*** BEGIN value-header ***/ | |
15 | GType @enum_name@_get_type (void); | |
16 | #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) | |
17 | /*** END value-header ***/ | |
18 | ||
19 | /*** BEGIN file-tail ***/ | |
20 | G_END_DECLS | |
21 | ||
22 | #endif /* __XAPP_ENUMS_H__ */ | |
23 | /*** END file-tail ***/ |
1 | 1 | #include <glib-object.h> |
2 | 2 | |
3 | 3 | #include "xapp-gtk-window.h" |
4 | #include "xapp-icon-chooser-button.h" | |
5 | #include "xapp-stack-sidebar.h" | |
4 | 6 | |
5 | 7 | void |
6 | 8 | xapp_glade_catalog_init (const gchar *catalog_name); |
9 | 11 | xapp_glade_catalog_init (const gchar *catalog_name) |
10 | 12 | { |
11 | 13 | g_type_ensure (XAPP_TYPE_GTK_WINDOW); |
14 | g_type_ensure (XAPP_TYPE_ICON_CHOOSER_BUTTON); | |
15 | g_type_ensure (XAPP_TYPE_STACK_SIDEBAR); | |
12 | 16 | } |
3 | 3 | <glade-widget-classes> |
4 | 4 | <glade-widget-class name="XAppGtkWindow" generic-name="xappgtkwindow" |
5 | 5 | title="XAppGtkWindow" parent="GtkWindow" /> |
6 | <glade-widget-class name="XAppIconChooserButton" generic-name="xappiconchooserbutton" | |
7 | title="XAppIconChooserButton" /> | |
8 | <glade-widget-class name="XAppStackSidebar" generic-name="xappstacksidebar" | |
9 | title="XAppStackSidebar" /> | |
6 | 10 | </glade-widget-classes> |
7 | 11 | |
8 | 12 | <glade-widget-group name="xapp-widgets" title="XApp Widgets"> |
9 | 13 | <glade-widget-class-ref name="XAppGtkWindow" /> |
14 | <glade-widget-class-ref name="XAppIconChooserButton" /> | |
15 | <glade-widget-class-ref name="XAppStackSidebar" /> | |
10 | 16 | </glade-widget-group> |
11 | 17 | </glade-catalog> |
59 | 59 | guint progress; |
60 | 60 | gboolean progress_pulse; |
61 | 61 | } XAppGtkWindowPrivate; |
62 | ||
63 | struct _XAppGtkWindow | |
64 | { | |
65 | GtkWindow parent_object; | |
66 | ||
67 | XAppGtkWindowPrivate *priv; | |
68 | }; | |
69 | 62 | |
70 | 63 | G_DEFINE_TYPE_WITH_PRIVATE (XAppGtkWindow, xapp_gtk_window, GTK_TYPE_WINDOW) |
71 | 64 | |
339 | 332 | xapp_gtk_window_realize (GtkWidget *widget) |
340 | 333 | { |
341 | 334 | XAppGtkWindow *window = XAPP_GTK_WINDOW (widget); |
342 | XAppGtkWindowPrivate *priv = window->priv; | |
335 | XAppGtkWindowPrivate *priv = xapp_gtk_window_get_instance_private (window); | |
343 | 336 | |
344 | 337 | GTK_WIDGET_CLASS (xapp_gtk_window_parent_class)->realize (widget); |
345 | 338 | |
357 | 350 | xapp_gtk_window_finalize (GObject *object) |
358 | 351 | { |
359 | 352 | XAppGtkWindow *window = XAPP_GTK_WINDOW (object); |
360 | XAppGtkWindowPrivate *priv = window->priv; | |
353 | XAppGtkWindowPrivate *priv = xapp_gtk_window_get_instance_private (window); | |
361 | 354 | |
362 | 355 | clear_icon_strings (priv); |
363 | 356 | |
368 | 361 | xapp_gtk_window_init (XAppGtkWindow *window) |
369 | 362 | { |
370 | 363 | XAppGtkWindowPrivate *priv; |
371 | ||
372 | window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, XAPP_TYPE_GTK_WINDOW, XAppGtkWindowPrivate); | |
373 | ||
374 | priv = window->priv; | |
364 | priv = xapp_gtk_window_get_instance_private (window); | |
375 | 365 | |
376 | 366 | priv->icon_name = NULL; |
377 | 367 | priv->icon_path = NULL; |
420 | 410 | { |
421 | 411 | g_return_if_fail (XAPP_IS_GTK_WINDOW (window)); |
422 | 412 | |
423 | set_icon_name_internal (GTK_WINDOW (window), window->priv, icon_name); | |
413 | XAppGtkWindowPrivate *priv = xapp_gtk_window_get_instance_private (window); | |
414 | ||
415 | set_icon_name_internal (GTK_WINDOW (window), priv, icon_name); | |
424 | 416 | } |
425 | 417 | |
426 | 418 | /** |
442 | 434 | { |
443 | 435 | g_return_if_fail (XAPP_IS_GTK_WINDOW (window)); |
444 | 436 | |
445 | set_icon_from_file_internal (GTK_WINDOW (window), window->priv, file_name, error); | |
437 | XAppGtkWindowPrivate *priv = xapp_gtk_window_get_instance_private (window); | |
438 | ||
439 | set_icon_from_file_internal (GTK_WINDOW (window), priv, file_name, error); | |
446 | 440 | } |
447 | 441 | |
448 | 442 | /** |
468 | 462 | { |
469 | 463 | g_return_if_fail (XAPP_IS_GTK_WINDOW (window)); |
470 | 464 | |
471 | set_progress_internal (GTK_WINDOW (window), window->priv, progress); | |
465 | XAppGtkWindowPrivate *priv = xapp_gtk_window_get_instance_private (window); | |
466 | ||
467 | set_progress_internal (GTK_WINDOW (window), priv, progress); | |
472 | 468 | } |
473 | 469 | |
474 | 470 | /** |
493 | 489 | g_return_if_fail (XAPP_IS_GTK_WINDOW (window)); |
494 | 490 | g_return_if_fail (XAPP_IS_GTK_WINDOW (window)); |
495 | 491 | |
496 | set_progress_pulse_internal (GTK_WINDOW (window), window->priv, pulse); | |
492 | XAppGtkWindowPrivate *priv = xapp_gtk_window_get_instance_private (window); | |
493 | ||
494 | set_progress_pulse_internal (GTK_WINDOW (window), priv, pulse); | |
497 | 495 | } |
498 | 496 | |
499 | 497 |
9 | 9 | |
10 | 10 | #define XAPP_TYPE_GTK_WINDOW (xapp_gtk_window_get_type ()) |
11 | 11 | |
12 | G_DECLARE_FINAL_TYPE (XAppGtkWindow, xapp_gtk_window, XAPP, GTK_WINDOW, GtkWindow) | |
12 | G_DECLARE_DERIVABLE_TYPE (XAppGtkWindow, xapp_gtk_window, XAPP, GTK_WINDOW, GtkWindow) | |
13 | ||
14 | struct _XAppGtkWindowClass | |
15 | { | |
16 | GtkWindowClass parent_class; | |
17 | ||
18 | gpointer padding[12]; | |
19 | }; | |
13 | 20 | |
14 | 21 | /* Class */ |
15 | 22 | GtkWidget *xapp_gtk_window_new (GtkWindowType type); |
0 | #include <config.h> | |
1 | #include "xapp-icon-chooser-button.h" | |
2 | #include <glib/gi18n-lib.h> | |
3 | ||
4 | #define XAPP_BUTTON_ICON_SIZE_DEFAULT GTK_ICON_SIZE_DIALOG | |
5 | ||
6 | /** | |
7 | * SECTION:xapp-icon-chooser-button | |
8 | * @Short_description: A button for selecting an icon | |
9 | * @Title: XAppIconChooserButton | |
10 | * | |
11 | * The XAppIconChooserButton creates a button so that | |
12 | * the user can select an icon. When the button is clicked | |
13 | * it will open an XAppIconChooserDialog. The currently | |
14 | * selected icon will be displayed as the button image. | |
15 | */ | |
16 | ||
17 | typedef struct | |
18 | { | |
19 | GtkWidget *image; | |
20 | XAppIconChooserDialog *dialog; | |
21 | GtkIconSize icon_size; | |
22 | gchar *icon_string; | |
23 | } XAppIconChooserButtonPrivate; | |
24 | ||
25 | struct _XAppIconChooserButton | |
26 | { | |
27 | GtkButton parent_instance; | |
28 | }; | |
29 | ||
30 | enum | |
31 | { | |
32 | PROP_0, | |
33 | PROP_ICON_SIZE, | |
34 | PROP_ICON, | |
35 | N_PROPERTIES | |
36 | }; | |
37 | ||
38 | static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; | |
39 | ||
40 | G_DEFINE_TYPE_WITH_PRIVATE (XAppIconChooserButton, xapp_icon_chooser_button, GTK_TYPE_BUTTON) | |
41 | ||
42 | static void | |
43 | on_clicked (GtkButton *button) | |
44 | { | |
45 | XAppIconChooserButtonPrivate *priv; | |
46 | GtkResponseType response; | |
47 | ||
48 | priv = xapp_icon_chooser_button_get_instance_private (XAPP_ICON_CHOOSER_BUTTON (button)); | |
49 | ||
50 | gtk_window_set_transient_for (GTK_WINDOW (priv->dialog), GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button)))); | |
51 | ||
52 | if (priv->icon_string == NULL) | |
53 | { | |
54 | response = xapp_icon_chooser_dialog_run (priv->dialog); | |
55 | } | |
56 | else | |
57 | { | |
58 | response = xapp_icon_chooser_dialog_run_with_icon (priv->dialog, priv->icon_string); | |
59 | } | |
60 | ||
61 | if (response == GTK_RESPONSE_OK) | |
62 | { | |
63 | gchar *icon; | |
64 | ||
65 | icon = xapp_icon_chooser_dialog_get_icon_string (priv->dialog); | |
66 | xapp_icon_chooser_button_set_icon (XAPP_ICON_CHOOSER_BUTTON (button), icon); | |
67 | } | |
68 | } | |
69 | ||
70 | void | |
71 | xapp_icon_chooser_button_get_property (GObject *object, | |
72 | guint prop_id, | |
73 | GValue *value, | |
74 | GParamSpec *pspec) | |
75 | { | |
76 | XAppIconChooserButton *button; | |
77 | XAppIconChooserButtonPrivate *priv; | |
78 | ||
79 | button = XAPP_ICON_CHOOSER_BUTTON (object); | |
80 | priv = xapp_icon_chooser_button_get_instance_private (button); | |
81 | ||
82 | switch (prop_id) | |
83 | { | |
84 | case PROP_ICON_SIZE: | |
85 | g_value_set_enum (value, priv->icon_size); | |
86 | break; | |
87 | case PROP_ICON: | |
88 | g_value_set_string (value, priv->icon_string); | |
89 | break; | |
90 | default: | |
91 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
92 | break; | |
93 | } | |
94 | } | |
95 | ||
96 | void | |
97 | xapp_icon_chooser_button_set_property (GObject *object, | |
98 | guint prop_id, | |
99 | const GValue *value, | |
100 | GParamSpec *pspec) | |
101 | { | |
102 | XAppIconChooserButton *button; | |
103 | XAppIconChooserButtonPrivate *priv; | |
104 | ||
105 | button = XAPP_ICON_CHOOSER_BUTTON (object); | |
106 | priv = xapp_icon_chooser_button_get_instance_private (button); | |
107 | ||
108 | switch (prop_id) | |
109 | { | |
110 | case PROP_ICON_SIZE: | |
111 | xapp_icon_chooser_button_set_icon_size (button, g_value_get_enum (value)); | |
112 | break; | |
113 | case PROP_ICON: | |
114 | xapp_icon_chooser_button_set_icon (button, g_value_get_string (value)); | |
115 | break; | |
116 | default: | |
117 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
118 | break; | |
119 | } | |
120 | } | |
121 | ||
122 | static void | |
123 | xapp_icon_chooser_button_init (XAppIconChooserButton *button) | |
124 | { | |
125 | XAppIconChooserButtonPrivate *priv; | |
126 | ||
127 | priv = xapp_icon_chooser_button_get_instance_private (button); | |
128 | ||
129 | priv->image = gtk_image_new_from_icon_name ("unkown", XAPP_BUTTON_ICON_SIZE_DEFAULT); | |
130 | gtk_button_set_image (GTK_BUTTON (button), priv->image); | |
131 | ||
132 | gtk_widget_set_hexpand (GTK_WIDGET (button), FALSE); | |
133 | gtk_widget_set_vexpand (GTK_WIDGET (button), FALSE); | |
134 | gtk_widget_set_halign (GTK_WIDGET (button), GTK_ALIGN_CENTER); | |
135 | gtk_widget_set_valign (GTK_WIDGET (button), GTK_ALIGN_CENTER); | |
136 | ||
137 | xapp_icon_chooser_button_set_icon_size (button, -1); | |
138 | ||
139 | priv->dialog = xapp_icon_chooser_dialog_new (); | |
140 | } | |
141 | ||
142 | static void | |
143 | xapp_icon_chooser_button_class_init (XAppIconChooserButtonClass *klass) | |
144 | { | |
145 | GtkBindingSet *binding_set; | |
146 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
147 | GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass); | |
148 | ||
149 | object_class->get_property = xapp_icon_chooser_button_get_property; | |
150 | object_class->set_property = xapp_icon_chooser_button_set_property; | |
151 | ||
152 | button_class->clicked = on_clicked; | |
153 | ||
154 | /** | |
155 | * XAppIconChooserButton:icon-size: | |
156 | * | |
157 | * The size to use when displaying the icon. | |
158 | */ | |
159 | obj_properties[PROP_ICON_SIZE] = | |
160 | g_param_spec_enum ("icon-size", | |
161 | _("Icon size"), | |
162 | _("The preferred icon size."), | |
163 | GTK_TYPE_ICON_SIZE, | |
164 | GTK_ICON_SIZE_DND, | |
165 | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); | |
166 | ||
167 | /** | |
168 | * XAppIconChooserButton:icon: | |
169 | * | |
170 | * The preferred size to use when looking up icons. This only works with icon names. | |
171 | * Additionally, there is no guarantee that a selected icon name will exist in a | |
172 | * particular size. | |
173 | */ | |
174 | obj_properties[PROP_ICON] = | |
175 | g_param_spec_string ("icon", | |
176 | _("Icon"), | |
177 | _("The string representing the icon."), | |
178 | "", | |
179 | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); | |
180 | ||
181 | g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties); | |
182 | } | |
183 | ||
184 | /** | |
185 | * xapp_icon_chooser_button_new: | |
186 | * | |
187 | * Creates a new #XAppIconChooserButton and sets its icon to @icon. | |
188 | * | |
189 | * Returns: a newly created #XAppIconChooserButton | |
190 | */ | |
191 | XAppIconChooserButton * | |
192 | xapp_icon_chooser_button_new (void) | |
193 | { | |
194 | return g_object_new (XAPP_TYPE_ICON_CHOOSER_BUTTON, NULL); | |
195 | } | |
196 | ||
197 | /** | |
198 | * xapp_icon_chooser_button_new_with_size: | |
199 | * @icon_size: the size of icon to use in the button, or NULL to use the default value. | |
200 | * | |
201 | * Creates a new #XAppIconChooserButton, and sets the sizes of the button image and the icons in | |
202 | * the dialog. Note that xapp_icon_chooser_button_new_with_size (NULL, NULL) is the same as calling | |
203 | * xapp_icon_chooser_button_new (). | |
204 | * | |
205 | * Returns: a newly created #XAppIconChooserButton | |
206 | */ | |
207 | XAppIconChooserButton * | |
208 | xapp_icon_chooser_button_new_with_size (GtkIconSize icon_size) | |
209 | { | |
210 | XAppIconChooserButton *button; | |
211 | ||
212 | button = g_object_new (XAPP_TYPE_ICON_CHOOSER_BUTTON, NULL); | |
213 | ||
214 | xapp_icon_chooser_button_set_icon_size (button, icon_size); | |
215 | ||
216 | return button; | |
217 | } | |
218 | ||
219 | /** | |
220 | * xapp_icon_chooser_button_set_icon_size: | |
221 | * @button: a #XAppIconChooserButton | |
222 | * @icon_size: the size of icon to use in the button, or -1 to use the default value. | |
223 | * | |
224 | * Sets the icon size used in the button. | |
225 | */ | |
226 | void | |
227 | xapp_icon_chooser_button_set_icon_size (XAppIconChooserButton *button, | |
228 | GtkIconSize icon_size) | |
229 | { | |
230 | XAppIconChooserButtonPrivate *priv; | |
231 | gint width, height; | |
232 | gchar *icon; | |
233 | ||
234 | priv = xapp_icon_chooser_button_get_instance_private (button); | |
235 | ||
236 | if (icon_size == -1) | |
237 | { | |
238 | priv->icon_size = XAPP_BUTTON_ICON_SIZE_DEFAULT; | |
239 | } | |
240 | else | |
241 | { | |
242 | priv->icon_size = icon_size; | |
243 | } | |
244 | ||
245 | gtk_icon_size_lookup (priv->icon_size, &width, &height); | |
246 | gtk_image_set_pixel_size (GTK_IMAGE (priv->image), width); | |
247 | ||
248 | // We need to make sure the icon gets resized if it's a file path. Since | |
249 | // this means regenerating the pixbuf anyway, it's easier to just call | |
250 | // xapp_icon_chooser_button_set_icon, but we need to dup the string so | |
251 | // it doens't get freed before it gets used. | |
252 | icon = g_strdup(priv->icon_string); | |
253 | xapp_icon_chooser_button_set_icon (button, icon); | |
254 | g_free (icon); | |
255 | ||
256 | g_object_notify (G_OBJECT (button), "icon-size"); | |
257 | } | |
258 | ||
259 | /** | |
260 | * xapp_icon_chooser_button_get_icon: | |
261 | * @button: a #XAppIconChooserButton | |
262 | * | |
263 | * Gets the icon from the #XAppIconChooserButton. | |
264 | * | |
265 | * returns: a string representing the icon. This may be an icon name or a file path. | |
266 | */ | |
267 | const gchar* | |
268 | xapp_icon_chooser_button_get_icon (XAppIconChooserButton *button) | |
269 | { | |
270 | XAppIconChooserButtonPrivate *priv; | |
271 | ||
272 | priv = xapp_icon_chooser_button_get_instance_private (button); | |
273 | ||
274 | return priv->icon_string; | |
275 | } | |
276 | ||
277 | /** | |
278 | * xapp_icon_chooser_button_set_icon: | |
279 | * @button: a #XAppIconChooserButton | |
280 | * @icon: (nullable): a string representing the icon to be set. This may be an icon name or a file path. | |
281 | * | |
282 | * Sets the icon on the #XAppIconChooserButton. | |
283 | */ | |
284 | void | |
285 | xapp_icon_chooser_button_set_icon (XAppIconChooserButton *button, | |
286 | const gchar *icon) | |
287 | { | |
288 | XAppIconChooserButtonPrivate *priv; | |
289 | const gchar *icon_string; | |
290 | ||
291 | priv = xapp_icon_chooser_button_get_instance_private (button); | |
292 | ||
293 | if (priv->icon_string != NULL) | |
294 | { | |
295 | g_free (priv->icon_string); | |
296 | } | |
297 | ||
298 | if (icon == NULL) | |
299 | { | |
300 | priv->icon_string = NULL; | |
301 | icon_string = "unkown"; | |
302 | } | |
303 | else | |
304 | { | |
305 | priv->icon_string = g_strdup (icon); | |
306 | icon_string = icon; | |
307 | } | |
308 | ||
309 | if (g_strrstr (icon_string, "/")) | |
310 | { | |
311 | GdkPixbuf *pixbuf; | |
312 | gint width, height; | |
313 | ||
314 | gtk_icon_size_lookup (priv->icon_size, &width, &height); | |
315 | ||
316 | pixbuf = gdk_pixbuf_new_from_file_at_size (icon_string, width, height, NULL); | |
317 | ||
318 | gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf); | |
319 | } | |
320 | else | |
321 | { | |
322 | gtk_image_set_from_icon_name (GTK_IMAGE (priv->image), icon_string, priv->icon_size); | |
323 | } | |
324 | ||
325 | g_object_notify (G_OBJECT (button), "icon"); | |
326 | } | |
327 | ||
328 | /** | |
329 | * xapp_icon_chooser_button_get_dialog: | |
330 | * @button: a #XAppIconChooserButton | |
331 | * | |
332 | * Gets a reference to the icon chooser dialog for the #XAppIconChooserButton. | |
333 | * This is useful for setting properties on the dialog. | |
334 | * | |
335 | * Returns: (transfer none): the #XAppIconChooserDialog | |
336 | */ | |
337 | XAppIconChooserDialog * | |
338 | xapp_icon_chooser_button_get_dialog (XAppIconChooserButton *button) | |
339 | { | |
340 | XAppIconChooserButtonPrivate *priv; | |
341 | ||
342 | priv = xapp_icon_chooser_button_get_instance_private (button); | |
343 | ||
344 | return priv->dialog; | |
345 | } |
0 | #ifndef _XAPP_ICON_CHOOSER_BUTTON_H_ | |
1 | #define _XAPP_ICON_CHOOSER_BUTTON_H_ | |
2 | ||
3 | #include <glib-object.h> | |
4 | #include <gtk/gtk.h> | |
5 | #include "xapp-icon-chooser-dialog.h" | |
6 | #include "xapp-enums.h" | |
7 | ||
8 | G_BEGIN_DECLS | |
9 | ||
10 | #define XAPP_TYPE_ICON_CHOOSER_BUTTON (xapp_icon_chooser_button_get_type ()) | |
11 | ||
12 | G_DECLARE_FINAL_TYPE (XAppIconChooserButton, xapp_icon_chooser_button, XAPP, ICON_CHOOSER_BUTTON, GtkButton) | |
13 | ||
14 | XAppIconChooserButton * xapp_icon_chooser_button_new (void); | |
15 | ||
16 | XAppIconChooserButton * xapp_icon_chooser_button_new_with_size (GtkIconSize icon_size); | |
17 | ||
18 | void xapp_icon_chooser_button_set_icon_size (XAppIconChooserButton *button, | |
19 | GtkIconSize icon_size); | |
20 | ||
21 | void xapp_icon_chooser_button_set_icon (XAppIconChooserButton *button, | |
22 | const gchar *icon); | |
23 | ||
24 | const gchar* xapp_icon_chooser_button_get_icon (XAppIconChooserButton *button); | |
25 | XAppIconChooserDialog * xapp_icon_chooser_button_get_dialog (XAppIconChooserButton *button); | |
26 | ||
27 | G_END_DECLS | |
28 | ||
29 | #endif /* _XAPP_ICON_CHOOSER_DIALOG_H_ */ |
0 | #include <config.h> | |
1 | #include <gdk/gdk.h> | |
2 | #include <math.h> | |
3 | #include "xapp-enums.h" | |
4 | #include "xapp-icon-chooser-dialog.h" | |
5 | #include "xapp-stack-sidebar.h" | |
6 | #include <glib/gi18n-lib.h> | |
7 | #include <glib/gstdio.h> | |
8 | ||
9 | #define DEBUG_REFS 0 | |
10 | #define DEBUG_ICON_THEME 0 | |
11 | ||
12 | /** | |
13 | * SECTION:xapp-icon-chooser-dialog | |
14 | * @Short_description: A dialog for selecting an icon | |
15 | * @Title: XAppIconChooserDialog | |
16 | * | |
17 | * The XAppIconChooserDialog creates a dialog so that | |
18 | * the user can select an icon. It provides the ability | |
19 | * to browse by category, search by icon name, or select | |
20 | * from a specific file. | |
21 | */ | |
22 | ||
23 | typedef struct | |
24 | { | |
25 | const gchar *name; /* This is a translation which doesn't get freed */ | |
26 | GList *icons; | |
27 | GList *iter; | |
28 | GtkListStore *model; | |
29 | } IconCategoryInfo; | |
30 | ||
31 | typedef struct | |
32 | { | |
33 | GtkResponseType response; | |
34 | XAppIconSize icon_size; | |
35 | GtkListStore *category_list; | |
36 | GtkListStore *search_icon_store; | |
37 | GFileEnumerator *search_file_enumerator; | |
38 | GCancellable *cancellable; | |
39 | GList *full_icon_list; | |
40 | GList *search_iter; | |
41 | GHashTable *categories; | |
42 | GHashTable *pixbufs_by_name; | |
43 | GtkWidget *search_bar; | |
44 | GtkWidget *icon_view; | |
45 | GtkWidget *list_box; | |
46 | GtkWidget *select_button; | |
47 | GtkWidget *browse_button; | |
48 | GtkWidget *action_area; | |
49 | GtkWidget *loading_bar; | |
50 | gchar *icon_string; | |
51 | gchar *current_text; | |
52 | gulong search_changed_id; | |
53 | gboolean allow_paths; | |
54 | IconCategoryInfo *current_category; | |
55 | } XAppIconChooserDialogPrivate; | |
56 | ||
57 | struct _XAppIconChooserDialog | |
58 | { | |
59 | XAppGtkWindow parent_instance; | |
60 | }; | |
61 | ||
62 | typedef struct | |
63 | { | |
64 | XAppIconChooserDialog *dialog; | |
65 | GtkListStore *model; | |
66 | IconCategoryInfo *category_info; | |
67 | GCancellable *cancellable; | |
68 | GdkPixbuf *pixbuf; | |
69 | const gchar *name; | |
70 | gboolean chunk_end; | |
71 | } NamedIconInfoLoadCallbackInfo; | |
72 | ||
73 | typedef struct | |
74 | { | |
75 | XAppIconChooserDialog *dialog; | |
76 | GtkListStore *model; | |
77 | GCancellable *cancellable; | |
78 | GFileEnumerator *enumerator; | |
79 | gchar *short_name; | |
80 | gchar *long_name; | |
81 | gboolean chunk_end; | |
82 | } FileIconInfoLoadCallbackInfo; | |
83 | ||
84 | typedef struct | |
85 | { | |
86 | const gchar *name; | |
87 | const gchar *contexts[5]; | |
88 | } IconCategoryDefinition; | |
89 | ||
90 | static IconCategoryDefinition categories[] = { | |
91 | // Category name context names | |
92 | { | |
93 | N_("Actions"), { "Actions", NULL } | |
94 | }, | |
95 | { | |
96 | N_("Applications"), { "Applications", "Apps", NULL } | |
97 | }, | |
98 | { | |
99 | N_("Categories"), { "Categories", NULL } | |
100 | }, | |
101 | { | |
102 | N_("Devices"), { "Devices", NULL } | |
103 | }, | |
104 | { | |
105 | N_("Emblems"), { "Emblems", NULL } | |
106 | }, | |
107 | { | |
108 | N_("Emoji"), { "Emotes", NULL } | |
109 | }, | |
110 | { | |
111 | N_("Mime types"), { "MimeTypes", "Mimetypes", NULL } | |
112 | }, | |
113 | { | |
114 | N_("Places"), { "Places", NULL } | |
115 | }, | |
116 | { | |
117 | N_("Status"), { "Status", "Notifications", NULL } | |
118 | }, | |
119 | { | |
120 | N_("Other"), { "Panel", NULL } | |
121 | } | |
122 | }; | |
123 | ||
124 | enum | |
125 | { | |
126 | CLOSE, | |
127 | SELECT, | |
128 | LAST_SIGNAL | |
129 | }; | |
130 | ||
131 | enum | |
132 | { | |
133 | PROP_0, | |
134 | PROP_ICON_SIZE, | |
135 | PROP_ALLOW_PATHS, | |
136 | N_PROPERTIES | |
137 | }; | |
138 | ||
139 | enum | |
140 | { | |
141 | COLUMN_DISPLAY_NAME, | |
142 | COLUMN_FULL_NAME, | |
143 | COLUMN_PIXBUF, | |
144 | }; | |
145 | ||
146 | static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; | |
147 | ||
148 | static guint signals[LAST_SIGNAL] = {0, }; | |
149 | ||
150 | G_DEFINE_TYPE_WITH_PRIVATE (XAppIconChooserDialog, xapp_icon_chooser_dialog, XAPP_TYPE_GTK_WINDOW) | |
151 | ||
152 | static void on_category_selected (GtkListBox *list_box, | |
153 | XAppIconChooserDialog *dialog); | |
154 | ||
155 | static void on_search_text_changed (GtkSearchEntry *entry, | |
156 | XAppIconChooserDialog *dialog); | |
157 | ||
158 | static void on_icon_view_selection_changed (GtkIconView *icon_view, | |
159 | gpointer user_data); | |
160 | ||
161 | static void on_icon_store_icons_added (GtkTreeModel *tree_model, | |
162 | GtkTreePath *path, | |
163 | GtkTreeIter *iter, | |
164 | gpointer user_data); | |
165 | ||
166 | static void on_browse_button_clicked (GtkButton *button, | |
167 | gpointer user_data); | |
168 | ||
169 | static void on_select_button_clicked (GtkButton *button, | |
170 | gpointer user_data); | |
171 | ||
172 | static void on_cancel_button_clicked (GtkButton *button, | |
173 | gpointer user_data); | |
174 | ||
175 | static gboolean on_search_bar_key_pressed (GtkWidget *widget, | |
176 | GdkEvent *event, | |
177 | gpointer user_data); | |
178 | ||
179 | static gboolean on_delete_event (GtkWidget *widget, | |
180 | GdkEventAny *event); | |
181 | ||
182 | static gboolean on_select_event (XAppIconChooserDialog *dialog, | |
183 | GdkEventAny *event); | |
184 | ||
185 | static void on_icon_view_item_activated (GtkIconView *iconview, | |
186 | GtkTreePath *path, | |
187 | gpointer user_data); | |
188 | ||
189 | static void load_categories (XAppIconChooserDialog *dialog); | |
190 | ||
191 | static void load_icons_for_category (XAppIconChooserDialog *dialog, | |
192 | IconCategoryInfo *category_info, | |
193 | guint icon_size); | |
194 | ||
195 | static void search_path (XAppIconChooserDialog *dialog, | |
196 | const gchar *path_string, | |
197 | GtkListStore *icon_store); | |
198 | ||
199 | static void search_icon_name (XAppIconChooserDialog *dialog, | |
200 | const gchar *name_string, | |
201 | GtkListStore *icon_store); | |
202 | ||
203 | static gint list_box_sort (GtkListBoxRow *row1, | |
204 | GtkListBoxRow *row2, | |
205 | gpointer user_data); | |
206 | ||
207 | static gint search_model_sort (GtkTreeModel *model, | |
208 | GtkTreeIter *a, | |
209 | GtkTreeIter *b, | |
210 | gpointer user_data); | |
211 | ||
212 | static void | |
213 | free_category_info (IconCategoryInfo *category_info) | |
214 | { | |
215 | g_list_free_full (category_info->icons, g_free); | |
216 | ||
217 | g_clear_object (&category_info->model); | |
218 | ||
219 | g_free (category_info); | |
220 | } | |
221 | ||
222 | static void | |
223 | free_file_info (FileIconInfoLoadCallbackInfo *file_info) | |
224 | { | |
225 | g_object_unref (file_info->cancellable); | |
226 | ||
227 | g_object_unref (file_info->enumerator); | |
228 | ||
229 | g_free (file_info->short_name); | |
230 | g_free (file_info->long_name); | |
231 | ||
232 | g_free (file_info); | |
233 | } | |
234 | ||
235 | static void | |
236 | free_named_info (NamedIconInfoLoadCallbackInfo *named_info) | |
237 | { | |
238 | g_object_unref (named_info->cancellable); | |
239 | ||
240 | g_clear_object (&named_info->pixbuf); | |
241 | ||
242 | g_free (named_info); | |
243 | } | |
244 | ||
245 | #if DEBUG_REFS | |
246 | static void | |
247 | on_cancellable_finalize (gpointer data, | |
248 | GObject *object) | |
249 | { | |
250 | g_printerr ("Cancellable Finalize: %p\n", object); | |
251 | } | |
252 | ||
253 | static void | |
254 | on_enumerator_finalize (gpointer data, | |
255 | GObject *object) | |
256 | { | |
257 | g_printerr ("Enumerator Finalize: %p\n", object); | |
258 | } | |
259 | #endif | |
260 | ||
261 | static void | |
262 | on_enumerator_toggle_ref_called (gpointer data, | |
263 | GObject *object, | |
264 | gboolean is_last_ref) | |
265 | { | |
266 | XAppIconChooserDialog *dialog; | |
267 | XAppIconChooserDialogPrivate *priv; | |
268 | ||
269 | dialog = XAPP_ICON_CHOOSER_DIALOG (data); | |
270 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
271 | ||
272 | if (is_last_ref) | |
273 | { | |
274 | g_object_remove_toggle_ref (object, | |
275 | (GToggleNotify) on_enumerator_toggle_ref_called, | |
276 | dialog); | |
277 | ||
278 | priv->search_file_enumerator = NULL; | |
279 | } | |
280 | } | |
281 | ||
282 | static void | |
283 | clear_search_state (XAppIconChooserDialog *dialog) | |
284 | { | |
285 | XAppIconChooserDialogPrivate *priv; | |
286 | ||
287 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
288 | ||
289 | g_cancellable_cancel (priv->cancellable); | |
290 | g_clear_object (&priv->cancellable); | |
291 | ||
292 | g_clear_object (&priv->search_file_enumerator); | |
293 | ||
294 | gtk_widget_hide (priv->loading_bar); | |
295 | priv->search_iter = NULL; | |
296 | } | |
297 | ||
298 | void | |
299 | xapp_icon_chooser_dialog_get_property (GObject *object, | |
300 | guint prop_id, | |
301 | GValue *value, | |
302 | GParamSpec *pspec) | |
303 | { | |
304 | XAppIconChooserDialog *dialog; | |
305 | XAppIconChooserDialogPrivate *priv; | |
306 | ||
307 | dialog = XAPP_ICON_CHOOSER_DIALOG (object); | |
308 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
309 | ||
310 | switch (prop_id) | |
311 | { | |
312 | case PROP_ICON_SIZE: | |
313 | g_value_set_enum (value, priv->icon_size); | |
314 | break; | |
315 | case PROP_ALLOW_PATHS: | |
316 | g_value_set_boolean (value, priv->allow_paths); | |
317 | break; | |
318 | default: | |
319 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
320 | break; | |
321 | } | |
322 | } | |
323 | ||
324 | void | |
325 | xapp_icon_chooser_dialog_set_property (GObject *object, | |
326 | guint prop_id, | |
327 | const GValue *value, | |
328 | GParamSpec *pspec) | |
329 | { | |
330 | XAppIconChooserDialog *dialog; | |
331 | XAppIconChooserDialogPrivate *priv; | |
332 | ||
333 | dialog = XAPP_ICON_CHOOSER_DIALOG (object); | |
334 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
335 | ||
336 | switch (prop_id) | |
337 | { | |
338 | case PROP_ICON_SIZE: | |
339 | priv->icon_size = g_value_get_enum (value); | |
340 | break; | |
341 | case PROP_ALLOW_PATHS: | |
342 | priv->allow_paths = g_value_get_boolean (value); | |
343 | if (priv->allow_paths) | |
344 | { | |
345 | gtk_widget_show (priv->browse_button); | |
346 | gtk_widget_set_no_show_all (priv->browse_button, FALSE); | |
347 | } | |
348 | else | |
349 | { | |
350 | gtk_widget_hide (priv->browse_button); | |
351 | gtk_widget_set_no_show_all (priv->browse_button, TRUE); | |
352 | } | |
353 | break; | |
354 | default: | |
355 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
356 | break; | |
357 | } | |
358 | } | |
359 | ||
360 | static void | |
361 | xapp_icon_chooser_dialog_dispose (GObject *object) | |
362 | { | |
363 | XAppIconChooserDialog *dialog; | |
364 | XAppIconChooserDialogPrivate *priv; | |
365 | ||
366 | dialog = XAPP_ICON_CHOOSER_DIALOG (object); | |
367 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
368 | ||
369 | if (priv->full_icon_list != NULL) | |
370 | { | |
371 | g_list_free_full (priv->full_icon_list, g_free); | |
372 | priv->full_icon_list = NULL; | |
373 | } | |
374 | ||
375 | if (priv->categories != NULL) | |
376 | { | |
377 | g_hash_table_destroy (priv->categories); | |
378 | priv->categories = NULL; | |
379 | } | |
380 | ||
381 | if (priv->pixbufs_by_name != NULL) | |
382 | { | |
383 | g_hash_table_destroy (priv->pixbufs_by_name); | |
384 | priv->pixbufs_by_name = NULL; | |
385 | } | |
386 | ||
387 | g_clear_pointer (&priv->icon_string, g_free); | |
388 | g_clear_pointer (&priv->current_text, g_free); | |
389 | g_clear_object (&priv->cancellable); | |
390 | ||
391 | G_OBJECT_CLASS (xapp_icon_chooser_dialog_parent_class)->dispose (object); | |
392 | } | |
393 | ||
394 | static void | |
395 | xapp_icon_chooser_dialog_init (XAppIconChooserDialog *dialog) | |
396 | { | |
397 | XAppIconChooserDialogPrivate *priv; | |
398 | GtkWidget *main_box; | |
399 | GtkWidget *secondary_box; | |
400 | GtkWidget *toolbar; | |
401 | GtkWidget *overlay; | |
402 | GtkWidget *spinner; | |
403 | GtkWidget *spinner_label; | |
404 | GtkWidget *loading_bar_box; | |
405 | GtkToolItem *tool_item; | |
406 | GtkWidget *toolbar_box; | |
407 | GtkWidget *right_box; | |
408 | GtkCellRenderer *renderer; | |
409 | GtkTreeViewColumn *column; | |
410 | GtkStyleContext *style; | |
411 | GtkSizeGroup *button_size_group; | |
412 | GtkWidget *cancel_button; | |
413 | GtkWidget *button_area; | |
414 | GtkWidget *selection; | |
415 | GtkWidget *scrolled_window; | |
416 | ||
417 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
418 | ||
419 | priv->icon_size = XAPP_ICON_SIZE_32; | |
420 | priv->categories = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) free_category_info); | |
421 | ||
422 | /* pixbufs_by_name will save pixbufs generated by icon name, so they can avoid the load_pixbuf call | |
423 | * when they're reloaded (like re-selecting a previously selected category) */ | |
424 | priv->pixbufs_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); | |
425 | ||
426 | priv->response = GTK_RESPONSE_NONE; | |
427 | priv->icon_string = NULL; | |
428 | priv->current_text = NULL; | |
429 | priv->cancellable = NULL; | |
430 | priv->allow_paths = TRUE; | |
431 | priv->full_icon_list = NULL; | |
432 | ||
433 | priv->search_icon_store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF); | |
434 | ||
435 | gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->search_icon_store), | |
436 | COLUMN_DISPLAY_NAME, | |
437 | search_model_sort, | |
438 | priv, | |
439 | NULL); | |
440 | gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->search_icon_store), | |
441 | COLUMN_DISPLAY_NAME, | |
442 | GTK_SORT_ASCENDING); | |
443 | ||
444 | g_signal_connect (priv->search_icon_store, "row-inserted", | |
445 | G_CALLBACK (on_icon_store_icons_added), dialog); | |
446 | ||
447 | gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 450); | |
448 | gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), TRUE); | |
449 | gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG); | |
450 | gtk_window_set_title (GTK_WINDOW (dialog), _("Choose an icon")); | |
451 | ||
452 | main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); | |
453 | gtk_container_add (GTK_CONTAINER (dialog), main_box); | |
454 | ||
455 | // toolbar | |
456 | toolbar = gtk_toolbar_new (); | |
457 | gtk_box_pack_start (GTK_BOX (main_box), toolbar, FALSE, FALSE, 0); | |
458 | style = gtk_widget_get_style_context (toolbar); | |
459 | gtk_style_context_add_class (style, "primary-toolbar"); | |
460 | ||
461 | tool_item = gtk_tool_item_new (); | |
462 | gtk_toolbar_insert (GTK_TOOLBAR (toolbar), tool_item, 0); | |
463 | gtk_tool_item_set_expand (tool_item, TRUE); | |
464 | ||
465 | toolbar_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); | |
466 | gtk_container_add (GTK_CONTAINER (tool_item), toolbar_box); | |
467 | style = gtk_widget_get_style_context (GTK_WIDGET (toolbar_box)); | |
468 | gtk_style_context_add_class (style, "linked"); | |
469 | ||
470 | priv->search_bar = gtk_search_entry_new (); | |
471 | gtk_box_pack_start (GTK_BOX (toolbar_box), priv->search_bar, TRUE, TRUE, 0); | |
472 | gtk_entry_set_placeholder_text (GTK_ENTRY (priv->search_bar), _("Search")); | |
473 | ||
474 | priv->search_changed_id = g_signal_connect (priv->search_bar, "search-changed", | |
475 | G_CALLBACK (on_search_text_changed), dialog); | |
476 | g_signal_connect (priv->search_bar, "key-press-event", | |
477 | G_CALLBACK (on_search_bar_key_pressed), dialog); | |
478 | ||
479 | priv->browse_button = gtk_button_new_with_label (_("Browse")); | |
480 | gtk_box_pack_start (GTK_BOX (toolbar_box), priv->browse_button, FALSE, FALSE, 0); | |
481 | ||
482 | g_signal_connect (priv->browse_button, "clicked", | |
483 | G_CALLBACK (on_browse_button_clicked), dialog); | |
484 | ||
485 | secondary_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); | |
486 | gtk_box_pack_start (GTK_BOX (main_box), secondary_box, TRUE, TRUE, 0); | |
487 | ||
488 | // context list | |
489 | scrolled_window = gtk_scrolled_window_new (NULL, NULL); | |
490 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); | |
491 | gtk_box_pack_start (GTK_BOX (secondary_box), scrolled_window, FALSE, FALSE, 0); | |
492 | ||
493 | priv->list_box = gtk_list_box_new (); | |
494 | gtk_container_add(GTK_CONTAINER (scrolled_window), GTK_WIDGET (priv->list_box)); | |
495 | gtk_list_box_set_sort_func (GTK_LIST_BOX (priv->list_box), list_box_sort, NULL, NULL); | |
496 | g_signal_connect (priv->list_box, "selected-rows-changed", | |
497 | G_CALLBACK (on_category_selected), dialog); | |
498 | ||
499 | style = gtk_widget_get_style_context (GTK_WIDGET (scrolled_window)); | |
500 | gtk_style_context_add_class (style, "sidebar"); | |
501 | ||
502 | right_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); | |
503 | gtk_box_pack_start (GTK_BOX (secondary_box), right_box, TRUE, TRUE, 0); | |
504 | ||
505 | overlay = gtk_overlay_new (); | |
506 | gtk_box_pack_start (GTK_BOX (right_box), overlay, TRUE, TRUE, 0); | |
507 | ||
508 | // icon view | |
509 | scrolled_window = gtk_scrolled_window_new (NULL, NULL); | |
510 | gtk_overlay_add_overlay (GTK_OVERLAY (overlay), scrolled_window); | |
511 | gtk_widget_set_halign (scrolled_window, GTK_ALIGN_FILL); | |
512 | gtk_widget_set_valign (scrolled_window, GTK_ALIGN_FILL); | |
513 | ||
514 | priv->loading_bar = gtk_frame_new (NULL); | |
515 | gtk_frame_set_shadow_type (GTK_FRAME (priv->loading_bar), GTK_SHADOW_NONE); | |
516 | ||
517 | gtk_style_context_add_class (gtk_widget_get_style_context (priv->loading_bar), | |
518 | "background"); | |
519 | ||
520 | gtk_overlay_add_overlay (GTK_OVERLAY (overlay), priv->loading_bar); | |
521 | gtk_widget_set_halign (priv->loading_bar, GTK_ALIGN_START); | |
522 | gtk_widget_set_valign (priv->loading_bar, GTK_ALIGN_END); | |
523 | gtk_widget_set_no_show_all (priv->loading_bar, TRUE); | |
524 | ||
525 | loading_bar_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); | |
526 | gtk_container_add (GTK_CONTAINER (priv->loading_bar), loading_bar_box); | |
527 | g_object_set (loading_bar_box, | |
528 | "margin", 4, | |
529 | NULL); | |
530 | ||
531 | spinner = gtk_spinner_new (); | |
532 | gtk_spinner_start (GTK_SPINNER (spinner)); | |
533 | gtk_box_pack_start (GTK_BOX (loading_bar_box), spinner, FALSE, FALSE, 4); | |
534 | ||
535 | spinner_label = gtk_label_new (_("Loading...")); | |
536 | gtk_box_pack_start (GTK_BOX (loading_bar_box), spinner_label, FALSE, FALSE, 4); | |
537 | ||
538 | gtk_widget_show_all (loading_bar_box); | |
539 | ||
540 | priv->icon_view = gtk_icon_view_new (); | |
541 | gtk_container_add(GTK_CONTAINER (scrolled_window), GTK_WIDGET (priv->icon_view)); | |
542 | ||
543 | gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (priv->icon_view), COLUMN_PIXBUF); | |
544 | gtk_icon_view_set_text_column (GTK_ICON_VIEW (priv->icon_view), COLUMN_DISPLAY_NAME); | |
545 | gtk_icon_view_set_tooltip_column (GTK_ICON_VIEW (priv->icon_view), COLUMN_FULL_NAME); | |
546 | ||
547 | g_signal_connect (priv->icon_view, "selection-changed", | |
548 | G_CALLBACK (on_icon_view_selection_changed), dialog); | |
549 | g_signal_connect (priv->icon_view, "item-activated", | |
550 | G_CALLBACK (on_icon_view_item_activated), dialog); | |
551 | ||
552 | // buttons | |
553 | button_area = gtk_action_bar_new (); | |
554 | priv->action_area = button_area; | |
555 | gtk_box_pack_start (GTK_BOX (main_box), button_area, FALSE, FALSE, 0); | |
556 | ||
557 | button_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); | |
558 | ||
559 | priv->select_button = gtk_button_new_with_label (_("Select")); | |
560 | style = gtk_widget_get_style_context (GTK_WIDGET (priv->select_button)); | |
561 | gtk_style_context_add_class (style, "text-button"); | |
562 | gtk_size_group_add_widget (button_size_group, priv->select_button); | |
563 | gtk_action_bar_pack_end (GTK_ACTION_BAR (button_area), priv->select_button); | |
564 | ||
565 | g_signal_connect (priv->select_button, "clicked", | |
566 | G_CALLBACK (on_select_button_clicked), dialog); | |
567 | ||
568 | cancel_button = gtk_button_new_with_label (_("Cancel")); | |
569 | style = gtk_widget_get_style_context (GTK_WIDGET (cancel_button)); | |
570 | gtk_style_context_add_class (style, "text-button"); | |
571 | gtk_size_group_add_widget (button_size_group, cancel_button); | |
572 | gtk_action_bar_pack_end (GTK_ACTION_BAR (button_area), cancel_button); | |
573 | ||
574 | g_signal_connect (cancel_button, "clicked", | |
575 | G_CALLBACK (on_cancel_button_clicked), dialog); | |
576 | ||
577 | load_categories (dialog); | |
578 | } | |
579 | ||
580 | static void | |
581 | xapp_icon_chooser_dialog_class_init (XAppIconChooserDialogClass *klass) | |
582 | { | |
583 | GtkBindingSet *binding_set; | |
584 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
585 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); | |
586 | ||
587 | object_class->get_property = xapp_icon_chooser_dialog_get_property; | |
588 | object_class->set_property = xapp_icon_chooser_dialog_set_property; | |
589 | object_class->dispose = xapp_icon_chooser_dialog_dispose; | |
590 | ||
591 | widget_class->delete_event = on_delete_event; | |
592 | ||
593 | /** | |
594 | * XAppIconChooserDialog:icon-size: | |
595 | * | |
596 | * The preferred size to use when looking up icons. This only works with icon names. | |
597 | * Additionally, there is no guarantee that a selected icon name will exist in a | |
598 | * particular size. | |
599 | */ | |
600 | obj_properties[PROP_ICON_SIZE] = | |
601 | g_param_spec_enum ("icon-size", | |
602 | _("Icon size"), | |
603 | _("The preferred icon size."), | |
604 | XAPP_TYPE_ICON_SIZE, | |
605 | XAPP_ICON_SIZE_32, | |
606 | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); | |
607 | ||
608 | /** | |
609 | * XAppIconChooserDialog:allow-paths: | |
610 | * | |
611 | * Whether to allow paths to be searched and selected or only icon names. | |
612 | */ | |
613 | obj_properties[PROP_ALLOW_PATHS] = | |
614 | g_param_spec_boolean ("allow-paths", | |
615 | _("Allow Paths"), | |
616 | _("Whether to allow paths."), | |
617 | TRUE, | |
618 | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); | |
619 | ||
620 | g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties); | |
621 | ||
622 | // keybinding signals | |
623 | signals[CLOSE] = | |
624 | g_signal_new ("close", | |
625 | G_TYPE_FROM_CLASS (klass), | |
626 | G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, | |
627 | G_STRUCT_OFFSET (GtkWidgetClass, delete_event), | |
628 | NULL, NULL, NULL, | |
629 | G_TYPE_NONE, 0); | |
630 | ||
631 | signals[SELECT] = | |
632 | g_signal_new ("select", | |
633 | G_TYPE_FROM_CLASS (klass), | |
634 | G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, | |
635 | 0, | |
636 | NULL, NULL, NULL, | |
637 | G_TYPE_NONE, 0); | |
638 | ||
639 | binding_set = gtk_binding_set_by_class (klass); | |
640 | gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "close", 0); | |
641 | gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0, "select", 0); | |
642 | gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0, "select", 0); | |
643 | ||
644 | gtk_widget_class_set_css_name (widget_class, "stacksidebar"); | |
645 | } | |
646 | ||
647 | /** | |
648 | * xapp_icon_chooser_dialog_new: | |
649 | * | |
650 | * Creates a new #XAppIconChooserDialog. | |
651 | * | |
652 | * Returns: a newly created #XAppIconChooserDialog | |
653 | */ | |
654 | XAppIconChooserDialog * | |
655 | xapp_icon_chooser_dialog_new (void) | |
656 | { | |
657 | return g_object_new (XAPP_TYPE_ICON_CHOOSER_DIALOG, NULL); | |
658 | } | |
659 | ||
660 | /** | |
661 | * xapp_icon_chooser_dialog_run: | |
662 | * @dialog: a #XAppIconChooserDialog | |
663 | * | |
664 | * Shows the dialog and enters a separate main loop until an icon is chosen or the action is canceled. | |
665 | * | |
666 | * xapp_icon_chooser_dialog_run (), xapp_icon_chooser_dialog_run_with_icon(), and | |
667 | * xapp_icon_chooser_dialog_run_with_category () may all be called multiple times. This is useful for | |
668 | * applications which use this dialog multiple times, as it may improve performance for subsequent | |
669 | * calls. | |
670 | * | |
671 | * Returns: GTK_RESPONSE_OK if the user selected an icon, or GTK_RESPONSE_CANCEL otherwise | |
672 | */ | |
673 | gint | |
674 | xapp_icon_chooser_dialog_run (XAppIconChooserDialog *dialog) | |
675 | { | |
676 | XAppIconChooserDialogPrivate *priv; | |
677 | ||
678 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
679 | ||
680 | gtk_widget_show_all (GTK_WIDGET (dialog)); | |
681 | gtk_widget_grab_focus (priv->search_bar); | |
682 | ||
683 | gtk_main (); | |
684 | ||
685 | return priv->response; | |
686 | } | |
687 | ||
688 | /** | |
689 | * xapp_icon_chooser_dialog_run_with_icon: | |
690 | * @dialog: a #XAppIconChooserDialog | |
691 | * @icon: a string representing the icon that should be selected | |
692 | * | |
693 | * Like xapp_icon_chooser_dialog_run but selects the icon specified by @icon. This can be either an | |
694 | * icon name or a path. Passing an icon string or path that doesn't exist is accepted, but it may show | |
695 | * multiple results, or none at all. This behavior is useful if, for example, you wish to have the | |
696 | * user select an image file from a particular directory. | |
697 | * | |
698 | * If the property allow_paths is FALSE, setting a path will yield no results when the dialog is opened. | |
699 | * | |
700 | * xapp_icon_chooser_dialog_run (), xapp_icon_chooser_dialog_run_with_icon(), and | |
701 | * xapp_icon_chooser_dialog_run_with_category () may all be called multiple times. This is useful for | |
702 | * applications which use this dialog multiple times, as it may improve performance for subsequent | |
703 | * calls. | |
704 | * | |
705 | * Returns: GTK_RESPONSE_OK if the user selected an icon, or GTK_RESPONSE_CANCEL otherwise | |
706 | */ | |
707 | gint | |
708 | xapp_icon_chooser_dialog_run_with_icon (XAppIconChooserDialog *dialog, | |
709 | gchar *icon) | |
710 | { | |
711 | XAppIconChooserDialogPrivate *priv; | |
712 | ||
713 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
714 | ||
715 | gtk_widget_show_all (GTK_WIDGET (dialog)); | |
716 | gtk_entry_set_text (GTK_ENTRY (priv->search_bar), icon); | |
717 | gtk_widget_grab_focus (priv->search_bar); | |
718 | ||
719 | gtk_main (); | |
720 | ||
721 | return priv->response; | |
722 | } | |
723 | ||
724 | /** | |
725 | * xapp_icon_chooser_dialog_run_with_category: | |
726 | * @dialog: a #XAppIconChooserDialog | |
727 | * | |
728 | * Like xapp_icon_chooser_dialog_run but selects a particular category specified by @category. | |
729 | * This is used when there is a particular category of icon that is more appropriate than the | |
730 | * others. If the category does not exist, the first category in the list will be selected. To | |
731 | * get a list of possible categories, use gtk_icon_theme_list_contexts (). | |
732 | * | |
733 | * xapp_icon_chooser_dialog_run (), xapp_icon_chooser_dialog_run_with_icon(), and | |
734 | * xapp_icon_chooser_dialog_run_with_category () may all be called multiple times. This is useful for | |
735 | * applications which use this dialog multiple times, as it may improve performance for subsequent | |
736 | * calls. | |
737 | * | |
738 | * Returns: GTK_RESPONSE_OK if the user selected an icon, or GTK_RESPONSE_CANCEL otherwise | |
739 | */ | |
740 | gint | |
741 | xapp_icon_chooser_dialog_run_with_category (XAppIconChooserDialog *dialog, | |
742 | gchar *category) | |
743 | { | |
744 | XAppIconChooserDialogPrivate *priv; | |
745 | GList *children; | |
746 | ||
747 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
748 | ||
749 | gtk_widget_show_all (GTK_WIDGET (dialog)); | |
750 | gtk_widget_grab_focus (priv->search_bar); | |
751 | ||
752 | children = gtk_container_get_children (GTK_CONTAINER (priv->list_box)); | |
753 | for ( ; children; children = children->next) | |
754 | { | |
755 | GtkWidget *row; | |
756 | GtkWidget *child; | |
757 | const gchar *context; | |
758 | ||
759 | row = children->data; | |
760 | child = gtk_bin_get_child (GTK_BIN (row)); | |
761 | context = gtk_label_get_text (GTK_LABEL (child)); | |
762 | if (g_strcmp0 (context, category) == 0) | |
763 | { | |
764 | gtk_list_box_select_row (GTK_LIST_BOX (priv->list_box), GTK_LIST_BOX_ROW (row)); | |
765 | break; | |
766 | } | |
767 | } | |
768 | ||
769 | gtk_main (); | |
770 | ||
771 | return priv->response; | |
772 | } | |
773 | ||
774 | /** | |
775 | * xapp_icon_chooser_dialog_get_icon_string: | |
776 | * @dialog: a #XAppIconChooserDialog | |
777 | * | |
778 | * Gets the currently selected icon from the dialog. If allow-paths is TRUE, this function may return | |
779 | * either an icon name or a path depending on what the user selects. Otherwise it will only return an | |
780 | * icon name. | |
781 | * | |
782 | * Returns: (transfer full): the string representation of the currently selected icon or NULL | |
783 | * if no icon is selected. | |
784 | */ | |
785 | gchar * | |
786 | xapp_icon_chooser_dialog_get_icon_string (XAppIconChooserDialog *dialog) | |
787 | { | |
788 | XAppIconChooserDialogPrivate *priv; | |
789 | ||
790 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
791 | ||
792 | return g_strdup (priv->icon_string); | |
793 | } | |
794 | ||
795 | static void | |
796 | xapp_icon_chooser_dialog_close (XAppIconChooserDialog *dialog, | |
797 | GtkResponseType response) | |
798 | { | |
799 | XAppIconChooserDialogPrivate *priv; | |
800 | ||
801 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
802 | ||
803 | priv->response = response; | |
804 | gtk_widget_hide (GTK_WIDGET (dialog)); | |
805 | ||
806 | gtk_main_quit (); | |
807 | } | |
808 | ||
809 | static void | |
810 | on_custom_button_clicked (GtkButton *button, | |
811 | gpointer user_data) | |
812 | { | |
813 | GtkResponseType response_id; | |
814 | ||
815 | response_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "response-id")); | |
816 | ||
817 | xapp_icon_chooser_dialog_close (XAPP_ICON_CHOOSER_DIALOG (user_data), response_id); | |
818 | } | |
819 | ||
820 | /** | |
821 | * xapp_icon_chooser_dialog_add_button: | |
822 | * @dialog: an #XAppIconChooserDialog | |
823 | * @button: a #GtkButton to add | |
824 | * @packing: the #GtkPackType to specify start or end packing to the action bar | |
825 | * @response_id: the dialog response id to return when this button is clicked. | |
826 | * | |
827 | * Allows a button to be added to the #GtkActionBar of the dialog with a custom | |
828 | * response id. | |
829 | */ | |
830 | void | |
831 | xapp_icon_chooser_dialog_add_button (XAppIconChooserDialog *dialog, | |
832 | GtkWidget *button, | |
833 | GtkPackType packing, | |
834 | GtkResponseType response_id) | |
835 | { | |
836 | XAppIconChooserDialogPrivate *priv; | |
837 | GtkWidget *action_area; | |
838 | ||
839 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
840 | ||
841 | g_signal_connect (button, | |
842 | "clicked", | |
843 | G_CALLBACK (on_custom_button_clicked), | |
844 | dialog); | |
845 | ||
846 | /* This saves having to use a custom container for callback data. */ | |
847 | g_object_set_data (G_OBJECT (button), | |
848 | "response-id", GINT_TO_POINTER (response_id)); | |
849 | ||
850 | if (packing == GTK_PACK_START) | |
851 | { | |
852 | gtk_action_bar_pack_start (GTK_ACTION_BAR (priv->action_area), button); | |
853 | } | |
854 | else | |
855 | { | |
856 | gtk_action_bar_pack_start (GTK_ACTION_BAR (priv->action_area), button); | |
857 | } | |
858 | } | |
859 | ||
860 | static gint | |
861 | list_box_sort (GtkListBoxRow *row1, | |
862 | GtkListBoxRow *row2, | |
863 | gpointer user_data) | |
864 | { | |
865 | GtkWidget *item; | |
866 | const gchar *label1; | |
867 | const gchar *label2; | |
868 | ||
869 | item = gtk_bin_get_child (GTK_BIN (row1)); | |
870 | label1 = gtk_label_get_text (GTK_LABEL (item)); | |
871 | ||
872 | item = gtk_bin_get_child (GTK_BIN (row2)); | |
873 | label2 = gtk_label_get_text (GTK_LABEL (item)); | |
874 | ||
875 | return g_strcmp0 (label1, label2); | |
876 | } | |
877 | ||
878 | static gint | |
879 | search_model_sort (GtkTreeModel *model, | |
880 | GtkTreeIter *a, | |
881 | GtkTreeIter *b, | |
882 | gpointer user_data) | |
883 | { | |
884 | gchar *a_value; | |
885 | gchar *b_value; | |
886 | gchar *search_str; | |
887 | gboolean a_starts_with; | |
888 | gboolean b_starts_with; | |
889 | gint ret; | |
890 | ||
891 | XAppIconChooserDialogPrivate *priv = (XAppIconChooserDialogPrivate *) user_data; | |
892 | ||
893 | search_str = priv->current_text; | |
894 | ||
895 | gtk_tree_model_get (model, a, COLUMN_DISPLAY_NAME, &a_value, -1); | |
896 | gtk_tree_model_get (model, b, COLUMN_DISPLAY_NAME, &b_value, -1); | |
897 | ||
898 | ret = g_strcmp0 (a_value, b_value); | |
899 | ||
900 | if (search_str == NULL) | |
901 | { | |
902 | } | |
903 | else | |
904 | if (g_strcmp0 (a_value, search_str) == 0) | |
905 | { | |
906 | ret = -1; | |
907 | } | |
908 | else | |
909 | if (g_strcmp0 (b_value, search_str) == 0) | |
910 | { | |
911 | ret = 1; | |
912 | } | |
913 | else | |
914 | { | |
915 | a_starts_with = g_str_has_prefix (a_value, search_str); | |
916 | b_starts_with = g_str_has_prefix (b_value, search_str); | |
917 | ||
918 | if (a_starts_with && !b_starts_with) | |
919 | { | |
920 | ret = -1; | |
921 | } | |
922 | ||
923 | if (!a_starts_with && b_starts_with) | |
924 | { | |
925 | ret = 1; | |
926 | } | |
927 | } | |
928 | ||
929 | g_free (a_value); | |
930 | g_free (b_value); | |
931 | ||
932 | return ret; | |
933 | } | |
934 | ||
935 | static void | |
936 | load_categories (XAppIconChooserDialog *dialog) | |
937 | { | |
938 | XAppIconChooserDialogPrivate *priv; | |
939 | GtkListBoxRow *row; | |
940 | GtkIconTheme *theme; | |
941 | GList *contexts, *l; | |
942 | gint i; | |
943 | gint j; | |
944 | ||
945 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
946 | ||
947 | theme = gtk_icon_theme_get_default (); | |
948 | contexts = gtk_icon_theme_list_contexts (theme); | |
949 | ||
950 | for (i = 0; i < G_N_ELEMENTS (categories); i++) | |
951 | { | |
952 | IconCategoryDefinition *category; | |
953 | IconCategoryInfo *category_info; | |
954 | GtkWidget *row; | |
955 | GtkWidget *label; | |
956 | GList *context_icons; | |
957 | ||
958 | category = &categories[i]; | |
959 | ||
960 | category_info = g_new0 (IconCategoryInfo, 1); | |
961 | ||
962 | category_info->name = _(category->name); | |
963 | ||
964 | for (j = 0; category->contexts[j] != NULL; j++) | |
965 | { | |
966 | GList *match; | |
967 | ||
968 | context_icons = gtk_icon_theme_list_icons (theme, category->contexts[j]); | |
969 | category_info->icons = g_list_concat (category_info->icons, context_icons); | |
970 | ||
971 | match = g_list_find_custom (contexts, category->contexts[j], (GCompareFunc) g_strcmp0); | |
972 | ||
973 | if (match) | |
974 | { | |
975 | contexts = g_list_remove_link (contexts, match); | |
976 | g_free (match->data); | |
977 | g_list_free (match); | |
978 | } | |
979 | } | |
980 | ||
981 | /* Any contexts not consumed by categories should be added to the 'other' category */ | |
982 | if (i == (G_N_ELEMENTS (categories) - 1) && g_list_length (contexts) > 0) | |
983 | { | |
984 | for (l = contexts; l != NULL; l = l->next) | |
985 | { | |
986 | ||
987 | #if DEBUG_ICON_THEME | |
988 | g_message ("Adding unused category to Other category: '%s'", (gchar *) l->data); | |
989 | #endif | |
990 | context_icons = gtk_icon_theme_list_icons (theme, (gchar *) l->data); | |
991 | ||
992 | category_info->icons = g_list_concat (category_info->icons, context_icons); | |
993 | } | |
994 | } | |
995 | ||
996 | if (g_list_length (category_info->icons) == 0) | |
997 | { | |
998 | free_category_info (category_info); | |
999 | ||
1000 | continue; | |
1001 | } | |
1002 | ||
1003 | /* Add the list of icons for this category into our master search list */ | |
1004 | priv->full_icon_list = g_list_concat (priv->full_icon_list, | |
1005 | g_list_copy_deep (category_info->icons, (GCopyFunc) g_strdup, NULL)); | |
1006 | ||
1007 | category_info->model = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF); | |
1008 | g_signal_connect (category_info->model, "row-inserted", | |
1009 | G_CALLBACK (on_icon_store_icons_added), dialog); | |
1010 | ||
1011 | category_info->icons = g_list_sort (category_info->icons, (GCompareFunc) g_utf8_collate); | |
1012 | ||
1013 | row = gtk_list_box_row_new (); | |
1014 | label = gtk_label_new (category_info->name); | |
1015 | gtk_label_set_xalign (GTK_LABEL (label), 0.0); | |
1016 | gtk_widget_set_margin_start (GTK_WIDGET (label), 6); | |
1017 | gtk_widget_set_margin_end (GTK_WIDGET (label), 6); | |
1018 | ||
1019 | gtk_container_add (GTK_CONTAINER (row), label); | |
1020 | gtk_container_add (GTK_CONTAINER (priv->list_box), row); | |
1021 | ||
1022 | g_hash_table_insert (priv->categories, row, category_info); | |
1023 | } | |
1024 | ||
1025 | g_list_free_full (contexts, g_free); | |
1026 | ||
1027 | priv->full_icon_list = g_list_sort (priv->full_icon_list, (GCompareFunc) g_utf8_collate); | |
1028 | ||
1029 | row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (priv->list_box), 0); | |
1030 | gtk_list_box_select_row (GTK_LIST_BOX (priv->list_box), row); | |
1031 | } | |
1032 | ||
1033 | GdkPixbuf * | |
1034 | wrangle_pixbuf_size (GdkPixbuf *pixbuf, | |
1035 | gint icon_size) | |
1036 | { | |
1037 | gint width, height, new_width, new_height; | |
1038 | GdkPixbuf *out_pixbuf; | |
1039 | ||
1040 | new_width = new_height = -1; | |
1041 | ||
1042 | width = gdk_pixbuf_get_width (pixbuf); | |
1043 | height = gdk_pixbuf_get_height (pixbuf); | |
1044 | ||
1045 | if ((width > height ? width : height) > icon_size) | |
1046 | { | |
1047 | if (width > icon_size) | |
1048 | { | |
1049 | new_width = icon_size; | |
1050 | new_height = floor (((float) height / width) * new_width); | |
1051 | } | |
1052 | else if (height > icon_size) | |
1053 | { | |
1054 | new_height = icon_size; | |
1055 | new_width = floor (((float) width / height) * new_height); | |
1056 | } | |
1057 | ||
1058 | out_pixbuf = gdk_pixbuf_scale_simple (pixbuf, | |
1059 | new_width, | |
1060 | new_height, | |
1061 | GDK_INTERP_BILINEAR); | |
1062 | } | |
1063 | else | |
1064 | { | |
1065 | out_pixbuf = g_object_ref (pixbuf); | |
1066 | } | |
1067 | ||
1068 | return out_pixbuf; | |
1069 | } | |
1070 | ||
1071 | static gboolean | |
1072 | load_next_file_search_chunk (gpointer user_data) | |
1073 | { | |
1074 | XAppIconChooserDialogPrivate *priv; | |
1075 | FileIconInfoLoadCallbackInfo *callback_info; | |
1076 | ||
1077 | callback_info = (FileIconInfoLoadCallbackInfo*) user_data; | |
1078 | priv = xapp_icon_chooser_dialog_get_instance_private (callback_info->dialog); | |
1079 | ||
1080 | if (g_cancellable_is_cancelled (callback_info->cancellable)) | |
1081 | { | |
1082 | free_file_info (callback_info); | |
1083 | ||
1084 | return G_SOURCE_REMOVE; | |
1085 | } | |
1086 | ||
1087 | search_path (callback_info->dialog, | |
1088 | priv->current_text, | |
1089 | priv->search_icon_store); | |
1090 | ||
1091 | free_file_info (callback_info); | |
1092 | ||
1093 | return G_SOURCE_REMOVE; | |
1094 | } | |
1095 | ||
1096 | static gboolean | |
1097 | load_next_category_chunk (gpointer user_data) | |
1098 | { | |
1099 | XAppIconChooserDialogPrivate *priv; | |
1100 | NamedIconInfoLoadCallbackInfo *callback_info; | |
1101 | ||
1102 | callback_info = (NamedIconInfoLoadCallbackInfo*) user_data; | |
1103 | priv = xapp_icon_chooser_dialog_get_instance_private (callback_info->dialog); | |
1104 | ||
1105 | if (g_cancellable_is_cancelled (callback_info->cancellable)) | |
1106 | { | |
1107 | free_named_info (callback_info); | |
1108 | ||
1109 | return G_SOURCE_REMOVE; | |
1110 | } | |
1111 | ||
1112 | load_icons_for_category (callback_info->dialog, | |
1113 | callback_info->category_info, | |
1114 | priv->icon_size); | |
1115 | ||
1116 | free_named_info (callback_info); | |
1117 | ||
1118 | return G_SOURCE_REMOVE; | |
1119 | } | |
1120 | ||
1121 | static gboolean | |
1122 | load_next_name_search_chunk (gpointer user_data) | |
1123 | { | |
1124 | XAppIconChooserDialogPrivate *priv; | |
1125 | NamedIconInfoLoadCallbackInfo *callback_info; | |
1126 | ||
1127 | callback_info = (NamedIconInfoLoadCallbackInfo*) user_data; | |
1128 | priv = xapp_icon_chooser_dialog_get_instance_private (callback_info->dialog); | |
1129 | ||
1130 | if (g_cancellable_is_cancelled (callback_info->cancellable)) | |
1131 | { | |
1132 | free_named_info (callback_info); | |
1133 | ||
1134 | return G_SOURCE_REMOVE; | |
1135 | } | |
1136 | ||
1137 | search_icon_name (callback_info->dialog, | |
1138 | priv->current_text, | |
1139 | priv->search_icon_store); | |
1140 | ||
1141 | free_named_info (callback_info); | |
1142 | ||
1143 | return G_SOURCE_REMOVE; | |
1144 | } | |
1145 | ||
1146 | static void | |
1147 | finish_pixbuf_load_from_file (GObject *stream, | |
1148 | GAsyncResult *res, | |
1149 | gpointer *user_data) | |
1150 | { | |
1151 | XAppIconChooserDialogPrivate *priv; | |
1152 | FileIconInfoLoadCallbackInfo *callback_info; | |
1153 | GdkPixbuf *pixbuf, *final_pixbuf; | |
1154 | GError *error = NULL; | |
1155 | GtkTreeIter iter; | |
1156 | ||
1157 | callback_info = (FileIconInfoLoadCallbackInfo *) user_data; | |
1158 | priv = xapp_icon_chooser_dialog_get_instance_private (callback_info->dialog); | |
1159 | ||
1160 | pixbuf = gdk_pixbuf_new_from_stream_finish (res, &error); | |
1161 | ||
1162 | g_input_stream_close (G_INPUT_STREAM (stream), NULL, NULL); | |
1163 | g_object_unref (stream); | |
1164 | ||
1165 | if (g_cancellable_is_cancelled (callback_info->cancellable)) | |
1166 | { | |
1167 | g_clear_object (&pixbuf); | |
1168 | free_file_info (callback_info); | |
1169 | ||
1170 | return; | |
1171 | } | |
1172 | ||
1173 | if (pixbuf == NULL) | |
1174 | { | |
1175 | if (error && (error->domain != G_IO_ERROR || error->code != G_IO_ERROR_CANCELLED)) | |
1176 | { | |
1177 | g_message ("%s\n", error->message); | |
1178 | } | |
1179 | ||
1180 | final_pixbuf = NULL; | |
1181 | g_clear_error (&error); | |
1182 | } | |
1183 | else | |
1184 | { | |
1185 | final_pixbuf = wrangle_pixbuf_size (pixbuf, priv->icon_size); | |
1186 | g_object_unref (pixbuf); | |
1187 | } | |
1188 | ||
1189 | if (final_pixbuf) | |
1190 | { | |
1191 | GtkTreeIter iter; | |
1192 | ||
1193 | gtk_list_store_append (callback_info->model, &iter); | |
1194 | gtk_list_store_set (callback_info->model, &iter, | |
1195 | COLUMN_DISPLAY_NAME, callback_info->short_name, | |
1196 | COLUMN_FULL_NAME, callback_info->long_name, | |
1197 | COLUMN_PIXBUF, final_pixbuf, | |
1198 | -1); | |
1199 | ||
1200 | g_object_unref (final_pixbuf); | |
1201 | } | |
1202 | ||
1203 | if (callback_info->chunk_end) | |
1204 | { | |
1205 | g_idle_add ((GSourceFunc) load_next_file_search_chunk, callback_info); | |
1206 | } | |
1207 | else | |
1208 | { | |
1209 | free_file_info (callback_info); | |
1210 | } | |
1211 | ||
1212 | } | |
1213 | ||
1214 | static void | |
1215 | add_named_entry (NamedIconInfoLoadCallbackInfo *callback_info, | |
1216 | GdkPixbuf *pixbuf) | |
1217 | { | |
1218 | GtkTreeIter iter; | |
1219 | ||
1220 | gtk_list_store_append (callback_info->model, &iter); | |
1221 | gtk_list_store_set (callback_info->model, &iter, | |
1222 | COLUMN_DISPLAY_NAME, callback_info->name, | |
1223 | COLUMN_FULL_NAME, callback_info->name, | |
1224 | COLUMN_PIXBUF, pixbuf, | |
1225 | -1); | |
1226 | } | |
1227 | ||
1228 | static gboolean | |
1229 | add_named_entry_with_existing_pixbuf (gpointer user_data) | |
1230 | { | |
1231 | NamedIconInfoLoadCallbackInfo *callback_info; | |
1232 | ||
1233 | callback_info = (NamedIconInfoLoadCallbackInfo*) user_data; | |
1234 | ||
1235 | if (g_cancellable_is_cancelled (callback_info->cancellable)) | |
1236 | { | |
1237 | free_named_info (callback_info); | |
1238 | ||
1239 | return G_SOURCE_REMOVE; | |
1240 | } | |
1241 | ||
1242 | /* Category results have a category_info attached. */ | |
1243 | if (callback_info->category_info) | |
1244 | { | |
1245 | add_named_entry (callback_info, callback_info->pixbuf); | |
1246 | ||
1247 | if (callback_info->chunk_end) | |
1248 | { | |
1249 | g_idle_add ((GSourceFunc) load_next_category_chunk, callback_info); | |
1250 | ||
1251 | return G_SOURCE_REMOVE; | |
1252 | } | |
1253 | } | |
1254 | /* Otherwise, it's a search result set */ | |
1255 | else | |
1256 | { | |
1257 | add_named_entry (callback_info, callback_info->pixbuf); | |
1258 | ||
1259 | if (callback_info->chunk_end) | |
1260 | { | |
1261 | g_idle_add ((GSourceFunc) load_next_name_search_chunk, callback_info); | |
1262 | ||
1263 | return G_SOURCE_REMOVE; | |
1264 | } | |
1265 | } | |
1266 | ||
1267 | free_named_info (callback_info); | |
1268 | ||
1269 | return G_SOURCE_REMOVE; | |
1270 | } | |
1271 | ||
1272 | static void | |
1273 | finish_pixbuf_load_from_name (GObject *info, | |
1274 | GAsyncResult *res, | |
1275 | gpointer *user_data) | |
1276 | { | |
1277 | XAppIconChooserDialogPrivate *priv; | |
1278 | NamedIconInfoLoadCallbackInfo *callback_info; | |
1279 | GdkPixbuf *pixbuf; | |
1280 | GError *error = NULL; | |
1281 | ||
1282 | callback_info = (NamedIconInfoLoadCallbackInfo*) user_data; | |
1283 | priv = xapp_icon_chooser_dialog_get_instance_private (callback_info->dialog); | |
1284 | ||
1285 | pixbuf = gtk_icon_info_load_icon_finish (GTK_ICON_INFO (info), res, &error); | |
1286 | g_object_unref (info); | |
1287 | ||
1288 | if (g_cancellable_is_cancelled (callback_info->cancellable)) | |
1289 | { | |
1290 | g_clear_object (&pixbuf); | |
1291 | ||
1292 | free_named_info (callback_info); | |
1293 | ||
1294 | return; | |
1295 | } | |
1296 | ||
1297 | if (pixbuf == NULL) | |
1298 | { | |
1299 | if (error && (error->domain != G_IO_ERROR || error->code != G_IO_ERROR_CANCELLED)) | |
1300 | { | |
1301 | g_message ("%s\n", error->message); | |
1302 | } | |
1303 | ||
1304 | free_named_info (callback_info); | |
1305 | ||
1306 | g_clear_error (&error); | |
1307 | ||
1308 | return; | |
1309 | } | |
1310 | ||
1311 | /* Hash table 'takes' reference, we don't have to free pixbuf. | |
1312 | callback_info->name is already owned by priv->full_icon_list so | |
1313 | it needs to be copied */ | |
1314 | g_hash_table_insert (priv->pixbufs_by_name, | |
1315 | g_strdup (callback_info->name), | |
1316 | (gpointer) pixbuf); | |
1317 | ||
1318 | /* If there's a category_info, this is a category selection. */ | |
1319 | if (callback_info->category_info) | |
1320 | { | |
1321 | add_named_entry (callback_info, pixbuf); | |
1322 | ||
1323 | if (callback_info->chunk_end) | |
1324 | { | |
1325 | g_idle_add ((GSourceFunc) load_next_category_chunk, callback_info); | |
1326 | ||
1327 | return; | |
1328 | } | |
1329 | } | |
1330 | /* Otherwise, it's a search result set */ | |
1331 | else | |
1332 | { | |
1333 | add_named_entry (callback_info, pixbuf); | |
1334 | ||
1335 | if (callback_info->chunk_end) | |
1336 | { | |
1337 | g_idle_add ((GSourceFunc) load_next_name_search_chunk, callback_info); | |
1338 | ||
1339 | return; | |
1340 | } | |
1341 | } | |
1342 | ||
1343 | free_named_info (callback_info); | |
1344 | } | |
1345 | ||
1346 | #define CATEGORY_CHUNK_SIZE 500 | |
1347 | ||
1348 | static void | |
1349 | load_icons_for_category (XAppIconChooserDialog *dialog, | |
1350 | IconCategoryInfo *category_info, | |
1351 | guint icon_size) | |
1352 | { | |
1353 | XAppIconChooserDialogPrivate *priv; | |
1354 | GtkIconTheme *theme; | |
1355 | gint chunk_count; | |
1356 | ||
1357 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
1358 | theme = gtk_icon_theme_get_default (); | |
1359 | ||
1360 | for (chunk_count = 0; chunk_count < CATEGORY_CHUNK_SIZE; chunk_count++) | |
1361 | { | |
1362 | if (category_info->iter == NULL) | |
1363 | { | |
1364 | category_info->iter = category_info->icons; | |
1365 | } | |
1366 | else | |
1367 | { | |
1368 | category_info->iter = category_info->iter->next; | |
1369 | } | |
1370 | ||
1371 | if (category_info->iter) | |
1372 | { | |
1373 | GdkPixbuf *pixbuf; | |
1374 | NamedIconInfoLoadCallbackInfo *callback_info; | |
1375 | const gchar *name = category_info->iter->data; | |
1376 | ||
1377 | callback_info = g_new0 (NamedIconInfoLoadCallbackInfo, 1); | |
1378 | callback_info->dialog = dialog; | |
1379 | callback_info->category_info = category_info; | |
1380 | callback_info->model = category_info->model; | |
1381 | callback_info->cancellable = g_object_ref (priv->cancellable); | |
1382 | callback_info->name = name; | |
1383 | callback_info->chunk_end = (chunk_count == CATEGORY_CHUNK_SIZE - 1); | |
1384 | ||
1385 | pixbuf = g_hash_table_lookup (priv->pixbufs_by_name, name); | |
1386 | ||
1387 | if (pixbuf != NULL) | |
1388 | { | |
1389 | callback_info->pixbuf = g_object_ref (pixbuf); | |
1390 | g_idle_add ((GSourceFunc) add_named_entry_with_existing_pixbuf, callback_info); | |
1391 | } | |
1392 | else | |
1393 | { | |
1394 | GtkIconInfo *info; | |
1395 | ||
1396 | info = gtk_icon_theme_lookup_icon (theme, name, icon_size, GTK_ICON_LOOKUP_FORCE_SIZE); | |
1397 | gtk_icon_info_load_icon_async (info, NULL, (GAsyncReadyCallback) (finish_pixbuf_load_from_name), callback_info); | |
1398 | } | |
1399 | } | |
1400 | else | |
1401 | { | |
1402 | gtk_widget_hide (priv->loading_bar); | |
1403 | break; // Quit the count early, we're out of data | |
1404 | } | |
1405 | } | |
1406 | } | |
1407 | ||
1408 | static void | |
1409 | on_category_selected (GtkListBox *list_box, | |
1410 | XAppIconChooserDialog *dialog) | |
1411 | { | |
1412 | XAppIconChooserDialogPrivate *priv; | |
1413 | GList *selection; | |
1414 | GtkWidget *selected; | |
1415 | IconCategoryInfo *category_info; | |
1416 | GtkTreePath *new_path; | |
1417 | ||
1418 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
1419 | ||
1420 | clear_search_state (dialog); | |
1421 | ||
1422 | selection = gtk_list_box_get_selected_rows (GTK_LIST_BOX (priv->list_box)); | |
1423 | ||
1424 | if (!selection) | |
1425 | { | |
1426 | return; | |
1427 | } | |
1428 | ||
1429 | gtk_widget_show (priv->loading_bar); | |
1430 | ||
1431 | g_signal_handler_block (priv->search_bar, priv->search_changed_id); | |
1432 | gtk_entry_set_text (GTK_ENTRY (priv->search_bar), ""); | |
1433 | g_signal_handler_unblock (priv->search_bar, priv->search_changed_id); | |
1434 | ||
1435 | selected = selection->data; | |
1436 | category_info = g_hash_table_lookup (priv->categories, selected); | |
1437 | ||
1438 | priv->cancellable = g_cancellable_new (); | |
1439 | ||
1440 | #if DEBUG_REFS | |
1441 | g_object_weak_ref (G_OBJECT (priv->cancellable), (GWeakNotify) on_cancellable_finalize, priv); | |
1442 | #endif | |
1443 | ||
1444 | priv->current_category = category_info; | |
1445 | ||
1446 | gtk_list_store_clear (GTK_LIST_STORE (category_info->model)); | |
1447 | gtk_icon_view_set_model (GTK_ICON_VIEW (priv->icon_view), GTK_TREE_MODEL (category_info->model)); | |
1448 | ||
1449 | load_icons_for_category (dialog, | |
1450 | category_info, | |
1451 | priv->icon_size); | |
1452 | ||
1453 | gtk_adjustment_set_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (priv->icon_view)), 0.0); | |
1454 | ||
1455 | g_list_free (selection); | |
1456 | } | |
1457 | ||
1458 | #define SEARCH_CHUNK_SIZE 2 | |
1459 | ||
1460 | static void | |
1461 | search_path (XAppIconChooserDialog *dialog, | |
1462 | const gchar *path_string, | |
1463 | GtkListStore *icon_store) | |
1464 | { | |
1465 | XAppIconChooserDialogPrivate *priv; | |
1466 | gchar *search_str = NULL; | |
1467 | GFile *dir; | |
1468 | GFileInfo *child_info = NULL;; | |
1469 | gint chunk_count; | |
1470 | ||
1471 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
1472 | ||
1473 | if (g_file_test (path_string, G_FILE_TEST_IS_DIR)) | |
1474 | { | |
1475 | dir = g_file_new_for_path (path_string); | |
1476 | } | |
1477 | else | |
1478 | { | |
1479 | GFile *file; | |
1480 | ||
1481 | file = g_file_new_for_path (path_string); | |
1482 | dir = g_file_get_parent (file); | |
1483 | search_str = g_file_get_basename (file); | |
1484 | g_object_unref (file); | |
1485 | } | |
1486 | ||
1487 | if (!g_file_query_exists (dir, NULL) || | |
1488 | g_file_query_file_type (dir, G_FILE_QUERY_INFO_NONE, NULL) != G_FILE_TYPE_DIRECTORY) | |
1489 | { | |
1490 | g_free (search_str); | |
1491 | g_object_unref (dir); | |
1492 | return; | |
1493 | } | |
1494 | ||
1495 | if (!priv->search_file_enumerator) | |
1496 | { | |
1497 | priv->search_file_enumerator = g_file_enumerate_children (dir, | |
1498 | G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," | |
1499 | G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE "," | |
1500 | G_FILE_ATTRIBUTE_STANDARD_SIZE "," | |
1501 | G_FILE_ATTRIBUTE_STANDARD_NAME, | |
1502 | G_FILE_QUERY_INFO_NONE, NULL, NULL); | |
1503 | ||
1504 | g_object_add_toggle_ref (G_OBJECT (priv->search_file_enumerator), | |
1505 | (GToggleNotify) on_enumerator_toggle_ref_called, | |
1506 | dialog); | |
1507 | ||
1508 | #if DEBUG_REFS | |
1509 | g_object_weak_ref (G_OBJECT (priv->search_file_enumerator), (GWeakNotify) on_enumerator_finalize, dialog); | |
1510 | #endif | |
1511 | } | |
1512 | ||
1513 | chunk_count = 0; | |
1514 | ||
1515 | while (chunk_count < SEARCH_CHUNK_SIZE) | |
1516 | { | |
1517 | const gchar *child_name; | |
1518 | gchar *child_path; | |
1519 | GFile *child; | |
1520 | GError *error = NULL; | |
1521 | ||
1522 | g_clear_object (&child_info); | |
1523 | child_info = g_file_enumerator_next_file (priv->search_file_enumerator, NULL, NULL); | |
1524 | ||
1525 | if (!child_info) | |
1526 | { | |
1527 | break; | |
1528 | } | |
1529 | ||
1530 | child_name = g_file_info_get_name (child_info); | |
1531 | child = g_file_enumerator_get_child (priv->search_file_enumerator, child_info); | |
1532 | child_path = g_file_get_path (child); | |
1533 | ||
1534 | if (search_str == NULL || g_str_has_prefix (child_name, search_str)) | |
1535 | { | |
1536 | priv->current_category = NULL; | |
1537 | ||
1538 | gchar *content_type; | |
1539 | gboolean uncertain; | |
1540 | ||
1541 | content_type = g_content_type_guess (child_name, NULL, 0, &uncertain); | |
1542 | ||
1543 | if (content_type && g_str_has_prefix (content_type, "image") && !uncertain) | |
1544 | { | |
1545 | GFileInputStream *stream; | |
1546 | ||
1547 | stream = g_file_read (child, NULL, &error); | |
1548 | ||
1549 | if (stream != NULL) | |
1550 | { | |
1551 | FileIconInfoLoadCallbackInfo *callback_info; | |
1552 | ||
1553 | callback_info = g_new0 (FileIconInfoLoadCallbackInfo, 1); | |
1554 | callback_info->dialog = dialog; | |
1555 | callback_info->model = icon_store; | |
1556 | callback_info->cancellable = g_object_ref (priv->cancellable); | |
1557 | callback_info->enumerator = g_object_ref (priv->search_file_enumerator); | |
1558 | callback_info->short_name = g_strdup (child_name); | |
1559 | callback_info->long_name = g_strdup (child_path); | |
1560 | callback_info->chunk_end = (chunk_count == SEARCH_CHUNK_SIZE - 1); | |
1561 | ||
1562 | gdk_pixbuf_new_from_stream_async (G_INPUT_STREAM (stream), | |
1563 | NULL, | |
1564 | (GAsyncReadyCallback) finish_pixbuf_load_from_file, | |
1565 | callback_info); | |
1566 | ||
1567 | chunk_count ++; | |
1568 | } | |
1569 | else | |
1570 | { | |
1571 | if (error) | |
1572 | { | |
1573 | g_message ("%s\n", error->message); | |
1574 | g_error_free (error); | |
1575 | } | |
1576 | } | |
1577 | } | |
1578 | ||
1579 | g_free (content_type); | |
1580 | } | |
1581 | ||
1582 | g_free (child_path); | |
1583 | g_object_unref (child); | |
1584 | } | |
1585 | ||
1586 | if (!child_info) | |
1587 | { | |
1588 | if (priv->search_file_enumerator) | |
1589 | { | |
1590 | g_object_unref (priv->search_file_enumerator); | |
1591 | } | |
1592 | ||
1593 | gtk_widget_hide (priv->loading_bar); | |
1594 | } | |
1595 | else | |
1596 | { | |
1597 | g_clear_object (&child_info); | |
1598 | } | |
1599 | ||
1600 | g_free (search_str); | |
1601 | g_object_unref (dir); | |
1602 | } | |
1603 | ||
1604 | static void | |
1605 | search_icon_name (XAppIconChooserDialog *dialog, | |
1606 | const gchar *name_string, | |
1607 | GtkListStore *icon_store) | |
1608 | { | |
1609 | XAppIconChooserDialogPrivate *priv; | |
1610 | GtkIconTheme *theme; | |
1611 | GList *icons; | |
1612 | GtkIconInfo *info; | |
1613 | NamedIconInfoLoadCallbackInfo *callback_info; | |
1614 | gint chunk_count; | |
1615 | ||
1616 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
1617 | ||
1618 | theme = gtk_icon_theme_get_default (); | |
1619 | ||
1620 | icons = priv->full_icon_list; | |
1621 | ||
1622 | chunk_count = 0; | |
1623 | ||
1624 | while (chunk_count < SEARCH_CHUNK_SIZE) | |
1625 | { | |
1626 | if (priv->search_iter == NULL) | |
1627 | { | |
1628 | priv->search_iter = icons; | |
1629 | } | |
1630 | else | |
1631 | { | |
1632 | priv->search_iter = priv->search_iter->next; | |
1633 | } | |
1634 | ||
1635 | if (priv->search_iter) | |
1636 | { | |
1637 | priv->current_category = NULL; | |
1638 | ||
1639 | if (g_strrstr (priv->search_iter->data, name_string)) | |
1640 | { | |
1641 | GdkPixbuf *pixbuf; | |
1642 | NamedIconInfoLoadCallbackInfo *callback_info; | |
1643 | const gchar *name = priv->search_iter->data; | |
1644 | ||
1645 | callback_info = g_new0 (NamedIconInfoLoadCallbackInfo, 1); | |
1646 | callback_info->dialog = dialog; | |
1647 | callback_info->model = priv->search_icon_store; | |
1648 | callback_info->cancellable = g_object_ref (priv->cancellable); | |
1649 | callback_info->category_info = NULL; | |
1650 | callback_info->name = name; | |
1651 | callback_info->chunk_end = (chunk_count == SEARCH_CHUNK_SIZE - 1); | |
1652 | ||
1653 | pixbuf = g_hash_table_lookup (priv->pixbufs_by_name, name); | |
1654 | ||
1655 | if (pixbuf != NULL) | |
1656 | { | |
1657 | callback_info->pixbuf = g_object_ref (pixbuf); | |
1658 | g_idle_add ((GSourceFunc) add_named_entry_with_existing_pixbuf, callback_info); | |
1659 | } | |
1660 | else | |
1661 | { | |
1662 | GtkIconInfo *info; | |
1663 | ||
1664 | info = gtk_icon_theme_lookup_icon (theme, name, priv->icon_size, GTK_ICON_LOOKUP_FORCE_SIZE); | |
1665 | gtk_icon_info_load_icon_async (info, NULL, (GAsyncReadyCallback) (finish_pixbuf_load_from_name), callback_info); | |
1666 | } | |
1667 | ||
1668 | chunk_count++; | |
1669 | } | |
1670 | } | |
1671 | else | |
1672 | { | |
1673 | gtk_widget_hide (priv->loading_bar); | |
1674 | ||
1675 | break; // Quit the count early, we're out of data | |
1676 | } | |
1677 | } | |
1678 | } | |
1679 | ||
1680 | static void | |
1681 | on_search_text_changed (GtkSearchEntry *entry, | |
1682 | XAppIconChooserDialog *dialog) | |
1683 | { | |
1684 | XAppIconChooserDialogPrivate *priv; | |
1685 | const gchar *search_text; | |
1686 | ||
1687 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
1688 | ||
1689 | /* The search cancellable is carried in search callback data. If the | |
1690 | * text changes, we cancel it here, our load callbacks will check the | |
1691 | * state, and react appropriately (like not adding results to the model). */ | |
1692 | clear_search_state (dialog); | |
1693 | ||
1694 | gtk_list_box_select_row (GTK_LIST_BOX (priv->list_box), NULL); | |
1695 | ||
1696 | priv->cancellable = g_cancellable_new (); | |
1697 | ||
1698 | #if DEBUG_REFS | |
1699 | g_object_weak_ref (G_OBJECT (priv->cancellable), (GWeakNotify) on_cancellable_finalize, dialog); | |
1700 | #endif | |
1701 | ||
1702 | search_text = gtk_entry_get_text (GTK_ENTRY (entry)); | |
1703 | ||
1704 | if (g_strcmp0 (search_text, "") == 0) | |
1705 | { | |
1706 | g_clear_pointer (&priv->current_text, g_free); | |
1707 | g_clear_pointer (&priv->icon_string, g_free); | |
1708 | ||
1709 | gtk_widget_hide (priv->loading_bar); | |
1710 | ||
1711 | gtk_list_store_clear (GTK_LIST_STORE (priv->search_icon_store)); | |
1712 | } | |
1713 | else | |
1714 | if (strlen (search_text) < 2) | |
1715 | { | |
1716 | return; | |
1717 | } | |
1718 | else | |
1719 | { | |
1720 | g_free (priv->current_text); | |
1721 | priv->current_text = g_strdup (search_text); | |
1722 | ||
1723 | gtk_widget_show (priv->loading_bar); | |
1724 | ||
1725 | gtk_list_store_clear (GTK_LIST_STORE (priv->search_icon_store)); | |
1726 | gtk_icon_view_set_model (GTK_ICON_VIEW (priv->icon_view), GTK_TREE_MODEL (priv->search_icon_store)); | |
1727 | if (g_strrstr (search_text, "/")) | |
1728 | { | |
1729 | if (priv->allow_paths) | |
1730 | { | |
1731 | search_path (dialog, search_text, priv->search_icon_store); | |
1732 | } | |
1733 | } | |
1734 | else | |
1735 | { | |
1736 | search_icon_name (dialog, search_text, priv->search_icon_store); | |
1737 | } | |
1738 | } | |
1739 | } | |
1740 | ||
1741 | static void | |
1742 | on_icon_view_selection_changed (GtkIconView *icon_view, | |
1743 | gpointer user_data) | |
1744 | { | |
1745 | XAppIconChooserDialogPrivate *priv; | |
1746 | GList *selected_items; | |
1747 | gchar *icon_string = NULL; | |
1748 | ||
1749 | priv = xapp_icon_chooser_dialog_get_instance_private (user_data); | |
1750 | ||
1751 | selected_items = gtk_icon_view_get_selected_items (icon_view); | |
1752 | if (selected_items == NULL) | |
1753 | { | |
1754 | gtk_widget_set_sensitive (GTK_WIDGET (priv->select_button), FALSE); | |
1755 | } | |
1756 | else | |
1757 | { | |
1758 | GtkTreePath *tree_path; | |
1759 | GtkTreeModel *model; | |
1760 | GtkTreeIter iter; | |
1761 | ||
1762 | gtk_widget_set_sensitive (GTK_WIDGET (priv->select_button), TRUE); | |
1763 | ||
1764 | tree_path = selected_items->data; | |
1765 | model = gtk_icon_view_get_model (icon_view); | |
1766 | gtk_tree_model_get_iter (model, &iter, tree_path); | |
1767 | gtk_tree_model_get (model, &iter, COLUMN_FULL_NAME, &icon_string, -1); | |
1768 | } | |
1769 | ||
1770 | if (priv->icon_string != NULL) | |
1771 | { | |
1772 | g_free (priv->icon_string); | |
1773 | } | |
1774 | ||
1775 | priv->icon_string = icon_string; | |
1776 | ||
1777 | g_list_free_full (selected_items, (GDestroyNotify) gtk_tree_path_free); | |
1778 | } | |
1779 | ||
1780 | static void | |
1781 | on_icon_store_icons_added (GtkTreeModel *tree_model, | |
1782 | GtkTreePath *path, | |
1783 | GtkTreeIter *iter, | |
1784 | gpointer user_data) | |
1785 | { | |
1786 | XAppIconChooserDialogPrivate *priv; | |
1787 | GtkTreePath *new_path; | |
1788 | ||
1789 | priv = xapp_icon_chooser_dialog_get_instance_private (user_data); | |
1790 | ||
1791 | if (tree_model != gtk_icon_view_get_model (GTK_ICON_VIEW (priv->icon_view))) { | |
1792 | return; | |
1793 | } | |
1794 | ||
1795 | new_path = gtk_tree_path_new_first (); | |
1796 | gtk_icon_view_select_path (GTK_ICON_VIEW (priv->icon_view), new_path); | |
1797 | ||
1798 | gtk_tree_path_free (new_path); | |
1799 | } | |
1800 | ||
1801 | static void | |
1802 | on_browse_button_clicked (GtkButton *button, | |
1803 | gpointer user_data) | |
1804 | { | |
1805 | XAppIconChooserDialog *dialog = user_data; | |
1806 | XAppIconChooserDialogPrivate *priv; | |
1807 | GtkWidget *file_dialog; | |
1808 | const gchar *search_text; | |
1809 | GtkFileFilter *file_filter; | |
1810 | gint response; | |
1811 | ||
1812 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
1813 | ||
1814 | file_dialog = gtk_file_chooser_dialog_new (_("Select image file"), | |
1815 | GTK_WINDOW (dialog), | |
1816 | GTK_FILE_CHOOSER_ACTION_OPEN, | |
1817 | _("Cancel"), | |
1818 | GTK_RESPONSE_CANCEL, | |
1819 | _("Open"), | |
1820 | GTK_RESPONSE_ACCEPT, | |
1821 | NULL); | |
1822 | ||
1823 | search_text = gtk_entry_get_text (GTK_ENTRY (priv->search_bar)); | |
1824 | if (g_strrstr (search_text, "/")) | |
1825 | { | |
1826 | gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (file_dialog), search_text); | |
1827 | } | |
1828 | else | |
1829 | { | |
1830 | gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (file_dialog), "/usr/share/icons/"); | |
1831 | } | |
1832 | ||
1833 | file_filter = gtk_file_filter_new (); | |
1834 | gtk_file_filter_set_name (file_filter, _("Image")); | |
1835 | gtk_file_filter_add_pixbuf_formats (file_filter); | |
1836 | gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_dialog), file_filter); | |
1837 | ||
1838 | response = gtk_dialog_run (GTK_DIALOG (file_dialog)); | |
1839 | if (response == GTK_RESPONSE_ACCEPT) | |
1840 | { | |
1841 | gchar *filename; | |
1842 | ||
1843 | filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_dialog)); | |
1844 | gtk_entry_set_text (GTK_ENTRY (priv->search_bar), filename); | |
1845 | g_free (filename); | |
1846 | } | |
1847 | ||
1848 | gtk_widget_destroy (file_dialog); | |
1849 | } | |
1850 | ||
1851 | static void | |
1852 | on_select_button_clicked (GtkButton *button, | |
1853 | gpointer user_data) | |
1854 | { | |
1855 | xapp_icon_chooser_dialog_close (XAPP_ICON_CHOOSER_DIALOG (user_data), GTK_RESPONSE_OK); | |
1856 | } | |
1857 | ||
1858 | static void | |
1859 | on_cancel_button_clicked (GtkButton *button, | |
1860 | gpointer user_data) | |
1861 | { | |
1862 | xapp_icon_chooser_dialog_close (XAPP_ICON_CHOOSER_DIALOG (user_data), GTK_RESPONSE_CANCEL); | |
1863 | } | |
1864 | ||
1865 | static gboolean | |
1866 | on_delete_event (GtkWidget *widget, | |
1867 | GdkEventAny *event) | |
1868 | { | |
1869 | xapp_icon_chooser_dialog_close (XAPP_ICON_CHOOSER_DIALOG (widget), GTK_RESPONSE_CANCEL); | |
1870 | ||
1871 | return TRUE; | |
1872 | } | |
1873 | ||
1874 | static gboolean | |
1875 | on_select_event (XAppIconChooserDialog *dialog, | |
1876 | GdkEventAny *event) | |
1877 | { | |
1878 | XAppIconChooserDialogPrivate *priv; | |
1879 | ||
1880 | priv = xapp_icon_chooser_dialog_get_instance_private (dialog); | |
1881 | ||
1882 | if (priv->icon_string != NULL) | |
1883 | { | |
1884 | xapp_icon_chooser_dialog_close (dialog, GTK_RESPONSE_OK); | |
1885 | } | |
1886 | } | |
1887 | ||
1888 | static void | |
1889 | on_icon_view_item_activated (GtkIconView *iconview, | |
1890 | GtkTreePath *path, | |
1891 | gpointer user_data) | |
1892 | { | |
1893 | xapp_icon_chooser_dialog_close (XAPP_ICON_CHOOSER_DIALOG (user_data), GTK_RESPONSE_OK); | |
1894 | } | |
1895 | ||
1896 | static gboolean | |
1897 | on_search_bar_key_pressed (GtkWidget *widget, | |
1898 | GdkEvent *event, | |
1899 | gpointer user_data) | |
1900 | { | |
1901 | XAppIconChooserDialogPrivate *priv; | |
1902 | guint keyval; | |
1903 | ||
1904 | priv = xapp_icon_chooser_dialog_get_instance_private (user_data); | |
1905 | ||
1906 | gdk_event_get_keyval (event, &keyval); | |
1907 | switch (keyval) | |
1908 | { | |
1909 | case GDK_KEY_Escape: | |
1910 | xapp_icon_chooser_dialog_close (XAPP_ICON_CHOOSER_DIALOG (user_data), GTK_RESPONSE_CANCEL); | |
1911 | return TRUE; | |
1912 | case GDK_KEY_Return: | |
1913 | case GDK_KEY_KP_Enter: | |
1914 | if (priv->icon_string != NULL) | |
1915 | { | |
1916 | xapp_icon_chooser_dialog_close (XAPP_ICON_CHOOSER_DIALOG (user_data), GTK_RESPONSE_OK); | |
1917 | } | |
1918 | return TRUE; | |
1919 | } | |
1920 | ||
1921 | return FALSE; | |
1922 | } |
0 | #ifndef _XAPP_ICON_CHOOSER_DIALOG_H_ | |
1 | #define _XAPP_ICON_CHOOSER_DIALOG_H_ | |
2 | ||
3 | #include <glib-object.h> | |
4 | #include <gtk/gtk.h> | |
5 | ||
6 | #include "xapp-gtk-window.h" | |
7 | ||
8 | G_BEGIN_DECLS | |
9 | ||
10 | #define XAPP_TYPE_ICON_CHOOSER_DIALOG (xapp_icon_chooser_dialog_get_type ()) | |
11 | ||
12 | G_DECLARE_FINAL_TYPE (XAppIconChooserDialog, xapp_icon_chooser_dialog, XAPP, ICON_CHOOSER_DIALOG, XAppGtkWindow) | |
13 | ||
14 | typedef enum | |
15 | { | |
16 | XAPP_ICON_SIZE_16 = 16, | |
17 | XAPP_ICON_SIZE_22 = 22, | |
18 | XAPP_ICON_SIZE_24 = 24, | |
19 | XAPP_ICON_SIZE_32 = 32, | |
20 | XAPP_ICON_SIZE_48 = 48, | |
21 | XAPP_ICON_SIZE_96 = 96 | |
22 | } XAppIconSize; | |
23 | ||
24 | XAppIconChooserDialog * xapp_icon_chooser_dialog_new (void); | |
25 | ||
26 | gint xapp_icon_chooser_dialog_run (XAppIconChooserDialog *dialog); | |
27 | ||
28 | gint xapp_icon_chooser_dialog_run_with_icon (XAppIconChooserDialog *dialog, | |
29 | gchar *icon); | |
30 | ||
31 | gint xapp_icon_chooser_dialog_run_with_category (XAppIconChooserDialog *dialog, | |
32 | gchar *category); | |
33 | ||
34 | gchar * xapp_icon_chooser_dialog_get_icon_string (XAppIconChooserDialog *dialog); | |
35 | ||
36 | void xapp_icon_chooser_dialog_add_button (XAppIconChooserDialog *dialog, | |
37 | GtkWidget *button, | |
38 | GtkPackType packing, | |
39 | GtkResponseType response_id); | |
40 | G_END_DECLS | |
41 | ||
42 | #endif /* _XAPP_ICON_CHOOSER_DIALOG_H_ */ |
0 | 0 | #include <gdk/gdk.h> |
1 | 1 | #include "xapp-preferences-window.h" |
2 | #include "xapp-stack-sidebar.h" | |
2 | 3 | |
3 | 4 | /** |
4 | 5 | * SECTION:xapp-preferences-window |
13 | 14 | |
14 | 15 | typedef struct |
15 | 16 | { |
16 | GtkWidget *stack; | |
17 | GtkWidget *side_switcher; | |
18 | GtkWidget *button_area; | |
19 | GtkSizeGroup *button_size_group; | |
17 | GtkWidget *stack; | |
18 | XAppStackSidebar *side_switcher; | |
19 | GtkWidget *button_area; | |
20 | 20 | |
21 | gint num_pages; | |
21 | gint num_pages; | |
22 | 22 | } XAppPreferencesWindowPrivate; |
23 | 23 | |
24 | 24 | enum |
37 | 37 | XAppPreferencesWindowPrivate *priv = xapp_preferences_window_get_instance_private (window); |
38 | 38 | GtkWidget *main_box; |
39 | 39 | GtkWidget *secondary_box; |
40 | GtkStyleContext *style_context; | |
40 | 41 | |
41 | 42 | gtk_window_set_default_size (GTK_WINDOW (window), 600, 400); |
42 | 43 | gtk_window_set_skip_taskbar_hint (GTK_WINDOW (window), TRUE); |
43 | 44 | gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DIALOG); |
45 | gtk_container_set_border_width (GTK_CONTAINER (window), 5); | |
44 | 46 | |
45 | 47 | main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); |
48 | gtk_container_set_border_width (GTK_CONTAINER (main_box), 5); | |
46 | 49 | gtk_container_add (GTK_CONTAINER (window), main_box); |
47 | 50 | |
48 | 51 | secondary_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); |
52 | gtk_container_set_border_width (GTK_CONTAINER (secondary_box), 5); | |
49 | 53 | gtk_box_pack_start (GTK_BOX (main_box), secondary_box, TRUE, TRUE, 0); |
50 | 54 | |
51 | priv->side_switcher = gtk_stack_sidebar_new (); | |
52 | gtk_widget_set_size_request (priv->side_switcher, 100, -1); | |
53 | gtk_box_pack_start (GTK_BOX (secondary_box), priv->side_switcher, FALSE, FALSE, 0); | |
54 | gtk_widget_set_no_show_all (priv->side_switcher, TRUE); | |
55 | style_context = gtk_widget_get_style_context (secondary_box); | |
56 | gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_FRAME); | |
57 | ||
58 | priv->side_switcher = xapp_stack_sidebar_new (); | |
59 | gtk_widget_set_size_request (GTK_WIDGET (priv->side_switcher), 100, -1); | |
60 | gtk_box_pack_start (GTK_BOX (secondary_box), GTK_WIDGET (priv->side_switcher), FALSE, FALSE, 0); | |
61 | gtk_widget_set_no_show_all (GTK_WIDGET (priv->side_switcher), TRUE); | |
55 | 62 | |
56 | 63 | priv->stack = gtk_stack_new (); |
57 | 64 | gtk_stack_set_transition_type (GTK_STACK (priv->stack), GTK_STACK_TRANSITION_TYPE_CROSSFADE); |
58 | 65 | gtk_box_pack_start (GTK_BOX (secondary_box), priv->stack, TRUE, TRUE, 0); |
59 | gtk_stack_sidebar_set_stack (GTK_STACK_SIDEBAR (priv->side_switcher), GTK_STACK (priv->stack)); | |
66 | xapp_stack_sidebar_set_stack (priv->side_switcher, GTK_STACK (priv->stack)); | |
60 | 67 | |
61 | priv->button_area = gtk_action_bar_new (); | |
68 | style_context = gtk_widget_get_style_context (priv->stack); | |
69 | gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_VIEW); | |
70 | ||
71 | priv->button_area = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); | |
72 | gtk_container_set_border_width (GTK_CONTAINER (priv->button_area), 5); | |
62 | 73 | gtk_box_pack_start (GTK_BOX (main_box), priv->button_area, FALSE, FALSE, 0); |
63 | 74 | gtk_widget_set_no_show_all (priv->button_area, TRUE); |
64 | ||
65 | priv->button_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); | |
66 | 75 | |
67 | 76 | /* Keep track of the number of pages so we can hide the stack switcher with < 2 */ |
68 | 77 | priv->num_pages = 0; |
134 | 143 | |
135 | 144 | if (priv->num_pages > 1) |
136 | 145 | { |
137 | gtk_widget_set_no_show_all (priv->side_switcher, FALSE); | |
146 | gtk_widget_set_no_show_all (GTK_WIDGET (priv->side_switcher), FALSE); | |
138 | 147 | } |
139 | 148 | } |
140 | 149 | |
160 | 169 | g_return_if_fail (XAPP_IS_PREFERENCES_WINDOW (window)); |
161 | 170 | g_return_if_fail (GTK_IS_WIDGET (button)); |
162 | 171 | |
163 | if (pack_type == GTK_PACK_START) | |
172 | gtk_container_add (GTK_CONTAINER (priv->button_area), button); | |
173 | ||
174 | if (pack_type == GTK_PACK_END) | |
164 | 175 | { |
165 | gtk_action_bar_pack_start (GTK_ACTION_BAR (priv->button_area), button); | |
166 | gtk_widget_set_margin_start (button, 6); | |
176 | gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (priv->button_area), button, TRUE); | |
167 | 177 | } |
168 | else if (pack_type == GTK_PACK_END) | |
169 | { | |
170 | gtk_action_bar_pack_end (GTK_ACTION_BAR (priv->button_area), button); | |
171 | gtk_widget_set_margin_end (button, 6); | |
172 | } | |
173 | else | |
178 | else if (pack_type != GTK_PACK_START) | |
174 | 179 | { |
175 | 180 | return; |
176 | 181 | } |
178 | 183 | style_context = gtk_widget_get_style_context (button); |
179 | 184 | gtk_style_context_add_class (style_context, "text-button"); |
180 | 185 | |
181 | gtk_size_group_add_widget (priv->button_size_group, button); | |
182 | 186 | gtk_widget_set_no_show_all (priv->button_area, FALSE); |
183 | }⏎ | |
187 | } |
0 | /* Based on gtkstacksidebar.c */ | |
1 | ||
2 | #include "xapp-stack-sidebar.h" | |
3 | ||
4 | /** | |
5 | * SECTION:xapp-stack-sidebar | |
6 | * @Title: XAppStackSidebar | |
7 | * @Short_description: An automatic sidebar widget | |
8 | * | |
9 | * A XAppStackSidebar allows you to quickly and easily provide a | |
10 | * consistent "sidebar" object for your user interface | |
11 | * | |
12 | * In order to use a XAppStackSidebar, you simply use a GtkStack to | |
13 | * organize your UI flow, and add the sidebar to your sidebar area. You | |
14 | * can use xapp_stack_sidebar_set_stack() to connect the #XAppStackSidebar | |
15 | * to the #GtkStack. The #XAppStackSidebar is an extended version of the | |
16 | * the #GtkStackSidebar that allows showing an icon in addition to the text. | |
17 | * | |
18 | * # CSS nodes | |
19 | * | |
20 | * XAppStackSidebar has a single CSS node with the name stacksidebar and | |
21 | * style class .sidebar | |
22 | * | |
23 | * When circumstances require it, XAppStackSidebar adds the | |
24 | * .needs-attention style class to the widgets representing the stack | |
25 | * pages. | |
26 | */ | |
27 | ||
28 | ||
29 | struct _XAppStackSidebar | |
30 | { | |
31 | GtkBin parent_instance; | |
32 | ||
33 | GtkListBox *list; | |
34 | GtkStack *stack; | |
35 | GHashTable *rows; | |
36 | gboolean in_child_changed; | |
37 | }; | |
38 | ||
39 | enum | |
40 | { | |
41 | PROP_0, | |
42 | PROP_STACK, | |
43 | N_PROPERTIES | |
44 | }; | |
45 | ||
46 | static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; | |
47 | ||
48 | G_DEFINE_TYPE (XAppStackSidebar, xapp_stack_sidebar, GTK_TYPE_BIN) | |
49 | ||
50 | static void | |
51 | xapp_stack_sidebar_set_property (GObject *object, | |
52 | guint prop_id, | |
53 | const GValue *value, | |
54 | GParamSpec *pspec) | |
55 | { | |
56 | XAppStackSidebar *sidebar = XAPP_STACK_SIDEBAR (object); | |
57 | ||
58 | switch (prop_id) | |
59 | { | |
60 | case PROP_STACK: | |
61 | xapp_stack_sidebar_set_stack (XAPP_STACK_SIDEBAR (object), g_value_get_object (value)); | |
62 | break; | |
63 | default: | |
64 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
65 | break; | |
66 | } | |
67 | } | |
68 | ||
69 | static void | |
70 | xapp_stack_sidebar_get_property (GObject *object, | |
71 | guint prop_id, | |
72 | GValue *value, | |
73 | GParamSpec *pspec) | |
74 | { | |
75 | XAppStackSidebar *sidebar = XAPP_STACK_SIDEBAR (object); | |
76 | ||
77 | switch (prop_id) | |
78 | { | |
79 | case PROP_STACK: | |
80 | g_value_set_object (value, sidebar->stack); | |
81 | break; | |
82 | default: | |
83 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
84 | break; | |
85 | } | |
86 | } | |
87 | ||
88 | static gint | |
89 | sort_list (GtkListBoxRow *row1, | |
90 | GtkListBoxRow *row2, | |
91 | gpointer userdata) | |
92 | { | |
93 | XAppStackSidebar *sidebar = XAPP_STACK_SIDEBAR (userdata); | |
94 | GtkWidget *item; | |
95 | GtkWidget *widget; | |
96 | gint left = 0; | |
97 | gint right = 0; | |
98 | ||
99 | ||
100 | if (row1) | |
101 | { | |
102 | item = gtk_bin_get_child (GTK_BIN (row1)); | |
103 | widget = g_object_get_data (G_OBJECT (item), "stack-child"); | |
104 | gtk_container_child_get (GTK_CONTAINER (sidebar->stack), widget, | |
105 | "position", &left, | |
106 | NULL); | |
107 | } | |
108 | ||
109 | if (row2) | |
110 | { | |
111 | item = gtk_bin_get_child (GTK_BIN (row2)); | |
112 | widget = g_object_get_data (G_OBJECT (item), "stack-child"); | |
113 | gtk_container_child_get (GTK_CONTAINER (sidebar->stack), widget, | |
114 | "position", &right, | |
115 | NULL); | |
116 | } | |
117 | ||
118 | if (left < right) | |
119 | { | |
120 | return -1; | |
121 | } | |
122 | ||
123 | if (left == right) | |
124 | { | |
125 | return 0; | |
126 | } | |
127 | ||
128 | return 1; | |
129 | } | |
130 | ||
131 | static void | |
132 | xapp_stack_sidebar_row_selected (GtkListBox *box, | |
133 | GtkListBoxRow *row, | |
134 | gpointer user_data) | |
135 | { | |
136 | XAppStackSidebar *sidebar = XAPP_STACK_SIDEBAR (user_data); | |
137 | GtkWidget *item; | |
138 | GtkWidget *widget; | |
139 | ||
140 | if (sidebar->in_child_changed) | |
141 | { | |
142 | return; | |
143 | } | |
144 | ||
145 | if (!row) | |
146 | { | |
147 | return; | |
148 | } | |
149 | ||
150 | item = gtk_bin_get_child (GTK_BIN (row)); | |
151 | widget = g_object_get_data (G_OBJECT (item), "stack-child"); | |
152 | gtk_stack_set_visible_child (sidebar->stack, widget); | |
153 | } | |
154 | ||
155 | static void | |
156 | xapp_stack_sidebar_init (XAppStackSidebar *sidebar) | |
157 | { | |
158 | GtkStyleContext *style; | |
159 | GtkWidget *sw; | |
160 | ||
161 | sw = gtk_scrolled_window_new (NULL, NULL); | |
162 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), | |
163 | GTK_POLICY_NEVER, | |
164 | GTK_POLICY_AUTOMATIC); | |
165 | ||
166 | gtk_container_add (GTK_CONTAINER (sidebar), sw); | |
167 | ||
168 | sidebar->list = GTK_LIST_BOX (gtk_list_box_new ()); | |
169 | ||
170 | gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (sidebar->list)); | |
171 | ||
172 | gtk_list_box_set_sort_func (sidebar->list, sort_list, sidebar, NULL); | |
173 | ||
174 | g_signal_connect (sidebar->list, "row-selected", | |
175 | G_CALLBACK (xapp_stack_sidebar_row_selected), sidebar); | |
176 | ||
177 | style = gtk_widget_get_style_context (GTK_WIDGET (sidebar)); | |
178 | gtk_style_context_add_class (style, "sidebar"); | |
179 | ||
180 | gtk_widget_show_all (GTK_WIDGET (sidebar)); | |
181 | ||
182 | sidebar->rows = g_hash_table_new (NULL, NULL); | |
183 | } | |
184 | ||
185 | static void | |
186 | update_row (XAppStackSidebar *sidebar, | |
187 | GtkWidget *widget, | |
188 | GtkWidget *row) | |
189 | { | |
190 | GList *children; | |
191 | GList *list; | |
192 | GtkWidget *item; | |
193 | gchar *title; | |
194 | gchar *icon_name; | |
195 | gboolean needs_attention; | |
196 | GtkStyleContext *context; | |
197 | ||
198 | gtk_container_child_get (GTK_CONTAINER (sidebar->stack), | |
199 | widget, | |
200 | "title", &title, | |
201 | "icon-name", &icon_name, | |
202 | "needs-attention", &needs_attention, | |
203 | NULL); | |
204 | ||
205 | item = gtk_bin_get_child (GTK_BIN (row)); | |
206 | ||
207 | children = gtk_container_get_children (GTK_CONTAINER (item)); | |
208 | for (list = children; list != NULL; list = list->next) | |
209 | { | |
210 | GtkWidget *child = list->data; | |
211 | ||
212 | if (GTK_IS_LABEL (child)) | |
213 | { | |
214 | gtk_label_set_text (GTK_LABEL (child), title); | |
215 | } | |
216 | else if (GTK_IS_IMAGE (child)) | |
217 | { | |
218 | gtk_image_set_from_icon_name (GTK_IMAGE (child), icon_name, GTK_ICON_SIZE_MENU); | |
219 | } | |
220 | } | |
221 | ||
222 | gtk_widget_set_visible (row, gtk_widget_get_visible (widget) && (title != NULL || icon_name != NULL)); | |
223 | ||
224 | context = gtk_widget_get_style_context (row); | |
225 | if (needs_attention) | |
226 | { | |
227 | gtk_style_context_add_class (context, GTK_STYLE_CLASS_NEEDS_ATTENTION); | |
228 | } | |
229 | else | |
230 | { | |
231 | gtk_style_context_remove_class (context, GTK_STYLE_CLASS_NEEDS_ATTENTION); | |
232 | } | |
233 | ||
234 | g_free (title); | |
235 | g_free (icon_name); | |
236 | g_list_free (children); | |
237 | } | |
238 | ||
239 | static void | |
240 | on_position_updated (GtkWidget *widget, | |
241 | GParamSpec *pspec, | |
242 | XAppStackSidebar *sidebar) | |
243 | { | |
244 | gtk_list_box_invalidate_sort (sidebar->list); | |
245 | } | |
246 | ||
247 | static void | |
248 | on_child_updated (GtkWidget *widget, | |
249 | GParamSpec *pspec, | |
250 | XAppStackSidebar *sidebar) | |
251 | { | |
252 | GtkWidget *row; | |
253 | ||
254 | row = g_hash_table_lookup (sidebar->rows, widget); | |
255 | update_row (sidebar, widget, row); | |
256 | } | |
257 | ||
258 | static void | |
259 | add_child (GtkWidget *widget, | |
260 | XAppStackSidebar *sidebar) | |
261 | { | |
262 | GtkWidget *item; | |
263 | GtkWidget *label; | |
264 | GtkWidget *icon; | |
265 | GtkWidget *row; | |
266 | ||
267 | /* Check we don't actually already know about this widget */ | |
268 | if (g_hash_table_lookup (sidebar->rows, widget)) | |
269 | { | |
270 | return; | |
271 | } | |
272 | ||
273 | /* Make a pretty item when we add children */ | |
274 | item = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); | |
275 | gtk_widget_set_margin_start (item, 6); | |
276 | gtk_widget_set_margin_end (item, 6); | |
277 | ||
278 | icon = gtk_image_new (); | |
279 | gtk_box_pack_start (GTK_BOX (item), icon, FALSE, FALSE, 0); | |
280 | ||
281 | label = gtk_label_new (""); | |
282 | gtk_box_pack_start (GTK_BOX (item), label, FALSE, FALSE, 0); | |
283 | ||
284 | row = gtk_list_box_row_new (); | |
285 | gtk_container_add (GTK_CONTAINER (row), item); | |
286 | gtk_widget_show_all (item); | |
287 | ||
288 | update_row (sidebar, widget, row); | |
289 | ||
290 | /* Hook up events */ | |
291 | g_signal_connect (widget, "child-notify::title", | |
292 | G_CALLBACK (on_child_updated), sidebar); | |
293 | g_signal_connect (widget, "child-notify::icon-name", | |
294 | G_CALLBACK (on_child_updated), sidebar); | |
295 | g_signal_connect (widget, "child-notify::needs-attention", | |
296 | G_CALLBACK (on_child_updated), sidebar); | |
297 | g_signal_connect (widget, "notify::visible", | |
298 | G_CALLBACK (on_child_updated), sidebar); | |
299 | g_signal_connect (widget, "child-notify::position", | |
300 | G_CALLBACK (on_position_updated), sidebar); | |
301 | ||
302 | g_object_set_data (G_OBJECT (item), "stack-child", widget); | |
303 | g_hash_table_insert (sidebar->rows, widget, row); | |
304 | gtk_container_add (GTK_CONTAINER (sidebar->list), row); | |
305 | } | |
306 | ||
307 | static void | |
308 | remove_child (GtkWidget *widget, | |
309 | XAppStackSidebar *sidebar) | |
310 | { | |
311 | GtkWidget *row; | |
312 | ||
313 | row = g_hash_table_lookup (sidebar->rows, widget); | |
314 | if (!row) | |
315 | { | |
316 | return; | |
317 | } | |
318 | ||
319 | g_signal_handlers_disconnect_by_func (widget, on_child_updated, sidebar); | |
320 | g_signal_handlers_disconnect_by_func (widget, on_position_updated, sidebar); | |
321 | ||
322 | gtk_container_remove (GTK_CONTAINER (sidebar->list), row); | |
323 | g_hash_table_remove (sidebar->rows, widget); | |
324 | } | |
325 | ||
326 | static void | |
327 | populate_sidebar (XAppStackSidebar *sidebar) | |
328 | { | |
329 | GtkWidget *widget; | |
330 | GtkWidget *row; | |
331 | ||
332 | gtk_container_foreach (GTK_CONTAINER (sidebar->stack), (GtkCallback)add_child, sidebar); | |
333 | ||
334 | widget = gtk_stack_get_visible_child (sidebar->stack); | |
335 | if (widget) | |
336 | { | |
337 | row = g_hash_table_lookup (sidebar->rows, widget); | |
338 | gtk_list_box_select_row (sidebar->list, GTK_LIST_BOX_ROW (row)); | |
339 | } | |
340 | } | |
341 | ||
342 | static void | |
343 | clear_sidebar (XAppStackSidebar *sidebar) | |
344 | { | |
345 | gtk_container_foreach (GTK_CONTAINER (sidebar->stack), (GtkCallback)remove_child, sidebar); | |
346 | } | |
347 | ||
348 | static void | |
349 | on_child_changed (GtkWidget *widget, | |
350 | GParamSpec *pspec, | |
351 | XAppStackSidebar *sidebar) | |
352 | { | |
353 | GtkWidget *child; | |
354 | GtkWidget *row; | |
355 | ||
356 | child = gtk_stack_get_visible_child (GTK_STACK (widget)); | |
357 | row = g_hash_table_lookup (sidebar->rows, child); | |
358 | ||
359 | if (row != NULL) | |
360 | { | |
361 | sidebar->in_child_changed = TRUE; | |
362 | gtk_list_box_select_row (sidebar->list, GTK_LIST_BOX_ROW (row)); | |
363 | sidebar->in_child_changed = FALSE; | |
364 | } | |
365 | } | |
366 | ||
367 | static void | |
368 | on_stack_child_added (GtkContainer *container, | |
369 | GtkWidget *widget, | |
370 | XAppStackSidebar *sidebar) | |
371 | { | |
372 | add_child (widget, sidebar); | |
373 | } | |
374 | ||
375 | static void | |
376 | on_stack_child_removed (GtkContainer *container, | |
377 | GtkWidget *widget, | |
378 | XAppStackSidebar *sidebar) | |
379 | { | |
380 | remove_child (widget, sidebar); | |
381 | } | |
382 | ||
383 | static void | |
384 | disconnect_stack_signals (XAppStackSidebar *sidebar) | |
385 | { | |
386 | g_signal_handlers_disconnect_by_func (sidebar->stack, on_stack_child_added, sidebar); | |
387 | g_signal_handlers_disconnect_by_func (sidebar->stack, on_stack_child_removed, sidebar); | |
388 | g_signal_handlers_disconnect_by_func (sidebar->stack, on_child_changed, sidebar); | |
389 | g_signal_handlers_disconnect_by_func (sidebar->stack, disconnect_stack_signals, sidebar); | |
390 | } | |
391 | ||
392 | static void | |
393 | connect_stack_signals (XAppStackSidebar *sidebar) | |
394 | { | |
395 | g_signal_connect_after (sidebar->stack, "add", | |
396 | G_CALLBACK (on_stack_child_added), sidebar); | |
397 | g_signal_connect_after (sidebar->stack, "remove", | |
398 | G_CALLBACK (on_stack_child_removed), sidebar); | |
399 | g_signal_connect (sidebar->stack, "notify::visible-child", | |
400 | G_CALLBACK (on_child_changed), sidebar); | |
401 | g_signal_connect_swapped (sidebar->stack, "destroy", | |
402 | G_CALLBACK (disconnect_stack_signals), sidebar); | |
403 | } | |
404 | ||
405 | static void | |
406 | xapp_stack_sidebar_dispose (GObject *object) | |
407 | { | |
408 | XAppStackSidebar *sidebar = XAPP_STACK_SIDEBAR (object); | |
409 | ||
410 | xapp_stack_sidebar_set_stack (sidebar, NULL); | |
411 | ||
412 | G_OBJECT_CLASS (xapp_stack_sidebar_parent_class)->dispose (object); | |
413 | } | |
414 | ||
415 | static void | |
416 | xapp_stack_sidebar_finalize (GObject *object) | |
417 | { | |
418 | XAppStackSidebar *sidebar = XAPP_STACK_SIDEBAR (object); | |
419 | ||
420 | g_hash_table_destroy (sidebar->rows); | |
421 | ||
422 | G_OBJECT_CLASS (xapp_stack_sidebar_parent_class)->finalize (object); | |
423 | } | |
424 | ||
425 | static void | |
426 | xapp_stack_sidebar_class_init (XAppStackSidebarClass *klass) | |
427 | { | |
428 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
429 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); | |
430 | ||
431 | object_class->dispose = xapp_stack_sidebar_dispose; | |
432 | object_class->finalize = xapp_stack_sidebar_finalize; | |
433 | object_class->set_property = xapp_stack_sidebar_set_property; | |
434 | object_class->get_property = xapp_stack_sidebar_get_property; | |
435 | ||
436 | obj_properties[PROP_STACK] = | |
437 | g_param_spec_object ("stack", | |
438 | "Stack", | |
439 | "Associated stack for this XAppStackSidebar", | |
440 | GTK_TYPE_STACK, | |
441 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); | |
442 | ||
443 | g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties); | |
444 | ||
445 | gtk_widget_class_set_css_name (widget_class, "stacksidebar"); | |
446 | } | |
447 | ||
448 | /** | |
449 | * xapp_stack_sidebar_new: | |
450 | * | |
451 | * Creates a new sidebar. | |
452 | * | |
453 | * Returns: the new #XAppStackSidebar | |
454 | */ | |
455 | ||
456 | XAppStackSidebar * | |
457 | xapp_stack_sidebar_new (void) | |
458 | { | |
459 | return g_object_new (XAPP_TYPE_STACK_SIDEBAR, NULL); | |
460 | } | |
461 | ||
462 | /** | |
463 | * xapp_stack_sidebar_set_stack: | |
464 | * @sidebar: a #XAppStackSidebar | |
465 | * @stack: a #GtkStack | |
466 | * | |
467 | * Set the #GtkStack associated with this #XAppStackSidebar. | |
468 | * | |
469 | * The sidebar widget will automatically update according to the order | |
470 | * (packing) and items within the given #GtkStack. | |
471 | */ | |
472 | ||
473 | void | |
474 | xapp_stack_sidebar_set_stack (XAppStackSidebar *sidebar, | |
475 | GtkStack *stack) | |
476 | { | |
477 | g_return_if_fail (XAPP_IS_STACK_SIDEBAR (sidebar)); | |
478 | g_return_if_fail (GTK_IS_STACK (stack) || stack == NULL); | |
479 | ||
480 | if (sidebar->stack == stack) | |
481 | { | |
482 | return; | |
483 | } | |
484 | ||
485 | if (sidebar->stack) | |
486 | { | |
487 | disconnect_stack_signals (sidebar); | |
488 | clear_sidebar (sidebar); | |
489 | g_clear_object (&sidebar->stack); | |
490 | } | |
491 | ||
492 | if (stack) | |
493 | { | |
494 | sidebar->stack = g_object_ref (stack); | |
495 | populate_sidebar (sidebar); | |
496 | connect_stack_signals (sidebar); | |
497 | } | |
498 | ||
499 | gtk_widget_queue_resize (GTK_WIDGET (sidebar)); | |
500 | ||
501 | g_object_notify (G_OBJECT (sidebar), "stack"); | |
502 | } | |
503 | ||
504 | /** | |
505 | * xapp_stack_sidebar_get_stack: | |
506 | * @sidebar: a #XAppStackSidebar | |
507 | * | |
508 | * Retrieves the stack. | |
509 | * See xapp_stack_sidebar_set_stack(). | |
510 | * | |
511 | * Returns: (nullable) (transfer none): the associated #GtkStack or | |
512 | * %NULL if none has been set explicitly | |
513 | */ | |
514 | ||
515 | GtkStack * | |
516 | xapp_stack_sidebar_get_stack (XAppStackSidebar *sidebar) | |
517 | { | |
518 | g_return_val_if_fail (XAPP_IS_STACK_SIDEBAR (sidebar), NULL); | |
519 | ||
520 | return GTK_STACK (sidebar->stack); | |
521 | } |
0 | #ifndef _XAPP_STACK_SIDEBAR_H_ | |
1 | #define _XAPP_STACK_SIDEBAR_H_ | |
2 | ||
3 | #include <glib-object.h> | |
4 | #include <gtk/gtk.h> | |
5 | ||
6 | G_BEGIN_DECLS | |
7 | ||
8 | #define XAPP_TYPE_STACK_SIDEBAR (xapp_stack_sidebar_get_type ()) | |
9 | ||
10 | G_DECLARE_FINAL_TYPE (XAppStackSidebar, xapp_stack_sidebar, XAPP, STACK_SIDEBAR, GtkBin) | |
11 | ||
12 | XAppStackSidebar *xapp_stack_sidebar_new (void); | |
13 | ||
14 | void xapp_stack_sidebar_set_stack (XAppStackSidebar *sidebar, | |
15 | GtkStack *stack); | |
16 | ||
17 | GtkStack *xapp_stack_sidebar_get_stack (XAppStackSidebar *sidebar); | |
18 | ||
19 | G_END_DECLS | |
20 | ||
21 | #endif /*_XAPP_STACK_SIDEBAR_H_ */ |
0 | 0 | project('xapp', |
1 | 1 | 'c', |
2 | version : '1.2.2' | |
2 | version : '1.4.2' | |
3 | 3 | ) |
4 | 4 | |
5 | 5 | gnome = import('gnome') |
26 | 26 | top_inc = include_directories('.') |
27 | 27 | |
28 | 28 | subdir('libxapp') |
29 | # subdir('po') | |
29 | subdir('po') | |
30 | 30 | subdir('pygobject') |
31 | 31 | subdir('files') |
32 | 32 | subdir('schemas') |
0 | libxapp/xapp-gtk-window.c | |
1 | libxapp/xapp-kbd-layout-controller.c | |
2 | libxapp/xapp-monitor-blanker.c | |
3 | libxapp/xapp-preferences-window.c |
0 | # SOME DESCRIPTIVE TITLE. | |
1 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER | |
2 | # This file is distributed under the same license as the PACKAGE package. | |
3 | # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. | |
4 | # | |
5 | msgid "" | |
6 | msgstr "" | |
7 | "Project-Id-Version: \n" | |
8 | "Report-Msgid-Bugs-To: \n" | |
9 | "POT-Creation-Date: 2018-09-25 13:17-0600\n" | |
10 | "PO-Revision-Date: 2018-09-25 13:42-0600\n" | |
11 | "Language-Team: \n" | |
12 | "MIME-Version: 1.0\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | "X-Generator: Poedit 2.0.6\n" | |
16 | "Last-Translator: \n" | |
17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" | |
18 | "Language: es\n" | |
19 | ||
20 | #: libxapp/xapp-icon-chooser-dialog.c:153 | |
21 | msgid "Browse" | |
22 | msgstr "Ramonear" | |
23 | ||
24 | #: libxapp/xapp-icon-chooser-dialog.c:192 | |
25 | #: libxapp/xapp-icon-chooser-dialog.c:674 | |
26 | msgid "Cancel" | |
27 | msgstr "Cancelar" | |
28 | ||
29 | #: libxapp/xapp-icon-chooser-dialog.c:691 | |
30 | msgid "Image" | |
31 | msgstr "Archivos de imagen" | |
32 | ||
33 | #: libxapp/xapp-icon-chooser-dialog.c:676 | |
34 | msgid "Open" | |
35 | msgstr "Abrir" | |
36 | ||
37 | #: libxapp/xapp-icon-chooser-dialog.c:183 | |
38 | msgid "Select" | |
39 | msgstr "Elegir" | |
40 | ||
41 | #: libxapp/xapp-icon-chooser-dialog.c:671 | |
42 | msgid "Select image file" | |
43 | msgstr "Elegir un archivo de icono" |
0 | # SOME DESCRIPTIVE TITLE. | |
1 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER | |
2 | # This file is distributed under the same license as the PACKAGE package. | |
3 | # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. | |
4 | # | |
5 | msgid "" | |
6 | msgstr "" | |
7 | "Project-Id-Version: \n" | |
8 | "Report-Msgid-Bugs-To: \n" | |
9 | "POT-Creation-Date: 2018-09-25 13:17-0600\n" | |
10 | "PO-Revision-Date: 2018-09-25 13:43-0600\n" | |
11 | "Language-Team: \n" | |
12 | "MIME-Version: 1.0\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | "X-Generator: Poedit 2.0.6\n" | |
16 | "Last-Translator: \n" | |
17 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" | |
18 | "Language: fr\n" | |
19 | ||
20 | #: libxapp/xapp-icon-chooser-dialog.c:153 | |
21 | msgid "Browse" | |
22 | msgstr "" | |
23 | ||
24 | #: libxapp/xapp-icon-chooser-dialog.c:192 | |
25 | #: libxapp/xapp-icon-chooser-dialog.c:674 | |
26 | msgid "Cancel" | |
27 | msgstr "" | |
28 | ||
29 | #: libxapp/xapp-icon-chooser-dialog.c:691 | |
30 | msgid "Image" | |
31 | msgstr "" | |
32 | ||
33 | #: libxapp/xapp-icon-chooser-dialog.c:676 | |
34 | msgid "Open" | |
35 | msgstr "" | |
36 | ||
37 | #: libxapp/xapp-icon-chooser-dialog.c:183 | |
38 | msgid "Select" | |
39 | msgstr "" | |
40 | ||
41 | #: libxapp/xapp-icon-chooser-dialog.c:671 | |
42 | msgid "Select image file" | |
43 | msgstr "" |
20 | 20 | class GtkWindow(XApp.GtkWindow): |
21 | 21 | pass |
22 | 22 | |
23 | class GtkButton(XApp.IconChooserButton): | |
24 | pass | |
25 | ||
26 | class GtkBin(XApp.StackSidebar): | |
27 | pass | |
28 | ||
23 | 29 | GtkWindow = override(GtkWindow) |
30 | GtkButton = override(GtkButton) | |
31 | GtkBin = override(GtkBin) | |
24 | 32 | __all__.append('GtkWindow') |
33 | __all__.append('GtkButton') | |
34 | __all__.append('GtkBin') |
0 | #!/usr/bin/python3 | |
1 | ||
2 | import gi | |
3 | gi.require_version('Gtk', '3.0') | |
4 | gi.require_version('XApp', '1.0') | |
5 | ||
6 | from gi.repository import Gtk, XApp, Gdk | |
7 | ||
8 | import argparse | |
9 | import signal | |
10 | signal.signal(signal.SIGINT, signal.SIG_DFL) | |
11 | ||
12 | if __name__ == '__main__': | |
13 | parser = argparse.ArgumentParser() | |
14 | group = parser.add_mutually_exclusive_group() | |
15 | # if there are no arguments supplied, we should create a new launcher in the default location (usually ~/.local/share/applications/) | |
16 | group.add_argument('-c', '--category', dest='category', metavar='CATEGORY', | |
17 | help='The category to select when the dialog is opened.') | |
18 | group.add_argument('-i', '--icon-string', dest='icon', metavar='ICON_STRING', | |
19 | help='The icon to select when the dialog is opened. This can be an icon name or a path. ' | |
20 | 'If the icon doesn\'t exist, it will not cause an error, but there may not be an icon ' | |
21 | 'selected.') | |
22 | parser.add_argument('-b', '--clipboard', dest='clipboard', action='store_true', | |
23 | help='If this option is supplied, the result will be copied to the clipboard when an icon is ' | |
24 | 'selected and the dialog closed.') | |
25 | parser.add_argument('-p', '--disallow-paths', dest='paths', action='store_false', | |
26 | help='causes the path.') | |
27 | ||
28 | args = parser.parse_args() | |
29 | ||
30 | dialog = XApp.IconChooserDialog(allow_paths=args.paths) | |
31 | dialog.set_skip_taskbar_hint(False) | |
32 | if args.category: | |
33 | response = dialog.run_with_category(args.category) | |
34 | elif args.icon: | |
35 | response = dialog.run_with_icon(args.icon) | |
36 | else: | |
37 | response = dialog.run() | |
38 | ||
39 | if response == Gtk.ResponseType.OK: | |
40 | string = dialog.get_icon_string() | |
41 | print(string) | |
42 | ||
43 | if args.clipboard: | |
44 | clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) | |
45 | clipboard.set_text(string, -1) | |
46 | clipboard.store() | |
47 | else: | |
48 | print('Dialog canceled') |
0 | # SOME DESCRIPTIVE TITLE. | |
1 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER | |
2 | # This file is distributed under the same license as the PACKAGE package. | |
3 | # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. | |
4 | # | |
5 | #, fuzzy | |
6 | msgid "" | |
7 | msgstr "" | |
8 | "Project-Id-Version: PACKAGE VERSION\n" | |
9 | "Report-Msgid-Bugs-To: \n" | |
10 | "POT-Creation-Date: 2018-11-15 10:11+0000\n" | |
11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | |
12 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | |
13 | "Language-Team: LANGUAGE <LL@li.org>\n" | |
14 | "Language: \n" | |
15 | "MIME-Version: 1.0\n" | |
16 | "Content-Type: text/plain; charset=CHARSET\n" | |
17 | "Content-Transfer-Encoding: 8bit\n" | |
18 | ||
19 | #: libxapp/xapp-icon-chooser-button.c:162 | |
20 | #: libxapp/xapp-icon-chooser-dialog.c:601 | |
21 | msgid "Icon size" | |
22 | msgstr "" | |
23 | ||
24 | #: libxapp/xapp-icon-chooser-button.c:163 | |
25 | #: libxapp/xapp-icon-chooser-dialog.c:602 | |
26 | msgid "The preferred icon size." | |
27 | msgstr "" | |
28 | ||
29 | #: libxapp/xapp-icon-chooser-button.c:177 | |
30 | msgid "Icon" | |
31 | msgstr "" | |
32 | ||
33 | #: libxapp/xapp-icon-chooser-button.c:178 | |
34 | msgid "The string representing the icon." | |
35 | msgstr "" | |
36 | ||
37 | #: libxapp/xapp-icon-chooser-dialog.c:93 | |
38 | msgid "Actions" | |
39 | msgstr "" | |
40 | ||
41 | #: libxapp/xapp-icon-chooser-dialog.c:96 | |
42 | msgid "Applications" | |
43 | msgstr "" | |
44 | ||
45 | #: libxapp/xapp-icon-chooser-dialog.c:99 | |
46 | msgid "Categories" | |
47 | msgstr "" | |
48 | ||
49 | #: libxapp/xapp-icon-chooser-dialog.c:102 | |
50 | msgid "Devices" | |
51 | msgstr "" | |
52 | ||
53 | #: libxapp/xapp-icon-chooser-dialog.c:105 | |
54 | msgid "Emblems" | |
55 | msgstr "" | |
56 | ||
57 | #: libxapp/xapp-icon-chooser-dialog.c:108 | |
58 | msgid "Emoji" | |
59 | msgstr "" | |
60 | ||
61 | #: libxapp/xapp-icon-chooser-dialog.c:111 | |
62 | msgid "Mime types" | |
63 | msgstr "" | |
64 | ||
65 | #: libxapp/xapp-icon-chooser-dialog.c:114 | |
66 | msgid "Places" | |
67 | msgstr "" | |
68 | ||
69 | #: libxapp/xapp-icon-chooser-dialog.c:117 | |
70 | msgid "Status" | |
71 | msgstr "" | |
72 | ||
73 | #: libxapp/xapp-icon-chooser-dialog.c:120 | |
74 | msgid "Other" | |
75 | msgstr "" | |
76 | ||
77 | #: libxapp/xapp-icon-chooser-dialog.c:449 | |
78 | msgid "Choose an icon" | |
79 | msgstr "" | |
80 | ||
81 | #: libxapp/xapp-icon-chooser-dialog.c:471 | |
82 | msgid "Search" | |
83 | msgstr "" | |
84 | ||
85 | #: libxapp/xapp-icon-chooser-dialog.c:478 | |
86 | msgid "Browse" | |
87 | msgstr "" | |
88 | ||
89 | #: libxapp/xapp-icon-chooser-dialog.c:534 | |
90 | msgid "Loading..." | |
91 | msgstr "" | |
92 | ||
93 | #: libxapp/xapp-icon-chooser-dialog.c:558 | |
94 | msgid "Select" | |
95 | msgstr "" | |
96 | ||
97 | #: libxapp/xapp-icon-chooser-dialog.c:567 | |
98 | #: libxapp/xapp-icon-chooser-dialog.c:1787 | |
99 | msgid "Cancel" | |
100 | msgstr "" | |
101 | ||
102 | #: libxapp/xapp-icon-chooser-dialog.c:614 | |
103 | msgid "Allow Paths" | |
104 | msgstr "" | |
105 | ||
106 | #: libxapp/xapp-icon-chooser-dialog.c:615 | |
107 | msgid "Whether to allow paths." | |
108 | msgstr "" | |
109 | ||
110 | #: libxapp/xapp-icon-chooser-dialog.c:1784 | |
111 | msgid "Select image file" | |
112 | msgstr "" | |
113 | ||
114 | #: libxapp/xapp-icon-chooser-dialog.c:1789 | |
115 | msgid "Open" | |
116 | msgstr "" | |
117 | ||
118 | #: libxapp/xapp-icon-chooser-dialog.c:1804 | |
119 | msgid "Image" | |
120 | msgstr "" |