Package sabayon :: Module sessionwindow
[hide private]
[frames] | no frames]

Source Code for Module sabayon.sessionwindow

  1  # 
  2  # Copyright (C) 2005 Red Hat, Inc. 
  3  # 
  4  # This program is free software; you can redistribute it and/or modify 
  5  # it under the terms of the GNU General Public License as published by 
  6  # the Free Software Foundation; either version 2 of the License, or 
  7  # (at your option) any later version. 
  8  # 
  9  # This program is distributed in the hope that it will be useful, 
 10  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 12  # GNU General Public License for more details. 
 13  # 
 14  # You should have received a copy of the GNU General Public License 
 15  # along with this program; if not, write to the Free Software 
 16  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 17  # 
 18   
 19  import time 
 20  import gobject 
 21  import gtk 
 22  import gtk.gdk 
 23  import pango 
 24  import util 
 25  import userprofile 
 26  import protosession 
 27  import changeswindow 
 28  import sessionwidget 
 29  import saveconfirm 
 30  import aboutdialog 
 31  import debuglog 
 32  import errors 
 33  from lockdownappliersabayon import LockdownApplierSabayon 
 34  from Pessulus import maindialog as lockdowndialog 
 35  from config import * 
 36   
 37  _ui_string = ''' 
 38  <ui> 
 39    <menubar name="Menubar"> 
 40      <menu action="ProfileMenu"> 
 41        <menuitem action="Save"/> 
 42        <separator/> 
 43        <menuitem action="Quit"/> 
 44      </menu> 
 45      <menu action="EditMenu"> 
 46        <menuitem action="Changes"/> 
 47        <menuitem action="Lockdown"/> 
 48        <menuitem action="EnforceMandatory"/> 
 49      </menu> 
 50      <menu action="HelpMenu"> 
 51        <menuitem action="Contents"/> 
 52        <menuitem action="About"/> 
 53      </menu> 
 54    </menubar> 
 55  </ui> 
 56  ''' 
 57   
