summaryrefslogtreecommitdiff
path: root/indicator-applet
diff options
context:
space:
mode:
authormbkma <[email protected]>2026-01-31 12:11:25 +0100
committermbkma <[email protected]>2026-01-31 12:11:25 +0100
commitbb4fa0ea945f9dd4d0bff0182c381c2f14f78ae3 (patch)
tree523c91356c59557d0bcfa0a878f4e5cadd0c4ab2 /indicator-applet
parentd433bbd9aeb7ae3377e59c70f6608c3e29f9ebbc (diff)
downloadmate-applets-integrate-all-applets.tar.bz2
mate-applets-integrate-all-applets.tar.xz
merge mate-indicator-applet and sensors-applet into mate-appletsintegrate-all-applets
Diffstat (limited to 'indicator-applet')
-rw-r--r--indicator-applet/Makefile.am5
-rw-r--r--indicator-applet/data/Makefile.am78
-rw-r--r--indicator-applet/data/mate-indicator-applet.svg203
-rw-r--r--indicator-applet/data/org.mate.applets.Indicator.mate-panel-applet.desktop.in.in12
-rw-r--r--indicator-applet/data/org.mate.applets.IndicatorAppmenu.mate-panel-applet.desktop.in.in12
-rw-r--r--indicator-applet/data/org.mate.applets.IndicatorComplete.mate-panel-applet.desktop.in.in12
-rw-r--r--indicator-applet/data/org.mate.panel.applet.IndicatorAppletAppmenuFactory.service.in3
-rw-r--r--indicator-applet/data/org.mate.panel.applet.IndicatorAppletCompleteFactory.service.in3
-rw-r--r--indicator-applet/data/org.mate.panel.applet.IndicatorAppletFactory.service.in3
-rw-r--r--indicator-applet/src/Makefile.am89
-rw-r--r--indicator-applet/src/applet-main.c1103
-rw-r--r--indicator-applet/src/eggaccelerators.c527
-rw-r--r--indicator-applet/src/eggaccelerators.h81
-rw-r--r--indicator-applet/src/tomboykeybinder.c341
-rw-r--r--indicator-applet/src/tomboykeybinder.h43
15 files changed, 2515 insertions, 0 deletions
diff --git a/indicator-applet/Makefile.am b/indicator-applet/Makefile.am
new file mode 100644
index 00000000..4fc04575
--- /dev/null
+++ b/indicator-applet/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = \
+ src \
+ data
+
+-include $(top_srcdir)/git.mk
diff --git a/indicator-applet/data/Makefile.am b/indicator-applet/data/Makefile.am
new file mode 100644
index 00000000..04ed1148
--- /dev/null
+++ b/indicator-applet/data/Makefile.am
@@ -0,0 +1,78 @@
+##############################
+# Applets
+##############################
+
+appletdir = $(datadir)/mate-panel/applets
+applet_in_files = \
+ org.mate.applets.Indicator.mate-panel-applet.desktop.in \
+ org.mate.applets.IndicatorComplete.mate-panel-applet.desktop.in \
+ org.mate.applets.IndicatorAppmenu.mate-panel-applet.desktop.in
+applet_DATA = $(applet_in_files:.mate-panel-applet.desktop.in=.mate-panel-applet)
+
+%.mate-panel-applet.desktop.in: %.mate-panel-applet.desktop.in.in Makefile
+ $(AM_V_GEN)sed \
+ -e "s|\@LIBEXECDIR\@|$(libexecdir)|" \
+ $< > $@
+
+%.mate-panel-applet: %.mate-panel-applet.desktop.in Makefile
+ $(AM_V_GEN) $(MSGFMT) --desktop --keyword=Name --keyword=Description --template $< -d $(top_srcdir)/po -o $@
+
+##############################
+# DBus Services
+##############################
+
+servicedir = $(datadir)/dbus-1/services
+service_in_files = \
+ org.mate.panel.applet.IndicatorAppletFactory.service.in \
+ org.mate.panel.applet.IndicatorAppletCompleteFactory.service.in \
+ org.mate.panel.applet.IndicatorAppletAppmenuFactory.service.in
+service_DATA = $(service_in_files:.service.in=.service)
+
+%.service: %.service.in Makefile
+ $(AM_V_GEN)sed \
+ -e "s|\@LIBEXECDIR\@|$(libexecdir)|" \
+ $< > $@
+
+##############################
+# Icons
+##############################
+
+iconsdir = $(datadir)/icons/hicolor/scalable/apps
+
+icons_DATA = mate-indicator-applet.svg
+
+gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor
+
+install-data-hook: update-icon-cache
+uninstall-hook: update-icon-cache
+update-icon-cache:
+ @-if test -z "$(DESTDIR)"; then \
+ echo "Updating Gtk icon cache."; \
+ $(gtk_update_icon_cache); \
+ else \
+ echo "*** Icon cache not updated. After (un)install, run this:"; \
+ echo "*** $(gtk_update_icon_cache)"; \
+ fi
+
+
+##############################
+# Autojunk
+##############################
+
+CLEANFILES = \
+ $(applet_in_files) \
+ $(applet_DATA) \
+ $(service_DATA) \
+ *.bak
+
+EXTRA_DIST = \
+ $(icons_DATA) \
+ org.mate.applets.Indicator.mate-panel-applet.desktop.in.in \
+ org.mate.applets.IndicatorComplete.mate-panel-applet.desktop.in.in \
+ org.mate.applets.IndicatorAppmenu.mate-panel-applet.desktop.in.in \
+ $(service_in_files)
+
+MAINTAINERCLEANFILES = \
+ Makefile.in
+
+-include $(top_srcdir)/git.mk
diff --git a/indicator-applet/data/mate-indicator-applet.svg b/indicator-applet/data/mate-indicator-applet.svg
new file mode 100644
index 00000000..5e9beeda
--- /dev/null
+++ b/indicator-applet/data/mate-indicator-applet.svg
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32px"
+ height="32px"
+ id="svg3499"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="add-notification-applet.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs3501">
+ <linearGradient
+ id="linearGradient3547">
+ <stop
+ style="stop-color:#cfcfcf;stop-opacity:1;"
+ offset="0"
+ id="stop3549" />
+ <stop
+ id="stop3553"
+ offset="0.46295282"
+ style="stop-color:#8e8e92;stop-opacity:1;" />
+ <stop
+ style="stop-color:#f5f5f5;stop-opacity:1;"
+ offset="1"
+ id="stop3557" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3245">
+ <stop
+ style="stop-color:#414141;stop-opacity:1;"
+ offset="0"
+ id="stop3247" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="1"
+ id="stop3249" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter3374"
+ x="-0.21687568"
+ width="1.4337514"
+ y="-0.10843168"
+ height="1.2168634">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.99401352"
+ id="feGaussianBlur3376" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3547"
+ id="linearGradient3565"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(0,39)"
+ x1="-230.10732"
+ y1="54"
+ x2="-229.75377"
+ y2="-2" />
+ <filter
+ inkscape:collect="always"
+ id="filter4144">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.7"
+ id="feGaussianBlur4146" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3245"
+ id="linearGradient4153"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.7000187,0,0,0.7000187,-65.177182,5.3745318)"
+ x1="119.7433"
+ y1="17.315649"
+ x2="111.59486"
+ y2="12.926581" />
+ <filter
+ inkscape:collect="always"
+ id="filter4158"
+ x="-0.24"
+ width="1.48"
+ y="-0.24"
+ height="1.48">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.66262488"
+ id="feGaussianBlur4160" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="15.532905"
+ inkscape:cy="14.679277"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ showborder="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid3605"
+ visible="true"
+ enabled="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata3504">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <path
+ style="opacity:0.4;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;filter:url(#filter4144);enable-background:accumulate"
+ d="M 16.000005,2 C 8.2720052,2 2.0000052,8.271999 2.0000052,16 C 2.0000052,23.728 8.2720062,30 16.000005,30 C 23.728005,30 30.000006,23.727999 30.000005,16 C 30.000005,8.272 23.728006,2.000001 16.000005,2 z M 16.000005,3 C 23.176003,3.000002 29.000005,8.824 29.000005,16 C 29.000005,23.175999 23.176003,29 16.000005,29 C 8.8240062,29 3.0000052,23.176 3.0000052,16 C 3.0000052,8.824 8.8240072,3 16.000005,3 z"
+ id="path4138" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;fill:url(#linearGradient3565);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path3539"
+ sodipodi:cx="-227"
+ sodipodi:cy="65"
+ sodipodi:rx="28"
+ sodipodi:ry="28"
+ d="M -199,65 A 28,28 0 1 1 -255,65 A 28,28 0 1 1 -199,65 z"
+ transform="matrix(0.5,0,0,0.5,129.5,-16.499999)" />
+ <path
+ style="opacity:0.22999998;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 16.000005,2 C 8.2720052,2 2.0000052,8.271999 2.0000052,16 C 2.0000052,23.728 8.2720062,30 16.000005,30 C 23.728005,30 30.000006,23.727999 30.000005,16 C 30.000005,8.272 23.728006,2.000001 16.000005,2 z M 16.000005,3 C 23.176003,3.000002 29.000005,8.824 29.000005,16 C 29.000005,23.175999 23.176003,29 16.000005,29 C 8.8240062,29 3.0000052,23.176 3.0000052,16 C 3.0000052,8.824 8.8240072,3 16.000005,3 z"
+ id="path3541" />
+ <path
+ style="opacity:1;fill:#ffffff;fill-opacity:0.53731346;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 16.000005,3 C 8.8240072,3 3.0000052,8.824 3.0000052,16 C 3.0000052,16.167008 3.0250052,16.334513 3.0312552,16.5 C 3.2936652,9.554839 8.9910152,4 16.000005,4 C 23.008995,4.000002 28.706345,9.554839 28.968755,16.5 C 28.975008,16.334513 29.000005,16.167008 29.000005,16 C 29.000005,8.824 23.176003,3.000002 16.000005,3 z"
+ id="path3543" />
+ <path
+ style="opacity:1;fill:#ffffff;fill-opacity:0.34199135;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 16.000005,29 C 8.8240072,29 3.0000052,23.176 3.0000052,16 C 3.0000052,15.832992 3.0250052,15.665487 3.0312552,15.5 C 3.2936652,22.445161 8.9910152,28 16.000005,28 C 23.008995,27.999998 28.706345,22.445161 28.968755,15.5 C 28.975008,15.665487 29.000005,15.832992 29.000005,16 C 29.000005,23.176 23.176003,28.999998 16.000005,29 z"
+ id="path3545" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;filter:url(#filter4158);enable-background:accumulate"
+ d="M -109,4 C -110.656,4 -112,5.344 -112,7 C -112,7.0538767 -112.00281,7.1030693 -112,7.15625 C -112.00255,7.2095519 -112,7.2585695 -112,7.3125 C -112,9.1410001 -110.516,10.625 -108.6875,10.625 C -106.859,10.625 -105.375,9.1410003 -105.375,7.3125 C -105.375,5.4839999 -106.859,4.0000001 -108.6875,4 C -108.74143,4 -108.79045,3.9974473 -108.84375,4 C -108.89693,3.9971904 -108.94612,4 -109,4 z"
+ id="path3549"
+ transform="matrix(0.7000187,0,0,0.7000187,92.327025,5.3745318)" />
+ <path
+ style="opacity:0.67346939;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;filter:url(#filter3374);enable-background:accumulate"
+ d="M -114,12 L -114,14 L -114,14.5 C -113.99997,14.776131 -113.77613,14.999972 -113.5,15 L -112,15 L -112,23 L -114,23 L -114,25 L -114,25.5 C -113.99997,25.776131 -113.77613,25.999972 -113.5,26 L -103.5,26 C -103.22387,25.999972 -103.00003,25.776131 -103,25.5 L -103,23.5 C -103.00003,23.223869 -103.22387,23.000028 -103.5,23 L -104,23 L -105,23 L -105,14.5 L -105,12.5 C -105.00003,12.223869 -105.22387,12.000028 -105.5,12 L -106,12 L -114,12 z"
+ id="path3551"
+ transform="matrix(0.7000187,0,0,0.7000187,92.327025,5.3745318)" />
+ <path
+ style="opacity:0.15;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 12.809276,13.774756 C 12.641943,13.806685 12.52189,13.95444 12.524893,14.124766 L 12.524893,15.524803 C 12.524914,15.7181 12.681606,15.874793 12.874903,15.874812 L 13.924931,15.874812 L 13.924931,21.474962 L 12.874903,21.474962 C 12.681606,21.474982 12.524914,21.631674 12.524893,21.824971 L 12.524893,23.225009 C 12.524914,23.418306 12.681606,23.574998 12.874903,23.575018 L 19.87509,23.575018 C 20.068386,23.574998 20.225078,23.418306 20.225099,23.225009 L 20.225099,21.824971 C 20.225078,21.631674 20.068386,21.474982 19.87509,21.474962 L 18.825062,21.474962 L 18.825062,15.524803 L 18.825062,14.124766 C 18.82504,13.931469 18.668348,13.774776 18.475052,13.774756 L 12.874903,13.774756 C 12.853076,13.772701 12.831102,13.772701 12.809276,13.774756 z"
+ id="path3553" />
+ <path
+ style="fill:url(#linearGradient4153);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.33333337;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 12.524893,13.774756 L 12.524893,15.174794 L 13.924931,15.174794 L 13.924931,21.474962 L 12.524893,21.474962 L 12.524893,22.874999 L 19.52508,22.874999 L 19.52508,21.474962 L 18.125043,21.474962 L 18.125043,15.174794 L 18.125043,13.774756 L 12.524893,13.774756 z"
+ id="path3555" />
+ <path
+ style="opacity:0.56734691;fill:#606060;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 13.224912,14.49665 L 18.125043,14.49665 L 18.125043,15.546679 L 18.125043,21.846847 C 18.125064,22.040144 18.281756,22.196837 18.475052,22.196856 L 19.52508,22.196856 L 19.52508,22.896875 L 13.224912,22.896875 L 13.224912,22.196856 L 14.27494,22.196856 C 14.468236,22.196837 14.624928,22.040144 14.624949,21.846847 L 14.624949,15.546679 C 14.624928,15.353382 14.468236,15.196689 14.27494,15.196669 L 13.224912,15.196669 L 13.224912,14.49665 z"
+ id="path3557" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:0.43000004;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path3559"
+ sodipodi:cx="116.5"
+ sodipodi:cy="7.5"
+ sodipodi:rx="3.5"
+ sodipodi:ry="3.5"
+ d="M 120,7.5 A 3.5,3.5 0 1 1 113,7.5 A 3.5,3.5 0 1 1 120,7.5 z"
+ transform="matrix(0.6625177,0,0,0.6625177,-60.93957,5.5245358)" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.33333337;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 16.024987,8.1746066 C 14.865756,8.1746066 13.924931,9.1154317 13.924931,10.274663 C 13.924931,11.433894 14.865756,12.374719 16.024987,12.374719 C 17.184218,12.374719 18.125043,11.433894 18.125043,10.274663 C 18.125043,9.1154317 17.184218,8.1746066 16.024987,8.1746066 z"
+ id="path3561" />
+ <path
+ style="opacity:0.56734691;fill:#606060;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 16.19353,8.6818428 C 15.19564,8.6818428 14.38576,9.4917224 14.38576,10.48961 C 14.38576,11.487497 15.19564,12.297377 16.19353,12.297377 C 17.191414,12.297377 18.001293,11.487497 18.001293,10.48961 C 18.001293,9.4917224 17.191414,8.6818428 16.19353,8.6818428 z"
+ id="path3563" />
+ </g>
+</svg>
diff --git a/indicator-applet/data/org.mate.applets.Indicator.mate-panel-applet.desktop.in.in b/indicator-applet/data/org.mate.applets.Indicator.mate-panel-applet.desktop.in.in
new file mode 100644
index 00000000..e6734a72
--- /dev/null
+++ b/indicator-applet/data/org.mate.applets.Indicator.mate-panel-applet.desktop.in.in
@@ -0,0 +1,12 @@
+[Applet Factory]
+Id=IndicatorAppletFactory
+Location=@LIBEXECDIR@/mate-indicator-applet
+Name=Indicator Applet Factory
+Description=Indicator Applet Factory
+
+[IndicatorApplet]
+Name=Indicator Applet
+Description=An indicator of something that needs your attention on the desktop
+# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+Icon=mate-indicator-applet
+MateComponentId=OAFIID:MATE_IndicatorApplet
diff --git a/indicator-applet/data/org.mate.applets.IndicatorAppmenu.mate-panel-applet.desktop.in.in b/indicator-applet/data/org.mate.applets.IndicatorAppmenu.mate-panel-applet.desktop.in.in
new file mode 100644
index 00000000..404b601d
--- /dev/null
+++ b/indicator-applet/data/org.mate.applets.IndicatorAppmenu.mate-panel-applet.desktop.in.in
@@ -0,0 +1,12 @@
+[Applet Factory]
+Id=IndicatorAppletAppmenuFactory
+Location=@LIBEXECDIR@/mate-indicator-applet-appmenu
+Name=Indicator Applet Appmenu Factory
+Description=Indicator Applet Appmenu Factory
+
+[IndicatorAppletAppmenu]
+Name=Indicator Applet Appmenu
+Description=A applet containing the application menus.
+# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+Icon=mate-indicator-applet
+MateComponentId=OAFIID:MATE_IndicatorAppletAppmenu
diff --git a/indicator-applet/data/org.mate.applets.IndicatorComplete.mate-panel-applet.desktop.in.in b/indicator-applet/data/org.mate.applets.IndicatorComplete.mate-panel-applet.desktop.in.in
new file mode 100644
index 00000000..2762db48
--- /dev/null
+++ b/indicator-applet/data/org.mate.applets.IndicatorComplete.mate-panel-applet.desktop.in.in
@@ -0,0 +1,12 @@
+[Applet Factory]
+Id=IndicatorAppletCompleteFactory
+Location=@LIBEXECDIR@/mate-indicator-applet-complete
+Name=Indicator Applet Complete Factory
+Description=Indicator Applet Complete Factory
+
+[IndicatorAppletComplete]
+Name=Indicator Applet Complete
+Description=A unified applet containing all of the indicators.
+# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+Icon=mate-indicator-applet
+MateComponentId=OAFIID:MATE_IndicatorAppletComplete
diff --git a/indicator-applet/data/org.mate.panel.applet.IndicatorAppletAppmenuFactory.service.in b/indicator-applet/data/org.mate.panel.applet.IndicatorAppletAppmenuFactory.service.in
new file mode 100644
index 00000000..69853543
--- /dev/null
+++ b/indicator-applet/data/org.mate.panel.applet.IndicatorAppletAppmenuFactory.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.mate.panel.applet.IndicatorAppletAppmenuFactory
+Exec=@LIBEXECDIR@/mate-indicator-applet-appmenu
diff --git a/indicator-applet/data/org.mate.panel.applet.IndicatorAppletCompleteFactory.service.in b/indicator-applet/data/org.mate.panel.applet.IndicatorAppletCompleteFactory.service.in
new file mode 100644
index 00000000..24f1f834
--- /dev/null
+++ b/indicator-applet/data/org.mate.panel.applet.IndicatorAppletCompleteFactory.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.mate.panel.applet.IndicatorAppletCompleteFactory
+Exec=@LIBEXECDIR@/mate-indicator-applet-complete
diff --git a/indicator-applet/data/org.mate.panel.applet.IndicatorAppletFactory.service.in b/indicator-applet/data/org.mate.panel.applet.IndicatorAppletFactory.service.in
new file mode 100644
index 00000000..0339d51a
--- /dev/null
+++ b/indicator-applet/data/org.mate.panel.applet.IndicatorAppletFactory.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.mate.panel.applet.IndicatorAppletFactory
+Exec=@LIBEXECDIR@/mate-indicator-applet
diff --git a/indicator-applet/src/Makefile.am b/indicator-applet/src/Makefile.am
new file mode 100644
index 00000000..d11977ea
--- /dev/null
+++ b/indicator-applet/src/Makefile.am
@@ -0,0 +1,89 @@
+if WITH_AYATANA_INDICATOR
+INDICATOR_CFLAGS = $(AYATANA_INDICATOR_CFLAGS) \
+ $(AYATANA_INDICATOR_NG_CFLAGS)
+INDICATOR_LIBS = $(AYATANA_INDICATOR_LIBS) \
+ $(AYATANA_INDICATOR_NG_LIBS)
+endif
+
+if WITH_UBUNTU_INDICATOR
+INDICATOR_CFLAGS = $(UBUNTU_INDICATOR_CFLAGS) \
+ $(UBUNTU_INDICATOR_NG_CFLAGS)
+INDICATOR_LIBS = $(UBUNTU_INDICATOR_LIBS) \
+ $(UBUNTU_INDICATOR_NG_LIBS)
+endif
+
+libexec_PROGRAMS = \
+ mate-indicator-applet \
+ mate-indicator-applet-appmenu \
+ mate-indicator-applet-complete
+
+mate_indicator_applet_CFLAGS = \
+ -DG_LOG_DOMAIN=\""Indicator-Applet"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -DINDICATOR_DIR=\""$(INDICATORDIR)"\" \
+ -DINDICATOR_ICONS_DIR=\""$(INDICATORICONSDIR)"\" \
+ -DINDICATOR_APPLET \
+ -I$(srcdir)/.. \
+ $(INDICATOR_APPLET_CFLAGS) \
+ $(INDICATOR_CFLAGS) \
+ $(WARN_CFLAGS)
+
+mate_indicator_applet_SOURCES = \
+ applet-main.c \
+ eggaccelerators.c \
+ eggaccelerators.h \
+ tomboykeybinder.c \
+ tomboykeybinder.h
+
+mate_indicator_applet_LDADD = \
+ $(INDICATOR_APPLET_LIBS) \
+ $(INDICATOR_LIBS) \
+ -lX11
+
+mate_indicator_applet_appmenu_CFLAGS = \
+ -DG_LOG_DOMAIN=\""Indicator-Applet-Appmenu"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -DINDICATOR_DIR=\""$(INDICATORDIR)"\" \
+ -DINDICATOR_ICONS_DIR=\""$(INDICATORICONSDIR)"\" \
+ -DINDICATOR_APPLET_APPMENU \
+ -I$(srcdir)/.. \
+ $(INDICATOR_APPLET_CFLAGS) \
+ $(INDICATOR_CFLAGS) \
+ $(WARN_CFLAGS)
+
+mate_indicator_applet_appmenu_SOURCES = \
+ applet-main.c \
+ eggaccelerators.c \
+ eggaccelerators.h \
+ tomboykeybinder.c \
+ tomboykeybinder.h
+
+mate_indicator_applet_appmenu_LDADD = \
+ $(INDICATOR_APPLET_LIBS) \
+ $(INDICATOR_LIBS) \
+ -lX11
+
+mate_indicator_applet_complete_CFLAGS = \
+ -DG_LOG_DOMAIN=\""Indicator-Applet-Complete"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -DINDICATOR_DIR=\""$(INDICATORDIR)"\" \
+ -DINDICATOR_ICONS_DIR=\""$(INDICATORICONSDIR)"\" \
+ -DINDICATOR_APPLET_COMPLETE \
+ -I$(srcdir)/.. \
+ $(INDICATOR_APPLET_CFLAGS) \
+ $(INDICATOR_CFLAGS) \
+ $(WARN_CFLAGS)
+
+mate_indicator_applet_complete_SOURCES = \
+ applet-main.c \
+ eggaccelerators.c \
+ eggaccelerators.h \
+ tomboykeybinder.c \
+ tomboykeybinder.h
+
+mate_indicator_applet_complete_LDADD = \
+ $(INDICATOR_APPLET_LIBS) \
+ $(INDICATOR_LIBS) \
+ -lX11
+
+-include $(top_srcdir)/git.mk
diff --git a/indicator-applet/src/applet-main.c b/indicator-applet/src/applet-main.c
new file mode 100644
index 00000000..41c0c66f
--- /dev/null
+++ b/indicator-applet/src/applet-main.c
@@ -0,0 +1,1103 @@
+/*
+A small wrapper utility to load indicators and put them as menu items
+into the mate-panel using it's applet interface.
+
+Copyright 2009-2010 Canonical Ltd.
+
+Authors:
+ Ted Gould <[email protected]>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 3, as published
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <config.h>
+#include <glib/gi18n.h>
+#include <mate-panel-applet.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#if HAVE_UBUNTU_INDICATOR
+
+#define INDICATOR_SERVICE_APPMENU "libappmenu.so"
+#define INDICATOR_SERVICE_ME "libme.so"
+#define INDICATOR_SERVICE_DATETIME "libdatetime.so"
+
+#define INDICATOR_SERVICE_APPMENU_NG "com.canonical.indicator.appmenu"
+#define INDICATOR_SERVICE_ME_NG "com.canonical.indicator.me"
+#define INDICATOR_SERVICE_DATETIME_NG "com.canonical.indicator.datetime"
+
+#include <libindicator/indicator-object.h>
+#endif
+
+#if HAVE_AYATANA_INDICATOR
+
+#define INDICATOR_SERVICE_APPMENU "libayatana-appmenu.so"
+#define INDICATOR_SERVICE_ME "libayatana-me.so"
+#define INDICATOR_SERVICE_DATETIME "libayatana-datetime.so"
+
+#define INDICATOR_SERVICE_APPMENU_NG "org.ayatana.indicator.appmenu"
+#define INDICATOR_SERVICE_ME_NG "org.ayatana.indicator.me"
+#define INDICATOR_SERVICE_DATETIME_NG "org.ayatana.indicator.datetime"
+
+#include <libayatana-indicator/indicator-object.h>
+#endif
+
+/* For new style indicators */
+
+#if HAVE_UBUNTU_INDICATOR && HAVE_UBUNTU_INDICATOR_NG
+
+#include <libido/libido.h>
+#include <libindicator/indicator-ng.h>
+
+#define INDICATOR_SERVICE_DIR "/usr/share/unity/indicators"
+
+#endif
+
+#if HAVE_AYATANA_INDICATOR && HAVE_AYATANA_INDICATOR_NG
+
+#include <libayatana-ido/libayatana-ido.h>
+#include <libayatana-indicator/indicator-ng.h>
+
+#define INDICATOR_SERVICE_DIR "/usr/share/ayatana/indicators"
+
+#endif
+
+#include "tomboykeybinder.h"
+
+static gchar * indicator_order[] = {
+#if HAVE_UBUNTU_INDICATOR
+ "libapplication.so",
+ "libmessaging.so",
+ "libsoundmenu.so",
+ "libdatetime.so",
+ "libsession.so",
+#endif
+#if HAVE_AYATANA_INDICATOR
+ "libayatana-application.so",
+ "libayatana-messaging.so",
+ "libayatana-soundmenu.so",
+ "libayatana-datetime.so",
+ "libayatana-session.so",
+#endif
+ NULL
+};
+
+static GtkPackDirection packdirection;
+static MatePanelAppletOrient orient;
+static guint size;
+
+#define MENU_DATA_INDICATOR_OBJECT "indicator-object"
+#define MENU_DATA_INDICATOR_ENTRY "indicator-entry"
+
+#define IO_DATA_ORDER_NUMBER "indicator-order-number"
+
+static gboolean applet_fill_cb (MatePanelApplet * applet, const gchar * iid, gpointer data);
+
+static void update_accessible_desc (IndicatorObjectEntry * entry, GtkWidget * menuitem);
+
+/*************
+ * main
+ * ***********/
+
+#ifdef INDICATOR_APPLET
+MATE_PANEL_APPLET_OUT_PROCESS_FACTORY ("IndicatorAppletFactory",
+ PANEL_TYPE_APPLET,
+ "indicator-applet",
+ applet_fill_cb, NULL);
+#endif
+#ifdef INDICATOR_APPLET_COMPLETE
+MATE_PANEL_APPLET_OUT_PROCESS_FACTORY ("IndicatorAppletCompleteFactory",
+ PANEL_TYPE_APPLET,
+ "indicator-applet-complete",
+ applet_fill_cb, NULL);
+#endif
+#ifdef INDICATOR_APPLET_APPMENU
+MATE_PANEL_APPLET_OUT_PROCESS_FACTORY ("IndicatorAppletAppmenuFactory",
+ PANEL_TYPE_APPLET,
+ "indicator-applet-appmenu",
+ applet_fill_cb, NULL);
+#endif
+
+/*************
+ * log files
+ * ***********/
+#ifdef INDICATOR_APPLET
+#define LOG_FILE_NAME "indicator-applet.log"
+#endif
+#ifdef INDICATOR_APPLET_COMPLETE
+#define LOG_FILE_NAME "indicator-applet-complete.log"
+#endif
+#ifdef INDICATOR_APPLET_APPMENU
+#define LOG_FILE_NAME "indicator-applet-appmenu.log"
+#endif
+GOutputStream * log_file = NULL;
+
+/*****************
+ * Hotkey support
+ * **************/
+#ifdef INDICATOR_APPLET
+gchar * hotkey_keycode = "<Super>M";
+#endif
+#ifdef INDICATOR_APPLET_SESSION
+gchar * hotkey_keycode = "<Super>S";
+#endif
+#ifdef INDICATOR_APPLET_COMPLETE
+gchar * hotkey_keycode = "<Super>S";
+#endif
+#ifdef INDICATOR_APPLET_APPMENU
+gchar * hotkey_keycode = "<Super>F1";
+#endif
+
+/********************
+ * Environment Names
+ * *******************/
+#ifdef INDICATOR_APPLET
+#define INDICATOR_SPECIFIC_ENV "indicator-applet-original"
+#endif
+#ifdef INDICATOR_APPLET_COMPLETE
+#define INDICATOR_SPECIFIC_ENV "indicator-applet-complete"
+#endif
+#ifdef INDICATOR_APPLET_APPMENU
+#define INDICATOR_SPECIFIC_ENV "indicator-applet-appmenu"
+#endif
+
+static const gchar * indicator_env[] = {
+ "indicator-applet",
+ INDICATOR_SPECIFIC_ENV,
+ NULL
+};
+
+/*************
+ * init function
+ * ***********/
+
+static gint
+name2order (const gchar * name) {
+ int i;
+
+ for (i = 0; indicator_order[i] != NULL; i++) {
+ if (g_strcmp0(name, indicator_order[i]) == 0) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+typedef struct _incoming_position_t incoming_position_t;
+struct _incoming_position_t {
+ gint objposition;
+ gint entryposition;
+ gint menupos;
+ gboolean found;
+};
+
+/* This function helps by determining where in the menu list
+ this new entry should be placed. It compares the objects
+ that they're on, and then the individual entries. Each
+ is progressively more expensive. */
+static void
+place_in_menu (GtkWidget * widget, gpointer user_data)
+{
+ incoming_position_t * position = (incoming_position_t *)user_data;
+ if (position->found) {
+ /* We've already been placed, just finish the foreach */
+ return;
+ }
+
+ IndicatorObject * io = INDICATOR_OBJECT(g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_OBJECT));
+ g_assert(io != NULL);
+
+ gint objposition = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER));
+ /* We've already passed it, well, then this is where
+ we should be be. Stop! */
+ if (objposition > position->objposition) {
+ position->found = TRUE;
+ return;
+ }
+
+ /* The objects don't match yet, keep looking */
+ if (objposition < position->objposition) {
+ position->menupos++;
+ return;
+ }
+
+ /* The objects are the same, let's start looking at entries. */
+ IndicatorObjectEntry * entry = (IndicatorObjectEntry *)g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
+ gint entryposition = indicator_object_get_location(io, entry);
+
+ if (entryposition > position->entryposition) {
+ position->found = TRUE;
+ return;
+ }
+
+ if (entryposition < position->entryposition) {
+ position->menupos++;
+ return;
+ }
+
+ /* We've got the same object and the same entry. Well,
+ let's just put it right here then. */
+ position->found = TRUE;
+ return;
+}
+
+static void
+something_shown (GtkWidget * widget, gpointer user_data)
+{
+ GtkWidget * menuitem = GTK_WIDGET(user_data);
+ gtk_widget_show(menuitem);
+}
+
+static void
+something_hidden (GtkWidget * widget, gpointer user_data)
+{
+ GtkWidget * menuitem = GTK_WIDGET(user_data);
+ gtk_widget_hide(menuitem);
+}
+
+static void
+sensitive_cb (GObject * obj, GParamSpec * pspec, gpointer user_data)
+{
+ g_return_if_fail(GTK_IS_WIDGET(obj));
+ g_return_if_fail(GTK_IS_WIDGET(user_data));
+
+ gtk_widget_set_sensitive(GTK_WIDGET(user_data), gtk_widget_get_sensitive(GTK_WIDGET(obj)));
+ return;
+}
+
+static void
+entry_activated (GtkWidget * widget, gpointer user_data)
+{
+ g_return_if_fail(GTK_IS_WIDGET(widget));
+ gpointer pio = g_object_get_data(G_OBJECT(widget), "indicator");
+ g_return_if_fail(INDICATOR_IS_OBJECT(pio));
+ IndicatorObject * io = INDICATOR_OBJECT(pio);
+
+ return indicator_object_entry_activate(io, (IndicatorObjectEntry *)user_data, gtk_get_current_event_time());
+}
+
+static gboolean
+entry_scrolled (GtkWidget *menuitem, GdkEventScroll *event, gpointer data)
+{
+ IndicatorObject *io = g_object_get_data (G_OBJECT (menuitem), MENU_DATA_INDICATOR_OBJECT);
+ IndicatorObjectEntry *entry = g_object_get_data (G_OBJECT (menuitem), MENU_DATA_INDICATOR_ENTRY);
+
+ g_return_val_if_fail(INDICATOR_IS_OBJECT(io), FALSE);
+
+ g_signal_emit_by_name (io, "scroll", 1, event->direction);
+ g_signal_emit_by_name (io, "scroll-entry", entry, 1, event->direction);
+ g_signal_emit_by_name (io, INDICATOR_OBJECT_SIGNAL_ENTRY_SCROLLED, entry, 1, event->direction);
+
+ return FALSE;
+}
+
+static gboolean
+entry_pressed (GtkWidget *menuitem, GdkEvent *event, gpointer data)
+{
+ g_return_val_if_fail(GTK_IS_MENU_ITEM(menuitem), FALSE);
+
+ if (((GdkEventButton*)event)->button == 2) /* middle button */
+ {
+ gtk_widget_grab_focus(menuitem);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+entry_released (GtkWidget *menuitem, GdkEvent *event, gpointer data)
+{
+ g_return_val_if_fail(GTK_IS_MENU_ITEM(menuitem), FALSE);
+
+ if (((GdkEventButton*)event)->button == 2) /* middle button */
+ {
+ IndicatorObject *io = g_object_get_data (G_OBJECT (menuitem), MENU_DATA_INDICATOR_OBJECT);
+ IndicatorObjectEntry *entry = g_object_get_data (G_OBJECT (menuitem), MENU_DATA_INDICATOR_ENTRY);
+
+ g_return_val_if_fail(INDICATOR_IS_OBJECT(io), FALSE);
+
+ g_signal_emit_by_name (io, INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE, entry,
+ ((GdkEventButton*)event)->time);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+accessible_desc_update_cb (GtkWidget * widget, gpointer userdata)
+{
+ gpointer data = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
+
+ if (data != userdata) {
+ return;
+ }
+
+ IndicatorObjectEntry * entry = (IndicatorObjectEntry *)data;
+ update_accessible_desc(entry, widget);
+}
+
+static void
+accessible_desc_update (IndicatorObject * io, IndicatorObjectEntry * entry, GtkWidget * menubar)
+{
+ gtk_container_foreach(GTK_CONTAINER(menubar), accessible_desc_update_cb, entry);
+ return;
+}
+
+#define PANEL_PADDING 8
+static gboolean
+entry_resized (GtkWidget *applet, guint newsize, gpointer data)
+{
+ IndicatorObject *io = (IndicatorObject *)data;
+
+ size = newsize;
+
+ /* Work on the entries */
+ GList * entries = indicator_object_get_entries(io);
+ GList * entry = NULL;
+
+ for (entry = entries; entry != NULL; entry = g_list_next(entry)) {
+ IndicatorObjectEntry * entrydata = (IndicatorObjectEntry *)entry->data;
+ if (entrydata->image != NULL) {
+ /* Resize to fit panel */
+ gtk_image_set_pixel_size (entrydata->image, size - PANEL_PADDING);
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+entry_added (IndicatorObject * io, IndicatorObjectEntry * entry, GtkWidget * menubar)
+{
+ g_debug("Signal: Entry Added");
+ gboolean something_visible = FALSE;
+ gboolean something_sensitive = FALSE;
+
+ GtkWidget * menuitem = gtk_menu_item_new();
+ GtkWidget * box = (packdirection == GTK_PACK_DIRECTION_LTR) ?
+ gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3) : gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
+
+ /* Allows indicators to receive mouse scroll event */
+ gtk_widget_add_events(GTK_WIDGET(menuitem), GDK_SCROLL_MASK);
+ gtk_widget_add_events(GTK_WIDGET(menuitem), GDK_BUTTON_PRESS_MASK);
+ gtk_widget_add_events(GTK_WIDGET(menuitem), GDK_BUTTON_RELEASE_MASK);
+
+ g_object_set_data (G_OBJECT (menuitem), "indicator", io);
+ g_object_set_data (G_OBJECT (menuitem), "box", box);
+
+ g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(entry_activated), entry);
+ g_signal_connect(G_OBJECT(menuitem), "scroll-event", G_CALLBACK(entry_scrolled), entry);
+ g_signal_connect(G_OBJECT(menuitem), "button-press-event", G_CALLBACK(entry_pressed), entry);
+ g_signal_connect(G_OBJECT(menuitem), "button-release-event", G_CALLBACK(entry_released), entry);
+
+ if (entry->image != NULL) {
+ /* Resize to fit panel */
+ gtk_image_set_pixel_size (entry->image, size - PANEL_PADDING);
+ gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(entry->image), FALSE, FALSE, 1);
+ if (gtk_widget_get_visible(GTK_WIDGET(entry->image))) {
+ something_visible = TRUE;
+ }
+
+ if (gtk_widget_get_sensitive(GTK_WIDGET(entry->image))) {
+ something_sensitive = TRUE;
+ }
+
+ g_signal_connect(G_OBJECT(entry->image), "show", G_CALLBACK(something_shown), menuitem);
+ g_signal_connect(G_OBJECT(entry->image), "hide", G_CALLBACK(something_hidden), menuitem);
+
+ g_signal_connect(G_OBJECT(entry->image), "notify::sensitive", G_CALLBACK(sensitive_cb), menuitem);
+ }
+ if (entry->label != NULL) {
+ switch(packdirection) {
+ case GTK_PACK_DIRECTION_LTR:
+ gtk_label_set_angle(GTK_LABEL(entry->label), 0.0);
+ break;
+ case GTK_PACK_DIRECTION_TTB:
+ gtk_label_set_angle(GTK_LABEL(entry->label),
+ (orient == MATE_PANEL_APPLET_ORIENT_LEFT) ?
+ 270.0 : 90.0);
+ break;
+ default:
+ break;
+ }
+ gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(entry->label), FALSE, FALSE, 1);
+
+ if (gtk_widget_get_visible(GTK_WIDGET(entry->label))) {
+ something_visible = TRUE;
+ }
+
+ if (gtk_widget_get_sensitive(GTK_WIDGET(entry->label))) {
+ something_sensitive = TRUE;
+ }
+
+ g_signal_connect(G_OBJECT(entry->label), "show", G_CALLBACK(something_shown), menuitem);
+ g_signal_connect(G_OBJECT(entry->label), "hide", G_CALLBACK(something_hidden), menuitem);
+
+ g_signal_connect(G_OBJECT(entry->label), "notify::sensitive", G_CALLBACK(sensitive_cb), menuitem);
+ }
+ gtk_container_add(GTK_CONTAINER(menuitem), box);
+ gtk_widget_show(box);
+
+ if (entry->menu != NULL) {
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(entry->menu));
+ }
+
+ incoming_position_t position;
+ position.objposition = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER));
+ position.entryposition = indicator_object_get_location(io, entry);
+ position.menupos = 0;
+ position.found = FALSE;
+
+ gtk_container_foreach(GTK_CONTAINER(menubar), place_in_menu, &position);
+
+ gtk_menu_shell_insert(GTK_MENU_SHELL(menubar), menuitem, position.menupos);
+
+ if (something_visible) {
+ if (entry->accessible_desc != NULL) {
+ update_accessible_desc(entry, menuitem);
+ }
+ gtk_widget_show(menuitem);
+ }
+ gtk_widget_set_sensitive(menuitem, something_sensitive);
+
+ g_object_set_data(G_OBJECT(menuitem), MENU_DATA_INDICATOR_ENTRY, entry);
+ g_object_set_data(G_OBJECT(menuitem), MENU_DATA_INDICATOR_OBJECT, io);
+
+ return;
+}
+
+static void
+entry_removed_cb (GtkWidget * widget, gpointer userdata)
+{
+ gpointer data = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
+
+ if (data != userdata) {
+ return;
+ }
+
+ IndicatorObjectEntry * entry = (IndicatorObjectEntry *)data;
+ if (entry->label != NULL) {
+ g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), G_CALLBACK(something_shown), widget);
+ g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), G_CALLBACK(something_hidden), widget);
+ g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), G_CALLBACK(sensitive_cb), widget);
+ }
+ if (entry->image != NULL) {
+ g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), G_CALLBACK(something_shown), widget);
+ g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), G_CALLBACK(something_hidden), widget);
+ g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), G_CALLBACK(sensitive_cb), widget);
+ }
+
+ gtk_widget_destroy(widget);
+ return;
+}
+
+static void
+entry_removed (IndicatorObject * io G_GNUC_UNUSED, IndicatorObjectEntry * entry,
+ gpointer user_data)
+{
+ g_debug("Signal: Entry Removed");
+
+ gtk_container_foreach(GTK_CONTAINER(user_data), entry_removed_cb, entry);
+
+ return;
+}
+
+static void
+entry_moved_find_cb (GtkWidget * widget, gpointer userdata)
+{
+ gpointer * array = (gpointer *)userdata;
+ if (array[1] != NULL) {
+ return;
+ }
+
+ gpointer data = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
+
+ if (data != array[0]) {
+ return;
+ }
+
+ array[1] = widget;
+ return;
+}
+
+/* Gets called when an entry for an object was moved. */
+static void
+entry_moved (IndicatorObject * io, IndicatorObjectEntry * entry,
+ gint old G_GNUC_UNUSED, gint new G_GNUC_UNUSED, gpointer user_data)
+{
+ GtkWidget * menubar = GTK_WIDGET(user_data);
+
+ gpointer array[2];
+ array[0] = entry;
+ array[1] = NULL;
+
+ gtk_container_foreach(GTK_CONTAINER(menubar), entry_moved_find_cb, array);
+ if (array[1] == NULL) {
+ g_warning("Moving an entry that isn't in our menus.");
+ return;
+ }
+
+ GtkWidget * mi = GTK_WIDGET(array[1]);
+ g_object_ref(G_OBJECT(mi));
+ gtk_container_remove(GTK_CONTAINER(menubar), mi);
+
+ incoming_position_t position;
+ position.objposition = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER));
+ position.entryposition = indicator_object_get_location(io, entry);
+ position.menupos = 0;
+ position.found = FALSE;
+
+ gtk_container_foreach(GTK_CONTAINER(menubar), place_in_menu, &position);
+
+ gtk_menu_shell_insert(GTK_MENU_SHELL(menubar), mi, position.menupos);
+ g_object_unref(G_OBJECT(mi));
+
+ return;
+}
+
+static void
+menu_show (IndicatorObject * io, IndicatorObjectEntry * entry,
+ guint32 timestamp, gpointer user_data)
+{
+ GtkWidget * menubar = GTK_WIDGET(user_data);
+
+ if (entry == NULL) {
+ /* Close any open menus instead of opening one */
+ GList * entries = indicator_object_get_entries(io);
+ GList * iterator = NULL;
+ for (iterator = entries; iterator != NULL; iterator = g_list_next(iterator)) {
+ IndicatorObjectEntry * entrydata = (IndicatorObjectEntry *)iterator->data;
+ gtk_menu_popdown(entrydata->menu);
+ }
+ g_list_free(entries);
+
+ /* And tell the menubar to exit activation mode too */
+ gtk_menu_shell_cancel(GTK_MENU_SHELL(menubar));
+ return;
+ }
+
+ // TODO: do something sensible here
+}
+
+static void
+update_accessible_desc(IndicatorObjectEntry * entry, GtkWidget * menuitem)
+{
+ /* FIXME: We need to deal with the use case where the contents of the
+ label overrides what is found in the atk object's name, or at least
+ orca speaks the label instead of the atk object name.
+ */
+ AtkObject * menuitem_obj = gtk_widget_get_accessible(menuitem);
+ if (menuitem_obj == NULL) {
+ /* Should there be an error printed here? */
+ return;
+ }
+
+ if (entry->accessible_desc != NULL) {
+ atk_object_set_name(menuitem_obj, entry->accessible_desc);
+ } else {
+ atk_object_set_name(menuitem_obj, "");
+ }
+ return;
+}
+
+static void
+load_indicator (MatePanelApplet *applet, GtkWidget * menubar, IndicatorObject *io, const gchar *name)
+{
+ /* Set the environment it's in */
+ indicator_object_set_environment(io, (const GStrv)indicator_env);
+
+ /* Attach the 'name' to the object */
+#if HAVE_AYATANA_INDICATOR_NG || HAVE_UBUNTU_INDICATOR_NG
+ int pos = 5000 - indicator_object_get_position(io);
+ if (pos > 5000) {
+ pos = name2order(name);
+ }
+#else
+ int pos = name2order(name);
+#endif
+
+ g_object_set_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER, GINT_TO_POINTER(pos));
+
+ /* Connect to its signals */
+ g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, G_CALLBACK(entry_added), menubar);
+ g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED, G_CALLBACK(entry_removed), menubar);
+ g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_MOVED, G_CALLBACK(entry_moved), menubar);
+ g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_MENU_SHOW, G_CALLBACK(menu_show), menubar);
+ g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, G_CALLBACK(accessible_desc_update), menubar);
+
+ /* Track panel resize */
+ g_signal_connect_object(G_OBJECT(applet), "change-size", G_CALLBACK(entry_resized), G_OBJECT(io), 0);
+
+ /* Work on the entries */
+ GList * entries = indicator_object_get_entries(io);
+ GList * entry = NULL;
+
+ for (entry = entries; entry != NULL; entry = g_list_next(entry)) {
+ IndicatorObjectEntry * entrydata = (IndicatorObjectEntry *)entry->data;
+ entry_added(io, entrydata, menubar);
+ }
+
+ g_list_free(entries);
+}
+
+static gboolean
+load_module (const gchar * name, MatePanelApplet *applet, GtkWidget * menubar)
+{
+ g_debug("Looking at Module: %s", name);
+ g_return_val_if_fail(name != NULL, FALSE);
+
+ if (!g_str_has_suffix(name, G_MODULE_SUFFIX)) {
+ return FALSE;
+ }
+
+ g_debug("Loading Module: %s", name);
+
+ /* Build the object for the module */
+ gchar * fullpath = g_build_filename(INDICATOR_DIR, name, NULL);
+ IndicatorObject * io = indicator_object_new_from_file(fullpath);
+ g_free(fullpath);
+
+ load_indicator(applet, menubar, io, name);
+
+ return TRUE;
+}
+
+static void
+load_modules (MatePanelApplet *applet, GtkWidget *menubar, gint *indicators_loaded)
+{
+ if (g_file_test(INDICATOR_DIR, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
+ GDir * dir = g_dir_open(INDICATOR_DIR, 0, NULL);
+
+ const gchar * name;
+ gint count = 0;
+ while ((name = g_dir_read_name(dir)) != NULL) {
+#ifdef INDICATOR_APPLET_APPMENU
+ if (g_strcmp0(name, INDICATOR_SERVICE_APPMENU)) {
+ continue;
+ }
+#else
+ if (!g_strcmp0(name, INDICATOR_SERVICE_APPMENU)) {
+ continue;
+ }
+#endif
+#ifdef INDICATOR_APPLET
+ if (!g_strcmp0(name, INDICATOR_SERVICE_ME)) {
+ continue;
+ }
+ if (!g_strcmp0(name, INDICATOR_SERVICE_DATETIME)) {
+ continue;
+ }
+#endif
+ if (load_module(name, applet, menubar)) {
+ count++;
+ }
+ }
+
+ *indicators_loaded += count;
+
+ g_dir_close (dir);
+ }
+}
+
+#if HAVE_AYATANA_INDICATOR_NG || HAVE_UBUNTU_INDICATOR_NG
+
+static void
+load_indicators_from_indicator_files (MatePanelApplet *applet, GtkWidget *menubar, gint *indicators_loaded)
+{
+ GDir *dir;
+ const gchar *name;
+ GError *error = NULL;
+
+ dir = g_dir_open (INDICATOR_SERVICE_DIR, 0, &error);
+
+ if (!dir) {
+ g_warning ("unable to open indicator service file directory: %s", error->message);
+ g_error_free (error);
+
+ return;
+ }
+
+ gint count = 0;
+ while ((name = g_dir_read_name (dir))) {
+ gchar *filename;
+ IndicatorNg *indicator;
+
+ filename = g_build_filename (INDICATOR_SERVICE_DIR, name, NULL);
+ indicator = indicator_ng_new_for_profile (filename, "desktop", &error);
+ g_free (filename);
+
+#ifdef INDICATOR_APPLET_APPMENU
+ if (g_strcmp0(name, INDICATOR_SERVICE_APPMENU_NG)) {
+ continue;
+ }
+#else
+ if (!g_strcmp0(name, INDICATOR_SERVICE_APPMENU_NG)) {
+ continue;
+ }
+#endif
+#ifdef INDICATOR_APPLET
+ if (!g_strcmp0(name, INDICATOR_SERVICE_ME_NG)) {
+ continue;
+ }
+ if (!g_strcmp0(name, INDICATOR_SERVICE_DATETIME_NG)) {
+ continue;
+ }
+#endif
+
+ if (indicator) {
+ load_indicator(applet, menubar, INDICATOR_OBJECT (indicator), name);
+ count++;
+ }else{
+ g_warning ("unable to load '%s': %s", name, error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ *indicators_loaded += count;
+
+ g_dir_close (dir);
+}
+#endif /* HAVE_AYATANA_INDICATOR_NG || HAVE_UBUNTU_INDICATOR_NG */
+
+static void
+hotkey_filter (char * keystring G_GNUC_UNUSED, gpointer data)
+{
+ g_return_if_fail(GTK_IS_MENU_SHELL(data));
+
+ /* Oh, wow, it's us! */
+ GList * children = gtk_container_get_children(GTK_CONTAINER(data));
+ if (children == NULL) {
+ g_debug("Menubar has no children");
+ return;
+ }
+
+ gtk_menu_shell_select_item(GTK_MENU_SHELL(data), GTK_WIDGET(g_list_last(children)->data));
+ g_list_free(children);
+ return;
+}
+
+static gboolean
+menubar_press (GtkWidget * widget,
+ GdkEventButton *event,
+ gpointer data G_GNUC_UNUSED)
+{
+ if (event->button != 1) {
+ g_signal_stop_emission_by_name(widget, "button-press-event");
+ }
+
+ return FALSE;
+}
+
+static gboolean
+menubar_on_draw (GtkWidget * widget,
+ cairo_t * cr,
+ GtkWidget * menubar)
+{
+ /* FIXME: either port to gtk_render_focus or remove this function */
+ if (gtk_widget_has_focus(menubar))
+ gtk_paint_focus(gtk_widget_get_style(widget),
+ cr,
+ gtk_widget_get_state(menubar),
+ widget, "menubar-applet", 0, 0, -1, -1);
+
+ return FALSE;
+}
+
+static void
+about_cb (GtkAction *action G_GNUC_UNUSED,
+ gpointer data G_GNUC_UNUSED)
+{
+ static const gchar *authors[] = {
+ "Ted Gould <[email protected]>",
+ NULL
+ };
+
+ static gchar *license[] = {
+ N_("This program is free software: you can redistribute it and/or modify it "
+ "under the terms of the GNU General Public License version 3, as published "
+ "by the Free Software Foundation."),
+ N_("This program is distributed in the hope that it will be useful, but "
+ "WITHOUT ANY WARRANTY; without even the implied warranties of "
+ "MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR "
+ "PURPOSE. See the GNU General Public License for more details."),
+ N_("You should have received a copy of the GNU General Public License along "
+ "with this program. If not, see <http://www.gnu.org/licenses/>."),
+ NULL
+ };
+ gchar *license_i18n;
+
+ license_i18n = g_strconcat (_(license[0]), "\n\n", _(license[1]), "\n\n", _(license[2]), NULL);
+
+ gtk_show_about_dialog(NULL,
+ "version", VERSION,
+ "copyright", _("Copyright \xc2\xa9 2009-2010 Canonical, Ltd.\n"
+ "Copyright \xc2\xa9 2011-2021 MATE developers"),
+#ifdef INDICATOR_APPLET_APPMENU
+ "comments", _("An applet to hold your application menus."),
+#endif
+ "comments", _("An applet to hold all of the system indicators."),
+ "authors", authors,
+ "license", license_i18n,
+ "wrap-license", TRUE,
+ "translator-credits", _("translator-credits"),
+ "logo-icon-name", "mate-indicator-applet",
+ "icon-name", "mate-indicator-applet",
+ "website", "https://mate-desktop.org",
+ "website-label", _("MATE Website"),
+ NULL
+ );
+
+ g_free (license_i18n);
+
+ return;
+}
+
+static gboolean
+swap_orient_cb (GtkWidget *item, gpointer data)
+{
+ GtkWidget *from = (GtkWidget *) data;
+ GtkWidget *to = (GtkWidget *) g_object_get_data(G_OBJECT(from), "to");
+ g_object_ref(G_OBJECT(item));
+ gtk_container_remove(GTK_CONTAINER(from), item);
+ if (GTK_IS_LABEL(item)) {
+ switch(packdirection) {
+ case GTK_PACK_DIRECTION_LTR:
+ gtk_label_set_angle(GTK_LABEL(item), 0.0);
+ break;
+ case GTK_PACK_DIRECTION_TTB:
+ gtk_label_set_angle(GTK_LABEL(item),
+ (orient == MATE_PANEL_APPLET_ORIENT_LEFT) ?
+ 270.0 : 90.0);
+ break;
+ default:
+ break;
+ }
+ }
+ gtk_box_pack_start(GTK_BOX(to), item, FALSE, FALSE, 0);
+ return TRUE;
+}
+
+static gboolean
+reorient_box_cb (GtkWidget *menuitem, gpointer data)
+{
+ GtkWidget *from = g_object_get_data(G_OBJECT(menuitem), "box");
+ GtkWidget *to = (packdirection == GTK_PACK_DIRECTION_LTR) ?
+ gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0) : gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ g_object_set_data(G_OBJECT(from), "to", to);
+ gtk_container_foreach(GTK_CONTAINER(from), (GtkCallback)swap_orient_cb,
+ from);
+ gtk_container_remove(GTK_CONTAINER(menuitem), from);
+ gtk_container_add(GTK_CONTAINER(menuitem), to);
+ g_object_set_data(G_OBJECT(menuitem), "box", to);
+ gtk_widget_show_all(menuitem);
+ return TRUE;
+}
+
+static gboolean
+matepanelapplet_reorient_cb (GtkWidget *applet, MatePanelAppletOrient neworient,
+ gpointer data)
+{
+ GtkWidget *menubar = (GtkWidget *)data;
+ if ((((neworient == MATE_PANEL_APPLET_ORIENT_UP) ||
+ (neworient == MATE_PANEL_APPLET_ORIENT_DOWN)) &&
+ ((orient == MATE_PANEL_APPLET_ORIENT_LEFT) ||
+ (orient == MATE_PANEL_APPLET_ORIENT_RIGHT))) ||
+ (((neworient == MATE_PANEL_APPLET_ORIENT_LEFT) ||
+ (neworient == MATE_PANEL_APPLET_ORIENT_RIGHT)) &&
+ ((orient == MATE_PANEL_APPLET_ORIENT_UP) ||
+ (orient == MATE_PANEL_APPLET_ORIENT_DOWN)))) {
+ packdirection = (packdirection == GTK_PACK_DIRECTION_LTR) ?
+ GTK_PACK_DIRECTION_TTB : GTK_PACK_DIRECTION_LTR;
+ gtk_menu_bar_set_pack_direction(GTK_MENU_BAR(menubar),
+ packdirection);
+ orient = neworient;
+ gtk_container_foreach(GTK_CONTAINER(menubar),
+ (GtkCallback)reorient_box_cb, NULL);
+ }
+ orient = neworient;
+ return FALSE;
+}
+
+#ifdef N_
+#undef N_
+#endif
+#define N_(x) x
+
+static void
+log_to_file_cb (GObject * source_obj G_GNUC_UNUSED,
+ GAsyncResult * result G_GNUC_UNUSED, gpointer user_data)
+{
+ g_free(user_data);
+ return;
+}
+
+static void
+log_to_file (const gchar * domain G_GNUC_UNUSED,
+ GLogLevelFlags level G_GNUC_UNUSED,
+ const gchar * message,
+ gpointer data G_GNUC_UNUSED)
+{
+ if (log_file == NULL) {
+ GError * error = NULL;
+ gchar * filename = g_build_filename(g_get_user_cache_dir(), LOG_FILE_NAME, NULL);
+ GFile * file = g_file_new_for_path(filename);
+ g_free(filename);
+
+ if (!g_file_test(g_get_user_cache_dir(), G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
+ GFile * cachedir = g_file_new_for_path(g_get_user_cache_dir());
+ g_file_make_directory_with_parents(cachedir, NULL, &error);
+
+ if (error != NULL) {
+ g_error("Unable to make directory '%s' for log file: %s", g_get_user_cache_dir(), error->message);
+ }
+ }
+
+ g_file_delete(file, NULL, NULL);
+
+ GFileIOStream * io = g_file_create_readwrite(file,
+ G_FILE_CREATE_REPLACE_DESTINATION, /* flags */
+ NULL, /* cancelable */
+ &error); /* error */
+ if (error != NULL) {
+ g_error("Unable to replace file: %s", error->message);
+ }
+
+ log_file = g_io_stream_get_output_stream(G_IO_STREAM(io));
+ }
+
+ gchar * outputstring = g_strdup_printf("%s\n", message);
+ g_output_stream_write_async(log_file,
+ outputstring, /* data */
+ strlen(outputstring), /* length */
+ G_PRIORITY_LOW, /* priority */
+ NULL, /* cancelable */
+ log_to_file_cb, /* callback */
+ outputstring); /* data */
+
+ return;
+}
+
+static gboolean
+applet_fill_cb (MatePanelApplet * applet, const gchar * iid G_GNUC_UNUSED,
+ gpointer data G_GNUC_UNUSED)
+{
+#if HAVE_AYATANA_INDICATOR_NG || HAVE_UBUNTU_INDICATOR_NG
+ ido_init();
+#endif
+
+ static const GtkActionEntry menu_actions[] = {
+ {"About", "help-about", N_("_About"), NULL, NULL, G_CALLBACK(about_cb)}
+ };
+ static const gchar *menu_xml = "<menuitem name=\"About\" action=\"About\"/>";
+
+ static gboolean first_time = FALSE;
+ GtkWidget *menubar;
+ gint indicators_loaded = 0;
+ GtkActionGroup *action_group;
+
+ if (!first_time)
+ {
+ first_time = TRUE;
+#ifdef INDICATOR_APPLET
+ g_set_application_name(_("Indicator Applet"));
+#endif
+#ifdef INDICATOR_APPLET_COMPLETE
+ g_set_application_name(_("Indicator Applet Complete"));
+#endif
+#ifdef INDICATOR_APPLET_APPMENU
+ g_set_application_name(_("Indicator Applet Application Menu"));
+#endif
+
+ g_log_set_default_handler(log_to_file, NULL);
+
+ tomboy_keybinder_init();
+ }
+
+ /* Set panel options */
+ gtk_container_set_border_width(GTK_CONTAINER (applet), 0);
+ mate_panel_applet_set_flags(applet, MATE_PANEL_APPLET_EXPAND_MINOR);
+ menubar = gtk_menu_bar_new();
+ action_group = gtk_action_group_new ("Indicator Applet Actions");
+ gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (action_group, menu_actions,
+ G_N_ELEMENTS (menu_actions),
+ menubar);
+ mate_panel_applet_setup_menu(applet, menu_xml, action_group);
+ g_object_unref(action_group);
+#ifdef INDICATOR_APPLET
+ atk_object_set_name (gtk_widget_get_accessible (GTK_WIDGET (applet)),
+ "indicator-applet");
+#endif
+#ifdef INDICATOR_APPLET_COMPLETE
+ atk_object_set_name (gtk_widget_get_accessible (GTK_WIDGET (applet)),
+ "indicator-applet-complete");
+#endif
+#ifdef INDICATOR_APPLET_APPMENU
+ atk_object_set_name (gtk_widget_get_accessible (GTK_WIDGET (applet)),
+ "indicator-applet-appmenu");
+#endif
+
+ /* Init some theme/icon stuff */
+ gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(),
+ INDICATOR_ICONS_DIR);
+ /* g_debug("Icons directory: %s", INDICATOR_ICONS_DIR); */
+ gtk_widget_set_name(GTK_WIDGET (applet), "fast-user-switch-applet");
+
+ /* Build menubar */
+ size = (mate_panel_applet_get_size (applet));
+ orient = (mate_panel_applet_get_orient(applet));
+ packdirection = ((orient == MATE_PANEL_APPLET_ORIENT_UP) ||
+ (orient == MATE_PANEL_APPLET_ORIENT_DOWN)) ?
+ GTK_PACK_DIRECTION_LTR : GTK_PACK_DIRECTION_TTB;
+ gtk_menu_bar_set_pack_direction(GTK_MENU_BAR(menubar),
+ packdirection);
+ gtk_widget_set_can_focus (menubar, TRUE);
+ gtk_widget_set_name(GTK_WIDGET (menubar), "fast-user-switch-menubar");
+ g_signal_connect(menubar, "button-press-event", G_CALLBACK(menubar_press), NULL);
+ g_signal_connect_after(menubar, "draw", G_CALLBACK(menubar_on_draw), menubar);
+ g_signal_connect(applet, "change-orient",
+ G_CALLBACK(matepanelapplet_reorient_cb), menubar);
+ gtk_container_set_border_width(GTK_CONTAINER(menubar), 0);
+
+ /* Add in filter func */
+ tomboy_keybinder_bind(hotkey_keycode, hotkey_filter, menubar);
+
+ load_modules(applet, menubar, &indicators_loaded);
+#if HAVE_AYATANA_INDICATOR_NG || HAVE_UBUNTU_INDICATOR_NG
+ load_indicators_from_indicator_files(applet, menubar, &indicators_loaded);
+#endif
+
+ if (indicators_loaded == 0) {
+ /* A label to allow for click through */
+ GtkWidget * item = gtk_label_new(_("No Indicators"));
+ mate_panel_applet_set_background_widget(applet, item);
+ gtk_container_add(GTK_CONTAINER(applet), item);
+ gtk_widget_show(item);
+ } else {
+ gtk_container_add(GTK_CONTAINER(applet), menubar);
+ mate_panel_applet_set_background_widget(applet, menubar);
+ gtk_widget_show(menubar);
+ }
+
+ gtk_widget_show(GTK_WIDGET(applet));
+
+ return TRUE;
+
+}
diff --git a/indicator-applet/src/eggaccelerators.c b/indicator-applet/src/eggaccelerators.c
new file mode 100644
index 00000000..60436615
--- /dev/null
+++ b/indicator-applet/src/eggaccelerators.c
@@ -0,0 +1,527 @@
+/* eggaccelerators.c
+ * Copyright (C) 2002 Red Hat, Inc.; Copyright 1998, 2001 Tim Janik
+ * Developed by Havoc Pennington, Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ */
+
+#include "eggaccelerators.h"
+
+#include <string.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdkkeysyms.h>
+
+enum
+{
+ EGG_MODMAP_ENTRY_SHIFT = 0,
+ EGG_MODMAP_ENTRY_LOCK = 1,
+ EGG_MODMAP_ENTRY_CONTROL = 2,
+ EGG_MODMAP_ENTRY_MOD1 = 3,
+ EGG_MODMAP_ENTRY_MOD2 = 4,
+ EGG_MODMAP_ENTRY_MOD3 = 5,
+ EGG_MODMAP_ENTRY_MOD4 = 6,
+ EGG_MODMAP_ENTRY_MOD5 = 7,
+ EGG_MODMAP_ENTRY_LAST = 8
+};
+
+#define MODMAP_ENTRY_TO_MODIFIER(x) (1 << (x))
+
+typedef struct
+{
+ EggVirtualModifierType mapping[EGG_MODMAP_ENTRY_LAST];
+
+} EggModmap;
+
+const EggModmap* egg_keymap_get_modmap (GdkKeymap *keymap);
+
+static inline gboolean
+is_alt (const gchar *string)
+{
+ return ((string[0] == '<') &&
+ (string[1] == 'a' || string[1] == 'A') &&
+ (string[2] == 'l' || string[2] == 'L') &&
+ (string[3] == 't' || string[3] == 'T') &&
+ (string[4] == '>'));
+}
+
+static inline gboolean
+is_ctl (const gchar *string)
+{
+ return ((string[0] == '<') &&
+ (string[1] == 'c' || string[1] == 'C') &&
+ (string[2] == 't' || string[2] == 'T') &&
+ (string[3] == 'l' || string[3] == 'L') &&
+ (string[4] == '>'));
+}
+
+static inline gboolean
+is_modx (const gchar *string)
+{
+ return ((string[0] == '<') &&
+ (string[1] == 'm' || string[1] == 'M') &&
+ (string[2] == 'o' || string[2] == 'O') &&
+ (string[3] == 'd' || string[3] == 'D') &&
+ (string[4] >= '1' && string[4] <= '5') &&
+ (string[5] == '>'));
+}
+
+static inline gboolean
+is_ctrl (const gchar *string)
+{
+ return ((string[0] == '<') &&
+ (string[1] == 'c' || string[1] == 'C') &&
+ (string[2] == 't' || string[2] == 'T') &&
+ (string[3] == 'r' || string[3] == 'R') &&
+ (string[4] == 'l' || string[4] == 'L') &&
+ (string[5] == '>'));
+}
+
+static inline gboolean
+is_shft (const gchar *string)
+{
+ return ((string[0] == '<') &&
+ (string[1] == 's' || string[1] == 'S') &&
+ (string[2] == 'h' || string[2] == 'H') &&
+ (string[3] == 'f' || string[3] == 'F') &&
+ (string[4] == 't' || string[4] == 'T') &&
+ (string[5] == '>'));
+}
+
+static inline gboolean
+is_shift (const gchar *string)
+{
+ return ((string[0] == '<') &&
+ (string[1] == 's' || string[1] == 'S') &&
+ (string[2] == 'h' || string[2] == 'H') &&
+ (string[3] == 'i' || string[3] == 'I') &&
+ (string[4] == 'f' || string[4] == 'F') &&
+ (string[5] == 't' || string[5] == 'T') &&
+ (string[6] == '>'));
+}
+
+static inline gboolean
+is_control (const gchar *string)
+{
+ return ((string[0] == '<') &&
+ (string[1] == 'c' || string[1] == 'C') &&
+ (string[2] == 'o' || string[2] == 'O') &&
+ (string[3] == 'n' || string[3] == 'N') &&
+ (string[4] == 't' || string[4] == 'T') &&
+ (string[5] == 'r' || string[5] == 'R') &&
+ (string[6] == 'o' || string[6] == 'O') &&
+ (string[7] == 'l' || string[7] == 'L') &&
+ (string[8] == '>'));
+}
+
+static inline gboolean
+is_release (const gchar *string)
+{
+ return ((string[0] == '<') &&
+ (string[1] == 'r' || string[1] == 'R') &&
+ (string[2] == 'e' || string[2] == 'E') &&
+ (string[3] == 'l' || string[3] == 'L') &&
+ (string[4] == 'e' || string[4] == 'E') &&
+ (string[5] == 'a' || string[5] == 'A') &&
+ (string[6] == 's' || string[6] == 'S') &&
+ (string[7] == 'e' || string[7] == 'E') &&
+ (string[8] == '>'));
+}
+
+static inline gboolean
+is_meta (const gchar *string)
+{
+ return ((string[0] == '<') &&
+ (string[1] == 'm' || string[1] == 'M') &&
+ (string[2] == 'e' || string[2] == 'E') &&
+ (string[3] == 't' || string[3] == 'T') &&
+ (string[4] == 'a' || string[4] == 'A') &&
+ (string[5] == '>'));
+}
+
+static inline gboolean
+is_super (const gchar *string)
+{
+ return ((string[0] == '<') &&
+ (string[1] == 's' || string[1] == 'S') &&
+ (string[2] == 'u' || string[2] == 'U') &&
+ (string[3] == 'p' || string[3] == 'P') &&
+ (string[4] == 'e' || string[4] == 'E') &&
+ (string[5] == 'r' || string[5] == 'R') &&
+ (string[6] == '>'));
+}
+
+static inline gboolean
+is_hyper (const gchar *string)
+{
+ return ((string[0] == '<') &&
+ (string[1] == 'h' || string[1] == 'H') &&
+ (string[2] == 'y' || string[2] == 'Y') &&
+ (string[3] == 'p' || string[3] == 'P') &&
+ (string[4] == 'e' || string[4] == 'E') &&
+ (string[5] == 'r' || string[5] == 'R') &&
+ (string[6] == '>'));
+}
+
+/**
+ * egg_accelerator_parse_virtual:
+ * @accelerator: string representing an accelerator
+ * @accelerator_key: return location for accelerator keyval
+ * @accelerator_mods: return location for accelerator modifier mask
+ *
+ * Parses a string representing a virtual accelerator. The format
+ * looks like "&lt;Control&gt;a" or "&lt;Shift&gt;&lt;Alt&gt;F1" or
+ * "&lt;Release&gt;z" (the last one is for key release). The parser
+ * is fairly liberal and allows lower or upper case, and also
+ * abbreviations such as "&lt;Ctl&gt;" and "&lt;Ctrl&gt;".
+ *
+ * If the parse fails, @accelerator_key and @accelerator_mods will
+ * be set to 0 (zero) and %FALSE will be returned. If the string contains
+ * only modifiers, @accelerator_key will be set to 0 but %TRUE will be
+ * returned.
+ *
+ * The virtual vs. concrete accelerator distinction is a relic of
+ * how the X Window System works; there are modifiers Mod2-Mod5 that
+ * can represent various keyboard keys (numlock, meta, hyper, etc.),
+ * the virtual modifier represents the keyboard key, the concrete
+ * modifier the actual Mod2-Mod5 bits in the key press event.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+egg_accelerator_parse_virtual (const gchar *accelerator,
+ guint *accelerator_key,
+ EggVirtualModifierType *accelerator_mods)
+{
+ guint keyval;
+ GdkModifierType mods;
+ gint len;
+ gboolean bad_keyval;
+
+ if (accelerator_key)
+ *accelerator_key = 0;
+ if (accelerator_mods)
+ *accelerator_mods = 0;
+
+ g_return_val_if_fail (accelerator != NULL, FALSE);
+
+ bad_keyval = FALSE;
+
+ keyval = 0;
+ mods = 0;
+ len = strlen (accelerator);
+ while (len)
+ {
+ if (*accelerator == '<')
+ {
+ if (len >= 9 && is_release (accelerator))
+ {
+ accelerator += 9;
+ len -= 9;
+ mods |= EGG_VIRTUAL_RELEASE_MASK;
+ }
+ else if (len >= 9 && is_control (accelerator))
+ {
+ accelerator += 9;
+ len -= 9;
+ mods |= EGG_VIRTUAL_CONTROL_MASK;
+ }
+ else if (len >= 7 && is_shift (accelerator))
+ {
+ accelerator += 7;
+ len -= 7;
+ mods |= EGG_VIRTUAL_SHIFT_MASK;
+ }
+ else if (len >= 6 && is_shft (accelerator))
+ {
+ accelerator += 6;
+ len -= 6;
+ mods |= EGG_VIRTUAL_SHIFT_MASK;
+ }
+ else if (len >= 6 && is_ctrl (accelerator))
+ {
+ accelerator += 6;
+ len -= 6;
+ mods |= EGG_VIRTUAL_CONTROL_MASK;
+ }
+ else if (len >= 6 && is_modx (accelerator))
+ {
+ static const guint mod_vals[] = {
+ EGG_VIRTUAL_ALT_MASK, EGG_VIRTUAL_MOD2_MASK, EGG_VIRTUAL_MOD3_MASK,
+ EGG_VIRTUAL_MOD4_MASK, EGG_VIRTUAL_MOD5_MASK
+ };
+
+ len -= 6;
+ accelerator += 4;
+ mods |= mod_vals[*accelerator - '1'];
+ accelerator += 2;
+ }
+ else if (len >= 5 && is_ctl (accelerator))
+ {
+ accelerator += 5;
+ len -= 5;
+ mods |= EGG_VIRTUAL_CONTROL_MASK;
+ }
+ else if (len >= 5 && is_alt (accelerator))
+ {
+ accelerator += 5;
+ len -= 5;
+ mods |= EGG_VIRTUAL_ALT_MASK;
+ }
+ else if (len >= 6 && is_meta (accelerator))
+ {
+ accelerator += 6;
+ len -= 6;
+ mods |= EGG_VIRTUAL_META_MASK;
+ }
+ else if (len >= 7 && is_hyper (accelerator))
+ {
+ accelerator += 7;
+ len -= 7;
+ mods |= EGG_VIRTUAL_HYPER_MASK;
+ }
+ else if (len >= 7 && is_super (accelerator))
+ {
+ accelerator += 7;
+ len -= 7;
+ mods |= EGG_VIRTUAL_SUPER_MASK;
+ }
+ else
+ {
+ gchar last_ch;
+
+ last_ch = *accelerator;
+ while (last_ch && last_ch != '>')
+ {
+ last_ch = *accelerator;
+ accelerator += 1;
+ len -= 1;
+ }
+ }
+ }
+ else
+ {
+ keyval = gdk_keyval_from_name (accelerator);
+
+ if (keyval == 0)
+ bad_keyval = TRUE;
+
+ accelerator += len;
+ len -= len;
+ }
+ }
+
+ if (accelerator_key)
+ *accelerator_key = gdk_keyval_to_lower (keyval);
+ if (accelerator_mods)
+ *accelerator_mods = mods;
+
+ return !bad_keyval;
+}
+
+void
+egg_keymap_resolve_virtual_modifiers (GdkKeymap *keymap,
+ EggVirtualModifierType virtual_mods,
+ GdkModifierType *concrete_mods)
+{
+ GdkModifierType concrete;
+ int i;
+ const EggModmap *modmap;
+
+ g_return_if_fail (GDK_IS_KEYMAP (keymap));
+ g_return_if_fail (concrete_mods != NULL);
+
+ modmap = egg_keymap_get_modmap (keymap);
+
+ /* Not so sure about this algorithm. */
+
+ concrete = 0;
+ i = 0;
+ while (i < EGG_MODMAP_ENTRY_LAST)
+ {
+ if (modmap->mapping[i] & virtual_mods)
+ concrete |= (1 << i);
+
+ ++i;
+ }
+
+ *concrete_mods = concrete;
+}
+
+void
+egg_keymap_virtualize_modifiers (GdkKeymap *keymap,
+ GdkModifierType concrete_mods,
+ EggVirtualModifierType *virtual_mods)
+{
+ GdkModifierType virtual;
+ int i;
+ const EggModmap *modmap;
+
+ g_return_if_fail (GDK_IS_KEYMAP (keymap));
+ g_return_if_fail (virtual_mods != NULL);
+
+ modmap = egg_keymap_get_modmap (keymap);
+
+ /* Not so sure about this algorithm. */
+
+ virtual = 0;
+ i = 0;
+ while (i < EGG_MODMAP_ENTRY_LAST)
+ {
+ if ((1 << i) & concrete_mods)
+ {
+ EggVirtualModifierType cleaned;
+
+ cleaned = modmap->mapping[i] & ~(EGG_VIRTUAL_MOD2_MASK |
+ EGG_VIRTUAL_MOD3_MASK |
+ EGG_VIRTUAL_MOD4_MASK |
+ EGG_VIRTUAL_MOD5_MASK);
+
+ if (cleaned != 0)
+ {
+ virtual |= cleaned;
+ }
+ else
+ {
+ /* Rather than dropping mod2->mod5 if not bound,
+ * go ahead and use the concrete names
+ */
+ virtual |= modmap->mapping[i];
+ }
+ }
+
+ ++i;
+ }
+
+ *virtual_mods = virtual;
+}
+
+static void
+reload_modmap (GdkKeymap *keymap,
+ EggModmap *modmap)
+{
+ XModifierKeymap *xmodmap;
+ int map_size;
+ int i;
+
+ /* FIXME multihead */
+ xmodmap = XGetModifierMapping (gdk_x11_get_default_xdisplay ());
+
+ memset (modmap->mapping, 0, sizeof (modmap->mapping));
+
+ /* there are 8 modifiers, and the first 3 are shift, shift lock,
+ * and control
+ */
+ map_size = 8 * xmodmap->max_keypermod;
+ i = 3 * xmodmap->max_keypermod;
+ while (i < map_size)
+ {
+ /* get the key code at this point in the map,
+ * see if its keysym is one we're interested in
+ */
+ int keycode = xmodmap->modifiermap[i];
+ GdkKeymapKey *keys;
+ guint *keyvals;
+ int n_entries;
+ int j;
+ EggVirtualModifierType mask;
+
+ keys = NULL;
+ keyvals = NULL;
+ n_entries = 0;
+
+ gdk_keymap_get_entries_for_keycode (keymap,
+ keycode,
+ &keys, &keyvals, &n_entries);
+
+ mask = 0;
+ j = 0;
+ while (j < n_entries)
+ {
+ if (keyvals[j] == GDK_KEY_Num_Lock)
+ mask |= EGG_VIRTUAL_NUM_LOCK_MASK;
+ else if (keyvals[j] == GDK_KEY_Scroll_Lock)
+ mask |= EGG_VIRTUAL_SCROLL_LOCK_MASK;
+ else if (keyvals[j] == GDK_KEY_Meta_L ||
+ keyvals[j] == GDK_KEY_Meta_R)
+ mask |= EGG_VIRTUAL_META_MASK;
+ else if (keyvals[j] == GDK_KEY_Hyper_L ||
+ keyvals[j] == GDK_KEY_Hyper_R)
+ mask |= EGG_VIRTUAL_HYPER_MASK;
+ else if (keyvals[j] == GDK_KEY_Super_L ||
+ keyvals[j] == GDK_KEY_Super_R)
+ mask |= EGG_VIRTUAL_SUPER_MASK;
+ else if (keyvals[j] == GDK_KEY_Mode_switch)
+ mask |= EGG_VIRTUAL_MODE_SWITCH_MASK;
+
+ ++j;
+ }
+
+ /* Mod1Mask is 1 << 3 for example, i.e. the
+ * fourth modifier, i / keyspermod is the modifier
+ * index
+ */
+ modmap->mapping[i/xmodmap->max_keypermod] |= mask;
+
+ g_free (keyvals);
+ g_free (keys);
+
+ ++i;
+ }
+
+ /* Add in the not-really-virtual fixed entries */
+ modmap->mapping[EGG_MODMAP_ENTRY_SHIFT] |= EGG_VIRTUAL_SHIFT_MASK;
+ modmap->mapping[EGG_MODMAP_ENTRY_CONTROL] |= EGG_VIRTUAL_CONTROL_MASK;
+ modmap->mapping[EGG_MODMAP_ENTRY_LOCK] |= EGG_VIRTUAL_LOCK_MASK;
+ modmap->mapping[EGG_MODMAP_ENTRY_MOD1] |= EGG_VIRTUAL_ALT_MASK;
+ modmap->mapping[EGG_MODMAP_ENTRY_MOD2] |= EGG_VIRTUAL_MOD2_MASK;
+ modmap->mapping[EGG_MODMAP_ENTRY_MOD3] |= EGG_VIRTUAL_MOD3_MASK;
+ modmap->mapping[EGG_MODMAP_ENTRY_MOD4] |= EGG_VIRTUAL_MOD4_MASK;
+ modmap->mapping[EGG_MODMAP_ENTRY_MOD5] |= EGG_VIRTUAL_MOD5_MASK;
+
+ XFreeModifiermap (xmodmap);
+}
+
+const EggModmap*
+egg_keymap_get_modmap (GdkKeymap *keymap)
+{
+ EggModmap *modmap;
+
+ /* This is all a hack, much simpler when we can just
+ * modify GDK directly.
+ */
+
+ modmap = g_object_get_data (G_OBJECT (keymap),
+ "egg-modmap");
+
+ if (modmap == NULL)
+ {
+ modmap = g_new0 (EggModmap, 1);
+
+ /* FIXME modify keymap change events with an event filter
+ * and force a reload if we get one
+ */
+
+ reload_modmap (keymap, modmap);
+
+ g_object_set_data_full (G_OBJECT (keymap),
+ "egg-modmap",
+ modmap,
+ g_free);
+ }
+
+ g_assert (modmap != NULL);
+
+ return modmap;
+}
diff --git a/indicator-applet/src/eggaccelerators.h b/indicator-applet/src/eggaccelerators.h
new file mode 100644
index 00000000..3ecdd1d6
--- /dev/null
+++ b/indicator-applet/src/eggaccelerators.h
@@ -0,0 +1,81 @@
+/* eggaccelerators.h
+ * Copyright (C) 2002 Red Hat, Inc.
+ * Developed by Havoc Pennington
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ */
+
+#ifndef __EGG_ACCELERATORS_H__
+#define __EGG_ACCELERATORS_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+/* Where a value is also in GdkModifierType we coincide,
+ * otherwise we don't overlap.
+ */
+typedef enum
+{
+ EGG_VIRTUAL_SHIFT_MASK = 1 << 0,
+ EGG_VIRTUAL_LOCK_MASK = 1 << 1,
+ EGG_VIRTUAL_CONTROL_MASK = 1 << 2,
+
+ EGG_VIRTUAL_ALT_MASK = 1 << 3, /* fixed as Mod1 */
+
+ EGG_VIRTUAL_MOD2_MASK = 1 << 4,
+ EGG_VIRTUAL_MOD3_MASK = 1 << 5,
+ EGG_VIRTUAL_MOD4_MASK = 1 << 6,
+ EGG_VIRTUAL_MOD5_MASK = 1 << 7,
+
+#if 0
+ GDK_BUTTON1_MASK = 1 << 8,
+ GDK_BUTTON2_MASK = 1 << 9,
+ GDK_BUTTON3_MASK = 1 << 10,
+ GDK_BUTTON4_MASK = 1 << 11,
+ GDK_BUTTON5_MASK = 1 << 12,
+ /* 13, 14 are used by Xkb for the keyboard group */
+#endif
+
+ EGG_VIRTUAL_META_MASK = 1 << 24,
+ EGG_VIRTUAL_SUPER_MASK = 1 << 25,
+ EGG_VIRTUAL_HYPER_MASK = 1 << 26,
+ EGG_VIRTUAL_MODE_SWITCH_MASK = 1 << 27,
+ EGG_VIRTUAL_NUM_LOCK_MASK = 1 << 28,
+ EGG_VIRTUAL_SCROLL_LOCK_MASK = 1 << 29,
+
+ /* Also in GdkModifierType */
+ EGG_VIRTUAL_RELEASE_MASK = 1 << 30,
+
+ /* 28-31 24-27 20-23 16-19 12-15 8-11 4-7 0-3
+ * 7 f 0 0 0 0 f f
+ */
+ EGG_VIRTUAL_MODIFIER_MASK = 0x7f0000ff
+
+} EggVirtualModifierType;
+
+gboolean egg_accelerator_parse_virtual (const gchar *accelerator,
+ guint *accelerator_key,
+ EggVirtualModifierType *accelerator_mods);
+void egg_keymap_resolve_virtual_modifiers (GdkKeymap *keymap,
+ EggVirtualModifierType virtual_mods,
+ GdkModifierType *concrete_mods);
+void egg_keymap_virtualize_modifiers (GdkKeymap *keymap,
+ GdkModifierType concrete_mods,
+ EggVirtualModifierType *virtual_mods);
+
+G_END_DECLS
+
+#endif /* __EGG_ACCELERATORS_H__ */
diff --git a/indicator-applet/src/tomboykeybinder.c b/indicator-applet/src/tomboykeybinder.c
new file mode 100644
index 00000000..cfa62dd1
--- /dev/null
+++ b/indicator-applet/src/tomboykeybinder.c
@@ -0,0 +1,341 @@
+/* tomboykeybinder.c
+ * Copyright (C) 2008 Novell
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ */
+#include <string.h>
+
+#include <gdk/gdk.h>
+#include <gdk/gdkwindow.h>
+#include <gdk/gdkx.h>
+#include <X11/Xlib.h>
+
+#include "eggaccelerators.h"
+#include "tomboykeybinder.h"
+
+/* Uncomment the next line to print a debug trace. */
+/* #define DEBUG */
+
+#ifdef DEBUG
+# define TRACE(x) x
+#else
+# define TRACE(x) do {} while (FALSE);
+#endif
+
+typedef struct _Binding {
+ TomboyBindkeyHandler handler;
+ gpointer user_data;
+ char *keystring;
+ uint keycode;
+ uint modifiers;
+} Binding;
+
+static GSList *bindings = NULL;
+static guint32 last_event_time = 0;
+static gboolean processing_event = FALSE;
+
+static guint num_lock_mask, caps_lock_mask, scroll_lock_mask;
+
+static void
+lookup_ignorable_modifiers (GdkKeymap *keymap)
+{
+ egg_keymap_resolve_virtual_modifiers (keymap,
+ EGG_VIRTUAL_LOCK_MASK,
+ &caps_lock_mask);
+
+ egg_keymap_resolve_virtual_modifiers (keymap,
+ EGG_VIRTUAL_NUM_LOCK_MASK,
+ &num_lock_mask);
+
+ egg_keymap_resolve_virtual_modifiers (keymap,
+ EGG_VIRTUAL_SCROLL_LOCK_MASK,
+ &scroll_lock_mask);
+}
+
+static void
+grab_ungrab_with_ignorable_modifiers (GdkWindow *rootwin,
+ Binding *binding,
+ gboolean grab)
+{
+ guint mod_masks [] = {
+ 0, /* modifier only */
+ num_lock_mask,
+ caps_lock_mask,
+ scroll_lock_mask,
+ num_lock_mask | caps_lock_mask,
+ num_lock_mask | scroll_lock_mask,
+ caps_lock_mask | scroll_lock_mask,
+ num_lock_mask | caps_lock_mask | scroll_lock_mask,
+ };
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (mod_masks); i++) {
+ if (grab) {
+ XGrabKey (GDK_WINDOW_XDISPLAY (rootwin),
+ binding->keycode,
+ binding->modifiers | mod_masks [i],
+ GDK_WINDOW_XID (rootwin),
+ False,
+ GrabModeAsync,
+ GrabModeAsync);
+ } else {
+ XUngrabKey (GDK_WINDOW_XDISPLAY (rootwin),
+ binding->keycode,
+ binding->modifiers | mod_masks [i],
+ GDK_WINDOW_XID (rootwin));
+ }
+ }
+}
+
+static gboolean
+do_grab_key (Binding *binding)
+{
+ GdkDisplay *gdk_display = gdk_display_get_default ();
+ GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_display);
+ GdkWindow *rootwin = gdk_get_default_root_window ();
+
+ EggVirtualModifierType virtual_mods = 0;
+ guint keysym = 0;
+
+ if (keymap == NULL || rootwin == NULL)
+ return FALSE;
+
+ if (!egg_accelerator_parse_virtual (binding->keystring,
+ &keysym,
+ &virtual_mods))
+ return FALSE;
+
+ TRACE (g_print ("Got accel %d, %d\n", keysym, virtual_mods));
+
+ binding->keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (rootwin),
+ keysym);
+ if (binding->keycode == 0)
+ return FALSE;
+
+ TRACE (g_print ("Got keycode %d\n", binding->keycode));
+
+ egg_keymap_resolve_virtual_modifiers (keymap,
+ virtual_mods,
+ &binding->modifiers);
+ if (binding->modifiers == 0)
+ return FALSE;
+
+ TRACE (g_print ("Got modmask %d\n", binding->modifiers));
+
+ gdk_x11_display_error_trap_push (gdk_display);
+
+ grab_ungrab_with_ignorable_modifiers (rootwin,
+ binding,
+ TRUE /* grab */);
+
+ gdk_display_flush (gdk_display);
+
+ if (gdk_x11_display_error_trap_pop (gdk_display)) {
+ g_warning ("Binding '%s' failed!\n", binding->keystring);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+do_ungrab_key (Binding *binding)
+{
+ GdkWindow *rootwin = gdk_get_default_root_window ();
+
+ TRACE (g_print ("Removing grab for '%s'\n", binding->keystring));
+
+ grab_ungrab_with_ignorable_modifiers (rootwin,
+ binding,
+ FALSE /* ungrab */);
+
+ return TRUE;
+}
+
+static GdkFilterReturn
+filter_func (GdkXEvent *gdk_xevent, GdkEvent *event G_GNUC_UNUSED,
+ gpointer data G_GNUC_UNUSED)
+{
+ GdkFilterReturn return_val = GDK_FILTER_CONTINUE;
+ XEvent *xevent = (XEvent *) gdk_xevent;
+ guint event_mods;
+ GSList *iter;
+
+ TRACE (g_print ("Got Event! %d, %d\n", xevent->type, event->type));
+
+ switch (xevent->type) {
+ case KeyPress:
+ TRACE (g_print ("Got KeyPress! keycode: %d, modifiers: %d\n",
+ xevent->xkey.keycode,
+ xevent->xkey.state));
+
+ /*
+ * Set the last event time for use when showing
+ * windows to avoid anti-focus-stealing code.
+ */
+ processing_event = TRUE;
+ last_event_time = xevent->xkey.time;
+
+ event_mods = xevent->xkey.state & ~(num_lock_mask |
+ caps_lock_mask |
+ scroll_lock_mask);
+
+ for (iter = bindings; iter != NULL; iter = iter->next) {
+ Binding *binding = (Binding *) iter->data;
+
+ if (binding->keycode == xevent->xkey.keycode &&
+ binding->modifiers == event_mods) {
+
+ TRACE (g_print ("Calling handler for '%s'...\n",
+ binding->keystring));
+
+ (binding->handler) (binding->keystring,
+ binding->user_data);
+ }
+ }
+
+ processing_event = FALSE;
+ break;
+ case KeyRelease:
+ TRACE (g_print ("Got KeyRelease! \n"));
+ break;
+ }
+
+ return return_val;
+}
+
+static void
+keymap_changed (GdkKeymap *map G_GNUC_UNUSED)
+{
+ GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_display_get_default ());
+ GSList *iter;
+
+ TRACE (g_print ("Keymap changed! Regrabbing keys..."));
+
+ for (iter = bindings; iter != NULL; iter = iter->next) {
+ Binding *binding = (Binding *) iter->data;
+ do_ungrab_key (binding);
+ }
+
+ lookup_ignorable_modifiers (keymap);
+
+ for (iter = bindings; iter != NULL; iter = iter->next) {
+ Binding *binding = (Binding *) iter->data;
+ do_grab_key (binding);
+ }
+}
+
+void
+tomboy_keybinder_init (void)
+{
+ GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_display_get_default ());
+ GdkWindow *rootwin = gdk_get_default_root_window ();
+
+ lookup_ignorable_modifiers (keymap);
+
+ gdk_window_add_filter (rootwin,
+ filter_func,
+ NULL);
+
+ g_signal_connect (keymap,
+ "keys_changed",
+ G_CALLBACK (keymap_changed),
+ NULL);
+}
+
+void
+tomboy_keybinder_bind (const char *keystring,
+ TomboyBindkeyHandler handler,
+ gpointer user_data)
+{
+ Binding *binding;
+ gboolean success;
+
+ binding = g_new0 (Binding, 1);
+ binding->keystring = g_strdup (keystring);
+ binding->handler = handler;
+ binding->user_data = user_data;
+
+ /* Sets the binding's keycode and modifiers */
+ success = do_grab_key (binding);
+
+ if (success) {
+ bindings = g_slist_prepend (bindings, binding);
+ } else {
+ g_free (binding->keystring);
+ g_free (binding);
+ }
+}
+
+void
+tomboy_keybinder_unbind (const char *keystring,
+ TomboyBindkeyHandler handler)
+{
+ GSList *iter;
+
+ for (iter = bindings; iter != NULL; iter = iter->next) {
+ Binding *binding = (Binding *) iter->data;
+
+ if (strcmp (keystring, binding->keystring) != 0 ||
+ handler != binding->handler)
+ continue;
+
+ do_ungrab_key (binding);
+
+ bindings = g_slist_remove (bindings, binding);
+
+ g_free (binding->keystring);
+ g_free (binding);
+ break;
+ }
+}
+
+/*
+ * From eggcellrenderkeys.c.
+ */
+gboolean
+tomboy_keybinder_is_modifier (guint keycode)
+{
+ gint i;
+ gint map_size;
+ XModifierKeymap *mod_keymap;
+ gboolean retval = FALSE;
+
+ mod_keymap = XGetModifierMapping (gdk_x11_get_default_xdisplay());
+
+ map_size = 8 * mod_keymap->max_keypermod;
+
+ i = 0;
+ while (i < map_size) {
+ if (keycode == mod_keymap->modifiermap[i]) {
+ retval = TRUE;
+ break;
+ }
+ ++i;
+ }
+
+ XFreeModifiermap (mod_keymap);
+
+ return retval;
+}
+
+guint32
+tomboy_keybinder_get_current_event_time (void)
+{
+ if (processing_event)
+ return last_event_time;
+ else
+ return GDK_CURRENT_TIME;
+}
diff --git a/indicator-applet/src/tomboykeybinder.h b/indicator-applet/src/tomboykeybinder.h
new file mode 100644
index 00000000..cfb27159
--- /dev/null
+++ b/indicator-applet/src/tomboykeybinder.h
@@ -0,0 +1,43 @@
+/* tomboykeybinder.h
+ * Copyright (C) 2008 Novell
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ */
+#ifndef __TOMBOY_KEY_BINDER_H__
+#define __TOMBOY_KEY_BINDER_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef void (* TomboyBindkeyHandler) (char *keystring, gpointer user_data);
+
+void tomboy_keybinder_init (void);
+
+void tomboy_keybinder_bind (const char *keystring,
+ TomboyBindkeyHandler handler,
+ gpointer user_data);
+
+void tomboy_keybinder_unbind (const char *keystring,
+ TomboyBindkeyHandler handler);
+
+gboolean tomboy_keybinder_is_modifier (guint keycode);
+
+guint32 tomboy_keybinder_get_current_event_time (void);
+
+G_END_DECLS
+
+#endif /* __TOMBOY_KEY_BINDER_H__ */
+