def __init__(self, graph=None):
        '''
        '''
        
        self.graph = graph
        
        self.force_directed_graph = ForceDirectedGraph(graph=self.graph, graphical_event_manager=self)   
        self.last_generation_timestamp = None        
        
        self.display_node_labels = False
        
        self.b1_down = False
        self.b2_down = False
        self.b3_down = False
        
        self.b1_x = None
        self.b1_y = None
        
        self.mx = None
        self.my = None
        
        self.started = False
        
        self.i = 0
        
        self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.win.set_title(WIN_TITLE)
        self.gw = W_1
        self.gh = H_1 #int(float(self.gw) / 1.618)
        self.win.resize(self.gw, self.gh)
        self.win.set_position(gtk.WIN_POS_CENTER)
        self.win.connect('destroy', gtk.main_quit)
        #self.win.realize()

        self.da = gtk.DrawingArea()

        self.win.add(self.da)
        self.da.set_size_request(self.gw, self.gh)
        self.win.set_resizable(False)
        
        # REGISTER HANDLERS FOR GTK EVENTS
        
        # WINDOW / PAINT EVENTS
        # 
        self.da.connect("expose-event", self.area_expose_cb)
        
        # MOUSE EVENTS
        #
        self.da.connect("button_press_event", self.button_press_event)
        self.da.connect("button_release_event", self.button_released_event)  
        self.da.connect("motion_notify_event", self.motion_notify_event)
        #self.da.connect("scroll_event", self.scroll_event)
        
        # KEYBOARD EVENTS
        #
        self.win.connect("key-press-event", self.on_key_press_event)
        
        self.da.set_events(
            gtk.gdk.EXPOSURE_MASK 
            | gtk.gdk.BUTTON_PRESS_MASK 
            | gtk.gdk.BUTTON_RELEASE_MASK 
            | gtk.gdk.POINTER_MOTION_MASK
            | gtk.gdk.KEY_PRESS_MASK
         #   gtk.gdk.SCROLL_MASK            
            )
              
        self.da.show()
        self.win.show_all()
class GEM(object):
    '''
    graphical event manager
    framework for handling simple process driven and interactive graphics
    '''    
    
    def __init__(self, graph=None):
        '''
        '''
        
        self.graph = graph
        
        self.force_directed_graph = ForceDirectedGraph(graph=self.graph, graphical_event_manager=self)   
        self.last_generation_timestamp = None        
        
        self.display_node_labels = False
        
        self.b1_down = False
        self.b2_down = False
        self.b3_down = False
        
        self.b1_x = None
        self.b1_y = None
        
        self.mx = None
        self.my = None
        
        self.started = False
        
        self.i = 0
        
        self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.win.set_title(WIN_TITLE)
        self.gw = W_1
        self.gh = H_1 #int(float(self.gw) / 1.618)
        self.win.resize(self.gw, self.gh)
        self.win.set_position(gtk.WIN_POS_CENTER)
        self.win.connect('destroy', gtk.main_quit)
        #self.win.realize()

        self.da = gtk.DrawingArea()

        self.win.add(self.da)
        self.da.set_size_request(self.gw, self.gh)
        self.win.set_resizable(False)
        
        # REGISTER HANDLERS FOR GTK EVENTS
        
        # WINDOW / PAINT EVENTS
        # 
        self.da.connect("expose-event", self.area_expose_cb)
        
        # MOUSE EVENTS
        #
        self.da.connect("button_press_event", self.button_press_event)
        self.da.connect("button_release_event", self.button_released_event)  
        self.da.connect("motion_notify_event", self.motion_notify_event)
        #self.da.connect("scroll_event", self.scroll_event)
        
        # KEYBOARD EVENTS
        #
        self.win.connect("key-press-event", self.on_key_press_event)
        
        self.da.set_events(
            gtk.gdk.EXPOSURE_MASK 
            | gtk.gdk.BUTTON_PRESS_MASK 
            | gtk.gdk.BUTTON_RELEASE_MASK 
            | gtk.gdk.POINTER_MOTION_MASK
            | gtk.gdk.KEY_PRESS_MASK
         #   gtk.gdk.SCROLL_MASK            
            )
              
        self.da.show()
        self.win.show_all()
    
    def area_expose_cb(self, area, event):
        '''
        '''
        self.area = area
        
        self.style = self.da.get_style()
        self.gc = self.style.fg_gc[gtk.STATE_NORMAL]
        
        self.w, self.h = area.window.get_size()
        
        self.started = True

        return True                
        
    def button_press_event(self, widget, event):                    

        if (event.button == 1):                        
            
            # NOTE POSITION OF ORIGINAL CLICK
            self.b1_x = event.x
            self.b1_y = event.y
            
            self.b1_down = True
            
            # should do node selection here
            
            self.last_b1_drag_position = (event.x, event.y)
            
            self.handle_node_select_attempt(event.x, event.y)
            
            # translated x,y for button press canvaS x,y
            # check which nodes are within selection volume
            # find the closest of the nodes
            # select it
            # change node colour based on selection
            #     potentially change colour of adjacent edges, and neighbout nodes
        
        elif (event.button == 3):            
            self.b3_z = event.x
            self.b3_down = True
        
        elif (event.button == 2):
            self.b2_down = True  
        
        elif (event.button == 4):
            pass
        
        elif (event.button == 5):
            pass

        return True

    def motion_notify_event(self, widget, event):
        '''
        record mouse movement, calc deltas
        call self.force_directed_graph.move(d_x, d_y, d_z), passing deltas
        '''

        if (self.b1_down == True):
            
            (x_1_now, y_1_now) = (event.x, event.y)
            (x_0_now, y_0_now) = self.force_directed_graph.reverse(x_1_now, y_1_now, W_0, H_0, W_1, H_1)
            
            selected_nodes = [x for x in self.graph.nodes() if x.is_selected]
            
            if len(selected_nodes) > 0:            

                selected_node = selected_nodes[0]
    
                selected_node.position.x = x_0_now
                selected_node.position.y = y_0_now 
      
        elif (self.b3_down == True):
            pass

        return True
        
    def button_released_event(self, widget, event):   
        '''
        toggle status of b1/b2/b3_down
        '''             

        if (event.button == 1):
            self.b1_down = False
        elif (event.button == 2):
            self.b2_down = False
        elif (event.button == 3):
            self.b3_down = False     

        return True   

    def on_key_press_event(self, widget, event):        
        self.display_node_labels = not self.display_node_labels

    def handle_node_select_attempt(self, x1, y1):
        
        x0, y0 = self.force_directed_graph.reverse(x1, y1, W_0, H_0, W_1, H_1)
        
        # distances
        r2s = {} # r2 : Node        
        for node in self.graph.nodes():      
            # r2 = (x - mx0)^2 + (y - my0)^2      
            r2s[node] = math.pow(node.position.x - x0, 2) + math.pow(node.position.y - y0, 2)
           
        for node in r2s.keys():
            r2 = r2s[node]
            if r2 > (MINIMUM_NODE_SELECTION_RADIUS * MINIMUM_NODE_SELECTION_RADIUS):
                r2s.pop(node)
        
        closest_node = None
        if len(r2s) > 0:
            sorted_nodes = sorted(r2s.keys(), key = lambda x : r2s[x])
            closest_node = sorted_nodes[0]
        
        for node in self.graph.nodes():
            # RESET ALL OTHER NODES
            if node != closest_node:
                node.is_selected = False
            # TOGGLE SELECTION ON TRAGET NODE
            elif node == closest_node:
                node.is_selected = not node.is_selected 
                
    def time_tick_handler(self):
        '''
        only run if (self.started == True)
        
        construct gdk pixmap
        draw a white background
        call rot.iterate on pixmap
        draw pixmap to window area
        show change
        '''        
        if (self.started != True):
            return True        
        
        now = time.clock()
        
        # ------------------------------------------------------------------------
        
        # CALC POINTER POSITION
        #
        
