]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/wx/lib/calendar.py
updates from Andrea, plus some fixes
[wxWidgets.git] / wxPython / wx / lib / calendar.py
index 6125518e93e3b1e4cb3619fb78483c7736ab3b1a..38342c115f12fc59629c9524dfc62a6a972b380a 100644 (file)
 #   Instead set set_x_mrg and set_y_mrg
 # o Changed default X and Y Margin to 0.
 # o Added wxCalendar.SetMargin.
+#
+# 17/03/2004 - Joerg "Adi" Sieker adi@sieker.info
+# o Added keyboard navigation to the control.
+#   Use the cursor keys to navigate through the ages. :)
+#   The Home key function as go to today
+# o select day is now a filled rect instead of just an outline
+#
+# 15/04/2005 - Joe "shmengie" Brown joebrown@podiatryfl.com
+# o Adjusted spin control size/placement (On Windows ctrls were overlapping).
+# o Set Ok/Cancel buttons to wx.ID_OK & wx.ID_CANCEL to provide default dialog
+#   behaviour.
+# o If no date has been clicked clicked, OnOk set the result to calend's date,
+#   important if keyboard only navigation is used.
+#
+# 12/10/2006 - Walter Barnes walter_barnes05@yahoo.com
+# o Fixed CalDraw to properly render months that start on a Sunday.
+
 
 import wx
 
@@ -117,8 +134,8 @@ def DefaultColors():
     colors[COLOR_FONT] = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT)
     colors[COLOR_3D_LIGHT] = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNHIGHLIGHT)
     colors[COLOR_3D_DARK] = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)
-    colors[COLOR_HIGHLIGHT_FONT]  =  'PINK'
-    colors[COLOR_HIGHLIGHT_BACKGROUND] = 'RED'
+    colors[COLOR_HIGHLIGHT_FONT]  =  wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
+    colors[COLOR_HIGHLIGHT_BACKGROUND] = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
     return colors
 # calendar drawing routing
 
@@ -235,11 +252,22 @@ class CalDraw:
             self.cal_sel[val] = (cfont, cbackgrd)
 
     # draw border around the outside of the main display rectangle
-    def DrawBorder(self, DC):   
-        brush = wx.Brush(MakeColor(self.colors[COLOR_BACKGROUND]), wx.SOLID)
-        DC.SetBrush(brush)
-        DC.SetPen(wx.Pen(MakeColor(self.colors[COLOR_BORDER]), 1))
+    def DrawBorder(self, DC, transparent = False):   
         if self.outer_border is True:
+            if transparent == False:
+                brush = wx.Brush(MakeColor(self.colors[COLOR_BACKGROUND]), wx.SOLID)
+            else:
+                brush = wx.TRANSPARENT_BRUSH
+            DC.SetBrush(brush)
+            DC.SetPen(wx.Pen(MakeColor(self.colors[COLOR_BORDER])))
+            # full display window area
+            rect = wx.Rect(self.cx_st, self.cy_st, self.sizew, self.sizeh)  
+            DC.DrawRectangleRect(rect)
+
+    def DrawFocusIndicator(self, DC):
+        if self.outer_border is True:
+            DC.SetBrush(wx.TRANSPARENT_BRUSH)
+            DC.SetPen(wx.Pen(MakeColor(self.colors[COLOR_HIGHLIGHT_BACKGROUND]), style=wx.DOT))
             # full display window area
             rect = wx.Rect(self.cx_st, self.cy_st, self.sizew, self.sizeh)  
             DC.DrawRectangleRect(rect)
@@ -264,6 +292,9 @@ class CalDraw:
         else:
             start_pos = dow
 
+        if start_pos > 6:
+            start_pos = 0
+
         self.st_pos = start_pos
 
         self.cal_days = []
@@ -275,6 +306,8 @@ class CalDraw:
             self.cal_days.append(str(i))
             i = i + 1
 
+        self.end_pos = start_pos + dim - 1
+
         return start_pos
 
     def SetWeekEnd(self, font_color=None, backgrd = None):
@@ -284,6 +317,9 @@ class CalDraw:
             self.SetColor(COLOR_WEEKEND_BACKGROUND, MakeColor(backgrd))
 
         date = 6 - int(self.dow)     # start day of first saturday
+        if date == 0:                #...unless we start on Sunday
+            self.cal_sel[1] = (self.GetColor(COLOR_WEEKEND_FONT), self.GetColor(COLOR_WEEKEND_BACKGROUND))
+            date = 7
 
         while date <= self.dim:
             self.cal_sel[date] = (self.GetColor(COLOR_WEEKEND_FONT), self.GetColor(COLOR_WEEKEND_BACKGROUND))  # Saturday
