Пример #1
0
 def _add_item_cb(self):
     # grab the next pressed key and show the first dialog
     self.grab_key_func = self.grabbed_key_func
     self.dia = EmcDialog(title=_('Add a new key'),
                          style='cancel',
                          text=_('Press a key on your keyboard'),
                          canc_cb=self.ungrab_key)
Пример #2
0
 def item_selected(self, url, user_data):
     self.dia = EmcDialog(style='progress',
                          title=user_data,
                          text='Press start to test a 5MB download')
     self.dia.button_add('Close', lambda b: self.dia.delete())
     self.dia.button_add('delete()', self.delete_btn_cb)
     self.dia.button_add('To file (5M)', self.start_btn_cb, self.url1)
     self.dia.button_add('To mem (13K)', self.start_btn_cb, self.url2)
Пример #3
0
 def item_selected(self, url, user_data):
     self._val = ini.get_float(self._sec, self._opt)
     self._dia = EmcDialog(style='minimal', title=self._lbl, text='')
     self._dia.button_add(_('Ok'), self._btn_ok_cb)
     self._dia.button_add(None, self._btn_plus_cb, icon='icon/plus')
     self._dia.button_add(None, self._btn_minus_cb, icon='icon/minus')
     self._dia.button_add(_('Cancel'), self._btn_canc_cb)
     self._dia_text_update()
Пример #4
0
    def __init__(self, title=None, done_cb=None, **kargs):
        self._user_cb = done_cb
        self._user_kargs = kargs

        self._dialog = EmcDialog(title or _('Source Selector'), style='list')
        self._dialog.button_add(_('Select'), self._btn_select_cb)
        self._dialog.button_add(_('Browse'), self._btn_browse_cb, default=True)

        self.populate_devices()
Пример #5
0
 def item_selected(self, url, user_data):
     self.dia = EmcDialog(title='EmcExe test',
                          text='Press a button to run a command')
     self.dia.button_add('Close', lambda b: self.dia.delete())
     self.dia.button_add('delete()', lambda b: self.exe.delete())
     self.dia.button_add('"wrong"', self.run_btn_cb, ('wrong', ))
     self.dia.button_add('"infinite.py"', self.run_btn_cb,
                         (sys.executable, self.infinite_path))
     self.dia.button_add('"ls -l"', self.run_btn_cb, ('ls', '-l'))
     self.dia.button_add('"ls"', self.run_btn_cb, ('ls', ))
Пример #6
0
    def __init__(self, pid=None, name=None, lang=DEFAULT_INFO_LANG):
        self.pid = pid
        self.info = None

        tmdb = TMDBv3(lang=lang)
        if name:
            tmdb.person_search(name, self._search_done_cb)
        elif pid:
            tmdb.get_cast_info(self.pid, self._fetch_done_cb)
        self._dia = EmcDialog(style='minimal',
                              title=_('Fetching info'),
                              content='image/tmdb_logo.png',
                              spinner=True)
Пример #7
0
    def populate_folder(self, root, folder):
        if folder == '':  # back in '/'
            self.populate_devices()
            return

        try:
            folders = os.listdir(folder)
        except PermissionError:
            EmcDialog(style='error', text=_('Permission denied'))
            return

        self._dialog.list_clear()

        # back item
        parent = os.path.normpath(os.path.join(folder, '..'))
        it = self._dialog.list_item_append(_('Back'), 'icon/back')
        it.data['root'] = root
        it.data['path'] = parent if parent != folder else ''  # back in '/'

        # folders
        for fname in utils.natural_sort(folders):
            fullpath = os.path.join(folder, fname)
            if fname[0] != '.' and os.path.isdir(fullpath):
                it = self._dialog.list_item_append(fname, 'icon/folder')
                it.data['root'] = root
                it.data['path'] = fullpath

        self._dialog.list_go()
