Beispiel #1
0
class rsimguiMainWindowController(NSWindowController):
    application = IBOutlet()
    runButton = IBOutlet()
    stepButton = IBOutlet()
    numSlotsTextField = IBOutlet()
    runtimeTextField = IBOutlet()

    def setRunButtonsEnabled_(self, state):
        self.runButton.setEnabled_(state)
        self.stepButton.setEnabled_(state)

    def setEditingEnabled_(self, state):
        self.numSlotsTextField.setEnabled_(state)
        #self.runtimeTextField.setEnabled_(state)

    def runTime(self):
        return self.runtimeTextField.intValue()

    def numSlots(self):
        return self.numSlotsTextField.intValue()

    def resetSimulation_(self, sender):
        self.setRunButtonsEnabled_(True)
        self.setEditingEnabled_(True)
        self.application.resetSimulation()

    def runSimulation_(self, sender):
        runtime = self.runTime()
        numSlots = self.numSlots()
        self.application.runSimulation(runtime, numSlots)

    def stepSimulation_(self, sender):
        numSlots = self.numSlots()
        self.application.stepSimulation(numSlots)
class controller(NSWindowController):
    iTunesManagesMyLibrary = ivar('iTunesManagesMyLibrary')
    #iTunesManagesMyLibrary = ivar()
    iTunesManagesMyLibraryMenuItem = IBOutlet()
    iTunesLibraryLocationMenuItem = IBOutlet()
    iTunesLibraryLocationMenuItemEnabled = ivar('iTunesLibraryLocationMenuItemEnabled')

    def awakeFromNib(self):
        #NSLog('awakeFromNib')
        self.iTunesManagesMyLibrary = config.get_config_option('iTunesManagesMyLibrary')
        self.iTunesManagesMyLibraryMenuItem.setState_(self.iTunesManagesMyLibrary)
        self.refreshMenuEnable()

    #@accessor
    #def iTunesManagesMyLibraryValue(self):
    #    return self.iTunesManagesMyLibrary

    @accessor
    def setITunesManagesMyLibrary_(self, value):
        #NSLog('setITunesManagesMyLibrary_')
        self.iTunesManagesMyLibrary = value
        #NSLog(str(self.iTunesManagesMyLibrary))
        config.set_config_option('iTunesManagesMyLibrary', value)
        self.refreshMenuEnable()

    @IBAction
    def setLibraryLocation_(self, sender):
        #NSLog('setLibraryLocation_')
        panel = NSOpenPanel.openPanel()
        panel.setCanChooseDirectories_(YES)
        panel.setAllowsMultipleSelection_(NO)
        panel.setCanChooseFiles_(NO)
        old_path = config.get_config_option('iTunesLibraryLocation')
        ret = panel.runModalForDirectory_file_types_(old_path, None, None)
        #NSLog(str(ret))
        if ret:
            path = panel.filenames()[0]
            config.set_config_option('iTunesLibraryLocation', path)
        else:
            # Canceled
            pass

    def refreshMenuEnable(self):
        #NSLog('refreshMenuEnable')
        if self.iTunesManagesMyLibrary:
            #NSLog('NO')
            #self.iTunesLibraryLocationMenuItem.setEnabled_(NO)
            self.iTunesLibraryLocationMenuItemEnabled = NO
        else:
            #NSLog('YES')
            #self.iTunesLibraryLocationMenuItem.setEnabled_(YES)
            self.iTunesLibraryLocationMenuItemEnabled = YES

    @IBAction
    def toggleITunesManagesMyLibrary_(self, sender):
        #NSLog('toggleITunesManagesMyLibrary_')
        #self.refreshMenuEnable()
        pass
Beispiel #3
0
class LauncherWindow(NSWindow):
    # command list, currently implemented as a simple multilned label
    label = IBOutlet()

    # command text field
    textField = IBOutlet()

    # regex for arithmetic expressions
    calc_re = re.compile(r'[\d\(\)\.\+\-\*/\s]{2}[\d\(\)\.\+\-\*/\s]')

    def awakeFromNib(self):
        NSLog('awakeFromNib')
        self.selected_cmd = None

    def controlTextDidChange_(self, notification):
        text = self.textField.stringValue()

        # try to eval arithmetic expressions or lookup commands otherwise
        if self.calc_re.match(text):
            try:
                self.label.setStringValue_(eval(text))
            except:
                pass

        else:
            self.update_commands_list(prefix=text.split(' ')[0])

    @IBAction
    def onEnter_(self, sender):
        # command was selected from the commands list
        if self.selected_cmd:
            cmd = self.selected_cmd[1]
            arg = self.textField.stringValue()[len(self.selected_cmd[0]):]

            self.selected_cmd = None

        # command is selected from text field
        else:
            if not self.textField.stringValue():
                return

            split = self.textField.stringValue().split(' ', 1)

            cmd = chili.get_command(split[0])
            arg = split[1] if len(split) > 1 else None

        if cmd:
            # run command
            self.performSelectorInBackground_withObject_(
                objc.selector(self.runCmd_, signature='v@:@'), (cmd, arg))

    def runCmd_(self, cmdArg):
        try:
            # call command function
            cmdArg[0](cmdArg[1])
        except Exception, e:
            self.windowController().report_exception(e)
class MSUMainWindowController(NSWindowController):
    '''
    Controls the main window
    '''

    theTabView = IBOutlet()
    theWindow = IBOutlet()

    def windowShouldClose_(self, sender):
        # just quit
        NSApp.terminate_(self)
class MainWindowController(NSWindowController):

    labelField = IBOutlet()
    quitButton = IBOutlet()

    def awakeFromNib(self):
        self.labelField.setStringValue_("Hello, World!")

    @IBAction
    def quitButtonClicked_(self, sender):
        NSApp.terminate_(self)
Beispiel #6
0
class ___PROJECTNAMEASIDENTIFIER___AppDelegate(NSObject):
    window = IBOutlet()

    def applicationDidFinishLaunching_(self, sender):
        pass

    def applicationWillTerminate_(self, notification):
        pass
Beispiel #7
0
class MTAppDelegate(NSObject):

    main_window_controller = IBOutlet()

    def applicationDidFinishLaunching_(self, sender):
        NSLog("Application did finish launching.")
        self.main_window_controller.initMainWindow()

    def application_openFile_(self, app, filename):
        self.main_window_controller.getApplicationInfo_(filename)
Beispiel #8
0
class LLAppDelegate(NSObject):

    logWindowController = IBOutlet()
    prefs = NSUserDefaults.standardUserDefaults()

    def applicationDidFinishLaunching_(self, sender):
        self.prefs.registerDefaults_({
            u"logfile": u"/var/log/system.log",
        })
        logfile = self.prefs.stringForKey_(u"logfile")
        self.logWindowController.showLogWindow_(logfile)
        self.logWindowController.watchLogFile_(logfile)
Beispiel #9
0
class EkoAppDelegate(NSObject):
    targetURL = IBOutlet()
    namespace = IBOutlet()
    requestView = IBOutlet()

    @IBAction
    def updateURLs_(self, sender):
        target_url = self.targetURL.stringValue()
        namespace = self.namespace.stringValue()
        with object_lock(self.request_items):
            self.request_items[:] = []
            self.requestView.reloadData()
        if hasattr(self, "client_thread"):
            self.client_thread.cancel()
        self.client_thread = EkoClientThread.newAtURL_usingNamespace_withItems_(
            target_url, namespace, self.request_items)
        self.client_thread.start()
        ##self.client_thread = NSThread.new()
        ##self.client_thread.initWithTarget_selector_object_(
        ##    None, selector(runEkoClientThread_, isClassMethod=True),
        ##    (target_url, namespace, self.request_items))
        ##self.client_thread.start()
        #NSThread.detachNewThreadSelector_toTarget_withObject_(
        #    selector(MyTestClass.myClassMeth_,
        #             isClassMethod=True,
        #             argumentTypes="s"),
        #    MyTestClass,
        #    "hello")
        #t = NSThread.new()
        #t.initWithTarget_selector_object_(None, selector(my_test_target), "hello")
        #t.start()
        #self.t = t

    def applicationDidFinishLaunching_(self, sender):
        logging.basicConfig(level=logging.DEBUG)
        logging.root.addHandler(NSLogHandler())
        #CFRunLoopSourceCreate(None, 0, ())
        self.request_items = NSMutableArray.new()
        self.requestView.setDataSource_(
            RequestItemDataSource.newWithSource_(self.request_items))
Beispiel #10
0
class MSUAppDelegate(NSObject):
    '''Implements some NSApplicationDelegateProtocol methods'''

    # since this subclasses NSObject,
    # it doesn't have a Python __init__method
    # pylint: disable=no-init

    # several delegate methods pass a 'sender' object that we don't use
    # pylint: disable=unused-argument

    statusWindowController = IBOutlet()
    logWindowController = IBOutlet()

    def applicationWillFinishLaunching_(self, sender):
        '''NSApplicationDelegate method
        Sent by the default notification center immediately before the
        application object is initialized.'''

        # pylint: disable=no-self-use

        consoleuser = munki.getconsoleuser()
        if consoleuser == None or consoleuser == u"loginwindow":
            # don't show menu bar
            NSMenu.setMenuBarVisible_(NO)
            # make sure we're active
            NSApp.activateIgnoringOtherApps_(YES)

    def applicationDidFinishLaunching_(self, sender):
        '''NSApplicationDelegate method
        Sent by the default notification center after the application has
        been launched and initialized but before it has received its first
        event.'''

        # Prevent automatic relaunching at login on Lion+
        if NSApp.respondsToSelector_('disableRelaunchOnLogin'):
            NSApp.disableRelaunchOnLogin()

        # show the default initial view
        self.statusWindowController.initStatusSession()
Beispiel #11
0
class TextureItem(NSCollectionViewItem):
    icon = IBOutlet()

    def loadImage(self):
        imageobject = io.BytesIO()
        imgdata = self.image.copy()
        imgdata.thumbnail((88, 88), Image.ANTIALIAS)
        imgdata.save(imageobject, format='png')

        imageobject.seek(0)

        dataout = imageobject.read()

        d = NSData.alloc().initWithBytes_length_(dataout, len(dataout))
        nsimg = NSImage.alloc().initWithData_(d)
        self.icon.setImage_(nsimg)

    def setKey_image_rotated_position_index_(self, key, image, rotated,
                                             position, index):

        self.key = key
        self.image = image
        self.rotated = rotated
        self.position = position
        self.index = index

        self.loadImage()

    def viewDidLoad(self):
        super(NSCollectionViewItem, self).viewDidLoad()

    @IBAction
    def changeIcon_(self, sender):
        panel = NSOpenPanel.openPanel()
        panel.setCanChooseFiles_(YES)
        panel.setCanChooseDirectories_(NO)
        panel.setAllowsMultipleSelection_(NO)
        panel.setAllowedFileTypes_(NSImage.imageTypes())

        clicked = panel.runModal()
        if clicked == NSFileHandlingPanelOKButton:
            self.image = Image.open(panel.URLs()[0].path()).resize(
                self.image.size)
            self.loadImage()
            NSNotificationCenter.defaultCenter(
            ).postNotificationName_object_userInfo_("SetIcon", self, {
                "image": self.image,
                "index": self.index
            })
Beispiel #12
0
class MainWindowController(NSWindowController):

    spokenTextField = IBOutlet()

    @IBAction
    def speakButtonClicked_(self, sender):
        NSLog(u"Speak button clicked")
        NSLog(self.spokenTextField.stringValue())
        scriptPath = NSBundle.mainBundle().pathForResource_ofType_(
            u"Script", u"sh")
        if scriptPath:
            scriptProcess = NSTask.launchedTaskWithLaunchPath_arguments_(
                scriptPath, [self.spokenTextField.stringValue()])
            scriptProcess.waitUntilExit()
        else:
            NSLog(u"Couldn't find Script.sh")
Beispiel #13
0
class ViewController(NSViewController):
    label = IBOutlet()
    fileDisplay = IBOutlet()

    graphicSize = IBOutlet()
    blockDensity = IBOutlet()
    blockSize = IBOutlet()

    countLabel = IBOutlet()

    imagePreview = IBOutlet()

    drawBtn = IBOutlet()

    errCorrection = IBOutlet()

    def previewSVG(self):
        try:
            """drawing = svg2rlg(self.chosenFile.UTF8String())
            new_bites = io.BytesIO()
            renderPM.drawToFile(drawing,new_bites,fmt='png')"""
            with wand.image.Image() as imag:
                with wand.color.Color('transparent') as background_color:
                    library.MagickSetBackgroundColor(imag.wand,
                                                     background_color.resource)
                imag.read(filename=self.chosenFile.UTF8String())
                new_bites = io.BytesIO(imag.make_blob("png32"))

            im = Image.open(new_bites)
            im = im.crop(im.getbbox())
            im = im.resize((im.width * 3, im.height * 3))
            #im.thumbnail((133,133),Image.ANTIALIAS)
            im.save(join(expanduser("~"), ".GGD2_prev.png"), format='PNG')

            img = NSImage.alloc().initWithContentsOfFile_(
                join(expanduser("~"), ".GGD2_prev.png"))
            self.imagePreview.setImage_(img)
        except Exception, e:
            print(e)
