fuss-app-indicator 10.3 KB
Newer Older
1
#!/usr/bin/env python
2
3
#
# File: fuss-app-indicator
4
5
#
# Copyright (C) 2012-2016 Christopher R. Gabriel <cgabriel@truelite.it>
6
7
#
# Based on
8
9
# ClassicMenu Indicator - an indicator applet for Unity, that
#                         provides the main menu of Gnome2/Gnome Classic.
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#
# Copyright (C) 2011 Florian Diesch <devel@florian-diesch.de>
#
# Homepage: http://www.florian-diesch.de/software/classicmenu-indicator/
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 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/>.

import sys
29
import os.path
30
31
32
33
34
35
import gmenu
import gtk, glib, gobject
import appindicator
import re
import textwrap
import subprocess
Christopher R. Gabriel's avatar
Christopher R. Gabriel committed
36
from xdg import IconTheme as xdgicon
Christopher R. Gabriel's avatar
Christopher R. Gabriel committed
37
import webbrowser
38

39
__version__ = "8.0"
40
41
42
43
44
45
46
47
48
49

APP_NAME = 'Fuss App Indicator'
APP_VERSION = __version__


re_command = re.compile('%[UFuf]')
cmd_terminal = 'gnome-terminal -e'

include_nodisplay = False

Christopher R. Gabriel's avatar
Christopher R. Gabriel committed
50
resources = (
Christopher R. Gabriel's avatar
Christopher R. Gabriel committed
51
52
53
54
55
56
57
    ("https://campus.fuss.bz.it/", "Campus (Chamilo)"),
    ("https://moodle.fuss.bz.it/", "LMS (Moodle)"),
    ("https://cloud.fuss.bz.it", "Cloud (FUSS Cloud)"),
    ("https://docs.fuss.bz.it/", "Docs (Pydio)"),
    ("https://share.fuss.bz.it/", "Share (ownCloud)"),
    ("https://poll.fuss.bz.it/", "Sondaggi (LimeSurvey)"),
)
Christopher R. Gabriel's avatar
Christopher R. Gabriel committed
58

59
60
FUSS_CLIENT_CONF = "/etc/fuss-client/server.conf"

61
62
63
class FussAppIndicator(object):
    def __init__(self, menu_name):
        self.indicator = appindicator.Indicator('fuss-app-indicator-%s' % menu_name,
64
                                                'fuss-indicator-%s' % menu_name,
65
66
67
68
                                                appindicator.CATEGORY_SYSTEM_SERVICES)
        self.icon_size = 22
        self.update_requested = False
        self.indicator.set_status (appindicator.STATUS_ACTIVE)
69
        self.server_url = self.discover_server()
70
71
72
73
74
        self.trees = []
        tree = self.create_tree('fuss-%s.menu' % menu_name)
        self.trees.append(tree)
        self.indicator.set_menu(self.create_menu())

75
76
77
78
79
80
81
82
83
    def discover_server(self):
        if os.path.isfile(FUSS_CLIENT_CONF):
            from ConfigParser import ConfigParser
            c = ConfigParser()
            c.read(FUSS_CLIENT_CONF)
            if c.has_option("Server", "address"):
                url = c.get("Server", "address")
                return "http://%s/webpasswd" % url
        return None
84

85
86
87
88
89
90
    def run(self):
        try:
            gtk.main()
        except KeyboardInterrupt:
            pass

91
    def create_menu_item(self, entry):
92
93
94
95
96
97
98
99
100
101
102
        icon = entry.get_icon()
        name = entry.get_name()

        menu_item = gtk.ImageMenuItem(name)

        default_theme = gtk.icon_theme_get_default()

        if (icon):
            menu_item = gtk.ImageMenuItem(name)

            try:
103
                if default_theme.lookup_icon(icon, self.icon_size,
104
                                             gtk.ICON_LOOKUP_USE_BUILTIN):
105
                    pixbuf = default_theme.load_icon(icon, self.icon_size,
106
107
108
109
110
111
112
113
114
                                                     gtk.ICON_LOOKUP_USE_BUILTIN)
                    if pixbuf.get_height() > self.icon_size:
                        scale = pixbuf.get_height() / float(self.icon_size)
                        width = int(pixbuf.get_width() * scale)
                        pixbuf.scale_simple(width, self.icon_size, gtk.gdk.INTERP_BILINEAR)
                    img = gtk.image_new_from_pixbuf(pixbuf)
                else:
                    icon_path = xdgicon.getIconPath(icon)
                    if icon_path:
115
                        pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(icon_path,
116
117
118
119
                                                                      self.icon_size,
                                                                      self.icon_size)
                        img = gtk.image_new_from_pixbuf(pixbuf)
                    else:
120
                        img = gtk.Image()
121
122
                        img.set_from_icon_name(icon, self.icon_size)

Christopher R. Gabriel's avatar
Christopher R. Gabriel committed
123
            except glib.GError:
124
125
                img = gtk.Image()
                img.set_from_icon_name('', self.icon_size)
126

127
128
129
            menu_item.set_image(img)
            menu_item.set_always_show_image(True)
        else:
130
131
            menu_item = gtk.MenuItem(name)

132
133
134
135
136
137
138
139
140
141
        if entry.get_type() ==  gmenu.TYPE_ENTRY:
            menu_item.connect('activate', self.on_menuitem_activate, entry)

        menu_item.show()
        return menu_item


    def process_entry(self, menu, entry):
        menu.append(self.create_menu_item(entry))

142