Пример #8
0
    def item_selected(self, url, user_data):
        def storage_events_cb(event):
            if event != 'STORAGE_CHANGED':
                return
            dia.list_clear()
            for device in storage.list_devices():
                txt = '{0.label} [ {0.device} ➙ {0.mount_point} ]'.format(
                    device)
                dia.list_item_append(txt, device.icon, device=device)
            dia.list_go()

        def dia_canc_cb(dia):
            # events.listener_del('uit_storage')
            dia.delete()

        def dia_sel_cb(dia, device):
            print(device)
            txt = '<small>{}</>'.format(utf8_to_markup(str(device)))
            EmcDialog(style='info', title='Device info', text=txt)

        dia = EmcDialog(title='Storage devices',
                        style='list',
                        done_cb=dia_sel_cb,
                        canc_cb=dia_canc_cb)
        storage_events_cb('STORAGE_CHANGED')
Пример #9
0
 def item_selected(self, url, user_data):
     self.dialog = dia = EmcDialog('EmcIdler test', 'Press "create"')
     dia.button_add('Close test', self.close_test)
     dia.button_add('delete()', lambda b: self.idler.delete())
     dia.button_add('resume()', lambda b: self.idler.resume())
     dia.button_add('pause()', lambda b: self.idler.pause())
     dia.button_add('Create oneshot', self.idler_create_oneshot)
     dia.button_add('Create', self.idler_create)
Пример #10
0
    def _fetch_done_cb(self, tmdb, result):
        self.info = result
        self._dia.delete()

        text = ''
        if self.info.get('biography'):
            text += '%s<br><br>' % self.info['biography'].replace('\n', '<br>')
        if self.info.get('birthday'):
            text += '<name>%s:</name> %s<br>' % (_('Birthday'),
                                                 self.info['birthday'])
        if self.info.get('deathday'):
            text += '<name>%s:</name> %s<br>' % (_('Deathday'),
                                                 self.info['deathday'])
        if self.info.get('place_of_birth'):
            text += '<name>%s:</name> %s<br>' % (_('Place of birth'),
                                                 self.info['place_of_birth'])

        self._dia = EmcDialog(title=self.info['name'],
                              style='panel',
                              content=self.info['profile_path'],
                              text=text)

        # merge cast and crew in a single movies list
        movies = {}  # key: tmdbid val: tmdb-data-dict + 'jobs'
        for movie in self.info['credits']['crew']:
            tid = movie['id']
            if tid in movies:
                movies[tid]['jobs'].append(movie.get('job'))
            else:
                movie['jobs'] = [movie.get('job')]
                movies[tid] = movie

        for movie in self.info['credits']['cast']:
            tid = movie['id']
            if tid in movies:
                movies[tid]['character'] = movie.get('character')
            else:
                movies[tid] = movie

        self._dia.button_add(
            _('Movies (%s)') % len(movies),
            lambda b: self.movies_dialog(movies))
        num = len(self.info['images']['profiles'])
        self._dia.button_add(
            _('Photos (%s)') % num, lambda b: self.photos_dialog())
Пример #11
0
 def item_selected(self, url, data):
     key, event, mod = data
     txt = '%s<br><br>%s ⇾ %s' % (
         _('Are you sure you want to remove the mapping?'), key, event)
     EmcDialog(style='yesno',
               title=_('Remove key'),
               text=txt,
               done_cb=self._remove_confirmed,
               user_data=data)
Пример #12
0
 def item_selected(self, url, user_data):
     self.timer = None
     self.dialog = dia = EmcDialog('EmcTimer test', 'Press "create"')
     dia.on_delete(self.dialog_deleted, dkey1='val1', dkey2=2)
     dia.button_add('Close test', lambda b: self.dialog.delete())
     dia.button_add('delete()', lambda b: self.timer.delete())
     dia.button_add('stop()', lambda b: self.timer.stop())
     dia.button_add('start()', lambda b: self.timer.start())
     dia.button_add('reset()', lambda b: self.timer.reset())
     dia.button_add('Create oneshot', self.timer_create_oneshot)
     dia.button_add('Create onstart', self.timer_create_onstart)
Пример #13
0
 def item_selected(self, url, user_data):
     self.dia = d = EmcDialog(title="Test EmcImage")
     d.button_add('Close', selected_cb=lambda b: self.dia.delete())
     d.button_add('set(error)', selected_cb=self.from_not_exists)
     d.button_add('set(\'\')', selected_cb=self.unset, cb_data="e")
     d.button_add('set(None)', selected_cb=self.unset, cb_data="n")
     d.button_add('URL dest', selected_cb=self.from_url_with_dest)
     d.button_add('URL auto', selected_cb=self.from_url)
     d.button_add('Image', selected_cb=self.from_image)
     d.button_add('Icon', selected_cb=self.from_icon)
     d.button_add('Fullpath', selected_cb=self.from_fullpath, default=True)
