Package flumotion :: Package admin :: Package gtk :: Module message
[hide private]

Source Code for Module flumotion.admin.gtk.message

  1  # -*- Mode: Python -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3  # 
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007,2008 Fluendo, S.L. (www.fluendo.com). 
  6  # All rights reserved. 
  7   
  8  # This file may be distributed and/or modified under the terms of 
  9  # the GNU General Public License version 2 as published by 
 10  # the Free Software Foundation. 
 11  # This file is distributed without any warranty; without even the implied 
 12  # warranty of merchantability or fitness for a particular purpose. 
 13  # See "LICENSE.GPL" in the source distribution for more information. 
 14   
 15  # Licensees having purchased or holding a valid Flumotion Advanced 
 16  # Streaming Server license may use this file in accordance with the 
 17  # Flumotion Advanced Streaming Server Commercial License Agreement. 
 18  # See "LICENSE.Flumotion" in the source distribution for more information. 
 19   
 20  # Headers in this file shall remain intact. 
 21   
 22  """a view display messages containing warnings, errors and information.""" 
 23   
 24  import gettext 
 25  import os 
 26  import time 
 27   
 28  import pango 
 29  import gtk 
 30   
 31  from flumotion.common import log 
 32  from flumotion.common.documentation import getMessageWebLink 
 33  from flumotion.common.i18n import Translator 
 34  from flumotion.common.messages import ERROR, WARNING, INFO 
 35  from flumotion.configure import configure 
 36   
 37  _ = gettext.gettext 
 38  __version__ = "$Rev: 7094 $" 
 39  _stock_icons = { 
 40      ERROR: gtk.STOCK_DIALOG_ERROR, 
 41      WARNING: gtk.STOCK_DIALOG_WARNING, 
 42      INFO: gtk.STOCK_DIALOG_INFO, 
 43      } 
 44  _headings = { 
 45      ERROR: _('Error'), 
 46      WARNING: _('Warning'), 
 47      INFO: _('Note'), 
 48      } 
 49   
 50   