143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
    def process_directory(self, menu, dir):
        if dir:
            for item in dir.get_contents():
                type = item.get_type()
                if type == gmenu.TYPE_ENTRY:
                    self.process_entry(menu, item)
                elif type == gmenu.TYPE_DIRECTORY:
                    new_menu = gtk.Menu()
                    menu_item = self.create_menu_item(item)
                    menu_item.set_submenu(new_menu)
                    menu.append(menu_item)
                    menu_item.show()
                    self.process_directory(new_menu, item)
                elif type == gmenu.TYPE_ALIAS:
                    aliased = item.get_item()
                    if aliased.get_type() == gmenu.TYPE_ENTRY:
                        self.process_entry(menu, aliased)
                elif type == gmenu.TYPE_SEPARATOR:
                    menu_item = gtk.SeparatorMenuItem()
                    menu.append(menu_item)
                    menu_item.show()
                elif type in [ gmenu.TYPE_HEADER ]:
                    pass
                else:
                    print >> sys.stderr, 'Unsupported item type: %s' % type


    def add_to_menu(self, menu, tree):
171
        root = tree.get_root_directory()
172
173
        self.process_directory(menu, root)

Christopher R. Gabriel's avatar
Christopher R. Gabriel committed
174
175
    def open_url(self, obj, url):
        webbrowser.open(url)
176
177
178

    def on_menuitem_change_password(self, menuitem):
        webbrowser.open(self.server_url)
179

180
181
182
183
184
185
186
187
188
189
190
191
    def create_menu(self):
        menu = gtk.Menu()

        for t in self.trees:
            if t:
                self.add_to_menu(menu, t)
                menu_item = gtk.SeparatorMenuItem()
                menu.append(menu_item)

        menu_item = gtk.MenuItem('%s'%APP_NAME)
        menu.append(menu_item)

192

193
194
195
        submenu = gtk.Menu()
        menu_item.set_submenu(submenu)

196
197
198
199
200
201
202
203
204
        # password
        if self.server_url:
            menu_item = gtk.SeparatorMenuItem()
            menu.append(menu_item)
            menu_item = gtk.MenuItem("Cambia Password")
            menu_item.connect('activate', self.on_menuitem_change_password)
            menu.append(menu_item)
            menu_item = gtk.SeparatorMenuItem()
            menu.append(menu_item)
205

Christopher R. Gabriel's avatar
Christopher R. Gabriel committed
206
207
208
209
        resource_menu_item = gtk.MenuItem('Risorse')
        menu.append(resource_menu_item)
        resource_menu = gtk.Menu()
        resource_menu_item.set_submenu(resource_menu)
210

Christopher R. Gabriel's avatar
Christopher R. Gabriel committed
211
212
213
214
        for r in resources:
            r_item = gtk.MenuItem(r[1])
            resource_menu.append(r_item)
            r_item.connect('activate', self.open_url, r[0])
215
216


217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
        menu_item = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
        menu_item.connect('activate', self.on_menuitem_about_activate)
        submenu.append(menu_item)

        menu.show_all()
        return menu;

    def create_tree(self, name):
        flags = gmenu.FLAGS_NONE
        if include_nodisplay:
            flags = flags | gmenu.FLAGS_INCLUDE_NODISPLAY
        tree = gmenu.lookup_tree(name, flags)
        if tree.get_root_directory():
            tree.add_monitor(self.on_menu_file_changed)
            return tree
        else:
            return None


    def update_menu(self):
        self.update_requested = False
238
        self.indicator.set_menu(self.create_menu())
239
240
241
242
243
244
245
246
        return False    # Don't run again

#####################
## Signal-Behandlung
#####################

    def on_menuitem_activate(self, menuitem, entry):
        command = entry.get_exec()
247
        if command:
248
249
250
            # refs #277 #278
            # workaround for some .desktop files where %i and %c don't
            # get properly substituted.
251
252
            command = command.replace('%i', '')
            command = command.replace('-caption %c', '')
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
            command=re_command.sub('', command)
            if entry.get_launch_in_terminal():
                command = '%s %s' % (cmd_terminal, command)
            subprocess.Popen(command, shell=True)


    def on_menu_file_changed(self, tree):
        if not self.update_requested:
            self.update_requested = True
            gobject.timeout_add(5000, self.update_menu)

    def on_menuitem_about_activate(self, menuitem):
        dlg = gtk.AboutDialog()
        dlg.set_name(APP_NAME)
        dlg.set_version(APP_VERSION)
        dlg.set_website('http://www.fuss.bz.it')
        dlg.set_authors(['Christopher R. Gabriel <cgabriel@fuss.bz.it>'])
Christopher R. Gabriel's avatar
Christopher R. Gabriel committed
270
        dlg.set_copyright('Copyright (c) 2012-2016 The Fuss Project')
271
272
        dlg.set_license(textwrap.dedent(
            """
Christopher R. Gabriel's avatar
Christopher R. Gabriel committed
273
            Copyright (c) 2012-2016 The Fuss Project <info@fuss.bz.it>
274

275
            Homepage: http://www.fuss.bz.it/
276

277
278
279
280
            This program is free software: you can redistribute it and/or modify
            it under the terms of the GNU General Public License as published by
            the Free Software Foundation, either version 3 of the License, or
            (at your option) any later version.
281

282
283
284
285
            This program 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 General Public License for more details.
286

287
288
289
290
291
292
293
294
295
296
297
298
299
300
            You should have received a copy of the GNU General Public License
            along with this program.  If not, see <http://www.gnu.org/licenses/>.
            """))

        dlg.run()
        dlg.destroy()


def main():
    indicator = FussAppIndicator(sys.argv[1])
    indicator.run()

if __name__ == '__main__':
    main()