Пример #14
0
    def item_selected(self, url, user_data):
        dia = EmcDialog(self._lbl,
                        style='list',
                        done_cb=self._dia_list_selected_cb)

        if self._mul:
            choosed = ini.get_string_list(self._sec, self._opt)
        else:
            choosed = [ini.get(self._sec, self._opt)]

        item = None
        for code2, (code3, code5, name) in sorted(utils.iso639_table.items(),
                                                  key=lambda x: x[1][2]):
            if name is not None:
                if code2 in choosed:
                    item = dia.list_item_append(name, end='icon/check_on')
                    item.data['code2'] = code2
                else:
                    it = dia.list_item_append(name)
                    it.data['code2'] = code2
        dia.list_go()

        if item:
            item.show()
            item.selected = True
Пример #15
0
 def _idler_cb(generator):
     try:
         fname = next(generator)
         os.remove(fname)
         dia.my_counter += 1
     except StopIteration:
         EmcDialog(style='cancel',
                   title=_('Clear online images cache'),
                   text='Operation completed, %d files deleted.' %
                   dia.my_counter)
         dia.delete()
         return ecore.ECORE_CALLBACK_CANCEL
     return ecore.ECORE_CALLBACK_RENEW
Пример #16
0
def _clear_remotes_cache():
    def _idler_cb(generator):
        try:
            fname = next(generator)
            os.remove(fname)
            dia.my_counter += 1
        except StopIteration:
            EmcDialog(style='cancel',
                      title=_('Clear online images cache'),
                      text='Operation completed, %d files deleted.' %
                      dia.my_counter)
            dia.delete()
            return ecore.ECORE_CALLBACK_CANCEL
        return ecore.ECORE_CALLBACK_RENEW

    dia = EmcDialog(style='minimal',
                    title=_('Clear online images cache'),
                    spinner=True,
                    text=_('Operation in progress, please wait...'))
    dia.my_counter = 0
    gen = utils.grab_files(os.path.join(utils.user_cache_dir, 'remotes'))
    ecore.Idler(_idler_cb, gen)
Пример #17
0
 def item_selected(self, url, user_data):
     dia = EmcDialog(self._lbl,
                     style='list',
                     done_cb=self._dia_list_selected_cb)
     for string in self._sli:
         if string == ini.get(self._sec, self._opt):
             it = dia.list_item_append(string, end='icon/check_on')
             it.selected = True
         else:
             dia.list_item_append(string)
     dia.list_go()
Пример #18
0
 def __init__(self, conf_group, title=None, done_cb=None):
     # EmcDialog.__init__(self, title, style='list')
     self._dialog = dia = EmcDialog(title or _('Sources Manager'),
                                    style='list')
     dia.button_add(_('Done'), icon='icon/ok',
                    selected_cb=self._cb_btn_done)
     dia.button_add(_('Add'), icon='icon/plus',
                    selected_cb=self._cb_btn_add)
     dia.button_add(_('Remove'), icon='icon/minus',
                    selected_cb=self._cb_btn_remove)
     self._sources = ini.get_string_list(conf_group, 'folders', ';')
     self._conf_group = conf_group
     self._done_cb = done_cb
     self._populate()