Beispiel #14
0
class PMController(NSWindowController):

    titleField = IBOutlet()
    urlField = IBOutlet()
    bodyField = IBOutlet()
    charCountLabel = IBOutlet()
    actionButton = IBOutlet()
    twitterCheckbox = IBOutlet()
    gplusCheckbox = IBOutlet()
    
    # Confirm token sheet.
    confirmTokenSheet = IBOutlet()
    confirmTokenField = IBOutlet()
    
    # Publish log sheet.
    publishLogWindow = IBOutlet()
    publishLogField = IBOutlet()
    publishCancelButton = IBOutlet()

    twitter = TwitterSyndicator()
    gplus = GPlusSyndicator()
    ltp = LinkTextProcessor()
    

    def awakeFromNib(self):
        NSLog("Awake from nib.")
        self.setPreviewMode(True)
        self.bodyField.setDelegate_(self)
        self.urlField.setDelegate_(self)
        self.titleField.setDelegate_(self)

        # Style the bodyField.
        self.bodyField.setFont_(NSFont.fontWithName_size_("Monaco", 13))
        self.bodyField.setRichText_(NO)
        self.bodyField.setUsesFontPanel_(NO)
    
        # Authenticate to twitter if we can.
        if self.twitter.is_authenticated():
            self.twitter.login()
            self.twitterCheckbox.setState_(NSOnState)
            self.ltp.syndicators.append(self.twitter)
        
        # Authenticate to G+ if we can.
        if self.gplus.is_authenticated():
            self.gplus.login()
            self.gplusCheckbox.setState_(NSOnState)
            self.ltp.syndicators.append(self.gplus)

        # Listen to the NSApplicationWillTerminateNotification.
        center = NSNotificationCenter.defaultCenter()
        center.addObserver_selector_name_object_(self, "applicationWillTerminateNotification:", NSApplicationWillTerminateNotification, None)
                
        self.setupStatusBar()

        self.didPublish = False
        self.didPreview = False


    def applicationWillTerminateNotification_(self, notification):
        NSLog("applicationWillTerminateNotification_")


    @IBAction
    def post_(self, sender):
        url = self.urlField.stringValue()
        title = self.titleField.stringValue()
        body = self.bodyField.string()
        # TODO(smus): Validate all fields.

        if self.isPreview:
            # Relinquish control to the link text processor for a preview.
            self.ltp.set_content(url, title, body)
            # Show the preview in a browser window.
            self.ltp.preview_content()
            # Go into publish mode.
            self.setPreviewMode(False)
            self.didPreview = True
        else:
            # If in publish mode, push to S3, publish to twitter & G+.
            print 'Syndicators: ' + str(self.ltp.syndicators)
            self.ltp.publish_syndicate()
            self.didPublish = True
            self.hideWindow()

    @IBAction
    def cancel_(self, sender):
        NSLog(u"Cancel")
        # Remove the link if one was created.
        self.ltp.clean()
        # Exit the application.
        self.hideWindow()
    
    @IBAction
    def twitterChecked_(self, sender):
        if self.twitterCheckbox.state() == NSOnState:
            self.twitter.login()
            if not self.twitter.is_authenticated():
                self.currentService = self.twitter
                self.showTheSheet_(sender)

        isTwitterEnabled = bool(self.twitterCheckbox.state() == NSOnState)
        if isTwitterEnabled:
            self.ltp.syndicators.append(self.twitter)
        else:
            self.ltp.syndicators.remove(self.twitter)

    @IBAction
    def gplusChecked_(self, sender):
        if self.gplusCheckbox.state() == NSOnState:
            self.gplus.login()
            print self.gplus.is_authenticated()
            if not self.gplus.is_authenticated():
                self.currentService = self.gplus
                self.showTheSheet_(sender)
        
        isGPlusEnabled = bool(self.gplusCheckbox.state() == NSOnState)
        if isGPlusEnabled:
            self.ltp.syndicators.append(self.gplus)
        else:
            self.ltp.syndicators.remove(self.gplus)

    @IBAction
    def confirmToken_(self, sender):
        NSLog("Confirmed token")
        verifier = self.confirmTokenField.stringValue()

        if self.currentService == self.twitter:
            self.twitter.confirm_verifier(verifier)
        elif self.currentService == self.gplus:
            self.gplus.confirm_verifier(verifier)
                
        self.endTheSheet_(sender)

    @IBAction
    def cancelToken_(self, sender):
        self.endTheSheet_(sender)

    def doPreview_(self, sender):
        NSLog("Doing a preview.")
        self.ltp.preview()

    def doPublish_(self, sender):
        NSLog("Doing a publish.")
        self.ltp.publish()
    
    def doLink_(self, sender):
        NSLog("Doing a link.")
        self.showWindow()


    def showTheSheet_(self, sender):
        NSApp.beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo_(
                self.confirmTokenSheet, NSApp.mainWindow(), self, None, None)

    def endTheSheet_(self, sender):
        NSApp.endSheet_(self.confirmTokenSheet)
        self.confirmTokenSheet.orderOut_(sender)
    
    def hideWindow(self):
        # Hide the window.
        self.window().orderOut_(self)
        
        # Cleanup if the app did not publish (to keep state consistent).
        if not self.didPublish:
            self.ltp.clean()

        # Reset the internal state.
        self.didPreview = False
        self.didPublish = False
        
        # Clear all of the UI elements.
        self.titleField.setStringValue_('')
        self.urlField.setStringValue_('')
        self.bodyField.setString_('')
    
    def showWindow(self):
        self.window().makeKeyAndOrderFront_(self)
        self.window().setLevel_(NSStatusWindowLevel)
        # Give the window focus.
        NSApp.activateIgnoringOtherApps_(YES)

    def setPreviewMode(self, isPreview):
        self.isPreview = isPreview
        # Update the UI.
        self.actionButton.setTitle_(isPreview and "Preview" or "Publish")

    def controlTextDidChange_(self, notification):
        # If any of the text changes, go into preview mode.
        self.setPreviewMode(True)
        self.enableButtonIfValid()

        changedField = notification.object()
        if changedField == self.urlField:
            # If the URL field, try to infer the title.
            url = self.urlField.stringValue()
            title = self.inferTitleFromURL(url)
            if title:
                NSLog("Setting title to be: " + title)
                self.titleField.setStringValue_(title)

    def textDidChange_(self, notification):
        # Go back to preview mode.
        self.setPreviewMode(True)
        self.enableButtonIfValid()
        # If the body text changes, update the count.
        text = self.bodyField.string()
        self.charCountLabel.setStringValue_(len(text))
        NSLog(u"Length: %d" % len(text))


    def inferTitleFromURL(self, url):
        from mechanize import Browser
        from urlparse import urlparse
        try:
            result = urlparse(url)
            if result.scheme not in ['http', 'https']:
                return None
            browser = Browser()
            browser.open(url)
            return unicode(browser.title(), 'utf8')
        except Exception as e:
            NSLog("Exception: " + str(e))
            return None

    def quit(self):
        NSApp.performSelector_withObject_afterDelay_("terminate:", None, 0);

    def enableButtonIfValid(self):
        # If all of the text fields have content in them, enable the button.
        # Otherwise, disable it.
        url = unicode(self.urlField.stringValue())
        title = unicode(self.titleField.stringValue())
        body = unicode(self.bodyField.string())
        isEnabled = url and title and body

        self.actionButton.setEnabled_(isEnabled and YES or NO)

    def setupStatusBar(self):
        statusbar = NSStatusBar.systemStatusBar()
        statusitem = statusbar.statusItemWithLength_(20).retain()
        
        icon = NSImage.imageNamed_('status')
        icon.setSize_((20, 20))
        statusitem.setImage_(icon)
        
        iconHighlight = NSImage.imageNamed_('status-hi')
        iconHighlight.setSize_((20, 20))
        statusitem.setAlternateImage_(iconHighlight)
        
        statusitem.setHighlightMode_(1)
        
        # TODO: Put this whole menu creation stuff into interface builder!
        menu = NSMenu.alloc().init()
        
        linkMenu = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Post a Link', 'doLink:', '')
        linkMenu.setTarget_(self);
        # Make it possible to invoke the link menu via a keyboard shortcut.
        linkMenu.setKeyEquivalentModifierMask_(NSShiftKeyMask | NSCommandKeyMask)
        linkMenu.setKeyEquivalent_('l')
        menu.addItem_(linkMenu)
        
        
        menu.addItem_(NSMenuItem.separatorItem())
        
        previewMenu = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Update Preview', 'doPreview:', '')
        previewMenu.setTarget_(self);
        menu.addItem_(previewMenu)
        
        publishMenuItem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Publish', None, '')
        publishMenu = NSMenu.alloc().init();
        s3MenuItem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('S3', 'doPublish:', '')
        s3MenuItem.setTarget_(self);
        publishMenu.addItem_(s3MenuItem)
        publishMenuItem.setSubmenu_(publishMenu)
        menu.addItem_(publishMenuItem)
        
        menu.addItem_(NSMenuItem.separatorItem())

        quitMenu = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Quit', 'terminate:', '')
        menu.addItem_(quitMenu)
        
        statusitem.setMenu_(menu)
        menu.release();
    
    @IBAction
    def hidePublishLog_(self, sender):
        self.publishLogWindow.orderOut_(self);

    def showPublishLog(self):
        self.publishLogWindow.makeKeyAndOrderFront_(self);
Beispiel #15
0
class IEDController(NSObject):

    mainWindow = IBOutlet()

    sourceBox = IBOutlet()
    sourceImage = IBOutlet()
    sourceLabel = IBOutlet()

    updateController = IBOutlet()
    addPkgController = IBOutlet()
    logController = IBOutlet()

    buildButton = IBOutlet()

    buildProgressWindow = IBOutlet()
    buildProgressPhase = IBOutlet()
    buildProgressBar = IBOutlet()
    buildProgressMessage = IBOutlet()

    advancedWindow = IBOutlet()
    volumeName = IBOutlet()
    volumeSize = IBOutlet()
    finalizeAsrImagescan = IBOutlet()
    filesystem = IBOutlet()
    filesystemApfs = IBOutlet()
    filesystemHfs = IBOutlet()

    def awakeFromNib(self):
        LogDebug("awakeFromNib")

        # Initialize UI.
        self.buildProgressBar.setMaxValue_(100.0)
        self.buildProgressMessage.setStringValue_("")
        self.setSourcePlaceholder()

        # We're a delegate for the drag and drop target, protocol:
        #   (void)acceptInstaller:(NSString *)path
        self.sourceBox.setDelegate_(self)
        self.sourceImage.setDelegate_(self)
        self.sourceLabel.setDelegate_(self)

        # We're a delegate for the update controller, protocol:
        #   (void)updateControllerChanged
        if self.updateController:
            self.updateController.setDelegate_(self)
        else:
            alert = NSAlert.alloc().init()
            alert.setMessageText_("Unable to initialize update controller")
            alert.setInformativeText_(
                "AutoDMG will not function correctly, check the log for details."
            )
            alert.runModal()

        # Main workflow logic.
        self.workflow = IEDWorkflow.alloc().initWithDelegate_(self)

        # Enabled state for main window.
        self.enabled = True

        # When busy is true quitting gets a confirmation prompt.
        self._busy = False

        # Currently loaded template.
        self.templateURL = None

        # Filesystem selection.
        self.filesystem.setAutoenablesItems_(False)
        self.filesystemHfs.setRepresentedObject_("hfs")
        self.filesystemApfs.setRepresentedObject_("apfs")
        if IEDUtil.hostMajorVersion() < 13:
            self.filesystem.selectItem_(self.filesystemHfs)
            self.filesystemApfs.setEnabled_(False)
        else:
            self.filesystem.selectItem_(self.filesystemApfs)
            self.filesystemApfs.setEnabled_(True)

    # Methods to communicate with app delegate.

    def cleanup(self):
        self.workflow.cleanup()

    def busy(self):
        return self._busy

    def setBusy_(self, busy):
        self._busy = busy
        if busy:
            self.disableMainWindowControls()
        else:
            self.enableMainWindowControls()

    # Helper methods.

    def setSourcePlaceholder(self):
        self.sourceLabel.setStringValue_("Drop %s Installer Here" %
                                         (IEDUtil.hostOSName()))
        image = NSImage.imageNamed_("Installer Placeholder 10.%d" %
                                    IEDUtil.hostMajorVersion())
        if not image:
            image = NSImage.imageNamed_("Installer Placeholder")
        self.sourceImage.animator().setImage_(image)
        self.sourceImage.animator().setAlphaValue_(1.0)

    def validateMenuItem_(self, menuItem):
        return not self.busy()

    def displayAlert_text_(self, message, text):
        LogDebug("Displaying alert: %@ (%@)", message, text)
        alert = NSAlert.alloc().init()
        alert.setMessageText_(message)
        alert.setInformativeText_(text)
        alert.runModal()

    def disableMainWindowControls(self):
        self.enabled = False
        self.sourceBox.stopAcceptingDrag()
        self.sourceImage.stopAcceptingDrag()
        self.sourceLabel.stopAcceptingDrag()
        self.updateController.disableControls()
        self.addPkgController.disableControls()
        self.buildButton.setEnabled_(False)

    def enableMainWindowControls(self):
        self.enabled = True
        self.sourceBox.startAcceptingDrag()
        self.sourceImage.startAcceptingDrag()
        self.sourceLabel.startAcceptingDrag()
        self.updateController.enableControls()
        self.addPkgController.enableControls()
        self.updateBuildButton()

    def updateBuildButton(self):
        buildEnabled = self.enabled and \
                       self.workflow.hasSource() and \
                       self.updateController.allUpdatesDownloaded()
        self.buildButton.setEnabled_(buildEnabled)

    # Act on user dropping an installer.

    def acceptSource_(self, path):
        self.setBusy_(True)
        self.workflow.setSource_(path)

    # Workflow delegate methods.

    def detachFailed_details_(self, dmgPath, details):
        self.displayAlert_text_("Failed to detach %s" % dmgPath, details)

    def ejectingSource(self):
        self.sourceImage.animator().setAlphaValue_(0.5)
        self.sourceLabel.setStringValue_("Ejecting")
        self.sourceLabel.setTextColor_(NSColor.disabledControlTextColor())

    def examiningSource_(self, path):
        self.foundSourceForIcon_(path)
        self.sourceLabel.setStringValue_("Examining")
        self.sourceLabel.setTextColor_(NSColor.disabledControlTextColor())

    def foundSourceForIcon_(self, path):
        icon = NSWorkspace.sharedWorkspace().iconForFile_(path)
        icon.setSize_(NSMakeSize(256.0, 256.0))
        tiff = icon.TIFFRepresentation()
        image = NSImage.alloc().initWithData_(tiff)
        self.sourceImage.animator().setAlphaValue_(1.0)
        self.sourceImage.animator().setImage_(image)

    def sourceSucceeded_(self, info):
        self.installerName = info["name"]
        self.installerVersion = info["version"]
        self.installerBuild = info["build"]
        self.sourceLabel.setStringValue_(
            "%s %s %s" % (info["name"], info["version"], info["build"]))
        self.sourceLabel.setTextColor_(NSColor.controlTextColor())
        self.updateController.loadProfileForVersion_build_(
            info["version"], info["build"])
        template = info["template"]
        if template:
            LogInfo("Template found in image: %@", repr(template))
            # Don't default to applying updates to an image that was built
            # with updates applied, and vice versa.
            if template.applyUpdates:
                self.updateController.applyUpdatesCheckbox.setState_(
                    NSOffState)
            else:
                self.updateController.applyUpdatesCheckbox.setState_(NSOnState)
        else:
            if info["sourceType"] == IEDWorkflow.SYSTEM_IMAGE:
                LogInfo("No template found in image")
                # If the image doesn't have a template inside, assume that updates
                # were applied.
                self.updateController.applyUpdatesCheckbox.setState_(
                    NSOffState)
        self.setBusy_(False)

    def sourceFailed_text_(self, message, text):
        self.displayAlert_text_(message, text)
        self.setSourcePlaceholder()
        self.sourceLabel.setTextColor_(NSColor.disabledControlTextColor())
        self.setBusy_(False)

    # Opening installer via menu.

    @LogException
    @IBAction
    def locateInstaller_(self, menuItem):
        if menuItem.isAlternate():
            try:
                path = (glob.glob("/Applications/*/Install*OS*.app") +
                        glob.glob("/Applications/Install*OS*.app"))[-1]
                if IEDUtil.mightBeSource_(path):
                    self.acceptSource_(path)
                else:
                    NSBeep()
            except IndexError:
                NSBeep()
        else:
            IEDPanelPathManager.loadPathForName_("Installer")

            panel = NSOpenPanel.openPanel()
            panel.setDelegate_(self)
            panel.setExtensionHidden_(False)
            panel.setAllowedFileTypes_(["app", "dmg"])

            result = panel.runModal()
            if result != NSFileHandlingPanelOKButton:
                return

            IEDPanelPathManager.savePathForName_("Installer")

            self.acceptSource_(panel.URL().path())

    # NSOpenSavePanelDelegate.

    def panel_shouldEnableURL_(self, sender, url):
        return IEDUtil.mightBeSource_(url.path())

    # Act on update controller changing.

    def updateControllerChanged(self):
        if self.enabled:
            self.updateBuildButton()

    # Act on user showing log window.

    @LogException
    @IBAction
    def displayAdvancedWindow_(self, sender):
        self.advancedWindow.makeKeyAndOrderFront_(self)

    # Act on build button.

    @LogException
    @IBAction
    def buildButtonClicked_(self, sender):
        IEDPanelPathManager.loadPathForName_("Image")

        panel = NSSavePanel.savePanel()
        panel.setExtensionHidden_(False)
        panel.setAllowedFileTypes_(["dmg"])
        imageName = "osx"
        formatter = NSDateFormatter.alloc().init()
        formatter.setDateFormat_("yyMMdd")
        if self.updateController.packagesToInstall():
            dateStr = formatter.stringFromDate_(
                self.updateController.profileController.publicationDate)
            imageName = "osx_updated_%s" % dateStr
        if self.addPkgController.packagesToInstall():
            dateStr = formatter.stringFromDate_(NSDate.date())
            imageName = "osx_custom_%s" % dateStr
        panel.setNameFieldStringValue_(
            "%s-%s-%s.%s" %
            (imageName, self.installerVersion, self.installerBuild,
             self.filesystem.selectedItem().representedObject()))
        result = panel.runModal()
        if result != NSFileHandlingPanelOKButton:
            return

        IEDPanelPathManager.savePathForName_("Image")

        exists, error = panel.URL().checkResourceIsReachableAndReturnError_(
            None)
        if exists:
            success, error = NSFileManager.defaultManager(
            ).removeItemAtURL_error_(panel.URL(), None)
            if not success:
                NSApp.presentError_(error)
                return

        # Create a template to save inside the image.
        template = IEDTemplate.alloc().init()
        template.setSourcePath_(self.workflow.source())
        if self.updateController.packagesToInstall():
            template.setApplyUpdates_(True)
        else:
            template.setApplyUpdates_(False)
        if not template.setAdditionalPackages_(
            [x.path() for x in self.addPkgController.packagesToInstall()]):
            self.displayAlert_text_("Additional packages failed verification",
                                    template.additionalPackageError)
            return
        template.setOutputPath_(panel.URL().path())
        if self.volumeName.stringValue():
            template.setVolumeName_(self.volumeName.stringValue())
            self.workflow.setVolumeName_(self.volumeName.stringValue().strip())
        if self.volumeSize.stringValue():
            template.setVolumeSize_(self.volumeSize.intValue())
            self.workflow.setVolumeSize_(self.volumeSize.intValue())

        finalize_asr_imagescan = bool(self.finalizeAsrImagescan.state())
        template.setFinalizeAsrImagescan_(finalize_asr_imagescan)
        self.workflow.setFinalizeAsrImagescan_(finalize_asr_imagescan)

        fsType = self.filesystem.selectedItem().representedObject()
        template.setFilesystem_(fsType)
        self.workflow.setFilesystem_(fsType)

        self.workflow.setTemplate_(template)

        self.workflow.setPackagesToInstall_(
            self.updateController.packagesToInstall() +
            self.addPkgController.packagesToInstall())
        self.workflow.setOutputPath_(panel.URL().path())
        self.workflow.start()

    # Workflow delegate methods.

    def buildStartingWithOutput_(self, outputPath):
        self.buildProgressWindow.setTitle_(os.path.basename(outputPath))
        self.buildProgressPhase.setStringValue_("Starting")
        self.buildProgressBar.setIndeterminate_(True)
        self.buildProgressBar.startAnimation_(self)
        self.buildProgressBar.setDoubleValue_(0.0)
        self.buildProgressMessage.setStringValue_("")
        self.buildProgressWindow.makeKeyAndOrderFront_(self)
        self.setBusy_(True)

    def buildSetTotalWeight_(self, totalWeight):
        self.buildProgressBar.setMaxValue_(totalWeight)

    def buildSetPhase_(self, phase):
        self.buildProgressPhase.setStringValue_(phase)

    def buildSetProgress_(self, progress):
        self.buildProgressBar.setDoubleValue_(progress)
        self.buildProgressBar.setIndeterminate_(False)

    def buildSetProgressMessage_(self, message):
        self.buildProgressMessage.setStringValue_(message)

    def buildSucceeded(self):
        alert = NSAlert.alloc().init()
        alert.setMessageText_("Build successful")
        alert.setInformativeText_("Built %s" %
                                  os.path.basename(self.workflow.outputPath()))
        alert.addButtonWithTitle_("OK")
        alert.addButtonWithTitle_("Reveal")
        button = alert.runModal()
        if button == NSAlertSecondButtonReturn:
            fileURL = NSURL.fileURLWithPath_(self.workflow.outputPath())
            NSWorkspace.sharedWorkspace().activateFileViewerSelectingURLs_(
                [fileURL])

    def buildFailed_details_(self, message, details):
        alert = NSAlert.alloc().init()
        alert.setMessageText_(message)
        alert.setInformativeText_(details)
        alert.addButtonWithTitle_("OK")
        alert.addButtonWithTitle_("View Log")
        button = alert.runModal()
        if button == NSAlertSecondButtonReturn:
            self.logController.displayLogWindow_(self)

    def buildStopped(self):
        self.buildProgressWindow.orderOut_(self)
        self.setBusy_(False)

    # Load and save templates.

    def saveTemplate(self):
        if self.templateURL:
            self.saveTemplateToURL_(self.templateURL)
        else:
            self.saveTemplateAs()

    def saveTemplateAs(self):
        IEDPanelPathManager.loadPathForName_("Template")

        panel = NSSavePanel.savePanel()
        panel.setExtensionHidden_(False)
        panel.setAllowedFileTypes_(["adtmpl"])
        formatter = NSDateFormatter.alloc().init()
        formatter.setDateFormat_("yyMMdd")
        dateStr = formatter.stringFromDate_(NSDate.date())
        panel.setNameFieldStringValue_("AutoDMG-%s.adtmpl" % (dateStr))
        result = panel.runModal()
        if result != NSFileHandlingPanelOKButton:
            return

        IEDPanelPathManager.savePathForName_("Template")

        exists, error = panel.URL().checkResourceIsReachableAndReturnError_(
            None)
        if exists:
            success, error = NSFileManager.defaultManager(
            ).removeItemAtURL_error_(panel.URL(), None)
            if not success:
                NSApp.presentError_(error)
                return

        self.saveTemplateToURL_(panel.URL())

    def saveTemplateToURL_(self, url):
        LogDebug("saveTemplateToURL:%@", url)
        self.templateURL = url
        NSDocumentController.sharedDocumentController(
        ).noteNewRecentDocumentURL_(url)

        # Create a template from the current state.
        template = IEDTemplate.alloc().init()
        if self.workflow.source():
            template.setSourcePath_(self.workflow.source())
        if self.updateController.packagesToInstall():
            template.setApplyUpdates_(True)
        else:
            template.setApplyUpdates_(False)
        if self.volumeName.stringValue():
            template.setVolumeName_(self.volumeName.stringValue())
        if self.volumeSize.intValue():
            template.setVolumeSize_(self.volumeSize.intValue())
        if self.finalizeAsrImagescan.state() == NSOffState:
            template.setFinalizeAsrImagescan_(False)
        if IEDUtil.hostMajorVersion() >= 13:
            template.setFilesystem_(
                self.filesystem.selectedItem().representedObject())
        template.setAdditionalPackages_(
            [x.path() for x in self.addPkgController.packagesToInstall()])

        error = template.saveTemplateAndReturnError_(url.path())
        if error:
            self.displayAlert_text_("Couldn't save template", error)

    def openTemplate(self):
        IEDPanelPathManager.loadPathForName_("Template")

        panel = NSOpenPanel.openPanel()
        panel.setExtensionHidden_(False)
        panel.setAllowedFileTypes_(["adtmpl"])

        result = panel.runModal()
        if result != NSFileHandlingPanelOKButton:
            return

        IEDPanelPathManager.savePathForName_("Template")

        return self.openTemplateAtURL_(panel.URL())

    def openTemplateAtURL_(self, url):
        LogDebug("openTemplateAtURL:%@", url)
        self.templateURL = None
        template = IEDTemplate.alloc().init()
        error = template.loadTemplateAndReturnError_(url.path())
        if error:
            self.displayAlert_text_("Couldn't open template", error)
            return False
        self.templateURL = url
        NSDocumentController.sharedDocumentController(
        ).noteNewRecentDocumentURL_(url)
        # AdditionalPackages.
        LogDebug("Setting additional packages to %@",
                 template.additionalPackages)
        self.addPkgController.replacePackagesWithPaths_(
            template.additionalPackages)
        # ApplyUpdates.
        if template.applyUpdates:
            LogDebug("Enable updates")
            self.updateController.applyUpdatesCheckbox.setState_(NSOnState)
        else:
            LogDebug("Disable updates")
            self.updateController.applyUpdatesCheckbox.setState_(NSOffState)
        # VolumeName.
        self.volumeName.setStringValue_("")
        if template.volumeName:
            LogDebug("Setting volume name to %@", template.volumeName)
            self.volumeName.setStringValue_(template.volumeName)
        # VolumeSize.
        self.volumeSize.setStringValue_("")
        if template.volumeSize:
            LogDebug("Setting volume size to %@", template.volumeSize)
            self.volumeSize.setIntValue_(template.volumeSize)
        # Finalize task: ASR imagescan.
        self.finalizeAsrImagescan.setState_(NSOnState)
        if template.finalizeAsrImagescan == False:
            LogDebug("Setting 'Finalize: Scan for restore' to %@",
                     template.finalizeAsrImagescan)
            self.finalizeAsrImagescan.setState_(NSOffState)
        # Filesystem.
        if template.filesystem:
            if IEDUtil.hostMajorVersion() < 13:
                if template.filesystem != "hfs":
                    LogWarning(
                        "Ignoring template filesystem (%@), using hfs on 10.%d",
                        template.filesystem, IEDUtil.hostMajorVersion())
            else:
                LogDebug("Setting filesystem to %@", template.filesystem)
                for item in self.filesystem.itemArray():
                    if item.representedObject() == template.filesystem:
                        self.filesystem.selectItem_(item)
                        break
                else:
                    LogWarning("Unknown filesystem '%@'", template.filesystem)

        # SourcePath.
        if template.sourcePath:
            LogDebug("Setting source to %@", template.sourcePath)
            self.setBusy_(True)
            self.workflow.setSource_(template.sourcePath)
        return True