51 -class MessageButton(gtk.ToggleButton):
52 """ 53 I am a button at the top right of the message view, representing a message. 54 """
55 - def __init__(self, message):
56 gtk.ToggleButton.__init__(self) 57 58 self.message = message 59 60 i = gtk.Image() 61 i.set_from_stock(_stock_icons.get(message.level, 62 gtk.STOCK_MISSING_IMAGE), 63 gtk.ICON_SIZE_BUTTON) 64 i.show() 65 self.add(i) 66 self.set_focus_on_click(False) 67 self.set_relief(gtk.RELIEF_NONE)
68
69 - def __repr__(self):
70 return '<MessageButton for %s at %d>' % (self.message, id(self))
71 72 73 # instantiated through create_function in glade files
74 -class MessagesView(gtk.VBox):
75 """ 76 I am a widget that can show messages. 77 """ 78 # I am a vbox with first row the label and icons, 79 # second row a separator 80 # and third row a text view
81 - def __init__(self):
82 gtk.VBox.__init__(self) 83 84 h1 = gtk.HBox() 85 self.pack_start(h1, False, False, 0) 86 self.label = gtk.Label() 87 self.label.show() 88 h1.pack_start(self.label, False, False, 6) 89 90 # button box holding the message icons at the top right 91 h2 = gtk.HBox() 92 h1.pack_end(h2, False, False, 0) 93 s = gtk.HSeparator() 94 self.pack_start(s, False, False, 6) 95 sw = gtk.ScrolledWindow() 96 sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 97 sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) 98 self.pack_start(sw, True, True, 0) 99 100 # text view shows the messages, plus debug information 101 # FIXME: needs to be hyperlinkable in the future 102 tv = gtk.TextView() 103 tv.set_wrap_mode(gtk.WRAP_WORD) 104 tv.set_left_margin(6) 105 tv.set_right_margin(6) 106 tv.set_accepts_tab(False) 107 tv.set_cursor_visible(False) 108 tv.set_editable(False) 109 #tv.set_sensitive(False) 110 # connect signals to act on the hyperlink 111 tv.connect('event-after', self._after_textview__event) 112 tv.connect('motion-notify-event', 113 self._on_textview___motion_notify_event) 114 sw.add(tv) 115 116 self.active_button = None 117 self.buttonbox = h2 118 self.textview = tv 119 120 self.show_all() 121 self.clear() 122 123 self._translator = Translator() 124 localedir = os.path.join(configure.localedatadir, 'locale') 125 # FIXME: add locales as messages from domains come in 126 self._translator.addLocaleDir(configure.PACKAGE, localedir)
127
128 - def clear(self):
129 """ 130 Remove all messages and hide myself. 131 """ 132 for child in self.buttonbox.get_children(): 133 self.clearMessage(child.message.id) 134 self.hide()
135
136 - def addMessage(self, m):
137 """ 138 Add a message to me. 139 @type m: L{flumotion.common.messages.Message} 140 """ 141 # clear all previously added messages with the same id. This allows 142 # us to replace for example a "probing" message with the 143 # result message 144 self.clearMessage(m.id) 145 146 # add a message button to show this message 147 b = MessageButton(m) 148 b.sigid = b.connect('toggled', self._on_message_button__toggled, m) 149 b.show() 150 self.buttonbox.pack_start(b, False, False, 0) 151 152 firstButton = self._sortMessages() 153 154 if not self.active_button: 155 b.set_active(True) 156 elif b == firstButton: 157 b.set_active(True) 158 self.show()
159
160 - def clearMessage(self, id):
161 """ 162 Clear all messages with the given id. 163 Will bring the remaining most important message to the front, 164 or hide the view completely if no messages are left. 165 """ 166 for button in self.buttonbox.get_children(): 167 if button.message.id != id: 168 continue 169 170 self.buttonbox.remove(button) 171 button.disconnect(button.sigid) 172 button.sigid = 0 173 if not self.buttonbox.get_children(): 174 self.active_button = None 175 self.hide() 176 elif self.active_button == button: 177 self.active_button = self.buttonbox.get_children()[0] 178 self.active_button.set_active(True) 179 break
180 181 # Private 182
183 - def _addMessageToBuffer(self, message):
184 # FIXME: it would be good to have a "Debug" button when 185 # applicable, instead of always showing the text 186 text = self._translator.translate(message) 187 188 # F0.4: timestamp was added in 0.4.2 189 if hasattr(message, 'timestamp'): 190 text += _("\nPosted on %s.\n") % time.strftime( 191 "%c", time.localtime(message.timestamp)) 192 193 if message.debug: 194 text += "\n\n" + _("Debug information:\n") + message.debug + '\n' 195 196 textbuffer = gtk.TextBuffer() 197 textbuffer.set_text(text) 198 self.textview.set_buffer(textbuffer) 199 self.label.set_markup('<b>%s</b>' % 200 _headings.get(message.level, _('Message'))) 201 202 # if we have help information, add it to the end of the text view 203 # FIXME: it probably looks nicer right after the message and 204 # before the timestamp 205 description = getattr(message, 'description') 206 if description: 207 titer = textbuffer.get_end_iter() 208 # we set the 'link' data field on tags to identify them 209 translated = self._translator.translateTranslatable(description) 210 tag = textbuffer.create_tag(translated) 211 tag.set_property('underline', pango.UNDERLINE_SINGLE) 212 tag.set_property('foreground', 'blue') 213 tag.set_data('link', getMessageWebLink(message)) 214 textbuffer.insert_with_tags_by_name(titer, translated, 215 tag.get_property('name'))
216
217 - def _sortMessages(self):
218 # Sort all messages first by (reverse of) level, then priority 219 children = [(-w.message.level, w.message.priority, w) 220 for w in self.buttonbox.get_children()] 221 children.sort() 222 children.reverse() 223 children = [(i, children[i][2]) for i in range(len(children))] 224 for child in children: 225 self.buttonbox.reorder_child(child[1], child[0]) 226 227 # the first button, e.g. highest priority 228 return children[0][1]
229 230 # Callbacks 231
232 - def _on_message_button__toggled(self, button, message):
233 # on toggling the button, show the message 234 if not button.get_active(): 235 if self.active_button == button: 236 button.set_active(True) 237 return 238 old_active = self.active_button 239 self.active_button = button 240 if old_active and old_active != button: 241 old_active.set_active(False) 242 243 self._addMessageToBuffer(message)
244 245 # when the mouse cursor moves, set the cursor image accordingly
246 - def _on_textview___motion_notify_event(self, textview, event):
247 x, y = textview.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, 248 int(event.x), int(event.y)) 249 tags = textview.get_iter_at_location(x, y).get_tags() 250 # without this call, further motion notify events don't get 251 # triggered 252 textview.window.get_pointer() 253 254 # if any of the tags is a link, show a hand 255 cursor = None 256 for tag in tags: 257 if tag.get_data('link'): 258 cursor = gtk.gdk.Cursor(gtk.gdk.HAND2) 259 break 260 textview.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(cursor) 261 return False
262
263 - def _after_textview__event(self, textview, event):
264 if event.type != gtk.gdk.BUTTON_RELEASE: 265 return False 266 if event.button != 1: 267 return False 268 269 textbuffer = textview.get_buffer() 270 # we shouldn't follow a link if the user has selected something 271 bounds = textbuffer.get_selection_bounds() 272 if bounds: 273 [start, end] = bounds 274 if start.get_offset() != end.get_offset(): 275 return False 276 277 x, y = textview.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, 278 int(event.x), int(event.y)) 279 iter = textview.get_iter_at_location(x, y) 280 281 for tag in iter.get_tags(): 282 link = tag.get_data('link') 283 if link: 284 import webbrowser 285 log.debug('messageview', 'opening %s' % link) 286 webbrowser.open(link) 287 break 288 289 return False
290