Пример #19
0
def _sys_info():
    import pyudev
    from nepymc import __version__ as emc_version

    try:
        from efl import __version__ as efl_version
    except ImportError:
        efl_version = _('Not found')

    try:
        from PySide2 import __version__ as pyside_version
    except ImportError:
        pyside_version = _('Not found')

    # win_w, win_h = gui.win.size
    # scr_x, scr_y, scr_w, scr_h = gui.win.screen_size
    # dpi_x, dpi_y = gui.win.screen_dpi

    text = '<h3>{}</h3>' \
           '<b>{}:</b> {}<br>' \
           '<h3>{}</h3>' \
           '<b>{}:</b> {}<br>'\
           '<b>{}:</b> {}<br>' \
           '<b>{}:</b> {}' \
           '<h3>{}</h3>' \
           '<b>{}:</b> {}<br>' \
           '<b>{}:</b> {}<br>' \
           '<b>{}:</b> {} <b>{}:</b> {}<br>' \
           '<b>{}:</b> {}<br>' \
           '<b>{}:</b> {}<br>'.format(
             _('Core'),
             _('Theme'), ini.get('general', 'theme'), #gui.theme_file,  # TODO
             # _('Window size'), win_w, win_h,  # TODO
             # _('screen'), scr_w, scr_h, scr_x, scr_y,  # TODO
             # _('dpi'), dpi_x, dpi_y,  # TODO
             _('Paths'),
             _('Base folder'), utils.emc_base_dir,
             _('Config folder'), utils.user_conf_dir,
             _('Cache folder'), utils.user_cache_dir,
             _('Versions'),
             _('NepyMC'), emc_version,
             _('Python'), sys.version,
             _('Udev'), pyudev.udev_version(), _('pyudev'), pyudev.__version__,
             _('Python-EFL'), efl_version,
             _('PySide'), pyside_version,
           )
    EmcDialog(style='panel', title=_('System info'), text=text)
Пример #20
0
 def movies_dialog(self, movies):
     dia = EmcDialog(style='list', title=self.info['name'])
     for movie in sorted(movies.values(), key=itemgetter('title')):
         label = '<big>{}</big>'.format(movie['title'])
         if 'character' in movie:
             label += ' <i>{} <big>{}</big></i>'.format(
                 _('as'), movie['character'])
         if 'jobs' in movie:
             label += ' <i>({})</i>'.format(', '.join(movie['jobs']))
         dia.list_item_append(label, movie.get('poster_path'))
     dia.list_go()
Пример #21
0
    def grabbed_key_func(self, key):
        # ungrab remote keys & delete the first dialog
        self.grab_key_func = None
        self.pressed_key = key
        self.dia.delete()

        # create the dialog to choose the event to bind
        dia = EmcDialog(title=_('Choose an event to bind'),
                        style='list',
                        done_cb=self.event_choosed_cb)
        for event in input_events.STANDARD_EVENTS.split():
            dia.list_item_append(event)
        dia.list_go()
Пример #22
0
 def item_selected(self, url, user_data):
     dia = EmcDialog(self._lbl,
                     style='list',
                     done_cb=self._dia_list_selected_cb)
     i = 0
     for string in self._vals:
         if i == ini.get_int(self._sec, self._opt):
             it = dia.list_item_append(string, end='icon/check_on')
             it.selected = True
         else:
             it = dia.list_item_append(string)
         it.data['i'] = i
         i += 1
     dia.list_go()
Пример #23
0
def _vkeyb_layouts_list():
    dia = EmcDialog(title=_('Virtual keyboard layouts'),
                    style='list',
                    done_cb=_vkeyb_layouts_select_cb)
    dia.button_add(_('Close'),
                   selected_cb=_vkeyb_layouts_close_cb,
                   cb_data=dia)
    dia.button_add(_('Select'),
                   default=True,
                   selected_cb=_vkeyb_layouts_select_cb,
                   cb_data=dia)

    avail = ini.get_string_list('general', 'keyb_layouts')
    for k in sorted(gui.keyboard_layouts.keys()):
        name = gui.keyboard_layouts[k][0]
        end = 'icon/check_on' if k in avail else 'icon/check_off'
        it = dia.list_item_append(name, end=end)
        it.data['key'] = k
Пример #24
0
    def __init__(self, name, version=None):
        self._name = name
        self._vers = version
        self._vkey = '__database__version__'
        self._sync_timer = None

        # build the db name (different db for py2 and py3)
        dbname = os.path.join(utils.user_conf_dir,
                              'db_py%d_%s' % (sys.version_info[0], name))
        DBG('Open db: ' + name + ' from file: ' + dbname)

        # check if the db exist (or is the first time we use it)
        first_run = False if glob.glob(dbname + '*') else True

        # open the shelve
        self._sh = shelve.open(dbname)

        if (not first_run) and (version is not None) and (self.get_version() !=
                                                          version):
            # the db is outdated
            text = _(
                '<b>The database %s is outdated!</b><br><br>'
                'The old file has been renamed with a .backup extension and '
                'a new (empty) one has been created.<br><br>Sorry for the incovenience.'
            ) % (name)
            EmcDialog(style='warning', title=_('EpyMC Database'), text=text)

            # close the shelve
            self._sh.close()

            # rename db files to .backup
            for fname in glob.glob(dbname + '*'):
                os.rename(fname, fname + '.backup')

            # reopen a new (empty) shelve
            self._sh = shelve.open(dbname)

        if version is not None:
            # store the version inside the db
            self._sh[self._vkey] = version

        _instances.append(self)
