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

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