class TimezoneWindow(Singleton): SCREEN_NAME = 'timezone' def _singleton_init(self, controlState, xml): self.timezones = TimezoneList() chosenTZ = userchoices.getTimezone() if chosenTZ: self.selectedTZ = self.timezones.findByCityName(chosenTZ['city']) else: self.selectedTZ = self.timezones.defaultTimezone connectSignalHandlerByDict( self, TimezoneWindow, xml, { ('advanced_ok', 'clicked'): 'onCityListSelect', ('advanced_cancel', 'clicked'): 'onCityListCancel', ('tz_button', 'clicked'): 'onAdvancedClicked', ('TimezoneDrawingArea', 'motion_notify_event'): 'onTZDrawMotion', ('TimezoneDrawingArea', 'button_press_event'): 'onTZDrawPress', ('TimezoneDrawingArea', 'button_release_event'): 'onTZDrawRelease', ('TimezoneDrawingArea', 'expose_event'): 'onTZDrawExpose', }) self.tzEntry = xml.get_widget("tz_entry") self.advancedDialog = AdvancedDialog(xml) self.allTZsView = xml.get_widget("tz_treeview") self.setupMap(xml) self.setupTimezone(xml) def __init__(self, controlState, xml): controlState.displayHeaderBar = 1 controlState.windowIcon = 'timezone.png' controlState.windowTitle = "Time Zone Settings" controlState.windowText = "Select the time zone for ESX" def prepareAdvancedDialog(self): # Note, the Advanced Dialog should be prepared every time before it is # shown, that will set the active city and scroll to it. # prepare the TreeView with its model and columns allTZsModel = gtk.ListStore(str, str, object) self.allTZsView.set_model(allTZsModel) if not self.allTZsView.get_columns(): renderer = gtk.CellRendererText() # zoneColumn gets its text from index 0 in the model zoneColumn = gtk.TreeViewColumn('Zone Name', renderer, text=0) self.allTZsView.append_column(zoneColumn) # zoneColumn gets its text from index 1 in the model offsetColumn = gtk.TreeViewColumn('UTC Offset', renderer, text=1) self.allTZsView.append_column(offsetColumn) self.allTZsView.set_search_column(0) # populate the model, highlight the selected timezone seenZoneNames = [] for index, tz in enumerate(self.timezones.sortedIter()): if tz.zoneName in seenZoneNames: continue # don't show duplicate zone names seenZoneNames.append(tz.zoneName) allTZsModel.append((tz.zoneName, tz.offset, tz)) if self.selectedTZ == tz: # NOTE, this "cursor" is the highlighted row, not the mouse self.allTZsView.set_cursor(index) self.allTZsView.scroll_to_cell(index, None, True, 0.5) def setupTimezone(self, xml): self.tzEntry.set_text(self.selectedTZ.zoneName) def setupMap(self, xml): self.cityHoverLabel = xml.get_widget("hover_label") self.drawingArea = xml.get_widget("TimezoneDrawingArea") bgFileName = os.path.join(IMAGE_DIR, 'background.png') self.bgImage = PixbufSprite(bgFileName) bgFileName = os.path.join(IMAGE_DIR, 'map.png') self.mapImage = PixbufSprite(bgFileName) self.sprites = [ self.bgImage, self.mapImage, ] #note: order is important. it is the drawing order self.listeners = [] #note: order is important. (event handling order) def placeCityDot(tz, clickCB, offsetSlice, isSelected): if not tz.cityPos: return #this city doesn't have a place on the map city = City(tz.city, tz.cityPos) city.clickCB = clickCB offsetSlice.cityGroup.cities.append(city) if isSelected: offsetSlice.selected = True city.selected = True offsetSlicesByName = {} secondPass = [] for tzList in self.timezones.groupByOffsets().values(): offsetName = tzList[0].offset offsetPos = tzList[0].offsetPos if not offsetPos: secondPass += [tz for tz in tzList if tz.showAsMapOffset] continue #this offset doesn't have a place on the map offsetSlice = OffsetSlice(offsetName, offsetPos) offsetSlice.cityGroup = CityGroup(self) offsetSlice.clickCB = self.onOffsetSliceClick offsetSlicesByName[offsetName] = offsetSlice for tz in tzList: isSelected = (tz == self.selectedTZ) placeCityDot(tz, self.onCityClick, offsetSlice, isSelected) self.sprites.append(offsetSlice) self.listeners.append(offsetSlice) # Second pass: collect those cities important enough to be on # the map, but whose offset is weird, so we put them in the slice # that best approximates it. for tz in secondPass: offsetSlice = offsetSlicesByName.get(tz.showAsMapOffset, None) if not offsetSlice: log.warn( 'Offset %s not on the map. TZ %s could not be drawn' % (tz.showAsMapOffset, tz.zoneName)) continue isSelected = (tz == self.selectedTZ) placeCityDot(tz, self.onCityClick, offsetSlice, isSelected) def notifyCityHovered(self, city): self.cityHoverLabel.set_text(city.cityName) def drawAll(self): for sprite in self.sprites: sprite.DrawOn(self.drawWindow) def onCityListCancel(self, widget, *args): self.advancedDialog.hide() def onAdvancedClicked(self, *args): self.prepareAdvancedDialog() self.advancedDialog.show() def onCityListSelect(self, widget, *args): model, treeIter = self.allTZsView.get_selection().get_selected() if not treeIter: MessageWindow(None, "Invalid Time Zone", "You must select a time zone.") return zoneName, offset, tz = model[treeIter] self.selectedTZ = tz log.debug('TZ %s chosen from list.' % str(tz)) self.tzEntry.set_text(tz.zoneName) for sprite in self.sprites: if not isinstance(sprite, OffsetSlice): continue if sprite.offsetName in [tz.offset, tz.showAsMapOffset]: sprite.selected = True else: sprite.selected = False for citySprite in sprite.cityGroup.cities: if citySprite.cityName == tz.city: citySprite.selected = True else: citySprite.selected = False self.advancedDialog.hide() self.drawingArea.queue_draw() def onOffsetSliceClick(self, offsetSlice): # current behaviour is for the city closest to the mouse to be # always highlighted, so an offset will never get a click by # itself. Therefore, pass. return def onCityClick(self, city): log.debug('onCityClick %s' % (city.cityName)) tz = self.timezones.findByCityName(city.cityName) self.tzEntry.set_text(tz.zoneName) self.selectedTZ = tz def onTZDrawExpose(self, *args): self.drawWindow = self.drawingArea.window assert self.drawWindow self.drawAll() #return False def onTZDrawMotion(self, widget, event): self.cityHoverLabel.set_text('') for listener in self.listeners: if hasattr(listener, 'HandleMotion'): listener.HandleMotion((event.x, event.y)) self.drawingArea.queue_draw() def onTZDrawPress(self, widget, event): log.debug('Press at %d %d' % (event.x, event.y)) for listener in self.listeners: if hasattr(listener, 'HandlePress'): listener.HandlePress((event.x, event.y)) self.drawingArea.queue_draw() def onTZDrawRelease(self, widget, event): log.debug('Release at %d %d' % (event.x, event.y)) for listener in self.listeners: if hasattr(listener, 'HandleRelease'): listener.HandleRelease((event.x, event.y)) self.drawingArea.queue_draw() def getNext(self): tz = self.selectedTZ userchoices.setTimezone(tz.zoneName, tz.offset, tz.city) tz.runtimeAction()
class TimezoneWindow(Singleton): SCREEN_NAME = 'timezone' def _singleton_init(self, controlState, xml): self.timezones = TimezoneList() chosenTZ = userchoices.getTimezone() if chosenTZ: self.selectedTZ = self.timezones.findByCityName( chosenTZ['city'] ) else: self.selectedTZ = self.timezones.defaultTimezone connectSignalHandlerByDict(self, TimezoneWindow, xml, { ('advanced_ok', 'clicked'): 'onCityListSelect', ('advanced_cancel', 'clicked'): 'onCityListCancel', ('tz_button', 'clicked'): 'onAdvancedClicked', ('TimezoneDrawingArea', 'motion_notify_event'): 'onTZDrawMotion', ('TimezoneDrawingArea', 'button_press_event'): 'onTZDrawPress', ('TimezoneDrawingArea', 'button_release_event'): 'onTZDrawRelease', ('TimezoneDrawingArea', 'expose_event'): 'onTZDrawExpose', }) self.tzEntry = xml.get_widget("tz_entry") self.advancedDialog = AdvancedDialog(xml) self.allTZsView = xml.get_widget("tz_treeview") self.setupMap(xml) self.setupTimezone(xml) def __init__(self, controlState, xml): controlState.displayHeaderBar = 1 controlState.windowIcon = 'timezone.png' controlState.windowTitle = "Time Zone Settings" controlState.windowText = "Select the time zone for ESX" def prepareAdvancedDialog(self): # Note, the Advanced Dialog should be prepared every time before it is # shown, that will set the active city and scroll to it. # prepare the TreeView with its model and columns allTZsModel = gtk.ListStore(str, str, object) self.allTZsView.set_model(allTZsModel) if not self.allTZsView.get_columns(): renderer = gtk.CellRendererText() # zoneColumn gets its text from index 0 in the model zoneColumn = gtk.TreeViewColumn('Zone Name', renderer, text=0) self.allTZsView.append_column(zoneColumn) # zoneColumn gets its text from index 1 in the model offsetColumn = gtk.TreeViewColumn('UTC Offset', renderer, text=1) self.allTZsView.append_column(offsetColumn) self.allTZsView.set_search_column(0) # populate the model, highlight the selected timezone seenZoneNames = [] for index, tz in enumerate(self.timezones.sortedIter()): if tz.zoneName in seenZoneNames: continue # don't show duplicate zone names seenZoneNames.append(tz.zoneName) allTZsModel.append((tz.zoneName, tz.offset, tz)) if self.selectedTZ == tz: # NOTE, this "cursor" is the highlighted row, not the mouse self.allTZsView.set_cursor(index) self.allTZsView.scroll_to_cell(index, None, True, 0.5) def setupTimezone(self, xml): self.tzEntry.set_text(self.selectedTZ.zoneName) def setupMap(self, xml): self.cityHoverLabel = xml.get_widget("hover_label") self.drawingArea = xml.get_widget("TimezoneDrawingArea") bgFileName = os.path.join(IMAGE_DIR, 'background.png') self.bgImage = PixbufSprite(bgFileName) bgFileName = os.path.join(IMAGE_DIR, 'map.png') self.mapImage = PixbufSprite(bgFileName) self.sprites = [ self.bgImage, self.mapImage, ] #note: order is important. it is the drawing order self.listeners= [] #note: order is important. (event handling order) def placeCityDot(tz, clickCB, offsetSlice, isSelected): if not tz.cityPos: return #this city doesn't have a place on the map city = City(tz.city, tz.cityPos) city.clickCB = clickCB offsetSlice.cityGroup.cities.append(city) if isSelected: offsetSlice.selected = True city.selected = True offsetSlicesByName = {} secondPass = [] for tzList in self.timezones.groupByOffsets().values(): offsetName = tzList[0].offset offsetPos = tzList[0].offsetPos if not offsetPos: secondPass += [tz for tz in tzList if tz.showAsMapOffset] continue #this offset doesn't have a place on the map offsetSlice = OffsetSlice(offsetName, offsetPos) offsetSlice.cityGroup = CityGroup(self) offsetSlice.clickCB = self.onOffsetSliceClick offsetSlicesByName[offsetName] = offsetSlice for tz in tzList: isSelected = (tz == self.selectedTZ) placeCityDot(tz, self.onCityClick, offsetSlice, isSelected) self.sprites.append(offsetSlice) self.listeners.append(offsetSlice) # Second pass: collect those cities important enough to be on # the map, but whose offset is weird, so we put them in the slice # that best approximates it. for tz in secondPass: offsetSlice = offsetSlicesByName.get(tz.showAsMapOffset, None) if not offsetSlice: log.warn('Offset %s not on the map. TZ %s could not be drawn' % (tz.showAsMapOffset, tz.zoneName)) continue isSelected = (tz == self.selectedTZ) placeCityDot(tz, self.onCityClick, offsetSlice, isSelected) def notifyCityHovered(self, city): self.cityHoverLabel.set_text( city.cityName ) def drawAll(self): for sprite in self.sprites: sprite.DrawOn( self.drawWindow ) def onCityListCancel(self, widget, *args): self.advancedDialog.hide() def onAdvancedClicked(self, *args): self.prepareAdvancedDialog() self.advancedDialog.show() def onCityListSelect(self, widget, *args): model, treeIter = self.allTZsView.get_selection().get_selected() if not treeIter: MessageWindow(None, "Invalid Time Zone", "You must select a time zone.") return zoneName, offset, tz = model[treeIter] self.selectedTZ = tz log.debug('TZ %s chosen from list.' % str(tz)) self.tzEntry.set_text(tz.zoneName) for sprite in self.sprites: if not isinstance(sprite, OffsetSlice): continue if sprite.offsetName in [tz.offset, tz.showAsMapOffset]: sprite.selected = True else: sprite.selected = False for citySprite in sprite.cityGroup.cities: if citySprite.cityName == tz.city: citySprite.selected = True else: citySprite.selected = False self.advancedDialog.hide() self.drawingArea.queue_draw() def onOffsetSliceClick(self, offsetSlice): # current behaviour is for the city closest to the mouse to be # always highlighted, so an offset will never get a click by # itself. Therefore, pass. return def onCityClick(self, city): log.debug( 'onCityClick %s' % (city.cityName) ) tz = self.timezones.findByCityName( city.cityName ) self.tzEntry.set_text(tz.zoneName) self.selectedTZ = tz def onTZDrawExpose(self, *args): self.drawWindow = self.drawingArea.window assert self.drawWindow self.drawAll() #return False def onTZDrawMotion(self, widget, event): self.cityHoverLabel.set_text('') for listener in self.listeners: if hasattr(listener, 'HandleMotion'): listener.HandleMotion( (event.x, event.y) ) self.drawingArea.queue_draw() def onTZDrawPress(self, widget, event): log.debug( 'Press at %d %d' % (event.x, event.y) ) for listener in self.listeners: if hasattr(listener, 'HandlePress'): listener.HandlePress( (event.x, event.y) ) self.drawingArea.queue_draw() def onTZDrawRelease(self, widget, event): log.debug( 'Release at %d %d' % (event.x, event.y) ) for listener in self.listeners: if hasattr(listener, 'HandleRelease'): listener.HandleRelease( (event.x, event.y) ) self.drawingArea.queue_draw() def getNext(self): tz = self.selectedTZ userchoices.setTimezone(tz.zoneName, tz.offset, tz.city) tz.runtimeAction()
class TimezoneWindow(TextRunner): "Determine timezone of the system." def __init__(self): super(TimezoneWindow, self).__init__() self.substep = self.start self.timezones = TimezoneList() self.userinput = None self.uiTitle = 'Timezone' if not userchoices.getTimezone(): # not set in userchoices # copy default timezone values into userchoices dtz = self.timezones.defaultTimezone userchoices.setTimezone(dtz.zoneName, dtz.offset, dtz.city) dtz.runtimeAction() self.scrollable = None def start(self): self.setSubstepEnv( {'next': self.askConfirm } ) def askConfirm(self): currentTz = userchoices.getTimezone() if currentTz['city']: formattedTz = tzDictStrWithCity % currentTz else: formattedTz = tzDictStrWithoutCity % currentTz ui = { 'title': self.uiTitle, 'body': askConfirmText % formattedTz, 'menu': { '1': self.stepForward, '2': self.showTzList, '<': self.stepBack, '?': self.help, } } self.setSubstepEnv(ui) def help(self): self.pushSubstep() ui = { 'title': self.uiTitle + ' (Help)', 'body': helpText, 'menu': { '*': self.popSubstep } } self.setSubstepEnv(ui) def showTzList(self): scrollable = [] for iName, tz in enumerate(self.timezones.sortedIter()): if tz.city: format = tzEnumStrWithCity else: format = tzEnumStrWithoutCity # use 1-indexed formattedTz = format % (iName+1, tz.offset, tz.zoneName, tz.city) scrollable.append(formattedTz) self.setScrollEnv(scrollable, SCROLL_LIMIT) self.setSubstepEnv( {'next': self.scrollDisplay } ) def scrollDisplay(self): "display timezone choices" self.buildScrollDisplay(self.scrollable, self.uiTitle, self.update, "<number>: keyboard choice", allowStepRestart=True) def update(self): # use skeleton below for exceptions errorSkeleton = { 'title': self.uiTitle + ' (Update)', 'menu': { '*': self.popSubstep }, # add 'body' } # check for numeric input try: selected = int(self.userinput)-1 # revert to 0-indexed except ValueError: # non-integer input tz = None else: sortedTimezones = list(self.timezones.sortedIter()) if 0 <= selected < len(sortedTimezones): tz = sortedTimezones[selected] else: tz = None if not tz: self.pushSubstep() errorSkeleton['body'] = helpText self.setSubstepEnv(errorSkeleton) return # register the choice userchoices.setTimezone(tz.zoneName, tz.offset, tz.city) tz.runtimeAction() log.debug('set tz to %s (%s)' % (tz.zoneName, tz.city)) # choice acepted self.setSubstepEnv( {'next': self.askConfirm } )