Beispiel #16
0
class MSCStatusController(NSObject):
    '''
    Handles status messages from managedsoftwareupdate
    '''

    session_started = False
    got_status_update = False
    timer = None

    _status_stopBtnDisabled = False
    _status_stopBtnHidden = False
    _status_message = u''
    _status_detail = u''
    _status_percent = -1
    _status_stopBtnState = 0

    statusWindowController = IBOutlet()

    def registerForNotifications(self):
        '''Register for notification messages'''
        notification_center = NSDistributedNotificationCenter.defaultCenter()
        notification_center.addObserver_selector_name_object_suspensionBehavior_(
            self, self.updateStatus_,
            'com.googlecode.munki.managedsoftwareupdate.statusUpdate', None,
            NSNotificationSuspensionBehaviorDeliverImmediately)
        self.receiving_notifications = True

    def unregisterForNotifications(self):
        '''Tell the DistributedNotificationCenter to stop sending us notifications'''
        NSDistributedNotificationCenter.defaultCenter().removeObserver_(self)
        # set self.receiving_notifications to False so our process monitoring
        # thread will exit
        self.receiving_notifications = False

    def startMunkiStatusSession(self):
        '''Initialize things for monitoring a managedsoftwareupdate session'''
        self.initStatusSession()
        self.session_started = True
        # start our process monitor timer so we can be notified about
        # process failure
        self.timeout_counter = 6
        self.saw_process = False
        self.timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
            5.0, self, self.checkProcess_, None, YES)

    def checkProcess_(self, timer):
        '''Monitors managedsoftwareupdate process for failure to start
        or unexpected exit, so we're not waiting around forever if
        managedsoftwareupdate isn't running.'''
        PYTHON_SCRIPT_NAME = u'managedsoftwareupdate'
        NEVER_STARTED = -2
        UNEXPECTEDLY_QUIT = -1

        if self.session_started:
            if self.got_status_update:
                # we got a status update since we last checked; no need to
                # check the process table
                self.timeout_counter = 6
                self.saw_process = True
                # clear the flag so we have to get another status update
                self.got_status_update = False
            elif munki.pythonScriptRunning(PYTHON_SCRIPT_NAME):
                self.timeout_counter = 6
                self.saw_process = True
            else:
                msclog.debug_log('managedsoftwareupdate not running...')
                self.timeout_counter -= 1
            if self.timeout_counter == 0:
                msclog.debug_log(
                    'Timed out waiting for managedsoftwareupdate.')
                if self.saw_process:
                    self.sessionEnded_(UNEXPECTEDLY_QUIT)
                else:
                    self.sessionEnded_(NEVER_STARTED)

    def sessionStarted(self):
        '''Accessor method'''
        return self.session_started

    def sessionEnded_(self, result):
        '''clean up after a managesoftwareupdate session ends'''
        if self.timer:
            self.timer.invalidate()
            self.timer = None
        self.cleanUpStatusSession()
        # tell the window controller the update session is done
        self.statusWindowController.munkiStatusSessionEndedWithStatus_errorMessage_(
            result, "")

    def updateStatus_(self, notification):
        '''Got update status notification from managedsoftwareupdate'''
        msclog.debug_log('Got munkistatus update notification')
        self.got_status_update = True
        info = notification.userInfo()
        msclog.debug_log('%s' % info)
        # explictly get keys from info object; PyObjC in Mountain Lion
        # seems to need this
        info_keys = info.keys()
        if 'message' in info_keys:
            self.setMessage_(info['message'])
        if 'detail' in info_keys:
            self.setDetail_(info['detail'])
        if 'percent' in info_keys:
            self.setPercentageDone_(info['percent'])
        if 'stop_button_visible' in info_keys:
            if info['stop_button_visible']:
                self.showStopButton()
            else:
                self.hideStopButton()
        if 'stop_button_enabled' in info_keys:
            if info['stop_button_enabled']:
                self.enableStopButton()
            else:
                self.disableStopButton()

        command = info.get('command')
        if not self.session_started and command not in [
                'showRestartAlert', 'quit'
        ]:
            # we got a status message but we didn't start the session
            # so switch to the right mode
            self.startMunkiStatusSession()
        if command:
            msclog.debug_log('Received command: %s' % command)
        if command == 'activate':
            pass

        elif command == 'showRestartAlert':
            if self.session_started:
                self.sessionEnded_(0)
            self.doRestartAlert()
        elif command == 'quit':
            self.sessionEnded_(0)


