summaryrefslogtreecommitdiff
path: root/poezio/core/core.py
diff options
context:
space:
mode:
Diffstat (limited to 'poezio/core/core.py')
-rw-r--r--poezio/core/core.py302
1 files changed, 85 insertions, 217 deletions
diff --git a/poezio/core/core.py b/poezio/core/core.py
index b708e2cf..814f7813 100644
--- a/poezio/core/core.py
+++ b/poezio/core/core.py
@@ -44,6 +44,7 @@ from poezio.theming import get_theme
from poezio import keyboard
from poezio.core.completions import CompletionCore
+from poezio.core.tabs import Tabs
from poezio.core.commands import CommandCore
from poezio.core.handlers import HandlerCore
from poezio.core.structs import POSSIBLE_SHOW, DEPRECATED_ERRORS, \
@@ -77,7 +78,8 @@ class Core(object):
self.bookmarks = BookmarkList()
self.debug = False
self.remote_fifo = None
- self.avatar_cache = FileSystemPerJidCache(CACHE_DIR, 'avatars', binary=True)
+ self.avatar_cache = FileSystemPerJidCache(
+ CACHE_DIR, 'avatars', binary=True)
# a unique buffer used to store global information
# that are displayed in almost all tabs, in an
# information window.
@@ -93,8 +95,7 @@ class Core(object):
self.xml_tab = None
self.xml_buffer = TextBuffer()
- self.tabs = []
- self._current_tab_nb = 0
+ self.tabs = Tabs()
self.previous_tab_nb = 0
own_nick = config.get('default_nick')
@@ -397,8 +398,7 @@ class Core(object):
Called when the option create_gaps is changed.
Remove all gaptabs if switching from gaps to nogaps.
"""
- if value.lower() == "false":
- self.tabs = [tab for tab in self.tabs if tab]
+ self.tabs.update_gaps(value.lower() == "false")
def on_request_receipts_config_change(self, option, value):
"""
@@ -662,7 +662,7 @@ class Core(object):
except ValueError:
pass
else:
- if self.current_tab().nb == nb and config.get(
+ if self.tabs.current_tab.nb == nb and config.get(
'go_to_previous_tab_on_alt_number'):
self.go_to_previous_tab()
else:
@@ -687,8 +687,9 @@ class Core(object):
ok = ok and config.silent_set('info_win_height',
self.information_win_size, 'var')
if not ok:
- self.information('Unable to save runtime preferences'
- ' in the config file', 'Error')
+ self.information(
+ 'Unable to save runtime preferences'
+ ' in the config file', 'Error')
def on_roster_enter_key(self, roster_row):
"""
@@ -715,9 +716,9 @@ class Core(object):
Messages are namedtuples of the form
('txt nick_color time str_time nickname user')
"""
- if not isinstance(self.current_tab(), tabs.ChatTab):
+ if not isinstance(self.tabs.current_tab, tabs.ChatTab):
return None
- return self.current_tab().get_conversation_messages()
+ return self.tabs.current_tab.get_conversation_messages()
def insert_input_text(self, text):
"""
@@ -784,8 +785,9 @@ class Core(object):
'Could not open the fifo for writing (%s)',
os.path.join(fifo_path, './', 'poezio.fifo'),
exc_info=True)
- self.information('Could not open the fifo '
- 'file for writing: %s' % exc, 'Error')
+ self.information(
+ 'Could not open the fifo '
+ 'file for writing: %s' % exc, 'Error')
return
args = (pipes.quote(arg.replace('\n', ' ')) for arg in command)
@@ -829,7 +831,7 @@ class Core(object):
keyboard.continuation_keys_callback = None
cb(key)
else:
- self.current_tab().on_input(key, raw)
+ self.tabs.current_tab.on_input(key, raw)
def try_execute(self, line):
"""
@@ -837,7 +839,7 @@ class Core(object):
"""
line = '/' + line
try:
- self.current_tab().execute_command(line)
+ self.tabs.current_tab.execute_command(line)
except:
log.error('Execute failed (%s)', line, exc_info=True)
@@ -872,8 +874,9 @@ class Core(object):
msg = msg.replace('\n', '|') if msg else ''
ok = ok and config.silent_set('status_message', msg)
if not ok:
- self.information('Unable to save the status in '
- 'the config file', 'Error')
+ self.information(
+ 'Unable to save the status in '
+ 'the config file', 'Error')
def get_bookmark_nickname(self, room_name):
"""
@@ -909,9 +912,9 @@ class Core(object):
conversation.
Returns False if the current tab is not a conversation tab
"""
- if not isinstance(self.current_tab(), tabs.ChatTab):
+ if not isinstance(self.tabs.current_tab, tabs.ChatTab):
return False
- self.current_tab().command_say(msg)
+ self.tabs.current_tab.command_say(msg)
return True
def invite(self, jid, room, reason=None):
@@ -977,15 +980,8 @@ class Core(object):
def get_tabs(self, cls=None):
"Get all the tabs of a type"
if cls is None:
- cls = tabs.Tab
- return [tab for tab in self.tabs if isinstance(tab, cls)]
-
- def current_tab(self):
- """
- returns the current room, the one we are viewing
- """
- self.current_tab_nb = self.current_tab_nb
- return self.tabs[self.current_tab_nb]
+ return self.tabs.get_tabs()
+ return self.tabs.by_class(cls)
def get_conversation_by_jid(self, jid, create=True, fallback_barejid=True):
"""
@@ -999,16 +995,16 @@ class Core(object):
jid = safeJID(jid)
# We first check if we have a static conversation opened
# with this precise resource
- conversation = self.get_tab_by_name(jid.full,
- tabs.StaticConversationTab)
+ conversation = self.tabs.by_name_and_class(jid.full,
+ tabs.StaticConversationTab)
if jid.bare == jid.full and not conversation:
- conversation = self.get_tab_by_name(jid.full,
- tabs.DynamicConversationTab)
+ conversation = self.tabs.by_name_and_class(
+ jid.full, tabs.DynamicConversationTab)
if not conversation and fallback_barejid:
# If not, we search for a conversation with the bare jid
- conversation = self.get_tab_by_name(jid.bare,
- tabs.DynamicConversationTab)
+ conversation = self.tabs.by_name_and_class(
+ jid.bare, tabs.DynamicConversationTab)
if not conversation:
if create:
# We create a dynamic conversation with the bare Jid if
@@ -1020,23 +1016,6 @@ class Core(object):
conversation = None
return conversation
- def get_tab_by_name(self, name, typ=None):
- """
- Get the tab with the given name.
- If typ is provided, return a tab of this type only
- """
- for tab in self.tabs:
- if tab.name == name:
- if (typ and isinstance(tab, typ)) or\
- not typ:
- return tab
- return None
-
- def get_tab_by_number(self, number):
- if 0 <= number < len(self.tabs):
- return self.tabs[number]
- return None
-
def add_tab(self, new_tab, focus=False):
"""
Appends the new_tab in the tab list and
@@ -1046,82 +1025,12 @@ class Core(object):
if focus:
self.command.win("%s" % new_tab.nb)
- def insert_tab_nogaps(self, old_pos, new_pos):
- """
- Move tabs without creating gaps
- old_pos: old position of the tab
- new_pos: desired position of the tab
- """
- tab = self.tabs[old_pos]
- if new_pos < old_pos:
- self.tabs.pop(old_pos)
- self.tabs.insert(new_pos, tab)
- elif new_pos > old_pos:
- self.tabs.insert(new_pos, tab)
- self.tabs.remove(tab)
- else:
- return False
- return True
-
- def insert_tab_gaps(self, old_pos, new_pos):
- """
- Move tabs and create gaps in the eventual remaining space
- old_pos: old position of the tab
- new_pos: desired position of the tab
- """
- tab = self.tabs[old_pos]
- target = None if new_pos >= len(self.tabs) else self.tabs[new_pos]
- if not target:
- if new_pos < len(self.tabs):
- old_tab = self.tabs[old_pos]
- self.tabs[new_pos], self.tabs[old_pos] = old_tab, tabs.GapTab(
- self)
- else:
- self.tabs.append(self.tabs[old_pos])
- self.tabs[old_pos] = tabs.GapTab(self)
- else:
- if new_pos > old_pos:
- self.tabs.insert(new_pos, tab)
- self.tabs[old_pos] = tabs.GapTab(self)
- elif new_pos < old_pos:
- self.tabs[old_pos] = tabs.GapTab(self)
- self.tabs.insert(new_pos, tab)
- else:
- return False
- i = self.tabs.index(tab)
- done = False
- # Remove the first Gap on the right in the list
- # in order to prevent global shifts when there is empty space
- while not done:
- i += 1
- if i >= len(self.tabs):
- done = True
- elif not self.tabs[i]:
- self.tabs.pop(i)
- done = True
- # Remove the trailing gaps
- i = len(self.tabs) - 1
- while isinstance(self.tabs[i], tabs.GapTab):
- self.tabs.pop()
- i -= 1
- return True
-
def insert_tab(self, old_pos, new_pos=99999):
"""
Insert a tab at a position, changing the number of the following tabs
returns False if it could not move the tab, True otherwise
"""
- if old_pos <= 0 or old_pos >= len(self.tabs):
- return False
- elif new_pos <= 0:
- return False
- elif new_pos == old_pos:
- return False
- elif not self.tabs[old_pos]:
- return False
- if config.get('create_gaps'):
- return self.insert_tab_gaps(old_pos, new_pos)
- return self.insert_tab_nogaps(old_pos, new_pos)
+ self.tabs.insert_tab(old_pos, new_pos, config.get('create_gaps'))
### Move actions (e.g. go to next room) ###
@@ -1129,22 +1038,18 @@ class Core(object):
"""
rotate the rooms list to the right
"""
- self.current_tab().on_lose_focus()
- self.current_tab_nb += 1
- while not self.tabs[self.current_tab_nb]:
- self.current_tab_nb += 1
- self.current_tab().on_gain_focus()
+ self.tabs.current_tab.on_lose_focus()
+ self.tabs.next()
+ self.tabs.current_tab.on_gain_focus()
self.refresh_window()
def rotate_rooms_left(self, args=None):
"""
rotate the rooms list to the right
"""
- self.current_tab().on_lose_focus()
- self.current_tab_nb -= 1
- while not self.tabs[self.current_tab_nb]:
- self.current_tab_nb -= 1
- self.current_tab().on_gain_focus()
+ self.tabs.current_tab.on_lose_focus()
+ self.tabs.prev()
+ self.tabs.current_tab.on_gain_focus()
self.refresh_window()
def go_to_room_number(self):
@@ -1190,7 +1095,7 @@ class Core(object):
priority = tabs.STATE_PRIORITY
tab_refs = {}
# put all the active tabs in a dict of lists by state
- for tab in self.tabs:
+ for tab in self.tabs.get_tabs():
if not tab:
continue
if tab.state not in tab_refs:
@@ -1204,8 +1109,8 @@ class Core(object):
for state in states:
for tab in tab_refs[state]:
- if (tab.nb < self.current_tab_nb
- and tab_refs[state][-1].nb > self.current_tab_nb):
+ if (tab.nb < self.tabs.current_tab_intex and
+ tab_refs[state][-1].nb > self.tabs.current_tab_index):
continue
self.command.win(str(tab.nb))
return
@@ -1213,33 +1118,14 @@ class Core(object):
def focus_tab_named(self, tab_name, type_=None):
"""Returns True if it found a tab to focus on"""
- for tab in self.tabs:
- if tab.name == tab_name:
- if (type_ and (isinstance(tab, type_))) or not type_:
- self.command.win(str(tab.nb))
- return True
- return False
-
- @property
- def current_tab_nb(self):
- """Wrapper for the current tab number"""
- return self._current_tab_nb
-
- @current_tab_nb.setter
- def current_tab_nb(self, value):
- """
- Prevents the tab number from going over the total number of opened
- tabs, or under 0
- """
- old = self._current_tab_nb
- if value >= len(self.tabs):
- self._current_tab_nb = 0
- elif value < 0:
- self._current_tab_nb = len(self.tabs) - 1
+ if type_ is None:
+ tab = self.tabs.by_name(tab_name)
else:
- self._current_tab_nb = value
- if old != self._current_tab_nb and self.tabs[self._current_tab_nb]:
- self.events.trigger('tab_change', old, self._current_tab_nb)
+ tab = self.tabs.by_name_and_class(tab_name, type_)
+ if tab:
+ self.command_win(str(tab.nb))
+ return True
+ return False
### Opening actions ###
@@ -1270,7 +1156,7 @@ class Core(object):
self.command.win(str(tab.nb))
return tab
# create the new tab
- tab = self.get_tab_by_name(room_name, tabs.MucTab)
+ tab = self.tabs.by_name_and_class(room_name, tabs.MucTab)
if not tab:
return None
new_tab = tabs.PrivateTab(self, complete_jid, tab.own_nick)
@@ -1310,8 +1196,8 @@ class Core(object):
this updates the name of all the opened private conversations
with him/her
"""
- tab = self.get_tab_by_name('%s/%s' % (room_name, old_nick),
- tabs.PrivateTab)
+ tab = self.tabs.by_name_and_class('%s/%s' % (room_name, old_nick),
+ tabs.PrivateTab)
if tab:
tab.rename_user(old_nick, user)
@@ -1321,8 +1207,8 @@ class Core(object):
The user left the MUC: add a message in the associated
private conversation
"""
- tab = self.get_tab_by_name('%s/%s' % (room_name, user.nick),
- tabs.PrivateTab)
+ tab = self.tabs.by_name_and_class('%s/%s' % (room_name, user.nick),
+ tabs.PrivateTab)
if tab:
tab.user_left(status_message, user)
@@ -1331,8 +1217,8 @@ class Core(object):
The user joined a MUC: add a message in the associated
private conversation
"""
- tab = self.get_tab_by_name('%s/%s' % (room_name, nick),
- tabs.PrivateTab)
+ tab = self.tabs.by_name_and_class('%s/%s' % (room_name, nick),
+ tabs.PrivateTab)
if tab:
tab.user_rejoined(nick)
@@ -1357,7 +1243,7 @@ class Core(object):
tab.activate(reason=reason)
def on_user_changed_status_in_private(self, jid, status):
- tab = self.get_tab_by_name(jid, tabs.ChatTab)
+ tab = self.tabs.by_name_and_class(jid, tabs.ChatTab)
if tab is not None: # display the message in private
tab.update_status(status)
@@ -1367,35 +1253,16 @@ class Core(object):
"""
was_current = tab is None
if tab is None:
- tab = self.current_tab()
+ tab = self.tabs.current_tab
if isinstance(tab, tabs.RosterInfoTab):
return # The tab 0 should NEVER be closed
tab.on_close()
del tab.key_func # Remove self references
del tab.commands # and make the object collectable
- nb = tab.nb
- if was_current:
- if self.previous_tab_nb != nb:
- self.current_tab_nb = self.previous_tab_nb
- self.previous_tab_nb = 0
- if config.get('create_gaps'):
- if nb >= len(self.tabs) - 1:
- self.tabs.remove(tab)
- nb -= 1
- while not self.tabs[nb]: # remove the trailing gaps
- self.tabs.pop()
- nb -= 1
- else:
- self.tabs[nb] = tabs.GapTab(self)
- else:
- self.tabs.remove(tab)
+ self.tabs.delete(tab, gap=config.get('create_gaps'))
logger.close(tab.name)
- if self.current_tab_nb >= len(self.tabs):
- self.current_tab_nb = len(self.tabs) - 1
- while not self.tabs[self.current_tab_nb]:
- self.current_tab_nb -= 1
if was_current:
- self.current_tab().on_gain_focus()
+ self.tabs.current_tab.on_gain_focus()
self.refresh_window()
import gc
gc.collect()
@@ -1408,10 +1275,10 @@ class Core(object):
Search for a ConversationTab with the given jid (full or bare),
if yes, add the given message to it
"""
- tab = self.get_tab_by_name(jid, tabs.ConversationTab)
+ tab = self.tabs.by_name_and_class(jid, tabs.ConversationTab)
if tab is not None:
tab.add_message(msg, typ=2)
- if self.current_tab() is tab:
+ if self.tabs.current_tab is tab:
self.refresh_window()
####################### Curses and ui-related stuff ###########################
@@ -1444,7 +1311,7 @@ class Core(object):
nb_lines = self.information_buffer.add_message(
msg, nickname=typ, nick_color=color)
popup_on = config.get('information_buffer_popup_on').split()
- if isinstance(self.current_tab(), tabs.RosterInfoTab):
+ if isinstance(self.tabs.current_tab, tabs.RosterInfoTab):
self.refresh_window()
elif typ != '' and typ.lower() in popup_on:
popup_time = config.get('popup_time') + (nb_lines - 1) * 2
@@ -1452,7 +1319,7 @@ class Core(object):
else:
if self.information_win_size != 0:
self.information_win.refresh()
- self.current_tab().refresh_input()
+ self.tabs.current_tab.refresh_input()
return True
def _init_curses(self, stdscr):
@@ -1486,8 +1353,8 @@ class Core(object):
Refresh everything
"""
nocursor = curses.curs_set(0)
- self.current_tab().state = 'current'
- self.current_tab().refresh()
+ self.tabs.current_tab.state = 'current'
+ self.tabs.current_tab.refresh()
self.doupdate()
curses.curs_set(nocursor)
@@ -1495,7 +1362,7 @@ class Core(object):
"""
Refresh the window containing the tab list
"""
- self.current_tab().refresh_tab_win()
+ self.tabs.current_tab.refresh_tab_win()
self.refresh_input()
self.doupdate()
@@ -1503,8 +1370,8 @@ class Core(object):
"""
Refresh the input if it exists
"""
- if self.current_tab().input:
- self.current_tab().input.refresh()
+ if self.tabs.current_tab.input:
+ self.tabs.current_tab.input.refresh()
self.doupdate()
def scroll_page_down(self):
@@ -1512,7 +1379,7 @@ class Core(object):
Scroll a page down, if possible.
Returns True on success, None on failure.
"""
- if self.current_tab().on_scroll_down():
+ if self.tabs.current_tab.on_scroll_down():
self.refresh_window()
return True
@@ -1521,7 +1388,7 @@ class Core(object):
Scroll a page up, if possible.
Returns True on success, None on failure.
"""
- if self.current_tab().on_scroll_up():
+ if self.tabs.current_tab.on_scroll_up():
self.refresh_window()
return True
@@ -1530,7 +1397,7 @@ class Core(object):
Scroll a line up, if possible.
Returns True on success, None on failure.
"""
- if self.current_tab().on_line_up():
+ if self.tabs.current_tab.on_line_up():
self.refresh_window()
return True
@@ -1539,7 +1406,7 @@ class Core(object):
Scroll a line down, if possible.
Returns True on success, None on failure.
"""
- if self.current_tab().on_line_down():
+ if self.tabs.current_tab.on_line_down():
self.refresh_window()
return True
@@ -1548,7 +1415,7 @@ class Core(object):
Scroll half a screen down, if possible.
Returns True on success, None on failure.
"""
- if self.current_tab().on_half_scroll_up():
+ if self.tabs.current_tab.on_half_scroll_up():
self.refresh_window()
return True
@@ -1557,7 +1424,7 @@ class Core(object):
Scroll half a screen down, if possible.
Returns True on success, None on failure.
"""
- if self.current_tab().on_half_scroll_down():
+ if self.tabs.current_tab.on_half_scroll_down():
self.refresh_window()
return True
@@ -1565,8 +1432,8 @@ class Core(object):
"""
Expand the information win a number of lines
"""
- if self.information_win_size >= self.current_tab().height -5 or \
- self.information_win_size+nb >= self.current_tab().height-4 or\
+ if self.information_win_size >= self.tabs.current_tab.height -5 or \
+ self.information_win_size+nb >= self.tabs.current_tab.height-4 or\
self.size.core_degrade_y:
return 0
self.information_win_size += nb
@@ -1595,10 +1462,10 @@ class Core(object):
Scroll the information buffer up
"""
self.information_win.scroll_up(self.information_win.height)
- if not isinstance(self.current_tab(), tabs.RosterInfoTab):
+ if not isinstance(self.tabs.current_tab, tabs.RosterInfoTab):
self.information_win.refresh()
else:
- info = self.current_tab().information_win
+ info = self.tabs.current_tab.information_win
info.scroll_up(info.height)
self.refresh_window()
@@ -1607,10 +1474,10 @@ class Core(object):
Scroll the information buffer down
"""
self.information_win.scroll_down(self.information_win.height)
- if not isinstance(self.current_tab(), tabs.RosterInfoTab):
+ if not isinstance(self.tabs.current_tab, tabs.RosterInfoTab):
self.information_win.refresh()
else:
- info = self.current_tab().information_win
+ info = self.tabs.current_tab.information_win
info.scroll_down(info.height)
self.refresh_window()
@@ -1717,7 +1584,8 @@ class Core(object):
tab.need_resize = True
else:
tab.resize()
- if self.tabs:
+
+ if len(self.tabs):
self.full_screen_redraw()
def read_keyboard(self):
@@ -2057,7 +1925,7 @@ class Core(object):
for bm in bookmarks:
if not (bm.autojoin or config.get('open_all_bookmarks')):
continue
- tab = self.get_tab_by_name(bm.jid, tabs.MucTab)
+ tab = self.tabs.by_name_and_class(bm.jid, tabs.MucTab)
nick = bm.nick if bm.nick else self.own_nick
if not tab:
self.open_new_room(
@@ -2085,9 +1953,9 @@ class Core(object):
type_ = iq['error']['type']
condition = iq['error']['condition']
if not (type_ == 'cancel' and condition == 'item-not-found'):
- self.information('Unable to fetch the remote'
- ' bookmarks; %s: %s' % (type_, condition),
- 'Error')
+ self.information(
+ 'Unable to fetch the remote'
+ ' bookmarks; %s: %s' % (type_, condition), 'Error')
return
remote_bookmarks = self.bookmarks.remote()
self.join_initial_rooms(remote_bookmarks)
@@ -2100,7 +1968,7 @@ class Core(object):
"""
Display the error in the tab
"""
- tab = self.get_tab_by_name(room_name, tabs.MucTab)
+ tab = self.tabs.by_name_and_class(room_name, tabs.MucTab)
if not tab:
return
error_message = self.get_error_message(error)