Пример #25
0
class EmcFolderSelector(object):
    """
    Open a dialog that allow the user to choose a path on the filesystem.

    Args:
       title:
          The (optional) dialog title.
       done_cb:
          The (mandatory) function to call when the selection is done.
          Signature: func(path, **kargs)
       **kargs:
          Any other keyword arguments will be passed back in the done_cd func
    """

    def __init__(self, title=None, done_cb=None, **kargs):
        self._user_cb = done_cb
        self._user_kargs = kargs

        self._dialog = EmcDialog(title or _('Source Selector'), style='list')
        self._dialog.button_add(_('Select'), self._btn_select_cb)
        self._dialog.button_add(_('Browse'), self._btn_browse_cb, default=True)

        self.populate_devices()

    def populate_devices(self):
        self._dialog.list_clear()

        # other storage devices
        for dev in storage.list_devices():
            if dev.is_mounted:
                it = self._dialog.list_item_append(dev.label, dev.icon)
                it.data['root'] = it.data['path'] = dev.mount_point
        self._dialog.list_go()

    def populate_folder(self, root, folder):
        if folder == '':  # back in '/'
            self.populate_devices()
            return

        try:
            folders = os.listdir(folder)
        except PermissionError:
            EmcDialog(style='error', text=_('Permission denied'))
            return

        self._dialog.list_clear()

        # back item
        parent = os.path.normpath(os.path.join(folder, '..'))
        it = self._dialog.list_item_append(_('Back'), 'icon/back')
        it.data['root'] = root
        it.data['path'] = parent if parent != folder else ''  # back in '/'

        # folders
        for fname in utils.natural_sort(folders):
            fullpath = os.path.join(folder, fname)
            if fname[0] != '.' and os.path.isdir(fullpath):
                it = self._dialog.list_item_append(fname, 'icon/folder')
                it.data['root'] = root
                it.data['path'] = fullpath

        self._dialog.list_go()

    def _btn_browse_cb(self, btn):
        it = self._dialog.list_item_selected_get()
        if len(it.data['path']) < len(it.data['root']):
            self.populate_devices()
        else:
            self.populate_folder(it.data['root'], it.data['path'])

    def _btn_select_cb(self, btn):
        path = self._dialog.list_item_selected_get().data['path']
        if path and callable(self._user_cb):
            self._user_cb('file://' + path, **self._user_kargs)
        self._dialog.delete()
Пример #26
0
class StdConfigItemNumber(StdConfigItemBase):
    def __init__(self, fmt, udm, min, max, step, *args):
        self._fmt = fmt
        self._udm = udm
        self._min = min
        self._max = max
        self._step = step
        self._val = None
        self._dia = None
        StdConfigItemBase.__init__(self, *args)

    def label_end_get(self, url, user_data):
        if self._udm:
            return ini.get(self._sec, self._opt) + ' ' + self._udm
        else:
            return ini.get(self._sec, self._opt)

    def item_selected(self, url, user_data):
        self._val = ini.get_float(self._sec, self._opt)
        self._dia = EmcDialog(style='minimal', title=self._lbl, text='')
        self._dia.button_add(_('Ok'), self._btn_ok_cb)
        self._dia.button_add(None, self._btn_plus_cb, icon='icon/plus')
        self._dia.button_add(None, self._btn_minus_cb, icon='icon/minus')
        self._dia.button_add(_('Cancel'), self._btn_canc_cb)
        self._dia_text_update()

    def _dia_text_update(self):
        val = (self._fmt % self._val) + ' ' + self._udm
        self._dia.text_set('<br><br><br><center><bigger>%s</bigger></center>' %
                           val)

    def _btn_plus_cb(self, btn):
        self._val += self._step
        self._val = min(self._val, self._max)
        self._dia_text_update()

    def _btn_minus_cb(self, btn):
        self._val -= self._step
        self._val = max(self._val, self._min)
        self._dia_text_update()

    def _btn_canc_cb(self, btn):
        self._dia.delete()

    def _btn_ok_cb(self, btn):
        val = self._fmt % self._val
        ini.set(self._sec, self._opt, val)
        self._dia.delete()
        StdConfigItemBase.__done__(self)