##### required status methods #####

    def initStatusSession(self):
        '''Initialize the main window for update status'''
        self.statusWindowController._update_in_progress = True
        if self.statusWindowController.currentPageIsUpdatesPage():
            self.statusWindowController.webView.reload_(self)
            self.statusWindowController.displayUpdateCount()

    def cleanUpStatusSession(self):
        '''Clean up after status session ends'''
        self.session_started = False
        # reset all our status variables
        self.statusWindowController._update_in_progress = False
        self._status_stopBtnDisabled = False
        self._status_stopBtnHidden = False
        self._status_stopBtnState = 0
        self._status_message = u''
        self._status_detail = u''
        self._status_percent = -1

    def setPercentageDone_(self, percent):
        '''Display precentage done'''
        try:
            if float(percent) > 100.0:
                percent = 100
        except ValueError:
            percent = 0
        self._status_percent = percent
        document = self.statusWindowController.webView.mainFrameDocument()
        if document:
            spinner = document.getElementById_('updates-progress-spinner')
            if spinner:  # we are displaying the updates status page
                progress = document.getElementById_('progress-bar')
                if progress:
                    if float(percent) < 0:
                        # indeterminate
                        progress.setClassName_('indeterminate')
                        progress.removeAttribute_('style')
                    else:
                        progress.setClassName_('')
                        progress.setAttribute__('style',
                                                'width: %s%%' % percent)

    def doRestartAlert(self):
        '''Display a restart alert -- some item just installed or removed
        requires a restart'''
        msclog.log("MSC", "restart_required")
        self._status_restartAlertDismissed = 0
        alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
            NSLocalizedString(u"Restart Required", u"Restart Required title"),
            NSLocalizedString(u"Restart", u"Restart button title"), nil, nil,
            u"%@",
            NSLocalizedString(
                u"Software installed or removed requires a restart. You will "
                "have a chance to save open documents.",
                u"Restart Required alert detail"))
        alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
            self.statusWindowController.window(), self,
            self.restartAlertDidEnd_returnCode_contextInfo_, nil)

    @AppHelper.endSheetMethod
    def restartAlertDidEnd_returnCode_contextInfo_(self, alert, returncode,
                                                   contextinfo):
        '''Called when restartAlert ends'''
        msclog.log("MSC", "restart_confirmed")
        self._status_restartAlertDismissed = 1
        munki.restartNow()

    def setMessage_(self, messageText):
        '''Display main status message'''
        messageText = NSBundle.mainBundle().localizedStringForKey_value_table_(
            messageText, messageText, None)
        self._status_message = messageText
        document = self.statusWindowController.webView.mainFrameDocument()
        if document:
            spinner = document.getElementById_('updates-progress-spinner')
            if spinner:  # we are displaying the updates status page
                textElement = document.getElementById_('primary-status-text')
                if textElement:
                    if messageText:
                        textElement.setInnerText_(messageText)
                    else:
                        textElement.setInnerHTML_('&nbsp;')

    def setDetail_(self, detailText):
        '''Display status detail'''
        detailText = NSBundle.mainBundle().localizedStringForKey_value_table_(
            detailText, detailText, None)
        self._status_detail = detailText
        document = self.statusWindowController.webView.mainFrameDocument()
        if document:
            spinner = document.getElementById_('updates-progress-spinner')
            if spinner:  # we are displaying the updates status page
                textElement = document.getElementById_('secondary-status-text')
                if textElement:
                    if detailText:
                        textElement.setInnerText_(detailText)
                    else:
                        textElement.setInnerHTML_('&nbsp;')

    def getStopBtnState(self):
        '''Get the state (pressed or not) of the stop button'''
        return self._status_stopBtnState

    def hideStopButton(self):
        '''Hide the stop button'''
        if self._status_stopBtnState:
            return
        self._status_stopBtnHidden = True
        document = self.statusWindowController.webView.mainFrameDocument()
        if document:
            spinner = document.getElementById_('updates-progress-spinner')
            if spinner:  # we are displaying the updates status page
                install_btn = document.getElementById_(
                    'install-all-button-text')
                if install_btn:
                    btn_classes = install_btn.className().split(' ')
                    if not 'hidden' in btn_classes:
                        btn_classes.append('hidden')
                        install_btn.setClassName_(' '.join(btn_classes))

    def showStopButton(self):
        '''Show the stop button'''
        if self._status_stopBtnState:
            return
        self._status_stopBtnHidden = False
        document = self.statusWindowController.webView.mainFrameDocument()
        if document:
            spinner = document.getElementById_('updates-progress-spinner')
            if spinner:  # we are displaying the updates status page
                install_btn = document.getElementById_(
                    'install-all-button-text')
                if install_btn:
                    btn_classes = install_btn.className().split(' ')
                    if 'hidden' in btn_classes:
                        btn_classes.remove('hidden')
                        install_btn.setClassName_(' '.join(btn_classes))

    def enableStopButton(self):
        '''Enable the stop button'''
        if self._status_stopBtnState:
            return
        self._status_stopBtnDisabled = False
        document = self.statusWindowController.webView.mainFrameDocument()
        if document:
            spinner = document.getElementById_('updates-progress-spinner')
            if spinner:  # we are displaying the updates status page
                install_btn = document.getElementById_(
                    'install-all-button-text')
                if install_btn:
                    btn_classes = install_btn.className().split(' ')
                    if 'disabled' in btn_classes:
                        btn_classes.remove('disabled')
                        install_btn.setClassName_(' '.join(btn_classes))

    def disableStopButton(self):
        '''Disable the stop button'''
        if self._status_stopBtnState:
            return
        self._status_stopBtnDisabled = True
        document = self.statusWindowController.webView.mainFrameDocument()
        if document:
            spinner = document.getElementById_('updates-progress-spinner')
            if spinner:  # we are displaying the updates status page
                install_btn = document.getElementById_(
                    'install-all-button-text')
                if install_btn:
                    btn_classes = install_btn.className().split(' ')
                    if not 'disabled' in btn_classes:
                        btn_classes.append('disabled')
                        install_btn.setClassName_(' '.join(btn_classes))

    def getRestartAlertDismissed(self):
        '''Was the restart alert dimissed?'''
        return self._status_restartAlertDismissed
class xcPROJECTNAMEASIDENTIFIERxc_AppDelegate(NSObject):
    window = IBOutlet()
    _managedObjectModel = None
    _persistentStoreCoordinator = None
    _managedObjectContext = None
    
    def applicationDidFinishLaunching_(self, sender):
        self.managedObjectContext()

    def applicationSupportFolder(self):
        paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)
        basePath = paths[0] if (len(paths) > 0) else NSTemporaryDirectory()
        return os.path.join(basePath, "xcPROJECTNAMEASIDENTIFIERxc")

    def managedObjectModel(self):
        if self._managedObjectModel: return self._managedObjectModel
            
        self._managedObjectModel = NSManagedObjectModel.mergedModelFromBundles_(None)
        return self._managedObjectModel
    
    def persistentStoreCoordinator(self):
        if self._persistentStoreCoordinator: return self._persistentStoreCoordinator
        
        applicationSupportFolder = self.applicationSupportFolder()
        if not os.path.exists(applicationSupportFolder):
            os.mkdir(applicationSupportFolder)
        
        storePath = os.path.join(applicationSupportFolder, "xcPROJECTNAMEASIDENTIFIERxc.xml")
        url = NSURL.fileURLWithPath_(storePath)
        self._persistentStoreCoordinator = NSPersistentStoreCoordinator.alloc().initWithManagedObjectModel_(self.managedObjectModel())
        
        success, error = self._persistentStoreCoordinator.addPersistentStoreWithType_configuration_URL_options_error_(NSXMLStoreType, None, url, None, None)
        if not success:
            NSApp().presentError_(error)
        
        return self._persistentStoreCoordinator
        
    def managedObjectContext(self):
        if self._managedObjectContext:  return self._managedObjectContext
        
        coordinator = self.persistentStoreCoordinator()
        if coordinator:
            self._managedObjectContext = NSManagedObjectContext.alloc().init()
            self._managedObjectContext.setPersistentStoreCoordinator_(coordinator)
        
        return self._managedObjectContext
    
    def windowWillReturnUndoManager_(self, window):
        return self.managedObjectContext().undoManager()
        
    @IBAction
    def saveAction_(self, sender):
        success, error = self.managedObjectContext().save_(None)
        if not success:
            NSApp().presentError_(error)

    def applicationShouldTerminate_(self, sender):
        if not self._managedObjectContext:
            return NSTerminateNow
        if not self._managedObjectContext.commitEditing():
            return NSTerminateCancel
        
        if self._managedObjectContext.hasChanges():
            success, error = self._managedObjectContext.save_(None)

            if success:
                return NSTerminateNow
            
            if NSApp().presentError_(error):
                return NSTerminateCancel
            else:
                alertReturn = NSRunAlertPanel(None, "Could not save changes while quitting. Quit anyway?" , "Quit anyway", "Cancel", nil)
                if alertReturn == NSAlertAlternateReturn:
                    return NSTerminateCancel

        return NSTerminateNow
Beispiel #18
0
class IEDLog(NSObject):

    # Singleton instance.
    _instance = None

    logWindow = IBOutlet()
    logTableView = IBOutlet()
    levelSelector = IBOutlet()

    logLines = list()
    visibleLogLines = list()

    def init(self):
        # Initialize singleton.
        if IEDLog._instance is not None:
            return IEDLog._instance
        self = super(IEDLog, self).init()
        if self is None:
            return None
        IEDLog._instance = self
        self.logAtBottom = True
        return self

    def awakeFromNib(self):
        global defaults
        self.levelSelector.selectItemAtIndex_(
            defaults.integerForKey_(u"LogLevel"))
        self.logTableView.setDataSource_(self)
        nc = NSNotificationCenter.defaultCenter()
        nc.addObserver_selector_name_object_(
            self, self.logViewScrolled_, NSViewBoundsDidChangeNotification,
            self.logTableView.enclosingScrollView().contentView())

    # Helper methods.

    def addMessage_level_(self, message, level):
        # Log messages may come from background threads.
        self.performSelectorOnMainThread_withObject_waitUntilDone_(
            self.addMessageAndLevel_, {
                u"message": message,
                u"level": level
            }, False)

    def addMessageAndLevel_(self, messageAndLevel):
        message = messageAndLevel[u"message"]
        level = messageAndLevel[u"level"]
        logLine = IEDLogLine.alloc().initWithMessage_level_(message, level)
        self.logLines.append(logLine)
        if defaults.integerForKey_(u"LogLevel") >= level:
            self.visibleLogLines.append(logLine)
            if self.logTableView:
                self.logTableView.reloadData()
                if self.logAtBottom:
                    self.logTableView.scrollRowToVisible_(
                        len(self.visibleLogLines) - 1)

    # Act on user showing log window.

    @LogException
    @IBAction
    def displayLogWindow_(self, sender):
        self.logAtBottom = True
        self.logTableView.scrollRowToVisible_(len(self.visibleLogLines) - 1)
        self.logWindow.makeKeyAndOrderFront_(self)

    # Act on notification for log being scrolled by user.

    def logViewScrolled_(self, notification):
        tableViewHeight = self.logTableView.bounds().size.height
        scrollView = self.logTableView.enclosingScrollView()
        scrollRect = scrollView.documentVisibleRect()
        scrollPos = scrollRect.origin.y + scrollRect.size.height

        if scrollPos >= tableViewHeight:
            self.logAtBottom = True
        else:
            self.logAtBottom = False

    # Act on user filtering log.

    @LogException
    @IBAction
    def setLevel_(self, sender):
        self.visibleLogLines = [
            x for x in self.logLines
            if x.level() <= self.levelSelector.indexOfSelectedItem()
        ]
        self.logAtBottom = True
        self.logTableView.reloadData()
        self.logTableView.scrollRowToVisible_(len(self.visibleLogLines) - 1)

    # Act on user clicking save button.

    @LogException
    @IBAction
    def saveLog_(self, sender):
        panel = NSSavePanel.savePanel()
        panel.setExtensionHidden_(False)
        panel.setAllowedFileTypes_([u"log", u"txt"])
        formatter = NSDateFormatter.alloc().init()
        formatter.setDateFormat_(u"yyyy-MM-dd HH.mm")
        dateStr = formatter.stringFromDate_(NSDate.date())
        panel.setNameFieldStringValue_(u"AutoDMG %s" % dateStr)
        result = panel.runModal()
        if result != NSFileHandlingPanelOKButton:
            return

        exists, error = panel.URL().checkResourceIsReachableAndReturnError_(
            None)
        if exists:
            success, error = NSFileManager.defaultManager(
            ).removeItemAtURL_error_(panel.URL(), None)
            if not success:
                NSApp.presentError_(error)
                return

        success, error = NSData.data().writeToURL_options_error_(
            panel.URL(), 0, None)
        if not success:
            NSApp.presentError_(error)
            return

        fh, error = NSFileHandle.fileHandleForWritingToURL_error_(
            panel.URL(), None)
        if fh is None:
            NSAlert.alertWithError_(error).runModal()
            return
        formatter = NSDateFormatter.alloc().init()
        formatter.setDateFormat_(u"yyyy-MM-dd HH:mm:ss")
        for logLine in self.logLines:
            textLine = NSString.stringWithFormat_(
                u"%@ %@: %@\n", formatter.stringFromDate_(logLine.date()),
                IEDLogLevelName(logLine.level()), logLine.message())
            fh.writeData_(textLine.dataUsingEncoding_(NSUTF8StringEncoding))
        fh.closeFile()

    # We're an NSTableViewDataSource.

    def numberOfRowsInTableView_(self, tableView):
        return len(self.visibleLogLines)

    def tableView_objectValueForTableColumn_row_(self, tableView, column, row):
        if column.identifier() == u"date":
            return self.visibleLogLines[row].date()
        elif column.identifier() == u"level":
            return IEDLogLevelName(self.visibleLogLines[row].level())
        elif column.identifier() == u"message":
            return self.visibleLogLines[row].message()
Beispiel #19
0
class IEDAppDelegate(NSObject):

    mainWindowController = IBOutlet()
    appVersionController = IBOutlet()
    helpMenuItem = IBOutlet()

    def init(self):
        self = super(IEDAppDelegate, self).init()
        if self is None:
            return None

        return self

    def initialize(self):
        # Log version info on startup.
        version, build = IEDUtil.getAppVersion()
        LogInfo("AutoDMG v%@ build %@", version, build)
        name, version, build = IEDUtil.readSystemVersion_("/")
        LogInfo("%@ %@ %@", name, version, build)
        LogInfo("%@ %@ (%@)", platform.python_implementation(),
                platform.python_version(), platform.python_compiler())
        LogInfo("PyObjC %@", pyObjCVersion)

        # Initialize user defaults before application starts.
        defaultsPath = NSBundle.mainBundle().pathForResource_ofType_(
            "Defaults", "plist")
        defaultsDict = NSDictionary.dictionaryWithContentsOfFile_(defaultsPath)
        defaults.registerDefaults_(defaultsDict)

    def applicationDidFinishLaunching_(self, sender):
        NSApplication.sharedApplication().disableRelaunchOnLogin()
        version, build = IEDUtil.getAppVersion()
        if version.lower().endswith("b"):
            NSApplication.sharedApplication().dockTile().setBadgeLabel_("beta")
        updateProfileInterval = defaults.integerForKey_(
            "UpdateProfileInterval")
        if updateProfileInterval:
            lastCheck = defaults.objectForKey_("LastUpdateProfileCheck")
            if lastCheck.timeIntervalSinceNow() < -60 * 60 * 18:
                self.mainWindowController.updateController.checkForProfileUpdatesSilently(
                )

        appVersionCheckInterval = defaults.integerForKey_(
            "AppVersionCheckInterval")
        if appVersionCheckInterval:
            lastCheck = defaults.objectForKey_("LastAppVersionCheck")
            if lastCheck.timeIntervalSinceNow() < -60 * 60 * 18:
                self.appVersionController.checkForAppUpdateSilently_(True)

    def applicationShouldTerminate_(self, sender):
        LogDebug("applicationShouldTerminate:")
        if self.mainWindowController.busy():
            alert = NSAlert.alloc().init()
            alert.setAlertStyle_(NSCriticalAlertStyle)
            alert.setMessageText_("Application busy")
            alert.setInformativeText_("Quitting now could leave the "
                                      "system in an unpredictable state.")
            alert.addButtonWithTitle_("Quit")
            alert.addButtonWithTitle_("Stay")
            button = alert.runModal()
            if button == NSAlertSecondButtonReturn:
                return NSTerminateCancel
        return NSTerminateNow

    def applicationWillTerminate_(self, sender):
        LogDebug("applicationWillTerminate:")
        self.mainWindowController.cleanup()

    @LogException
    @IBAction
    def showHelp_(self, sender):
        NSWorkspace.sharedWorkspace().openURL_(
            NSURL.URLWithString_(defaults.stringForKey_("HelpURL")))

    # Trampolines for document handling.

    @LogException
    @IBAction
    def saveDocument_(self, sender):
        LogDebug("saveDocument:")
        self.mainWindowController.saveTemplate()

    @LogException
    @IBAction
    def saveDocumentAs_(self, sender):
        LogDebug("saveDocumentAs:")
        self.mainWindowController.saveTemplateAs()

    @LogException
    @IBAction
    def openDocument_(self, sender):
        LogDebug("openDocument:")
        self.mainWindowController.openTemplate()

    def validateMenuItem_(self, menuItem):
        if menuItem == self.helpMenuItem:
            return True
        else:
            return not self.mainWindowController.busy()

    def application_openFile_(self, application, filename):
        return self.mainWindowController.openTemplateAtURL_(
            NSURL.fileURLWithPath_(filename))
