1# vim:fileencoding=utf-8
2# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
3from __python__ import bound_methods, hash_literals
4
5from elementmaker import E
6from gettext import gettext as _
7
8from book_list.globals import get_session_data
9from complete import create_search_bar
10from book_list.item_list import create_item, create_item_list
11from dom import clear, svgicon, unique_id
12from read_book.shortcuts import (
13    key_as_text, keyevent_as_shortcut, shortcut_differs, shortcuts_definition,
14    shortcuts_group_desc
15)
16from widgets import create_button
17
18
19def get_container():
20    return document.getElementById(get_container.id)
21
22
23def restore_defaults(close_func):
24    get_container().dataset.changed = 'true'
25    for item in get_container().querySelectorAll('[data-user-data]'):
26        q = JSON.parse(item.dataset.userData)
27        q.shortcuts = shortcuts_definition()[q.name].shortcuts
28        item.dataset.userData = JSON.stringify(q)
29    close_func()
30
31
32def as_groups(shortcuts):
33    ans = {}
34    for sc_name in Object.keys(shortcuts):
35        sc = shortcuts[sc_name]
36        if not ans[sc.group]:
37            ans[sc.group] = {}
38        ans[sc.group][sc_name] = sc
39    return ans
40
41
42def sort_group_key(group, sc_name):
43    return group[sc_name].short.toLowerCase()
44
45
46def sc_as_item(sc_name, sc, shortcuts):
47    cuts = shortcuts or sc.shortcuts
48    return create_item(sc.short, action=customize_shortcut.bind(None, sc_name), subtitle=sc.long, data=JSON.stringify({'name': sc_name, 'shortcuts': cuts}))
49
50
51
52def remove_key(evt):
53    key_container = evt.currentTarget.parentNode.parentNode
54    key_container.parentNode.removeChild(key_container)
55
56
57def key_widget(key):
58    return E.tr(
59        data_shortcut=JSON.stringify(keyevent_as_shortcut(key)),
60        E.td(style="padding: 0.5rem; border-right: solid 3px; margin-right: 0.5rem; margin-top: 0.5rem", key_as_text(key)),
61        E.td(E.a(class_='simple-link', '\xa0', svgicon('remove'), ' ', _('Remove'), onclick=remove_key)),
62    )
63
64
65def close_customize_shortcut(apply_changes):
66    container = get_container()
67    container.firstChild.style.display = 'flex'
68    container.firstChild.nextSibling.style.display = 'block'
69    container.lastChild.style.display = 'none'
70    if close_customize_shortcut.shortcut_being_customized:
71        for item in container.firstChild.querySelectorAll('[data-user-data]'):
72            q = JSON.parse(item.dataset.userData)
73            if q.name is close_customize_shortcut.shortcut_being_customized:
74                item.scrollIntoView()
75                break
76    if apply_changes:
77        shortcuts = v'[]'
78        for x in container.lastChild.querySelectorAll('[data-shortcut]'):
79            sc = JSON.parse(x.dataset.shortcut)
80            shortcuts.push(sc)
81        sc_name = container.lastChild.dataset.scName
82        for item in container.querySelectorAll('[data-user-data]'):
83            q = JSON.parse(item.dataset.userData)
84            if q.name is sc_name:
85                q.shortcuts = shortcuts
86                item.dataset.userData = JSON.stringify(q)
87                break
88        get_container().dataset.changed = 'true'
89        commit_keyboard(create_keyboard_panel.onchange, None)
90
91
92def add_key_widget():
93    uid = unique_id('add-new-shortcut')
94    return E.div(style='margin-top: 1ex; margin-bottom: 1ex',
95        id=uid,
96        E.div(create_button(_('Add a new shortcut'), icon="plus", action=def (evt):
97            div = document.getElementById(uid)
98            div.firstChild.style.display = 'none'
99            div.lastChild.style.display = 'block'
100            div.lastChild.querySelector('input').focus()
101        )),
102        E.div(style='display:none',
103            E.input(readonly='readonly',
104                value=_('Press the key combination to use as a shortcut'),
105                style='width: 90vw',
106                onkeydown=def (evt):
107                    evt.preventDefault()
108                    evt.stopPropagation()
109                    if evt.key not in v"['Control', 'Meta', 'Alt', 'Shift']":
110                        key_con = get_container().querySelector('.key-container')
111                        key_con.appendChild(key_widget(evt))
112                        div = document.getElementById(uid)
113                        div.firstChild.style.display = 'block'
114                        div.lastChild.style.display = 'none'
115        ))
116    )
117
118
119def customize_shortcut(sc_name):
120    container = get_container()
121    container.firstChild.style.display = 'none'
122    container.firstChild.nextSibling.style.display = 'none'
123    container.lastChild.style.display = 'block'
124    close_customize_shortcut.shortcut_being_customized = sc_name
125    shortcuts = v'[]'
126    for item in container.querySelectorAll('[data-user-data]'):
127        q = JSON.parse(item.dataset.userData)
128        if q.name is sc_name:
129            shortcuts = q.shortcuts
130            break
131    container = container.lastChild
132    clear(container)
133    container.dataset.scName = sc_name
134    sc = shortcuts_definition()[sc_name]
135    container.appendChild(E.h4(sc.short))
136    if sc.long:
137        container.appendChild(E.div(sc.long, style='font-style: italic; font-size: smaller; margin-top: 1ex'))
138    container.appendChild(E.div(style='margin-top: 1rem', _('Existing shortcuts:')))
139    key_con = container.appendChild(E.table(class_="key-container"))
140    for key in shortcuts:
141        key_con.appendChild(key_widget(key))
142    container.appendChild(E.div(style='margin-top:1ex;', add_key_widget()))
143    container.appendChild(E.div(style='margin-top:1ex; display:flex; justify-content: flex-end',
144        create_button(_('OK'), action=close_customize_shortcut.bind(None, True)),
145        E.span('\xa0'),
146        create_button(_('Cancel'), action=close_customize_shortcut.bind(None, False)),
147    ))
148
149
150def run_search():
151    container = get_container()
152    query = container.querySelector(f'[name=search-for-sc]').value or ''
153    query = query.toLowerCase()
154    for item in get_container().querySelectorAll('[data-user-data]'):
155        q = item.textContent.toLowerCase()
156        matches = not query or q.indexOf(query) > -1
157        item.style.display = 'list-item' if matches else 'none'
158
159
160def create_keyboard_panel(container, apply_func, cancel_func, onchange):
161    create_keyboard_panel.onchange = onchange
162    container.appendChild(E.div(id=unique_id('keyboard-settings'), style='margin: 1rem'))
163    container = container.lastChild
164    container.dataset.changed = 'false'
165    get_container.id = container.id
166    search_button = create_button(_('Search'), icon='search')
167    sb = create_search_bar(
168        run_search, 'search-for-sc', placeholder=_('Search for shortcut'), button=search_button)
169    container.appendChild(E.div(
170        style='margin-bottom: 1ex; display: flex; width: 100%; justify-content: space-between', sb, E.span('\xa0\xa0'), search_button
171    ))
172    container.firstChild.firstChild.style.flexGrow = '100'
173    container.appendChild(E.div())
174    container.appendChild(E.div(style='display: none'))
175    container = container.firstChild.nextSibling
176    sd = get_session_data()
177    custom_shortcuts = sd.get('keyboard_shortcuts')
178    groups = as_groups(shortcuts_definition())
179    for group_name in Object.keys(groups):
180        container.appendChild(E.h3(style='margin-top: 1ex', shortcuts_group_desc()[group_name]))
181        group = groups[group_name]
182        items = []
183        for sc_name in sorted(Object.keys(group), key=sort_group_key.bind(None, group)):
184            sc = group[sc_name]
185            items.push(sc_as_item(sc_name, sc, custom_shortcuts[sc_name]))
186        container.appendChild(E.div())
187        create_item_list(container.lastChild, items)
188
189    container.appendChild(E.div(
190        style='margin-top: 1rem', create_button(_('Restore defaults'), action=restore_defaults.bind(None, apply_func))
191    ))
192
193
194develop = create_keyboard_panel
195
196
197def shortcuts_differ(a, b):
198    if a.length is not b.length:
199        return True
200    for x, y in zip(a, b):
201        if shortcut_differs(x, y):
202            return True
203    return False
204
205
206def commit_keyboard(onchange, container):
207    sd = get_session_data()
208    vals = {}
209    for item in get_container().querySelectorAll('[data-user-data]'):
210        q = JSON.parse(item.dataset.userData)
211        if shortcuts_differ(q.shortcuts, shortcuts_definition()[q.name].shortcuts):
212            vals[q.name] = q.shortcuts
213    sd.set('keyboard_shortcuts', vals)
214    if container is not None:
215        create_keyboard_panel.onchange = None
216    if get_container().dataset.changed is 'true':
217        onchange()
218