@@ -298,6 +334,8 @@ class CalDraw:
     # get the display rectange list of the day grid
     def GetRect(self):      
         cnt = 0
+        h = 0
+        w = 0
         for y in self.gridy[1:-1]:
             if y == self.gridy[-2]:
                 h = h + self.restH
@@ -338,7 +376,7 @@ class CalDraw:
 
         tw,th = DC.GetTextExtent(month)
         adjust = self.cx_st + (self.sizew-tw)/2
-        DC.DrawText(month, (adjust, self.cy_st + th))
+        DC.DrawText(month, adjust, self.cy_st + th)
 
         year = str(self.year)
         tw,th = DC.GetTextExtent(year)
@@ -348,7 +386,7 @@ class CalDraw:
 
         f = wx.Font(sizef, self.font, wx.NORMAL, self.bold)
         DC.SetFont(f)
-        DC.DrawText(year, (self.cx_st + adjust, self.cy_st + th))
+        DC.DrawText(year, self.cx_st + adjust, self.cy_st + th)
 
     def DrawWeek(self, DC):     # draw the week days
         # increase by 1 to include all gridlines
@@ -410,7 +448,7 @@ class CalDraw:
             else:
                 pen = wx.Pen(MakeColor(self.GetColor(COLOR_BACKGROUND)), 1, wx.SOLID)
             DC.SetPen(pen)
-            DC.DrawRectangle( pointXY, pointWH)
+            DC.DrawRectanglePointSize( pointXY, pointWH)
             
             old_pen = DC.GetPen()
 
@@ -419,12 +457,12 @@ class CalDraw:
             # draw the horizontal hilight
             startPoint = wx.Point(x + 1 , y + 1)
             endPoint   = wx.Point(x + width - 1, y + 1)
-            DC.DrawLine(startPoint, endPoint )
+            DC.DrawLinePoint(startPoint, endPoint )
 
             # draw the vertical hilight
             startPoint = wx.Point(x + 1 , y + 1)
             endPoint   = wx.Point(x + 1, y + height - 2)
-            DC.DrawLine(startPoint, endPoint )
+            DC.DrawLinePoint(startPoint, endPoint )
 
             pen = wx.Pen(MakeColor(self.colors[COLOR_3D_DARK]), 1, wx.SOLID)
             DC.SetPen(pen)
@@ -432,25 +470,22 @@ class CalDraw:
             # draw the horizontal lowlight
             startPoint = wx.Point(x + 1, y + height - 2)
             endPoint   = wx.Point(x + width - 1, y + height - 2)
-            DC.DrawLine(startPoint, endPoint )
+            DC.DrawLinePoint(startPoint, endPoint )
             
             # draw the vertical lowlight
             startPoint = wx.Point(x + width - 2 , y + 2)
             endPoint   = wx.Point(x + width - 2, y + height - 2)
-            DC.DrawLine(startPoint, endPoint )
+            DC.DrawLinePoint(startPoint, endPoint )
 
             pen = wx.Pen(MakeColor(self.colors[COLOR_FONT]), 1, wx.SOLID)
             
             DC.SetPen(pen)
                 
             point = (x+diffx, y+diffy)
-            DC.DrawText(day, point)
+            DC.DrawTextPoint(day, point)
             cnt_x = cnt_x + 1
 
-    # draw the day numbers
-    def DrawNum(self, DC):      
-        f = wx.Font(10, self.font, wx.NORMAL, self.bold)      # initial font setting
-
+    def _CalcFontSize(self, DC, f):
         if self.num_auto == True:
             test_size = self.max_num_size      # max size
             test_day = ' 99 '
@@ -468,13 +503,18 @@ class CalDraw:
             f.SetPointSize(self.num_size)   # set fixed size
             DC.SetFont(f)
 
+    # draw the day numbers
+    def DrawNum(self, DC):      
+        f = wx.Font(10, self.font, wx.NORMAL, self.bold)      # initial font setting
+        self._CalcFontSize(DC, f)
+
         cnt_x = 0
         cnt_y = 1
         for val in self.cal_days:
             x = self.gridx[cnt_x]
             y = self.gridy[cnt_y]
 
-            self.DrawDayText(x, y, val, f, DC)
+            self._DrawDayText(x, y, val, f, DC)
 
             if cnt_x < 6:
                 cnt_x = cnt_x + 1
@@ -482,7 +522,8 @@ class CalDraw:
                 cnt_x = 0
                 cnt_y = cnt_y + 1
 
