"""
This file is part of alot.
Alot 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.
Notmuch 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 notmuch. If not, see .
Copyright (C) 2011 Patrick Totzke
"""
import urwid
import os
from settings import config
from settings import get_palette
import command
from widgets import PromptWidget
from buffer import BufferListBuffer
class UI:
buffers = []
current_buffer = None
def __init__(self, db, log, accounts, initialquery, colourmode):
self.dbman = db
self.logger = log
self.accounts = accounts
if not colourmode:
colourmode = config.getint('general', 'colourmode')
self.logger.info('setup gui in %d colours' % colourmode)
self.mainframe = urwid.Frame(urwid.SolidFill(' '))
self.mainloop = urwid.MainLoop(self.mainframe,
get_palette(),
handle_mouse=False,
unhandled_input=self.keypress)
self.mainloop.screen.set_terminal_properties(colors=colourmode)
self.logger.debug('setup bindings')
self.bindings = {
'I': ('search', {'query': 'tag:inbox AND NOT tag:killed'}),
'U': ('search', {'query': 'tag:unread'}),
'x': ('buffer_close', {}),
'tab': ('buffer_next', {}),
'shift tab': ('buffer_prev', {}),
'\\': ('search_prompt', {}),
'q': ('shutdown', {}),
';': ('buffer_list', {}),
'L': ('open_taglist', {}),
's': ('shell', {}),
'@': ('refresh_buffer', {}),
'm': ('compose', {}),
}
cmd = command.factory('search', query=initialquery)
self.apply_command(cmd)
self.mainloop.run()
def shutdown(self):
"""
close the ui. this is _not_ the main shutdown procedure:
there is a shutdown command that will eventually call this.
"""
raise urwid.ExitMainLoop()
def prompt(self, prefix='>', text='', completer=None):
self.logger.info('open prompt')
prefix_widget = PromptWidget(prefix, text, completer)
footer = self.mainframe.get_footer()
self.mainframe.set_footer(prefix_widget)
self.mainframe.set_focus('footer')
self.mainloop.draw_screen()
while True:
keys = None
while not keys:
keys = self.mainloop.screen.get_input()
for key in keys:
if key == 'enter':
self.mainframe.set_footer(footer)
self.mainframe.set_focus('body')
return prefix_widget.get_input()
if key == 'esc':
self.mainframe.set_footer(footer)
self.mainframe.set_focus('body')
return None
else:
size = (20,) # don't know why they want a size here
prefix_widget.keypress(size, key)
self.mainloop.draw_screen()
def cmdshell(self, prefix='>', text='', completer=None):
self.logger.info('open command shell')
edit = urwid.Edit()
lines = [edit]
footer = self.mainframe.get_footer()
self.mainframe.set_footer(edit)
self.mainframe.set_focus('footer')
self.mainloop.draw_screen()
shenv = command.MyCmd()
while True:
keys = None
while not keys:
keys = self.mainloop.screen.get_input()
for key in keys:
if key == 'enter':
et = edit.get_edit_text()
edit.set_edit_text('')
lines.insert(-1, (urwid.Text('>' + et)))
result = shenv.run(et)
for rline in result.splitlines():
lines.insert(-1, urwid.Text(rline))
lb = urwid.BoxAdapter(urwid.ListBox(lines), len(lines))
self.mainframe.set_footer(lb)
self.mainframe.set_focus('footer')
self.mainloop.draw_screen()
if key == 'esc':
self.mainframe.set_footer(footer)
self.mainframe.set_focus('body')
return None
else:
size = (20,) # don't know why they want a size here
self.mainframe.footer.keypress(size, key)
self.mainloop.draw_screen()
def buffer_open(self, b):
"""
register and focus new buffer
"""
self.buffers.append(b)
self.buffer_focus(b)
def buffer_close(self, buf):
buffers = self.buffers
if buf not in buffers:
string = 'tried to close unknown buffer: %s. \n\ni have:%s'
self.logger.error(string % (buf, self.buffers))
elif len(buffers) == 1:
self.logger.info('closing the last buffer, exiting')
cmd = command.factory('shutdown')
self.apply_command(cmd)
else:
if self.current_buffer == buf:
self.logger.debug('UI: closing current buffer %s' % buf)
index = buffers.index(buf)
buffers.remove(buf)
self.current_buffer = buffers[index % len(buffers)]
else:
string = 'closing buffer %d:%s'
self.logger.debug(string % (buffers.index(buf), buf))
index = buffers.index(buf)
buffers.remove(buf)
def buffer_focus(self, buf):
"""
focus given buffer. must be contained in self.buffers
"""
if buf not in self.buffers:
self.logger.error('tried to focus unknown buffer')
else:
self.current_buffer = buf
if isinstance(self.current_buffer, BufferListBuffer):
self.current_buffer.rebuild()
self.update()
def get_buffers_of_type(self, t):
return filter(lambda x: isinstance(x, t), self.buffers)
def update(self):
"""
redraw interface
"""
#who needs a header?
#head = urwid.Text('notmuch gui')
#h=urwid.AttrMap(head, 'header')
#self.mainframe.set_header(h)
#body
self.mainframe.set_body(self.current_buffer)
#footer
self.update_footer()
def update_footer(self):
idx = self.buffers.index(self.current_buffer)
lefttxt = '%d: [%s] %s' % (idx, self.current_buffer.typename,
self.current_buffer)
footerleft = urwid.Text(lefttxt, align='left')
righttxt = 'total messages: %d' % self.dbman.count_messages('*')
footerright = urwid.Text(righttxt, align='right')
columns = urwid.Columns([
footerleft,
('fixed', len(righttxt), footerright)])
footer = urwid.AttrMap(columns, 'footer')
self.mainframe.set_footer(footer)
def keypress(self, key):
if key in self.bindings:
self.logger.debug("got globally bounded key: %s" % key)
(cmdname, parms) = self.bindings[key]
cmd = command.factory(cmdname, **parms)
self.apply_command(cmd)
elif key == ':':
self.cmdshell()
else:
self.logger.debug('unhandeled input: %s' % input)
def apply_command(self, cmd):
if cmd:
if cmd.prehook:
self.logger.debug('calling pre-hook')
try:
cmd.prehook(self, self.dbman)
except:
self.logger.exception('prehook failed')
self.logger.debug('apply command: %s' % cmd)
cmd.apply(self)
if cmd.posthook:
self.logger.debug('calling post-hook')
try:
cmd.posthook(self, self.dbman)
except:
self.logger.exception('posthook failed')