#        (mx0, my0) = (0,0)
#        if self.mx and self.my:
#            mx0, my0 = self.force_directed_graph.reverse(self.mx, self.my, W_0, H_0, W_1, H_1)
#            print('Pointer (x0,y0) = (%i, %i), (x1,y1) = (%i, %i)' % (mx0, my0, self.mx, self.my))
        
        # ------------------------------------------------------------------------        
        
        # PERIOIDIC INTERFERENCE WITH SIMULATION - ADD/REMOVE NODE @ RANDOM
        #
        # HANDLE GENERATION ZERO
        #
        if not self.last_generation_timestamp:
            self.last_generation_timestamp = time.clock()     
               
        # HANDLE SUBSEQUENT GENERATIONS
        #
        elif now - self.last_generation_timestamp > GENERATION_INTERVAL:
            
            node_count = len(self.graph.nodes())
            
            min_node_count = DEMO_GRAPH_SIZE / 2
            max_node_count = DEMO_GRAPH_SIZE * 2
            
            # LOWER BOUND ON NODE COUNT
            if node_count <= min_node_count:
                new_node = add_node_to_graph_at_random(self.graph)
            # UPPER BOUND ON NODE COUNT
            elif node_count >= max_node_count:
                remove_node_from_graph_at_random(self.graph)
            # LAISSEZ FAIRE ZONE
            else:
                x = randint(1,2)
                if x % 2 == 0:            
                    remove_node_from_graph_at_random(self.graph)
                else:
                    new_node = add_node_to_graph_at_random(self.graph)
                self.last_generation_timestamp = now
        
        # --------------------------------------------------
        
        # print('\n'*80)
        
        # REPORT POINTER POSITION        
        #
        #print('(W0, H0) = %i, %i | (W1, H1) = %i, %i' % (W_0, H_0, W_1, H_1))
        
        # REPORT ON NODES
        #        
        #print(' ' + ('Idx').rjust(5) + ('x0').rjust(10) + ' ' + ('y0').rjust(10) + ('x1').rjust(10) + ' ' + ('y1').rjust(10))
        for tag in sorted(self.graph.nodes(), key = lambda x : x.idx):
            
            x = tag.position.x
            y = tag.position.y
            idx = tag.idx
            
            tx, ty = self.force_directed_graph.translate(x, y, W_0, H_0, W_1, H_1)
            
            selected_token = '*' if tag.is_selected else ' '
            
            #print(selected_token + ' ' + ('%i' % idx).rjust(5) + ' ' + ('%.2f' % x).rjust(10) + ' ' + ('%.2f' % y).rjust(10)+ ' ' + ('%.2f' % tx).rjust(10) + ' ' + ('%.2f' % ty).rjust(10))
        
        # construct pixmap
        #
        pixmap = gtk.gdk.Pixmap(self.da.window, self.gw, self.gh, depth=-1)
        
        # draw white background
        #
        pixmap.draw_rectangle(self.style.white_gc, True, 0, 0, self.gw, self.gh)
        
        # call rot.iterate on pixmap        
        #
        self.force_directed_graph.iterate(pixmap, self.gc, self.style, NODE_LABEL_VERT_SPACING)        
        
        # draw pixmap to window
        #
        self.area.window.draw_drawable(self.gc, pixmap, 0, 0, 0, 0, -1, -1)  
                
        # show changes
        #
        self.area.show()
      
        return True # return True => repeat