def onClick(self, event): """Swap through the three states when the check box is clicked.""" if self.widgetId: w = self.getWxWidget() state = w.Get3StateValue() if (state == wx.CHK_UNDETERMINED and pr.getActive() is pr.getDefaults()): state = wx.CHK_UNCHECKED w.Set3StateValue(state) if state == wx.CHK_CHECKED: pr.getActive().setValue(self.widgetId, 'yes') newValue = 'yes' elif state == wx.CHK_UNCHECKED: pr.getActive().setValue(self.widgetId, 'no') newValue = 'no' else: pr.getActive().removeValue(self.widgetId) newValue = 'default' self.updateState() events.send(events.EditNotify(self.widgetId, newValue)) # Let wxWidgets process the event, too. event.Skip()
def create(name, gameComponent): """Create a new profile with the specified name. The identifier of the profile is generated automatically. The identifier is not visible to the user. @param name The visible name of the new profile. @param gameComponent The game that the profile will be using. @return The Profile object that was created. """ # Generate an unused identifier. identifier = _getNewProfileId() p = sb.profile.Profile(identifier) # Set the basic information of the profile. p.setName(name) p.setValue('game', gameComponent) # Add it to the list of profiles and send a notification. profiles.append(p) events.send(events.ProfileNotify(p)) setActive(p) return p
def refresh(): """Empties the data of all loaded addons from the database and reloads everything. Sends out a 'addon-database-reloaded' notification.""" init() loadAll() events.send(events.Notify("addon-database-reloaded"))
def onWindowClose(self, ev): """Handle the window close event that is sent when the user tries to close the main window. Broadcasts a 'quit' events.Notify object so that all the listeners will know that the application is closing down. @param ev A wxCloseEvent object. """ # Send a 'quit' command so that everyone has a chance to save # their current status. events.send(events.Notify('quit')) # Save window size to UserHome/conf/window.conf. winSize = self.GetSizeTuple() fileName = os.path.join(paths.getUserPath(paths.CONF), 'window.conf') try: f = file(fileName, 'w') f.write('# This file is generated automatically.\n') f.write('appearance main (\n') f.write(' width = %i\n height = %i\n' % winSize) f.write(' x = %i\n y = %i\n' % self.GetPositionTuple()) if self.splitPos != None: f.write(' split-position = %i\n' % self.splitPos) f.write(' profile-split-position = %i\n' % self.profSplitPos) f.write(')\n') except: # Window size not saved. pass # Destroy the main frame. self.Destroy() return True
def setValue(self, settingId, newValue, doNotify=True): """Set the value of the setting in this profile. @param settingId Identifier of the setting to set. @param newValue New value for the setting (as a string). @param doNotify If True, a notification event will be sent. """ value = None if st.isDefined(settingId): # System settings are not saved into profiles. return # Change the value of an existing Value. for v in self.values: if v.getId() == settingId: value = v if value: value.setValue(newValue) else: value = Value(settingId, newValue) self.values.append(value) # A kludge for detecting a change of the language. if self is profdb.defaults and settingId == 'language': language.change(newValue) # Send a notification of this. if doNotify: events.send(events.ValueNotify(settingId, newValue, self))
def onToggle(self, ev): """Handle a wxWidgets toggle button click event.""" # Since this is supposed to be a normal button and not a # toggle button, return to the Off-state automatically. self.getWxWidget().SetValue(False) events.send(events.Command(self.widgetId)) # This causes a reaction. self.react()
def onItemSelected(self, ev): """Handle the wxWidgets event that is sent when the current selection changes in the list box. @param ev A wxWidgets event. """ ev.Skip() events.send(events.SelectNotify(self.widgetId, self.items[ev.GetSelection()][0]))
def prepareWindows(): """Layout windows and make everything ready for action.""" events.send(events.Notify('preparing-windows')) app.mainFrame.updateMenus() app.mainFrame.updateLayout() app.mainFrame.Show() app.mainFrame.Freeze()
def onFocus(self, event): """Handle the wxWidgets event that's sent when the widget received input focus. @param event A wxWidgets event. """ # Send a focus event if a focus identifier has been specified. if self.focusId: events.send(events.FocusNotify(self.focusId)) event.Skip()
def onItemDeselected(self, event): """Handle the wxWidgets item deselection event. @param event A wxWidgets event. """ if self.widgetId: item = self.items[self.getWxWidget().GetItemData(event.GetIndex())] events.send(events.DeselectNotify(self.widgetId, item)) event.Skip()
def onTabChange(self, ev): """Handle the wxWidgets event that occurs when the currently selected tab changes.""" # Figure out which panel is now visible. tabPanel = self.getWxWidget().GetPage(ev.GetSelection()) # Send a notification event. notification = events.SelectNotify(self.widgetId, self.__lookupPanel(tabPanel)) events.send(notification)
def onThumbTrack(self, ev): if self.widgetId: newValue = self.getValue() if newValue != self.oldValue: pr.getActive().setValue(self.widgetId, str(newValue)) # Send a notification. events.send(events.EditNotify(self.widgetId, newValue)) self.oldValue = newValue
def hide(identifier): """Mark a profile hidden.""" prof = get(identifier) if prof is defaults: # Hiding the defaults is not possible. return prof.setHidden(True) events.send(events.ProfileNotify(prof))
def onClick(self, ev): """Clicking a label causes a focus request to be sent.""" focusId = self.focusId if not focusId: focusId = self.widgetId if focusId: event = events.FocusRequestNotify(focusId) events.send(event) ev.Skip()
def refresh(hasChanged=False): """Send a notification that will cause everybody to refresh their state with regards to the active profile. @todo Move this out of the module so the import is unnecessary? """ ui.freeze() event = events.ActiveProfileNotify(active, hasChanged) events.send(event) ui.unfreeze()
def removeValue(self, settingId): """Remove this value from the profile entirely. @param settingId Identifier of the setting. """ # Kludge for detecting the change of the language. if self is profdb.defaults and settingId == 'language': language.change(None) for v in self.values: if v.getId() == settingId: self.values.remove(v) events.send(events.ValueNotify(settingId, None, self))
def __new__(meta, class_name, bases, new_attrs): post_funcs = [] early_funcs = [] events.send(events.ClassCreateSignal, bases[0], class_name, bases, new_attrs, post_funcs, early_funcs) cls = type.__new__(meta, class_name, bases, new_attrs) for func in early_funcs: func(cls) if new_attrs.has_key("__classinit__"): cls.__classinit__ = staticmethod(cls.__classinit__.im_func) cls.__classinit__(cls, new_attrs) for func in post_funcs: func(cls) return cls
def duplicate(identifier, name): if identifier == 'defaults': return # Generate an unused identifier. p = get(identifier).duplicate(_getNewProfileId()) p.setName(name) # Add it to the list of profiles and send a notification. profiles.append(p) events.send(events.ProfileNotify(p)) setActive(p) return p
def startMainLoop(): """Start the wxWidgets main loop.""" # Before we give wxPython all control, update the layout of UI # areas. app.showMainWindow() # Display any issues that came up during init. logger.show() # Notify everyone that the main loop is about to kick in. events.send(events.Notify('init-done')) app.MainLoop()
def __new__(meta, class_name, bases, new_attrs): post_funcs = [] early_funcs = [] events.send(events.ClassCreateSignal, bases[0], class_name, bases, new_attrs, post_funcs, early_funcs) cls = type.__new__(meta, class_name, bases, new_attrs) for func in early_funcs: func(cls) if '__classinit__' in new_attrs: cls.__classinit__ = staticmethod(cls.__classinit__.im_func) cls.__classinit__(cls, new_attrs) for func in post_funcs: func(cls) return cls
def onItemSelected(self, event): """Handle the wxWidgets item selection event. @param event A wxWidgets event. """ if self.widgetId and self.style == List.STYLE_COLUMNS: # Identifier of the selected item is sent with the # notification. item = self.items[self.getWxWidget().GetItemData(event.GetIndex())] # Send a notification about the selected item. events.send(events.SelectNotify(self.widgetId, item)) event.Skip() self.react()
def onRightDown(self, event): """Right button down.""" w = self.getWxWidget() #print "right down in list " + str(event.GetPosition()) item = w.HitTest(event.GetPosition()) #print item if item >= 0: w.SetSelection(item) if self.widgetId: events.send(events.SelectNotify( self.widgetId, self.items[item][0])) event.Skip()
def onRightDown(self, event): """Right button down.""" w = self.getWxWidget() #print "right down in list " + str(event.GetPosition()) item = w.HitTest(event.GetPosition()) #print item if item >= 0: w.SetSelection(item) if self.widgetId: events.send( events.SelectNotify(self.widgetId, self.items[item][0])) event.Skip()
def onRightClick(self, ev): # Let wxWidgets handle the event, too. widget = self.getWxWidget() if not widget.IsEnabled(): return # Going up or down? if ev.ButtonDown(): # Mouse right down doesn't do anything. return # Request an update to the popup menu. if self.menuId: events.send(events.Notify(self.menuId + '-update-request')) # Display the popup menu. if self.menuItems: menu = wx.Menu() self.commandMap = {} idCounter = 10000 # Create the menu items. for item in self.menuItems: if type(item) == tuple: itemId = item[0] itemCommand = item[1] else: itemId = item itemCommand = item if itemId == '-': # This is just a separator. menu.AppendSeparator() continue # Generate a new ID for the item. wxId = idCounter idCounter += 1 self.commandMap[wxId] = itemCommand menu.Append( wxId, widgets.uniConv(language.translate('menu-' + itemId))) wx.EVT_MENU(widget, wxId, self.onPopupCommand) # Display the menu. The callback gets called during this. widget.PopupMenu(menu, ev.GetPosition()) menu.Destroy()
def onRightClick(self, ev): # Let wxWidgets handle the event, too. widget = self.getWxWidget() if not widget.IsEnabled(): return # Going up or down? if ev.ButtonDown(): # Mouse right down doesn't do anything. return # Request an update to the popup menu. if self.menuId: events.send(events.Notify(self.menuId + '-update-request')) # Display the popup menu. if self.menuItems: menu = wx.Menu() self.commandMap = {} idCounter = 10000 # Create the menu items. for item in self.menuItems: if type(item) == tuple: itemId = item[0] itemCommand = item[1] else: itemId = item itemCommand = item if itemId == '-': # This is just a separator. menu.AppendSeparator() continue # Generate a new ID for the item. wxId = idCounter idCounter += 1 self.commandMap[wxId] = itemCommand menu.Append(wxId, widgets.uniConv(language.translate('menu-' + itemId))) wx.EVT_MENU(widget, wxId, self.onPopupCommand) # Display the menu. The callback gets called during this. widget.PopupMenu(menu, ev.GetPosition()) menu.Destroy()
def install(sourceName): """Installs the specified addon into the user's addon database. @param fileName Full path of the addon to install. @throw AddonFormatError If the addon is not one of the recognized formats, this exception is raised. @return Identifier of the installed addon. @throws Exception The installation failed. """ isManifest = paths.hasExtension("manifest", sourceName) if isManifest: # Manifests are copied to the manifest directory. destPath = paths.getUserPath(paths.MANIFESTS) else: destPath = paths.getUserPath(paths.ADDONS) destName = os.path.join(destPath, os.path.basename(sourceName)) try: # Directories must be copied as a tree. if os.path.isdir(sourceName): shutil.copytree(sourceName, destName) else: shutil.copy2(sourceName, destName) # Load the new addon. if not isManifest: identifier = load(destName) else: identifier = loadManifest(destName) # Now that the addon has been installed, save the updated cache. writeMetaCache() # Send a notification of the new addon. notify = events.Notify("addon-installed") notify.addon = identifier events.send(notify) except Exception, x: # Unsuccessful. raise x
def install(sourceName): """Installs the specified addon into the user's addon database. @param fileName Full path of the addon to install. @throw AddonFormatError If the addon is not one of the recognized formats, this exception is raised. @return Identifier of the installed addon. @throws Exception The installation failed. """ isManifest = paths.hasExtension('manifest', sourceName) if isManifest: # Manifests are copied to the manifest directory. destPath = paths.getUserPath(paths.MANIFESTS) else: destPath = paths.getUserPath(paths.ADDONS) destName = os.path.join(destPath, os.path.basename(sourceName)) try: # Directories must be copied as a tree. if os.path.isdir(sourceName): shutil.copytree(sourceName, destName) else: shutil.copy2(sourceName, destName) # Load the new addon. if not isManifest: identifier = load(destName) else: identifier = loadManifest(destName) # Now that the addon has been installed, save the updated cache. writeMetaCache() # Send a notification of the new addon. notify = events.Notify('addon-installed') notify.addon = identifier events.send(notify) except Exception, x: # Unsuccessful. raise x
def uninstall(identifier, doNotify=False): """Uninstall an addon. Uninstalling an addon causes a full reload of the entire addon database. @param identifier Identifier of the addon to uninstall. @param doNotify True, if a notification should be sent. """ addon = get(identifier) path = addon.getSource() destPath = os.path.join(paths.getUserPath(paths.UNINSTALLED), os.path.basename(path)) if os.path.exists(destPath): # Simply remove any existing addons with the same name. # TODO: Is this wise? Rename old. if os.path.isdir(destPath): shutil.rmtree(destPath) else: os.remove(destPath) shutil.move(path, destPath) # Is there a manifest to uninstall? manifest = addon.getId() + '.manifest' manifestFile = os.path.join(paths.getUserPath(paths.MANIFESTS), manifest) if os.path.exists(manifestFile): # Move it to the uninstalled folder. destPath = os.path.join(paths.getUserPath(paths.UNINSTALLED), manifest) if os.path.exists(destPath): os.remove(destPath) shutil.move(manifestFile, destPath) # Mark as uninstalled. addon.uninstall() # Detach from all profiles. for p in sb.profdb.getProfiles(): p.dontUseAddon(identifier) # Send a notification. if doNotify: notify = events.Notify('addon-uninstalled') notify.addon = identifier events.send(notify)
def onLeftUp(self, ev): """Handle the wxWidgets event when the user clicks on the list widget. @param ev A wxWidgets event object. """ w = self.getWxWidget() item, flags = w.HitTest(ev.GetPosition()) if item < 0: ev.Skip() return if flags & wx.LIST_HITTEST_ONITEMICON: e = events.Notify(self.widgetId + '-icon-click') e.item = self.items[w.GetItemData(item)] events.send(e) ev.Skip()
def __new__(meta, class_name, bases, new_attrs): post_funcs = [] early_funcs = [] events.send(events.ClassCreateSignal, bases[0], class_name, bases, new_attrs, post_funcs, early_funcs) cls = type.__new__(meta, class_name, bases, new_attrs) for func in early_funcs: func(cls) if new_attrs.has_key('__classinit__'): cls.__classinit__ = staticmethod(cls.__classinit__.im_func) cls.__classinit__(cls, new_attrs) if new_attrs.has_key('__init__'): lock = threading.RLock() cls.__init__ = threadSafeMethod(lock)(cls.__init__) for func in post_funcs: func(cls) return cls
def uninstall(identifier, doNotify=False): """Uninstall an addon. Uninstalling an addon causes a full reload of the entire addon database. @param identifier Identifier of the addon to uninstall. @param doNotify True, if a notification should be sent. """ addon = get(identifier) path = addon.getSource() destPath = os.path.join(paths.getUserPath(paths.UNINSTALLED), os.path.basename(path)) if os.path.exists(destPath): # Simply remove any existing addons with the same name. # TODO: Is this wise? Rename old. if os.path.isdir(destPath): shutil.rmtree(destPath) else: os.remove(destPath) shutil.move(path, destPath) # Is there a manifest to uninstall? manifest = addon.getId() + ".manifest" manifestFile = os.path.join(paths.getUserPath(paths.MANIFESTS), manifest) if os.path.exists(manifestFile): # Move it to the uninstalled folder. destPath = os.path.join(paths.getUserPath(paths.UNINSTALLED), manifest) if os.path.exists(destPath): os.remove(destPath) shutil.move(manifestFile, destPath) # Mark as uninstalled. addon.uninstall() # Detach from all profiles. for p in sb.profdb.getProfiles(): p.dontUseAddon(identifier) # Send a notification. if doNotify: notify = events.Notify("addon-uninstalled") notify.addon = identifier events.send(notify)
def addAddon(self, identifier): """Attaches an addon to the profile. If this is the Defaults profile, the addon is detached from all the other profiles. This is necessary because of the exclusion rule (see getUsedAddons()). @param identifier Addon identifier. """ if identifier not in self.addons: self.addons.append(identifier) # Also send a notification. events.send(events.AttachNotify(identifier, self, True)) if self is profdb.defaults: # Detach from others. The appropriate detach events # will be transmitted. for p in profdb.getProfiles(): if p is not self: p.removeAddon(identifier)
def change(language): """Change the language at runtime. @param language Identifier of the new language. """ try: if language: actual = language else: actual = 'language-english' # Select the new language. select(actual[9:]) # Send a notification so others may update their content. events.send(events.LanguageNotify(actual)) except KeyError: # Language doesn't exist? pass
def onItemSelected(self, ev): """Handle the wxWidgets event that is sent when the current selection changes in the list box. @param ev A wxWidgets event. """ if self.widgetId: newSelection = self.items[ev.GetSelection()] if sb.confdb.isSettingDefined(self.widgetId): # This is a setting. if newSelection == 'default': pr.getActive().removeValue(self.widgetId) else: pr.getActive().setValue(self.widgetId, newSelection) # Notify everyone of the change. events.send(events.EditNotify(self.widgetId, newSelection)) else: # Normal list. events.send(events.SelectNotify(self.widgetId, newSelection))
def onEnterWidget(self, event): """Handle the wxWidget event of entering the window.""" if self.focusId: events.send(events.FocusNotify(self.focusId)) event.Skip()
def onPopupCommand(self, ev): events.send(events.Command(self.commandMap[ev.GetId()]))
def onClick(self, ev): """Handle a wxWidgets button click event.""" events.send(events.Command(self.widgetId)) # This causes a reaction. self.react()
def onColumnClick(self, ev): events.send( events.Notify(self.columns[ev.GetColumn()] + '-column-click')) ev.Skip()
def onItemDoubleClick(self, ev): ev.Skip() events.send( events.SelectNotify(self.widgetId, self.items[ev.GetSelection()][0], True))
def onSelectionChanged(self, treeEvent): """Send a notification that the selection has changed in the tree.""" item = treeEvent.GetItem() events.send(events.SelectNotify(self.widgetId, self.lookupItem(item)))
def onLeftClick(self, event): if self.widgetId: events.send(events.FocusNotify(self.widgetId)) self.getWxWidget().SetFocus() event.Skip()
def commandHandler(event): """This is called when a Command event is broadcasted.""" global profileListDisabled if event.hasId('freeze'): profileListDisabled = True elif event.hasId('unfreeze'): profileListDisabled = False #notifyHandler(events.Notify('active-profile-changed')) pr.refresh() elif event.hasId('new-profile'): dialog, area = sb.util.dialog.createButtonDialog('new-profile-dialog', ['cancel', 'ok'], 'ok', resizable=False) dialog.disableWidget('ok') entry = area.createArea(alignment=ALIGN_HORIZONTAL) entry.setExpanding(False) # The name of the profile. entry.setWeight(1) entry.setBorder(4, ui.BORDER_RIGHT) entry.createText('new-profile-name', ':', align=wt.Text.RIGHT) entry.setBorder(0) entry.setWeight(2) nameField = entry.createTextField('') nameField.focus() # Only enable the OK button if there is something in the name field. def buttonEnabler(): dialog.enableWidget('ok', len(nameField.getText()) > 0) nameField.addReaction(buttonEnabler) # The game component. entry = area.createArea(alignment=ALIGN_HORIZONTAL) entry.setExpanding(False) entry.setWeight(1) entry.setBorder(4, ui.BORDER_RIGHT) entry.createText('new-profile-game', ':', align=wt.Text.RIGHT) entry.setBorder(0) entry.setWeight(2) gameDrop = entry.createDropList('') for game in st.getGameComponents(): gameDrop.addItem(game.getId()) gameDrop.sortItems() # Select the first one. gameDrop.selectItem(gameDrop.getItems()[0]) if dialog.run() == 'ok': # Get the values from the controls. pr.create(nameField.getText(), gameDrop.getSelectedItem()) elif event.hasId('rename-profile'): dialog, area = sb.util.dialog.createButtonDialog( 'rename-profile-dialog', ['cancel', 'ok'], 'ok', resizable=False) prof = pr.getActive() # A text field of the new name. entry = area.createArea(alignment=ALIGN_HORIZONTAL) entry.setExpanding(False) # The name of the profile. entry.setWeight(1) entry.setBorder(4, ui.BORDER_RIGHT) entry.createText('rename-profile-name', ':', align=wt.Text.RIGHT) entry.setBorder(0) entry.setWeight(2) nameField = entry.createTextField('') nameField.setText(prof.getName()) nameField.focus() # Only enable the OK button if there is something in the name # field. def buttonEnabler(): dialog.enableWidget('ok', len(nameField.getText()) > 0) nameField.addReaction(buttonEnabler) if dialog.run() == 'ok': prof.setName(nameField.getText()) # The profile list needs to be updated, too. events.send(events.ProfileNotify(prof)) pr.refresh() elif event.hasId('reset-profile'): dialog, area = sb.util.dialog.createButtonDialog( 'reset-profile-dialog', [ 'cancel', 'reset-profile-values', 'reset-profile-addons', 'reset-profile-everything' ], 'cancel', resizable=False) text = language.translate('reset-profile-query') message = area.createRichText( language.expand(text, pr.getActive().getName())) # Accept these as dialog-closing commands. dialog.addEndCommand('reset-profile-values') dialog.addEndCommand('reset-profile-addons') dialog.addEndCommand('reset-profile-everything') result = dialog.run() if result == 'cancel': return resetValues = True resetAddons = True if result == 'reset-profile-values': resetAddons = False elif result == 'reset-profile-addons': resetValues = False pr.reset(pr.getActive().getId(), resetValues, resetAddons) pr.refresh() elif event.hasId('delete-profile'): # If this is a system profile, just hide it. if pr.getActive().isSystemProfile(): pr.hide(pr.getActive().getId()) return dialog, area = sb.util.dialog.createButtonDialog( 'delete-profile-dialog', ['no', 'yes'], 'no', resizable=False) text = language.translate('delete-profile-query') area.createRichText(language.expand(text, pr.getActive().getName())) if dialog.run() == 'yes': # Get the values from the controls. pr.remove(pr.getActive().getId()) elif event.hasId('duplicate-profile'): dialog, area = sb.util.dialog.createButtonDialog( 'duplicate-profile-dialog', ['cancel', 'ok'], 'ok', resizable=False) text = language.translate('duplicating-profile') area.setWeight(1) area.createRichText(language.expand(text, pr.getActive().getName())) area.setWeight(1) entry = area.createArea(alignment=ALIGN_HORIZONTAL) entry.setExpanding(False) # The name of the profile. entry.setWeight(1) entry.setBorder(4, ui.BORDER_RIGHT) entry.createText('new-profile-name', ':', align=wt.Text.RIGHT) entry.setBorder(0) entry.setWeight(3) nameField = entry.createTextField('') nameField.setText(pr.getActive().getName()) nameField.focus() # Only enable the OK button if there is something in the name field. def buttonEnabler(): dialog.enableWidget('ok', len(nameField.getText()) > 0) nameField.addReaction(buttonEnabler) if dialog.run() == 'ok': pr.duplicate(pr.getActive().getId(), nameField.getText()) elif event.hasId('hide-profile'): pr.hide(pr.getActive().getId()) elif event.hasId('unhide-profiles'): # Display a dialog bog for unhiding hidden profiles. hiddenProfiles = pr.getProfiles(lambda p: p.isHidden()) dialog, area = sb.util.dialog.createButtonDialog( 'unhide-profile-dialog', ['cancel', 'ok'], 'ok', resizable=False) area.setWeight(0) area.createText('unhiding-profiles') area.setWeight(3) area.setBorder(0) profList = area.createList('', sb.widget.list.List.STYLE_CHECKBOX) profList.setMinSize(50, 150) def selectAll(): # Check the entire list. for item in profList.getItems(): profList.checkItem(item, True) def clearAll(): # Uncheck the entire list. for item in profList.getItems(): profList.checkItem(item, False) area.setWeight(0) controls = area.createArea(alignment=ALIGN_HORIZONTAL, border=2) controls.setWeight(0) button = controls.createButton( 'unhide-select-all', style=sb.widget.button.Button.STYLE_MINI) button.addReaction(selectAll) button.resizeToBestSize() button = controls.createButton( 'unhide-clear-all', style=sb.widget.button.Button.STYLE_MINI) button.addReaction(clearAll) button.resizeToBestSize() for prof in hiddenProfiles: profList.addItem(prof.getId()) if dialog.run() == 'ok': # Unhide all the selected items. selected = profList.getSelectedItems() for sel in selected: pr.show(sel) if len(selected): # Select the first one that was shown. pr.setActive(pr.get(selected[0]))
def handleCommand(event): """This is called when someone sends a command event. @param event An events.Command object.""" # Figure out which addon has been currently selected in the addons # tree. The action will target this addon. selected = '' if event.hasId('refresh-addon-database'): ao.refresh() elif event.hasId('install-addon'): tab30.installer.run() elif event.hasId('uninstall-addon'): selectedItems = addonList.getSelectedItems() # Only take the uninstallable addons. addons = filter(lambda i: ao.isUninstallable(i), selectedItems) if len(addons) == 0: return # Make sure the user wants to uninstall. dialog, area = sb.util.dialog.createButtonDialog( 'uninstall-addon-dialog', ['no', 'yes'], 'no', resizable=False) if len(addons) == 1: text = language.translate('uninstall-addon-query') area.setExpanding(True) area.createRichText( language.expand(text, language.translate(addons[0]))) else: area.setWeight(0) area.createText('uninstall-addon-query-several') msg = '<html><ul>' for addon in addons: msg += '<li>' + language.translate(addon) + '</li>' msg += '</ul></html>' ft = area.createFormattedText() ft.setText(msg) ft.setMinSize(400, 150) if dialog.run() == 'yes': for addon in addons: ao.uninstall(addon) ao.refresh() elif event.hasId('addon-info'): sel = addonList.getSelectedItems() if len(sel) > 0: # Show the Addon Inspector. tab30.inspector.run(ao.get(sel[0])) elif event.hasId('addon-settings'): sel = addonList.getSelectedItems() if len(sel) > 0: command = events.Command('show-addon-settings') command.addon = sel[0] events.send(command) elif event.hasId('load-order'): # Open the Load Order dialog. tab30.loadorder.run(pr.getActive()) elif event.hasId('addon-list-check-selected'): for addon in addonList.getSelectedItems(): pr.getActive().useAddon(addon) elif event.hasId('addon-list-uncheck-selected'): for addon in addonList.getSelectedItems(): pr.getActive().dontUseAddon(addon) elif event.hasId('addon-list-check-all'): for addon in addonList.getItems(): pr.getActive().useAddon(addon) elif event.hasId('addon-list-uncheck-all'): for addon in addonList.getItems(): pr.getActive().dontUseAddon(addon) elif event.hasId('addon-show-parent-box'): for addon in addonList.getSelectedItems(): a = ao.get(addon) if a.getBox(): showInAddonList(a.getBox().getId()) break elif event.hasId('addon-show-box-category'): for addon in addonList.getSelectedItems(): a = ao.get(addon) if a.getType() == 'addon-type-box': addonList.deselectAllItems() tree.selectItem(a.getContentCategoryLongId()) refreshListIfVisible() break
def onPageChange(self, event): pageId = event.GetPage().getId() events.send(events.SelectNotify('wizard', pageId)) if self.pageReaction: self.pageReaction(event.GetPage())
def restore(): """Reads all the profiles from disk. Restores the currently active profile as well. This function has to be called after starting the program before any profiles are accessed. System profiles that aren't present in the user's profile directory are copied to the user's profile directory. """ global defaults global profiles global restoredActiveId # By default, restore selection to the Defaults profile. restoredActiveId = 'defaults' # Clear the current profile list. profiles = [] systemProfilePaths = [paths.getSystemPath(paths.PROFILES)] + \ paths.getBundlePaths(paths.PROFILES) userProfilePath = paths.getUserPath(paths.PROFILES) # List all the system and user profiles. availSystem = _listProfilesIn(systemProfilePaths) availUser = _listProfilesIn([userProfilePath]) # We are going to load only the profiles in the user's direcory, # but before that make sure that the user has an instance of each # system profile. for sysFile in availSystem: identifier = paths.getBase(sysFile) # Does this exist in the user's directory? gotIt = False for userFile in availUser: if paths.getBase(userFile) == identifier: gotIt = True break if not gotIt: # Since the system profile does not exist on the user, # copy it to the user profile directory. shutil.copyfile( sysFile, os.path.join(userProfilePath, os.path.basename(sysFile))) # Find every profile in system's and user's profile directories. # Files in the user's directory augment the files in the system # directory. for name in _listProfilesIn([userProfilePath]): load(os.path.join(userProfilePath, name), False) logger.show() defaults = get('defaults') if defaults is None: # Recreate the Defaults profile. load(os.path.join(systemProfilePath, "defaults.prof"), False) defaults = get('defaults') logger.show() # Set the default language. lang = defaults.getValue('language') if lang: language.change(lang.getValue()) # Send profile-loaded notifications. for p in profiles: if not p.isHidden(): events.send(events.ProfileNotify(p)) # Restore the previously active profile. prof = get(restoredActiveId) if prof: setActive(prof) else: setActive(defaults)
def generateOptions(profile): """Generate a text string of all the command line options used when launching a game. @param profile A profiles.Profile object. The values of settings are retrieved from here. @return All the options in a single string. Returns None if there is an unresolved addon conflict. """ clearLog() # Determine which addons are in use. The final list of addons # includes all the addons that must be loaded at launch (defaults; # required parts of boxes). usedAddonIds = profile.getFinalAddons() usedAddons = map(lambda id: ao.get(id), usedAddonIds) # Form the command line. cmdLine = [] # Determine the settings that apply to the components and # addons. effectiveSettings = st.getCompatibleSettings(profile) # Are there any system-specific options defined? if st.isDefined('common-options'): cmdLine.append(ex.evaluate(st.getSystemString('common-options'), None)) # All profiles use the same runtime directory. if st.isDefined('doomsday-base'): basePath = os.path.abspath(st.getSystemString('doomsday-base')) cmdLine.append('-basedir ' + paths.quote(basePath)) # Determine the components used by the profile. for compId in profile.getComponents(): # Append the component's options to the command line as-is. cmdLine.append( st.getComponent(compId).getOption() ) # Resolve conflicting addons by letting the user choose between # them. if not resolveAddonConflicts(usedAddons, profile): # Launch aborted! return None # Get the command line options for loading all the addons. for addon in usedAddons: params = addon.getCommandLine(profile).strip() if params: cmdLine.append(params) # Update IDs of used addons. usedAddonsId = map(lambda a: a.getId(), usedAddons) # Get the command line options from each setting. for setting in effectiveSettings: # If the setting's required addons are not being loaded, skip # this setting. skipThis = False for req in setting.getRequiredAddons(): if req not in usedAddonIds: skipThis = True break if skipThis: continue # All settings can't be used at all times. In # case of a conflict, some of the settings are ignored. if setting.isEnabled(profile): params = setting.getCommandLine(profile).strip() if params: cmdLine.append(params) # Send a launch options notification. This is done so that # plugins are able to observe/modify the list of launch options. events.send(events.LaunchNotify(cmdLine)) return string.join(cmdLine, ' ')
def handleCommand(event): if event.hasId('show-addon-paths'): events.send(events.Command('show-snowberry-settings')) prefTabs.selectTab('addon-paths') pathList.focus()
def show(identifier): """Clear the hide flag and show a profile.""" prof = get(identifier) prof.setHidden(False) events.send(events.ProfileNotify(prof))
def runWizard(): """Run the wizard dialog.""" # Make sure the help panel isn't updated during the wizard. #events.send(events.Command('freeze')) suggested = { 'doom1': 'DOOM.WAD', 'doom1-share': 'DOOM1.WAD', 'doom1-ultimate': 'DOOM.WAD', 'doom2': 'DOOM2.WAD', 'doom2-tnt': 'TNT.WAD', 'doom2-plut': 'PLUTONIA.WAD', 'heretic-ext': 'HERETIC.WAD', 'heretic-share': 'HERETIC.WAD', 'heretic': 'HERETIC.WAD', 'hexen': 'HEXEN.WAD', 'hexen-demo': 'HEXEN.WAD', 'hexen-dk': 'HEXEN.WAD', 'hacx': 'HACX.WAD', 'chex': 'CHEX.WAD' } events.mute() # Make the Defaults profile active. pr.setActive(pr.getDefaults()) wiz = WizardDialog(language.translate('setup-wizard'), paths.findBitmap('wizard')) # Language selection page. langPage = WizardPage(wiz, 'wizard-language') area = langPage.getArea() area.createText('wizard-language-explanation', maxLineLength=65).resizeToBestSize() sar, languageCheckBox = area.createSetting(st.getSetting('language')) languageCheckBox.getFromProfile(pr.getActive()) # Game selection. gamePage = ProfileWizardPage(wiz, 'wizard-games', None) gamePage.follows(langPage) area = gamePage.getArea() area.createText('wizard-select-games', maxLineLength=65).resizeToBestSize() area.setBorderDirs(ui.BORDER_NOT_BOTTOM) games = area.createList('', style=sb.widget.list.List.STYLE_CHECKBOX) area.setBorderDirs(ui.BORDER_NOT_TOP) games.setMinSize(50, 180) gamePage.setGameList(games) def allGames(): # Check the entire list. for item in games.getItems(): games.checkItem(item, True) def clearGames(): # Uncheck the entire list. for item in games.getItems(): games.checkItem(item, False) controls = area.createArea(alignment=ui.ALIGN_HORIZONTAL, border=2) controls.setWeight(0) button = controls.createButton('wizard-games-all', wb.Button.STYLE_MINI) button.addReaction(allGames) button.resizeToBestSize() button = controls.createButton('wizard-games-clear', wb.Button.STYLE_MINI) button.addReaction(clearGames) button.resizeToBestSize() # The pages will be linked together. previousPage = gamePage deathKingsWad = None # We'll do this dynamically. checkedProfiles = ['doom1', 'doom2', 'heretic', 'hexen'] # Only display the system profiles in the wizard (not any user # profiles). profiles = pr.getProfiles(lambda p: p.isSystemProfile()) for p in profiles: if p is not pr.getDefaults(): games.addItem(p.getId(), p.getId() in checkedProfiles) # Create a page for the profile. page = ProfileWizardPage(wiz, p.getId(), games) # Link the pages together. page.follows(previousPage) previousPage = page area = page.getArea() area.createText('wizard-locate-iwad', maxLineLength=65).resizeToBestSize() # The suggestion. if suggested.has_key(p.getId()): sugArea = area.createArea(alignment=ui.ALIGN_HORIZONTAL, border=2) sugArea.setExpanding(False) sugArea.setWeight(1) sugArea.createText('wizard-suggested-iwad', ':', align=wt.Text.RIGHT) sugArea.setWeight(2) sug = sugArea.createText('') sug.setText(suggested[p.getId()]) sar, page.iwadText = area.createSetting(st.getSetting('iwad')) if p.getId() == 'hexen-dk': area.setBorder(12, ui.BORDER_ALL) area.createLine() area.setBorder(6, ui.BORDER_ALL) area.createText('deathkings-selection-title').setHeadingStyle() # Death Kings is an extension to Hexen. It uses the # same Hexen IWAD, but also an addon IWAD. area.createText('wizard-locate-iwad-deathkings', maxLineLength=65).resizeToBestSize() entry = area.createArea(alignment=ui.ALIGN_HORIZONTAL, border=4) entry.setExpanding(False) entry.setWeight(1) entry.setBorderDirs(ui.BORDER_NOT_LEFT) deathKingsWad = entry.createTextField('') entry.setWeight(0) entry.setBorderDirs(ui.BORDER_TOP_BOTTOM) browseButton = entry.createButton('browse-button', wb.Button.STYLE_MINI) def browseDeathKings(): # Open a file browser. selection = sb.util.dialog.chooseFile('deathkings-selection-title', '', True, [('file-type-wad', 'wad')]) if len(selection) > 0: deathKingsWad.setText(selection) browseButton.addReaction(browseDeathKings) # Addon paths. pathsPage = ProfileWizardPage(wiz, 'wizard-addon-paths', games) pathsPage.follows(previousPage) area = pathsPage.getArea() area.createText('wizard-addon-paths-explanation', maxLineLength=65).resizeToBestSize() area.setBorderDirs(ui.BORDER_NOT_BOTTOM) pathList = area.createList('addon-paths-list') pathList.setMinSize(100, 120) # Insert the current custom paths into the list. for p in paths.getAddonPaths(): pathList.addItem(p) def addAddonPath(): selection = sb.util.dialog.chooseFolder('addon-paths-add-prompt', '') if selection: pathList.addItem(selection) pathList.selectItem(selection) def removeAddonPath(): selection = pathList.getSelectedItem() if selection: pathList.removeItem(selection) area.setWeight(0) area.setBorderDirs(ui.BORDER_NOT_TOP) commands = area.createArea(alignment=ui.ALIGN_HORIZONTAL, border=2) commands.setWeight(0) # Button for adding new paths. button = commands.createButton('new-addon-path', wb.Button.STYLE_MINI) button.addReaction(addAddonPath) # Button for removing a path. button = commands.createButton('delete-addon-path', wb.Button.STYLE_MINI) button.addReaction(removeAddonPath) # Launch options. quitPage = WizardPage(wiz, 'wizard-launching') quitPage.follows(pathsPage) area = quitPage.getArea() area.createText('wizard-launching-explanation').resizeToBestSize() sar, quitCheckBox = area.createSetting(st.getSetting('quit-on-launch')) quitCheckBox.getFromProfile(pr.getActive()) # List of unusable profiles, due to a missing IWAD. unusableProfiles = [] # When the page changes in the wizard, change the active profile. def changeActiveProfile(page): prof = pr.get(page.getId()) if prof: pr.setActive(prof) else: pr.setActive(pr.getDefaults()) # Update page with values from the current profile. page.update() wiz.setPageReaction(changeActiveProfile) if wiz.run(langPage) == 'ok': events.unmute() # Show the profiles that now have an IWAD. for prof in profiles: if prof.getValue('iwad', False) != None: pr.show(prof.getId()) if prof.getId() in unusableProfiles: unusableProfiles.remove(prof.getId()) else: pr.hide(prof.getId()) if prof.getId() not in unusableProfiles and \ prof.getId() in games.getSelectedItems(): unusableProfiles.append(prof.getId()) # Install the Death Kings WAD? if deathKingsWad: try: ident = ao.install(deathKingsWad.getText()) # Attach the WAD as an addon. kingsProfile = pr.get('hexen-dk') kingsProfile.useAddon(ident) except: # TODO: Handle error. pass # Update custom addon paths. currentAddonPaths = paths.getAddonPaths() wasModified = False for path in pathList.getItems(): if path not in currentAddonPaths: paths.addAddonPath(path) wasModified = True for path in currentAddonPaths: if path not in pathList.getItems(): paths.removeAddonPath(path) wasModified = True if wasModified: # Load addons from new paths. ao.refresh() events.send(events.Notify('addon-paths-changed')) # The wizard will only be shown once automatically. pr.getDefaults().setValue(HAS_BEEN_RUN, 'yes', False) else: # Wizard was canceled. events.unmute() pr.refresh() # This'll destroy all the pages of the wizard as well. wiz.destroy() # Enable help panel updates again. #events.send(events.Command('unfreeze')) # Tell the user about unusable profiles. if len(unusableProfiles) > 0: dialog, area = sb.util.dialog.createButtonDialog( 'wizard-unlaunchable-profiles', ['ok'], 'ok', resizable=False) # Compose a list of the unlaunchable profiles. profList = '' unusableProfiles.sort(lambda a, b: cmp(language.translate(a), language.translate(b))) for p in unusableProfiles: profList += "\n" + language.translate(p) msg = area.createText() msg.setText(language.translate('wizard-unlaunchable-profiles-listed') + "\n" + profList) msg.resizeToBestSize() dialog.run()
elif e.isList() and e.getName() == 'addons': # The addons attached to the profile. profile.setAddons(e.getContents()) elif e.isKey(): # A value for a setting? try: # No notifications will be sent. profile.setValue(e.getName(), e.getValue(), False) except KeyError: # Setting does not exist, ignore it. pass if notify: # The profile has been loaded. events.send(events.ProfileNotify(profile)) def _getNewProfileId(): # Choose the new identifier by comparing against the profiles # already loaded in memory. for i in range(10000): identifier = 'userprofile%i' % i if not get(identifier): # This is unused. break return identifier def create(name, gameComponent): """Create a new profile with the specified name. The identifier
def requestPopulation(): # Request others to create widgets in the setting tabs, if # necessary. for areaId, area in categoryTabs: events.send(events.PopulateNotify(areaId, area)) area.updateLayout()
def send(self, event_name, **keys): result1 = send(self._getMessage(event_name), **keys) result2 = send('%s.%d' % (event_name, id(self)), **keys) return [result1, result2]
def chooseAddons(dialogId, title, actionButton): """Opens an addon selection dialog. @param title Title of the dialog. @param actionButton The button that will perform the affirmative action of the dialog. """ dialog, area = sb.util.dialog.createButtonDialog(dialogId, ['cancel', actionButton], actionButton) dialog.addEndCommand('addon-dialog-add-to-custom-folders') # The action button is disabled until a valid selection has been # made. dialog.disableWidget(actionButton) area.setWeight(0) folderArea = area.createArea(alignment=ui.ALIGN_HORIZONTAL) folderArea.setExpanding(False) folderArea.setBorder(2, ui.BORDER_NOT_LEFT) folderArea.setWeight(0) folderArea.createText('addon-dialog-folder').resizeToBestSize() folderArea.setBorderDirs(ui.BORDER_ALL) folderArea.setWeight(1) pathField = folderArea.createTextField('') pathField.setText(os.getcwd()) pathField.select() pathField.focus() folderArea.setWeight(0) folderArea.setBorderDirs(ui.BORDER_NOT_RIGHT) browseButton = folderArea.createButton('addon-dialog-folder-browse', style=wg.Button.STYLE_MINI) #folderArea.addSpacer() #folderArea.setBorderDirs(ui.BORDER_NOT_RIGHT) folderCmdArea = area.createArea(alignment=ui.ALIGN_HORIZONTAL) folderCmdArea.setExpanding(False) folderCmdArea.setBorder(6, ui.BORDER_NOT_TOP) folderCmdArea.setWeight(1) folderCmdArea.addSpacer() folderCmdArea.setWeight(0) uninstButton = folderCmdArea.createButton( 'addon-dialog-folder-uninstalled') # Add to Custom Folders button. folderCmdArea.setBorder(6, ui.BORDER_LEFT | ui.BORDER_BOTTOM) addToMyButton = folderCmdArea.createButton( 'addon-dialog-add-to-custom-folders') def goToUninstalled(): pathField.setText(paths.getUserPath(paths.UNINSTALLED)) pathField.select() addToMyButton.disable() uninstButton.addReaction(goToUninstalled) area.createText('addon-dialog-found') area.setWeight(1) foundList = area.createList('', style=sb.widget.list.List.STYLE_COLUMNS) foundList.setMinSize(500, 300) area.setWeight(0) area.createText('addon-dialog-addons-copied', maxLineLength=70).resizeToBestSize() def selectAction(): dialog.enableWidget(actionButton) foundList.addReaction(selectAction) for col, width in [('name', None), ('type', 180)]: foundList.addColumn('addon-dialog-' + col, width) def updateList(): # Update the found addons list. foundList.clear() dialog.disableWidget(actionButton) extensions = ao.getAddonExtensions() + ['manifest'] # This should be done in addons.py. fileNames = os.listdir(pathField.getText()) for name in fileNames: type = '' for ext in extensions: if paths.hasExtension(ext, name): type = ext break if not type: # Unknown files are skipped. continue # Manifests don't appear in the list if the corresponding # addon is in the same directory. if paths.hasExtension('manifest', name): foundSame = False # Identifier of the addon the manifest belongs to. manifestId = paths.getBase(name) # See if the addon is in the list. for other in fileNames: if other == name: continue if manifestId == ao.formIdentifier(other)[0]: foundSame = True break if foundSame: # Don't add it. continue foundList.addItemWithColumns( name, name, language.translate('addon-dialog-type-' + type)) # Update reactions. def pathChanged(): if os.path.exists(pathField.getText()): updateList() if pathField.getText() != paths.getUserPath(paths.UNINSTALLED): addToMyButton.enable() else: addToMyButton.disable() def browseAction(): # Show a directory browser. selection = sb.util.dialog.chooseFolder('addon-dialog-browse-prompt', pathField.getText()) if len(selection): pathField.setText(selection) pathField.select() # The initial contents of the list. updateList() pathField.addReaction(pathChanged) browseButton.addReaction(browseAction) dialog.addEndCommand(actionButton) result = dialog.run() if result == actionButton: addonFiles = map(lambda name: os.path.join(pathField.getText(), name), foundList.getSelectedItems()) # Include any associated manifests. for name in addonFiles: manifest = ao.formIdentifier(name)[0] + '.manifest' manifestFile = os.path.join(pathField.getText(), manifest) if manifest not in addonFiles and os.path.exists(manifestFile): addonFiles.append(manifestFile) #print 'including ' + manifestFile + ' due to ' + name return addonFiles elif result == 'addon-dialog-add-to-custom-folders': paths.addAddonPath(pathField.getText()) events.send(events.Notify('addon-paths-changed')) ao.refresh() return [] # The dialog was canceled. return []