diff options
| author | mbkma <[email protected]> | 2026-01-31 12:11:25 +0100 |
|---|---|---|
| committer | mbkma <[email protected]> | 2026-01-31 12:11:25 +0100 |
| commit | bb4fa0ea945f9dd4d0bff0182c381c2f14f78ae3 (patch) | |
| tree | 523c91356c59557d0bcfa0a878f4e5cadd0c4ab2 /indicator-applet | |
| parent | d433bbd9aeb7ae3377e59c70f6608c3e29f9ebbc (diff) | |
| download | mate-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')
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 "<Control>a" or "<Shift><Alt>F1" or + * "<Release>z" (the last one is for key release). The parser + * is fairly liberal and allows lower or upper case, and also + * abbreviations such as "<Ctl>" and "<Ctrl>". + * + * 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__ */ + |
