Package pyjamas :: Package ui :: Module Calendar
[hide private]
[frames] | no frames]

Source Code for Module pyjamas.ui.Calendar

  1  # Date Time Example 
  2  # Copyright (C) 2009 Yit Choong (http://code.google.com/u/yitchoong/) 
  3  # Copyright (C) 2009 Luke Kenneth Casson Leighton <lkcl@lkcl.net> 
  4  # Copyright (C) 2012 Łukasz Mach <maho@pagema.net> 
  5   
  6  from pyjamas.ui.SimplePanel import SimplePanel 
  7  from pyjamas import Factory 
  8  from pyjamas.ui.VerticalPanel import  VerticalPanel 
  9  from pyjamas.ui.HorizontalPanel import HorizontalPanel 
 10  from pyjamas.ui.PopupPanel import  PopupPanel 
 11  from pyjamas.ui.Grid import Grid 
 12  from pyjamas.ui.Composite import Composite 
 13  from pyjamas.ui.Label import Label 
 14  from pyjamas.ui.Hyperlink import Hyperlink 
 15  from pyjamas.ui.HyperlinkImage import HyperlinkImage 
 16  from pyjamas.ui.HTML import HTML 
 17  from pyjamas.ui.FocusPanel import FocusPanel 
 18  from pyjamas.ui.TextBox import TextBox 
 19  from pyjamas.ui.Image import Image 
 20  from pyjamas.ui import HasAlignment 
 21  from pyjamas import DOM 
 22   
 23  import pygwt 
 24   
 25  import time 
 26  from datetime import datetime, date 
 27   
