Mercurial > libervia-backend
comparison frontends/src/primitivus/primitivus @ 223:86d249b6d9b7
Files reorganisation
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 29 Dec 2010 01:06:29 +0100 |
parents | frontends/primitivus/primitivus@3198bfd66daa |
children | fd9b7834d98a |
comparison
equal
deleted
inserted
replaced
222:3198bfd66daa | 223:86d249b6d9b7 |
---|---|
1 #!/usr/bin/python | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 """ | |
5 Primitivus: a SAT frontend | |
6 Copyright (C) 2009, 2010 Jérôme Poisson (goffi@goffi.org) | |
7 | |
8 This program is free software: you can redistribute it and/or modify | |
9 it under the terms of the GNU General Public License as published by | |
10 the Free Software Foundation, either version 3 of the License, or | |
11 (at your option) any later version. | |
12 | |
13 This program is distributed in the hope that it will be useful, | |
14 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 GNU General Public License for more details. | |
17 | |
18 You should have received a copy of the GNU General Public License | |
19 along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 """ | |
21 | |
22 | |
23 from quick_frontend.quick_app import QuickApp | |
24 from quick_frontend.quick_chat_list import QuickChatList | |
25 from quick_frontend.quick_contact_list import QuickContactList | |
26 from quick_frontend.quick_contact_management import QuickContactManagement | |
27 import urwid | |
28 from profile_manager import ProfileManager | |
29 from contact_list import ContactList | |
30 from chat import Chat | |
31 from gateways import GatewaysManager | |
32 from urwid_satext import sat_widgets | |
33 import logging | |
34 from logging import debug, info, error | |
35 import sys, os | |
36 from tools.jid import JID | |
37 from xmlui import XMLUI | |
38 from progress import Progress | |
39 | |
40 | |
41 ### logging configuration FIXME: put this elsewhere ### | |
42 logging.basicConfig(level=logging.CRITICAL, #TODO: configure it to put messages in a log file | |
43 format='%(message)s') | |
44 ### | |
45 | |
46 const_APP_NAME = "Primitivus" | |
47 const_PALETTE = [('title', 'black', 'light gray', 'standout,underline'), | |
48 ('title_focus', 'white,bold', 'light gray', 'standout,underline'), | |
49 ('selected', 'default', 'dark red'), | |
50 ('selected_focus', 'default,bold', 'dark red'), | |
51 ('default', 'default', 'default'), | |
52 ('default_focus', 'default,bold', 'default'), | |
53 ('alert', 'default,underline', 'default'), | |
54 ('alert_focus', 'default,bold,underline', 'default'), | |
55 ('date', 'light gray', 'default'), | |
56 ('my_nick', 'dark red,bold', 'default'), | |
57 ('other_nick', 'dark cyan,bold', 'default'), | |
58 ('menubar', 'light gray,bold', 'dark red'), | |
59 ('menubar_focus', 'light gray,bold', 'dark green'), | |
60 ('selected_menu', 'light gray,bold', 'dark green'), | |
61 ('menuitem', 'light gray,bold', 'dark red'), | |
62 ('menuitem_focus', 'light gray,bold', 'dark green'), | |
63 ('notifs', 'black,bold', 'yellow'), | |
64 ('notifs_focus', 'dark red', 'yellow'), | |
65 ('card_neutral', 'dark gray', 'white', 'standout,underline'), | |
66 ('card_neutral_selected', 'dark gray', 'dark green', 'standout,underline'), | |
67 ('card_special', 'brown', 'white', 'standout,underline'), | |
68 ('card_special_selected', 'brown', 'dark green', 'standout,underline'), | |
69 ('card_red', 'dark red', 'white', 'standout,underline'), | |
70 ('card_red_selected', 'dark red', 'dark green', 'standout,underline'), | |
71 ('card_black', 'black', 'white', 'standout,underline'), | |
72 ('card_black_selected', 'black', 'dark green', 'standout,underline'), | |
73 ('directory', 'dark cyan, bold', 'default'), | |
74 ('directory_focus', 'dark cyan, bold', 'dark green'), | |
75 ('separator', 'brown', 'default'), | |
76 ('warning', 'light red', 'default'), | |
77 ('progress_normal', 'default', 'black'), | |
78 ('progress_complete', 'default', 'light red'), | |
79 ] | |
80 | |
81 class ChatList(QuickChatList): | |
82 """This class manage the list of chat windows""" | |
83 | |
84 def __init__(self, host): | |
85 QuickChatList.__init__(self, host) | |
86 | |
87 def createChat(self, target): | |
88 return Chat(target, self.host) | |
89 | |
90 class PrimitivusApp(QuickApp): | |
91 | |
92 def __init__(self): | |
93 self.CM = QuickContactManagement() #FIXME: not the best place | |
94 QuickApp.__init__(self) | |
95 | |
96 ## main loop setup ## | |
97 self.main_widget = ProfileManager(self) | |
98 self.loop = urwid.MainLoop(self.main_widget, const_PALETTE, event_loop=urwid.GLibEventLoop(), input_filter=self.inputFilter, unhandled_input=self.keyHandler) | |
99 | |
100 ##misc setup## | |
101 self.chat_wins=ChatList(self) | |
102 self.notBar = sat_widgets.NotificationBar() | |
103 urwid.connect_signal(self.notBar,'change',self.onNotification) | |
104 self.progress_wid = Progress(self) | |
105 urwid.connect_signal(self.notBar.progress,'click',lambda x:self.addWindow(self.progress_wid)) | |
106 self.__saved_overlay = None | |
107 | |
108 def debug(self): | |
109 """convenient method to reset screen and launch p(u)db""" | |
110 try: | |
111 import pudb | |
112 pudb.set_trace() | |
113 except: | |
114 import os,pdb | |
115 os.system('reset') | |
116 print 'Entered debug mode' | |
117 pdb.set_trace() | |
118 | |
119 def writeLog(self, log, file_name='/tmp/primitivus_log'): | |
120 """method to write log in a temporary file, useful for debugging""" | |
121 with open(file_name, 'a') as f: | |
122 f.write(log+"\n") | |
123 | |
124 def redraw(self): | |
125 """redraw the screen""" | |
126 self.loop.draw_screen() | |
127 | |
128 def start(self): | |
129 self.i = 0 | |
130 self.loop.set_alarm_in(0,lambda a,b: self.postInit()) | |
131 self.loop.run() | |
132 | |
133 def inputFilter(self, input, raw): | |
134 if self.__saved_overlay and input != ['ctrl s']: | |
135 return | |
136 for i in input: | |
137 if isinstance(i,tuple): | |
138 if i[0] == 'mouse press': | |
139 if i[1] == 4: #Mouse wheel up | |
140 input[input.index(i)] = 'up' | |
141 if i[1] == 5: #Mouse wheel down | |
142 input[input.index(i)] = 'down' | |
143 return input | |
144 | |
145 def keyHandler(self, input): | |
146 if input == 'meta m': | |
147 """User want to (un)hide the menu roller""" | |
148 try: | |
149 if self.main_widget.header == None: | |
150 self.main_widget.header = self.menu_roller | |
151 else: | |
152 self.main_widget.header = None | |
153 except AttributeError: | |
154 pass | |
155 elif input == 'ctrl n': | |
156 """User wants to see next notification""" | |
157 self.notBar.showNext() | |
158 elif input == 'ctrl s': | |
159 """User wants to (un)hide overlay window""" | |
160 if isinstance(self.loop.widget,urwid.Overlay): | |
161 self.__saved_overlay = self.loop.widget | |
162 self.loop.widget = self.main_widget | |
163 else: | |
164 if self.__saved_overlay: | |
165 self.loop.widget = self.__saved_overlay | |
166 self.__saved_overlay = None | |
167 | |
168 elif input == 'ctrl d' and 'D' in self.bridge.getVersion(): #Debug only for dev versions | |
169 self.debug() | |
170 elif input == 'f2': #user wants to (un)hide the contact_list | |
171 try: | |
172 center_widgets = self.center_part.widget_list | |
173 if self.contactList in center_widgets: | |
174 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 | |
175 center_widgets.remove(self.contactList) | |
176 del self.center_part.column_types[0] | |
177 else: | |
178 center_widgets.insert(0, self.contactList) | |
179 self.center_part.column_types.insert(0, ('weight', 2)) | |
180 except AttributeError: | |
181 #The main widget is not built (probably in Profile Manager) | |
182 pass | |
183 elif input == 'window resize': | |
184 width,height = self.loop.screen_size | |
185 if height<=5 and width<=35: | |
186 if not 'save_main_widget' in dir(self): | |
187 self.save_main_widget = self.loop.widget | |
188 self.loop.widget = urwid.Filler(urwid.Text(_("Pleeeeasse, I can't even breathe !"))) | |
189 else: | |
190 if 'save_main_widget' in dir(self): | |
191 self.loop.widget = self.save_main_widget | |
192 del self.save_main_widget | |
193 try: | |
194 return self.menu_roller.checkShortcuts(input) | |
195 except AttributeError: | |
196 return input | |
197 | |
198 def __buildMenuRoller(self): | |
199 menu = sat_widgets.Menu(self.loop) | |
200 general = _("General") | |
201 menu.addMenu(general, _("Connect"), self.onConnectRequest) | |
202 menu.addMenu(general, _("Disconnect"), self.onDisconnectRequest) | |
203 menu.addMenu(general, _("Parameters"), self.onParam) | |
204 menu.addMenu(general, _("About"), self.onAboutRequest) | |
205 menu.addMenu(general, _("Exit"), self.onExitRequest, 'ctrl x') | |
206 contact = _("Contact") | |
207 menu.addMenu(contact, _("Add contact"), self.onAddContactRequest) | |
208 menu.addMenu(contact, _("Remove contact"), self.onRemoveContactRequest) | |
209 communication = _("Communication") | |
210 menu.addMenu(communication, _("Join room"), self.onJoinRoomRequest, 'meta j') | |
211 menu.addMenu(communication, _("Find Gateways"), self.onFindGatewaysRequest, 'meta g') | |
212 #additionals menus | |
213 #FIXME: do this in a more generic way (in quickapp) | |
214 add_menus = self.bridge.getMenus() | |
215 def add_menu_cb(menu): | |
216 category, item = menu | |
217 id = self.bridge.callMenu(category, item, "NORMAL", self.profile) | |
218 self.current_action_ids.add(id) | |
219 for new_menu in add_menus: | |
220 category,item,type = new_menu | |
221 assert(type=="NORMAL") #TODO: manage other types | |
222 menu.addMenu(unicode(category), unicode(item), add_menu_cb) | |
223 | |
224 menu_roller = sat_widgets.MenuRoller([(_('Main menu'),menu)]) | |
225 return menu_roller | |
226 | |
227 def __buildMainWidget(self): | |
228 self.contactList = ContactList(self, self.CM, on_click = self.contactSelected, on_change=lambda w: self.redraw()) | |
229 #self.center_part = urwid.Columns([('weight',2,self.contactList),('weight',8,Chat('',self))]) | |
230 self.center_part = urwid.Columns([('weight',2,self.contactList), ('weight',8,urwid.Filler(urwid.Text('')))]) | |
231 self.editBar = sat_widgets.AdvancedEdit('> ') | |
232 self.editBar.setCompletionMethod(self._nick_completion) | |
233 urwid.connect_signal(self.editBar,'click',self.onTextEntered) | |
234 self.menu_roller = self.__buildMenuRoller() | |
235 self.main_widget = sat_widgets.FocusFrame(self.center_part, header=self.menu_roller, footer=self.editBar, focus_part='footer') | |
236 return self.main_widget | |
237 | |
238 def _nick_completion(self, text, completion_data): | |
239 """Completion method which complete pseudo in group chat | |
240 for params, see AdvancedEdit""" | |
241 contact = self.contactList.get_contact() ###Based on the fact that there is currently only one contact selectableat once | |
242 if contact: | |
243 chat = self.chat_wins[contact] | |
244 if chat.type != "group": | |
245 return text | |
246 space = text.rfind(" ") | |
247 start = text[space+1:] | |
248 nicks = list(chat.occupants) | |
249 nicks.sort() | |
250 try: | |
251 start_idx=nicks.index(completion_data['last_nick'])+1 | |
252 if start_idx == len(nicks): | |
253 start_idx = 0 | |
254 except (KeyError,ValueError): | |
255 start_idx = 0 | |
256 for idx in range(start_idx,len(nicks)) + range(0,start_idx): | |
257 if nicks[idx].lower().startswith(start.lower()): | |
258 completion_data['last_nick'] = nicks[idx] | |
259 return text[:space+1] + nicks[idx] + (': ' if space < 0 else '') | |
260 return text | |
261 | |
262 | |
263 | |
264 def plug_profile(self, profile_key='@DEFAULT@'): | |
265 self.loop.widget = self.__buildMainWidget() | |
266 QuickApp.plug_profile(self, profile_key) | |
267 | |
268 def removePopUp(self, widget=None): | |
269 "Remove current pop-up, and if there is other in queue, show it" | |
270 self.loop.widget = self.main_widget | |
271 next_popup = self.notBar.getNextPopup() | |
272 if next_popup: | |
273 #we still have popup to show, we display it | |
274 self.showPopUp(next_popup) | |
275 | |
276 def showPopUp(self, pop_up_widget, perc_width=40, perc_height=40): | |
277 "Show a pop-up window if possible, else put it in queue" | |
278 if not isinstance(self.loop.widget,urwid.Overlay): | |
279 display_widget = urwid.Overlay(pop_up_widget, self.main_widget, 'center', ('relative', perc_width), 'middle', ('relative', perc_height)) | |
280 self.loop.widget = display_widget | |
281 self.redraw() | |
282 else: | |
283 self.notBar.addPopUp(pop_up_widget) | |
284 | |
285 def notify(self, message): | |
286 """"Notify message to user via notification bar""" | |
287 self.notBar.addMessage(message) | |
288 | |
289 def addWindow(self, widget): | |
290 """Display a window if possible, | |
291 else add it in the notification bar queue | |
292 @param widget: BoxWidget""" | |
293 assert(len(self.center_part.widget_list)<=2) | |
294 wid_idx = len(self.center_part.widget_list)-1 | |
295 self.center_part.widget_list[wid_idx] = widget | |
296 self.menu_roller.removeMenu(_('Chat menu')) | |
297 self.contactList.unselectAll() | |
298 self.redraw() | |
299 | |
300 def removeWindow(self): | |
301 """Remove window showed on the right column""" | |
302 #TODO: to a better Window management than this crappy hack | |
303 assert(len(self.center_part.widget_list)<=2) | |
304 wid_idx = len(self.center_part.widget_list)-1 | |
305 self.center_part.widget_list[wid_idx] = urwid.Filler(urwid.Text('')) | |
306 self.center_part.set_focus(0) | |
307 self.redraw() | |
308 | |
309 def addProgress (self, id, message): | |
310 """Follow a SàT progress bar | |
311 @param id: SàT id of the progression | |
312 @param message: message to show to identify the progression""" | |
313 self.progress_wid.addProgress(id, message) | |
314 | |
315 def setProgress(self, percentage): | |
316 """Set the progression shown in notification bar""" | |
317 self.notBar.setProgress(percentage) | |
318 | |
319 def contactSelected(self, contact_list): | |
320 contact = contact_list.get_contact() | |
321 if contact: | |
322 assert(len(self.center_part.widget_list)==2) | |
323 self.center_part.widget_list[1] = self.chat_wins[contact] | |
324 self.menu_roller.addMenu(_('Chat menu'), self.chat_wins[contact].getMenu()) | |
325 | |
326 def onTextEntered(self, editBar): | |
327 """Called when text is entered in the main edit bar""" | |
328 contact = self.contactList.get_contact() ###Based on the fact that there is currently only one contact selectableat once | |
329 if contact: | |
330 chat = self.chat_wins[contact] | |
331 self.bridge.sendMessage(contact, | |
332 editBar.get_edit_text(), | |
333 type = "groupchat" if chat.type == 'group' else "chat", | |
334 profile_key=self.profile) | |
335 editBar.set_edit_text('') | |
336 | |
337 def newMessage(self, from_jid, msg, type, to_jid, profile): | |
338 if not self.check_profile(profile): | |
339 return | |
340 QuickApp.newMessage(self, from_jid, msg, type, to_jid, profile) | |
341 sender = JID(from_jid) | |
342 if JID(self.contactList.selected).short != sender.short: | |
343 self.contactList.putAlert(sender) | |
344 | |
345 def _dialogOkCb(self, widget, data): | |
346 self.removePopUp() | |
347 answer_cb = data[0] | |
348 answer_data = [data[1]] if data[1] else [] | |
349 answer_cb(True, *answer_data) | |
350 | |
351 def _dialogCancelCb(self, widget, data): | |
352 self.removePopUp() | |
353 answer_cb = data[0] | |
354 answer_data = [data[1]] if data[1] else [] | |
355 answer_cb(False, *answer_data) | |
356 | |
357 | |
358 def showDialog(self, message, title="", type="info", answer_cb = None, answer_data = None): | |
359 if type == 'info': | |
360 popup = sat_widgets.Alert(unicode(title), unicode(message), ok_cb=answer_cb or self.removePopUp) #FIXME: remove unicode here when DBus Bridge will no return dbus.String anymore | |
361 flags = wx.OK | wx.ICON_INFORMATION | |
362 elif type == 'error': | |
363 popup = sat_widgets.Alert(unicode(title), unicode(message), ok_cb=answer_cb or self.removePopUp) #FIXME: remove unicode here when DBus Bridge will no return dbus.String anymore | |
364 elif type == 'yes/no': | |
365 popup = sat_widgets.ConfirmDialog(unicode(message), | |
366 yes_cb=self._dialogOkCb, yes_value = (answer_cb, answer_data), | |
367 no_cb=self._dialogCancelCb, no_value = (answer_cb, answer_data)) | |
368 else: | |
369 popup = sat_widgets.Alert(unicode(title), unicode(message), ok_cb=answer_cb or self.removePopUp) #FIXME: remove unicode here when DBus Bridge will no return dbus.String anymore | |
370 error(_('unmanaged dialog type: %s'), type) | |
371 self.showPopUp(popup) | |
372 | |
373 def onNotification(self, notBar): | |
374 """Called when a new notification has been received""" | |
375 if not isinstance(self.main_widget, sat_widgets.FocusFrame): | |
376 #if we are not in the main configuration, we ignore the notifications bar | |
377 return | |
378 if isinstance(self.main_widget.footer,sat_widgets.AdvancedEdit): | |
379 if not self.notBar.canHide(): | |
380 #the notification bar is not visible and has usefull informations, we show it | |
381 pile = urwid.Pile([self.notBar, self.editBar]) | |
382 self.main_widget.footer = pile | |
383 else: | |
384 if not isinstance(self.main_widget.footer, urwid.Pile): | |
385 error(_("INTERNAL ERROR: Unexpected class for main widget's footer")) | |
386 assert(False) | |
387 if self.notBar.canHide(): | |
388 #No notification left, we can hide the bar | |
389 self.main_widget.footer = self.editBar | |
390 | |
391 def actionResult(self, type, id, data): | |
392 if not id in self.current_action_ids: | |
393 debug (_('unknown id, ignoring')) | |
394 return | |
395 if type == "SUPPRESS": | |
396 self.current_action_ids.remove(id) | |
397 elif type == "XMLUI": | |
398 self.current_action_ids.remove(id) | |
399 debug (_("XML user interface received")) | |
400 misc = {} | |
401 #FIXME FIXME FIXME: must clean all this crap ! | |
402 title = _('Form') | |
403 if data['type'] == 'registration': | |
404 title = _('Registration') | |
405 misc['target'] = data['target'] | |
406 misc['action_back'] = self.bridge.gatewayRegister | |
407 ui = XMLUI(self, title=title, xml_data = data['xml'], misc = misc) | |
408 if data['type'] == 'registration': | |
409 ui.show('popup') | |
410 else: | |
411 ui.show('window') | |
412 elif type == "ERROR": | |
413 self.current_action_ids.remove(id) | |
414 self.showPopUp(sat_widgets.Alert(_("Error"), unicode(data["message"]), ok_cb=self.removePopUp)) #FIXME: remove unicode here when DBus Bridge will no return dbus.String anymore | |
415 elif type == "RESULT": | |
416 self.current_action_ids.remove(id) | |
417 if self.current_action_ids_cb.has_key(id): | |
418 callback = self.current_action_ids_cb[id] | |
419 del self.current_action_ids_cb[id] | |
420 callback(data) | |
421 elif type == "DICT_DICT": | |
422 self.current_action_ids.remove(id) | |
423 if self.current_action_ids_cb.has_key(id): | |
424 callback = self.current_action_ids_cb[id] | |
425 del self.current_action_ids_cb[id] | |
426 callback(data) | |
427 else: | |
428 error (_("FIXME FIXME FIXME: type [%s] not implemented") % type) | |
429 raise NotImplementedError | |
430 | |
431 ##DIALOGS CALLBACKS## | |
432 def onJoinRoom(self, button, edit): | |
433 self.removePopUp() | |
434 room_jid = JID(edit.get_edit_text()) | |
435 if room_jid.is_valid(): | |
436 self.bridge.joinMUC(room_jid.domain, room_jid.node, self.profiles[self.profile]['whoami'].node, self.profile) | |
437 else: | |
438 message = _("'%s' is an invalid JID !") % room_jid | |
439 error (message) | |
440 self.showPopUp(sat_widgets.Alert(_("Error"), message, ok_cb=self.removePopUp)) | |
441 | |
442 def onAddContact(self, button, edit): | |
443 self.removePopUp() | |
444 jid=JID(edit.get_edit_text()) | |
445 if jid.is_valid(): | |
446 self.bridge.addContact(jid.short, profile_key=self.profile) | |
447 else: | |
448 message = _("'%s' is an invalid JID !") % jid | |
449 error (message) | |
450 self.showPopUp(sat_widgets.Alert(_("Error"), message, ok_cb=self.removePopUp)) | |
451 | |
452 def onRemoveContact(self, button): | |
453 self.removePopUp() | |
454 info(_("Unsubscribing %s presence"),self.contactList.get_contact()) | |
455 self.bridge.delContact(self.contactList.get_contact(), profile_key=self.profile) | |
456 | |
457 #MENU EVENTS# | |
458 def onConnectRequest(self, menu): | |
459 self.bridge.connect(self.profile) | |
460 | |
461 def onDisconnectRequest(self, menu): | |
462 self.bridge.disconnect(self.profile) | |
463 | |
464 def onParam(self, menu): | |
465 params = XMLUI(self,xml_data=self.bridge.getParamsUI(self.profile)) | |
466 self.addWindow(params) | |
467 | |
468 def onExitRequest(self, menu): | |
469 QuickApp.onExit(self) | |
470 raise urwid.ExitMainLoop() | |
471 | |
472 def onJoinRoomRequest(self, menu): | |
473 """User wants to join a MUC room""" | |
474 pop_up_widget = sat_widgets.InputDialog(_("Entering a MUC room"), _("Please enter MUC's JID"), default_txt = 'room@muc_service.server.tld', cancel_cb=self.removePopUp, ok_cb=self.onJoinRoom) | |
475 self.showPopUp(pop_up_widget) | |
476 | |
477 def onFindGatewaysRequest(self, e): | |
478 debug(_("Find Gateways request")) | |
479 id = self.bridge.findGateways(self.profiles[self.profile]['whoami'].domain, self.profile) | |
480 self.current_action_ids.add(id) | |
481 self.current_action_ids_cb[id] = self.onGatewaysFound | |
482 | |
483 def onAddContactRequest(self, menu): | |
484 pop_up_widget = sat_widgets.InputDialog(_("Adding a contact"), _("Please enter new contact JID"), default_txt = 'name@server.tld', cancel_cb=self.removePopUp, ok_cb=self.onAddContact) | |
485 self.showPopUp(pop_up_widget) | |
486 | |
487 def onRemoveContactRequest(self, menu): | |
488 contact = self.contactList.get_contact() | |
489 if not contact: | |
490 self.showPopUp(sat_widgets.Alert(_("Error"), _("You have not selected any contact to delete !"), ok_cb=self.removePopUp)) | |
491 else: | |
492 pop_up_widget = sat_widgets.ConfirmDialog(_("Are you sure you want to delete the contact [%s] ?" % contact), yes_cb=self.onRemoveContact, no_cb=self.removePopUp) | |
493 self.showPopUp(pop_up_widget) | |
494 | |
495 def onAboutRequest(self, menu): | |
496 self.showPopUp(sat_widgets.Alert(_("About"), const_APP_NAME + " v" + self.bridge.getVersion(), ok_cb=self.removePopUp)) | |
497 | |
498 #MISC CALLBACKS# | |
499 | |
500 def onGatewaysFound(self, data): | |
501 """Called when SàT has found the server gateways""" | |
502 target = data['__private__']['target'] | |
503 del data['__private__'] | |
504 gatewayManager = GatewaysManager(self, data, server=target) | |
505 self.addWindow(gatewayManager) | |
506 | |
507 sat = PrimitivusApp() | |
508 sat.start() | |
509 |