Пример #27
0
class CastPanel(object):
    def __init__(self, pid=None, name=None, lang=DEFAULT_INFO_LANG):
        self.pid = pid
        self.info = None

        tmdb = TMDBv3(lang=lang)
        if name:
            tmdb.person_search(name, self._search_done_cb)
        elif pid:
            tmdb.get_cast_info(self.pid, self._fetch_done_cb)
        self._dia = EmcDialog(style='minimal',
                              title=_('Fetching info'),
                              content='image/tmdb_logo.png',
                              spinner=True)

    def _search_done_cb(self, tmdb, result):
        # TODO manage errors
        self.pid = result['results'][0]['id']
        tmdb.get_cast_info(self.pid, self._fetch_done_cb)

    def _fetch_done_cb(self, tmdb, result):
        self.info = result
        self._dia.delete()

        text = ''
        if self.info.get('biography'):
            text += '%s<br><br>' % self.info['biography'].replace('\n', '<br>')
        if self.info.get('birthday'):
            text += '<name>%s:</name> %s<br>' % (_('Birthday'),
                                                 self.info['birthday'])
        if self.info.get('deathday'):
            text += '<name>%s:</name> %s<br>' % (_('Deathday'),
                                                 self.info['deathday'])
        if self.info.get('place_of_birth'):
            text += '<name>%s:</name> %s<br>' % (_('Place of birth'),
                                                 self.info['place_of_birth'])

        self._dia = EmcDialog(title=self.info['name'],
                              style='panel',
                              content=self.info['profile_path'],
                              text=text)

        # merge cast and crew in a single movies list
        movies = {}  # key: tmdbid val: tmdb-data-dict + 'jobs'
        for movie in self.info['credits']['crew']:
            tid = movie['id']
            if tid in movies:
                movies[tid]['jobs'].append(movie.get('job'))
            else:
                movie['jobs'] = [movie.get('job')]
                movies[tid] = movie

        for movie in self.info['credits']['cast']:
            tid = movie['id']
            if tid in movies:
                movies[tid]['character'] = movie.get('character')
            else:
                movies[tid] = movie

        self._dia.button_add(
            _('Movies (%s)') % len(movies),
            lambda b: self.movies_dialog(movies))
        num = len(self.info['images']['profiles'])
        self._dia.button_add(
            _('Photos (%s)') % num, lambda b: self.photos_dialog())

    def photos_dialog(self):
        dia = EmcDialog(style='image_list_portrait', title=self.info['name'])
        for image in self.info['images']['profiles']:
            dia.list_item_append(None, image['file_path'])
        dia.list_go()

    def movies_dialog(self, movies):
        dia = EmcDialog(style='list', title=self.info['name'])
        for movie in sorted(movies.values(), key=itemgetter('title')):
            label = '<big>{}</big>'.format(movie['title'])
            if 'character' in movie:
                label += ' <i>{} <big>{}</big></i>'.format(
                    _('as'), movie['character'])
            if 'jobs' in movie:
                label += ' <i>({})</i>'.format(', '.join(movie['jobs']))
            dia.list_item_append(label, movie.get('poster_path'))
        dia.list_go()
Пример #28
0
 def photos_dialog(self):
     dia = EmcDialog(style='image_list_portrait', title=self.info['name'])
     for image in self.info['images']['profiles']:
         dia.list_item_append(None, image['file_path'])
     dia.list_go()
Пример #29
0
def _restart_needed():
    EmcDialog(
        style='info',
        title=_('Restart needed'),
        text=_(
            'You need to restart the program to apply the new configuration.'))
