Title: [commits] (stearns) [11302] Dashboard visual work:
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)

Modified Paths

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

Reply via email to