- Revision
- 11302
- Author
- stearns
- Date
- 2006-08-01 17:02:53 -0700 (Tue, 01 Aug 2006)
Log Message
Dashboard visual work:
- sectioning on by default, but only on triageStatus.
- Improve appearance of section bars
- Switch to Mimi's new icons in the column header (including the triage status column).
- Bumped schema version due to pref and block changes.
Known problems not addressed by this:
- indexing of the triageStatus attribute is still alphabetic, so the order's still wrong (done, later, now)
- the column header icons are showing up off-center and clipped due to a known column-header bug (bug 6168)
- section expansion/contraction is via double-clicking anywhere in the section row, not clicking on the triangle)
Misc. internal changes, too:
- Sections now managed by a section attribute editor
- Styles.getFont now supports individual parameters, not just CharacterStyle
- There's the beginnings of sectioning un it tests.
Fixes:
Bug 5341 (Friendly section header titles)
Bug 6320 (Triage: Sectioning by triage status, Now, Later, Done)
Bug 6321 (Sectioning: Section formatting)
Bug 6323 (Sectioning: Section titles show # of items in section)
Bug 6324 (Sectioning: Triage sections show block of color, right-aligned, with white outline)
- sectioning on by default, but only on triageStatus.
- Improve appearance of section bars
- Switch to Mimi's new icons in the column header (including the triage status column).
- Bumped schema version due to pref and block changes.
Known problems not addressed by this:
- indexing of the triageStatus attribute is still alphabetic, so the order's still wrong (done, later, now)
- the column header icons are showing up off-center and clipped due to a known column-header bug (bug 6168)
- section expansion/contraction is via double-clicking anywhere in the section row, not clicking on the triangle)
Misc. internal changes, too:
- Sections now managed by a section attribute editor
- Styles.getFont now supports individual parameters, not just CharacterStyle
- There's the beginnings of sectioning un it tests.
Fixes:
Bug 5341 (Friendly section header titles)
Bug 6320 (Triage: Sectioning by triage status, Now, Later, Done)
Bug 6321 (Sectioning: Section formatting)
Bug 6323 (Sectioning: Section titles show # of items in section)
Bug 6324 (Sectioning: Triage sections show block of color, right-aligned, with white outline)
Modified Paths
- trunk/chandler/application/Utility.py
- trunk/chandler/application/styles.conf
- trunk/chandler/parcels/osaf/framework/attributeEditors/AttributeEditors.py
- trunk/chandler/parcels/osaf/framework/blocks/Styles.py
- trunk/chandler/parcels/osaf/framework/blocks/Table.py
- trunk/chandler/parcels/osaf/pim/items.py
- trunk/chandler/parcels/osaf/views/main/Dashboard.py
- trunk/chandler/parcels/osaf/views/main/Sections.py
- trunk/chandler/parcels/osaf/views/main/__init__.py
- trunk/chandler/parcels/osaf/views/main/summaryblocks.py
- trunk/chandler/tools/QATestScripts/Functional/FunctionalTestSuite.py
Added Paths
Diff
Modified: trunk/chandler/application/Utility.py (11301 => 11302)
--- trunk/chandler/application/Utility.py 2006-08-01 22:56:40 UTC (rev 11301) +++ trunk/chandler/application/Utility.py 2006-08-02 00:02:53 UTC (rev 11302) @@ -33,7 +33,7 @@ # with your name (and some helpful text). The comment's really there just to # cause Subversion to warn you of a conflict when you update, in case someone # else changes it at the same time you do (that's why it's on the same line). -SCHEMA_VERSION = "229" #john: Fix bug in renaming "My" Collections to Dashboard +SCHEMA_VERSION = "230" # stearns: dashboard sectioning on by default logger = None # initialized in initLogging()
Modified: trunk/chandler/application/styles.conf (11301 => 11302)
--- trunk/chandler/application/styles.conf 2006-08-01 22:56:40 UTC (rev 11301) +++ trunk/chandler/application/styles.conf 2006-08-02 00:02:53 UTC (rev 11302) @@ -5,3 +5,11 @@ SelectedText = #000000 SelectedTextBackground = #FFFFFF + +[summary] +SectionBackground = #cccccc +SectionLabel = #000000 +SectionCount = #888888 +SectionSample_triageStatus_now = #00cc00 +SectionSample_triageStatus_later = #ffcc00 +SectionSample_triageStatus_done = #555555
Modified: trunk/chandler/parcels/osaf/framework/attributeEditors/AttributeEditors.py (11301 => 11302)
--- trunk/chandler/parcels/osaf/framework/attributeEditors/AttributeEditors.py 2006-08-01 22:56:40 UTC (rev 11301) +++ trunk/chandler/parcels/osaf/framework/attributeEditors/AttributeEditors.py 2006-08-02 00:02:53 UTC (rev 11302) @@ -37,7 +37,7 @@ from PyICU import ICUError, ICUtzinfo, UnicodeString from osaf.framework.blocks.Block import (ShownSynchronizer, wxRectangularChild, debugName) -from osaf.pim.items import ContentItem +from osaf.pim.items import ContentItem, triageStatusNames from application import schema from application.dialogs import RecurrenceDialog, TimeZoneList from util.MultiStateButton import BitmapInfo, MultiStateBitmapCache @@ -2274,9 +2274,7 @@ """ def GetChoices(self): # would be nice if this came directly from the enum - return (_(u"Now"), - _(u"Later"), - _(u"Done")) + return triageStatusNames class IconAttributeEditor (BaseAttributeEditor): """ @@ -2383,7 +2381,7 @@ #operation, mixinKind, debugName(item)) item.StampKind(operation, mixinKind) - def OnMouseChange(self, event, isIn, isDown, (item, attributeName)): + def OnMouseChange(self, event, cell, isIn, isDown, (item, attributeName)): """ Handle live changes of mouse state related to our cell. """
Modified: trunk/chandler/parcels/osaf/framework/blocks/Styles.py (11301 => 11302)
--- trunk/chandler/parcels/osaf/framework/blocks/Styles.py 2006-08-01 22:56:40 UTC (rev 11301) +++ trunk/chandler/parcels/osaf/framework/blocks/Styles.py 2006-08-02 00:02:53 UTC (rev 11302) @@ -88,25 +88,29 @@ fakeSuperscriptSizeScalingFactor = 0.7 -def getFont(characterStyle): +# We default to an 11-point font, which gets scaled by the size of the +# platform's default GUI font (which we measured just above). It's 11 +# because that's the size of the Mac's default GUI font, which is what +# Mimi thinks in terms of. (It's a float so the math works out.) +rawDefaultFontSize = 11.0 + +def getFont(characterStyle=None, family=None, size=rawDefaultFontSize, + style=wx.NORMAL, underline=False, weight=wx.NORMAL, name=""): + """ + Retrieve a font, using a CharacterStyle item or discrete specifications. + Scales the requested point size relative to the idealized 11-point size + on Mac that Mimi bases her specs on. + """ # First time, get a couple of defaults global platformDefaultFaceName, platformDefaultFamily, platformSizeScalingFactor if platformDefaultFaceName is None: defaultGuiFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) platformDefaultFaceName = defaultGuiFont.GetFaceName() platformDefaultFamily = defaultGuiFont.GetFamily() - platformSizeScalingFactor = defaultGuiFont.GetPointSize() / 11.0 - - # We default to an 11-point font, which gets scaled by the size of the - # platform's default GUI font (which we measured just above). It's 11 - # because that's the size of the Mac's default GUI font, which is what - # Mimi thinks in terms of. - family = platformDefaultFamily - size = 11 - style = wx.NORMAL - underline = False - weight = wx.NORMAL - name = "" + platformSizeScalingFactor = \ + defaultGuiFont.GetPointSize() / rawDefaultFontSize + + family = family or platformDefaultFamily if characterStyle is not None: size = characterStyle.fontSize
Modified: trunk/chandler/parcels/osaf/framework/blocks/Table.py (11301 => 11302)
--- trunk/chandler/parcels/osaf/framework/blocks/Table.py 2006-08-01 22:56:40 UTC (rev 11301) +++ trunk/chandler/parcels/osaf/framework/blocks/Table.py 2006-08-02 00:02:53 UTC (rev 11302) @@ -72,7 +72,7 @@ # it from disk bitmap = getattr(self, '_colbitmap%s' % column, None) if bitmap is None: - bitmap = wx.GetApp().GetImage(columnItem.icon + "Stamped.png") + bitmap = wx.GetApp().GetImage(columnItem.icon + ".png") setattr(self, '_colbitmap%s' % column, bitmap) grid.SetLabelBitmap(False, column, bitmap) @@ -100,8 +100,8 @@ delegate = AttributeEditors.getSingleton (type) attribute = self.defaultROAttribute grid = self.GetView() - assert (row < self.GetNumberRows() and - column < self.GetNumberCols()) + assert (row < grid.GetNumberRows() and + column < grid.GetNumberCols()) if (not grid.blockItem.columns[column].readOnly and not grid.ReadOnly (row, column)[0] and @@ -256,25 +256,26 @@ if not contents.isSelected((indexStart, indexEnd)): selectionChanged = True contents.addSelectionRange((indexStart, indexEnd)) + elif (firstRow == 0 and lastRow != 0 + and lastRow == self.GetNumberRows()-1): + # this is a special "deselection" event that the + # grid sends us just before selecting another + # single item. This happens just before a user + # simply clicks from one item to another. + + # this allows us to avoid broadcasting an empty + # deselection if the user is just clicking from + # one item to another. + contents.clearSelection() + postSelection = False else: + assert -1 not in (firstRow, lastRow) if contents.isSelected((indexStart, indexEnd)): selectionChanged = True # we always want to remove the old selection contents.removeSelectionRange((indexStart, indexEnd)) - if (firstRow == 0 and - lastRow != 0 and lastRow == self.GetNumberRows()-1): - # this is a special "deselection" event that the - # grid sends us just before selecting another - # single item. This happens just before a user - # simply clicks from one item to another. - - # this allows us to avoid broadcasting an empty - # deselection if the user is just clicking from - # one item to another. - postSelection = False - # possibly broadcast the "new" selection if selectionChanged and postSelection: gridTable = self.GetTable() @@ -587,10 +588,7 @@ """ DrawingUtilities.SetTextColorsAndFont (grid, attr, dc, isInSelection) value = grid.GetElementValue (row, column) - if __debug__: - item, attributeName = value - assert not item.isDeleted() - + assert len(value) != 2 or not value[0].isDeleted() self.delegate.Draw (dc, rect, value, isInSelection) class GridCellAttributeEditor (wx.grid.PyGridCellEditor):
Modified: trunk/chandler/parcels/osaf/pim/items.py (11301 => 11302)
--- trunk/chandler/parcels/osaf/pim/items.py 2006-08-01 22:56:40 UTC (rev 11301) +++ trunk/chandler/parcels/osaf/pim/items.py 2006-08-02 00:02:53 UTC (rev 11302) @@ -55,6 +55,18 @@ class TriageEnum(schema.Enumeration): values = "now", "later", "done" +triageStatusNames = _(u"Now"), _(u"Later"), _(u"Done") +def getTriageStatusName(value): + for i, triageValue in enumerate(TriageEnum.values): + if triageValue == value: + return triageStatusNames[i] + assert false + return u'' + +triageStatusOrder = dict((v, i) for i, v in enumerate(TriageEnum.values)) +def getTriageStatusOrder(value): + return triageStatusOrder[value] + class Calculated(property): """ A property with type information, in the style of our schema.* objects.
Modified: trunk/chandler/parcels/osaf/views/main/Dashboard.py (11301 => 11302)
--- trunk/chandler/parcels/osaf/views/main/Dashboard.py 2006-08-01 22:56:40 UTC (rev 11301) +++ trunk/chandler/parcels/osaf/views/main/Dashboard.py 2006-08-02 00:02:53 UTC (rev 11302) @@ -30,24 +30,24 @@ if __debug__: evtNames = { - 10028: 'ENTER_WINDOW', - 10029: 'LEAVE_WINDOW', - 10021: 'LEFT_DOWN', - 10022: 'LEFT_UP', - 10030: 'LEFT_DCLICK', - 10023: 'MIDDLE_DOWN', - 10024: 'MIDDLE_UP', - 10031: 'MIDDLE_DCLICK', - 10025: 'RIGHT_DOWN', - 10026: 'RIGHT_UP', - 10032: 'RIGHT_DCLICK', - 10027: 'MOTION', - 10036: 'MOUSEWHEEL', + wx.wxEVT_ENTER_WINDOW: 'ENTER_WINDOW', + wx.wxEVT_LEAVE_WINDOW: 'LEAVE_WINDOW', + wx.wxEVT_LEFT_DOWN: 'LEFT_DOWN', + wx.wxEVT_LEFT_UP: 'LEFT_UP', + wx.wxEVT_LEFT_DCLICK: 'LEFT_DCLICK', + wx.wxEVT_MIDDLE_DOWN: 'MIDDLE_DOWN', + wx.wxEVT_MIDDLE_UP: 'MIDDLE_UP', + wx.wxEVT_MIDDLE_DCLICK: 'MIDDLE_DCLICK', + wx.wxEVT_RIGHT_DOWN: 'RIGHT_DOWN', + wx.wxEVT_RIGHT_UP: 'RIGHT_UP', + wx.wxEVT_RIGHT_DCLICK: 'RIGHT_DCLICK', + wx.wxEVT_MOTION: 'MOTION', + wx.wxEVT_MOUSEWHEEL: 'MOUSEWHEEL', } class DashboardPrefs(Preferences): - showSections = schema.One(schema.Boolean, defaultValue = False) + showSections = schema.One(schema.Boolean, defaultValue = True) class wxDashboard(wxTable): def __init__(self, *arguments, **keywords): @@ -84,10 +84,14 @@ def getHandler(cell): if cell is None or -1 in cell: return None + renderer = self.GetCellRenderer(cell[1], cell[0]) try: - handler = self.GetCellRenderer(cell[1], cell[0]).delegate.OnMouseChange + # See if it's renderer with an attribute editor that wants + # mouse events + handler = renderer.delegate.OnMouseChange except AttributeError: - handler = None + # See if it's a section renderer that wants mouse events + handler = getattr(renderer, 'OnMouseChange', None) return handler # Figure out what cell we're in, and see whether it @@ -116,7 +120,8 @@ itemAttrPair = self.GetTable().GetValue(overCell[1], overCell[0]) #logger.debug("in=False, down=%s to old overCell: %s %s", #event.LeftIsDown(), *itemAttrPair) - oldHandler(event, False, event.LeftIsDown(), itemAttrPair) + oldHandler(event, overCell, False, event.LeftIsDown(), + itemAttrPair) skipIt = False # We'll need to notify the new cell too. @@ -138,7 +143,7 @@ itemAttrPair = self.GetTable().GetValue(cell[1], cell[0]) #logger.debug("in=True, down=%s to new cell: %s %s", #event.LeftIsDown(), *itemAttrPair) - handler(event, True, event.LeftIsDown(), itemAttrPair) + handler(event, cell, True, event.LeftIsDown(), itemAttrPair) skipIt = False finally:
Modified: trunk/chandler/parcels/osaf/views/main/Sections.py (11301 => 11302)
--- trunk/chandler/parcels/osaf/views/main/Sections.py 2006-08-01 22:56:40 UTC (rev 11301) +++ trunk/chandler/parcels/osaf/views/main/Sections.py 2006-08-02 00:02:53 UTC (rev 11302) @@ -14,15 +14,21 @@ import wx - -from application import schema - -from osaf.framework.blocks import ControlBlocks +from application import schema, styles +from i18n import OSAFMessageFactory as _ +from osaf.framework.blocks import ControlBlocks, DrawingUtilities, Styles +from osaf.framework.attributeEditors import BaseAttributeEditor +from osaf.pim import ContentItem +from osaf.pim.items import getTriageStatusName, getTriageStatusOrder from util.divisions import get_divisions -from osaf.framework.blocks import DrawingUtilities +import logging +logger = logging.getLogger(__name__) -from i18n import OSAFMessageFactory as _ +# Drawing geometry +margin = 10 +swatchWidth = 25 +swatchHeight = '__WXMAC__' in wx.PlatformInfo and 8 or 10 class SectionedGridDelegate(ControlBlocks.AttributeDelegate): @@ -45,9 +51,6 @@ # total rows in the table self.totalRows = 0 - self.RegisterDataType("Section", SectionRenderer(), - SectionEditor()) - self.previousIndex = self.blockItem.contents.indexName def SynchronizeDelegate(self): @@ -66,64 +69,80 @@ rebuild the sections - this is relatively cheap as long as there aren't a lot of sections """ + #for (row, ignored, ignored) in self.sectionRows: + #self.SetCellSize(row, 0, 1, 1) self.sectionRows = [] self.sectionLabels = [] self.sectionIndexes = [] - self.totalRows = 0 + self.sectionColors = [] + self.totalRows = len(self.blockItem.contents) dashboardPrefs = schema.ns('osaf.views.main', self.blockItem.itsView).dashboardPrefs if not dashboardPrefs.showSections: return - # regenerate index-based sections - each entry in - # self.sectionIndexes is the first index in the collection - # where we would need a section - indexName = self.blockItem.contents.indexName - if indexName not in (None, '__adhoc__'): - self.sectionIndexes = \ - get_divisions(self.blockItem.contents, - key=lambda x: getattr(x, indexName)) + indexName = self.blockItem.contents.indexName + # @@@ For 0.7alpha4, we only section on triage status + #if indexName in (None, '__adhoc__'): + if indexName != 'triageStatus': + return - # dont' show section headers for zero or one section + # Get the divisions + self.sectionIndexes = get_divisions(self.blockItem.contents, + key=lambda x: getTriageStatusOrder(getattr(x, indexName))) + + # don't show section headers for zero or one section if len(self.sectionIndexes) <= 1: return # now build the row-based sections - each entry in this array # is the actual row that contains the section divider + self.totalRows = 0 nextSectionRow = 0 for section in range(0, len(self.sectionIndexes)): sectionRow = nextSectionRow - if section in self.collapsedSections: - sectionLength = 0 - # previous section collapsed, so we're just one past - # the last one + if section == len(self.sectionIndexes)-1: + # last section - need to use blockItem.contents + # to determine the length + sectionTotal = (len(self.blockItem.contents) - + self.sectionIndexes[-1]) else: - if section == len(self.sectionIndexes)-1: - # last section - need to use blockItem.contents - # to determine the length - sectionLength = (len(self.blockItem.contents) - - self.sectionIndexes[-1]) - else: - # not collapsed, so determine the length of this - # section from the next section in self.sectionIndexes - sectionLength = (self.sectionIndexes[section+1] - - self.sectionIndexes[section]) + # not collapsed, so determine the length of this + # section from the next section in self.sectionIndexes + sectionTotal = (self.sectionIndexes[section+1] - + self.sectionIndexes[section]) + sectionVisible = (section not in self.collapsedSections + and sectionTotal or 0) + # might as well recall this for the next iteration through # the loop. the +1 is for the section header itself. - nextSectionRow = sectionRow + sectionLength + 1 + nextSectionRow = sectionRow + sectionVisible + 1 - self.sectionRows.append((sectionRow, sectionLength)) - self.totalRows += sectionLength + 1 - label = _(u"Section: %s") % \ - getattr(self.blockItem.contents[self.sectionIndexes[section]], indexName, _(u"<unknown>")) + self.sectionRows.append((sectionRow, sectionVisible, sectionTotal)) + self.totalRows += sectionVisible + 1 + + # Get the color name we'll use for this section + # For now, it's just the attribute value. + sectionValue = getattr(self.blockItem.contents[self.sectionIndexes[section]], + indexName, None) + self.sectionColors.append(sectionValue) + + # Get the label we'll use for this section + # By default, it's the value (which we just grabbed as the color + # name), unless this is an Enumeration whose `values` is a + # dictionary that maps to a string label + label = getTriageStatusName(sectionValue) self.sectionLabels.append(label) + #for (row, ignored, ignored) in self.sectionRows: + #self.SetCellSize(row, 0, 1, self.GetNumberCols()) + # make sure we're sane assert len(self.sectionRows) == len(self.sectionIndexes) - assert sum([length+1 for (row, length) in self.sectionRows]) == \ - self.totalRows + assert sum([visible+1 for (row, visible, total) in self.sectionRows]) \ + == self.totalRows def GetElementCount(self): @@ -148,22 +167,32 @@ return "Section" return super(SectionedGridDelegate, self).GetElementType(row, column) - - def ReadOnly(self, row, column): - itemIndex = self.RowToIndex(row) - if itemIndex == -1: - return False, True - - return super(SectionedGridDelegate, self).ReadOnly(row, column) def GetElementValue(self, row, column): - itemIndex = self.RowToIndex(row) if itemIndex == -1: - # this is just a hack because this value is getting passed - # to the default attribute editor - return object(), None - + # This is a section row. Return a tuple containing: + # - the attribute we're sectioned on, + # - the label for this section + # - the number of items in this section + # - the color name for this section (which may be None) + # - whether this section is expanded (True) or not (False) + # - whether this is the last (triageStatus) column + # + # Note that this tuple matches the one passed into + # SectionAttributeEditor.Draw, below. + for (section, (sectionRow, visible, itemCount)) in enumerate(self.sectionRows): + if row == sectionRow: + return (self.blockItem.contents.indexName, + self.sectionLabels[section], + itemCount, + self.sectionColors[section], + section not in self.collapsedSections, + column == len(self.blockItem.columns) - 1) + + assert False + return (None, u'', 0, None, False, False) + return super(SectionedGridDelegate, self).GetElementValue(row, column) def RowToIndex(self, row): @@ -181,7 +210,7 @@ sectionAdjust = len(self.sectionRows) - 1 # search backwards so we can jump right to the section number - for (reversedSection, (sectionRow, sectionSize)) in enumerate(reversed(self.sectionRows)): + for (reversedSection, (sectionRow, visible, total)) in enumerate(reversed(self.sectionRows)): section = sectionAdjust - reversedSection if row == sectionRow: @@ -210,7 +239,7 @@ linear search through the sections. Generally there aren't a lot of sections though so this should be reasonably fast. """ - if len(self.sectionIndexes) == 0: + if len(self.sectionIndexes) <= 1: return itemIndex sectionAdjust = len(self.sectionIndexes) - 1 @@ -223,7 +252,7 @@ # we should assert? Or maybe this is a valid case? return -1 else: - # Expanded sxection. Find the relative position + # Expanded section. Find the relative position # +1 accounts for header row indexOffset = itemIndex - sectionIndex sectionRow = self.sectionRows[section][0] @@ -236,7 +265,7 @@ return -1 def ToggleRow(self, row): - for (section, (sectionRow, length)) in enumerate(self.sectionRows): + for (section, (sectionRow, visible, total)) in enumerate(self.sectionRows): if row == sectionRow: if section in self.collapsedSections: self.ExpandSection(section) @@ -251,11 +280,11 @@ """ assert section not in self.collapsedSections - # subtract the oldLength - (oldPosition, oldLength) = self.sectionRows[section] + # subtract the oldVisibleCount + (oldPosition, oldVisibleCount, oldTotalCount) = self.sectionRows[section] - self.AdjustSectionPosition(section, -oldLength) - self.totalRows -= oldLength + self.AdjustSectionPosition(section, -oldVisibleCount) + self.totalRows -= oldVisibleCount self.collapsedSections.add(section) def ExpandSection(self, section): @@ -284,75 +313,98 @@ we have to adjust the given section as well as all sections following it. """ - for section, (sectionPosition, sectionLength) \ + for section, (sectionPosition, sectionVisibleCount, sectionTotalCount) \ in enumerate(self.sectionRows): if section >= startSection: self.sectionRows[section] = (sectionPosition + delta, - sectionLength) + sectionVisibleCount, + sectionTotalCount) - -class SectionRenderer(wx.grid.PyGridCellRenderer): +class SectionAttributeEditor(BaseAttributeEditor): def __init__(self, *args, **kwds): - super(SectionRenderer, self).__init__(*args, **kwds) + super(SectionAttributeEditor, self).__init__(*args, **kwds) self.brushes = DrawingUtilities.Gradients() - + def ReadOnly(self, *args): - # print "Who is calling RO?" - return False, False + """ + Sections are never editable. + (Contract/expand toggling is handled separately.) + """ + return True - def Draw(self, grid, attr, dc, rect, row, col, isSelected): + def Draw(self, dc, rect, + (attributeName, label, count, colorName, expanded, last), + isInSelection=False): dc.SetPen(wx.TRANSPARENT_PEN) - brush = self.brushes.GetGradientBrush(0, rect.height, - (153, 204, 255), (203, 229, 255), - "Vertical") + + sectionBackgroundColor = styles.cfg.get('summary', 'SectionBackground') + sectionLabelColor = styles.cfg.get('summary', 'SectionLabel') + sectionCountColor = styles.cfg.get('summary', 'SectionCount') + sectionSampleColor = colorName and styles.cfg.get('summary', + 'SectionSample_%s_%s' % (attributeName, colorName)) or None + + # We want a little space below the section bar, so it looks nice when + # they're contracted together... so erase a one-pixel-high rectangle at + # the bottom of our rect, then make ours a little smaller, + dc.SetBrush(wx.WHITE_BRUSH) + rect.height -= 1 + dc.DrawRectangleRect((rect.x, rect.y + rect.height, rect.width, 1)) + + # Draw the background + brush = wx.Brush(sectionBackgroundColor, wx.SOLID) dc.SetBrush(brush) dc.DrawRectangleRect(rect) + dc.SetTextBackground(sectionBackgroundColor) - if col == 0: - dc.SetFont(attr.GetFont()) - dc.SetTextForeground(wx.BLACK) - dc.SetBackgroundMode(wx.TRANSPARENT) - # look up row in section list - for section, (sectionRow, length) in enumerate(grid.sectionRows): - if row == sectionRow: - sectionTitle = grid.sectionLabels[section] - break - dc.DrawText(sectionTitle, 3, rect.y + 2) + # We'll center the 12-point text on the row, not counting descenders. + dc.SetFont(Styles.getFont(size=12)) + (labelWidth, labelHeight, labelDescent, ignored) = dc.GetFullTextExtent(label) + labelTop = rect.y + ((rect.height - labelHeight) / 2) + + # Draw the expando triangle + # (we'll cache the bitmap and its size so we're not constantly loading) + triSuffix = expanded and 'Open' or 'Closed' + cacheAttribute = '_tri%s' % triSuffix + triBitmapInfo = getattr(self, cacheAttribute, None) + if triBitmapInfo is None: + triBitmap = wx.GetApp().GetImage("SectionCaret%s.png" % triSuffix) + triWidth = triBitmap.GetWidth() + triHeight = triBitmap.GetHeight() + setattr(self, cacheAttribute, (triBitmap, triWidth, triHeight)) + else: + (triBitmap, triWidth, triHeight) = triBitmapInfo + triTop = rect.y + ((rect.height - triHeight) / 2) + dc.DrawBitmap(triBitmap, margin, triTop, True) + + # Draw the text label, if it overlaps the rect to be updated + labelPosition = margin + triWidth + dc.SetTextForeground(wx.BLACK) + dc.DrawText(label, labelPosition, labelTop) + + # Draw the item count, if it overlaps the rect to be updated + countPosition = labelPosition + labelWidth + if count == 1: + itemCount = _(u" 1 item ") % {'count': count } + else: + itemCount = _(u" %(count)d items ") % {'count': count } + dc.SetFont(Styles.getFont(size=10)) + (countWidth, countHeight, countDescent, ignored) = \ + dc.GetFullTextExtent(itemCount) + countTop = labelTop + (labelHeight - countHeight) \ + - (labelDescent - countDescent) + dc.SetTextForeground(sectionCountColor) + dc.DrawText(itemCount, countPosition, countTop) + + # If we're sectioned on triage status, draw the swatch + if colorName and last: + dc.SetPen(wx.WHITE_PEN) + brush = wx.Brush(sectionSampleColor, wx.SOLID) + dc.SetBrush(brush) + swatchX = rect.x + ((rect.width - swatchWidth) / 2) + swatchY = rect.y + ((rect.height - swatchHeight) / 2) + dc.DrawRectangleRect((swatchX, swatchY, swatchWidth, swatchHeight)) - -class SectionEditor(wx.grid.PyGridCellEditor): - def __init__(self, *args, **kwds): - super(SectionEditor, self).__init__(*args, **kwds) - - def StartingClick(self): - #print "StartingClick()" - (grid, row) = self.collapseInfo - grid.ToggleRow(row) - - def StartingKey(self, event): - #print "StartingKey()" - pass - - def Create(self, parent, id, evtHandler): - """ - Create a dummy control to make wx happy - the key here is SetControl() - """ - self.control = wx.Control(parent, id) - self.SetControl(self.control) - if evtHandler: - self.control.PushEventHandler(evtHandler) - #print "Create(%s, %s, %s)" % (parent, id, evtHandler) - - def BeginEdit(self, row, col, grid): - """ - Don't - """ - self.control.Hide() - self.collapseInfo = (grid, row) - #print "BeginEdit(%s, %s)" % (row,col) - - def EndEdit(self, row, col, grid): - del self.collapseInfo - #print "EndEdit(%s, %s)" % (row,col) - return False - + def OnMouseChange(self, event, cell, isIn, isDown, itemAttrNameTuple): + if event.GetEventType() == wx.wxEVT_LEFT_DCLICK: + grid = event.GetEventObject().GetParent() + grid.ToggleRow(cell[1])
Modified: trunk/chandler/parcels/osaf/views/main/__init__.py (11301 => 11302)
--- trunk/chandler/parcels/osaf/views/main/__init__.py 2006-08-01 22:56:40 UTC (rev 11301) +++ trunk/chandler/parcels/osaf/views/main/__init__.py 2006-08-02 00:02:53 UTC (rev 11302) @@ -13,9 +13,9 @@ # limitations under the License. -from Sections import SectionedGridDelegate -from SideBar import SidebarBlock, CPIATestSidebarBranchPointDelegate, SidebarBranchPointDelegate - +from Sections import SectionedGridDelegate, SectionAttributeEditor +from SideBar import (SidebarBlock, CPIATestSidebarBranchPointDelegate, + SidebarBranchPointDelegate) from Dashboard import DashboardPrefs def installParcel(parcel, oldVersion=None): @@ -23,7 +23,8 @@ from menus import makeMainMenus from mainblocks import makeMainView from summaryblocks import makeSummaryBlocks - + from osaf.framework.attributeEditors import AttributeEditorMapping + makeMainEvents (parcel) makeMainMenus (parcel) makeMainView (parcel) @@ -34,3 +35,7 @@ prompts.DialogPref.update(parcel, "clearCollectionPref") DashboardPrefs.update(parcel, "dashboardPrefs") + + AttributeEditorMapping.register(parcel, + { 'Section': 'SectionAttributeEditor' }, + __name__)
Modified: trunk/chandler/parcels/osaf/views/main/summaryblocks.py (11301 => 11302)
--- trunk/chandler/parcels/osaf/views/main/summaryblocks.py 2006-08-01 22:56:40 UTC (rev 11301) +++ trunk/chandler/parcels/osaf/views/main/summaryblocks.py 2006-08-02 00:02:53 UTC (rev 11302) @@ -51,13 +51,13 @@ scaleWidthsToFit = True, columns = [ Column.update(parcel, 'SumColTask', - icon = 'SumTask', + icon = 'ColHTask', valueType = 'kind', kind = pim.TaskMixin.getKind(repositoryView), width = 20, readOnly = True), Column.update(parcel, 'SumColMail', - icon = 'SumMail', + icon = 'ColHMail', valueType = 'kind', kind = pim.mail.MailMessageMixin.getKind(repositoryView), width = 20, @@ -74,7 +74,7 @@ width = 120, scaleColumn = True), Column.update(parcel, 'SumColCalendarEvent', - icon = 'SumEvent', + icon = 'ColHEvent', valueType = 'kind', kind = pim.CalendarEventMixin.getKind(repositoryView), width = 20, @@ -86,7 +86,7 @@ scaleColumn = True, readOnly = True), Column.update(parcel, 'SumColTriage', - heading = _(u'Triage'), + icon = 'ColHTriageStatus', attributeName = 'triageStatus', width = 40), ],
Modified: trunk/chandler/tools/QATestScripts/Functional/FunctionalTestSuite.py (11301 => 11302)
--- trunk/chandler/tools/QATestScripts/Functional/FunctionalTestSuite.py 2006-08-01 22:56:40 UTC (rev 11301) +++ trunk/chandler/tools/QATestScripts/Functional/FunctionalTestSuite.py 2006-08-02 00:02:53 UTC (rev 11302) @@ -45,6 +45,7 @@ "TestNewMail.py", "TestNewTask.py", "TestNewNote.py", + "TestTriageSectioning.py", "TestTableSelection.py", "TestStamping.py", "TestSharing.py",
Added: trunk/chandler/tools/QATestScripts/Functional/TestTriageSectioning.py (11301 => 11302)
--- trunk/chandler/tools/QATestScripts/Functional/TestTriageSectioning.py 2006-08-01 22:56:40 UTC (rev 11301) +++ trunk/chandler/tools/QATestScripts/Functional/TestTriageSectioning.py 2006-08-02 00:02:53 UTC (rev 11302) @@ -0,0 +1,81 @@ +# Copyright (c) 2003-2006 Open Source Applications Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from tools import QAUITestAppLib +import osaf.pim as pim + +fileName = "TestTriageSectioning.log" +logger = QAUITestAppLib.QALogger(fileName, "TestTriageSectioning") + +App_ns = QAUITestAppLib.App_ns + +try: + # setup + + # create a collection and select it in the sidebar + view = QAUITestAppLib.UITestView(logger) + view.SwitchToAllView() + + col = QAUITestAppLib.UITestItem("Collection", logger) + col.SetDisplayName("TestTriageSectioning") + User.emulate_sidebarClick(App_ns.sidebar, "TestTriageSectioning") + + items = [] + for status in pim.TriageEnum.values: + item = QAUITestAppLib.UITestItem("Note", logger) + if status != 'now': # it should default to 'now'! + item.item.triageStatus = status + items.append(item) + + # action + + # Get ready to bang on the dashboard + dashboardBlock = App_ns.TableSummaryView + dashboard = dashboardBlock.widget + rowHeight = dashboard.GetDefaultRowSize() + rowMiddle = rowHeight/2 + header_widget = dashboard.GetGridColLabelWindow() + + # sort by triage status + User.emulate_click(header_widget, header_widget.GetSize()[0] - 15, 3) + User.idle() + + # Check the data structures: see that we're sectioned properly + # for a table with three items of different status: + goodExpandedSectioning = [(0, 1, 1), (2, 1, 1), (4, 1, 1)] + sectionRows = getattr(dashboard, 'sectionRows', None) + if not sectionRows: + logger.ReportFailure("Dashboard not sectioned") + if sectionRows != goodExpandedSectioning: + logger.ReportFailure("Dashboard not sectioned properly") + + # Check that contraction and expansion work. For now, double-clicking + # expands and contracts the section rows; eventually, it'll be clicking on + # the triangle. + for row in (4, 2, 0): + User.emulate_click(dashboard, 100, row*rowHeight + rowMiddle, double=True) + User.idle() + if dashboard.sectionRows != [(0, 0, 1), (1, 0, 1), (2, 0, 1)]: + logger.ReportFailure("Dashboard didn't contract properly") + for row in (2, 1, 0): + User.emulate_click(dashboard, 100, row*rowHeight + rowMiddle, double=True) + User.idle() + if dashboard.sectionRows != goodExpandedSectioning: + logger.ReportFailure("Dashboard didn't expand properly") + + logger.Report("TriageSectioning") + +finally: + pass + logger.Close() Property changes on: trunk/chandler/tools/QATestScripts/Functional/TestTriageSectioning.py ___________________________________________________________________ Name: svn:executable + * Name: svn:mime-type + text/plain Name: svn:eol-style + native
_______________________________________________ Commits mailing list [email protected] http://lists.osafoundation.org/mailman/listinfo/commits