-    def DrawDayText(self, x, y, text, font, DC):
+    def _DrawDayText(self, x, y, text, font, DC):
+
         try:
             num_val = int(text)
             num_color = self.cal_sel[num_val][0]
@@ -512,25 +553,40 @@ class CalDraw:
 
         adj_v = adj_v + self.num_indent_vert
 
-        DC.DrawText(text, (x+adj_h, y+adj_v))
+        DC.DrawTextPoint(text, (x+adj_h, y+adj_v))
+        
+    def DrawDayText(self, DC, key):
+        f = wx.Font(10, self.font, wx.NORMAL, self.bold)      # initial font setting
+        self._CalcFontSize(DC, f)
+
+        if key > self.end_pos: 
+            key = self.end_pos
+
+        val = self.cal_days[key]
+        cnt_x = key % 7
+        cnt_y = int(key / 7)+1
+        x = self.gridx[cnt_x]
+        y = self.gridy[cnt_y]
+        self._DrawDayText(x, y, val, f, DC)
+
 
     # calculate the dimensions in the center of the drawing area
     def Center(self):       
         borderW = self.x_mrg * 2
         borderH = self.y_mrg + self.y_end + self.title_offset
 
-        self.cellW = int((self.sizew-borderW)/7)
-        self.cellH = int((self.sizeh-borderH)/7)
+        self.cellW = int((self.sizew - borderW)/7)
+        self.cellH = int((self.sizeh - borderH)/7)
 
         self.restW = ((self.sizew - borderW)%7 ) - 1
 
         # week title adjustment
-        self.weekHdrCellH = int(self.cellH*self.cal_week_scale)
+        self.weekHdrCellH = int(self.cellH * self.cal_week_scale)
         # recalculate the cell height exkl. the week header and
         # subtracting the size
-        self.cellH = int((self.sizeh-borderH-self.weekHdrCellH)/6)
+        self.cellH = int((self.sizeh - borderH - self.weekHdrCellH)/6)
 
-        self.restH = ((self.sizeh-borderH-self.weekHdrCellH)%6 ) - 1
+        self.restH = ((self.sizeh - borderH - self.weekHdrCellH)%6 ) - 1
         self.calW = self.cellW * 7
         self.calH = self.cellH * 6 + self.weekHdrCellH
 
@@ -550,7 +606,7 @@ class CalDraw:
             nkey = key + self.st_pos -1
             rect = self.rg[nkey]
 
-            DC.DrawRectangle((rect.x, rect.y), (rect.width, rect.height))
+            DC.DrawRectangleRect(rect)
 
     # calculate and draw the grid lines
     def DrawGrid(self, DC):
@@ -572,7 +628,7 @@ class CalDraw:
                 x1 = x1 + self.restW
 
             if self.hide_grid is False:
-                DC.DrawLine((x1, y1), (x1, y2))
+                DC.DrawLinePoint((x1, y1), (x1, y2))
 
             self.gridx.append(x1)
 
@@ -587,7 +643,7 @@ class CalDraw:
                 y1 = y1 + self.restH
 
             if self.hide_grid is False:
-                DC.DrawLine((x1, y1), (x2, y1))
+                DC.DrawLinePoint((x1, y1), (x2, y1))
 
             self.gridy.append(y1)
 
@@ -623,12 +679,14 @@ class PrtCalDraw(CalDraw):
     def SetPreview(self, preview):
         self.preview = preview
 
-class Calendar(wx.Window):
-    def __init__(self, parent, id, pos=wx.DefaultPosition, size=wx.DefaultSize):
-        wx.Window.__init__(self, parent, id, pos, size)
+class Calendar( wx.PyControl ):
+    def __init__(self, parent, id, pos=wx.DefaultPosition, size=wx.Size(200,200),
+                   style= 0, validator=wx.DefaultValidator,
+                   name= "calendar"):
+        wx.PyControl.__init__(self, parent, id, pos, size, style | wx.WANTS_CHARS, validator, name)
 
+        self.hasFocus = False
         # set the calendar control attributes
-        self.cal = None
 
         self.hide_grid = False
         self.hide_title = False
@@ -649,6 +707,9 @@ class Calendar(wx.Window):
         self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDEvent)
         self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightEvent)
         self.Bind(wx.EVT_RIGHT_DCLICK, self.OnRightDEvent)
+        self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
+        self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
 
         self.sel_key = None      #  last used by
         self.sel_lst = []        # highlighted selected days
@@ -661,7 +722,10 @@ class Calendar(wx.Window):
 
         self.Bind(wx.EVT_PAINT, self.OnPaint)
         self.Bind(wx.EVT_SIZE, self.OnSize)
