├── .clang-format ├── .gitignore ├── CHANGELOG.md ├── Makefile.am ├── README.md ├── autogen.sh ├── common.mk ├── configure.ac ├── mkrelease.sh ├── src ├── AppIndicatorApplet.plugin ├── Makefile.am ├── applet-main.c ├── applet.c ├── applet.h ├── plugin.c └── plugin.h └── update_format.sh /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AccessModifierOffset: 0 3 | AlignAfterOpenBracket: true 4 | AlignConsecutiveAssignments: false 5 | #uncomment for clang 3.9 6 | #AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlinesLeft: false 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: None 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLoopsOnASingleLine: false 16 | # AlwaysBreakAfterDefinitionReturnType: None 17 | #uncomment for clang 3.9 18 | #AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: true 20 | AlwaysBreakTemplateDeclarations: false 21 | BinPackArguments: false 22 | BinPackParameters: true 23 | # BraceWrapping: (not set since BreakBeforeBraces is not Custom) 24 | BreakBeforeBinaryOperators: None 25 | # BreakAfterJavaFieldAnnotations: (not java) 26 | BreakBeforeBinaryOperators: None 27 | BreakBeforeBraces: Linux 28 | BreakBeforeTernaryOperators: true 29 | BreakConstructorInitializersBeforeComma: false 30 | #uncomment for clang 3.9 31 | #BreakStringLiterals: false 32 | ColumnLimit: 100 33 | CommentPragmas: '\*\<' 34 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 35 | ConstructorInitializerIndentWidth: 4 36 | ContinuationIndentWidth: 4 37 | Cpp11BracedListStyle: false 38 | DerivePointerAlignment: false 39 | DisableFormat: false 40 | ExperimentalAutoDetectBinPacking: false 41 | ForEachMacros: [ ] 42 | #Uncomment for clang 3.9 43 | #IncludeCategories: 44 | # - Regex: '^"' 45 | # Priority: 1 46 | # IncludeIsMainRegex: (project doesn't use a main includes that can add other includes via regex) 47 | IndentCaseLabels: false 48 | IndentWidth: 8 49 | IndentWrappedFunctionNames: false 50 | # JavaScriptQuotes: (not javascript) 51 | KeepEmptyLinesAtTheStartOfBlocks: false 52 | Language: Cpp 53 | MacroBlockBegin: '' 54 | MacroBlockEnd: '' 55 | MaxEmptyLinesToKeep: 1 56 | NamespaceIndentation: None 57 | # ObjCBlockIndentWidth: (not objc) 58 | # ObjCSpaceAfterProperty: (not objc) 59 | # ObjCSpaceBeforeProtocolList: (not objc) 60 | PenaltyBreakBeforeFirstCallParameter: 400 61 | PenaltyBreakComment: 0 62 | # PenaltyBreakFirstLessLess: (not cpp) 63 | PenaltyBreakString: 500 64 | PenaltyExcessCharacter: 10000 65 | PenaltyReturnTypeOnItsOwnLine: 600 66 | PointerAlignment: Right 67 | #uncomment for clang 3.9 68 | #ReflowComments: true 69 | #uncomment for clang 3.9 70 | #SortIncludes: true 71 | SpaceAfterCStyleCast: false 72 | SpaceBeforeAssignmentOperators: true 73 | SpaceBeforeParens: ControlStatements 74 | SpaceInEmptyParentheses: false 75 | SpacesBeforeTrailingComments: 1 76 | SpacesInAngles: false 77 | SpacesInCStyleCastParentheses: false 78 | # SpacesInContainerLiterals: (not objc or javascript) 79 | SpacesInParentheses: false 80 | SpacesInSquareBrackets: false 81 | Standard: Cpp11 82 | TabWidth: 8 83 | UseTab: Never 84 | ... 85 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | *.su 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased](https://github.com/ubuntubudgie/budgie-indicator-applet/tree/HEAD) 4 | 5 | [Full Changelog](https://github.com/ubuntubudgie/budgie-indicator-applet/compare/0.7.1...HEAD) 6 | 7 | **Closed issues:** 8 | 9 | - Merge Commits from XFCE and Mate indicators [\#29](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/29) 10 | - Bug\#998730: budgie-indicator-applet: 0.7.1-1 [\#28](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/28) 11 | 12 | ## [0.7.1](https://github.com/ubuntubudgie/budgie-indicator-applet/tree/0.7.1) (2021-10-28) 13 | 14 | [Full Changelog](https://github.com/ubuntubudgie/budgie-indicator-applet/compare/v0.7.0...0.7.1) 15 | 16 | **Closed issues:** 17 | 18 | - the old oversized icon issue is back [\#27](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/27) 19 | 20 | ## [v0.7.0](https://github.com/ubuntubudgie/budgie-indicator-applet/tree/v0.7.0) (2021-06-12) 21 | 22 | [Full Changelog](https://github.com/ubuntubudgie/budgie-indicator-applet/compare/v0.6.2...v0.7.0) 23 | 24 | **Closed issues:** 25 | 26 | - The top bar increases height when the Slack icon appears on AppIndicador Applet. [\#25](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/25) 27 | - \(K\)SNI support and google chrome hangouts extension [\#22](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/22) 28 | - does not know how to scale SVG \(or QT ?\) icon? - \(was: Albert icon - too large\) [\#18](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/18) 29 | 30 | ## [v0.6.2](https://github.com/ubuntubudgie/budgie-indicator-applet/tree/v0.6.2) (2019-12-04) 31 | 32 | [Full Changelog](https://github.com/ubuntubudgie/budgie-indicator-applet/compare/v0.6.1...v0.6.2) 33 | 34 | **Fixed bugs:** 35 | 36 | - Open appindicator Using Right-Click, doesn't perform any action! [\#12](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/12) 37 | 38 | **Closed issues:** 39 | 40 | - Unable to kill System Apps [\#21](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/21) 41 | 42 | ## [v0.6.1](https://github.com/ubuntubudgie/budgie-indicator-applet/tree/v0.6.1) (2019-06-04) 43 | 44 | [Full Changelog](https://github.com/ubuntubudgie/budgie-indicator-applet/compare/v0.6...v0.6.1) 45 | 46 | **Implemented enhancements:** 47 | 48 | - Port to libayatana-indicator [\#11](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/11) 49 | 50 | **Closed issues:** 51 | 52 | - App Icons don't scale down if panel is under 37px [\#19](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/19) 53 | - Debian Buster - build against revised ayatana library [\#17](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/17) 54 | - Wireless signal strength icon not visible on default background color [\#16](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/16) 55 | 56 | **Merged pull requests:** 57 | 58 | - Panelsize [\#20](https://github.com/UbuntuBudgie/budgie-indicator-applet/pull/20) ([fossfreedom](https://github.com/fossfreedom)) 59 | 60 | ## [v0.6](https://github.com/ubuntubudgie/budgie-indicator-applet/tree/v0.6) (2018-04-14) 61 | 62 | [Full Changelog](https://github.com/ubuntubudgie/budgie-indicator-applet/compare/v0.5...v0.6) 63 | 64 | **Merged pull requests:** 65 | 66 | - Support Ayatana Indicators and Ubuntu Indicators [\#13](https://github.com/UbuntuBudgie/budgie-indicator-applet/pull/13) ([sunweaver](https://github.com/sunweaver)) 67 | 68 | ## [v0.5](https://github.com/ubuntubudgie/budgie-indicator-applet/tree/v0.5) (2018-02-16) 69 | 70 | [Full Changelog](https://github.com/ubuntubudgie/budgie-indicator-applet/compare/v0.4...v0.5) 71 | 72 | **Closed issues:** 73 | 74 | - Vertically oriented panel [\#10](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/10) 75 | - background color different than panel [\#9](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/9) 76 | 77 | ## [v0.4](https://github.com/ubuntubudgie/budgie-indicator-applet/tree/v0.4) (2017-08-20) 78 | 79 | [Full Changelog](https://github.com/ubuntubudgie/budgie-indicator-applet/compare/v0.3...v0.4) 80 | 81 | **Fixed bugs:** 82 | 83 | - Cannot remove appindicator via raven [\#2](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/2) 84 | - dropbox causes panel and system crash [\#1](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/1) 85 | 86 | **Closed issues:** 87 | 88 | - How to get Budgie show certain indicator icons? [\#8](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/8) 89 | - dropbox does not display [\#6](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/6) 90 | - System apps not showing; Dropbox is just black square icon [\#4](https://github.com/UbuntuBudgie/budgie-indicator-applet/issues/4) 91 | 92 | ## [v0.3](https://github.com/ubuntubudgie/budgie-indicator-applet/tree/v0.3) (2016-12-02) 93 | 94 | [Full Changelog](https://github.com/ubuntubudgie/budgie-indicator-applet/compare/v0.2...v0.3) 95 | 96 | ## [v0.2](https://github.com/ubuntubudgie/budgie-indicator-applet/tree/v0.2) (2016-11-11) 97 | 98 | [Full Changelog](https://github.com/ubuntubudgie/budgie-indicator-applet/compare/v0.1...v0.2) 99 | 100 | ## [v0.1](https://github.com/ubuntubudgie/budgie-indicator-applet/tree/v0.1) (2016-11-06) 101 | 102 | [Full Changelog](https://github.com/ubuntubudgie/budgie-indicator-applet/compare/b56d201da85d9821bd234a3eab728e2734d9af26...v0.1) 103 | 104 | 105 | 106 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 107 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | src 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | budgie-indicator-applet 2 | ----------------------- 3 | 4 | AppIndicator applet for budgie-desktop. 5 | 6 | *Help required to resolve the TODO and ENHANCEMENTS lists* 7 | 8 | 9 | To compile: 10 | 11 | sudo apt install git libtool dpkg-dev intltool libtool libgtk-3-dev libido3-0.1-dev libindicator3-dev libpeas-dev budgie-core-dev 12 | 13 | 14 | git clone https://github.com/ubuntubudgie/budgie-indicator-applet 15 | cd budgie-indicator-applet 16 | ./autogen.sh --prefix=/usr --libdir=/usr/lib 17 | make 18 | sudo make install 19 | 20 | To run: install the recommended packages 21 | 22 | sudo apt install gir1.2-appindicator3-0.1 23 | 24 | budgie-panel --replace & 25 | 26 | Use Raven to add the applet to the panel. 27 | 28 | 29 | TODO 30 | ----- 31 | 32 | - [x] Applet background needs to use panel colour for inbuilt-theme 33 | - [x] Code cleanup - copyright statements, unused code, change boilerplate budgie-applet 34 | - [x] Correct debian/copyright 35 | - [x] check all the build dependencies are actually needed 36 | 37 | Enhancements 38 | ----- 39 | 40 | - [ ] For the applet settings add capability to change indicator order 41 | - [x] Appindicator spacing is too wide when not using built-in-theme 42 | - [x] Applet background needs to respect raven stylise regions option 43 | - [x] Applet background needs to use panel colour for user-defined theme 44 | - [ ] When used with the system-tray applet hide the network applet icon rather than permanently hiding 45 | - [ ] Change from using GtkMenu and GtkMenuItem to GtkButtonBox/GtkButton and therefore allow GtkPopover when button click 46 | 47 | *Tips for Development* 48 | 49 | Use the following to run GTK Inspector - use to investigate CSS and other properties 50 | 51 | GTK_DEBUG=interactive budgie-panel --replace 52 | 53 | Use the following to print out g_debug messages 54 | 55 | G_MESSAGES_DEBUG=Indicator-Applet-DEBUG budgie-panel --replace 56 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | autoreconf --force --install --symlink --warnings=all 6 | 7 | args="\ 8 | --sysconfdir=/etc \ 9 | --localstatedir=/var \ 10 | --prefix=/usr \ 11 | --enable-silent-rules" 12 | 13 | ./configure CFLAGS="-g -O1 $CFLAGS" $args "$@" 14 | make clean 15 | -------------------------------------------------------------------------------- /common.mk: -------------------------------------------------------------------------------- 1 | AM_CFLAGS = \ 2 | -fstack-protector -Wall -pedantic \ 3 | -Wstrict-prototypes -Wundef -fno-common \ 4 | -Werror-implicit-function-declaration \ 5 | -Wformat -Wformat-security -Werror=format-security \ 6 | -Wno-conversion -Wunused-variable -Wunreachable-code \ 7 | -Wall -W -D_FORTIFY_SOURCE=2 -std=c11 -fPIC 8 | 9 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([budgie-indicator-applet], 0.7.2, [fossfreedom@ubuntu.com], [budgie-indicator-applet], [https://github.com/ubuntubudgie/budgie-indicator-applet]) 2 | AC_CONFIG_AUX_DIR([build-aux]) 3 | AM_INIT_AUTOMAKE([-Wno-portability no-dist-gzip dist-xz foreign subdir-objects]) 4 | AC_PROG_CC 5 | AC_PROG_CC_STDC 6 | LT_PREREQ(2.2) 7 | AC_CONFIG_HEADERS([config.h]) 8 | AC_PREFIX_DEFAULT(/usr/local) 9 | AM_SILENT_RULES([yes]) 10 | LT_INIT([disable-static]) 11 | AC_CONFIG_MACRO_DIR([m4]) 12 | 13 | m4_define([budgie_required_version], [1]) 14 | 15 | # Note budgie-1.0 depends on peas, gtk, etc. Sorta your package managers 16 | # business to make sure those deps are satisfied. 17 | # Also note this is just for the C and Vala subprojects! 18 | PKG_CHECK_MODULES(BUDGIE_PLUGIN, 19 | budgie-1.0 >= budgie_required_version 20 | ) 21 | 22 | UBUNTU_INDICATOR_API_VERSION=3 23 | UBUNTU_INDICATOR_REQUIRED_VERSION=0.3.90 24 | UBUNTU_INDICATOR_NG_VERSION=0.5 25 | UBUNTU_INDICATOR_PKG=indicator$INDICATOR_API_VERSION-0.4 26 | 27 | AYATANA_INDICATOR_API_VERSION=3 28 | AYATANA_INDICATOR_REQUIRED_VERSION=0.6.0 29 | AYATANA_INDICATOR_NG_VERSION=0.6.0 30 | AYATANA_INDICATOR_PKG=ayatana-indicator$INDICATOR_API_VERSION-0.4 31 | 32 | AC_ARG_WITH([ayatana-indicators], 33 | [AS_HELP_STRING([--with-ayatana-indicators], 34 | [build against Ayatana Indicators])], 35 | [with_ayatana_indicators='yes'], 36 | [with_ayatana_indicators='no'] 37 | ) 38 | 39 | AC_ARG_WITH([ubuntu-indicators], 40 | [AS_HELP_STRING([--with-ubuntu-indicators], 41 | [build against Ubuntu Indicators])], 42 | [with_ubuntu_indicators='yes'], 43 | [with_ubuntu_indicators='no'] 44 | ) 45 | 46 | ### 47 | ### Look for Ayatana Indicators 48 | ### 49 | 50 | PKG_CHECK_EXISTS(ayatana-indicator3-0.4, 51 | [have_ayatanaindicator="yes"], 52 | [have_ayatanaindicator="no"]) 53 | 54 | PKG_CHECK_EXISTS(ayatana-indicator3-0.4 >= $AYATANA_INDICATOR_NG_VERSION, 55 | [have_ayatanaindicator_ng="yes"], 56 | [have_ayatanaindicator_ng="no"]) 57 | 58 | ### 59 | ### Look for Ubuntu Indicators 60 | ### 61 | 62 | PKG_CHECK_EXISTS(indicator3-0.4, 63 | [have_ubuntuindicator="yes"], 64 | [have_ubuntuindicator="no"]) 65 | 66 | PKG_CHECK_EXISTS(indicator3-0.4 >= $UBUNTU_INDICATOR_NG_VERSION, 67 | [have_ubuntuindicator_ng="yes"], 68 | [have_ubuntuindicator_ng="no"]) 69 | 70 | ### decide on what Indicators implementation to use... 71 | 72 | if test "x$have_ayatanaindicator" = "xyes" && 73 | test "x$have_ubuntuindicator" != "xyes" && 74 | test "x$with_ubuntu_indicators" != "xyes"; then 75 | 76 | # use Ayatana Indicators (because they are present, and noone is enforcing Ubuntu Indicators) 77 | use_ayatanaindicator="yes"; 78 | indicator_enforced="no"; 79 | 80 | elif test "x$have_ubuntuindicator" = "xyes" && 81 | test "x$have_ayatanaindicator" != "xyes" && 82 | test "x$with_ayatana_indicators" != "xyes"; then 83 | 84 | # use Ubuntu Indicators (because they are present, and noone is enforcing Ayatana Indicators) 85 | use_ubuntuindicator="yes"; 86 | indicator_enforced="no"; 87 | 88 | elif test "x$have_ubuntuindicator" = "xyes" && 89 | test "x$have_ayatanaindicator" = "xyes" && 90 | test "x$with_ayatana_indicators" = "xyes"; then 91 | 92 | # both Indicator implementations are present, and we are asked to use Ayatana Indicators 93 | use_ayatanaindicator=yes; 94 | indicator_enforced=yes; 95 | 96 | elif test "x$have_ubuntuindicator" = "xyes" && 97 | test "x$have_ayatanaindicator" = "xyes" && 98 | test "x$with_ubuntu_indicators" = "xyes"; then 99 | 100 | # both Indicator implementations are present, and we are asked to use Ubuntu Indicators 101 | use_ubuntuindicator=yes; 102 | indicator_enforced=yes; 103 | 104 | elif test "x$have_ubuntuindicator" = "xyes" && 105 | test "x$have_ayatanaindicator" != "xyes" && 106 | test "x$with_ayatana_indicators" = "xyes"; then 107 | 108 | AC_MSG_ERROR([Ubuntu Indicators are present, but you want to build budgie-indicator-applet against Ayatana Indicators. This does not match.]) 109 | 110 | elif test "x$have_ubuntuindicator" != "xyes" && 111 | test "x$have_ayatanaindicator" = "xyes" && 112 | test "x$with_ubuntu_indicators" = "xyes"; then 113 | 114 | AC_MSG_ERROR([Ayatana Indicators are present, but you want to build budgie-indicator-applet against Ubuntu Indicators. This does not match.]) 115 | 116 | else 117 | 118 | AC_MSG_ERROR([Either Ayatana Indicators or Ubuntu Indicators are required to build budgie-indicator-applet.]) 119 | 120 | fi 121 | 122 | ### prepare Ayatana or Ubuntu Indicators implementation for the build, regarding to the decision reached above... 123 | 124 | if test "x$use_ayatanaindicator" = "xyes"; then 125 | 126 | AM_CONDITIONAL(WITH_AYATANA_INDICATOR, true) 127 | AM_CONDITIONAL(WITH_UBUNTU_INDICATOR, false) 128 | AC_DEFINE(HAVE_AYATANA_INDICATOR, 1, "Ayatana Indicators Support") 129 | AC_DEFINE(HAVE_UBUNTU_INDICATOR, 0, "DISABLED: Ubuntu Indicators Support") 130 | AC_DEFINE(HAVE_UBUNTU_INDICATOR_NG, 0, "DISABLED: Ubuntu Indicators NG Support") 131 | 132 | if test "x$indicator_enforced" = "xyes"; then 133 | AC_MSG_NOTICE([Using Ayatana Indicators for this build (as requested via configure option).]) 134 | else 135 | AC_MSG_NOTICE([Using Ayatana Indicators for this build.]) 136 | fi 137 | 138 | if test "x$have_ayatanaindicator_ng" = "xyes"; then 139 | PKG_CHECK_MODULES(AYATANA_INDICATOR_NG, ayatana-indicator3-0.4 >= $AYATANA_INDICATOR_NG_VERSION 140 | libayatana-ido3-0.4 >= 0.4.0, 141 | [AC_DEFINE(HAVE_AYATANA_INDICATOR_NG, 1, "New style indicators support")]) 142 | elif test "x$have_ayatanaindicator" = "xyes"; then 143 | PKG_CHECK_MODULES(AYATANA_INDICATOR, ayatana-indicator3-0.4 >= $AYATANA_INDICATOR_REQUIRED_VERSION) 144 | fi 145 | 146 | AC_SUBST(AYATANA_INDICATOR_CFLAGS) 147 | AC_SUBST(AYATANA_INDICATOR_LIBS) 148 | 149 | elif test "x$use_ubuntuindicator" = "xyes"; then 150 | 151 | # both Indicator implementations are present, and we are asked to use Ubuntu Indicators 152 | AM_CONDITIONAL(WITH_UBUNTU_INDICATOR, true) 153 | AM_CONDITIONAL(WITH_AYATANA_INDICATOR, false) 154 | AC_DEFINE(HAVE_UBUNTU_INDICATOR, 1, "Ubuntu Indicators Support") 155 | AC_DEFINE(HAVE_AYATANA_INDICATOR, 0, "No Ayatana Indicators Support") 156 | AC_DEFINE(HAVE_AYATANA_INDICATOR_NG, 0, "No Ayatana Indicators NG Support") 157 | 158 | if test "x$indicator_enforced" = "xyes"; then 159 | AC_MSG_NOTICE([Using Ubuntu Indicators for this build (as requested via configure option).]) 160 | else 161 | AC_MSG_NOTICE([Using Ubuntu Indicators for this build.]) 162 | fi 163 | 164 | if test "x$have_ubuntuindicator_ng" = "xyes"; then 165 | PKG_CHECK_MODULES(UBUNTU_INDICATOR_NG, indicator3-0.4 >= $UBUNTU_INDICATOR_NG_VERSION 166 | libido3-0.1 >= 13.10, 167 | [AC_DEFINE(HAVE_UBUNTU_INDICATOR_NG, 1, "New style indicators support")]) 168 | elif test "x$have_ubuntuindicator" = "xyes"; then 169 | PKG_CHECK_MODULES(UBUNTU_INDICATOR, indicator3-0.4 >= $UBUNTU_INDICATOR_REQUIRED_VERSION) 170 | fi 171 | 172 | AC_SUBST(UBUNTU_INDICATOR_CFLAGS) 173 | AC_SUBST(UBUNTU_INDICATOR_LIBS) 174 | 175 | fi 176 | 177 | if test "x$use_ubuntuindicator" = "xyes"; then 178 | INDICATORDIR=`$PKG_CONFIG --variable=indicatordir indicator3-0.4` 179 | INDICATORICONSDIR=`$PKG_CONFIG --variable=iconsdir indicator3-0.4` 180 | fi 181 | 182 | if test "x$use_ayatanaindicator" = "xyes"; then 183 | INDICATORDIR=`$PKG_CONFIG --variable=indicatordir ayatana-indicator3-0.4` 184 | INDICATORICONSDIR=`$PKG_CONFIG --variable=iconsdir ayatana-indicator3-0.4` 185 | fi 186 | 187 | AC_SUBST(INDICATORDIR) 188 | AC_SUBST(INDICATORICONSDIR) 189 | 190 | AC_CONFIG_FILES([Makefile 191 | src/Makefile]) 192 | 193 | AC_OUTPUT 194 | 195 | AC_MSG_RESULT([ 196 | budgie-indicator-applet $VERSION 197 | ======== 198 | 199 | prefix: ${prefix} 200 | libdir: ${libdir} 201 | sysconfdir: ${sysconfdir} 202 | exec_prefix: ${exec_prefix} 203 | bindir: ${bindir} 204 | datarootdir: ${datarootdir} 205 | 206 | compiler: ${CC} 207 | cflags: ${CFLAGS} 208 | ldflags: ${LDFLAGS} 209 | ]) 210 | -------------------------------------------------------------------------------- /mkrelease.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | git submodule init 5 | git submodule update 6 | 7 | VERSION="0.7.2" 8 | NAME="budgie-indicator-applet" 9 | git-archive-all.sh --prefix ${NAME}-${VERSION}/ --verbose ${NAME}-${VERSION}.tar 10 | xz -9 "${NAME}-${VERSION}.tar" 11 | 12 | gpg --armor --detach-sign "${NAME}-${VERSION}.tar.xz" 13 | gpg --verify "${NAME}-${VERSION}.tar.xz.asc" 14 | 15 | -------------------------------------------------------------------------------- /src/AppIndicatorApplet.plugin: -------------------------------------------------------------------------------- 1 | [Plugin] 2 | Module=appindicatorapplet.so 3 | Name=AppIndicator Applet 4 | Description=AppIndicator Applet 5 | Authors=David Mohammed 6 | Copyright=Copyright © 2016-2017 David Mohammed 7 | Website=https://ubuntubudgie.org 8 | Icon=user-home-symbolic 9 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | if WITH_AYATANA_INDICATOR 2 | INDICATOR_CFLAGS = $(AYATANA_INDICATOR_CFLAGS) \ 3 | $(AYATANA_INDICATOR_NG_CFLAGS) 4 | INDICATOR_LIBS = $(AYATANA_INDICATOR_LIBS) \ 5 | $(AYATANA_INDICATOR_NG_LIBS) 6 | endif 7 | 8 | if WITH_UBUNTU_INDICATOR 9 | INDICATOR_CFLAGS = $(UBUNTU_INDICATOR_CFLAGS) \ 10 | $(UBUNTU_INDICATOR_NG_CFLAGS) 11 | INDICATOR_LIBS = $(UBUNTU_INDICATOR_LIBS) \ 12 | $(UBUNTU_INDICATOR_NG_LIBS) 13 | endif 14 | 15 | include $(top_srcdir)/common.mk 16 | 17 | plugindir = $(libdir)/budgie-desktop/plugins 18 | 19 | appindicatorappletdir = $(plugindir)/appindicator-applet 20 | 21 | appindicatorapplet_LTLIBRARIES = \ 22 | libappindicatorapplet.la 23 | 24 | libappindicatorapplet_la_SOURCES = \ 25 | applet-main.c \ 26 | applet.c \ 27 | applet.h \ 28 | plugin.c \ 29 | plugin.h 30 | 31 | libappindicatorapplet_la_CFLAGS = \ 32 | $(BUDGIE_PLUGIN_CFLAGS) \ 33 | $(AM_CFLAGS) \ 34 | -DG_LOG_DOMAIN=\""Indicator-Applet"\" \ 35 | -DDATADIR=\""$(datadir)"\" \ 36 | -DINDICATOR_DIR=\""$(INDICATORDIR)"\" \ 37 | -DINDICATOR_ICONS_DIR=\""$(INDICATORICONSDIR)"\" \ 38 | -DINDICATOR_APPLET \ 39 | -I$(srcdir)/.. \ 40 | $(APPLET_CFLAGS) \ 41 | $(INDICATOR_CFLAGS) 42 | 43 | libappindicatorapplet_la_LIBADD = \ 44 | $(BUDGIE_PLUGIN_LIBS) \ 45 | $(APPLET_LIBS) \ 46 | $(INDICATOR_LIBS) \ 47 | -lX11 48 | 49 | libappindicatorapplet_la_LDFLAGS = \ 50 | -module \ 51 | -avoid-version \ 52 | -shared \ 53 | $(AM_LDFLAGS) 54 | 55 | appindicatorapplet_DATA = \ 56 | AppIndicatorApplet.plugin 57 | 58 | EXTRA_DIST = \ 59 | AppIndicatorApplet.plugin 60 | -------------------------------------------------------------------------------- /src/applet-main.c: -------------------------------------------------------------------------------- 1 | /* 2 | A small wrapper utility to load indicators and put them as menu items 3 | into the mate-panel using it's applet interface. 4 | 5 | Copyright 2009-2010 Canonical Ltd., 2016-2018 David Mohammed 6 | 7 | Authors: 8 | Ted Gould 9 | David Mohammed 10 | 11 | This program is free software: you can redistribute it and/or modify it 12 | under the terms of the GNU General Public License version 3, as published 13 | by the Free Software Foundation. 14 | 15 | This program is distributed in the hope that it will be useful, but 16 | WITHOUT ANY WARRANTY; without even the implied warranties of 17 | MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 18 | PURPOSE. See the GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License along 21 | with this program. If not, see . 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #if HAVE_UBUNTU_INDICATOR && HAVE_UBUNTU_INDICATOR_NG 32 | #include 33 | 34 | #define INDICATOR_SERVICE_DIR "/usr/share/unity/indicators" 35 | #endif 36 | 37 | #if HAVE_AYATANA_INDICATOR && HAVE_AYATANA_INDICATOR_NG 38 | #include 39 | 40 | #define INDICATOR_SERVICE_DIR "/usr/share/ayatana/indicators" 41 | #endif 42 | 43 | #if HAVE_UBUNTU_INDICATOR 44 | 45 | #define INDICATOR_SERVICE_APPMENU "libappmenu.so" 46 | #define INDICATOR_SERVICE_ME "libme.so" 47 | #define INDICATOR_SERVICE_DATETIME "libdatetime.so" 48 | 49 | #define INDICATOR_SERVICE_APPMENU_NG "com.canonical.indicator.appmenu" 50 | #define INDICATOR_SERVICE_ME_NG "com.canonical.indicator.me" 51 | #define INDICATOR_SERVICE_DATETIME_NG "com.canonical.indicator.datetime" 52 | 53 | #include 54 | 55 | static gchar *indicator_order[] = { "libapplication.so", "libmessaging.so", "libsoundmenu.so", 56 | "libdatetime.so", "libsession.so", NULL }; 57 | #endif 58 | 59 | #if HAVE_AYATANA_INDICATOR 60 | 61 | #define INDICATOR_SERVICE_APPMENU "libayatana-appmenu.so" 62 | #define INDICATOR_SERVICE_ME "libayatana-me.so" 63 | #define INDICATOR_SERVICE_DATETIME "libayatana-datetime.so" 64 | 65 | #define INDICATOR_SERVICE_APPMENU_NG "org.ayatana.indicator.appmenu" 66 | #define INDICATOR_SERVICE_ME_NG "org.ayatana.indicator.me" 67 | #define INDICATOR_SERVICE_DATETIME_NG "org.ayatana.indicator.datetime" 68 | 69 | #include 70 | 71 | static gchar *indicator_order[] = { "libayatana-application.so", "libayatana-messaging.so", "libayatana-soundmenu.so", 72 | "libayatana-datetime.so", "libayatana-session.so", NULL }; 73 | #endif 74 | 75 | static gchar *blacklist_applets[] = { "nm-applet", 0 }; 76 | 77 | void calc_default_icon_size(void); 78 | 79 | BudgiePanelPosition orient = BUDGIE_PANEL_POSITION_NONE; 80 | static int current_icon_size; 81 | static int panel_size; 82 | static int icon_size; 83 | static int small_icon_size; 84 | 85 | GtkPackDirection packdirection = (GtkPackDirection)GTK_ORIENTATION_HORIZONTAL; 86 | 87 | #define MENU_DATA_INDICATOR_OBJECT "indicator-object" 88 | #define MENU_DATA_INDICATOR_ENTRY "indicator-entry" 89 | 90 | #define IO_DATA_ORDER_NUMBER "indicator-order-number" 91 | 92 | void calc_default_icon_size() { 93 | int small_panel_size = 37; 94 | current_icon_size = 22; //appindicator spec size 95 | 96 | if (panel_size < small_panel_size ) { 97 | current_icon_size = current_icon_size + (panel_size - current_icon_size - 15); 98 | 99 | if (current_icon_size < 16) current_icon_size = 16; 100 | } 101 | } 102 | 103 | static void resize_image(GtkImage *image, __attribute__((unused)) gpointer user_data) 104 | { 105 | GdkPixbuf *pixbuf; 106 | GdkPixbuf *scaled_pixbuf; 107 | int pixbuf_height; 108 | int pixbuf_width; 109 | int pixbuf_size; 110 | g_debug("zzz resize_image"); 111 | 112 | pixbuf = gtk_image_get_pixbuf (image); 113 | 114 | if (pixbuf == NULL) return; 115 | 116 | pixbuf_height = gdk_pixbuf_get_height(pixbuf); 117 | if ( pixbuf_height !=0 && pixbuf_height != (current_icon_size-2)) { 118 | pixbuf_width = gdk_pixbuf_get_width(pixbuf); 119 | pixbuf_size = (int)((double)(current_icon_size-2) / pixbuf_height * pixbuf_width ); 120 | if (pixbuf_width <= 0 || (current_icon_size-2) <= 0 || pixbuf_size <= 0) return; 121 | 122 | scaled_pixbuf = gdk_pixbuf_scale_simple( pixbuf, 123 | pixbuf_size, 124 | (current_icon_size-2), GDK_INTERP_HYPER 125 | ); 126 | 127 | gtk_image_set_from_pixbuf (image, scaled_pixbuf); 128 | } 129 | 130 | } 131 | 132 | static gboolean 133 | entry_resized (__attribute__((unused)) AppIndicatorApplet *applet, __attribute__((unused)) int panel_size, __attribute__((unused)) int icon_size, __attribute__((unused)) int small_icon_size, gpointer data) 134 | { 135 | IndicatorObject *io = (IndicatorObject *)data; 136 | 137 | calc_default_icon_size(); 138 | 139 | /* Work on the entries */ 140 | if (io == NULL) return FALSE; 141 | GList * entries = indicator_object_get_entries(io); 142 | if (entries == NULL) return FALSE; 143 | GList * entry = NULL; 144 | 145 | for (entry = entries; entry != NULL; entry = g_list_next(entry)) { 146 | IndicatorObjectEntry * entrydata = (IndicatorObjectEntry *)entry->data; 147 | if (entrydata->image != NULL) { 148 | /* Resize to fit panel */ 149 | 150 | if (gtk_image_get_storage_type(entrydata->image) == GTK_IMAGE_PIXBUF) { 151 | resize_image(entrydata->image, NULL); 152 | } 153 | gtk_image_set_pixel_size (entrydata->image, current_icon_size); 154 | } 155 | } 156 | 157 | return FALSE; 158 | } 159 | 160 | static void update_accessible_desc(IndicatorObjectEntry *entry, GtkWidget *menuitem); 161 | 162 | /************* 163 | * main 164 | * ***********/ 165 | 166 | /************* 167 | * log files 168 | * ***********/ 169 | #define LOG_FILE_NAME "indicator-applet.log" 170 | GOutputStream *log_file = NULL; 171 | 172 | /******************** 173 | * Environment Names 174 | * *******************/ 175 | #define INDICATOR_SPECIFIC_ENV "indicator-applet-original" 176 | 177 | static const gchar *indicator_env[] = { "indicator-applet", INDICATOR_SPECIFIC_ENV, NULL }; 178 | 179 | /************* 180 | * init function 181 | * ***********/ 182 | 183 | static gint name2order(const gchar *name) 184 | { 185 | int i; 186 | 187 | for (i = 0; indicator_order[i] != NULL; i++) { 188 | if (g_strcmp0(name, indicator_order[i]) == 0) { 189 | return i; 190 | } 191 | } 192 | 193 | return -1; 194 | } 195 | 196 | typedef struct _incoming_position_t incoming_position_t; 197 | struct _incoming_position_t { 198 | gint objposition; 199 | gint entryposition; 200 | gint menupos; 201 | gboolean found; 202 | }; 203 | 204 | /* This function helps by determining where in the menu list 205 | this new entry should be placed. It compares the objects 206 | that they're on, and then the individual entries. Each 207 | is progressively more expensive. */ 208 | static void place_in_menu(GtkWidget *widget, gpointer user_data) 209 | { 210 | incoming_position_t *position = (incoming_position_t *)user_data; 211 | if (position->found) { 212 | /* We've already been placed, just finish the foreach */ 213 | return; 214 | } 215 | 216 | IndicatorObject *io = 217 | INDICATOR_OBJECT(g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_OBJECT)); 218 | g_assert(io != NULL); 219 | 220 | gint objposition = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER)); 221 | /* We've already passed it, well, then this is where 222 | we should be be. Stop! */ 223 | if (objposition > position->objposition) { 224 | position->found = TRUE; 225 | return; 226 | } 227 | 228 | /* The objects don't match yet, keep looking */ 229 | if (objposition < position->objposition) { 230 | position->menupos++; 231 | return; 232 | } 233 | 234 | /* The objects are the same, let's start looking at entries. */ 235 | IndicatorObjectEntry *entry = 236 | (IndicatorObjectEntry *)g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY); 237 | gint entryposition = indicator_object_get_location(io, entry); 238 | 239 | if (entryposition > position->entryposition) { 240 | position->found = TRUE; 241 | return; 242 | } 243 | 244 | if (entryposition < position->entryposition) { 245 | position->menupos++; 246 | return; 247 | } 248 | 249 | /* We've got the same object and the same entry. Well, 250 | let's just put it right here then. */ 251 | position->found = TRUE; 252 | return; 253 | } 254 | 255 | static void something_shown(__attribute__((unused)) GtkWidget *widget, gpointer user_data) 256 | { 257 | g_debug("zzz something shown"); 258 | GtkWidget *menuitem = GTK_WIDGET(user_data); 259 | gtk_widget_show(menuitem); 260 | } 261 | 262 | static void something_hidden(__attribute__((unused)) GtkWidget *widget, gpointer user_data) 263 | { 264 | g_debug("zzz something hidden"); 265 | GtkWidget *menuitem = GTK_WIDGET(user_data); 266 | gtk_widget_hide(menuitem); 267 | } 268 | 269 | static void sensitive_cb(GObject *obj, __attribute__((unused)) GParamSpec *pspec, 270 | gpointer user_data) 271 | { 272 | g_debug("zzz something made sensitive"); 273 | g_return_if_fail(GTK_IS_WIDGET(obj)); 274 | g_return_if_fail(GTK_IS_WIDGET(user_data)); 275 | 276 | gtk_widget_set_sensitive(GTK_WIDGET(user_data), gtk_widget_get_sensitive(GTK_WIDGET(obj))); 277 | return; 278 | } 279 | 280 | 281 | static void entry_activated(GtkWidget *widget, gpointer user_data) 282 | { 283 | g_return_if_fail(GTK_IS_WIDGET(widget)); 284 | gpointer pio = g_object_get_data(G_OBJECT(widget), "indicator"); 285 | g_return_if_fail(INDICATOR_IS_OBJECT(pio)); 286 | IndicatorObject *io = INDICATOR_OBJECT(pio); 287 | 288 | indicator_object_entry_activate(io, 289 | (IndicatorObjectEntry *)user_data, 290 | gtk_get_current_event_time()); 291 | } 292 | 293 | static gboolean entry_scrolled(GtkWidget *menuitem, GdkEventScroll *event, 294 | __attribute__((unused)) gpointer data) 295 | { 296 | IndicatorObject *io = g_object_get_data(G_OBJECT(menuitem), MENU_DATA_INDICATOR_OBJECT); 297 | IndicatorObjectEntry *entry = 298 | g_object_get_data(G_OBJECT(menuitem), MENU_DATA_INDICATOR_ENTRY); 299 | 300 | g_return_val_if_fail(INDICATOR_IS_OBJECT(io), FALSE); 301 | 302 | g_signal_emit_by_name(io, "scroll", 1, event->direction); 303 | g_signal_emit_by_name(io, "scroll-entry", entry, 1, event->direction); 304 | g_signal_emit_by_name(io, 305 | INDICATOR_OBJECT_SIGNAL_ENTRY_SCROLLED, 306 | entry, 307 | 1, 308 | event->direction); 309 | 310 | return FALSE; 311 | } 312 | 313 | static gboolean 314 | entry_pressed (GtkWidget *menuitem, GdkEvent *event, __attribute__((unused)) gpointer data) 315 | { 316 | g_return_val_if_fail(GTK_IS_MENU_ITEM(menuitem), FALSE); 317 | 318 | if (((GdkEventButton*)event)->button == 2) /* middle button */ 319 | { 320 | gtk_widget_grab_focus(menuitem); 321 | 322 | return TRUE; 323 | } 324 | 325 | return FALSE; 326 | } 327 | 328 | static gboolean 329 | entry_released (GtkWidget *menuitem, GdkEvent *event, __attribute__((unused)) gpointer data) 330 | { 331 | g_return_val_if_fail(GTK_IS_MENU_ITEM(menuitem), FALSE); 332 | 333 | if (((GdkEventButton*)event)->button == 2) /* middle button */ 334 | { 335 | IndicatorObject *io = g_object_get_data (G_OBJECT (menuitem), MENU_DATA_INDICATOR_OBJECT); 336 | IndicatorObjectEntry *entry = g_object_get_data (G_OBJECT (menuitem), MENU_DATA_INDICATOR_ENTRY); 337 | 338 | g_return_val_if_fail(INDICATOR_IS_OBJECT(io), FALSE); 339 | 340 | g_signal_emit_by_name (io, INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE, entry, 341 | ((GdkEventButton*)event)->time); 342 | 343 | return TRUE; 344 | } 345 | 346 | return FALSE; 347 | } 348 | 349 | static void accessible_desc_update_cb(GtkWidget *widget, gpointer userdata) 350 | { 351 | gpointer data = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY); 352 | 353 | if (data != userdata) { 354 | return; 355 | } 356 | 357 | IndicatorObjectEntry *entry = (IndicatorObjectEntry *)data; 358 | update_accessible_desc(entry, widget); 359 | } 360 | 361 | static void accessible_desc_update(__attribute__((unused)) IndicatorObject *io, 362 | IndicatorObjectEntry *entry, GtkWidget *menubar) 363 | { 364 | gtk_container_foreach(GTK_CONTAINER(menubar), accessible_desc_update_cb, entry); 365 | return; 366 | } 367 | 368 | GtkCssProvider *css_provider = NULL; 369 | 370 | static void entry_added(IndicatorObject *io, IndicatorObjectEntry *entry, GtkWidget *menubar) 371 | { 372 | g_debug("zzz Signal: Entry Added"); 373 | gboolean something_visible = FALSE; 374 | gboolean something_sensitive = FALSE; 375 | GtkStyleContext *context; 376 | GtkCssProvider *css_provider = NULL; 377 | 378 | /* 379 | * we don't want to have the nm-applet being displayed 380 | * budgie-desktop provides this 381 | */ 382 | if (entry->name_hint != NULL) { 383 | int loop = 0; 384 | while (blacklist_applets[loop]) { 385 | if (strstr(entry->name_hint, blacklist_applets[loop]) != NULL) { 386 | g_debug("exit here %s", entry->name_hint); 387 | return; 388 | } 389 | loop++; 390 | } 391 | g_debug("zzz %s", entry->name_hint); 392 | } else { 393 | g_debug("zzz no name_hint"); 394 | } 395 | 396 | GtkWidget *menuitem = gtk_menu_item_new(); 397 | GtkWidget *box = 398 | (orient == BUDGIE_PANEL_POSITION_TOP || orient == BUDGIE_PANEL_POSITION_BOTTOM) 399 | ? gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3) 400 | : gtk_box_new(GTK_ORIENTATION_VERTICAL, 3); 401 | 402 | /* Allows indicators to receive mouse scroll event in GTK+3 */ 403 | gtk_widget_add_events(GTK_WIDGET(menuitem), GDK_SCROLL_MASK); 404 | gtk_widget_add_events(GTK_WIDGET(menuitem), GDK_BUTTON_PRESS_MASK); 405 | gtk_widget_add_events(GTK_WIDGET(menuitem), GDK_BUTTON_RELEASE_MASK); 406 | 407 | g_object_set_data(G_OBJECT(menuitem), "indicator", io); 408 | g_object_set_data(G_OBJECT(menuitem), "box", box); 409 | 410 | g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(entry_activated), entry); 411 | g_signal_connect(G_OBJECT(menuitem), "scroll-event", G_CALLBACK(entry_scrolled), entry); 412 | g_signal_connect(G_OBJECT(menuitem), "button-press-event", G_CALLBACK(entry_pressed), entry); 413 | g_signal_connect(G_OBJECT(menuitem), "button-release-event", G_CALLBACK(entry_released), entry); 414 | 415 | if (entry->image != NULL) { 416 | g_debug("zzz have an image"); 417 | if (gtk_image_get_storage_type(entry->image) == GTK_IMAGE_PIXBUF) { 418 | g_debug("zzz have a pixbuf based image"); 419 | g_signal_connect(G_OBJECT(entry->image), 420 | "notify::pixbuf", 421 | G_CALLBACK(resize_image), 422 | NULL); 423 | resize_image(entry->image, NULL); 424 | } 425 | gtk_image_set_pixel_size(entry->image, current_icon_size); 426 | gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(entry->image), FALSE, FALSE, 1); 427 | if (gtk_widget_get_visible(GTK_WIDGET(entry->image))) { 428 | g_debug("zzz and is visible"); 429 | something_visible = TRUE; 430 | } 431 | 432 | if (gtk_widget_get_sensitive(GTK_WIDGET(entry->image))) { 433 | something_sensitive = TRUE; 434 | } 435 | 436 | g_signal_connect(G_OBJECT(entry->image), 437 | "show", 438 | G_CALLBACK(something_shown), 439 | menuitem); 440 | g_signal_connect(G_OBJECT(entry->image), 441 | "hide", 442 | G_CALLBACK(something_hidden), 443 | menuitem); 444 | 445 | g_signal_connect(G_OBJECT(entry->image), 446 | "notify::sensitive", 447 | G_CALLBACK(sensitive_cb), 448 | menuitem); 449 | } 450 | if (entry->label != NULL) { 451 | g_debug("zzz have a label"); 452 | switch (orient) { 453 | case BUDGIE_PANEL_POSITION_LEFT: 454 | gtk_label_set_angle(GTK_LABEL(entry->label), 270.0); 455 | break; 456 | case BUDGIE_PANEL_POSITION_RIGHT: 457 | gtk_label_set_angle(GTK_LABEL(entry->label), 90.0); 458 | break; 459 | default: 460 | // g_assert(1==2); 461 | gtk_label_set_angle(GTK_LABEL(entry->label), 0.0); 462 | } 463 | gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(entry->label), FALSE, FALSE, 1); 464 | 465 | if (gtk_widget_get_visible(GTK_WIDGET(entry->label))) { 466 | g_debug("zzz and is visible"); 467 | something_visible = TRUE; 468 | } 469 | 470 | if (gtk_widget_get_sensitive(GTK_WIDGET(entry->label))) { 471 | something_sensitive = TRUE; 472 | } 473 | 474 | g_signal_connect(G_OBJECT(entry->label), 475 | "show", 476 | G_CALLBACK(something_shown), 477 | menuitem); 478 | g_signal_connect(G_OBJECT(entry->label), 479 | "hide", 480 | G_CALLBACK(something_hidden), 481 | menuitem); 482 | 483 | g_signal_connect(G_OBJECT(entry->label), 484 | "notify::sensitive", 485 | G_CALLBACK(sensitive_cb), 486 | menuitem); 487 | } 488 | 489 | /* for the appindicator (menuitem) we need to style it with budgie-menubar 490 | * otherwise 491 | * all submenus are transparent for the system theme 492 | */ 493 | context = gtk_widget_get_style_context(GTK_WIDGET(menuitem)); 494 | gtk_style_context_add_class(context, "budgie-menubar"); 495 | context = gtk_widget_get_style_context(GTK_WIDGET(menubar)); 496 | gtk_style_context_remove_class(context, "menubar"); 497 | 498 | css_provider = gtk_css_provider_new(); 499 | gtk_css_provider_load_from_data(css_provider, 500 | ".budgie-menubar { \n" 501 | " padding-left: 2px; \n" 502 | " padding-right: 2px; \n" 503 | "} \n", 504 | -1, 505 | NULL); 506 | gtk_style_context_add_provider(GTK_STYLE_CONTEXT( 507 | gtk_widget_get_style_context(GTK_WIDGET(menuitem))), 508 | GTK_STYLE_PROVIDER(css_provider), 509 | GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); 510 | 511 | g_debug("zzz adding budgie-menubar"); 512 | 513 | gtk_container_add(GTK_CONTAINER(menuitem), box); 514 | gtk_widget_show(box); 515 | 516 | if (entry->menu != NULL) { 517 | gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(entry->menu)); 518 | } 519 | 520 | incoming_position_t position; 521 | position.objposition = 522 | GPOINTER_TO_INT(g_object_get_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER)); 523 | position.entryposition = indicator_object_get_location(io, entry); 524 | position.menupos = 0; 525 | position.found = FALSE; 526 | 527 | gtk_container_foreach(GTK_CONTAINER(menubar), place_in_menu, &position); 528 | 529 | gtk_menu_shell_insert(GTK_MENU_SHELL(menubar), menuitem, position.menupos); 530 | 531 | g_debug("zzz just about there"); 532 | if (something_visible) { 533 | if (entry->accessible_desc != NULL) { 534 | update_accessible_desc(entry, menuitem); 535 | } 536 | g_debug("zzz final show"); 537 | gtk_widget_show(menuitem); 538 | } 539 | gtk_widget_set_sensitive(menuitem, something_sensitive); 540 | 541 | g_object_set_data(G_OBJECT(menuitem), MENU_DATA_INDICATOR_ENTRY, entry); 542 | g_object_set_data(G_OBJECT(menuitem), MENU_DATA_INDICATOR_OBJECT, io); 543 | 544 | return; 545 | } 546 | 547 | static void entry_removed_cb(GtkWidget *widget, gpointer userdata) 548 | { 549 | gpointer data = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY); 550 | 551 | if (data != userdata) { 552 | return; 553 | } 554 | 555 | IndicatorObjectEntry *entry = (IndicatorObjectEntry *)data; 556 | if (entry->label != NULL) { 557 | g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), 558 | G_CALLBACK(something_shown), 559 | widget); 560 | g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), 561 | G_CALLBACK(something_hidden), 562 | widget); 563 | g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), 564 | G_CALLBACK(sensitive_cb), 565 | widget); 566 | } 567 | if (entry->image != NULL) { 568 | g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), 569 | G_CALLBACK(something_shown), 570 | widget); 571 | g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), 572 | G_CALLBACK(something_hidden), 573 | widget); 574 | g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), 575 | G_CALLBACK(sensitive_cb), 576 | widget); 577 | g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), 578 | G_CALLBACK(resize_image), 579 | widget); 580 | } 581 | 582 | gtk_widget_destroy(widget); 583 | return; 584 | } 585 | 586 | static void entry_removed(IndicatorObject *io G_GNUC_UNUSED, IndicatorObjectEntry *entry, 587 | gpointer user_data) 588 | { 589 | g_debug("Signal: Entry Removed"); 590 | 591 | gtk_container_foreach(GTK_CONTAINER(user_data), entry_removed_cb, entry); 592 | 593 | return; 594 | } 595 | 596 | static void entry_moved_find_cb(GtkWidget *widget, gpointer userdata) 597 | { 598 | gpointer *array = (gpointer *)userdata; 599 | if (array[1] != NULL) { 600 | return; 601 | } 602 | 603 | gpointer data = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY); 604 | 605 | if (data != array[0]) { 606 | return; 607 | } 608 | 609 | array[1] = widget; 610 | return; 611 | } 612 | 613 | /* Gets called when an entry for an object was moved. */ 614 | static void entry_moved(IndicatorObject *io, IndicatorObjectEntry *entry, gint old G_GNUC_UNUSED, 615 | gint new G_GNUC_UNUSED, gpointer user_data) 616 | { 617 | GtkWidget *menubar = GTK_WIDGET(user_data); 618 | 619 | gpointer array[2]; 620 | array[0] = entry; 621 | array[1] = NULL; 622 | 623 | gtk_container_foreach(GTK_CONTAINER(menubar), entry_moved_find_cb, array); 624 | if (array[1] == NULL) { 625 | g_warning("Moving an entry that isn't in our menus."); 626 | return; 627 | } 628 | 629 | GtkWidget *mi = GTK_WIDGET(array[1]); 630 | g_object_ref(G_OBJECT(mi)); 631 | gtk_container_remove(GTK_CONTAINER(menubar), mi); 632 | 633 | incoming_position_t position; 634 | position.objposition = 635 | GPOINTER_TO_INT(g_object_get_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER)); 636 | position.entryposition = indicator_object_get_location(io, entry); 637 | position.menupos = 0; 638 | position.found = FALSE; 639 | 640 | gtk_container_foreach(GTK_CONTAINER(menubar), place_in_menu, &position); 641 | 642 | gtk_menu_shell_insert(GTK_MENU_SHELL(menubar), mi, position.menupos); 643 | g_object_unref(G_OBJECT(mi)); 644 | 645 | return; 646 | } 647 | 648 | static void menu_show(IndicatorObject *io, IndicatorObjectEntry *entry, 649 | __attribute__((unused)) guint32 timestamp, gpointer user_data) 650 | { 651 | GtkWidget *menubar = GTK_WIDGET(user_data); 652 | 653 | if (entry == NULL) { 654 | /* Close any open menus instead of opening one */ 655 | GList *entries = indicator_object_get_entries(io); 656 | GList * iterator = NULL; 657 | for (iterator = entries; iterator != NULL; iterator = g_list_next(iterator)) { 658 | IndicatorObjectEntry * entrydata = (IndicatorObjectEntry *)iterator->data; 659 | gtk_menu_popdown(entrydata->menu); 660 | } 661 | g_list_free(entries); 662 | 663 | /* And tell the menubar to exit activation mode too */ 664 | gtk_menu_shell_cancel(GTK_MENU_SHELL(menubar)); 665 | return; 666 | } 667 | } 668 | 669 | static void update_accessible_desc(IndicatorObjectEntry *entry, GtkWidget *menuitem) 670 | { 671 | /* We need to deal with the use case where the contents of the 672 | label overrides what is found in the atk object's name, or at least 673 | orca speaks the label instead of the atk object name. 674 | */ 675 | AtkObject *menuitem_obj = gtk_widget_get_accessible(menuitem); 676 | if (menuitem_obj == NULL) { 677 | /* Should there be an error printed here? */ 678 | return; 679 | } 680 | 681 | if (entry->accessible_desc != NULL) { 682 | atk_object_set_name(menuitem_obj, entry->accessible_desc); 683 | } else { 684 | atk_object_set_name(menuitem_obj, ""); 685 | } 686 | return; 687 | } 688 | 689 | static void load_indicator(AppIndicatorApplet *applet, 690 | GtkWidget *menubar, 691 | IndicatorObject *io, 692 | const gchar *name) 693 | { 694 | /* Set the environment it's in */ 695 | indicator_object_set_environment(io, (const GStrv)indicator_env); 696 | g_debug("zzz load_indicator %s", name); 697 | /* Attach the 'name' to the object */ 698 | 699 | #if HAVE_AYATANA_INDICATOR_NG || HAVE_UBUNTU_INDICATOR_NG 700 | int pos = 5000 - indicator_object_get_position(io); 701 | if (pos > 5000) { 702 | pos = name2order(name); 703 | } 704 | #else 705 | int pos = name2order(name); 706 | #endif 707 | g_object_set_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER, GINT_TO_POINTER(pos)); 708 | 709 | /* Connect to its signals */ 710 | g_signal_connect(G_OBJECT(io), 711 | INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, 712 | G_CALLBACK(entry_added), 713 | menubar); 714 | g_signal_connect(G_OBJECT(io), 715 | INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED, 716 | G_CALLBACK(entry_removed), 717 | menubar); 718 | g_signal_connect(G_OBJECT(io), 719 | INDICATOR_OBJECT_SIGNAL_ENTRY_MOVED, 720 | G_CALLBACK(entry_moved), 721 | menubar); 722 | g_signal_connect(G_OBJECT(io), 723 | INDICATOR_OBJECT_SIGNAL_MENU_SHOW, 724 | G_CALLBACK(menu_show), 725 | menubar); 726 | g_signal_connect(G_OBJECT(io), 727 | INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, 728 | G_CALLBACK(accessible_desc_update), 729 | menubar); 730 | 731 | g_signal_connect(G_OBJECT(applet), 732 | "panel-size-changed", 733 | G_CALLBACK(entry_resized), 734 | G_OBJECT(io)); 735 | 736 | /* Work on the entries */ 737 | GList *entries = indicator_object_get_entries(io); 738 | GList *entry = NULL; 739 | 740 | for (entry = entries; entry != NULL; entry = g_list_next(entry)) { 741 | IndicatorObjectEntry *entrydata = (IndicatorObjectEntry *)entry->data; 742 | 743 | entry_added(io, entrydata, menubar); 744 | } 745 | 746 | g_list_free(entries); 747 | } 748 | 749 | #if HAVE_AYATANA_INDICATOR_NG || HAVE_UBUNTU_INDICATOR_NG 750 | void 751 | load_indicators_from_indicator_files (AppIndicatorApplet *applet, GtkWidget *menubar, gint *indicators_loaded) 752 | { 753 | GDir *dir; 754 | const gchar *name; 755 | GError *error = NULL; 756 | 757 | if (!g_file_test(INDICATOR_SERVICE_DIR, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) { 758 | return; // directory not available 759 | } 760 | 761 | dir = g_dir_open (INDICATOR_SERVICE_DIR, 0, &error); 762 | 763 | if (!dir) { 764 | g_warning ("unable to open indicator service file directory: %s", error->message); 765 | g_error_free (error); 766 | 767 | return; 768 | } 769 | 770 | gint count = 0; 771 | while ((name = g_dir_read_name (dir))) { 772 | gchar *filename; 773 | IndicatorNg *indicator; 774 | 775 | filename = g_build_filename (INDICATOR_SERVICE_DIR, name, NULL); 776 | indicator = indicator_ng_new_for_profile (filename, "desktop", &error); 777 | g_free (filename); 778 | 779 | if (!g_strcmp0(name, INDICATOR_SERVICE_APPMENU_NG)) { 780 | continue; 781 | } 782 | /*if (!g_strcmp0(name, INDICATOR_SERVICE_ME_NG)) { 783 | continue; 784 | }*/ 785 | /*if (!g_strcmp0(name, INDICATOR_SERVICE_DATETIME_NG)) { 786 | continue; 787 | }*/ 788 | if (indicator) { 789 | load_indicator(applet, menubar, INDICATOR_OBJECT (indicator), name); 790 | count++; 791 | }else{ 792 | g_warning ("unable to load '%s': %s", name, error->message); 793 | g_clear_error (&error); 794 | } 795 | } 796 | 797 | *indicators_loaded += count; 798 | 799 | g_dir_close (dir); 800 | } 801 | #endif /* HAVE_AYATANA_INDICATOR_NG || HAVE_UBUNTU_INDICATOR_NG */ 802 | 803 | static gboolean load_module(const gchar *name, AppIndicatorApplet *applet, GtkWidget *menubar) 804 | { 805 | g_debug("Looking at Module: %s", name); 806 | g_return_val_if_fail(name != NULL, FALSE); 807 | 808 | if (!g_str_has_suffix(name, G_MODULE_SUFFIX)) { 809 | return FALSE; 810 | } 811 | 812 | g_debug("Loading Module: %s", name); 813 | 814 | /* Build the object for the module */ 815 | gchar *fullpath = g_build_filename(INDICATOR_DIR, name, NULL); 816 | IndicatorObject *io = indicator_object_new_from_file(fullpath); 817 | g_free(fullpath); 818 | 819 | load_indicator(applet, menubar, io, name); 820 | 821 | return TRUE; 822 | } 823 | 824 | void update_panel_size(__attribute__((unused)) AppIndicatorApplet *applet, int u_panel_size, int u_icon_size, int u_small_icon_size){ 825 | g_debug("Panel size %d", u_panel_size); 826 | g_debug("icon size %d", u_icon_size); 827 | g_debug("small size %d", u_small_icon_size); 828 | panel_size = u_panel_size; 829 | icon_size = u_icon_size; 830 | small_icon_size = u_small_icon_size; 831 | 832 | calc_default_icon_size(); 833 | } 834 | 835 | void load_modules(AppIndicatorApplet *applet, GtkWidget *menubar, gint *indicators_loaded) 836 | { 837 | if (g_file_test(INDICATOR_DIR, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) { 838 | GDir *dir = g_dir_open(INDICATOR_DIR, 0, NULL); 839 | 840 | const gchar *name; 841 | gint count = 0; 842 | while ((name = g_dir_read_name(dir)) != NULL) { 843 | if (!g_strcmp0(name, INDICATOR_SERVICE_APPMENU)) { 844 | continue; 845 | } 846 | if (!g_strcmp0(name, INDICATOR_SERVICE_ME)) { 847 | continue; 848 | } 849 | if (!g_strcmp0(name, INDICATOR_SERVICE_DATETIME)) { 850 | continue; 851 | } 852 | g_debug("zzz a: %s", name); 853 | if (load_module(name, applet, menubar)) { 854 | count++; 855 | } 856 | } 857 | 858 | *indicators_loaded += count; 859 | 860 | g_dir_close(dir); 861 | } 862 | } 863 | 864 | void hotkey_filter(char *keystring G_GNUC_UNUSED, gpointer data) 865 | { 866 | g_return_if_fail(GTK_IS_MENU_SHELL(data)); 867 | 868 | /* Oh, wow, it's us! */ 869 | GList *children = gtk_container_get_children(GTK_CONTAINER(data)); 870 | if (children == NULL) { 871 | g_debug("Menubar has no children"); 872 | return; 873 | } 874 | 875 | gtk_menu_shell_select_item(GTK_MENU_SHELL(data), GTK_WIDGET(g_list_last(children)->data)); 876 | g_list_free(children); 877 | return; 878 | } 879 | 880 | gboolean menubar_on_draw(GtkWidget *widget, cairo_t *cr, GtkWidget *menubar) 881 | { 882 | if (gtk_widget_has_focus(menubar)) 883 | gtk_render_focus(gtk_widget_get_style_context(widget), 884 | cr, 885 | 0, 886 | 0, 887 | -1, 888 | -1); 889 | 890 | return FALSE; 891 | } 892 | 893 | #ifdef N_ 894 | #undef N_ 895 | #endif 896 | #define N_(x) x 897 | -------------------------------------------------------------------------------- /src/applet.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2016-2018 David Mohammed 3 | * 4 | * This program is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License version 3, as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 11 | * PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along 14 | * with this program. If not, see . 15 | */ 16 | 17 | #define _GNU_SOURCE 18 | 19 | #include 20 | 21 | #include "applet.h" 22 | #include 23 | #include 24 | #include 25 | 26 | #if HAVE_UBUNTU_INDICATOR && HAVE_UBUNTU_INDICATOR_NG 27 | #include 28 | #endif 29 | 30 | #if HAVE_AYATANA_INDICATOR && HAVE_AYATANA_INDICATOR_NG 31 | #include 32 | #endif 33 | 34 | void load_modules(AppIndicatorApplet *applet, GtkWidget *menubar, gint *indicators_loaded); 35 | void load_indicators_from_indicator_files(AppIndicatorApplet *applet, GtkWidget *menubar, gint *indicators_loaded); 36 | void update_panel_size(AppIndicatorApplet *applet, int panel_size, int icon_size, int small_icon_size); 37 | 38 | #define MENU_DATA_INDICATOR_OBJECT "indicator-object" 39 | #define MENU_DATA_INDICATOR_ENTRY "indicator-entry" 40 | 41 | #define IO_DATA_ORDER_NUMBER "indicator-order-number" 42 | 43 | extern GtkPackDirection packdirection; 44 | extern BudgiePanelPosition orient; 45 | 46 | static gboolean swap_orient_cb(GtkWidget *item, gpointer data) 47 | { 48 | GtkWidget *from = (GtkWidget *)data; 49 | GtkWidget *to = (GtkWidget *)g_object_get_data(G_OBJECT(from), "to"); 50 | g_object_ref(G_OBJECT(item)); 51 | gtk_container_remove(GTK_CONTAINER(from), item); 52 | if (GTK_IS_LABEL(item)) { 53 | switch (packdirection) { 54 | case GTK_PACK_DIRECTION_LTR: 55 | gtk_label_set_angle(GTK_LABEL(item), 0.0); 56 | break; 57 | case GTK_PACK_DIRECTION_TTB: 58 | gtk_label_set_angle(GTK_LABEL(item), 59 | (orient == BUDGIE_PANEL_POSITION_LEFT) ? 270.0 : 90.0); 60 | break; 61 | default: 62 | break; 63 | } 64 | } 65 | gtk_box_pack_start(GTK_BOX(to), item, FALSE, FALSE, 0); 66 | return TRUE; 67 | } 68 | 69 | static gboolean reorient_box_cb(GtkWidget *menuitem, gpointer data) 70 | { 71 | GtkWidget *from = g_object_get_data(G_OBJECT(menuitem), "box"); 72 | GtkWidget *to = (packdirection == GTK_PACK_DIRECTION_LTR) 73 | ? gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0) 74 | : gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 75 | g_object_set_data(G_OBJECT(from), "to", to); 76 | gtk_container_foreach(GTK_CONTAINER(from), (GtkCallback)swap_orient_cb, from); 77 | gtk_container_remove(GTK_CONTAINER(menuitem), from); 78 | gtk_container_add(GTK_CONTAINER(menuitem), to); 79 | g_object_set_data(G_OBJECT(menuitem), "box", to); 80 | gtk_widget_show_all(menuitem); 81 | return TRUE; 82 | } 83 | 84 | static void native_applet_real_panel_position_changed(BudgieApplet *base, 85 | BudgiePanelPosition position) 86 | { 87 | AppIndicatorApplet *self; 88 | self = (AppIndicatorApplet *)base; 89 | 90 | GtkWidget *menubar = self->menubar; 91 | 92 | switch (position) { 93 | case BUDGIE_PANEL_POSITION_NONE: 94 | g_debug("zzz changed none"); 95 | 96 | break; 97 | case BUDGIE_PANEL_POSITION_LEFT: 98 | case BUDGIE_PANEL_POSITION_RIGHT: 99 | orient = position; 100 | packdirection = GTK_PACK_DIRECTION_TTB; 101 | g_debug("zzz changed left/right"); 102 | 103 | break; 104 | default: 105 | g_debug("zzz changed horizontal"); 106 | orient = position; 107 | packdirection = GTK_PACK_DIRECTION_LTR; 108 | } 109 | 110 | if (orient != BUDGIE_PANEL_POSITION_NONE) { 111 | gtk_menu_bar_set_pack_direction(GTK_MENU_BAR(menubar), packdirection); 112 | 113 | gtk_container_foreach(GTK_CONTAINER(menubar), (GtkCallback)reorient_box_cb, NULL); 114 | } 115 | } 116 | 117 | static void native_applet_real_panel_size_changed(BudgieApplet *base, 118 | int panel_size, 119 | int icon_size, 120 | int small_icon_size) 121 | { 122 | AppIndicatorApplet *self; 123 | self = (AppIndicatorApplet *)base; 124 | update_panel_size(self, panel_size, icon_size, small_icon_size); 125 | } 126 | 127 | G_DEFINE_DYNAMIC_TYPE_EXTENDED(AppIndicatorApplet, appindicator_applet, BUDGIE_TYPE_APPLET, 0, ) 128 | 129 | extern GtkCssProvider *css_provider; 130 | 131 | /** 132 | * Handle cleanup 133 | */ 134 | static void appindicator_applet_dispose(GObject *object) 135 | { 136 | G_OBJECT_CLASS(appindicator_applet_parent_class)->dispose(object); 137 | if (css_provider != NULL) { 138 | g_object_unref(css_provider); 139 | css_provider = NULL; 140 | } 141 | } 142 | 143 | /** 144 | * Class initialisation 145 | */ 146 | static void appindicator_applet_class_init(AppIndicatorAppletClass *klazz) 147 | { 148 | GObjectClass *obj_class = G_OBJECT_CLASS(klazz); 149 | 150 | /* gobject vtable hookup */ 151 | obj_class->dispose = appindicator_applet_dispose; 152 | 153 | ((BudgieAppletClass *)klazz)->panel_position_changed = 154 | (void (*)(BudgieApplet *, 155 | BudgiePanelPosition))native_applet_real_panel_position_changed; 156 | ((BudgieAppletClass *)klazz)->panel_size_changed = 157 | (void (*)(BudgieApplet *, 158 | int, int, int))native_applet_real_panel_size_changed; 159 | } 160 | 161 | /** 162 | * We have no cleaning ourselves to do 163 | */ 164 | static void appindicator_applet_class_finalize(__budgie_unused__ AppIndicatorAppletClass *klazz) 165 | { 166 | } 167 | 168 | static GtkWidget *eventbox = NULL; 169 | static GtkWidget *menubar = NULL; 170 | 171 | static gboolean delay_load_indicators(AppIndicatorApplet *applet) 172 | { 173 | gint indicators_loaded = 0; 174 | 175 | gtk_menu_bar_set_pack_direction(GTK_MENU_BAR(menubar), packdirection); 176 | load_modules(applet, menubar, &indicators_loaded); 177 | 178 | if (indicators_loaded == 0) { 179 | /* A label to allow for click through */ 180 | GtkWidget *item = gtk_label_new("No Indicators"); 181 | gtk_container_add(GTK_CONTAINER(eventbox), item); 182 | gtk_widget_show(item); 183 | } else { 184 | gtk_container_add(GTK_CONTAINER(eventbox), menubar); 185 | gtk_widget_show(menubar); 186 | } 187 | return FALSE; 188 | } 189 | 190 | /** 191 | * We only support left click events; prevent confusion for right 192 | * click then left click fandango 193 | **/ 194 | static gboolean 195 | menubar_press (GtkWidget * widget, 196 | GdkEventButton *event, 197 | gpointer data G_GNUC_UNUSED) 198 | { 199 | if (event->button != 1) { 200 | g_signal_stop_emission_by_name(widget, "button-press-event"); 201 | } 202 | 203 | return FALSE; 204 | } 205 | 206 | /** 207 | * Initialisation of basic UI layout and such 208 | */ 209 | static void appindicator_applet_init(AppIndicatorApplet *self) 210 | { 211 | GtkCssProvider *css_provider = NULL; 212 | 213 | #if HAVE_AYATANA_INDICATOR_NG || HAVE_UBUNTU_INDICATOR_NG 214 | ido_init(); 215 | #endif 216 | 217 | gint indicators_loaded = 0; 218 | 219 | menubar = gtk_menu_bar_new(); 220 | self->menubar = menubar; 221 | 222 | g_signal_connect(menubar, "button-press-event", G_CALLBACK(menubar_press), NULL); 223 | 224 | css_provider = gtk_css_provider_new(); 225 | #if GTK_CHECK_VERSION(3, 20, 0) 226 | gtk_css_provider_load_from_data(css_provider, 227 | "menubar { \n" 228 | " background: transparent; } \n" 229 | ".budgie-menubar { \n" 230 | " padding-left: 2px; \n" 231 | " padding-right: 2px; \n" 232 | "} \n", 233 | -1, 234 | NULL); 235 | #else 236 | gtk_css_provider_load_from_data(css_provider, 237 | ".menuitem { \n" 238 | " background: transparent; } \n" 239 | ".budgie-menubar { \n" 240 | " padding-left: 2px; \n" 241 | " padding-right: 2px; \n" 242 | "} \n", 243 | -1, 244 | NULL); 245 | #endif 246 | gtk_style_context_add_provider(GTK_STYLE_CONTEXT( 247 | gtk_widget_get_style_context(GTK_WIDGET(menubar))), 248 | GTK_STYLE_PROVIDER(css_provider), 249 | GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); 250 | 251 | eventbox = gtk_event_box_new(); 252 | gtk_container_add(GTK_CONTAINER(self), eventbox); 253 | gtk_widget_show(eventbox); 254 | 255 | gtk_container_set_border_width(GTK_CONTAINER(menubar), 1); 256 | 257 | gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INDICATOR_ICONS_DIR); 258 | 259 | #if HAVE_AYATANA_INDICATOR_NG || HAVE_UBUNTU_INDICATOR_NG 260 | load_indicators_from_indicator_files (self, menubar, &indicators_loaded); 261 | #endif 262 | 263 | /* Show all of our things. */ 264 | gtk_widget_show_all(GTK_WIDGET(self)); 265 | 266 | g_timeout_add_seconds(1, (GSourceFunc)delay_load_indicators, self); 267 | } 268 | 269 | void appindicator_applet_init_gtype(GTypeModule *module) 270 | { 271 | appindicator_applet_register_type(module); 272 | } 273 | 274 | BudgieApplet *appindicator_applet_new() 275 | { 276 | // return applet_construct(APPINDICATOR_TYPE_NATIVE_APPLET, uuid); 277 | return g_object_new(APPINDICATOR_TYPE_NATIVE_APPLET, NULL); 278 | } 279 | -------------------------------------------------------------------------------- /src/applet.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2016-2018 David Mohammed 3 | * 4 | * This program is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License version 3, as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 11 | * PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along 14 | * with this program. If not, see . 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | #define __budgie_unused__ __attribute__((unused)) 23 | 24 | G_BEGIN_DECLS 25 | 26 | typedef struct _AppIndicatorApplet AppIndicatorApplet; 27 | typedef struct _AppIndicatorAppletClass AppIndicatorAppletClass; 28 | 29 | #define APPINDICATOR_TYPE_NATIVE_APPLET appindicator_applet_get_type() 30 | #define APPINDICATOR_NATIVE_APPLET(o) \ 31 | (G_TYPE_CHECK_INSTANCE_CAST((o), APPINDICATOR_TYPE_NATIVE_APPLET, AppIndicatorApplet)) 32 | #define APPINDICATOR_IS_NATIVE_APPLET(o) \ 33 | (G_TYPE_CHECK_INSTANCE_TYPE((o), APPINDICATOR_TYPE_NATIVE_APPLET)) 34 | #define APPINDICATOR_NATIVE_APPLET_CLASS(o) \ 35 | (G_TYPE_CHECK_CLASS_CAST((o), APPINDICATOR_TYPE_NATIVE_APPLET, AppIndicatorAppletClass)) 36 | #define APPINDICATOR_IS_NATIVE_APPLET_CLASS(o) \ 37 | (G_TYPE_CHECK_CLASS_TYPE((o), APPINDICATOR_TYPE_NATIVE_APPLET)) 38 | #define APPINDICATOR_NATIVE_APPLET_GET_CLASS(o) \ 39 | (G_TYPE_INSTANCE_GET_CLASS((o), APPINDICATOR_TYPE_NATIVE_APPLET, AppIndicatorAppletClass)) 40 | 41 | struct _AppIndicatorAppletClass { 42 | BudgieAppletClass parent_class; 43 | }; 44 | 45 | struct _AppIndicatorApplet { 46 | BudgieApplet parent; 47 | GSettings *settings; 48 | GtkWidget *menubar; 49 | }; 50 | 51 | GType appindicator_applet_get_type(void); 52 | 53 | /** 54 | * Public for the plugin to allow registration of types 55 | */ 56 | void appindicator_applet_init_gtype(GTypeModule *module); 57 | 58 | /** 59 | * Construct a new AppIndicatorApplet 60 | */ 61 | BudgieApplet *appindicator_applet_new(void); 62 | 63 | G_END_DECLS 64 | -------------------------------------------------------------------------------- /src/plugin.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2016-2018 David Mohammed 3 | * 4 | * This program is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License version 3, as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 11 | * PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along 14 | * with this program. If not, see . 15 | */ 16 | 17 | #define _GNU_SOURCE 18 | 19 | #include 20 | 21 | #include "applet.h" 22 | #include "plugin.h" 23 | 24 | static void appindicator_native_plugin_iface_init(BudgiePluginIface *iface); 25 | 26 | G_DEFINE_DYNAMIC_TYPE_EXTENDED(AppIndicatorNativePlugin, appindicator_native_plugin, G_TYPE_OBJECT, 27 | 0, 28 | G_IMPLEMENT_INTERFACE_DYNAMIC(BUDGIE_TYPE_PLUGIN, 29 | appindicator_native_plugin_iface_init)) 30 | 31 | /** 32 | * Return a new panel widget 33 | */ 34 | static BudgieApplet *native_applet_get_panel_widget(__budgie_unused__ BudgiePlugin *self, 35 | __budgie_unused__ gchar *uuid) 36 | { 37 | BudgieApplet *result = appindicator_applet_new(); 38 | // the following prevent lots of warnings and errors when 39 | // the applet is moved in the panel 40 | g_object_ref_sink(result); 41 | return result; 42 | } 43 | 44 | /** 45 | * Handle cleanup 46 | */ 47 | static void appindicator_native_plugin_dispose(GObject *object) 48 | { 49 | G_OBJECT_CLASS(appindicator_native_plugin_parent_class)->dispose(object); 50 | } 51 | 52 | /** 53 | * Class initialisation 54 | */ 55 | static void appindicator_native_plugin_class_init(AppIndicatorNativePluginClass *klazz) 56 | { 57 | GObjectClass *obj_class = G_OBJECT_CLASS(klazz); 58 | 59 | /* gobject vtable hookup */ 60 | obj_class->dispose = appindicator_native_plugin_dispose; 61 | } 62 | 63 | /** 64 | * Implement the BudgiePlugin interface, i.e the factory method get_panel_widget 65 | */ 66 | static void appindicator_native_plugin_iface_init(BudgiePluginIface *iface) 67 | { 68 | iface->get_panel_widget = native_applet_get_panel_widget; 69 | } 70 | 71 | /** 72 | * No-op, just skips compiler errors 73 | */ 74 | static void appindicator_native_plugin_init(__budgie_unused__ AppIndicatorNativePlugin *self) 75 | { 76 | } 77 | 78 | /** 79 | * We have no cleaning ourselves to do 80 | */ 81 | static void appindicator_native_plugin_class_finalize( 82 | __budgie_unused__ AppIndicatorNativePluginClass *klazz) 83 | { 84 | } 85 | 86 | /** 87 | * Export the types to the gobject type system 88 | */ 89 | G_MODULE_EXPORT void peas_register_types(PeasObjectModule *module) 90 | { 91 | appindicator_native_plugin_register_type(G_TYPE_MODULE(module)); 92 | 93 | /* Register the actual dynamic types contained in the resulting plugin */ 94 | appindicator_applet_init_gtype(G_TYPE_MODULE(module)); 95 | 96 | peas_object_module_register_extension_type(module, 97 | BUDGIE_TYPE_PLUGIN, 98 | APPINDICATOR_TYPE_NATIVE_PLUGIN); 99 | } 100 | -------------------------------------------------------------------------------- /src/plugin.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2016-2018 David Mohammed 3 | * 4 | * This program is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License version 3, as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 11 | * PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along 14 | * with this program. If not, see . 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | 21 | G_BEGIN_DECLS 22 | 23 | typedef struct _AppIndicatorNativePlugin AppIndicatorNativePlugin; 24 | typedef struct _AppIndicatorNativePluginClass AppIndicatorNativePluginClass; 25 | 26 | #define APPINDICATOR_TYPE_NATIVE_PLUGIN appindicator_native_plugin_get_type() 27 | #define APPINDICATOR_NATIVE_PLUGIN(o) \ 28 | (G_TYPE_CHECK_INSTANCE_CAST((o), APPINDICATOR_TYPE_NATIVE_PLUGIN, AppIndicatorNativePlugin)) 29 | #define APPINDICATOR_IS_NATIVE_PLUGIN(o) \ 30 | (G_TYPE_CHECK_INSTANCE_TYPE((o), APPINDICATOR_TYPE_NATIVE_PLUGIN)) 31 | #define APPINDICATOR_NATIVE_PLUGIN_CLASS(o) \ 32 | (G_TYPE_CHECK_CLASS_CAST((o), \ 33 | APPINDICATOR_TYPE_NATIVE_PLUGIN, \ 34 | AppIndicatorNativePluginClass)) 35 | #define APPINDICATOR_IS_NATIVE_PLUGIN_CLASS(o) \ 36 | (G_TYPE_CHECK_CLASS_TYPE((o), APPINDICATOR_TYPE_NATIVE_PLUGIN)) 37 | #define APPINDICATOR_NATIVE_PLUGIN_GET_CLASS(o) \ 38 | (G_TYPE_INSTANCE_GET_CLASS((o), \ 39 | APPINDICATOR_TYPE_NATIVE_PLUGIN, \ 40 | AppIndicatorNativePluginClass)) 41 | 42 | struct _AppIndicatorNativePluginClass { 43 | GObjectClass parent_class; 44 | }; 45 | 46 | struct _AppIndicatorNativePlugin { 47 | GObject parent; 48 | }; 49 | 50 | GType appindicator_native_plugin_get_type(void); 51 | 52 | G_END_DECLS 53 | -------------------------------------------------------------------------------- /update_format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | CI_EXCLUDES="! -path */gvc/* ! -path */src/imports/natray/*" 3 | 4 | # Ensure we're formatted everywhere. 5 | clang-format -i $(find . $CI_EXCLUDES -name '*.[ch]') 6 | 7 | # Check we have no typos. 8 | which misspell 2>/dev/null >/dev/null 9 | if [[ $? -eq 0 ]]; then 10 | misspell -error `find . $CI_EXCLUDES -name '*.[ch]'` 11 | misspell -error `find . $CI_EXCLUDES -name '*.vala'` 12 | fi 13 | --------------------------------------------------------------------------------