示例#1
0
 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()
示例#2
0
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()
示例#3
0
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]))
示例#4
0
def notifyHandler(event):
    "This is called when a Notify event is broadcasted."

    # We are interested in notifications that concern profiles.
    if event.hasId("quit"):
        # Save the current profile configuration.
        pr.save()

    elif event.hasId("language-changed"):
        # Just update the Defaults, because it's the only profile whose name
        # is translated.
        addListItem(pr.getDefaults())

    elif event.hasId("profile-updated"):
        # A profile has been loaded or updated.  Make sure it's in the
        # list.
        p = event.getProfile()

        # The Defaults profile should be first in the list.
        if p is pr.getDefaults():
            destIndex = 0
        else:
            # The new profile will be added to wherever the widget
            # wants to put it.
            destIndex = None

        if profileList.hasItem(p.getId()):
            # This already exists in the list.
            if p.isHidden():
                # Remove it completely.
                profileList.removeItem(p.getId())

                # Update the selected item since the active one is now
                # hidden.
                pr.setActive(pr.get(profileList.getSelectedItem()))
            else:
                # Just update the item.
                addListItem(p)

        elif not p.isHidden():
            # The item does not yet exist in the list.
            addListItem(p, toIndex=destIndex)

    elif event.hasId("active-profile-changed") and not profileListDisabled:
        # Highlight the correct profile in the list.
        profileList.selectItem(pr.getActive().getId())

        if pr.getActive() is pr.getDefaults():
            if deleteButton:
                deleteButton.disable()
            if dupeButton:
                dupeButton.disable()
            ui.disableMenuCommand("rename-profile")
            ui.disableMenuCommand("delete-profile")
            ui.disableMenuCommand("hide-profile")
            ui.disableMenuCommand("duplicate-profile")
            profileList.setPopupMenu(defaultsMenu)
        else:
            isSystem = pr.getActive().isSystemProfile()
            if deleteButton:
                deleteButton.enable(not isSystem)
            if dupeButton:
                dupeButton.enable()
            ui.enableMenuCommand("rename-profile")
            ui.enableMenuCommand("delete-profile", not isSystem)
            ui.enableMenuCommand("hide-profile")
            ui.enableMenuCommand("duplicate-profile")
            if isSystem:
                menu = systemMenu
            else:
                menu = normalMenu
            profileList.setPopupMenu(menu)

        # Update the banner image.
        if bannerImage:
            bannerImage.setImage(pr.getActive().getBanner())

    elif event.hasId("profile-list-selected"):
        # Change the currently active profile.
        p = pr.get(event.getSelection())
        pr.setActive(p)

        # Double-clicking causes a Play command.
        if event.isDoubleClick() and p is not pr.getDefaults():
            events.sendAfter(events.Command("play"))
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 notifyHandler(event):
    "This is called when a Notify event is broadcasted."

    # We are interested in notifications that concern profiles.
    if event.hasId('quit'):
        # Save the current profile configuration.
        pr.save()

    elif event.hasId('language-changed'):
        # Just update the Defaults, because it's the only profile whose name
        # is translated.
        addListItem(pr.getDefaults())

    elif event.hasId('profile-updated'):
        # A profile has been loaded or updated.  Make sure it's in the
        # list.
        p = event.getProfile()

        # The Defaults profile should be first in the list.
        if p is pr.getDefaults():
            destIndex = 0
        else:
            # The new profile will be added to wherever the widget
            # wants to put it.
            destIndex = None

        if profileList.hasItem(p.getId()):
            # This already exists in the list.
            if p.isHidden():
                # Remove it completely.
                profileList.removeItem(p.getId())

                # Update the selected item since the active one is now
                # hidden.
                pr.setActive(pr.get(profileList.getSelectedItem()))
            else:
                # Just update the item.
                addListItem(p)

        elif not p.isHidden():
            # The item does not yet exist in the list.
            addListItem(p, toIndex=destIndex)

    elif event.hasId('active-profile-changed') and not profileListDisabled:
        # Highlight the correct profile in the list.
        profileList.selectItem(pr.getActive().getId())

        if pr.getActive() is pr.getDefaults():
            if deleteButton: deleteButton.disable()
            if dupeButton: dupeButton.disable()
            ui.disableMenuCommand('rename-profile')
            ui.disableMenuCommand('delete-profile')
            ui.disableMenuCommand('hide-profile')
            ui.disableMenuCommand('duplicate-profile')
            profileList.setPopupMenu(defaultsMenu)
        else:
            isSystem = pr.getActive().isSystemProfile()
            if deleteButton: deleteButton.enable(not isSystem)
            if dupeButton: dupeButton.enable()
            ui.enableMenuCommand('rename-profile')
            ui.enableMenuCommand('delete-profile', not isSystem)
            ui.enableMenuCommand('hide-profile')
            ui.enableMenuCommand('duplicate-profile')
            if isSystem:
                menu = systemMenu
            else:
                menu = normalMenu
            profileList.setPopupMenu(menu)

        # Update the banner image.
        if bannerImage:
            bannerImage.setImage(pr.getActive().getBanner())

    elif event.hasId('profile-list-selected'):
        # Change the currently active profile.
        p = pr.get(event.getSelection())
        pr.setActive(p)

        # Double-clicking causes a Play command.
        if event.isDoubleClick() and p is not pr.getDefaults():
            events.sendAfter(events.Command('play'))