diff --git a/README.html b/README.html index 89736392..e9a184bd 100644 --- a/README.html +++ b/README.html @@ -115,12 +115,13 @@

Controls Note: All windows support changing the volume and muting / unmuting the player (provided that PyRadio is actually connected to a station).

+

Note: All windows - except the Search window - support changing the volume and muting / unmuting the player (provided that PyRadio is actually connected to a station).

Config file Top

PyRadio upon its execution tries to read its configuration file (i.e. ~/.config/pyradio/config). If this file is not found, it will be created. If an error occurs while parsing it, an error message will be displayed and PyRadio will terminate.

The file contains parameters such as the player to use, the playlist to load etc. It is heavily commented (as you can see here), so that manual editing is really easy. The best practice to manually edit this file is executing PyRadio with the -ocd command line option, which will open the configuration directory in your file manager, and then edit it using your preferable text editor.

@@ -234,9 +235,11 @@

MPlayer

Search function Top

On any window presenting a list of items (stations, playlists, themes) a search function is available by pressing “/”.

The Search Window supports normal and extend editing and in session history.

+

One can always get help by pressing the “?” key.

+

The search will be case insensitive under python 3; it will be case sensitive under python 2.

After a search term has been successfully found, next occurrence can be obtained using the “n” key and previous occurrence can be obtained using the “N” key.

Note: Python 2 users are confined in typing ASCII characters only.

-

Note: Currently, the search function is available on the stations’ and playlists’ window only.

+

Note: Currently, the search function is available on the stations’ and playlists’ and themes’ window only.

PyRadio Themes Top

