new approach to showing status for downloading and installing items from the optional_installs list

This commit is contained in:
Greg Neagle
2014-04-07 10:29:39 -07:00
parent 553ac2bc42
commit 97a001f7b0
8 changed files with 615 additions and 485 deletions
@@ -39,12 +39,8 @@ class MSUBadgedTemplateImage(NSImage):
if count == 0:
# no badge if there are no updates
return super(MSUBadgedTemplateImage, self).imageNamed_(name)
if count > 0:
count_str = str(count)
else:
count_str = str(0)
# build badge string and get its size
badgeString = NSString.stringWithString_(count_str)
badgeString = NSString.stringWithString_(unicode(count))
badgeFont = NSFontManager.sharedFontManager().fontWithFamily_traits_weight_size_(
badgeFontFamilyName, NSBoldFontMask, 0, badgeFontSize)
stringAttributes = { NSFontAttributeName: badgeFont }
@@ -100,12 +96,11 @@ class MSUBadgedTemplateImage(NSImage):
badgeEraseMask.fill()
NSGraphicsContext.restoreGraphicsState()
if count > 0:
# draw badge outline
badgeOutline.stroke()
# draw count string
badgeString.drawWithRect_options_attributes_(badgeStringRect, 0, stringAttributes)
# draw badge outline
badgeOutline.stroke()
# draw count string
badgeString.drawWithRect_options_attributes_(badgeStringRect, 0, stringAttributes)
# all done drawing!
compositeImage.unlockFocus()
@@ -53,6 +53,7 @@ class MSUMainWindowController(NSWindowController):
html_dir = None
# Cocoa UI binding properties
tabControl = IBOutlet()
webView = IBOutlet()
@@ -62,8 +63,18 @@ class MSUMainWindowController(NSWindowController):
updateButtonCell = IBOutlet()
windowMenuSeperatorItem = IBOutlet()
fullScreenMenuItem = IBOutlet()
updateProgressSpinner = IBOutlet()
_disableSoftwareViewButtons = False
@objc.accessor # PyObjC KVO hack
def disableSoftwareViewButtons(self):
return True
return self._disableSoftwareViewButtons
@objc.accessor # PyObjC KVO hack
def setDisableSoftwareViewButtons_(self, bool):
self._disableSoftwareViewButtons = bool
def appShouldTerminate(self):
'''called by app delegate when it receives applicationShouldTerminate:'''
if self.getUpdateCount() == 0:
@@ -127,7 +138,7 @@ class MSUMainWindowController(NSWindowController):
elif returncode == NSAlertOtherReturn:
msulog.log("user", "install_now_clicked")
# make sure this alert panel is gone before we proceed
# which might involve opening another aleet sheet
# which might involve opening another alert sheet
alert.window().orderOut_(self)
# initiate the updates
self.updateNow()
@@ -144,7 +155,11 @@ class MSUMainWindowController(NSWindowController):
def loadInitialView(self):
'''Called by app delegate from applicationDidFinishLaunching:'''
if MunkiItems.getEffectiveUpdateList():
optional_items = MunkiItems.getOptionalInstallItems()
if not optional_items:
# disable software buttons and menu items
self.setDisableSoftwareViewButtons_(True)
if not optional_items or self.getUpdateCount():
self.loadUpdatesPage_(self)
if not munki.thereAreUpdatesToBeForcedSoon():
self._alertedUserToOutstandingUpdates = True
@@ -241,7 +256,7 @@ class MSUMainWindowController(NSWindowController):
# what page are we currently viewing?
page_url = self.webView.mainFrameURL()
filename = NSURL.URLWithString_(page_url).lastPathComponent()
NSLog('Filename: %s' % filename)
#NSLog('Filename: %s' % filename)
name = os.path.splitext(filename)[0]
key, p, quoted_value = name.partition('-')
if key == 'detail':
@@ -271,6 +286,12 @@ class MSUMainWindowController(NSWindowController):
# closing the main window should be the same as quitting
NSApp.terminate_(self)
return NO
def windowDidBecomeMain_(self, notification):
self.tabControl.setEnabled_(YES)
def windowDidResignMain_(self, notification):
self.tabControl.setEnabled_(NO)
def configureFullScreenMenuItem(self):
'''check to see if NSWindow's toggleFullScreen: selector is implemented.
@@ -338,6 +359,7 @@ class MSUMainWindowController(NSWindowController):
if result == 0:
self.managedsoftwareupdate_task = "manualcheck"
NSApp.delegate().statusController.startMunkiStatusSession()
self.markRequestedItemsAsProcessing()
else:
self.munkiStatusSessionEnded_(2)
@@ -379,6 +401,7 @@ class MSUMainWindowController(NSWindowController):
def markPendingItemsAsInstalling(self):
'''While an install/removal session is happening, mark optional items
that are being installed/removed with the appropriate status'''
NSLog('markPendingItemsAsInstalling')
install_info = munki.getInstallInfo()
items_to_be_installed_names = [item['name']
for item in install_info.get('managed_installs', [])]
@@ -389,14 +412,31 @@ class MSUMainWindowController(NSWindowController):
new_status = None
if item['name'] in items_to_be_installed_names:
NSLog('Setting status for %s to "installing"' % item['name'])
new_status = 'installing'
new_status = u'installing'
elif item['name'] in items_to_be_removed_names:
NSLog('Setting status for %s to "removing"' % item['name'])
new_status = u'removing'
if new_status:
item['status'] = new_status
self.updateDOMforOptionalItem(item)
def markRequestedItemsAsProcessing(self):
'''When an update check session is happening, mark optional items
that have been requested as processing'''
NSLog('markRequestedItemsAsProcessing')
for item in MunkiItems.getOptionalInstallItems():
new_status = None
NSLog('Status for %s is %s' % (item['name'], item['status']))
if item['status'] == 'install-requested':
NSLog('Setting status for %s to "downloading"' % item['name'])
new_status = u'downloading'
elif item['status'] == 'removal-requested':
NSLog('Setting status for %s to "preparing-removal"' % item['name'])
new_status = u'preparing-removal'
if new_status:
item['status'] = new_status
self.updateDOMforOptionalItem(item)
def updateNow(self):
'''If user has added to/removed from the list of things to be updated,
run a check session. If there are no more changes, proceed to an update
@@ -425,8 +465,11 @@ class MSUMainWindowController(NSWindowController):
else:
self.managedsoftwareupdate_task = "checktheninstall"
NSApp.delegate().statusController.startMunkiStatusSession()
self.markRequestedItemsAsProcessing()
elif not self._alertedUserToOutstandingUpdates and MunkiItems.updatesContainNonOptionalItems():
# current list of updates contains some not explicitly chosen by the user
self._update_in_progress = False
self.displayUpdateCount()
self.loadUpdatesPage_(self)
self._alertedUserToOutstandingUpdates = True
self.alert_controller.alertToExtraUpdates()
@@ -445,18 +488,10 @@ class MSUMainWindowController(NSWindowController):
'''Display the update count as a badge in the window toolbar
and as an icon badge in the Dock'''
updateCount = self.getUpdateCount()
if self._update_in_progress:
btn_image = MSUBadgedTemplateImage.imageNamed_withCount_(
'toolbarUpdatesTemplate.pdf', -1)
self.updateButtonCell.setImage_(btn_image)
self.updateProgressSpinner.startAnimation_(self)
else:
self.updateProgressSpinner.stopAnimation_(self)
btn_image = MSUBadgedTemplateImage.imageNamed_withCount_(
btn_image = MSUBadgedTemplateImage.imageNamed_withCount_(
'toolbarUpdatesTemplate.pdf', updateCount)
self.updateButtonCell.setImage_(btn_image)
if updateCount:
self.updateButtonCell.setImage_(btn_image)
if updateCount not in [u'', 0]:
NSApp.dockTile().setBadgeLabel_(str(updateCount))
else:
NSApp.dockTile().setBadgeLabel_(None)
@@ -808,6 +843,8 @@ class MSUMainWindowController(NSWindowController):
def updateDOMforOptionalItem(self, item):
'''Update displayed status of an item'''
document = self.webView.mainFrameDocument()
if not document:
return
status_line = document.getElementById_('%s_status_text' % item['name'])
btn = document.getElementById_('%s_action_button_text' % item['name'])
if not btn or not status_line:
@@ -834,11 +871,15 @@ class MSUMainWindowController(NSWindowController):
if not item:
NSLog('Can\'t find item: %s' % item_name)
return
prior_status = item['status']
item.update_status()
self.displayUpdateCount()
self.updateDOMforOptionalItem(item)
if item['status'] in ['will-be-installed', 'update-will-be-installed', 'will-be-removed']:
if (item['status'] in ['install-requested', 'removal-requested']
or prior_status in ['will-be-installed', 'update-will-be-installed',
'will-be-removed']):
self._alertedUserToOutstandingUpdates = False
if not self._update_in_progress:
self.updateNow()
@@ -83,7 +83,7 @@ class MSUStatusController(NSObject):
NEVER_STARTED = -2
UNEXPECTEDLY_QUIT = -1
NSLog('checkProcess timer fired')
#NSLog('checkProcess timer fired')
if self.session_started:
if self.got_status_update:
@@ -511,160 +511,235 @@ class GenericItem(dict):
'''Return localized status display text'''
if self['status'] == 'unavailable':
return self.unavailable_reason_text()
map = { 'installed':
NSLocalizedString(u'Installed',
u'InstalledDisplayText'),
'installing':
NSLocalizedString(u'Installing',
u'InstallingDisplayText'),
'installed-not-removable':
NSLocalizedString(u'Installed',
u'InstalledDisplayText'),
'not-installed':
NSLocalizedString(u'Not installed',
u'NotInstalledDisplayText'),
'will-be-installed':
NSLocalizedString(u'Will be installed',
u'WillBeInstalledDisplayText'),
'must-be-installed':
NSLocalizedString(u'Will be installed',
u'InstallRequiredDisplayText'),
'will-be-removed':
NSLocalizedString(u'Will be removed',
u'WillBeRemovedDisplayText'),
'removing':
NSLocalizedString(u'Removing',
u'RemovingDisplayText'),
'update-will-be-installed':
NSLocalizedString(u'Update will be installed',
u'UpdateWillBeInstalledDisplayText'),
'update-must-be-installed':
NSLocalizedString(u'Update will be installed',
map = {
'install-error':
NSLocalizedString(u'Installation Error',
u'InstallErrorDisplayText'),
'removal-error':
NSLocalizedString(u'Removal Error',
u'RemovalErrorDisplayText'),
'installed':
NSLocalizedString(u'Installed',
u'InstalledDisplayText'),
'installing':
NSLocalizedString(u'Installing',
u'InstallingDisplayText'),
'installed-not-removable':
NSLocalizedString(u'Installed',
u'InstalledDisplayText'),
'not-installed':
NSLocalizedString(u'Not installed',
u'NotInstalledDisplayText'),
'install-requested':
NSLocalizedString(u'Install requested',
u'InstallRequestedDisplayText'),
'downloading':
NSLocalizedString(u'Downloading',
u'DownloadingDisplayText'),
'will-be-installed':
NSLocalizedString(u'Will be installed',
u'WillBeInstalledDisplayText'),
'must-be-installed':
NSLocalizedString(u'Will be installed',
u'InstallRequiredDisplayText'),
'removal-requested':
NSLocalizedString(u'Removal requested',
u'WillBeRemovedDisplayText'),
'preparing-removal':
NSLocalizedString(u'Preparing removal',
u'PreparingRemovalDisplayText'),
'will-be-removed':
NSLocalizedString(u'Will be removed',
u'WillBeRemovedDisplayText'),
'removing':
NSLocalizedString(u'Removing',
u'RemovingDisplayText'),
'update-will-be-installed':
NSLocalizedString(u'Update will be installed',
u'UpdateWillBeInstalledDisplayText'),
'update-must-be-installed':
NSLocalizedString(u'Update will be installed',
u'UpdateRequiredDisplayText'),
'update-available':
NSLocalizedString(u'Update available',
u'UpdateAvailableDisplayText'),
'unavailable':
NSLocalizedString(u'Unavailable',
u'UnavailableDisplayText'),
}
'update-available':
NSLocalizedString(u'Update available',
u'UpdateAvailableDisplayText'),
'unavailable':
NSLocalizedString(u'Unavailable',
u'UnavailableDisplayText'),
}
return map.get(self['status'], self['status'])
def short_action_text(self):
'''Return localized 'short' action text for button'''
map = { 'installed':
NSLocalizedString(u'Remove',
u'RemoveShortActionText'),
'installing':
NSLocalizedString(u'Installing',
u'InstallingShortActionText'),
'installed-not-removable':
NSLocalizedString(u'Installed',
u'InstalledShortActionText'),
'not-installed':
NSLocalizedString(u'Install',
u'InstallShortActionText'),
'will-be-installed':
NSLocalizedString(u'Cancel',
u'CancelInstallShortActionText'),
'must-be-installed':
NSLocalizedString(u'Required',
u'InstallRequiredShortActionText'),
'will-be-removed':
NSLocalizedString(u'Cancel',
u'CancelRemovalShortActionText'),
'removing':
NSLocalizedString(u'Removing',
u'RemovingShortActionText'),
'update-will-be-installed':
NSLocalizedString(u'Cancel',
u'CancelUpdateShortActionText'),
'update-must-be-installed':
NSLocalizedString(u'Required',
u'UpdateRequiredShortActionText'),
'update-available':
NSLocalizedString(u'Update',
u'UpdateShortActionText'),
'unavailable':
NSLocalizedString(u'Unavailable',
u'UnavailableShortActionText'),
map = {
'install-error':
NSLocalizedString(u'Cancel',
u'CancelInstallShortActionText'),
'removal-error':
NSLocalizedString(u'Cancel',
u'CancelRemovalShortActionText'),
'installed':
NSLocalizedString(u'Remove',
u'RemoveShortActionText'),
'installing':
NSLocalizedString(u'Installing',
u'InstallingShortActionText'),
'installed-not-removable':
NSLocalizedString(u'Installed',
u'InstalledShortActionText'),
'not-installed':
NSLocalizedString(u'Install',
u'InstallShortActionText'),
'install-requested':
NSLocalizedString(u'Cancel',
u'CancelInstallShortActionText'),
'downloading':
NSLocalizedString(u'Cancel',
u'CancelInstallShortActionText'),
'will-be-installed':
NSLocalizedString(u'Cancel',
u'CancelInstallShortActionText'),
'must-be-installed':
NSLocalizedString(u'Required',
u'InstallRequiredShortActionText'),
'removal-requested':
NSLocalizedString(u'Cancel',
u'CancelRemovalShortActionText'),
'preparing-removal':
NSLocalizedString(u'Cancel',
u'CancelInstallShortActionText'),
'will-be-removed':
NSLocalizedString(u'Cancel',
u'CancelRemovalShortActionText'),
'removing':
NSLocalizedString(u'Removing',
u'RemovingShortActionText'),
'update-will-be-installed':
NSLocalizedString(u'Cancel',
u'CancelUpdateShortActionText'),
'update-must-be-installed':
NSLocalizedString(u'Required',
u'UpdateRequiredShortActionText'),
'update-available':
NSLocalizedString(u'Update',
u'UpdateShortActionText'),
'unavailable':
NSLocalizedString(u'Unavailable',
u'UnavailableShortActionText'),
}
return map.get(self['status'], self['status'])
def long_action_text(self):
'''Return localized 'long' action text for button'''
map = {'installed':
NSLocalizedString(u'Remove',
u'RemoveLongActionText'),
'installing':
NSLocalizedString(u'Installing',
u'InstallingLongActionText'),
'installed-not-removable':
NSLocalizedString(u'Installed',
u'InstalledLongActionText'),
'not-installed':
NSLocalizedString(u'Install',
u'InstallLongActionText'),
'will-be-installed':
NSLocalizedString(u'Cancel install',
u'CancelInstallLongActionText'),
'must-be-installed':
NSLocalizedString(u'Install Required',
u'InstallRequiredLongActionText'),
'will-be-removed':
NSLocalizedString(u'Cancel removal',
u'CancelRemovalLongActionText'),
'removing':
NSLocalizedString(u'Removing',
u'RemovingLongActionText'),
'update-will-be-installed':
NSLocalizedString(u'Cancel update',
u'CancelUpdateLongActionText'),
'update-must-be-installed':
NSLocalizedString(u'Update Required',
u'UpdateRequiresLongActionText'),
'update-available':
NSLocalizedString(u'Update',
u'UpdateLongActionText'),
'unavailable':
NSLocalizedString(u'Currently Unavailable',
u'UnavailableShortActionText'),
map = {
'install-error':
NSLocalizedString(u'Cancel install',
u'CancelInstallLongActionText'),
'removal-error':
NSLocalizedString(u'Cancel removal',
u'CancelRemovalLongActionText'),
'installed':
NSLocalizedString(u'Remove',
u'RemoveLongActionText'),
'installing':
NSLocalizedString(u'Installing',
u'InstallingLongActionText'),
'installed-not-removable':
NSLocalizedString(u'Installed',
u'InstalledLongActionText'),
'not-installed':
NSLocalizedString(u'Install',
u'InstallLongActionText'),
'install-requested':
NSLocalizedString(u'Cancel install',
u'CancelInstallLongActionText'),
'downloading':
NSLocalizedString(u'Cancel install',
u'CancelInstallLongActionText'),
'will-be-installed':
NSLocalizedString(u'Cancel install',
u'CancelInstallLongActionText'),
'must-be-installed':
NSLocalizedString(u'Install Required',
u'InstallRequiredLongActionText'),
'removal-requested':
NSLocalizedString(u'Cancel removal',
u'CancelRemovalLongActionText'),
'preparing-removal':
NSLocalizedString(u'Cancel removal',
u'CancelRemovalLongActionText'),
'will-be-removed':
NSLocalizedString(u'Cancel removal',
u'CancelRemovalLongActionText'),
'removing':
NSLocalizedString(u'Removing',
u'RemovingLongActionText'),
'update-will-be-installed':
NSLocalizedString(u'Cancel update',
u'CancelUpdateLongActionText'),
'update-must-be-installed':
NSLocalizedString(u'Update Required',
u'UpdateRequiresLongActionText'),
'update-available':
NSLocalizedString(u'Update',
u'UpdateLongActionText'),
'unavailable':
NSLocalizedString(u'Currently Unavailable',
u'UnavailableShortActionText'),
}
return map.get(self['status'], self['status'])
def myitem_action_text(self):
'''Return localized 'My Items' action text for button'''
map = { 'installed':
NSLocalizedString(u'Remove',
u'RemoveLongActionText'),
'installing':
NSLocalizedString(u'Installing',
u'InstallingLongActionText'),
'installed-not-removable':
NSLocalizedString(u'Installed',
u'InstalledLongActionText'),
'will-be-removed':
NSLocalizedString(u'Cancel removal',
u'CancelRemovalLongActionText'),
'removing':
NSLocalizedString(u'Removing',
u'RemovingLongActionText'),
'update-available':
NSLocalizedString(u'Update',
u'UpdateLongActionText'),
'update-will-be-installed':
NSLocalizedString(u'Remove',
u'RemoveLongActionText'),
'update-must-be-installed':
NSLocalizedString(u'Update Required',
u'UpdateRequiredLongActionText'),
'will-be-installed':
NSLocalizedString(u'Cancel install',
u'CancelInstallLongActionText'),
'must-be-installed':
NSLocalizedString(u'Required',
u'InstallRequiredLongActionText'),
map = {
'install-error':
NSLocalizedString(u'Cancel install',
u'CancelInstallLongActionText'),
'removal-error':
NSLocalizedString(u'Cancel removal',
u'CancelRemovalLongActionText'),
'installed':
NSLocalizedString(u'Remove',
u'RemoveLongActionText'),
'installing':
NSLocalizedString(u'Installing',
u'InstallingLongActionText'),
'installed-not-removable':
NSLocalizedString(u'Installed',
u'InstalledLongActionText'),
'removal-requested':
NSLocalizedString(u'Cancel removal',
u'CancelRemovalLongActionText'),
'preparing-removal':
NSLocalizedString(u'Cancel removal',
u'CancelRemovalLongActionText'),
'will-be-removed':
NSLocalizedString(u'Cancel removal',
u'CancelRemovalLongActionText'),
'removing':
NSLocalizedString(u'Removing',
u'RemovingLongActionText'),
'update-available':
NSLocalizedString(u'Update',
u'UpdateLongActionText'),
'update-will-be-installed':
NSLocalizedString(u'Remove',
u'RemoveLongActionText'),
'update-must-be-installed':
NSLocalizedString(u'Update Required',
u'UpdateRequiredLongActionText'),
'install-requested':
NSLocalizedString(u'Cancel install',
u'CancelInstallLongActionText'),
'downloading':
NSLocalizedString(u'Cancel install',
u'CancelInstallLongActionText'),
'will-be-installed':
NSLocalizedString(u'Cancel install',
u'CancelInstallLongActionText'),
'must-be-installed':
NSLocalizedString(u'Required',
u'InstallRequiredLongActionText'),
}
return map.get(self['status'], self['status'])
@@ -728,13 +803,17 @@ class OptionalItem(GenericItem):
self_service_installs = SelfService().installs()
self_service_uninstalls = SelfService().uninstalls()
if self.get('installed'):
if self['name'] in self_service_uninstalls:
if self.get('removal_error'):
status = u'removal-error'
elif self.get('will_be_removed'):
status = u'will-be-removed'
elif self['dependent_items']:
status = u'installed-not-removable'
elif self['name'] in self_service_uninstalls:
status = u'removal-requested'
else: # not in managed_uninstalls
if not self.get('needs_update'):
if self['dependent_items']:
status = u'installed-not-removable'
elif self.get('uninstallable'):
if self.get('uninstallable'):
status = u'installed'
else: # not uninstallable
status = u'installed-not-removable'
@@ -748,7 +827,9 @@ class OptionalItem(GenericItem):
else: # not in managed_installs
status = u'update-available'
else: # not installed
if self.get('note'):
if self.get('install_error'):
status = u'install-error'
elif self.get('note'):
# TO-DO: handle this case better
# some reason we can't install
# usually not enough disk space
@@ -765,8 +846,10 @@ class OptionalItem(GenericItem):
status = u'unavailable'
elif self['dependent_items']:
status = u'must-be-installed'
elif self['name'] in self_service_installs:
elif self.get('will_be_installed'):
status = u'will-be-installed'
elif self['name'] in self_service_installs:
status = u'install-requested'
else: # not in managed_installs
status = u'not-installed'
return status
@@ -775,6 +858,20 @@ class OptionalItem(GenericItem):
'''return a full description for the item, inserting dynamic data
if needed'''
_description = self['raw_description']
if self.get('install_error'):
_description = NSLocalizedString(
u'<span class="warning">An installation attempt failed. '
'Installation will be attempted again.<br/>'
'If this situation continues, contact your systems administrator.'
'</span><br/><br/>',
u'InstallErrorMessage') + _description
elif self.get('removal_error'):
_description = NSLocalizedString(
u'<span class="warning">A removal attempt failed. '
'Removal will be attempted again.<br/>'
'If this situation continues, contact your systems administrator.'
'</span><br/><br/>',
u'RemovalErrorMessage') + _description
if self.get('dependent_items'):
# append dependency info to description:
_description += self.dependency_description()
@@ -785,13 +882,14 @@ class OptionalItem(GenericItem):
managed_update_names = getInstallInfo().get('managed_updates', [])
if self['status'] == 'update-available':
# mark the update for install
self['status'] = u'update-will-be-installed'
self['status'] = u'install-requested'
subscribe(self)
elif self['status'] == 'update-will-be-installed':
# cancel the update
self['status'] = u'update-available'
unmanage(self)
elif self['status'] == 'will-be-removed':
elif self['status'] in ['will-be-removed', 'removal-requested',
'preparing-removal', 'removal-error']:
if self['name'] in managed_update_names:
# update is managed, so user can't opt out
self['status'] = u'installed'
@@ -802,17 +900,21 @@ class OptionalItem(GenericItem):
# item is simply installed
self['status'] = u'installed'
unmanage(self)
elif self['status'] == 'will-be-installed':
elif self['status'] in ['will-be-installed', 'install-requested',
'downloading', 'install-error']:
# cancel install
self['status'] = u'not-installed'
if self.get('needs_update'):
self['status'] = u'update-available'
else:
self['status'] = u'not-installed'
unmanage(self)
elif self['status'] == 'not-installed':
# mark for install
self['status'] = u'will-be-installed'
self['status'] = u'install-requested'
subscribe(self)
elif self['status'] == 'installed':
# mark for removal
self['status'] = u'will-be-removed'
self['status'] = u'removal-requested'
unsubscribe(self)
@@ -804,6 +804,7 @@ div.lockup-container[data-columns-current="5"] div.lockup:nth-last-of-type(5) {
}
div.lockup {
position: relative;
padding: 10px
}
@@ -1281,13 +1282,58 @@ div.title div.select {
color: #CC0000 !important;
}
div.lockup li.will-be-installed, div.lockup li.will-be-removed,
div.lockup li.update-will-be-installed, div.lockup li.update-available,
span.will-be-installed, span.will-be-removed,
span.update-will-be-installed, span.update-available {
div.lockup li.will-be-installed,
div.lockup li.will-be-removed,
div.lockup li.install-requested,
div.lockup li.removal-requested,
div.lockup li.downloading,
div.lockup li.preparing-removal,
div.lockup li.update-will-be-installed,
div.lockup li.update-available,
div.lockup li.install-error,
div.lockup li.removal-error,
span.install-requested,
span.removal-requested,
span.downloading,
span.preparing-removal,
span.install-error,
span.removal-error,
span.will-be-installed,
span.will-be-removed,
span.update-will-be-installed,
span.update-available {
color: #CC0000 !important;
}
span.downloading:after,
span.preparing-removal:after,
span.installing:after,
span.removing:after,
div.lockup li.downloading:after,
div.lockup li.preparing-removal:after,
div.lockup li.installing:after,
div.lockup li.removing:after {
visibility: visible;
float: right;
content: ' ';
background: url(progress-spinner.png) 0 0 no-repeat;
-webkit-background-size: 21px 20px;
opacity: 1;
width: 21px;
height: 20px;
-webkit-animation-name: 'generic-loading-spinner-animation';
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
-webkit-animation-duration: 1s;
}
div.lockup li.downloading:after, div.lockup li.preparing-removal:after,
div.lockup li.installing:after, div.lockup li.removing:after {
position: absolute;
bottom: 2px;
right: 2px;
}
div.lockup li {
max-width: 190px;
}
@@ -1,150 +1,152 @@
/*
** updates.css for Managed Software Center.app
** Based on App Store CSS © 2013 Apple Inc.
*/
** updates.css for Managed Software Center.app
** Based on App Store CSS © 2013 Apple Inc.
*/
#page,#wrapper,#content {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-flex: 1;
-webkit-box-align: stretch
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-flex: 1;
-webkit-box-align: stretch
}
#content {
padding-top: 20px
padding-top: 20px
}
.main {
-webkit-box-flex: 0
-webkit-box-flex: 0
}
.hidden {
display: none!important;
opacity: 0
display: none!important;
opacity: 0
}
.main>div:not(:last-of-type) {
margin-bottom: 20px
margin-bottom: 20px
}
div.installations {
margin: 14px 0;
position: relative
margin: 14px 0;
position: relative
}
div.installations table {
width: 100%
width: 100%
}
div.installations table:not(.no-header):before {
border-width: 0 0 3px 0;
content: '';
display: block;
left: 0;
position: absolute;
top: 32px;
width: 100%;
z-index: 1
border-width: 0 0 3px 0;
content: '';
display: block;
left: 0;
position: absolute;
top: 32px;
width: 100%;
z-index: 1
}
div.installations thead {
border-radius: 5px 5px 0 0;
padding: 0 0 0 10px;
height: 32px;
border-bottom: 1px solid #d7d7d7;
text-shadow: 0 1px 0 #fff;
background: -webkit-gradient(linear,left bottom,left top,from( #e3e3e3),color-stop(0.49, #ededed),color-stop(0.5, #f4f4f4),to(#fff))
border-radius: 5px 5px 0 0;
padding: 0 0 0 10px;
height: 32px;
border-bottom: 1px solid #d7d7d7;
text-shadow: 0 1px 0 #fff;
background: -webkit-gradient(linear,left bottom,left top,from( #e3e3e3),color-stop(0.49, #ededed),color-stop(0.5, #f4f4f4),to(#fff))
}
div.installations th {
color: #53565e;
font-size: 14px;
font-weight: bold;
height: 32px;
line-height: 14px;
padding: 0 10px;
text-align: left
color: #53565e;
font-size: 14px;
font-weight: bold;
height: 32px;
line-height: 14px;
padding: 0 10px;
text-align: left
}
#updates div.installations th {
width: 100%;
display: block;
line-height: 32px
width: 100%;
display: block;
line-height: 32px
}
div.installations th:first-child {
-webkit-border-top-left-radius: 5px
-webkit-border-top-left-radius: 5px
}
div.installations th:last-child {
-webkit-border-top-right-radius: 5px
-webkit-border-top-right-radius: 5px
}
div.installations tbody tr:nth-of-type(even) {
background: -webkit-gradient(linear,left top,left bottom,from( #f3f3f3),to( #eaeaea))
background: -webkit-gradient(linear,left top,left bottom,from( #f3f3f3),to( #eaeaea))
}
.installation td {
border-top: 1px solid #fff;
border-bottom: 1px solid #d9d9d9;
padding: 0 10px
border-top: 1px solid #fff;
border-bottom: 1px solid #d9d9d9;
padding: 0 10px
}
.installation td,.installation td a {
color: #494949
color: #494949
}
.installation:first-child td {
border-top: transparent
border-top: transparent
}
.installation div.artwork {
position: absolute;
margin-top: 2px
position: absolute;
margin-top: 2px
}
.installation img.artwork {
height: auto;
max-width: 35px;
max-height: 35px;
margin: 0 15px 0 5px;
width: 35px
height: auto;
max-width: 35px;
max-height: 35px;
margin: 0 15px 0 5px;
width: 35px
}
.installation h2 {
color: #565656;
font-size: 13px;
font-weight: bold;
overflow: hidden;
text-overflow: ellipsis;
max-width: 300px
color: #565656;
font-size: 13px;
font-weight: bold;
overflow: hidden;
text-overflow: ellipsis;
max-width: 300px
}
.installation h2 a {
color: inherit
}
.installation .status {
text-align: right;
font-size: 10px;
line-height: 13px
color: inherit
}
/*
.installation .status {
text-align: right;
font-size: 10px;
line-height: 13px
}
*/
.installation .error {
color: #f00
color: #f00
}
.installation .status span {
display: block
display: block
}
.installation .install-button:not(button),.installation .install-button button,.adoption .install-button,.adoption #os-and-app-updates, {
overflow: hidden;
-webkit-border-radius: 5px
overflow: hidden;
-webkit-border-radius: 5px
}
#other-updates #os-updates,#os-and-app-updates #os-updates,
#os-and-app-updates #app-updates, div.subscribed-items {
margin: 0;
-webkit-border-radius: 0;
-webkit-box-shadow: none
margin: 0;
-webkit-border-radius: 0;
-webkit-box-shadow: none
}
#other-updates #os-updates,#os-and-app-updates #os-updates {
border-bottom: 1px solid #d9d9d9
border-bottom: 1px solid #d9d9d9
}
#other-updates #os-updatesth:last-child,#os-and-app-updates #os-updates th:last-child {
width: 100%;
display: block;
padding-right: 0
width: 100%;
display: block;
padding-right: 0
}
#os-and-app-updates th .scan-progress {
position: relative;
top: -1px
position: relative;
top: -1px
}
#os-and-app-updates th .scan-progress .activity-indicator {
top: 6px;
left: 0
top: 6px;
left: 0
}
#os-and-app-updates #os-updates:not(.hidden)+#app-updates tbody tr:nth-of-type(even) {
background: 0
background: 0
}
#os-and-app-updates #os-updates:not(.hidden)+#app-updates tbody tr:nth-of-type(odd) {
background: -webkit-gradient(linear,left top,left bottom,from( #f3f3f3),to( #eaeaea))
background: -webkit-gradient(linear,left top,left bottom,from( #f3f3f3),to( #eaeaea))
}
@-webkit-keyframes fade-out-and-remove {
0% {opacity: 1}
@@ -197,378 +199,323 @@ div.purchases .installation:hover button.hide {
pointer-events:auto
}
div.purchases .installation td.status span {
visibility: hidden;
line-height: 41px;
}
div.purchases .installation td.status span.downloading:after,
div.purchases .installation td.status span.preparing-removal:after,
div.purchases .installation td.status span.installing:after,
div.purchases .installation td.status span.removing:after {
margin-top: 11px;
}
div.purchases .installation td.status span.will-be-removed,
div.purchases .installation td.status span.will-be-installed
div.purchases .installation td.status span.will-be-installed,
div.purchases .installation td.status span.downloading,
div.purchases .installation td.status span.preparing-removal
{
visibility: visible;
}
#updates {
position: relative
position: relative
}
#updates :focus {
-webkit-focus-ring-color: transparent;
outline: 0
-webkit-focus-ring-color: transparent;
outline: 0
}
#updates #header {
margin-bottom: 15px;
padding: 40px 0 40px 0
margin-bottom: 15px;
padding: 40px 0 40px 0
}
#updates #header h1 {
font-size: 15px;
font-weight: bold;
color: #929292;
text-shadow: 0 1px 0 #fff;
text-align: center
font-size: 15px;
font-weight: bold;
color: #929292;
text-shadow: 0 1px 0 #fff;
text-align: center
}
div.updates table {
-webkit-box-orient: vertical;
display: -webkit-box
-webkit-box-orient: vertical;
display: -webkit-box
}
div.updates thead,div.updates tbody {
-webkit-box-orient: vertical;
display: -webkit-box;
width: 100%
-webkit-box-orient: vertical;
display: -webkit-box;
width: 100%
}
div.updates tr {
display: -webkit-box;
width: 100%
display: -webkit-box;
width: 100%
}
div.updates td {
display: block
display: block
}
div.updates .installation td {
padding-bottom: 15px;
padding-top: 15px;
white-space: nowrap
padding-bottom: 15px;
padding-top: 15px;
white-space: nowrap
}
div.updates .installation td:nth-child(1) {
width: 360px
width: 360px
}
div.updates .installation td:nth-child(2) {
-webkit-box-flex: 1;
white-space: normal
-webkit-box-flex: 1;
white-space: normal
}
div.updates .installation td:nth-child(3) {
vertical-align: bottom;
color: blue
vertical-align: bottom;
color: blue
}
div.updates .installation img.artwork {
min-width: 35px
min-width: 35px
}
div.updates .installation ul.info {
color: #62676C;
font-size: 11px;
line-height: 16px;
margin-left: 55px
color: #62676C;
font-size: 11px;
line-height: 16px;
margin-left: 55px
}
div.updates .installation ul.info h2 {
color: #565656;
margin-bottom: 1px
color: #565656;
margin-bottom: 1px
}
div.updates .installation ul.info li:nth-child(n+2) {
overflow: hidden;
text-overflow: ellipsis
overflow: hidden;
text-overflow: ellipsis
}
div.updates .installation p.description {
line-height: 16px;
padding-top: 1px
line-height: 16px;
padding-top: 1px
}
div.updates .installation .more-link {
display: block
display: block
}
div.updates .installation span.update {
-webkit-box-orient: horizontal;
-webkit-box-align: center;
display: -webkit-inline-box;
vertical-align: top;
min-height: 20px
-webkit-box-orient: horizontal;
-webkit-box-align: center;
display: -webkit-inline-box;
vertical-align: top;
min-height: 20px
}
div.updates .installation .status {
display: block;
margin-right: 10px
display: block;
margin-right: 10px
}
div.updates .all-os-updates td {
white-space: normal
white-space: normal
}
@media only screen and (-webkit-min-device-pixel-ratio: 2) {div.artwork .critical-updates-icon {
background: url(da-storefront/images/-dsi-/critical-updates-icon.2x.png);
background-size: 35px 35px;
background-repeat: no-repeat
background: url(da-storefront/images/-dsi-/critical-updates-icon.2x.png);
background-size: 35px 35px;
background-repeat: no-repeat
}
}
/*body:not(.show-incompatible) .installations.incompatible,body:not(.show-incompatible) .installation.incompatible {
display: none!important
}
div.os-updates .os-update-action {
color: rgba(0,0,0,.33)
}
div.os-updates .info .os-update-action {
padding-right: 15px;
background: url(da-storefront/images/restart_required.png) 100% 1px no-repeat;
background-size: 11px 11px
}*/
div.updates.os-updates .installation td:last-child {
-webkit-box-pack: center;
padding-top: 15px
-webkit-box-pack: center;
padding-top: 15px
}
div.updates.os-updates .installation span.update {
-webkit-box-align: center
-webkit-box-align: center
}
/*div.os-updates .installation .progress {
width: 200px
}*/
div.os-updates .installation h2 {
max-width: 100%;
margin-bottom: 5px
max-width: 100%;
margin-bottom: 5px
}
div.os-updates .installation td {
border: 0
border: 0
}
div.updates.os-updates .sub-installation td:last-child {
-webkit-box-pack: start
-webkit-box-pack: start
}
div.os-updates td.description p+p {
margin-top: 10px
margin-top: 10px
}
div.os-updates p.note {
color: #d30
color: #d30
}
/*div.os-updates p.note+p {
margin-top: 10px
}
div.os-updates p.note a.help-link {
font-weight: bold;
display: inline-block;
height: 16px;
width: 16px;
vertical-align: middle;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAp9JREFUeNrUVzuPElEUnhnG1YFMgKxLxIBRCwM0FDaEau0NLbGk0R+gNUqvoXb/wAZKSyhcG6DZAgpDKEwUDBsIOmTCzgQS8RxzxtyFeTEgG0/yFTNz73znnvflOfciAYIAGRAA3AIc0Lc5QAfMACpgCtDc/JR3sSYEOAIcEqkbQWUmgDFA8aoAnjgKuMOcdFNBy1wAhlYW8VlsDAPukwI+zrv4yG14mAVZxlGB24AHZPpdiZ/iBpW4tFMgTOQyt3u5SZbQWUuIKz6POZHXarWnyWTycSwWO2bfdzqd026328vn82c222Xi0I2YYIPwIeCe1c5CoRAtl8tvgsHgIzsFx+PxeS6XK7VaLdVm2TfAF1YB9HfKLtoVRXnvRG7IdDrthUKhFw7Z8Rl/K9CLIzvyRqPxbJW8Wq2WeJ5/gkDzs99wbaVSObZR4IA4OYF8f2h3olQqdeVn9Xr9HevrdDp9gqdm1yQSCSdrIackUJ5aVrhMJiOvnr5YLH5as+l8fsXnkiQ5ZRJyBkU3KddsNk/YZ7MACwQCd9lnTdNUF+Eii1QgLAXJstnsqd2adrv93O/3R1fi5tyFAgHMgswGTWZN+v3+69WaMBgMzuLxeMlN0xK3aDSmqYl1wCX5n2wQvJKbpSbGSiQSebXJfzy7YLFYfBBFUWbrgkMZNnWBYNYinQTLMkuOZvdA/leB2bZtDmJh6HHrjKeJJ8Fdj3QFGiD1ayBHzqlAfXmyyc5er/dyuVx+NDAajd56UAA5NSMNx9Qi9yVz4uQMBRSaXvclF8a4zhai4aau8CgT4jK9F4RpNJP/EblKo9hPq6lYp9FZoil21+RfAT+c7gWXpMgNmud3ZfY1crubkU4a/yJr+LaI9u80Bav/3eV0L9fz3wIMABtp4f4jVHJPAAAAAElFTkSuQmCC) no-repeat;
background-size: 16px 16px;
overflow: hidden;
margin-left: 3px;
position: relative;
top: -2px
}
div.os-updates .sub-installation td.description p {
font-size: 11px;
line-height: 16px;
margin-left: 1.5em
}
div.os-updates .sub-installation[single-update] td.description p {
margin-left: 0;
position: relative;
top: -10px
}
div.os-updates .sub-installation td.description h2 {
font-weight: normal
}
div.updates.os-updates .sub-installation td {
padding-top: 0
}
div.updates.os-updates .sub-installation[single-update] td {
padding-bottom: 0
}
.all-os-updates .toggle {
float: right;
color: #6D83A2;
display: inline-block;
text-decoration: none;
-webkit-appearance: none;
padding: 0 5px 0 0;
margin: 0;
border: 0;
background: transparent;
font-family: "Lucida Grande",Helvetica,sans-serif
}
.expanded .all-os-updates .toggle,.single-update .all-os-updates .toggle {
display: none
}*/
.installation .description a {
color: #3E6397
color: #3E6397
}
.installation [data-text-truncate-lines] a.text-truncate-toggle {
padding: 0;
line-height: 14px
padding: 0;
line-height: 14px
}
.installation .data-text-truncate-opened a.text-truncate-toggle {
display: none
display: none
}
.all-os-updates .toggle:before,.installation [data-text-truncate-lines] a.text-truncate-toggle:before {
content: "…";
color: currentcolor
content: "…";
color: currentcolor
}
.installation [data-text-truncate-lines] a.text-truncate-toggle:after {
display: none
display: none
}
div.os-updates tbody tr.sub-installation {
display: none;
background: rgba(0,0,0,.02)
display: none;
background: rgba(0,0,0,.02)
}
div.os-updates.single-update tbody tr.sub-installation {
background: transparent
background: transparent
}
.os-updates.expanded tbody tr.sub-installation,.os-updates.single-update tbody tr.sub-installation {
display: -webkit-box;
position: relative
display: -webkit-box;
position: relative
}
.os-updates.expanded tbody tr.sub-installation {
padding-top: 15px
padding-top: 15px
}
.os-updates.expanded tbody tr:nth-child(2) {
border-top: 1px solid rgba(0,0,0,.16)
border-top: 1px solid rgba(0,0,0,.16)
}
div.os-updates.expanded tbody tr:nth-child(n+3):before {
position: absolute;
content: '';
width: auto;
height: 1px;
top: 0;
right: 100px;
left: 240px;
background: -webkit-linear-gradient(left,rgba(0,0,0,.04) 0,rgba(0,0,0,.16) 20%,rgba(0,0,0,.16) 80%,rgba(0,0,0,.04) 100%)
position: absolute;
content: '';
width: auto;
height: 1px;
top: 0;
right: 100px;
left: 240px;
background: -webkit-linear-gradient(left,rgba(0,0,0,.04) 0,rgba(0,0,0,.16) 20%,rgba(0,0,0,.16) 80%,rgba(0,0,0,.04) 100%)
}
div.os-updates.expanded tbody tr.sosumi:before {
display: none
display: none
}
.activity-indicator {
font: bold 20px Helvetica
font: bold 20px Helvetica
}
#updates #header .scan-progress {
padding: 0 0 1px 13px
padding: 0 0 1px 13px
}
#updates #header .scan-progress h1::before {
display: none
display: none
}
#updates #header .scan-progress h1 {
padding-left: 0;
margin-top: -9px
padding-left: 0;
margin-top: -9px
}
.scan-progress.small .activity-indicator {
display: inline-block;
left: -10px;
top: 7px;
position: relative;
margin: 2px 0 1px
display: inline-block;
left: -10px;
top: 7px;
position: relative;
margin: 2px 0 1px
}
body.scanning .non-scan-progress,body.page-not-ready .non-scan-progress,body:not(.scanning):not(.page-not-ready) .scan-progress {
display: none
display: none
}
.activity-indicator {
-webkit-animation-name: rotatingLoader;
-webkit-animation-duration: .75s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear
-webkit-animation-name: rotatingLoader;
-webkit-animation-duration: .75s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear
}
@-webkit-keyframes rotatingLoader {0% {
-webkit-transform: rotate(0deg)
-webkit-transform: rotate(0deg)
}
8.32% {
-webkit-transform: rotate(0deg)
-webkit-transform: rotate(0deg)
}
8.33% {
-webkit-transform: rotate(30deg)
-webkit-transform: rotate(30deg)
}
16.65% {
-webkit-transform: rotate(30deg)
-webkit-transform: rotate(30deg)
}
16.66% {
-webkit-transform: rotate(60deg)
-webkit-transform: rotate(60deg)
}
24.99% {
-webkit-transform: rotate(60deg)
-webkit-transform: rotate(60deg)
}
25% {
-webkit-transform: rotate(90deg)
-webkit-transform: rotate(90deg)
}
33.32% {
-webkit-transform: rotate(90deg)
-webkit-transform: rotate(90deg)
}
33.33% {
-webkit-transform: rotate(120deg)
-webkit-transform: rotate(120deg)
}
41.65% {
-webkit-transform: rotate(120deg)
-webkit-transform: rotate(120deg)
}
41.66% {
-webkit-transform: rotate(150deg)
-webkit-transform: rotate(150deg)
}
49.99% {
-webkit-transform: rotate(150deg)
-webkit-transform: rotate(150deg)
}
50% {
-webkit-transform: rotate(180deg)
-webkit-transform: rotate(180deg)
}
58.32% {
-webkit-transform: rotate(180deg)
-webkit-transform: rotate(180deg)
}
58.33% {
-webkit-transform: rotate(210deg)
-webkit-transform: rotate(210deg)
}
66.65% {
-webkit-transform: rotate(210deg)
-webkit-transform: rotate(210deg)
}
66.66% {
-webkit-transform: rotate(240deg)
-webkit-transform: rotate(240deg)
}
74.99% {
-webkit-transform: rotate(240deg)
-webkit-transform: rotate(240deg)
}
75% {
-webkit-transform: rotate(270deg)
-webkit-transform: rotate(270deg)
}
83.32% {
-webkit-transform: rotate(270deg)
-webkit-transform: rotate(270deg)
}
83.33% {
-webkit-transform: rotate(300deg)
-webkit-transform: rotate(300deg)
}
91.65% {
-webkit-transform: rotate(300deg)
-webkit-transform: rotate(300deg)
}
91.66% {
-webkit-transform: rotate(330deg)
-webkit-transform: rotate(330deg)
}
100% {
-webkit-transform: rotate(330deg)
-webkit-transform: rotate(330deg)
}
}
#os-updates .installation button.cancel-or-add {
width: 19px;
height: 19px;
background: transparent url(delete-button-sprite.png) no-repeat;
-webkit-background-size: 19px 38px;
border: 0;
width: 19px;
height: 19px;
background: transparent url(delete-button-sprite.png) no-repeat;
-webkit-background-size: 19px 38px;
border: 0;
}
#other-updates .installation button.cancel-or-add {
width: 19px;
height: 19px;
background: transparent url(add-button-sprite.png) no-repeat;
-webkit-background-size: 19px 38px;
border: 0;
width: 19px;
height: 19px;
background: transparent url(add-button-sprite.png) no-repeat;
-webkit-background-size: 19px 38px;
border: 0;
}
#os-updates .installation button.cancel-or-add:hover,
#other-updates .installation button.cancel-or-add:hover {
background-position: bottom
background-position: bottom
}
tr.installation.added {
-webkit-animation: fade-in .5s;
-webkit-animation: fade-in .5s;
}
tr.installation.deleted {
-webkit-animation: fade-out-and-remove .5s;
-webkit-animation-fill-mode: forwards;
-webkit-animation: fade-out-and-remove .5s;
-webkit-animation-fill-mode: forwards;
}
@-webkit-keyframes fade-in {
0% {opacity: 0}
@@ -631,13 +578,13 @@ div.progress > span.indeterminate > span {
position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
background-image:
-webkit-gradient(linear, left bottom, right top,
color-stop(.25, rgba(255, 255, 255, .9)),
color-stop(.25, transparent), color-stop(.5, transparent),
color-stop(.5, rgba(255, 255, 255, .9)),
color-stop(.75, rgba(255, 255, 255, .9)),
color-stop(.75, transparent), to(transparent)
);
-webkit-gradient(linear, left bottom, right top,
color-stop(.25, rgba(255, 255, 255, .9)),
color-stop(.25, transparent), color-stop(.5, transparent),
color-stop(.5, rgba(255, 255, 255, .9)),
color-stop(.75, rgba(255, 255, 255, .9)),
color-stop(.75, transparent), to(transparent)
);
z-index: 1;
-webkit-background-size: 20px 20px;
-webkit-animation: barber-pole 2s linear infinite;
@@ -647,10 +594,10 @@ div.progress > span.indeterminate > span {
@-webkit-keyframes barber-pole {
0% {
background-position: 0 20px;
background-position: 0 20px;
}
100% {
background-position: 20px 0;
background-position: 20px 0;
}
}
@@ -285,6 +285,13 @@
<buttonCell type="squareTextured" bezelStyle="texturedSquare" image="AllItemsTemplate" imagePosition="only" alignment="center" state="on" tag="1" imageScaling="proportionallyDown" inset="2" id="769">
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
<connections>
<binding destination="KMl-S0-0FF" name="enabled" keyPath="disableSoftwareViewButtons" id="Izr-47-IE3">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
</connections>
</buttonCell>
</column>
<column>
@@ -310,10 +317,6 @@
<action selector="tabControlClicked:" target="KMl-S0-0FF" id="pKT-yQ-fzz"/>
</connections>
</matrix>
<progressIndicator horizontalHuggingPriority="750" verticalHuggingPriority="750" alphaValue="0.80000000000000004" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" id="oix-r8-qeB">
<rect key="frame" x="279" y="35" width="16" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</progressIndicator>
</subviews>
</customView>
<progressIndicator horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" id="X8X-xa-e0T">
@@ -365,7 +368,6 @@
<outlet property="searchField" destination="557" id="HM7-VX-9cg"/>
<outlet property="tabControl" destination="796" id="AEs-wG-NLb"/>
<outlet property="updateButtonCell" destination="fG2-SY-8g2" id="92m-84-OS5"/>
<outlet property="updateProgressSpinner" destination="oix-r8-qeB" id="jaC-eu-JnL"/>
<outlet property="webView" destination="S5Y-lq-2nB" id="TPJ-jP-F85"/>
<outlet property="window" destination="371" id="JK3-nH-LOl"/>
<outlet property="windowMenuSeperatorItem" destination="92" id="eCt-83-MtH"/>
@@ -13,9 +13,6 @@
<td>${version_to_install}</td>
<td>${size}</td>
<td class="status"><span class="${status}" id="${name}_status_text">${status_text}</span></td>
<td class="hide">
<!--<button class="hide" aria-label="Hide"></button>-->
</td>
<td>
<div class="msu-button install-updates">
<button class="button-area uppercase"