#!/usr/bin/python # -*- coding: utf-8 -*- """ Primitivus: a SAT frontend Copyright (C) 2009, 2010 Jérôme Poisson (goffi@goffi.org) This program 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. This program 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 this program. If not, see . """ from quick_frontend.quick_app import QuickApp from quick_frontend.quick_chat_list import QuickChatList from quick_frontend.quick_contact_list import QuickContactList from quick_frontend.quick_contact_management import QuickContactManagement import urwid from profile_manager import ProfileManager from contact_list import ContactList from chat import Chat import custom_widgets import pdb """from window import Window from editbox import EditBox from statusbar import StatusBar from chat import Chat from tools.jid import JID""" import logging from logging import debug, info, error #import locale import sys, os from tools.jid import JID #from curses import ascii #import locale #from signal import signal, SIGWINCH #import fcntl #import struct #import termios #from boxsizer import BoxSizer ### logging configuration FIXME: put this elsewhere ### logging.basicConfig(level=logging.CRITICAL, #TODO: configure it to put messages in a log file format='%(message)s') ### const_APP_NAME = "Primitivus" const_PALETTE = [('title', 'black', 'light gray', 'standout,underline'), ('selected', 'default', 'dark red'), ('selected_focus', 'default,bold', 'dark red'), ('default', 'default', 'default'), ('default_focus', 'default,bold', 'default'), ('alert', 'default,underline', 'default'), ('alert_focus', 'default,bold,underline', 'default'), ('date', 'light gray', 'default'), ('my_nick', 'dark red,bold', 'default'), ('other_nick', 'dark cyan,bold', 'default'), ('menubar', 'light gray,bold', 'dark red'), ('selected_menu', 'light gray,bold', 'dark green'), ('menuitem', 'light gray,bold', 'dark red'), ('menuitem_focus', 'light gray,bold', 'dark green'), ('card_neutral', 'dark gray', 'white', 'standout,underline'), ('card_neutral_selected', 'dark gray', 'dark green', 'standout,underline'), ('card_special', 'brown', 'white', 'standout,underline'), ('card_special_selected', 'brown', 'dark green', 'standout,underline'), ('card_red', 'dark red', 'white', 'standout,underline'), ('card_red_selected', 'dark red', 'dark green', 'standout,underline'), ('card_black', 'black', 'white', 'standout,underline'), ('card_black_selected', 'black', 'dark green', 'standout,underline'), ] class ChatList(QuickChatList): """This class manage the list of chat windows""" def __init__(self, host): QuickChatList.__init__(self, host) def createChat(self, target): return Chat(target, self.host) class PrimitivusApp(QuickApp): def __init__(self): self.CM = QuickContactManagement() #FIXME: not the best place QuickApp.__init__(self) #XXX: yes it's an unusual place for the constructor of a parent class, but the init order is important ## main loop setup ## self.main_widget = ProfileManager(self) self.loop = urwid.MainLoop(self.main_widget, const_PALETTE, event_loop=urwid.GLibEventLoop(), input_filter=self.inputFilter, unhandled_input=self.keyHandler) ##misc setup## self.chat_wins=ChatList(self) def debug(self): """convenient method to reset screen and launch pdb""" import os os.system('reset') print 'Entered debug mode' pdb.set_trace() def write_log(self, log, file_name='/tmp/primitivus_log'): """method to write log in a temporary file, useful for debugging""" f=open(file_name, 'a') f.write(log+"\n") f.close() def redraw(self): """redraw the screen""" self.loop.draw_screen() def start(self): self.i = 0 self.loop.set_alarm_in(0,lambda a,b: self.postInit()) self.loop.run() def inputFilter(self, input, raw): for i in input: if i.__class__==tuple: if i[0] == 'mouse press': if i[1] == 4: #Mouse wheel up input[input.index(i)] = 'up' if i[1] == 5: #Mouse wheel down input[input.index(i)] = 'down' return input def keyHandler(self, input): if input == 'meta m': try: if self.main_widget.header == None: self.main_widget.header = self.menu_roller else: self.main_widget.header = None except AttributeError: pass elif input == 'ctrl d' and 'D' in self.bridge.getVersion(): #Debug only for dev versions self.debug() elif input == 'f2': #user wants to (un)hide the contact_list try: center_widgets = self.center_part.widget_list if self.contactList in center_widgets: self.center_part.set_focus(0) #necessary as the focus change to the next object, we can go out of range if we are on the last object of self.center_part center_widgets.remove(self.contactList) del self.center_part.column_types[0] else: center_widgets.insert(0, self.contactList) self.center_part.column_types.insert(0, ('weight', 2)) except AttributeError: #The main widget is not built (probably in Profile Manager) pass elif input == 'window resize': width,height = self.loop.screen_size if height<=5 and width<=35: if not 'save_main_widget' in dir(self): self.save_main_widget = self.loop.widget self.loop.widget = urwid.Filler(urwid.Text(_("Pleeeeasse, I can't even breathe !"))) else: if 'save_main_widget' in dir(self): self.loop.widget = self.save_main_widget del self.save_main_widget try: return self.menu_roller.checkShortcuts(input) except AttributeError: return input def __buildMenuRoller(self): menu = custom_widgets.Menu(self.loop) general = _("General") menu.addMenu(general, _("Connect"), self.onConnectRequest) menu.addMenu(general, _("Disconnect"), self.onDisconnectRequest) menu.addMenu(general, _("About"), self.onAboutRequest) menu.addMenu(general, _("Exit"), self.onExitRequest, 'ctrl x') contact = _("Contact") menu.addMenu(contact, _("Add contact"), self.onAddContactRequest) menu.addMenu(contact, _("Remove contact"), self.onRemoveContactRequest) communication = _("Communication") menu.addMenu(communication, _("Join room"), self.onJoinRoomRequest, 'meta j') menu_roller = custom_widgets.MenuRoller([(_('Main menu'),menu)]) return menu_roller def __buildMainWidget(self): self.contactList = ContactList(self, self.CM, on_click = self.contactSelected, on_change=lambda w: self.redraw()) #self.center_part = urwid.Columns([('weight',2,self.contactList),('weight',8,Chat('',self))]) self.center_part = urwid.Columns([('weight',2,self.contactList), ('weight',8,urwid.Filler(urwid.Text('')))]) editBar = custom_widgets.AdvancedEdit('> ') urwid.connect_signal(editBar,'click',self.onTextEntered) self.menu_roller = self.__buildMenuRoller() self.main_widget = custom_widgets.FocusFrame(self.center_part, header=self.menu_roller, footer=editBar, focus_part='footer') return self.main_widget def plug_profile(self, profile_key='@DEFAULT@'): self.loop.widget = self.__buildMainWidget() QuickApp.plug_profile(self, profile_key) def removePopUp(self, widget=None): self.loop.widget = self.main_widget def showPopUp(self, pop_up_widget): display_widget = urwid.Overlay(pop_up_widget, self.main_widget, 'center', ('relative', 40), 'middle', ('relative', 40)) self.loop.widget = display_widget def contactSelected(self, contact_list): contact = contact_list.get_contact() if contact: assert(len(self.center_part.widget_list)==2) self.center_part.widget_list[1] = self.chat_wins[contact] self.menu_roller.addMenu(_('Chat menu'), self.chat_wins[contact].getMenu()) def onTextEntered(self, editBar): """Called when text is entered in the main edit bar""" contact = self.contactList.get_contact() ###Based on the fact that there is currently only one contact selectableat once if contact: chat = self.chat_wins[contact] self.bridge.sendMessage(contact, editBar.get_edit_text(), type = "groupchat" if chat.type == 'group' else "chat", profile_key=self.profile) editBar.set_edit_text('') def newMessage(self, from_jid, msg, type, to_jid, profile): if not self.check_profile(profile): return QuickApp.newMessage(self, from_jid, msg, type, to_jid, profile) sender = JID(from_jid) if JID(self.contactList.selected).short != sender.short: self.contactList.putAlert(sender) def onJoinRoom(self, button, edit): self.removePopUp() room_jid = JID(edit.get_edit_text()) if room_jid.is_valid(): self.bridge.joinMUC(room_jid.domain, room_jid.node, self.profiles[self.profile]['whoami'].node, self.profile) else: message = _("'%s' is an invalid JID !") % room_jid error (message) custom_widgets.Alert(_("Error"), message, ok_cb=self.removePopUp) def onAddContact(self, button, edit): self.removePopUp() jid=JID(edit.get_edit_text()) if jid.is_valid(): self.bridge.addContact(jid.short, profile_key=self.profile) else: message = _("'%s' is an invalid JID !") % jid error (message) custom_widgets.Alert(_("Error"), message, ok_cb=self.removePopUp) def onRemoveContact(self, button): self.removePopUp() info(_("Unsubscribing %s presence"),self.contactList.get_contact()) self.bridge.delContact(self.contactList.get_contact(), profile_key=self.profile) #Menu events# def onConnectRequest(self, menu): self.bridge.connect(self.profile) def onDisconnectRequest(self, menu): self.bridge.disconnect(self.profile) def onExitRequest(self, menu): raise urwid.ExitMainLoop() def onJoinRoomRequest(self, menu): """User wants to join a MUC room""" pop_up_widget = custom_widgets.InputDialog(_("Entering a MUC room"), _("Please enter MUC's JID"), default_txt = 'test@conference.necton2.int', cancel_cb=self.removePopUp, ok_cb=self.onJoinRoom) self.showPopUp(pop_up_widget) def onAddContactRequest(self, menu): pop_up_widget = custom_widgets.InputDialog(_("Adding a contact"), _("Please enter new contact JID"), default_txt = 'name@server.tld', cancel_cb=self.removePopUp, ok_cb=self.onAddContact) self.showPopUp(pop_up_widget) def onRemoveContactRequest(self, menu): contact = self.contactList.get_contact() if not contact: self.showPopUp(custom_widgets.Alert(_("Error"), _("You have not selected any contact to delete !"), ok_cb=self.removePopUp)) else: pop_up_widget = custom_widgets.ConfirmDialog(_("Are you sure you want to delete the contact [%s] ?" % contact), yes_cb=self.onRemoveContact, no_cb=self.removePopUp) self.showPopUp(pop_up_widget) def onAboutRequest(self, menu): self.showPopUp(custom_widgets.Alert(_("About"), const_APP_NAME + " v" + self.bridge.getVersion(), ok_cb=self.removePopUp)) sat = PrimitivusApp() sat.start()