PyRadio comes with 6 preconfigured (hard coded) themes:

    diff --git a/README.md b/README.md index 3bbf7e61..5840e560 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ e Change station's encoding DEL,x Delete selected station - - t T Load theme / Toggle transparency [Valid] [Valid] c Open Configuration window. - - +/ n N Search, go to next / previous result [Valid] [Valid] ? Show keys help [Valid] [Valid] # Redraw window [Valid] [Valid] Esc/q Quit - - @@ -108,7 +109,7 @@ Esc/q/Left/h - Cancel / close windo The same logic applies to all **PyRadio** windows. -**Note:** All windows support changing the volume and muting / unmuting the player (provided that **PyRadio** is actually connected to a station). +**Note:** All windows - except the *Search window* - support changing the volume and muting / unmuting the player (provided that **PyRadio** is actually connected to a station). ## Config file @@ -335,11 +336,15 @@ On any window presenting a list of items (stations, playlists, themes) a **searc The *Search Window* supports normal and extend editing and in session history. +One can always get help by pressing the "**?**" key. + +The search will be case insensitive under **python 3**; it will be case sensitive under **python 2**. + After a search term has been successfully found, next occurrence can be obtained using the "**n**" key and previous occurrence can be obtained using the "**N**" key. **Note:** **Python 2** users are confined in typing ASCII characters only. -**Note:** Currently, the **search function** is available on the stations' and playlists' window only. +**Note:** Currently, the **search function** is available on the stations' and playlists' and themes' window only. ## PyRadio Themes diff --git a/pyradio.1 b/pyradio.1 index 97fd4f0d..ef483f0a 100644 --- a/pyradio.1 +++ b/pyradio.1 @@ -87,7 +87,7 @@ Quit The same logic applies to all \fBpyradio\fR windows. .IP \fBNote: -All windows support changing the volume and muting / unmuting the player (provided that it is actually connected to a station). +All windows - except the \fISearch window\fR - support changing the volume and muting / unmuting the player (provided that \fBpyradio\fR is actually connected to a station). .SH CONFIG FILE \fBpyradio\fR upon its execution tries to read its configuration file (i.e. \fI~/.config/pyradio/config\fR). If this file is not found, it will be created. If an error occurs while parsing it, an error message will be displayed and \fBpyradio\fR will terminate. @@ -348,13 +348,17 @@ On any window presenting a list of items (stations, playlists, themes) a \fBsear The \fISearch Window\fR supports normal and extend editing and in session history. +One can always get help by pressing the "\fI?\fR" key. + +The search will be case insensitive under \fBpython 3\fR; it will be case sensitive under \fBpython 2\fR. + After a search term has been successfully found, next occurrence can be obtained using the "\fIn\fR" key and previous occurrence can be obtained using the "\fIN\fR" key. .IP \fBNote\fR \fBPython 2\fR users are confined in typing ASCII characters only. .IP \fBNote\fR -Currently, the \fBsearch function\fR is available on the stations' and playlists' window only. +Currently, the \fBsearch function\fR is available on the stations', playlists' and themes' window only. .SH PYRADIO THEMES .PP diff --git a/pyradio/radio.py b/pyradio/radio.py index 70be3580..998ee4e9 100644 --- a/pyradio/radio.py +++ b/pyradio/radio.py @@ -71,12 +71,16 @@ class PyRadio(object): we continue playing, otherwise, we stop playback """ active_stations = [ [ '', 0 ], [ '', -1 ] ] + """ Characters to be "ignored" by windows, so that certain + functions still work (like changing volume) """ + _chars_to_bypass = (ord('m'), ord('v'), ord('.'), + ord(','), ord('+'), ord('-'), + ord('?'), ord('#'), curses.KEY_RESIZE) + # Number of stations to change with the page up/down keys pageChange = 5 search = None - _stations_search = None - _playlists_search = None _last_played_station = '' @@ -120,6 +124,7 @@ def __init__(self, pyradio_config, play=False, req_player='', theme=''): self.stdscr = None self.requested_player = req_player self.number_of_items = len(self._cnf.stations) + """ list of functions to open for entering or redisplaying a mode """ self._redisplay = { @@ -157,11 +162,13 @@ def __init__(self, pyradio_config, play=False, req_player='', theme=''): self.ws.FOREIGN_PLAYLIST_COPY_ERROR_MODE: self._print_foreign_playlist_copy_error, self.ws.SEARCH_NORMAL_MODE: self._redisplay_search_show, self.ws.SEARCH_PLAYLIST_MODE: self._redisplay_search_show, + self.ws.SEARCH_THEME_MODE: self._redisplay_search_show, self.ws.THEME_MODE: self._redisplay_theme_mode, self.ws.PLAYLIST_RECOVERY_ERROR_MODE: self._print_playlist_recovery_error, self.ws.ASK_TO_CREATE_NEW_THEME_MODE: self._redisplay_ask_to_create_new_theme, self.ws.SEARCH_HELP_MODE: self._show_search_help, } + """ list of help functions """ self._display_help = { self.ws.NORMAL_MODE: self._show_main_help, @@ -177,10 +184,30 @@ def __init__(self, pyradio_config, play=False, req_player='', theme=''): self.ws.SELECT_ENCODING_MODE: self._show_config_encoding_help, } + """ search classes + 0 - station search + 1 - playlist search + 2 - theme search + """ + self._search_classes = [ None, None, None ] + + """ points to list in which the search will be performed """ + self._search_list = [] + + """ points to _search_classes for each supported mode """ + self._mode_to_search = { + self.ws.NORMAL_MODE: 0, + self.ws.SELECT_STATION_MODE: 0, + self.ws.PLAYLIST_MODE: 1, + self.ws.SELECT_PLAYLIST_MODE: 1, + self.ws.THEME_MODE: 2, + } + # which search mode opens from each allowed mode - self.search_modes = { + self._search_modes = { self.ws.NORMAL_MODE: self.ws.SEARCH_NORMAL_MODE, self.ws.PLAYLIST_MODE: self.ws.SEARCH_PLAYLIST_MODE, + self.ws.THEME_MODE: self.ws.SEARCH_THEME_MODE, } # search modes opened from main windows self.search_main_window_modes = ( @@ -239,31 +266,10 @@ def setup(self, stdscr): except: # no player self.ws.operation_mode = self.ws.NO_PLAYER_ERROR_MODE - if logger.isEnabledFor(logging.DEBUG): - logger.debug('MODE = self.ws.NO_PLAYER_ERROR_MODE') self.stdscr.nodelay(0) self.setupAndDrawScreen() - if self._stations_search is None: - self._stations_search = PyRadioSearch(parent = self.bodyWin, - begin_y = 0, begin_x = 0, - boxed = True, - has_history = True, - box_color = curses.color_pair(5), - caption_color = curses.color_pair(4), - edit_color = curses.color_pair(5), - cursor_color = curses.color_pair(6)) - if self._playlists_search is None: - self._playlists_search = PyRadioSearch(parent = self.bodyWin, - begin_y = 0, begin_x = 0, - boxed = True, - has_history = True, - box_color = curses.color_pair(5), - caption_color = curses.color_pair(4), - edit_color = curses.color_pair(5), - cursor_color = curses.color_pair(6)) - self.search = self._stations_search # position playlist in window self.bodyMaxY, self.bodyMaxX = self.bodyWin.getmaxyx() if self.selections[self.ws.PLAYLIST_MODE][0] < self.bodyMaxY - 2: @@ -501,6 +507,9 @@ def run(self): lambda: self.stop_update_notification_thread)) self._update_notification_thread.start() + """ set up stations search """ + self._give_me_a_search_class(self.ws.operation_mode) + #signal.signal(signal.SIGINT, self.ctrl_c_handler) self.log.write('Selected player: {}'.format(self._format_player_string()), help_msg=True) #self.log.write_right('Press ? for help') @@ -535,6 +544,21 @@ def run(self): self.ctrl_c_handler(0, 0) break + def _give_me_a_search_class(self, operation_mode): + """ get a search class for a givven operation mode + the class is returned in self.search + """ + if self._search_classes[self._mode_to_search[operation_mode]] is None: + self._search_classes[self._mode_to_search[operation_mode]] = PyRadioSearch(parent = self.bodyWin, + begin_y = 0, begin_x = 0, + boxed = True, + has_history = True, + box_color = curses.color_pair(5), + caption_color = curses.color_pair(4), + edit_color = curses.color_pair(5), + cursor_color = curses.color_pair(6)) + self.search = self._search_classes[self._mode_to_search[operation_mode]] + def ctrl_c_handler(self, signum, frame): self.ctrl_c_pressed = True if self._cnf.dirty_playlist: @@ -776,6 +800,7 @@ def _show_theme_selector(self, changed_from_config=False): self.jumpnr = '' self._random_requested = False self._theme_selector = None + self._give_me_a_search_class(self.ws.THEME_MODE) #if logger.isEnabledFor(logging.ERROR): # logger.error('DE\n\nself._theme = {0}\nself._theme_name = {1}\nself._cnf.theme = {2}\n\n'.format(self._theme, self._theme_name, self._cnf.theme)) self._theme_selector = PyRadioThemeSelector(self.bodyWin, self._cnf, self._theme, @@ -1028,6 +1053,7 @@ def _show_theme_help(self): Enter|,|Right|,|l |Apply selected theme. Space |Apply theme and make it default. s |Make theme default and close window. + /| / |n| / |N |Search, go to next / previous result. Esc|,|q|,|Left|,|h |Close window. %_Player Keys_ -|/|+| or |,|/|. |Change volume. @@ -1456,7 +1482,7 @@ def _open_playlist(self): self._show_help(txt, self.ws.NORMAL_MODE, caption=' ', prompt=' ', is_message=True) self.selections[self.ws.operation_mode] = [self.selection, self.startPos, self.playing, self._cnf.stations] self.ws.window_mode = self.ws.PLAYLIST_MODE - self.search = self._playlists_search + self._give_me_a_search_class(self.ws.PLAYLIST_MODE) self.selection, self.startPos, self.playing, self.stations = self.selections[self.ws.operation_mode] self.number_of_items, self.playing = self.readPlaylists() self.stations = self._cnf.playlists @@ -1727,11 +1753,31 @@ def create_tadays_date_file(a_path): delay(60, stop) def is_search_mode(self, a_mode): - for it in self.search_modes.items(): + for it in self._search_modes.items(): if it[1] == a_mode: return True return False + def _apply_search_result(self, ret, reapply=False): + def _apply_main_windows(ret): + self.setStation(ret) + self._put_selection_in_the_middle(force=True) + if reapply: + if self.ws.operation_mode in \ + [ self._mode_to_search[x] for x in self._mode_to_search.keys() ]: + _apply_main_windows(ret) + elif self.ws.operation_mode == self.ws.THEME_MODE: + self._theme_selector.set_theme(self._theme_selector._themes[ret]) + self.refreshBody() + + else: + if self.ws.operation_mode in self.search_main_window_modes: + _apply_main_windows(ret) + elif self.ws.previous_operation_mode == self.ws.THEME_MODE: + self._theme_selector.set_theme(self._theme_selector._themes[ret]) + self.ws.close_window() + self.refreshBody() + def keypress(self, char): #if logger.isEnabledFor(logging.ERROR): # logger.error('DE {}'.format(self.ws._dq)) @@ -1773,9 +1819,7 @@ def keypress(self, char): return if self.ws.operation_mode == self.ws.CONFIG_MODE: - if char not in (ord('m'), ord('v'), ord('.'), - ord(','), ord('+'), ord('-'), - ord('?'), ord('#'), curses.KEY_RESIZE): + if char not in self._chars_to_bypass: if char in (ord('r'), ord('d')): self._player_select_win = None self._encoding_select_win = None @@ -1897,9 +1941,7 @@ def keypress(self, char): return elif self.ws.operation_mode == self.ws.SELECT_PLAYER_MODE: - if char not in (ord('m'), ord('v'), ord('.'), - ord(','), ord('+'), ord('-'), - ord('?'), ord('#'), curses.KEY_RESIZE): + if char not in self._chars_to_bypass: ret, ret_list = self._player_select_win.keypress(char) if ret >= 0: if ret == 0: @@ -1913,9 +1955,7 @@ def keypress(self, char): elif self.ws.operation_mode == self.ws.SELECT_STATION_ENCODING_MODE: """ select station's encoding from main window """ - if char not in (ord('m'), ord('v'), ord('.'), - ord(','), ord('+'), ord('-'), - ord('?'), ord('#'), curses.KEY_RESIZE): + if char not in self._chars_to_bypass: ret, ret_encoding = self._encoding_select_win.keypress(char) if ret >= 0: if ret == 0: @@ -1940,9 +1980,7 @@ def keypress(self, char): elif self.ws.operation_mode == self.ws.SELECT_ENCODING_MODE: """ select global encoding from config window """ - if char not in (ord('m'), ord('v'), ord('.'), - ord(','), ord('+'), ord('-'), - ord('?'), ord('#'), curses.KEY_RESIZE): + if char not in self._chars_to_bypass: ret, ret_encoding = self._encoding_select_win.keypress(char) if ret >= 0: if ret == 0: @@ -1954,9 +1992,7 @@ def keypress(self, char): return elif self.ws.operation_mode == self.ws.SELECT_PLAYLIST_MODE: - if char not in (ord('m'), ord('v'), ord('.'), - ord(','), ord('+'), ord('-'), - ord('?'), ord('#'), curses.KEY_RESIZE): + if char not in self._chars_to_bypass: ret, ret_playlist = self._playlist_select_win.keypress(char) if ret >= 0: if ret == 0: @@ -1972,9 +2008,7 @@ def keypress(self, char): return elif self.ws.operation_mode == self.ws.SELECT_STATION_MODE: - if char not in (ord('m'), ord('v'), ord('.'), - ord(','), ord('+'), ord('-'), - ord('?'), ord('#'), curses.KEY_RESIZE): + if char not in self._chars_to_bypass: ret, ret_station = self._station_select_win.keypress(char) if ret >= 0: if ret == 0: @@ -2008,9 +2042,8 @@ def keypress(self, char): return elif self.ws.operation_mode == self.ws.THEME_MODE: - if char not in (ord('m'), ord('v'), ord('.'), - ord(','), ord('+'), ord('-'), ord('T'), - ord('?'), ord('#'), curses.KEY_RESIZE): + if char not in self._chars_to_bypass and \ + char not in (ord('T'), ord('/'), ord('n'), ord('N'), ): theme_id, save_theme = self._theme_selector.keypress(char) #if self._cnf.theme_not_supported: @@ -2077,78 +2110,85 @@ def keypress(self, char): """ if no player, don't serve keyboard """ return - elif char in (ord('/'), ) and \ - self.ws.operation_mode in self.search_modes.keys(): + elif char in (ord('/'), ) and self.ws.operation_mode in self._search_modes.keys(): self.jumpnr = '' self._random_requested = False - if self.ws.operation_mode in self.search_modes.keys(): - #self.search.string = '부' - if self.search.string: - tmp_string = self.search.string - logger.error('DE tmp_string = ' + tmp_string) - else: - tmp_string = '' - self.search.show(self.bodyWin) - if tmp_string: - self.search.string = tmp_string - self.ws.operation_mode = self.search_modes[self.ws.operation_mode] + #self.search.string = '부' + self.search.show(self.bodyWin) + self.ws.operation_mode = self._search_modes[self.ws.operation_mode] return elif char in (ord('n'), ) and \ - self.ws.operation_mode in self.search_modes.keys(): - self.jumpnr = '' - self._random_requested = False + self.ws.operation_mode in self._search_modes.keys(): + logger.error('DE n operation_mode = {}'.format(self.ws.operation_mode)) + self._give_me_a_search_class(self.ws.operation_mode) + if self.ws.operation_mode == self.ws.NORMAL_MODE: + self.jumpnr = '' + self._random_requested = False """ search forward """ - if self.search.string: + if self.ws.operation_mode in \ + ( self.ws.NORMAL_MODE, self.ws.PLAYLIST_MODE ): + self._search_list = self.stations sel = self.selection + 1 - if sel == len(self.stations): + elif self.ws.operation_mode == self.ws.THEME_MODE: + self._search_list = self._theme_selector._themes + sel = self._theme_selector.selection + 1 + if self.search.string: + if sel == len(self._search_list): sel = 0 - ret = self.search.get_next(self.stations, sel) + ret = self.search.get_next(self._search_list, sel) if ret is not None: - self.setStation(ret) - self._put_selection_in_the_middle(force=True) - self.refreshBody() + self._apply_search_result(ret, reapply=True) else: curses.ungetch('/') return elif char in (ord('N'), ) and \ - self.ws.operation_mode in self.search_modes.keys(): - self.jumpnr = '' - self._random_requested = False + self.ws.operation_mode in self._search_modes.keys(): + self._give_me_a_search_class(self.ws.operation_mode) + if self.ws.operation_mode == self.ws.NORMAL_MODE: + self.jumpnr = '' + self._random_requested = False """ search backwards """ - if self.search.string: + if self.ws.operation_mode in \ + ( self.ws.NORMAL_MODE, self.ws.PLAYLIST_MODE ): + self._search_list = self.stations sel = self.selection - 1 + elif self.ws.operation_mode == self.ws.THEME_MODE: + self._search_list = self._theme_selector._themes + sel = self._theme_selector.selection - 1 + if self.search.string: if sel < 0: - sel = len(self.stations) - 1 - ret = self.search.get_previous(self.stations, sel) + sel = len(self._search_list) - 1 + ret = self.search.get_previous(self._search_list, sel) if ret is not None: - self.setStation(ret) - self._put_selection_in_the_middle(force=True) - self.refreshBody() + self._apply_search_result(ret, reapply=True) else: curses.ungetch('/') return - elif self.ws.operation_mode in self.search_main_window_modes: - self._random_requested = False + elif self.ws.operation_mode in \ + [ self._search_modes[x] for x in self._search_modes.keys()]: + # serve search results ret = self.search.keypress(self.search._edit_win, char) if ret == 0: + if self.ws.operation_mode in self.search_main_window_modes: + self._search_list = self.stations + sel = self.selection + 1 + elif self.ws.previous_operation_mode == self.ws.THEME_MODE: + self._search_list = self._theme_selector._themes + sel = self._theme_selector.selection + 1 + # perform search - #self.ws.close_window() - #self.refreshBody() - sel = self.selection + 1 - if sel == len(self.stations): + if sel == len(self._search_list): sel = 0 - ret = self.search.get_next(self.stations, sel) + ret = self.search.get_next(self._search_list, sel) + logger.error('DE ret = {}'.format(ret)) if ret is None: if self.search.string: self.search.print_not_found() else: - self.setStation(ret) - self._put_selection_in_the_middle(force=True) - self.ws.close_window() - self.refreshBody() + self._apply_search_result(ret) elif ret == 2: # display help self._show_search_help() @@ -2436,7 +2476,7 @@ def keypress(self, char): self.jumpnr = '' self.selections[self.ws.operation_mode] = [self.selection, self.startPos, self.playing, self._cnf.playlists] self.ws.close_window() - self.search = self._stations_search + self._give_me_a_search_class(self.ws.operation_mode) self.selection, self.startPos, self.playing, self.stations = self.selections[self.ws.operation_mode] self.stations = self._cnf.stations self.number_of_items = len(self.stations) @@ -2652,7 +2692,7 @@ def keypress(self, char): self.ws.close_window() self.selection, self.startPos, self.playing, self.stations = self.selections[self.ws.operation_mode] self._align_stations_and_refresh(self.ws.PLAYLIST_MODE) - self.search = self._stations_search + self._give_me_a_search_class(self.ws.operation_mode) if self.playing < 0: self._put_selection_in_the_middle(force=True) self.refreshBody() diff --git a/pyradio/simple_curses_widgets.py b/pyradio/simple_curses_widgets.py index 1560c7df..78bd1eac 100644 --- a/pyradio/simple_curses_widgets.py +++ b/pyradio/simple_curses_widgets.py @@ -167,7 +167,7 @@ def _prepare_to_show(self): else: self._max_width = maxX - def refreshEditWindow(self): + def refreshEditWindow(self, opening=False): if self.focused: active_edit_color = self.edit_color active_cursor_color = self.cursor_color @@ -176,10 +176,13 @@ def refreshEditWindow(self): active_cursor_color = self.caption_color self._edit_win.erase() #self._edit_win.bkgd('-', curses.A_REVERSE) - if self._string: - self._edit_win.addstr(0, 0, self._string, active_edit_color) - else: + if opening: self._curs_pos = 0 + else: + if self._string: + self._edit_win.addstr(0, 0, self._string, active_edit_color) + else: + self._curs_pos = 0 if self.log is not None: self.log(' - curs_pos = {}\n'.format(self._curs_pos)) self._edit_win.chgat(0, self._curs_pos, 1, active_cursor_color) @@ -205,7 +208,7 @@ def show(self, parent_win, new_y=-1, new_x=-1): if self._boxed: self._caption_win.box() self._caption_win.refresh() - self.refreshEditWindow() + self.refreshEditWindow(opening=True) def keypress(self, win, char): """ diff --git a/pyradio/themes.py b/pyradio/themes.py index 22ef4dc3..583adc00 100644 --- a/pyradio/themes.py +++ b/pyradio/themes.py @@ -778,7 +778,7 @@ def keypress(self, char): return -2, False else: pass - elif char in (ord('n'), ): + elif char in (ord('a'), ): # new theme pass elif char in (curses.KEY_ENTER, ord('\n'), diff --git a/pyradio/window_stack.py b/pyradio/window_stack.py index 12ea8c8a..5488e806 100644 --- a/pyradio/window_stack.py +++ b/pyradio/window_stack.py @@ -13,14 +13,15 @@ class Window_Stack_Constants(object): PLAYLIST_MODE = 1 SEARCH_NORMAL_MODE = 2 SEARCH_PLAYLIST_MODE = 3 - CONFIG_MODE = 4 - SELECT_PLAYER_MODE = 5 - SELECT_ENCODING_MODE = 6 - SELECT_PLAYLIST_MODE = 7 - SELECT_STATION_MODE = 8 - SELECT_STATION_ENCODING_MODE = 9 - NEW_THEME_MODE = 10 - EDIT_THEME_MODE = 11 + SEARCH_THEME_MODE = 4 + CONFIG_MODE = 5 + SELECT_PLAYER_MODE = 6 + SELECT_ENCODING_MODE = 7 + SELECT_PLAYLIST_MODE = 8 + SELECT_STATION_MODE = 9 + SELECT_STATION_ENCODING_MODE = 10 + NEW_THEME_MODE = 11 + EDIT_THEME_MODE = 12 REMOVE_STATION_MODE = 50 SAVE_PLAYLIST_MODE = 51 ASK_TO_SAVE_PLAYLIST_WHEN_OPENING_PLAYLIST_MODE = 52 @@ -62,6 +63,7 @@ class Window_Stack_Constants(object): PLAYLIST_MODE: 'PLAYLIST_MODE', SEARCH_NORMAL_MODE: 'SEARCH_NORMAL_MODE', SEARCH_PLAYLIST_MODE: 'SEARCH_PLAYLIST_MODE', + SEARCH_THEME_MODE: 'SEARCH_THEME_MODE', CONFIG_MODE: 'CONFIG_MODE', SELECT_PLAYER_MODE: 'SELECT_PLAYER_MODE', SELECT_ENCODING_MODE: 'SELECT_ENCODING_MODE', @@ -179,7 +181,10 @@ def window_mode(self, a_mode): @property def previous_operation_mode(self): - return self._dq[-2][0] + try: + return self._dq[-2][0] + except: + return -2 @previous_operation_mode.setter def previous_open_window(self, a_mode):