28 -class Calendar(FocusPanel):
29 monthsOfYear = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 30 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] 31 daysOfWeek = ['S', 'M', 'T', 'W', 'T', 'F', 'S'] 32 today = 'Today' 33 tomorrow = 'Tomorrow' 34 yesterday = 'Yesterday' 35 cancel = 'Cancel' 36
37 - def __init__(self, **kwargs):
38 FocusPanel.__init__(self, **kwargs) 39 yr, mth, day = time.strftime("%Y-%m-%d").split("-") 40 self.todayYear = int(yr) 41 self.todayMonth = int(mth) # change to offset 0 as per javascript 42 self.todayDay = int(day) 43 44 self.currentMonth = self.todayMonth 45 self.currentYear = self.todayYear 46 self.currentDay = self.todayDay 47 48 self.selectedDateListeners = [] 49 50 self.defaultGrid = None # used later 51 52 return
53 54
55 - def setDate(self,_date):
56 """ _date - object of datetime.date class """ 57 self.currentMonth = _date.month 58 self.currentYear = _date.year 59 self.currentDay = _date.day
60
61 - def getMonthsOfYear(self):
62 return self.monthsOfYear
63
64 - def getDaysOfWeek(self):
65 return self.daysOfWeek
66
67 - def addSelectedDateListener(self, listener):
68 self.selectedDateListeners.append(listener)
69
70 - def removeSelectedDateListener(self, listener):
71 self.selectedDateListeners.remove(listener)
72
73 - def isLeapYear(self, year):
74 if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0): 75 return True 76 else: 77 return False
78
79 - def getDaysInMonth(self, mth, year):
80 days = 0 81 if mth in [1, 3, 5, 7, 8, 10, 12]: 82 days=31 83 elif mth in [4, 6, 9, 11]: 84 days = 30 85 elif (mth==2 and self.isLeapYear(year)): 86 days = 29 87 else: 88 days = 28 89 return days
90
91 - def setPosition(self, left, top):
92 element = self.getElement() 93 DOM.setStyleAttribute(element, "left", "%dpx" % left) 94 DOM.setStyleAttribute(element, "top", "%dpx" % top)
95
96 - def show(self, left, top):
97 if left < 0: 98 left = 0 99 if top < 0: 100 top = 0 101 self.setPosition(left, top) 102 self.drawCurrent() 103 self.setVisible(True)
104
105 - def drawCurrent(self):
106 yr, mth, day = self.currentYear, self.currentMonth, self.currentDay 107 self.draw(int(mth), int(yr))
108 109
110 - def draw(self, month , year):
111 tod = time.localtime() 112 mm = tod.tm_mon 113 yy = tod.tm_year 114 # has today changed and thus changed month? cater to rare case 115 # where widget in created on last day of month and 116 # page left till next day 117 hasChangeMonth = False 118 if yy <> self.todayYear or mm <> self.todayMonth: 119 hasChangeMonth = True 120 self.todayYear = yy 121 self.todayMonth = mm 122 123 # check to see if we have drawn the full widget before 124 if self.defaultGrid is None: 125 self.drawFull(month, year) 126 else: 127 # ok means we are re-drawing, but first check if it is the same 128 # as the defaultGrid, if yes, just use it 129 if not hasChangeMonth and month == self.todayMonth and \ 130 year == self.todayYear: 131 self.middlePanel.setWidget(self.defaultGrid) 132 self.currentMonth = self.todayMonth 133 self.currentYear = self.todayYear 134 else: 135 # we have to redraw the grid -- bah 136 g = self.drawGrid(month, year) 137 138 if hasChangeMonth: 139 # reset the default grid as we have changed months 140 self.defaultGrid = grid 141 142 # 143 # what about the title panel? 144 # 145 txt = "<b>" 146 txt += self.getMonthsOfYear()[month-1] + " " + str(year) 147 txt += "</b>" 148 self.titlePanel.setWidget(HTML(txt)) 149 self.setVisible(True) 150 151 return
152
153 - def drawFull(self, month, year):
154 # should be called only once when we draw the calendar for 155 # the first time 156 self.vp = VerticalPanel() 157 self.vp.setSpacing(2) 158 self.vp.addStyleName("calendarbox calendar-module calendar") 159 self.setWidget(self.vp) 160 self.setVisible(False) 161 # 162 mth = int(month) 163 yr = int(year) 164 165 tp = HorizontalPanel() 166 tp.addStyleName("calendar-top-panel") 167 tp.setSpacing(5) 168 169 h1 = Hyperlink('<<') 170 h1.addClickListener(getattr(self, 'onPreviousYear')) 171 h2 = Hyperlink('<') 172 h2.addClickListener(getattr(self, 'onPreviousMonth')) 173 h4 = Hyperlink('>') 174 h4.addClickListener(getattr(self, 'onNextMonth')) 175 h5 = Hyperlink('>>') 176 h5.addClickListener(getattr(self, 'onNextYear')) 177 178 tp.add(h1) 179 tp.add(h2) 180 181 # titlePanel can be changed, whenever we draw, so keep the reference 182 txt = "<b>" 183 txt += self.getMonthsOfYear()[mth-1] + " " + str(yr) 184 txt += "</b>" 185 self.titlePanel = SimplePanel() 186 self.titlePanel.setWidget(HTML(txt)) 187 self.titlePanel.setStyleName("calendar-center") 188 189 tp.add(self.titlePanel) 190 tp.add(h4) 191 tp.add(h5) 192 tvp = VerticalPanel() 193 tvp.setSpacing(10) 194 tvp.add(tp) 195 196 self.vp.add(tvp) 197 198 # done with top panel 199 200 self.middlePanel = SimplePanel() 201 grid = self.drawGrid(mth, yr) 202 self.middlePanel.setWidget(grid) 203 self.vp.add(self.middlePanel) 204 self.defaultGrid = grid 205 206 self._gridShortcutsLinks() 207 self._gridCancelLink() 208 # 209 # add code to test another way of doing the layout 210 # 211 self.setVisible(True) 212 return
213 232 241
242 - def drawGrid(self, month, year):
243 # draw the grid in the middle of the calendar 244 245 daysInMonth = self.getDaysInMonth(month, year) 246 # first day of the month & year 247 secs = time.mktime((year, month, 1, 0, 0, 0, 0, 0, -1)) 248 struct = time.localtime(secs) 249 # 0 - sunday for our needs instead 0 = monday in tm_wday 250 startPos = (struct.tm_wday + 1) % 7 251 slots = startPos + daysInMonth - 1 252 rows = int(slots/7) + 1 253 grid = Grid(rows+1, 7) # extra row for the days in the week 254 grid.setWidth("100%") 255 grid.addTableListener(self) 256 self.middlePanel.setWidget(grid) 257 # 258 # put some content into the grid cells 259 # 260 for i in range(7): 261 grid.setText(0, i, self.getDaysOfWeek()[i]) 262 grid.cellFormatter.addStyleName(0, i, "calendar-header") 263 # 264 # draw cells which are empty first 265 # 266 day =0 267 pos = 0 268 while pos < startPos: 269 grid.setText(1, pos , " ") 270 grid.cellFormatter.setStyleAttr(1, pos, "background", "#f3f3f3") 271 grid.cellFormatter.addStyleName(1, pos, "calendar-blank-cell") 272 pos += 1 273 # now for days of the month 274 row = 1 275 day = 1 276 col = startPos 277 while day <= daysInMonth: 278 if pos % 7 == 0 and day <> 1: 279 row += 1 280 col = pos % 7 281 grid.setText(row, col, str(day)) 282 if self.currentYear == self.todayYear and \ 283 self.currentMonth == self.todayMonth and day == self.todayDay: 284 grid.cellFormatter.addStyleName(row, col, "calendar-cell-today") 285 else: 286 grid.cellFormatter.addStyleName(row, col, "calendar-day-cell") 287 day += 1 288 pos += 1 289 # 290 # now blank lines on the last row 291 # 292 col += 1 293 while col < 7: 294 grid.setText(row, col, " ") 295 grid.cellFormatter.setStyleAttr(row, col, "background", "#f3f3f3") 296 grid.cellFormatter.addStyleName(row, col, "calendar-blank-cell") 297 col += 1 298 299 return grid
300
301 - def onCellClicked(self, grid, row, col):
302 if row == 0: 303 return 304 text = grid.getText(row, col).strip() 305 if text == "": 306 return 307 try: 308 selectedDay = int(text) 309 except ValueError, e: 310 return 311 # well if anyone is listening to the listener, fire that event 312 for listener in self.selectedDateListeners: 313 if hasattr(listener, "onDateSelected"): 314 listener.onDateSelected(self.currentYear, self.currentMonth, 315 selectedDay) 316 else: 317 listener(self.currentYear, self.currentMonth, selectedDay) 318 self.setVisible(False)
319 320
321 - def onPreviousYear(self, event):
322 self.drawPreviousYear()
323
324 - def onPreviousMonth(self, event):
325 self.drawPreviousMonth()
326
327 - def onNextMonth(self, event):
328 self.drawNextMonth()
329
330 - def onNextYear(self, event):
331 self.drawNextYear()
332
333 - def onDate(self, event, yy, mm, dd):
334 for listener in self.selectedDateListeners: 335 if hasattr(listener, "onDateSelected"): 336 listener.onDateSelected(yy, mm, dd) 337 else: 338 listener(yy, mm, dd) 339 self.setVisible(False)
340
341 - def onYesterday(self, event):
342 yesterday = time.localtime(time.time() - 3600 * 24) 343 mm = yesterday.tm_mon 344 dd = yesterday.tm_mday 345 yy = yesterday.tm_year 346 self.onDate(event, yy, mm, dd)
347
348 - def onToday(self, event):
349 tod = time.localtime() 350 mm = tod.tm_mon 351 dd = tod.tm_mday 352 yy = tod.tm_year 353 self.onDate(event, yy, mm, dd)
354
355 - def onTomorrow(self, event):
356 tom = time.localtime(time.time() + 3600 * 24) 357 mm = tom.tm_mon 358 dd = tom.tm_mday 359 yy = tom.tm_year 360 self.onDate(event, yy, mm, dd)
361
362 - def onCancel(self, event):
363 self.setVisible(False)
364
365 - def drawDate(self, month, year):
366 self.currentMonth = month 367 self.currentYear = year 368 self.draw(self.currentMonth, self.currentYear)
369
370 - def drawPreviousMonth(self):
371 if int(self.currentMonth) == 1: 372 self.currentMonth = 12 373 self.currentYear = int(self.currentYear) - 1 374 else: 375 self.currentMonth = int(self.currentMonth) - 1 376 self.draw(self.currentMonth, self.currentYear)
377
378 - def drawNextMonth(self):
379 if int(self.currentMonth) == 12: 380 self.currentMonth = 1 381 self.currentYear = int(self.currentYear) + 1 382 else: 383 self.currentMonth = int(self.currentMonth) + 1 384 self.draw(self.currentMonth, self.currentYear)
385
386 - def drawPreviousYear(self):
387 self.currentYear = int(self.currentYear) - 1 388 self.draw(self.currentMonth, self.currentYear)
389
390 - def drawNextYear(self):
391 self.currentYear = int(self.currentYear) + 1 392 self.draw(self.currentMonth, self.currentYear)
393 394 Factory.registerClass('pyjamas.ui.Calendar', 'Calendar', Calendar) 395 396
397 -class DateField(Composite):
398 399 img_base = None 400 icon_img = None 401 402 icon_style = "calendar-img" 403 today_text = "Today" 404 today_style = "calendar-today-link" 405
406 - def __init__(self, format='%d-%m-%Y'):
407 if self.img_base is None: 408 self.img_base = pygwt.getImageBaseURL(True) 409 if self.icon_img is None: 410 self.icon_img = self.img_base + 'icon_calendar.gif' 411 self.format = format 412 self.tbox = TextBox() 413 self.tbox.setVisibleLength(10) 414 # assume valid sep is - / . or nothing 415 if format.find('-') >= 0: 416 self.sep = '-' 417 elif format.find('/') >= 0: 418 self.sep = '/' 419 elif format.find('.') >= 0: 420 self.sep = '.' 421 else: 422 self.sep = '' 423 # self.sep = format[2] # is this too presumptious? 424 self.calendar = Calendar() 425 self.img = Image(self.icon_img) 426 self.img.addStyleName(self.icon_style) 427 self.calendarLink = HyperlinkImage(self.img) 428 self.todayLink = Hyperlink(self.today_text) 429 self.todayLink.addStyleName(self.today_style) 430 # 431 # lay it out 432 # 433 hp = HorizontalPanel() 434 hp.setSpacing(2) 435 vp = VerticalPanel() 436 hp.add(self.tbox) 437 vp.add(self.calendarLink) 438 vp.add(self.todayLink) 439 #vp.add(self.calendar) 440 hp.add(vp) 441 442 Composite.__init__(self) 443 self.initWidget(hp) 444 # 445 # done with layout, so now set up some listeners 446 # 447 self.tbox.addFocusListener(self) # hook to onLostFocus 448 self.calendar.addSelectedDateListener(getattr(self, "onDateSelected")) 449 self.todayLink.addClickListener(getattr(self, "onTodayClicked")) 450 self.calendarLink.addClickListener(getattr(self, "onShowCalendar"))
451
452 - def getTextBox(self):
453 return self.tbox
454
455 - def getCalendar(self):
456 return self.calendar
457
458 - def getDate(self):
459 """ returns datetime.date object or None if empty/unparsable by current format""" 460 _sdate = self.tbox.getText() 461 try: 462 return datetime.strptime(_sdate, self.format).date() 463 except ValueError: 464 return None
465
466 - def setID(self, id):
467 self.tbox.setID(id)
468
469 - def onDateSelected(self, yyyy, mm, dd):
470 secs = time.mktime((int(yyyy), int(mm), int(dd), 0, 0, 0, 0, 0, -1)) 471 d = time.strftime(self.format, time.localtime(secs)) 472 self.tbox.setText(d)
473
474 - def onLostFocus(self, sender):
475 # 476 text = self.tbox.getText().strip() 477 # if blank - leave it alone 478 if text and len(text) == 8: 479 # ok what format do we have? assume ddmmyyyy --> dd-mm-yyyy 480 txt = text[0:2] + self.sep + text[2:4] + self.sep + text[4:8] 481 self.tbox.setText(txt)
482
483 - def onFocus(self, sender):
484 pass
485
486 - def onTodayClicked(self, event):
487 today = time.strftime(self.format) 488 self.tbox.setText(today)
489
490 - def onShowCalendar(self, sender):
491 txt = self.tbox.getText().strip() 492 try: 493 if txt: 494 _d = datetime.strptime(txt,self.format).date() 495 self.calendar.setDate(_d) 496 except ValueError: pass 497 498 p = CalendarPopup(self.calendar) 499 x = self.tbox.getAbsoluteLeft() + 10 500 y = self.tbox.getAbsoluteTop() + 10 501 p.setPopupPosition(x, y) 502 p.show()
503 504 Factory.registerClass('pyjamas.ui.Calendar', 'DateField', DateField) 505 506
507 -class CalendarPopup(PopupPanel):
508 - def __init__(self, c):
509 PopupPanel.__init__(self, True) 510 p = SimplePanel() 511 p.add(c) 512 c.show(10, 10) 513 p.setWidth("100%") 514 self.setWidget(p)
515 516 Factory.registerClass('pyjamas.ui.Calendar', 'CalendarPopup', CalendarPopup) 517