58 -def dprint (fmt, *args):
59 debuglog.debug_log (False, debuglog.DEBUG_LOG_DOMAIN_ADMIN_TOOL, fmt % args)
60
61 -class ProfileChangesModel (gtk.ListStore):
62 __gsignals__ = { 63 "changed" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (userprofile.ProfileChange, )) 64 } 65 66 ( 67 COLUMN_CHANGE, 68 COLUMN_IGNORE, 69 COLUMN_MANDATORY, 70 COLUMN_LOCK_PIXBUF, 71 COLUMN_DESCRIPTION 72 ) = range (5) 73
74 - def __init__ (self, profile):
75 gtk.ListStore.__init__ (self, userprofile.ProfileChange, bool, bool, gtk.gdk.Pixbuf, str) 76 77 icon_theme = gtk.icon_theme_get_default () 78 79 self.locked_pixbuf = icon_theme.load_icon ("stock_lock", 22, 80 gtk.ICON_LOOKUP_FORCE_SVG) 81 self.unlocked_pixbuf = icon_theme.load_icon ("stock_lock-open", 22, 82 gtk.ICON_LOOKUP_FORCE_SVG) 83 84 self.profile = profile 85 self.profile.connect ("changed", self.handle_profile_change)
86 87 88 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_ADMIN_TOOL)
89 - def handle_profile_change (self, profile, new_change):
90 default_mandatory = False 91 ignore = new_change.get_ignore_default () 92 iter = self.find (new_change.get_source (), new_change.get_id ()) 93 old_change = None 94 if iter: 95 ignore = self[iter][self.COLUMN_IGNORE] 96 default_mandatory = self[iter][self.COLUMN_MANDATORY] 97 old_change = self[iter][self.COLUMN_CHANGE] 98 self.remove (iter) 99 100 mandatory = new_change.get_mandatory () 101 if mandatory == None: 102 mandatory = default_mandatory 103 104 if mandatory: 105 lock_pixbuf = self.locked_pixbuf 106 else: 107 lock_pixbuf = self.unlocked_pixbuf 108 109 discard_change = False 110 if old_change: 111 discard_change = new_change.merge_old_change (old_change) 112 113 if not discard_change: 114 row = self.prepend () 115 self.set (row, 116 self.COLUMN_CHANGE, new_change, 117 self.COLUMN_IGNORE, ignore, 118 self.COLUMN_MANDATORY, mandatory, 119 self.COLUMN_LOCK_PIXBUF, lock_pixbuf, 120 self.COLUMN_DESCRIPTION, new_change.get_short_description ()) 121 self.emit ("changed", new_change)
122
123 - def clear (self):
124 gtk.ListStore.clear (self) 125 self.emit ("changed", None)
126
127 - def find (self, source, id):
128 iter = self.get_iter_first () 129 while iter: 130 next = self.iter_next (iter) 131 change = self[iter][self.COLUMN_CHANGE] 132 if change.get_source () == source and \ 133 change.get_id () == id: 134 return iter 135 iter = next 136 return None
137 138 139 gobject.type_register (ProfileChangesModel) 140
141 -class SessionWindow:
142 - def __init__ (self, profile_name, profile_path, display_number):
143 self.profile_name = profile_name 144 self.profile_path = profile_path 145 self.display_number = display_number 146 147 self.profile = userprofile.UserProfile (profile_path) 148 149 self.changes_model = ProfileChangesModel (self.profile) 150 self.changes_model.connect ("changed", self.__changes_model_changed) 151 self.changes_window = None 152 self.lockdown_window = None 153 154 self.last_save_time = 0 155 156 self.window = gtk.Window () 157 self.window.set_title (_("Editing profile %s")%profile_name) 158 self.window.set_icon_name ("sabayon") 159 self.window.connect ("delete-event", 160 self.__handle_delete_event); 161 162 self.box = gtk.VBox () 163 self.window.add (self.box) 164 self.box.show () 165 166 self.__setup_menus () 167 self.__setup_session () 168 self.__setup_statusbar () 169 170 self.__set_needs_saving (False)
171 172 173 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_ADMIN_TOOL)
174 - def __add_widget (self, ui_manager, widget):
175 self.box.pack_start (widget, False, False, 0)
176
177 - def __setup_menus (self):
178 actions = [ 179 ("ProfileMenu", None, _("_Profile")), 180 ("Save", gtk.STOCK_SAVE, _("_Save"), "<control>S", _("Save profile"), self.__handle_save), 181 ("Quit", gtk.STOCK_QUIT, _("_Quit"), "<control>Q", _("Close the current window"), self.__handle_quit), 182 ("EditMenu", None, _("_Edit")), 183 ("Changes", gtk.STOCK_EDIT, _("_Changes"), "<control>H", _("Edit changes"), self.__handle_edit), 184 ("Lockdown", None, _("_Lockdown"),"<control>L", _("Edit Lockdown settings"), self.__handle_lockdown), 185 ("HelpMenu", None, _("_Help")), 186 ("Contents", gtk.STOCK_ABOUT, _("_Contents"),None, _("Help Contents"), self.__handle_help), 187 ("About", gtk.STOCK_ABOUT, _("_About"), None, _("About Sabayon"), self.__handle_about), 188 ] 189 toggle_actions = [ 190 ("EnforceMandatory", None, _("Enforce Mandatory"), None, _("Enforce mandatory settings in the editing session"), self.__handle_enforce_mandatory, True), 191 ] 192 action_group = gtk.ActionGroup ("WindowActions") 193 action_group.add_actions (actions) 194 action_group.add_toggle_actions (toggle_actions) 195 196 self.ui_manager = gtk.UIManager () 197 self.ui_manager.insert_action_group (action_group, 0) 198 self.ui_manager.connect ("add-widget", self.__add_widget) 199 self.ui_manager.add_ui_from_string (_ui_string) 200 self.ui_manager.ensure_update () 201 202 self.window.add_accel_group (self.ui_manager.get_accel_group ()) 203 204 self.save_action = action_group.get_action ("Save")
205
206 - def __set_needs_saving (self, needs_saving):
207 if needs_saving: 208 if not self.last_save_time: 209 self.last_save_time = int (time.time ()) 210 self.save_action.set_sensitive (True) 211 else: 212 self.last_save_time = 0 213 self.save_action.set_sensitive (False)
214 215 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_ADMIN_TOOL)
216 - def __changes_model_changed (self, model, change):
217 self.__set_needs_saving (not model.get_iter_first () is None)
218
219 - def __do_save (self):
220 # The changes model stores changes in reverse chronological order, so that 221 # the latest changes are always visible at the top of the window. To apply 222 # the changes, we need them in chronological order. So, we collect 223 # all the changes and reverse that list before committing the changes. 224 225 all_changes = [] 226 iter = self.changes_model.get_iter_first () 227 while iter: 228 change = self.changes_model[iter][ProfileChangesModel.COLUMN_CHANGE] 229 ignore = self.changes_model[iter][ProfileChangesModel.COLUMN_IGNORE] 230 mandatory = self.changes_model[iter][ProfileChangesModel.COLUMN_MANDATORY] 231 all_changes.append ((change, ignore, mandatory)) 232 iter = self.changes_model.iter_next (iter) 233 234 all_changes.reverse () 235 236 # Commit the changes! 237 238 for (change, ignore, mandatory) in all_changes: 239 if not ignore: 240 dprint ("Committing: %s, mandatory = %s", change.get_id (), mandatory) 241 try: 242 change.get_source ().commit_change (change, mandatory) 243 except: 244 errors.errors_log_recoverable_exception (debuglog.DEBUG_LOG_DOMAIN_ADMIN_TOOL, 245 "got an exception while commiting a change") 246 247 # Done 248 249 self.changes_model.clear () 250 self.profile.sync_changes ()
251
252 - def __do_saveconfirm (self):
253 if self.last_save_time: 254 dialog = saveconfirm.SaveConfirmationAlert (self.window, 255 self.profile_name, 256 time.time () - self.last_save_time) 257 response = dialog.run () 258 dialog.destroy () 259 260 if response == gtk.RESPONSE_CANCEL or \ 261 response == gtk.RESPONSE_DELETE_EVENT: 262 return False 263 if response == gtk.RESPONSE_YES: 264 self.__do_save () 265 266 return True
267 268 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_USER)
269 - def __handle_save (self, action):
270 self.__do_save ()
271 272 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_USER)
273 - def __handle_quit (self, action):
274 if self.__do_saveconfirm (): 275 self.window.destroy ()
276 277 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_USER)
278 - def __handle_delete_event (self, window, event):
279 return not self.__do_saveconfirm ()
280 281 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_USER)
282 - def __handle_help (self, action):
283 try: 284 gtk.show_uri (None, "ghelp:sabayon", gtk.get_current_event_time()) 285 except gobject.GError, e: 286 pass
287 288 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_USER)
289 - def __handle_about (self, action):
290 aboutdialog.show_about_dialog (self.window)
291 292 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_USER)
293 - def __handle_edit (self, action):
294 if not self.changes_window: 295 self.changes_window = changeswindow.ChangesWindow (self.changes_model, 296 self.profile_name, 297 self.window) 298 self.changes_window.window.connect ("delete-event", 299 gtk.Widget.hide_on_delete) 300 self.changes_window.window.present ()
301 302 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_USER)
303 - def __handle_lockdown (self, action):
304 if not self.lockdown_window: 305 debuglog.uprint ("Creating new Lockdown window") 306 applier = LockdownApplierSabayon (self.profile, self.changes_model) 307 self.lockdown_window = lockdowndialog.PessulusMainDialog (applier, False) 308 self.lockdown_window.window.set_title (_("Lockdown settings for %s")%self.profile_name) 309 else: 310 debuglog.uprint ("Presenting existing Lockdown window") 311 312 self.lockdown_window.window.present ()
313 314 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_USER)
315 - def __handle_enforce_mandatory (self, action):
316 active = action.get_active () 317 debuglog.uprint ("Setting enforce_mandatory to %s", active) 318 self.profile.set_enforce_mandatory (active)
319 320 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_ADMIN_TOOL)
321 - def __session_finished (self, session):
322 self.window.destroy ()
323 324 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_ADMIN_TOOL)
325 - def __session_mapped (self, session_widget, event):
326 dprint ("Session widget mapped; starting prototype session") 327 self.session_widget.disconnect (self.mapped_handler_id) 328 self.mapped_handler_id = 0 329 self.session.connect ("finished", self.__session_finished) 330 self.session.start (str (self.session_widget.session_window.xid)) 331 return False
332
333 - def __setup_session (self):
334 self.session = protosession.ProtoSession (self.profile_name, self.display_number) 335 336 try: 337 self.session.apply_profile () 338 except errors.RecoverableApplyErrorException, e: 339 errors_log_recoverable_exception (e) 340 dialog = gtk.MessageDialog (parent = None, 341 flags = gtk.DIALOG_MODAL, 342 type = gtk.MESSAGE_ERROR, 343 buttons = gtk.BUTTONS_NONE, 344 message_format = _("There was a recoverable error while applying the " 345 "user profile '%s'. You can report this error now " 346 "or try to continue editing the user profile.")) 347 (REPORT, CONTINUE) = range (2) 348 dialog.add_button (_("_Report this error"), REPORT) 349 dialog.add_button (_("_Continue editing"), CONTINUE) 350 response = dialog.run () 351 dialog.destroy () 352 353 if response == REPORT: 354 raise # the toplevel will catch the RecoverableApplyErrorException and exit 355 356 except errors.FatalApplyErrorException, e: 357 raise # FIXME: do we need any special processing? Should we give the user 358 # the option of continuing editing? 359 360 self.profile.start_monitoring () 361 screen = gtk.gdk.screen_get_default () 362 width = (screen.get_width () * 3) / 4 363 height = (screen.get_height () * 3) / 4 364 365 dprint ("Creating %dx%d session widget", width, height) 366 367 self.session_widget = sessionwidget.SessionWidget (width, height) 368 self.box.pack_start (self.session_widget, True, True) 369 self.mapped_handler_id = self.session_widget.connect ("map-event", self.__session_mapped) 370 self.session_widget.show ()
371 372 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_ADMIN_TOOL)
373 - def __update_statusbar (self, model, change):
374 if change: 375 self.statusbar.pop (self.status_context_id) 376 self.statusbar.push (self.status_context_id, 377 change.get_short_description ())
378 379 @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_ADMIN_TOOL)
380 - def __update_resize_grip (self, window, event):
381 if event.changed_mask & gtk.gdk.WINDOW_STATE_MAXIMIZED: 382 if event.new_window_state & gtk.gdk.WINDOW_STATE_MAXIMIZED: 383 self.statusbar.set_has_resize_grip (False) 384 else: 385 self.statusbar.set_has_resize_grip (True)
386
387 - def __setup_statusbar (self):
388 self.statusbar = gtk.Statusbar () 389 self.box.pack_start (self.statusbar, False, False) 390 self.statusbar.show () 391 392 self.status_context_id = self.statusbar.get_context_id ("sabayon-change") 393 394 self.changes_model.connect ("changed", self.__update_statusbar) 395 396 self.window.connect ("window-state-event", self.__update_resize_grip)
397