#!/usr/bin/env python import gtk from gtk import gdk import gobject from math import floor, ceil import datetime class CalendarEntry(gtk.DrawingArea): def __init__(self): gtk.DrawingArea.__init__(self) self.connect("expose_event", self.expose) # unmask events self.add_events(gdk.BUTTON_PRESS_MASK | gdk.BUTTON_RELEASE_MASK | gdk.POINTER_MOTION_MASK) self.focus = False self.trans = False self.day = None self.starthour = None self.length = None def expose(self, widget, event): context = widget.window.cairo_create() # set a clip region for the expose event context.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) context.clip() self.draw(context) return False def draw(self, context): rect = self.get_allocation() offset = 2 if self.focus: offset = 3 location1 = [ (offset, offset+15), (rect.width-(offset-1), offset+15), (rect.width-(offset-1), rect.height- 4 - offset), (rect.width-(4+offset), rect.height-offset + 1), (offset+5, rect.height-offset + 1), (offset, rect.height-4 -offset), (offset, offset+15), ] location2 = [ (1, 6), (6, 1), (rect.width-5, 1), (rect.width, 6), (rect.width, rect.height-5), (rect.width-5, rect.height), (6, rect.height), (1, rect.height-5), (1, 6), ] alpha = 1.0 if self.trans: alpha = 0.7 context.set_source_rgba(0.95, 0.17, 0.17, alpha) if self.focus and not self.trans: context.save() context.move_to(*location2[0]) for points in location2[1:]: context.line_to(*points) context.fill() context.restore() else: context.save() context.move_to(*location2[0]) for points in location2[1:]: context.line_to(*points) context.move_to(*location1[0]) for points in location1[1:]: context.line_to(*points) context.clip() context.move_to(*location2[0]) for points in location2[1:]: context.line_to(*points) context.fill() context.restore() context.set_source_rgba(0.984, 0.41, 0.41, alpha) context.save() context.move_to(*location1[0]) for points in location1[1:]: context.line_to(*points) context.fill() context.restore() class CalendarGrid(gtk.DrawingArea): #__gsignals__ = { # "time_changed": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_INT, gobject.TYPE_INT))) #} def __init__(self): gtk.DrawingArea.__init__(self) # gtk.Widget signals self.connect("expose_event", self.expose) self.connect("button_press_event", self.button_press) self.connect("button_release_event", self.button_release) self.connect("motion_notify_event", self.motion_notify) # unmask events self.add_events(gdk.BUTTON_PRESS_MASK | gdk.BUTTON_RELEASE_MASK | gtk.gdk.BUTTON_MOTION_MASK) self.max_y = 48 self.max_x = 7 self.points_matrix = range(self.max_x+1) self.selected = -1 self.last_entry = None self.dragging = False self.entries = [] self.focused = None def expose(self, widget, event): context = widget.window.cairo_create() # set a clip region for the expose event context.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) context.clip() self.draw(context) return False def draw(self, context): rect = self.get_allocation() self.set_point_matrix(rect) context.rectangle(rect.x, rect.y, rect.width, rect.height) context.set_source_rgb(1,1,1) context.fill() day = datetime.date.today().isoweekday() -1 context.rectangle(self.points_matrix[day][0][0], self.points_matrix[day][0][1], self.points_matrix[day+1][0][0] - self.points_matrix[day][0][0], rect.height) col = self.get_style().bg[gtk.STATE_SELECTED] context.set_source_rgba(col.red/65535.0, col.green/65535.0, col.blue/65535.0, 0.1) context.fill() if self.selected >= 0: day = self.selected context.rectangle(self.points_matrix[day][0][0], self.points_matrix[day][0][1], self.points_matrix[day+1][0][0] - self.points_matrix[day][0][0], rect.height) col = self.get_style().bg[gtk.STATE_SELECTED] context.set_source_rgba(col.red/65535.0, col.green/65535.0, col.blue/65535.0, 0.9) context.fill() for i in range(0,7): context.save() context.set_source_rgb(0.8, 0.8, 0.8) context.set_line_width(1) context.move_to(self.points_matrix[i][0][0], 0) context.line_to(self.points_matrix[i][40][0], rect.height) context.stroke() context.restore() for i in range(2,(self.max_y)/2, 2): context.save() context.set_source_rgb(0.8, 0.8, 0.8) context.set_line_width(1) context.move_to(*self.points_matrix[0][i*2]) context.line_to(rect.width, self.points_matrix[7][i*2][1]) context.stroke() context.restore() for i in range(1,(self.max_y)/2, 2): context.save() context.set_source_rgb(0.9, 0.9, 0.9) context.set_line_width(1) context.move_to(*self.points_matrix[0][i*2]) context.line_to(rect.width, self.points_matrix[7][i*2][1]) context.stroke() context.restore() for entry in self.entries: context.save() #context.rectangle(int(self.points_matrix[entry.day][entry.starthour][0]), int(self.points_matrix[entry.day][entry.starthour][1]), #int(self.points_matrix[entry.day+1][entry.starthour][0]-self.points_matrix[entry.day][entry.starthour][0]), #int(self.points_matrix[entry.day][entry.starthour+entry.length][1] - self.points_matrix[entry.day][entry.starthour][1])) #context.clip() context.translate(*self.points_matrix[entry.day][entry.starthour]) entry.draw(context) context.restore() def set_point_matrix(self, rect): x = 40.0; y = 0.0; height = rect.height / float(self.max_y); width = (rect.width - 40) / float(self.max_x); for i in range(self.max_x+1): self.points_matrix[i] = range(self.max_y+1) for j in range(self.max_y+1): self.points_matrix[i][j] = (floor(x + i * width), ceil(y + j * height)) def button_press(self, widget, event): width = (widget.get_allocation().width-41)/self.max_x selected = int(floor((event.x-40)/float(width))) if selected >= 0 and selected <= 6: self.selected = selected self.redraw_canvas() self.dragging = True return False def button_release(self, widget, event): self.dragging = False self.last_entry.trans = False self.last_entry = None self.redraw_canvas() return False def motion_notify(self, widget, event): if not self.dragging: return height = (widget.get_allocation().height)/self.max_y selected_y = int(floor((event.y)/float(height))) - 1 print (selected_y - ((event.y)/float(height) - 1)) if self.last_entry == None: entry = CalendarEntry() entry.day = self.selected entry.starthour = selected_y entry.length = 2 entry.trans = True entry.focus = True entry.show() if self.focused is not None : self.focused.focus = False self.focused = entry entry.size_allocate(gtk.gdk.Rectangle(int(self.points_matrix[self.selected][entry.starthour][0]), int(self.points_matrix[self.selected][entry.starthour][1]), int(self.points_matrix[self.selected+1][entry.starthour][0]-self.points_matrix[self.selected][entry.starthour][0]), int(self.points_matrix[self.selected][entry.starthour+entry.length][1] - self.points_matrix[self.selected][entry.starthour][1]))) self.entries.append(entry) self.last_entry = entry self.redraw_canvas() else : if selected_y >= self.last_entry.starthour and abs(selected_y - ((event.y)/float(height) - 1)) > 0.6: length = selected_y - self.last_entry.starthour if self.last_entry.length != length: print "Change", length,self.last_entry.length self.last_entry.length = length self.last_entry.size_allocate(gtk.gdk.Rectangle(int(self.points_matrix[self.last_entry.day][self.last_entry.starthour][0]), int(self.points_matrix[self.last_entry.day][self.last_entry.starthour][1]), int(self.points_matrix[self.last_entry.day+1][self.last_entry.starthour][0]-self.points_matrix[self.last_entry.day][self.last_entry.starthour][0]), int(self.points_matrix[self.last_entry.day][self.last_entry.starthour+self.last_entry.length][1] - self.points_matrix[self.last_entry.day][self.last_entry.starthour][1]))) self.redraw_canvas() elif selected_y < self.last_entry.starthour and abs(selected_y - ((event.y)/float(height) - 1))< 0.3 : length = self.last_entry.length + self.last_entry.starthour - selected_y if self.last_entry.starthour != selected_y: print "Change", length,self.last_entry.length self.last_entry.length = length self.last_entry.starthour = selected_y self.last_entry.size_allocate(gtk.gdk.Rectangle(int(self.points_matrix[self.last_entry.day][self.last_entry.starthour][0]), int(self.points_matrix[self.last_entry.day][self.last_entry.starthour][1]), int(self.points_matrix[self.last_entry.day+1][self.last_entry.starthour][0]-self.points_matrix[self.last_entry.day][self.last_entry.starthour][0]), int(self.points_matrix[self.last_entry.day][self.last_entry.starthour+self.last_entry.length][1] - self.points_matrix[self.last_entry.day][self.last_entry.starthour][1]))) self.redraw_canvas() def redraw_canvas(self): if self.window: alloc = self.get_allocation() rect = gdk.Rectangle(alloc.x, alloc.y, alloc.width, alloc.height) self.window.invalidate_rect(rect, True) #self.queue_draw_area(alloc.x, alloc.y, alloc.width, alloc.height) #self.window.process_updates(True) import sys def main(): window = gtk.Window() clock = CalendarGrid() window.add(clock) window.connect("destroy", gtk.main_quit) window.show_all() gtk.main() if __name__ == "__main__": main()