-
+        
+    def AcceptsFocus(self):
+        return self.IsShown() and self.IsEnabled()
+    
     def GetColor(self, name):
         return MakeColor(self.colors[name])
 
@@ -703,6 +767,65 @@ class Calendar(wx.Window):
         self.click = 'DRIGHT'
         self.ProcessClick(event)
 
+    def OnSetFocus(self, event):
+        self.hasFocus = True
+        self.DrawFocusIndicator(True)
+
+    def OnKillFocus(self, event):
+        self.hasFocus = False
+        self.DrawFocusIndicator(False)
+
+    def OnKeyDown(self, event):
+        if not self.hasFocus:
+            event.Skip()
+            return
+        
+        key_code = event.GetKeyCode()
+        
+        if key_code == wx.WXK_TAB:
+            forward = not event.ShiftDown()
+            ne = wx.NavigationKeyEvent()
+            ne.SetDirection(forward)
+            ne.SetCurrentFocus(self)
+            ne.SetEventObject(self)
+            self.GetParent().GetEventHandler().ProcessEvent(ne)
+            event.Skip()
+            return
+
+        delta = None
+
+        if key_code == wx.WXK_UP:
+            delta = -7 
+        elif key_code == wx.WXK_DOWN:
+            delta = 7 
+        elif key_code == wx.WXK_LEFT:
+            delta = -1 
+        elif key_code == wx.WXK_RIGHT:
+            delta = 1 
+        elif key_code == wx.WXK_HOME:
+            curDate = wx.DateTimeFromDMY(int(self.cal_days[self.sel_key]),self.month - 1,self.year)
+            newDate = wx.DateTime_Now()
+            ts = newDate - curDate
+            delta =  ts.GetDays()
+
+        if delta <> None:
+            curDate = wx.DateTimeFromDMY(int(self.cal_days[self.sel_key]),self.month - 1,self.year)
+            timeSpan = wx.TimeSpan_Days(delta)
+            newDate = curDate + timeSpan
+
+            if curDate.GetMonth() == newDate.GetMonth():
+                self.set_day = newDate.GetDay()
+                key = self.sel_key + delta
+                self.SelectDay(key)
+            else:
+                self.month = newDate.GetMonth() + 1
+                self.year = newDate.GetYear()
+                self.set_day = newDate.GetDay()
+                self.sel_key = None
+                self.DoDrawing(wx.ClientDC(self))
+
+        event.Skip()
+
     def SetSize(self, set_size):
         self.size = set_size
 
@@ -740,6 +863,7 @@ class Calendar(wx.Window):
 
     def SetDayValue(self, day):
         self.set_day = day
+        self.day = day
 
     def SetMonth(self, month):
         if month >= 1 and month <= 12:
@@ -890,7 +1014,36 @@ class Calendar(wx.Window):
         DC.EndDrawing()
 
     # draw the selection rectangle
-    def DrawRect(self, key, fgcolor = 'BLACK', width = 0):
+    def DrawFocusIndicator(self, draw):
+        DC = wx.ClientDC(self)
+        try:
+            if draw == True:
+                self.caldraw.DrawFocusIndicator(DC)
+            else:
+                self.caldraw.DrawBorder(DC,True)
+        except:
+            pass
+
+    def DrawRect(self, key, bgcolor = 'WHITE', fgcolor= 'PINK',width = 0):
+        if key == None:
+            return
+
+        DC = wx.ClientDC(self)
+        DC.BeginDrawing()
+
+        brush = wx.Brush(MakeColor(bgcolor))
+        DC.SetBrush(brush)
+
+        DC.SetPen(wx.TRANSPARENT_PEN)
+
+        rect = self.rg[key]
+        DC.DrawRectangle(rect.x+1, rect.y+1, rect.width-2, rect.height-2)
+
+        self.caldraw.DrawDayText(DC,key)
+
+        DC.EndDrawing()
+        
+    def DrawRectOrg(self, key, fgcolor = 'BLACK', width = 0):
         if key == None:
             return
 
@@ -906,28 +1059,39 @@ class Calendar(wx.Window):
             DC.SetPen(wx.Pen(MakeColor(self.GetColor(COLOR_GRID_LINES)), width))
 
         rect = self.rg[key]
-        DC.DrawRectangle((rect.x, rect.y), (rect.width, rect.height))
+        DC.DrawRectangleRect(rect)
 
         DC.EndDrawing()
 
     # set the day selection
 
     def SetDay(self, day):
-        day = day + self.st_pos - 1
-        self.SelectDay(day)
+        d = day + self.st_pos - 1
+        self.SelectDay(d)
 