class MSUStatusWindowController(NSObject):
    '''Controls the status window.'''

    # since this subclasses NSObject,
    # it doesn't have a Python __init__method
    # pylint: disable=no-init

    window = IBOutlet()
    logWindow = IBOutlet()
    messageFld = IBOutlet()
    detailFld = IBOutlet()
    progressIndicator = IBOutlet()
    stopBtn = IBOutlet()
    imageFld = IBOutlet()

    backdropWindow = IBOutlet()
    backdropImageFld = IBOutlet()

    stopBtnState = 0
    restartAlertDismissed = 0
    got_status_update = False
    receiving_notifications = False
    timer = None
    timeout_counter = 0
    saw_process = False
    managedsoftwareupdate_pid = None
    window_level = NSScreenSaverWindowLevel - 1


    @IBAction
    def stopBtnClicked_(self, sender):
        '''Called when stop button is clicked in the status window'''
        if debug:
            NSLog(u"Stop button was clicked.")
        sender.setState_(1)
        self.stopBtnState = 1
        sender.setEnabled_(False)
        # send a notification that stop button was clicked
        STOP_REQUEST_FLAG = ('/private/tmp/com.googlecode.munki.'
                             'managedsoftwareupdate.stop_requested')
        if not os.path.exists(STOP_REQUEST_FLAG):
            open(STOP_REQUEST_FLAG, 'w').close()

    def registerForNotifications(self):
        '''Register for notification messages'''
        dnc = NSDistributedNotificationCenter.defaultCenter()
        dnc.addObserver_selector_name_object_suspensionBehavior_(
            self,
            self.updateStatus_,
            'com.googlecode.munki.managedsoftwareupdate.statusUpdate',
            None,
            NSNotificationSuspensionBehaviorDeliverImmediately)
        dnc.addObserver_selector_name_object_suspensionBehavior_(
            self,
            self.managedsoftwareupdateStarted_,
            'com.googlecode.munki.managedsoftwareupdate.started',
            None,
            NSNotificationSuspensionBehaviorDeliverImmediately)
        dnc.addObserver_selector_name_object_suspensionBehavior_(
            self,
            self.managedsoftwareupdateEnded_,
            'com.googlecode.munki.managedsoftwareupdate.ended',
            None,
            NSNotificationSuspensionBehaviorDeliverImmediately)
        self.receiving_notifications = True

    def unregisterForNotifications(self):
        '''Tell the DistributedNotificationCenter to stop sending us
        notifications'''
        NSDistributedNotificationCenter.defaultCenter().removeObserver_(self)
        # set self.receiving_notifications to False so our process monitoring
        # thread will exit
        self.receiving_notifications = False

    def managedsoftwareupdateStarted_(self, notification):
        '''Called when we get a
        com.googlecode.munki.managedsoftwareupdate.started notification'''
        if 'pid' in notification.userInfo():
            self.managedsoftwareupdate_pid = notification.userInfo()['pid']
            NSLog('managedsoftwareupdate pid %s started'
                  % self.managedsoftwareupdate_pid)

    def managedsoftwareupdateEnded_(self, notification):
        '''Called when we get a
        com.googlecode.munki.managedsoftwareupdate.ended notification'''
        NSLog('managedsoftwareupdate pid %s ended'
              % notification.userInfo().get('pid'))

    def haveElCapPolicyBanner(self):
        '''Returns True if we are running El Cap or later and there is
        a loginwindow PolicyBanner in place'''
        # Get our Darwin major version
        darwin_vers = int(os.uname()[2].split('.')[0])
        if darwin_vers > 14:
            for test_file in ['/Library/Security/PolicyBanner.txt',
                              '/Library/Security/PolicyBanner.rtf',
                              '/Library/Security/PolicyBanner.rtfd']:
                if os.path.exists(test_file):
                    return True
        return False

    def setWindowLevel(self):
        '''Sets our NSWindowLevel. Works around issues with the loginwindow
        PolicyBanner in 10.11+ Some code based on earlier work by Pepijn
        Bruienne'''
        # bump our NSWindowLevel if we have a PolicyBanner in ElCap+
        if self.haveElCapPolicyBanner():
            NSLog('El Capitan+ loginwindow PolicyBanner found')
            self.window_level = NSScreenSaverWindowLevel

    def initStatusSession(self):
        '''Initialize our status session'''
        self.setWindowLevel()
        consoleuser = munki.getconsoleuser()
        if consoleuser == None or consoleuser == u"loginwindow":
            self.displayBackdropWindow()
            # needed so the window can show over the loginwindow
            self.window.setCanBecomeVisibleWithoutLogin_(True)
            self.window.setLevel_(self.window_level)

        self.window.center()
        self.messageFld.setStringValue_(
            NSLocalizedString(u"Starting…", None))
        self.detailFld.setStringValue_(u"")
        self.stopBtn.setHidden_(False)
        self.stopBtn.setEnabled_(True)
        self.stopBtnState = 0
        if self.imageFld:
            theImage = NSImage.imageNamed_("MunkiStatus")
            self.imageFld.setImage_(theImage)
        if self.progressIndicator:
            self.progressIndicator.setMinValue_(0.0)
            self.progressIndicator.setMaxValue_(100.0)
            self.progressIndicator.setIndeterminate_(True)
            self.progressIndicator.setUsesThreadedAnimation_(True)
            self.progressIndicator.startAnimation_(self)
        self.window.orderFrontRegardless()
        self.registerForNotifications()
        # start our process monitor timer so we can be notified about
        # process failure
        self.timeout_counter = 6
        self.saw_process = False
        self.timer = (NSTimer.
            scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
                5.0, self, self.checkProcess, None, YES))

    def checkProcess(self):
        '''Monitors managedsoftwareupdate process for failure to start
        or unexpected exit, so we're not waiting around forever if
        managedsoftwareupdate isn't running.'''
        PYTHON_SCRIPT_NAME = 'managedsoftwareupdate'
        NEVER_STARTED = -2
        UNEXPECTEDLY_QUIT = -1

        NSLog('checkProcess timer fired')

        if self.window_level == NSScreenSaverWindowLevel:
            # we're at the loginwindow, there is a PolicyBanner, and we're
            # running under 10.11+. Make sure we're in the front.
            NSApp.activateIgnoringOtherApps_(YES)
            if not self.logWindow.isVisible():
                self.window.makeKeyAndOrderFront_(self)

        if self.got_status_update:
            # we got a status update since we last checked; no need to
            # check the process table
            self.timeout_counter = 6
            self.saw_process = True
            # clear the flag so we have to get another status update
            self.got_status_update = False
        elif munki.pythonScriptRunning(PYTHON_SCRIPT_NAME):
            self.timeout_counter = 6
            self.saw_process = True
        else:
            NSLog('managedsoftwareupdate not running...')
            self.timeout_counter -= 1
        if self.timeout_counter == 0:
            NSLog('Timed out waiting for managedsoftwareupdate.')
            if self.saw_process:
                self.statusSessionFailed_(UNEXPECTEDLY_QUIT)
            else:
                self.statusSessionFailed_(NEVER_STARTED)

    def statusSessionFailed_(self, sessionResult):
        '''Called if the status session fails'''
        NSLog('statusSessionFailed: %s' % sessionResult)
        self.cleanUpStatusSession()
        NSApp.terminate_(self)

    def cleanUpStatusSession(self):
        '''Clean things up before we exit'''
        self.unregisterForNotifications()
        if self.backdropWindow and self.backdropWindow.isVisible():
            self.backdropWindow.orderOut_(self)
        self.window.orderOut_(self)
        # clean up timer
        if self.timer:
            self.timer.invalidate()
            self.timer = None

    def configureAndDisplayBackdropWindow_(self, window):
        '''Sets all our configuration options for our masking windows'''
        window.setCanBecomeVisibleWithoutLogin_(True)
        if self.haveElCapPolicyBanner():
            self.backdropWindow.setLevel_(self.window_level)
        else:
            self.backdropWindow.setLevel_(self.window_level - 1)
        translucentColor = NSColor.blackColor().colorWithAlphaComponent_(0.35)
        window.setBackgroundColor_(translucentColor)
        window.setOpaque_(False)
        window.setIgnoresMouseEvents_(False)
        window.setAlphaValue_(0.0)
        window.orderFrontRegardless()
        window.animator().setAlphaValue_(1.0)

    def displayBackdropWindow(self):
        '''Draw a window that covers the login UI'''
        self.backdropWindow.setCanBecomeVisibleWithoutLogin_(True)
        if self.haveElCapPolicyBanner():
            self.backdropWindow.setLevel_(self.window_level)
        else:
            self.backdropWindow.setLevel_(self.window_level - 1)
        screenRect = NSScreen.mainScreen().frame()
        self.backdropWindow.setFrame_display_(screenRect, True)

        darwin_vers = int(os.uname()[2].split('.')[0])
        if darwin_vers < 11:
            if self.backdropImageFld:
                bgImage = getLoginwindowPicture()
                self.backdropImageFld.setImage_(bgImage)
                self.backdropWindow.orderFrontRegardless()
        else:
            # Lion+
            # draw transparent/translucent windows to prevent interaction
            # with the login UI
            self.backdropImageFld.setHidden_(True)
            self.configureAndDisplayBackdropWindow_(self.backdropWindow)
            # are there any other screens?
            for screen in NSScreen.screens():
                if screen != NSScreen.mainScreen():
                    # create another masking window for this secondary screen
                    window_rect = screen.frame()
                    window_rect.origin = NSPoint(0.0, 0.0)
                    child_window = NSWindow.alloc(
                        ).initWithContentRect_styleMask_backing_defer_screen_(
                            window_rect,
                            NSBorderlessWindowMask, NSBackingStoreBuffered,
                            NO, screen)
                    self.configureAndDisplayBackdropWindow_(child_window)
                    if self.haveElCapPolicyBanner():
                        self.backdropWindow.addChildWindow_ordered_(
                            child_window, NSWindowAbove)

        if self.haveElCapPolicyBanner():
            # preserve the relative ordering of the backdrop window and the
            # status window IOW, clicking the backdrop window will not bring it
            # in front of the status window
            self.backdropWindow.addChildWindow_ordered_(
                self.window, NSWindowAbove)


    def updateStatus_(self, notification):
        '''Called when we get a
        com.googlecode.munki.managedsoftwareupdate.statusUpdate notification;
        update our status display with information from the notification'''

        if self.window_level == NSScreenSaverWindowLevel:
            # we're at the loginwindow, there is a PolicyBanner, and we're
            # running under 10.11+. Make sure we're in the front.
            NSApp.activateIgnoringOtherApps_(YES)
            if not self.logWindow.isVisible():
                self.window.makeKeyAndOrderFront_(self)

        self.got_status_update = True
        info = notification.userInfo()
        # explictly get keys from info object; PyObjC in Mountain Lion
        # seems to need this
        info_keys = info.keys()
        if 'message' in info_keys:
            self.setMessage_(info['message'])
        if 'detail' in info_keys:
            self.setDetail_(info['detail'])
        if 'percent' in info_keys:
            self.setPercentageDone_(info['percent'])
        if self.stopBtnState == 0 and 'stop_button_visible' in info_keys:
            if info['stop_button_visible']:
                self.showStopButton()
            else:
                self.hideStopButton()
        if self.stopBtnState == 0 and 'stop_button_enabled' in info_keys:
            if info['stop_button_enabled']:
                self.enableStopButton()
            else:
                self.disableStopButton()

        command = info.get('command')
        if command == 'activate':
            NSApp.activateIgnoringOtherApps_(YES)
            self.window.orderFrontRegardless()
        elif command == 'showRestartAlert':
            # clean up timer
            if self.timer:
                self.timer.invalidate()
                self.timer = None
            self.doRestartAlert()
        elif command == 'quit':
            self.cleanUpStatusSession()
            NSApp.terminate_(self)

    def setPercentageDone_(self, percent):
        '''Set progress indicator to display percent done'''
        if float(percent) < 0:
            if not self.progressIndicator.isIndeterminate():
                self.progressIndicator.setIndeterminate_(True)
                self.progressIndicator.startAnimation_(self)
        else:
            if self.progressIndicator.isIndeterminate():
                self.progressIndicator.stopAnimation_(self)
                self.progressIndicator.setIndeterminate_(False)
            self.progressIndicator.setDoubleValue_(float(percent))

    @AppHelper.endSheetMethod
    def restartAlertDidEnd_returnCode_contextInfo_(
            self, alert, returncode, contextinfo):
        '''Called when restart alert is dismissed'''
        # we don't use the returncode or contextinfo arguments
        # pylint: disable=unused-argument
        self.restartAlertDismissed = 1
        munki.restartNow()

    def doRestartAlert(self):
        '''Display a restart alert'''
        self.restartAlertDismissed = 0
        # pylint: disable=line-too-long
        nsa = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
            NSLocalizedString(u"Restart Required", None),
            NSLocalizedString(u"Restart", None),
            nil,
            nil,
            NSLocalizedString(
                u"Software installed or removed requires a restart. "
                "You will have a chance to save open documents.", None))
        # pylint: enable=line-too-long
        nsa.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
            self.window, self, self.restartAlertDidEnd_returnCode_contextInfo_,
            nil)

    def setMessage_(self, messageText):
        '''Set the main status message'''
        messageText = NSBundle.mainBundle().localizedStringForKey_value_table_(
            messageText, messageText, None)
        self.messageFld.setStringValue_(messageText)

    def setDetail_(self, detailText):
        '''Set the status detail text'''
        detailText = NSBundle.mainBundle().localizedStringForKey_value_table_(
            detailText, detailText, None)
        self.detailFld.setStringValue_(detailText)

    def getStopBtnState(self):
        '''Return True if the stop button was clicked; False otherwise'''
        return self.stopBtnState

    def hideStopButton(self):
        '''Hide the stop button'''
        self.stopBtn.setHidden_(True)

    def showStopButton(self):
        '''Show the stop button'''
        self.stopBtn.setHidden_(False)

    def enableStopButton(self):
        '''Enable the stop button'''
        self.stopBtn.setEnabled_(True)

    def disableStopButton(self):
        '''Disable the stop button'''
        self.stopBtn.setEnabled_(False)

    def getRestartAlertDismissed(self):
        '''Return True if the restart alert was dismissed; False otherwise'''
        return self.restartAlertDismissed
