Package list xapp / 5e0aa3b
Update upstream source from tag 'upstream/1.4.2' Update to upstream version '1.4.2' with Debian dir b03197424d490d32fb4405c85fddf3546ed7c0cc Norbert Preining 2 years ago
29 changed file(s) with 3580 addition(s) and 54 deletion(s). Raw diff Collapse all Expand all
1515
1616 <chapter>
1717 <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"/>
1821 <xi:include href="xml/xapp-kbd-layout-controller.xml"/>
1922 <xi:include href="xml/xapp-monitor-blanker.xml"/>
20 <xi:include href="xml/xapp-gtk-window.xml"/>
2123 <xi:include href="xml/xapp-preferences-window.xml"/>
24 <xi:include href="xml/xapp-stack-sidebar.xml"/>
2225
2326 </chapter>
2427 <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>
1010
1111 xapp_headers = [
1212 'xapp-gtk-window.h',
13 'xapp-icon-chooser-button.h',
14 'xapp-icon-chooser-dialog.h',
1315 'xapp-kbd-layout-controller.h',
1416 'xapp-monitor-blanker.h',
15 'xapp-preferences-window.h'
17 'xapp-preferences-window.h',
18 'xapp-stack-sidebar.h',
1619 ]
1720
1821 xapp_sources = [
1922 'xapp-glade-catalog.c',
2023 'xapp-gtk-window.c',
24 'xapp-icon-chooser-button.c',
25 'xapp-icon-chooser-dialog.c',
2126 'xapp-kbd-layout-controller.c',
2227 'xapp-monitor-blanker.c',
2328 'xapp-preferences-window.c',
29 'xapp-stack-sidebar.c',
2430 ]
2531
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
2640 libxapp = library('xapp',
27 sources : xapp_headers + xapp_sources,
41 sources : xapp_headers + xapp_sources + xapp_enums,
2842 include_directories: [top_inc],
2943 version: meson.project_version(),
3044 soversion: '1',
3145 dependencies: libdeps,
3246 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'],
3448 install: true
3549 )
3650
7488 sources: gir[0],
7589 metadata_dirs: meson.current_source_dir(),
7690 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 (&gtype_id)) {
33 GType new_type = g_@type@_register_static ("@EnumName@", values);
34 g_once_init_leave (&gtype_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 ***/
11 #include <glib-object.h>
22
33 #include "xapp-gtk-window.h"
4 #include "xapp-icon-chooser-button.h"
5 #include "xapp-stack-sidebar.h"
46
57 void
68 xapp_glade_catalog_init (const gchar *catalog_name);
911 xapp_glade_catalog_init (const gchar *catalog_name)
1012 {
1113 g_type_ensure (XAPP_TYPE_GTK_WINDOW);
14 g_type_ensure (XAPP_TYPE_ICON_CHOOSER_BUTTON);
15 g_type_ensure (XAPP_TYPE_STACK_SIDEBAR);
1216 }
33 <glade-widget-classes>
44 <glade-widget-class name="XAppGtkWindow" generic-name="xappgtkwindow"
55 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" />
610 </glade-widget-classes>
711
812 <glade-widget-group name="xapp-widgets" title="XApp Widgets">
913 <glade-widget-class-ref name="XAppGtkWindow" />
14 <glade-widget-class-ref name="XAppIconChooserButton" />
15 <glade-widget-class-ref name="XAppStackSidebar" />
1016 </glade-widget-group>
1117 </glade-catalog>
5959 guint progress;
6060 gboolean progress_pulse;
6161 } XAppGtkWindowPrivate;
62
63 struct _XAppGtkWindow
64 {
65 GtkWindow parent_object;
66
67 XAppGtkWindowPrivate *priv;
68 };
6962
7063 G_DEFINE_TYPE_WITH_PRIVATE (XAppGtkWindow, xapp_gtk_window, GTK_TYPE_WINDOW)
7164
339332 xapp_gtk_window_realize (GtkWidget *widget)
340333 {
341334 XAppGtkWindow *window = XAPP_GTK_WINDOW (widget);
342 XAppGtkWindowPrivate *priv = window->priv;
335 XAppGtkWindowPrivate *priv = xapp_gtk_window_get_instance_private (window);
343336
344337 GTK_WIDGET_CLASS (xapp_gtk_window_parent_class)->realize (widget);
345338
357350 xapp_gtk_window_finalize (GObject *object)
358351 {
359352 XAppGtkWindow *window = XAPP_GTK_WINDOW (object);
360 XAppGtkWindowPrivate *priv = window->priv;
353 XAppGtkWindowPrivate *priv = xapp_gtk_window_get_instance_private (window);
361354
362355 clear_icon_strings (priv);
363356
368361 xapp_gtk_window_init (XAppGtkWindow *window)
369362 {
370363 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);
375365
376366 priv->icon_name = NULL;
377367 priv->icon_path = NULL;
420410 {
421411 g_return_if_fail (XAPP_IS_GTK_WINDOW (window));
422412
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);
424416 }
425417
426418 /**
442434 {
443435 g_return_if_fail (XAPP_IS_GTK_WINDOW (window));
444436
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);
446440 }
447441
448442 /**
468462 {
469463 g_return_if_fail (XAPP_IS_GTK_WINDOW (window));
470464
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);
472468 }
473469
474470 /**
493489 g_return_if_fail (XAPP_IS_GTK_WINDOW (window));
494490 g_return_if_fail (XAPP_IS_GTK_WINDOW (window));
495491
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);
497495 }
498496
499497
99
1010 #define XAPP_TYPE_GTK_WINDOW (xapp_gtk_window_get_type ())
1111
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 };
1320
1421 /* Class */
1522 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_ */
00 #include <gdk/gdk.h>
11 #include "xapp-preferences-window.h"
2 #include "xapp-stack-sidebar.h"
23
34 /**
45 * SECTION:xapp-preferences-window
1314
1415 typedef struct
1516 {
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;
2020
21 gint num_pages;
21 gint num_pages;
2222 } XAppPreferencesWindowPrivate;
2323
2424 enum
3737 XAppPreferencesWindowPrivate *priv = xapp_preferences_window_get_instance_private (window);
3838 GtkWidget *main_box;
3939 GtkWidget *secondary_box;
40 GtkStyleContext *style_context;
4041
4142 gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
4243 gtk_window_set_skip_taskbar_hint (GTK_WINDOW (window), TRUE);
4344 gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DIALOG);
45 gtk_container_set_border_width (GTK_CONTAINER (window), 5);
4446
4547 main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
48 gtk_container_set_border_width (GTK_CONTAINER (main_box), 5);
4649 gtk_container_add (GTK_CONTAINER (window), main_box);
4750
4851 secondary_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
52 gtk_container_set_border_width (GTK_CONTAINER (secondary_box), 5);
4953 gtk_box_pack_start (GTK_BOX (main_box), secondary_box, TRUE, TRUE, 0);
5054
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);
5562
5663 priv->stack = gtk_stack_new ();
5764 gtk_stack_set_transition_type (GTK_STACK (priv->stack), GTK_STACK_TRANSITION_TYPE_CROSSFADE);
5865 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));
6067
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);
6273 gtk_box_pack_start (GTK_BOX (main_box), priv->button_area, FALSE, FALSE, 0);
6374 gtk_widget_set_no_show_all (priv->button_area, TRUE);
64
65 priv->button_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
6675
6776 /* Keep track of the number of pages so we can hide the stack switcher with < 2 */
6877 priv->num_pages = 0;
134143
135144 if (priv->num_pages > 1)
136145 {
137 gtk_widget_set_no_show_all (priv->side_switcher, FALSE);
146 gtk_widget_set_no_show_all (GTK_WIDGET (priv->side_switcher), FALSE);
138147 }
139148 }
140149
160169 g_return_if_fail (XAPP_IS_PREFERENCES_WINDOW (window));
161170 g_return_if_fail (GTK_IS_WIDGET (button));
162171
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)
164175 {
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);
167177 }
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)
174179 {
175180 return;
176181 }
178183 style_context = gtk_widget_get_style_context (button);
179184 gtk_style_context_add_class (style_context, "text-button");
180185
181 gtk_size_group_add_widget (priv->button_size_group, button);
182186 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