+    def IsDayInWeekend(self, key):
+        try:
+            t = Date(self.year, self.month, 1)
+            
+            day = self.cal_days[key]
+            day = int(day) + t.day_of_week
+            
+            if day % 7 == 6 or day % 7 == 0:
+                return True
+        except:
+            return False
+        
     def SelectDay(self, key):
         sel_size = 1
         # clear large selection
-        self.DrawRect(self.sel_key, self.GetColor(COLOR_BACKGROUND), sel_size)
-
-        if self.hide_grid is False:
-            self.DrawRect(self.sel_key, self.GetColor(COLOR_GRID_LINES), sel_size)
+        
+        if self.sel_key != None:
+            (cfont, bgcolor) = self.__GetColorsForDay(self.sel_key)
+            self.DrawRect(self.sel_key, bgcolor,cfont, sel_size)
 
-        self.DrawRect(key, self.GetColor(COLOR_HIGHLIGHT_BACKGROUND), sel_size)
+        self.DrawRect(key, self.GetColor(COLOR_HIGHLIGHT_BACKGROUND), self.GetColor(COLOR_HIGHLIGHT_FONT), sel_size)
         # store last used by
         self.sel_key = key
-        self.select_day = None
 
     def ClearDsp(self):
         self.Clear()
@@ -935,11 +1099,27 @@ class Calendar(wx.Window):
         self.set_x_mrg = xmarg
         self.set_y_mrg = ymarg
         self.set_y_end = ymarg
+    def __GetColorsForDay(self, key):
+        cfont   = self.GetColor(COLOR_FONT)
+        bgcolor = self.GetColor(COLOR_BACKGROUND)
+
+        if self.IsDayInWeekend(key) is True and self.show_weekend is True:
+            cfont   = self.GetColor(COLOR_WEEKEND_FONT)
+            bgcolor = self.GetColor(COLOR_WEEKEND_BACKGROUND)
+
+        try:
+            dayIdx = int(self.cal_days[key])
+            (cfont, bgcolor) = self.caldraw.cal_sel[dayIdx]
+        except:
+            pass
+
+        return (cfont, bgcolor)
 
 class CalenDlg(wx.Dialog):
     def __init__(self, parent, month=None, day = None, year=None):
         wx.Dialog.__init__(self, parent, -1, "Event Calendar", wx.DefaultPosition, (280, 360))
-
+        self.result = None
+        
         # set the calendar and attributes
         self.calend = Calendar(self, -1, (20, 60), (240, 200))
 
@@ -965,7 +1145,7 @@ class CalenDlg(wx.Dialog):
 
         # alternate spin button to control the month
         h = self.date.GetSize().height
-        self.m_spin = wx.SpinButton(self, -1, (130, 20), (h*2, h), wx.SP_VERTICAL)
+        self.m_spin = wx.SpinButton(self, -1, (115, 20), (h*1.5, h), wx.SP_VERTICAL)
         self.m_spin.SetRange(1, 12)
         self.m_spin.SetValue(start_month)
         self.Bind(wx.EVT_SPIN, self.OnMonthSpin, self.m_spin)
@@ -974,7 +1154,7 @@ class CalenDlg(wx.Dialog):
         self.dtext = wx.TextCtrl(self, -1, str(start_year), (160, 20), (60, -1))
         h = self.dtext.GetSize().height
 
-        self.y_spin = wx.SpinButton(self, -1, (220, 20), (h*2, h), wx.SP_VERTICAL)
+        self.y_spin = wx.SpinButton(self, -1, (225, 20), (h*1.5, h), wx.SP_VERTICAL)
         self.y_spin.SetRange(1980, 2010)
         self.y_spin.SetValue(start_year)
 
@@ -985,13 +1165,14 @@ class CalenDlg(wx.Dialog):
         y_pos = 280
         but_size = (60, 25)
 
-        btn = wx.Button(self, -1, ' Ok ', (x_pos, y_pos), but_size)
+        btn = wx.Button(self, wx.ID_OK, ' Ok ', (x_pos, y_pos), but_size)
         self.Bind(wx.EVT_BUTTON, self.OnOk, btn)
 
-        btn = wx.Button(self, -1, ' Close ', (x_pos + 120, y_pos), but_size)
+        btn = wx.Button(self, wx.ID_CANCEL, ' Close ', (x_pos + 120, y_pos), but_size)
         self.Bind(wx.EVT_BUTTON, self.OnCancel, btn)
 
-    def OnOk(self, event):
+    def OnOk(self, evt):
+        self.result = ['None', str(self.calend.day), Month[self.calend.month], str(self.calend.year)]
         self.EndModal(wx.ID_OK)
 
     def OnCancel(self, event):