class IEDAddPkgController(NSObject):

    addPkgLabel = IBOutlet()
    tableView = IBOutlet()
    removeButton = IBOutlet()

    movedRowsType = u"se.gu.it.AdditionalPackages"

    def init(self):
        self = super(IEDAddPkgController, self).init()
        if self is None:
            return None

        self.packages = list()
        self.packagePaths = set()

        return self

    def awakeFromNib(self):
        self.tableView.setDataSource_(self)
        self.tableView.registerForDraggedTypes_(
            [NSFilenamesPboardType, IEDAddPkgController.movedRowsType])
        self.dragEnabled = True

    # Helper methods.

    def disableControls(self):
        self.dragEnabled = False
        self.addPkgLabel.setTextColor_(NSColor.disabledControlTextColor())
        self.tableView.setEnabled_(False)
        self.removeButton.setEnabled_(False)

    def enableControls(self):
        self.dragEnabled = True
        self.addPkgLabel.setTextColor_(NSColor.controlTextColor())
        self.tableView.setEnabled_(True)
        self.removeButton.setEnabled_(True)

    def getPackageSize_(self, path):
        p = subprocess.Popen([u"/usr/bin/du", u"-sk", path],
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            LogError(u"du failed with exit code %d", p.returncode)
        else:
            return int(out.split()[0]) * 1024

    # External state of controller.

    def packagesToInstall(self):
        return self.packages

    # Act on remove button.

    @IBAction
    def removeButtonClicked_(self, sender):
        row = self.tableView.selectedRow()
        if row == -1:
            return
        self.packagePaths.remove(self.packages[row].path())
        del self.packages[row]
        self.tableView.reloadData()

    # We're an NSTableViewDataSource.

    def numberOfRowsInTableView_(self, tableView):
        return len(self.packages)

    def tableView_objectValueForTableColumn_row_(self, tableView, column, row):
        # FIXME: Use bindings.
        if column.identifier() == u"image":
            return self.packages[row].image()
        elif column.identifier() == u"name":
            return self.packages[row].name()

    def tableView_validateDrop_proposedRow_proposedDropOperation_(
            self, tableView, info, row, operation):
        if not self.dragEnabled:
            return NSDragOperationNone
        if info.draggingSource() == tableView:
            return NSDragOperationMove
        pboard = info.draggingPasteboard()
        paths = pboard.propertyListForType_(NSFilenamesPboardType)
        if not paths:
            return NSDragOperationNone
        for path in paths:
            # Don't allow multiple copies.
            if path in self.packagePaths:
                return NSDragOperationNone
            # Ensure the file extension is pkg or mpkg.
            name, ext = os.path.splitext(path)
            if ext.lower() not in (u".pkg", u".mpkg"):
                return NSDragOperationNone
        return NSDragOperationCopy

    def tableView_acceptDrop_row_dropOperation_(self, tableView, info, row,
                                                operation):
        if not self.dragEnabled:
            return False
        pboard = info.draggingPasteboard()
        # If the source is the tableView, we're reordering packages within the
        # table and the pboard contains the source row indices.
        if info.draggingSource() == tableView:
            indices = [
                int(i) for i in pboard.propertyListForType_(
                    IEDAddPkgController.movedRowsType).split(u",")
            ]
            for i in indices:
                self.packages[row], self.packages[i] = self.packages[
                    i], self.packages[row]
        else:
            # Otherwise it's a list of paths to add to the table.
            paths = pboard.propertyListForType_(NSFilenamesPboardType)
            for i, path in enumerate(paths):
                package = IEDPackage.alloc().init()
                package.setName_(os.path.basename(path))
                package.setPath_(path)
                package.setSize_(self.getPackageSize_(path))
                package.setImage_(
                    NSWorkspace.sharedWorkspace().iconForFile_(path))
                self.packages.insert(row + i, package)
                self.packagePaths.add(path)
        tableView.reloadData()
        return True

    def tableView_writeRowsWithIndexes_toPasteboard_(self, tableView,
                                                     rowIndexes, pboard):
        # When reordering packages put a list of indices as a string onto the pboard.
        indices = list()
        index = rowIndexes.firstIndex()
        while index != NSNotFound:
            indices.append(index)
            index = rowIndexes.indexGreaterThanIndex_(index)
        pboard.declareTypes_owner_([IEDAddPkgController.movedRowsType], self)
        pboard.setPropertyList_forType_(u",".join(unicode(i) for i in indices),
                                        IEDAddPkgController.movedRowsType)
        return True
Beispiel #22
0
class MSUupdatesViewController(NSViewController):
    '''
    Controls the updates view of the main window
    '''

    restartInfoFld = IBOutlet()
    restartImageFld = IBOutlet()
    descriptionView = IBOutlet()
    tableView = IBOutlet()
    optionalSoftwareBtn = IBOutlet()
    array_controller = IBOutlet()
    window_controller = IBOutlet()
    updateNowBtn = IBOutlet()

    _EMPTYUPDATELIST = NSArray.arrayWithArray_([{
        "image":
        NSImage.imageNamed_("Empty.png"),
        "name":
        "",
        "version":
        "",
        "size":
        "",
        "description":
        ""
    }])
    _updatelist = []

    def updatelist(self):
        #NSLog(u"MSUupdatesViewController.updatelist")
        return self._updatelist or self._EMPTYUPDATELIST

    objc.accessor(updatelist)  # PyObjC KVO hack

    def setUpdatelist_(self, newlist):
        #NSLog(u"MSUupdatesViewController.setUpdatelist_")
        self._updatelist = NSArray.arrayWithArray_(newlist)

    objc.accessor(setUpdatelist_)  # PyObjC KVO hack

    @IBAction
    def laterBtnClicked_(self, sender):
        NSApp.delegate().laterBtnClicked()

    @IBAction
    def updateNowBtnClicked_(self, sender):
        # alert the user to logout, proceed without logout, or cancel
        NSApp.delegate().confirmInstallUpdates()

    @IBAction
    def optionalSoftwareBtnClicked_(self, sender):
        # switch to optional software pane
        munki.log("user", "view_optional_software")
        self.window_controller.theTabView.selectNextTabViewItem_(sender)
        NSApp.delegate().optional_view_controller.AddRemoveBtn.setEnabled_(NO)
        #NSApp.delegate().buildOptionalInstallsData()

    def updateWebKitView_(self, description):
        if "</html>" in description or "</HTML>" in description:
            self.descriptionView.mainFrame().loadHTMLString_baseURL_(
                description, None)
        else:
            self.descriptionView.mainFrame(
            ).loadData_MIMEType_textEncodingName_baseURL_(
                buffer(description.encode('UTF-8')), u"text/plain", u"utf-8",
                None)

    def updateDescriptionView(self):
        #NSLog(u"MSUupdatesViewController.updateDescriptionView")
        if len(self.array_controller.selectedObjects()):
            row = self.array_controller.selectedObjects()[0]
            description = row.get("description", u"")
        else:
            description = u""
        self.performSelectorOnMainThread_withObject_waitUntilDone_(
            self.updateWebKitView_, description, YES)

    def tableViewSelectionDidChange_(self, sender):
        #NSLog(u"MSUupdatesViewController.tableViewSelectionDidChange_")
        #self.performSelectorOnMainThread_withObject_waitUntilDone_(
        #    self.updateDescriptionView, None, NO)
        self.updateDescriptionView()
class IEDUpdateController(NSObject):

    profileController = IBOutlet()

    updateBox = IBOutlet()

    applyUpdatesCheckbox = IBOutlet()
    updateTable = IBOutlet()
    updateTableImage = IBOutlet()
    updateTableLabel = IBOutlet()
    downloadButton = IBOutlet()

    downloadWindow = IBOutlet()
    downloadLabel = IBOutlet()
    downloadProgressBar = IBOutlet()
    downloadStopButton = IBOutlet()

    updateBoxHeight = IBOutlet()

    def init(self):
        self = super(IEDUpdateController, self).init()
        if self is None:
            return None

        self.cache = IEDUpdateCache.alloc().initWithDelegate_(self)
        self.updates = list()
        self.downloadTotalSize = 0
        self.downloads = list()
        self.delegate = None
        self.version = None
        self.build = None
        self.profileWarning = None
        self.boxTableSizeDelta = 0

        return self

    def setDelegate_(self, delegate):
        self.delegate = delegate

    def awakeFromNib(self):
        self.cachedImage = NSImage.imageNamed_("Package")
        self.uncachedImage = NSImage.imageNamed_("Package blue arrow")

        self.updatesAllOKImage = NSImage.imageNamed_("Checkmark")
        self.updatesToDownloadImage = NSImage.imageNamed_("Download")
        self.updatesWarningImage = NSImage.imageNamed_("Exclamation")
        self.updateTableImage.setImage_(None)
        self.updateTableLabel.setStringValue_("")
        self.updateTable.setDataSource_(self)

        self.boxTableSizeDelta = self.updateBox.frame(
        ).size.height - self.updateTable.frame().size.height

        self.updateHeight()

    def validateMenuItem_(self, menuItem):
        return not self.delegate.busy()

    # Helper methods.

    def updateHeight(self):
        rowHeight = 18
        rows = self.numberOfRowsInTableView_(self.updateTable)
        height = rows * rowHeight
        height = max(min(height, int(6.5 * rowHeight)), 3.5 * rowHeight)
        NSAnimationContext.beginGrouping()
        NSAnimationContext.currentContext().setDuration_(0.15)
        easeInEaseOut = Quartz.CAMediaTimingFunction.functionWithName_(
            Quartz.kCAMediaTimingFunctionEaseInEaseOut)
        NSAnimationContext.currentContext().setTimingFunction_(easeInEaseOut)
        self.updateBoxHeight.animator().setConstant_(height +
                                                     self.boxTableSizeDelta)
        NSAnimationContext.endGrouping()

    def disableControls(self):
        LogDebug("disableControls")
        self.applyUpdatesCheckbox.setEnabled_(False)
        self.updateTable.setEnabled_(False)
        self.downloadButton.setEnabled_(False)

    def enableControls(self):
        LogDebug("enableControls")
        self.applyUpdatesCheckbox.setEnabled_(len(self.updates) > 0)
        self.updateTable.setEnabled_(len(self.updates) > 0)
        self.downloadButton.setEnabled_(len(self.downloads) > 0)

    def showRemainingDownloads(self):
        if self.profileWarning:
            self.updateTableImage.setImage_(self.updatesWarningImage)
            self.updateTableLabel.setStringValue_(self.profileWarning)
            self.updateTableLabel.setTextColor_(NSColor.controlTextColor())
            return

        if len(self.downloads) == 0:
            self.updateTableLabel.setStringValue_("All updates downloaded")
            self.updateTableLabel.setTextColor_(
                NSColor.disabledControlTextColor())
            self.updateTableImage.setImage_(self.updatesAllOKImage)
        else:
            sizeStr = IEDUtil.formatByteSize_(self.downloadTotalSize)
            plurals = "s" if len(self.downloads) >= 2 else ""
            downloadLabel = "%d update%s to download (%s)" % (len(
                self.downloads), plurals, sizeStr)
            self.updateTableLabel.setStringValue_(downloadLabel)
            self.updateTableLabel.setEnabled_(True)
            self.updateTableLabel.setTextColor_(NSColor.controlTextColor())
            self.updateTableImage.setImage_(self.updatesToDownloadImage)

    def countDownloads(self):
        LogDebug("countDownloads")
        self.downloads = list()
        self.downloadTotalSize = 0
        for package in self.updates:
            if self.cache.isCached_(package.sha1()):
                package.setImage_(self.cachedImage)
            else:
                package.setImage_(self.uncachedImage)
                self.downloadTotalSize += package.size()
                self.downloads.append(package)
        self.updateTable.reloadData()
        self.showRemainingDownloads()
        self.updateHeight()

    # External state of controller.

    def allUpdatesDownloaded(self):
        if self.applyUpdatesCheckbox.state() == NSOffState:
            return True
        return len(self.downloads) == 0

    def packagesToInstall(self):
        if self.applyUpdatesCheckbox.state() == NSOffState:
            return []
        return self.updates

    # Act on profile update requested.

    @LogException
    @IBAction
    def checkForProfileUpdates_(self, sender):
        self.silent = False
        self.doCheckForProfileUpdates()

    def checkForProfileUpdatesSilently(self):
        self.silent = True
        self.doCheckForProfileUpdates()

    def doCheckForProfileUpdates(self):
        LogInfo("Checking for updates")
        self.dateBeforeUpdating = self.profileController.publicationDate
        self.disableControls()
        defaults = NSUserDefaults.standardUserDefaults()
        url = NSURL.URLWithString_(defaults.stringForKey_("UpdateProfilesURL"))
        self.profileController.updateFromURL_(url)

    @LogException
    @IBAction
    def cancelProfileUpdateCheck_(self, sender):
        self.profileController.cancelUpdateDownload()

    # IEDProfileController delegate methods.

    def profileUpdateAllDone(self):
        self.enableControls()
        if not self.silent:
            alert = NSAlert.alloc().init()
            formatter = NSDateFormatter.alloc().init()
            formatter.setDateFormat_("yyyy-MM-dd HH.mm")
            dateStr = formatter.stringFromDate_(
                self.profileController.publicationDate)
            if self.dateBeforeUpdating != self.profileController.publicationDate:
                alert.setMessageText_("Profile updated")
                alert.setInformativeText_("Publication date: %s" % dateStr)
            else:
                alert.setMessageText_("No profile update available")
                alert.setInformativeText_("Last update: %s" % dateStr)
            alert.runModal()

    def profileUpdateFailed_(self, error):
        alert = NSAlert.alloc().init()
        alert.setMessageText_(error.localizedDescription())
        alert.setInformativeText_(error.userInfo()[NSErrorFailingURLStringKey])
        alert.runModal()
        self.silent = True

    def profileUpdateSucceeded_(self, publicationDate):
        LogDebug("profileUpdateSucceeded:%@", publicationDate)
        defaults = NSUserDefaults.standardUserDefaults()
        defaults.setObject_forKey_(NSDate.date(), "LastUpdateProfileCheck")

    def profilesUpdated(self):
        LogDebug("profilesUpdated")
        self.cache.pruneAndCreateSymlinks(self.profileController.updatePaths)
        if self.version or self.build:
            self.loadProfileForVersion_build_(self.version, self.build)

    # Load update profile.

    def loadProfileForVersion_build_(self, version, build):
        LogDebug("loadProfileForVersion:%@ build:%@", version, build)
        self.version = version
        self.build = build
        self.updates = list()
        profile = self.profileController.profileForVersion_Build_(
            version, build)
        if profile is None:
            # No update profile for this build, try to figure out why.
            self.profileWarning = self.profileController.whyNoProfileForVersion_build_(
                version, build)
        else:
            if self.profileController.deprecatedOS:
                self.profileWarning = "No longer updated by Apple"
            else:
                self.profileWarning = None
            for update in profile:
                package = IEDPackage.alloc().init()
                package.setName_(update["name"])
                package.setPath_(self.cache.updatePath_(update["sha1"]))
                package.setSize_(update["size"])
                package.setUrl_(update["url"])
                package.setSha1_(update["sha1"])
                # Image is set by countDownloads().
                self.updates.append(package)
        self.countDownloads()

    # Act on apply updates checkbox changing.

    @LogException
    @IBAction
    def applyUpdatesCheckboxChanged_(self, sender):
        if self.delegate:
            self.delegate.updateControllerChanged()

    # Act on download button being clicked.

    @LogException
    @IBAction
    def downloadButtonClicked_(self, sender):
        self.disableControls()
        self.downloadLabel.setStringValue_("")
        self.downloadProgressBar.setIndeterminate_(True)
        self.downloadWindow.makeKeyAndOrderFront_(self)
        self.downloadCounter = 0
        self.downloadNumUpdates = len(self.downloads)
        self.cache.downloadUpdates_(self.downloads)

    # Act on download stop button being clicked.

    @LogException
    @IBAction
    def downloadStopButtonClicked_(self, sender):
        self.cache.stopDownload()

    # UpdateCache delegate methods.

    def downloadAllDone(self):
        LogDebug("downloadAllDone")
        self.downloadWindow.orderOut_(self)
        self.countDownloads()
        self.enableControls()
        if self.delegate:
            self.delegate.updateControllerChanged()

    def downloadStarting_(self, package):
        LogDebug("downloadStarting:")
        self.downloadProgressBar.setIndeterminate_(False)
        self.downloadProgressBar.setDoubleValue_(0.0)
        self.downloadProgressBar.setMaxValue_(package.size())
        self.downloadCounter += 1
        self.downloadLabel.setStringValue_(
            "%s (%s)" %
            (package.name(), IEDUtil.formatByteSize_(package.size())))

    def downloadStarted_(self, package):
        LogDebug("downloadStarted:")
        self.downloadStopButton.setEnabled_(True)

    def downloadStopped_(self, package):
        LogDebug("downloadStopped:")
        self.downloadStopButton.setEnabled_(False)

    def downloadGotData_bytesRead_(self, package, bytes):
        self.downloadProgressBar.setDoubleValue_(bytes)

    def downloadSucceeded_(self, package):
        LogDebug("downloadSucceeded:")
        self.countDownloads()
        if self.delegate:
            self.delegate.updateControllerChanged()

    def downloadFailed_withError_(self, package, message):
        LogDebug("downloadFailed:withError:")
        alert = NSAlert.alloc().init()
        alert.setMessageText_("Download failed")
        alert.setInformativeText_(message)
        alert.runModal()

    # We're an NSTableViewDataSource.

    def numberOfRowsInTableView_(self, tableView):
        return len(self.updates)

    def tableView_objectValueForTableColumn_row_(self, tableView, column, row):
        # FIXME: Use bindings.
        if column.identifier() == "image":
            return self.updates[row].image()
        elif column.identifier() == "name":
            return self.updates[row].name()
Beispiel #24
0
class PreferencesController(NSWindowController):
    # window
    windowObjc = IBOutlet()
    tabViewObjc = IBOutlet()

    # General
    generalTitle = IBOutlet()
    uiLanguageLabel = IBOutlet()
    uiLanguage = IBOutlet()
    destLanguageLabel = IBOutlet()
    destLanguage = IBOutlet()
    translationServiceLabel = IBOutlet()
    translationService = IBOutlet()

    ui_language_helper = None
    dest_language_helper = None
    translation_service_helper = None

    # Google
    googleServiceURLLabel = IBOutlet()
    googleServiceURL = IBOutlet()

    google_service_url_helper = None

    # Support
    sentryDsn = IBOutlet()

    def windowDidLoad(self):
        NSWindowController.windowDidLoad(self)

        # window
        self.windowObjc.setValue_forKey_(_('Preferences'), 'title')
        # tab view
        tab_view_objc = self.tabViewObjc.valueForKey_('tabViewItems')

        # General
        tab_view_objc.objectAtIndex_(0).setValue_forKey_(_('General'), 'label')
        self.uiLanguageLabel.setStringValue_(_('UI Language'))
        self.ui_language_helper = PopUpButtonHelper(
            objc_obj=self.uiLanguage,
            values=Languages,
            selected_value=config.Common.ui_language,
            text_map=Languages.text_map(),
        )
        self.destLanguageLabel.setStringValue_(_('Destination Language'))
        self.dest_language_helper = PopUpButtonHelper(
            objc_obj=self.destLanguage,
            values=Languages,
            selected_value=config.Common.dest_language,
            text_map=Languages.text_map(),
        )
        self.translationServiceLabel.setStringValue_(_('Translation Service'))
        self.translation_service_helper = PopUpButtonHelper(
            objc_obj=self.translationService,
            values=TranslationServices,
            selected_value=config.Common.translation_service,
            text_map=TranslationServices.text_map(),
        )

        # Google
        self.googleServiceURLLabel.setStringValue_(_('Service URL'))
        self.google_service_url_helper = PopUpButtonHelper(
            objc_obj=self.googleServiceURL,
            values=GOOGLE_SERVICE_URLS,
            selected_value=config.Google.service_url,
            text_map=None,
        )

        # Support
        self.sentryDsn.setStringValue_(config.Support.sentry_dsn)

    @IBAction
    def uiLanguage_(self, _):
        config.Common.ui_language = self \
            .ui_language_helper.selected_value
        config.dump()

        i18n()

    @IBAction
    def destLanguage_(self, _):
        config.Common.dest_language = self \
            .dest_language_helper.selected_value
        config.dump()

    @IBAction
    def translationService_(self, _):
        config.Common.translation_service = self \
            .translation_service_helper.selected_value
        config.dump()

    @IBAction
    def googleServiceURL_(self, _):
        config.Google.service_url = self \
            .google_service_url_helper.selected_value
        config.dump()

    @IBAction
    def sentryDsn_(self, _):
        config.Support.sentry_dsn = self.sentryDsn.stringValue()
        config.dump()
Beispiel #25
0
class LLLogWindowController(NSObject):

    window = IBOutlet()
    logView = IBOutlet()
    backdropWindow = IBOutlet()
    logFileData = LLLogViewDataSource.alloc().init()
    fileHandle = None
    updateTimer = None

    def showLogWindow_(self, title):
        # Base all sizes on the screen's dimensions.
        screenRect = NSScreen.mainScreen().frame()

        # Open a log window that covers most of the screen.
        self.window.setTitle_(title)
        self.window.setCanBecomeVisibleWithoutLogin_(True)
        self.window.setLevel_(NSStatusWindowLevel)
        self.window.orderFrontRegardless()
        # Resize the log window so that it leaves a border on all sides.
        # Add a little extra border at the bottom so we don't cover the
        # loginwindow message.
        windowRect = screenRect.copy()
        windowRect.origin.x = 100.0
        windowRect.origin.y = 200.0
        windowRect.size.width -= 200.0
        windowRect.size.height -= 300.0
        NSAnimationContext.beginGrouping()
        NSAnimationContext.currentContext().setDuration_(0.5)
        self.window.animator().setFrame_display_(windowRect, True)
        NSAnimationContext.endGrouping()

        # Create a transparent, black backdrop window that covers the whole
        # screen and fade it in slowly.
        self.WindowArray = NSMutableArray.new()
        for screen in NSScreen.screens():
            Rect = screen.frame()
            backdropWindow = NSWindow.alloc(
            ).initWithContentRect_styleMask_backing_defer_(
                Rect, NSBorderlessWindowMask, NSBackingStoreBuffered, NO)
            backdropWindow.setCanBecomeVisibleWithoutLogin_(True)
            backdropWindow.setLevel_(NSStatusWindowLevel)
            #backdropWindow.setFrame_display_(screenRect, True)
            translucentColor = NSColor.blackColor().colorWithAlphaComponent_(
                0.8)
            backdropWindow.setBackgroundColor_(translucentColor)
            backdropWindow.setOpaque_(False)
            backdropWindow.setIgnoresMouseEvents_(True)
            backdropWindow.setAlphaValue_(0.0)
            backdropWindow.orderFrontRegardless()
            NSAnimationContext.beginGrouping()
            NSAnimationContext.currentContext().setDuration_(1.0)
            backdropWindow.animator().setAlphaValue_(1.0)
            NSAnimationContext.endGrouping()
            self.WindowArray.addObject_(backdropWindow)
            backdropWindow.makeKeyAndOrderFront_(NSApp)

    def watchLogFile_(self, logFile):
        # Display and continuously update a log file in the main window.
        self.stopWatching()
        self.logFileData.removeAllLines()
        self.logView.setDataSource_(self.logFileData)
        self.logView.reloadData()
        self.fileHandle = NSFileHandle.fileHandleForReadingAtPath_(logFile)
        self.refreshLog()
        # Kick off a timer that updates the log view periodically.
        self.updateTimer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
            0.25, self, u"refreshLog", None, YES)

    def stopWatching(self):
        # Release the file handle and stop the update timer.
        if self.fileHandle is not None:
            self.fileHandle.closeFile()
            self.fileHandle = None
        if self.updateTimer is not None:
            self.updateTimer.invalidate()
            self.updateTimer = None

    def refreshLog(self):
        # Check for new available data, read it, and scroll to the bottom.
        data = self.fileHandle.availableData()
        if data.length():
            utf8string = NSString.alloc().initWithData_encoding_(
                data, NSUTF8StringEncoding)
            for line in utf8string.splitlines(True):
                if line.endswith(u"\n"):
                    self.logFileData.addLine_partial_(line.rstrip(u"\n"),
                                                      False)
                else:
                    self.logFileData.addLine_partial_(line, True)
            self.logView.reloadData()
            self.logView.scrollRowToVisible_(self.logFileData.lineCount() - 1)