Пример #30
0
class KeyboardModule(EmcModule):
    name = 'input_keyb'
    label = _('Input - Keyboard')
    icon = 'icon/keyboard'
    info = _('The keyboard module lets you control the application using '
             'your keyboard, or any other device that act as a keyboard.')

    def __init__(self):
        DBG('Init module')

        self.grab_key_func = None

        # set up default bindings
        section = 'keyboard'
        if not ini.has_section(section):
            ini.add_section(section)
            defs = EmcGui.instance().default_keymap_get()
            for key, event in defs.items():
                ini.set(section, key, event)

        # read mapping from config
        self.keys = dict()
        for key, event in ini.get_options(section):
            DBG('Map key "%s" to event %s' % (key, event))
            self.keys[key] = event

        # add an entry in the config gui section
        config_gui.root_item_add('keyb',
                                 50,
                                 _('Keyboard'),
                                 icon='icon/keyboard',
                                 callback=self.config_panel_cb)

        # ask the gui to forward key events to us
        EmcGui.instance().key_down_connect(self._key_down_cb)

    def __shutdown__(self):
        DBG('Shutdown module')
        config_gui.root_item_del('keyb')
        EmcGui.instance().key_down_connect(None)

    def _key_down_cb(self, key):
        DBG('Key: %s' % key)
        key = key.lower()

        # if grabbed request call the grab function, else emit the signal
        if self.grab_key_func and callable(self.grab_key_func):
            self.grab_key_func(key)
        else:
            try:
                input_events.event_emit(self.keys[key])
            except KeyError:
                print('Not mapped key: ' + key)

    # ---- config panel stuff ----
    class KeyItemClass(EmcItemClass):
        def item_selected(self, url, data):
            key, event, mod = data
            txt = '%s<br><br>%s ⇾ %s' % (
                _('Are you sure you want to remove the mapping?'), key, event)
            EmcDialog(style='yesno',
                      title=_('Remove key'),
                      text=txt,
                      done_cb=self._remove_confirmed,
                      user_data=data)

        def _remove_confirmed(self, dia):
            key, event, mod = dia.data_get()

            # remove key from mapping and from config
            mod.keys.pop(key, None)
            ini.remove_option('keyboard', key)

            # kill the dialog and refresh the browser
            bro = config_gui.browser_get()
            bro.refresh(hard=True)
            dia.delete()

        def label_get(self, url, data):
            key, event, mod = data
            return key

        def label_end_get(self, url, data):
            key, event, mod = data
            return event

        def icon_get(self, url, data):
            return 'icon/key'

    def config_panel_cb(self):
        bro = config_gui.browser_get()
        bro.page_add('config://keyb/', _('Keyboard'), None, self.populate_keyb)

    def populate_keyb(self, browser, url):
        config_gui.standard_item_action_add(_('Add a new key'),
                                            icon='icon/plus',
                                            cb=self._add_item_cb)
        for key, event in sorted(self.keys.items(), key=lambda x: x[1]):
            browser.item_add(self.KeyItemClass(), 'config://keyb/button',
                             (key, event, self))

    def _add_item_cb(self):
        # grab the next pressed key and show the first dialog
        self.grab_key_func = self.grabbed_key_func
        self.dia = EmcDialog(title=_('Add a new key'),
                             style='cancel',
                             text=_('Press a key on your keyboard'),
                             canc_cb=self.ungrab_key)

    def ungrab_key(self, dialog):
        self.grab_key_func = None
        dialog.delete()

    def grabbed_key_func(self, key):
        # ungrab remote keys & delete the first dialog
        self.grab_key_func = None
        self.pressed_key = key
        self.dia.delete()

        # create the dialog to choose the event to bind
        dia = EmcDialog(title=_('Choose an event to bind'),
                        style='list',
                        done_cb=self.event_choosed_cb)
        for event in input_events.STANDARD_EVENTS.split():
            dia.list_item_append(event)
        dia.list_go()

    def event_choosed_cb(self, dia):
        event = str(dia.list_item_selected_get().label)
        key = str(self.pressed_key)

        # save the pressed key in mapping and config
        self.keys[key] = event
        ini.set('keyboard', key, event)

        # kill the dialog and refresh the browser
        dia.delete()
        bro = config_gui.browser_get()
        bro.refresh(hard=True)