Beispiel #26
0
class ExperienceController(NSWindowController):

	# outlets for UI elements
	experienceText = IBOutlet()
	recordButton = IBOutlet()
	playAudioButton = IBOutlet()
	deleteAudioButton = IBOutlet()
	progressBar = IBOutlet()

	recordingAudio = False
	playingAudio = False
	audio_file = ''

	@IBAction
	def recordText_(self, sender):
		t = cfg.NOW()
		message = self.experienceText.stringValue()
		e = Experience(t, message)
		self.sniffer.activity_tracker.storage.session.add(e)
		# may not need to commit here, but could wait till next round of parsing
		self.sniffer.activity_tracker.storage.sqlcommit()

		print 'Received experience message of: ' + message
		self.expController.close()

	@IBAction
	def toggleAudioRecording_(self, sender):
		if self.recordingAudio:
			self.recordingAudio = False
			print "Stop audio message"

			# get the right name for our audio file
			dt = datetime.now().strftime("%y%m%d-%H%M%S%f")
			audioName = str(os.path.join(cfg.CURRENT_DIR, "audio/")) + dt + '.m4a'
			self.audio_file = audioName
			audioName = string.replace(audioName, "/", ":")
			audioName = audioName[1:]

			# start the audio recording
			s = NSAppleScript.alloc().initWithSource_("set filePath to \"" + audioName + "\" \n set placetosaveFile to a reference to file filePath \n tell application \"QuickTime Player\" \n set mydocument to document 1 \n tell document 1 \n stop \n end tell \n set newRecordingDoc to first document whose name = \"untitled\" \n export newRecordingDoc in placetosaveFile using settings preset \"Audio Only\" \n close newRecordingDoc without saving \n quit \n end tell")
			s.executeAndReturnError_(None)

			# log the experience in our table
			t = cfg.NOW()
			e = Experience(t, dt + '.m4a')
			self.sniffer.activity_tracker.storage.session.add(e)
			# may not need to commit here, but could wait till next round of parsing
			self.sniffer.activity_tracker.storage.sqlcommit()

			# reset controls
			self.expController.recordButton.setTitle_("Record")
			self.expController.recordButton.setEnabled_(False)
			self.expController.playAudioButton.setHidden_(False)
			self.expController.deleteAudioButton.setHidden_(False)
			self.expController.progressBar.setHidden_(False)

		else:
			self.recordingAudio = True
			print "Start audio message"

			s = NSAppleScript.alloc().initWithSource_("tell application \"QuickTime Player\" \n set new_recording to (new audio recording) \n tell new_recording \n start \n end tell \n tell application \"System Events\" \n set visible of process \"QuickTime Player\" to false \n repeat until visible of process \"QuickTime Player\" is false \n end repeat \n end tell \n end tell")
			s.executeAndReturnError_(None)

			self.expController.recordButton.setTitle_("Stop Recording")
			# TODO change button color to red while recording

	@IBAction
	def toggleAudioPlay_(self, sender):
		if self.playingAudio:
			self.stopAudioPlay()

		else:
			self.playingAudio = True
			self.expController.playAudioButton.setTitle_("Stop")

			s = NSAppleScript.alloc().initWithSource_("set filePath to POSIX file \"" + self.audio_file + "\" \n tell application \"QuickTime Player\" \n open filePath \n tell application \"System Events\" \n set visible of process \"QuickTime Player\" to false \n repeat until visible of process \"QuickTime Player\" is false \n end repeat \n end tell \n play the front document \n end tell")
			s.executeAndReturnError_(None)

			# Stop playback once end of audio file is reached
			length = mutagen.mp4.MP4(self.audio_file).info.length
			stop_thread = threading.Timer(length, self.stopAudioPlay)
			stop_thread.start()

			if length >= 1.0:
				advance_thread = threading.Timer(1.0, self.incrementProgressBar, [1.0, length])
				advance_thread.start()

			# s = objc.selector(self.stopAudioPlay,signature='v@:')
			# self.playbackTimer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(length, self, s, None, False)

	def incrementProgressBar(self, t, m):
		print "Got to incrementor"
		i = 1.0/m * 100.0
		self.expController.progressBar.incrementBy_(i)
		if t < m - 1.0:
			t += 1.0
			advance_thread = threading.Timer(1.0, self.incrementProgressBar, [t, m])
			advance_thread.start()

	def stopAudioPlay(self):
		self.playingAudio = False
		self.expController.playAudioButton.setTitle_("Play")
		value = self.expController.progressBar.doubleValue()
		self.expController.progressBar.incrementBy_(100.0 - value)
		self.expController.progressBar.incrementBy_(-100.0)

		s = NSAppleScript.alloc().initWithSource_("tell application \"QuickTime Player\" \n stop the front document \n close the front document \n end tell")
		s.executeAndReturnError_(None)

	@IBAction
	def deleteAudio_(self, sender):
		if (self.audio_file != '') & (self.audio_file != None) :
			if os.path.exists(self.audio_file):
				os.remove(self.audio_file)
		self.audio_file = ''

		# reset button visibility
		self.expController.recordButton.setEnabled_(True)
		self.expController.playAudioButton.setHidden_(True)
		self.expController.deleteAudioButton.setHidden_(True)
		self.expController.progressBar.setHidden_(True)

	# override window close to track when users close the experience window
	def overrideClose(self):
		s = objc.selector(self.setIgnoredAndClose_,signature='v@:@')
		self.expController.window().standardWindowButton_(NSWindowCloseButton).setTarget_(self.expController)
		self.expController.window().standardWindowButton_(NSWindowCloseButton).setAction_(s)
		self.expController.window().standardWindowButton_(NSWindowCloseButton).setKeyEquivalentModifierMask_(NSCommandKeyMask)
		self.expController.window().standardWindowButton_(NSWindowCloseButton).setKeyEquivalent_("w")

	def setIgnoredAndClose_(self, notification):
		self.ignored = True
		NSNotificationCenter.defaultCenter().postNotificationName_object_('experienceReceived',self)
		self.expController.close()

	def windowDidLoad(self):
		NSWindowController.windowDidLoad(self)

	def show(self, sniffer):
		try:
			# close the window if its already open
			if self.expController.window().isVisible():
				self.expController.close()
		except:
			pass

		self.sniffer = sniffer

		# open window from NIB file, show front and center
		self.expController = ExperienceController.alloc().initWithWindowNibName_("experience")
		self.expController.showWindow_(None)
		self.expController.window().makeKeyAndOrderFront_(None)
		self.expController.window().center()
		self.expController.retain()

		# needed to show window on top of other applications
		# NSNotificationCenter.defaultCenter().postNotificationName_object_('makeAppActive',self)

		self.sniffer.app.activateIgnoringOtherApps_(True)

		self.overrideClose(self)

		return self.expController

	show = classmethod(show)
Beispiel #27
0
class PreferencesController(NSWindowController):

    # outlets for UI elements
    screenshotSizePopup = IBOutlet()
    screenshotSizeMenu = IBOutlet()
    clearDataPopup = IBOutlet()

    sniffer = None

    # handle changes to the preferences
    @IBAction
    def changeScreenshot_(self, sender):
        screenshots = getValueForPreference('screenshots')
        periodic = getValueForPreference('periodicScreenshots')
        if screenshots and periodic:
            self.sniffer.activity_tracker.startLoops()
        elif not screenshots or not periodic:
            self.sniffer.activity_tracker.stopLoops()

    @IBAction
    def changePeriodicScreenshots_(self, sender):
        periodic = getValueForPreference('periodicScreenshots')
        if periodic:
            self.sniffer.activity_tracker.startLoops()
        else:
            self.sniffer.activity_tracker.stopLoops()

    @IBAction
    def changePeriodicRate_(self, sender):
        self.sniffer.activity_tracker.restartScreenshotLoop()

    @IBAction
    def changeKeystrokeRecording_(self, sender):
        print "You asked us to start/stop keystroke recording"

    @IBAction
    def changeBookmark_(self, sender):
        print "You asked us to start/stop periodic bookmarking"

    @IBAction
    def changeBookmarkRate_(self, sender):
        print "You asked us to change the bookmark rate"

    @IBAction
    def clearData_(self, sender):
        self.sniffer.activity_tracker.clearData()

    def windowDidLoad(self):
        NSWindowController.windowDidLoad(self)

        # Set screenshot size options based on screen's native height
        self.prefController.screenshotSizeMenu.removeAllItems()
        nativeHeight = int(NSScreen.mainScreen().frame().size.height)
        menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
            str(nativeHeight) + ' px', '', '')
        menuitem.setTag_(nativeHeight)
        self.prefController.screenshotSizeMenu.addItem_(menuitem)

        sizes = [1080, 720, 480]
        for x in sizes:
            if x < nativeHeight:
                menuitem = NSMenuItem.alloc(
                ).initWithTitle_action_keyEquivalent_(str(x) + ' px', '', '')
                menuitem.setTag_(x)
                self.prefController.screenshotSizeMenu.addItem_(menuitem)

        # update newly created screenshot size dropdown to select saved preference or default size
        selectedSize = NSUserDefaultsController.sharedUserDefaultsController(
        ).values().valueForKey_('imageSize')
        selectedMenuItem = self.prefController.screenshotSizeMenu.itemWithTag_(
            selectedSize)
        if (selectedMenuItem):
            self.prefController.screenshotSizePopup.selectItemWithTag_(
                selectedSize)
        else:
            nativeMenuItem = self.prefController.screenshotSizeMenu.itemWithTag_(
                nativeHeight)
            NSUserDefaultsController.sharedUserDefaultsController().defaults(
            ).setInteger_forKey_(nativeHeight, 'imageSize')
            self.prefController.screenshotSizePopup.selectItemWithTag_(
                nativeHeight)

    def show(self, sniffer):
        try:
            if self.prefController:
                self.prefController.close()
        except:
            pass

        self.sniffer = sniffer

        # open window from NIB file, show front and center
        self.prefController = PreferencesController.alloc(
        ).initWithWindowNibName_("preferences")
        self.prefController.showWindow_(None)
        self.prefController.window().makeKeyAndOrderFront_(None)
        self.prefController.window().center()
        self.prefController.retain()

        # NSNotificationCenter.defaultCenter().postNotificationName_object_('makeAppActive',self)

        self.sniffer.app.activateIgnoringOtherApps_(True)

        # make window close on Cmd-w
        self.prefController.window().standardWindowButton_(
            NSWindowCloseButton).setKeyEquivalentModifierMask_(
                NSCommandKeyMask)
        self.prefController.window().standardWindowButton_(
            NSWindowCloseButton).setKeyEquivalent_("w")

        return self.prefController

    show = classmethod(show)
Beispiel #28
0
class MSCLogWindowController(NSObject):
    '''Controller object for our log window'''

    # since this subclasses NSObject,
    # it doesn't have a Python __init__method
    # pylint: disable=no-init

    window = IBOutlet()
    logView = IBOutlet()
    searchField = IBOutlet()
    pathControl = IBOutlet()

    logFileData = MSCLogViewDataSource.alloc().init()

    fileHandle = None
    updateTimer = None

    def copy_(self, sender):
        '''Implements copy operation so we can copy data from table view'''
        text_to_copy = ''
        index_set = self.logView.selectedRowIndexes()
        index = index_set.firstIndex()
        while index != NSNotFound:
            line = self.logFileData.filteredData.objectAtIndex_(index)
            text_to_copy += line + '\n'
            index = index_set.indexGreaterThanIndex_(index)
        pasteboard = NSPasteboard.generalPasteboard()
        changeCount = pasteboard.clearContents()
        result = pasteboard.writeObjects_([text_to_copy])

    @IBAction
    def searchFilterChanged_(self, sender):
        '''User changed the search field'''
        filterString = self.searchField.stringValue().lower()
        self.logFileData.filterText = filterString
        self.logFileData.applyFilterToData()
        self.logView.reloadData()

    @IBAction
    def showLogWindow_(self, notification):
        '''Show the log window.'''

        if self.window.isVisible():
            # It's already open, just move it to front
            self.window.makeKeyAndOrderFront_(self)
            return

        screenRect = NSScreen.mainScreen().frame()
        windowRect = screenRect.copy()
        windowRect.origin.x = 100.0
        windowRect.origin.y = 200.0
        windowRect.size.width -= 200.0
        windowRect.size.height -= 300.0

        logfile = munki.pref('LogFile')
        self.pathControl.setURL_(NSURL.fileURLWithPath_(logfile))
        self.window.setTitle_(os.path.basename(logfile))
        self.window.setFrame_display_(windowRect, NO)
        self.window.makeKeyAndOrderFront_(self)
        self.watchLogFile_(logfile)

        # allow dragging from table view to outside of the app
        self.logView.setDraggingSourceOperationMask_forLocal_(
            NSDragOperationAll, NO)

    def watchLogFile_(self, logFile):
        '''Display and continuously update a log file in the main window.'''
        self.stopWatching()
        self.logFileData.removeAllLines()
        self.logView.setDataSource_(self.logFileData)
        self.logView.reloadData()
        self.fileHandle = NSFileHandle.fileHandleForReadingAtPath_(logFile)
        self.refreshLog()
        # Kick off a timer that updates the log view periodically.
        self.updateTimer = (
            NSTimer.
            scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
                0.25, self, self.refreshLog, None, YES))

    def stopWatching(self):
        '''Release the file handle and stop the update timer.'''
        if self.fileHandle is not None:
            self.fileHandle.closeFile()
            self.fileHandle = None
        if self.updateTimer is not None:
            self.updateTimer.invalidate()
            self.updateTimer = None

    def refreshLog(self):
        '''Check for new available data, read it, and scroll to the bottom.'''
        data = self.fileHandle.availableData()
        if data.length():
            utf8string = NSString.alloc().initWithData_encoding_(
                data, NSUTF8StringEncoding)
            for line in utf8string.splitlines(True):
                if line.endswith(u"\n"):
                    self.logFileData.addLine_partial_(line.rstrip(u"\n"),
                                                      False)
                else:
                    self.logFileData.addLine_partial_(line, True)
            self.logView.reloadData()
            self.logView.scrollRowToVisible_(self.logFileData.lineCount() - 1)

    def windowWillClose_(self, notification):
        '''NSWindow delegate method -- if our window is closing,
        stop watching the log file.'''
        self.stopWatching()
Beispiel #29
0
class MSCAppDelegate(NSObject):

    mainWindowController = IBOutlet()
    statusController = IBOutlet()
    passwordAlertController = IBOutlet()

    def applicationShouldTerminate_(self, sender):
        '''Called if user selects 'Quit' from menu'''
        return self.mainWindowController.appShouldTerminate()

    def applicationDidFinishLaunching_(self, sender):
        '''NSApplication delegate method called at launch'''
        NSLog("Finished launching")
        # setup client logging
        msclog.setup_logging()

        # userInfo dict can be nil, seems to be with 10.6
        if sender.userInfo():
            userNotification = sender.userInfo().get(
                'NSApplicationLaunchUserNotificationKey')
            # we get this notification at launch because it's too early to have declared ourself
            # a NSUserNotificationCenterDelegate
            if userNotification:
                NSLog("Launched via Notification interaction")
                self.userNotificationCenter_didActivateNotification_(
                    NSUserNotificationCenter.defaultUserNotificationCenter(),
                    userNotification)

        # Prevent automatic relaunching at login on Lion+
        if NSApp.respondsToSelector_('disableRelaunchOnLogin'):
            NSApp.disableRelaunchOnLogin()

        ver = NSBundle.mainBundle().infoDictionary().get(
            'CFBundleShortVersionString')
        msclog.log("MSC", "launched", "VER=%s" % ver)

        # if we're running under Snow Leopard, swap out the Dock icon for one
        # without the Retina assets to avoid an appearance issue when the
        # icon has a badge in the Dock (and App Switcher)
        # Darwin major version 10 is Snow Leopard (10.6)
        if os.uname()[2].split('.')[0] == '10':
            myImage = NSImage.imageNamed_("Managed Software Center 10_6")
            NSApp.setApplicationIconImage_(myImage)

        # if we are running under Mountain Lion or later set ourselves as a delegate
        # for NSUserNotificationCenter notifications
        if os.uname()[2].split('.')[0] > '11':
            NSUserNotificationCenter.defaultUserNotificationCenter(
            ).setDelegate_(self)

        # have the statuscontroller register for its own notifications
        self.statusController.registerForNotifications()

        # user may have launched the app manually, or it may have
        # been launched by /usr/local/munki/managedsoftwareupdate
        # to display available updates
        if munki.thereAreUpdatesToBeForcedSoon(hours=2):
            # skip the check and just display the updates
            # by pretending the lastcheck is now
            lastcheck = NSDate.date()
        else:
            lastcheck = munki.pref('LastCheckDate')
        max_cache_age = munki.pref('CheckResultsCacheSeconds')
        # if there is no lastcheck timestamp, check for updates.
        if not lastcheck:
            self.mainWindowController.checkForUpdates()
        elif lastcheck.timeIntervalSinceNow() * -1 > int(max_cache_age):
            # check for updates if the last check is over the
            # configured manualcheck cache age max.
            self.mainWindowController.checkForUpdates()
        elif MunkiItems.updateCheckNeeded():
            # check for updates if we have optional items selected for install
            # or removal that have not yet been processed
            self.mainWindowController.checkForUpdates()

        # load the initial view only if we are not already loading something else.
        # enables launching the app to a specific panel, eg. from URL handler
        if not self.mainWindowController.webView.isLoading():
            self.mainWindowController.loadInitialView()

    def applicationWillFinishLaunching_(self, notification):
        '''Installs URL handler for calls outside the app eg. web clicks'''
        man = NSAppleEventManager.sharedAppleEventManager()
        man.setEventHandler_andSelector_forEventClass_andEventID_(
            self, "openURL:withReplyEvent:",
            struct.unpack(">i", "GURL")[0],
            struct.unpack(">i", "GURL")[0])

    def openMunkiURL(self, url):
        '''Display page associated with munki:// url'''
        parsed_url = urlparse(url)
        if parsed_url.scheme != 'munki':
            msclog.debug_log("URL %s has unsupported scheme" % url)
            return
        filename = mschtml.unquote(parsed_url.netloc)
        # add .html if no extension
        if not os.path.splitext(filename)[1]:
            filename += u'.html'
        if filename.endswith(u'.html'):
            mschtml.build_page(filename)
            self.mainWindowController.load_page(filename)
        else:
            msclog.debug_log(
                "%s doesn't have a valid extension. Prevented from opening" %
                url)

    def openURL_withReplyEvent_(self, event, replyEvent):
        '''Handle openURL messages'''
        keyDirectObject = struct.unpack(">i", "----")[0]
        url = event.paramDescriptorForKeyword_(
            keyDirectObject).stringValue().decode('utf8')
        msclog.log("MSU", "Called by external URL: %s", url)
        self.openMunkiURL(url)

    def userNotificationCenter_didActivateNotification_(
            self, center, notification):
        '''User clicked on a Notification Center alert'''
        user_info = notification.userInfo()
        if user_info.get('action') == 'open_url':
            url = user_info.get('value', 'munki://updates')
            msclog.log("MSU", "Got user notification to open %s" % url)
            self.openMunkiURL(url)
            center.removeDeliveredNotification_(notification)
        else:
            msclog.log("MSU",
                       "Got user notification with unrecognized userInfo")

    def userNotificationCenter_shouldPresentNotification_(
            self, center, notification):
        return True

    def userNotificationCenter_didDeliverNotification_(self, center,
                                                       notification):
        pass
class IEDAddPkgController(NSObject):

    addPkgLabel = IBOutlet()
    tableView = IBOutlet()
    removeButton = IBOutlet()

    movedRowsType = "se.gu.it.AdditionalPackages"

    def init(self):
        self = super(IEDAddPkgController, self).init()
        if self is None:
            return None

        self.packages = list()
        self.packagePaths = set()

        return self

    def awakeFromNib(self):
        self.tableView.setDataSource_(self)
        self.tableView.registerForDraggedTypes_(
            [NSFilenamesPboardType, IEDAddPkgController.movedRowsType])
        self.dragEnabled = True

    # Helper methods.

    def disableControls(self):
        self.dragEnabled = False
        self.addPkgLabel.setTextColor_(NSColor.disabledControlTextColor())
        self.tableView.setEnabled_(False)
        self.removeButton.setEnabled_(False)

    def enableControls(self):
        self.dragEnabled = True
        self.addPkgLabel.setTextColor_(NSColor.controlTextColor())
        self.tableView.setEnabled_(True)
        self.removeButton.setEnabled_(True)

    # External state of controller.

    def packagesToInstall(self):
        return self.packages

    # Loading.

    def replacePackagesWithPaths_(self, packagePaths):
        del self.packages[:]
        self.packagePaths.clear()
        for path in packagePaths:
            package = IEDPackage.alloc().init()
            package.setName_(os.path.basename(path))
            package.setPath_(path)
            package.setSize_(IEDUtil.getPackageSize_(path))
            package.setImage_(NSWorkspace.sharedWorkspace().iconForFile_(path))
            self.packages.append(package)
            self.packagePaths.add(path)
        self.tableView.reloadData()

    # Act on remove button.

    @LogException
    @IBAction
    def removeButtonClicked_(self, sender):
        indexes = self.tableView.selectedRowIndexes()
        row = indexes.lastIndex()
        while row != NSNotFound:
            self.packagePaths.remove(self.packages[row].path())
            del self.packages[row]
            row = indexes.indexLessThanIndex_(row)
        self.tableView.reloadData()
        self.tableView.deselectAll_(self)

    # We're an NSTableViewDataSource.

    def numberOfRowsInTableView_(self, tableView):
        return len(self.packages)

    def tableView_objectValueForTableColumn_row_(self, tableView, column, row):
        # FIXME: Use bindings.
        if column.identifier() == "image":
            return self.packages[row].image()
        elif column.identifier() == "name":
            return self.packages[row].name()

    def tableView_validateDrop_proposedRow_proposedDropOperation_(
            self, tableView, info, row, operation):
        if not self.dragEnabled:
            return NSDragOperationNone
        if info.draggingSource() == tableView:
            return NSDragOperationMove
        pboard = info.draggingPasteboard()
        paths = [
            IEDUtil.resolvePath_(path)
            for path in pboard.propertyListForType_(NSFilenamesPboardType)
        ]
        if not paths:
            return NSDragOperationNone
        for path in paths:
            # Don't allow multiple copies.
            if path in self.packagePaths:
                return NSDragOperationNone
            # Ensure the file extension is valid for additonal packages.
            name, ext = os.path.splitext(path)
            if ext.lower() not in IEDUtil.PACKAGE_EXTENSIONS:
                return NSDragOperationNone
        return NSDragOperationCopy

    def tableView_acceptDrop_row_dropOperation_(self, tableView, info, row,
                                                operation):
        if not self.dragEnabled:
            return False
        pboard = info.draggingPasteboard()
        # If the source is the tableView, we're reordering packages within the
        # table and the pboard contains the source row indexes.
        if info.draggingSource() == tableView:
            indexes = [
                int(i) for i in pboard.propertyListForType_(
                    IEDAddPkgController.movedRowsType).split(",")
            ]
            # If the rows are dropped on top of another line, and the target
            # row is below the first source row, move the target row one line
            # down.
            if (operation == NSTableViewDropOn) and (indexes[0] < row):
                rowAdjust = 1
            else:
                rowAdjust = 0
            # Move the dragged rows out from the package list into draggedRows.
            draggedRows = list()
            for i in sorted(indexes, reverse=True):
                draggedRows.insert(0, (i, self.packages.pop(i)))
            # Adjust the target row since we have removed items.
            row -= len([x for x in draggedRows if x[0] < row])
            row += rowAdjust
            # Insert them at the new place.
            for i, (index, item) in enumerate(draggedRows):
                self.packages.insert(row + i, item)
            # Select the newly moved lines.
            selectedIndexes = NSIndexSet.indexSetWithIndexesInRange_(
                NSMakeRange(row, len(draggedRows)))
            tableView.selectRowIndexes_byExtendingSelection_(
                selectedIndexes, False)
        else:
            # Otherwise it's a list of paths to add to the table.
            paths = [
                IEDUtil.resolvePath_(path)
                for path in pboard.propertyListForType_(NSFilenamesPboardType)
            ]
            # Remove duplicates from list.
            seen = set()
            paths = [x for x in paths if x not in seen and not seen.add(x)]
            for i, path in enumerate(paths):
                package = IEDPackage.alloc().init()
                package.setName_(os.path.basename(path))
                package.setPath_(path)
                package.setSize_(IEDUtil.getPackageSize_(path))
                package.setImage_(
                    NSWorkspace.sharedWorkspace().iconForFile_(path))
                self.packages.insert(row + i, package)
                self.packagePaths.add(path)
        tableView.reloadData()
        return True

    def tableView_writeRowsWithIndexes_toPasteboard_(self, tableView,
                                                     rowIndexes, pboard):
        # When reordering packages put a list of indexes as a string onto the pboard.
        indexes = list()
        index = rowIndexes.firstIndex()
        while index != NSNotFound:
            indexes.append(index)
            index = rowIndexes.indexGreaterThanIndex_(index)
        pboard.declareTypes_owner_([IEDAddPkgController.movedRowsType], self)
        pboard.setPropertyList_forType_(",".join(str(i) for i in indexes),
                                        IEDAddPkgController.movedRowsType)
        return True