aboutsummaryrefslogblamecommitdiffstats
path: root/calendar/gui/print.c
blob: 9ae8fbf503a47a2b1d15f4b45c8d7dc3de616543 (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                                     
  
                                                                


                                                               


                                                                  



                                                                    
                                                                             







                                                        
  

   
                    
                   

      

                  
                     
                     
                 
                   
                 
 
                    
                       
 
                        



                               
                         
                      
 

                       
                                           
                                         
 
                      
                           
                            

                                    

  
                     
                            
                     

  
              
                                                                

                                                                  
 

                                         
 
                                                                 
 



                                                                      
 
                                
 
                                                    

 

                                                                 

                                                                   















                                                                      
              



                                          
                                                                              

 
















                                                                         




                                                                          



                                                                           
 

                       
                      
                          
 



                                           

                                 

                                                                              
                                                                     


                                                                           
                                                             
                                   

                                  

                                                                           
               


                                                     
                                          
 
                                                                   






                                                       

                                                               
 
                                                                     
                                                  
                                   






                                   


                                                                    

             











                                                   
                                                    
                                    
                           

  
               




                                                         

                                      
                                           
                                 

                         

                                    

                                  
                           

  
                                                                     
                                          



                                               
 
                               
 

                                                               
 
                                    








                                                                       

 



                                                                         
           





                              
 
               



                                    



                                                                      
                                                             












                                                 
                                                   
                                                                 
                                                   
 
                                                                           
                              
                                                                
 
                                                  




                                         
                                                                              




                                            
                             

                                      
 

                                   
 
                                         
                                                                     
 



                                                                     
 
                    
 
 
                                                                       




                                                                              
           
                                                 









                                                          
 
                                                               
 
                        

                                                                
                                                                       
 
                                           
 
                                              


                                                             
 

                                           
 
                                               
                                                                                     
 
                                           




                                                            


                                           
                                    

                                                              
                                           
 
                                              


                                                             
 

                                           
 
                                               


                                                              
 
                                           

                                                   
                                                      
                                  
         
 
                           

 
                                                                       

                                                     
           
                                      







                                     
 


                                               


           
                                  





                                 
 


                                                 

 
           
                                          






                                
 

                                                                    

                        
                                                  



                                                    
 
 




                                                                            









                                                








                                                                          
                                         










                                                                         
                                                                         
                                                                              
                                                                    
              
                                          







                                            
 

                                         

                    

                                                                 
 


                                                         
 


                                                                      
                                                                                      

                                                            


                                                       


                                                                           
 
                                       
                                   
                                                       
                        
 

                                           
 

                                             
 
                          
 
                           
 


                                                    

 
                                                                                  

                                                                              

                                     
                                       
                              
                                     
                       


                       
 

                                

                                       

 
                                                 







                                               
 
                                   
                  
 
                                                                          
                                                                        
                                           
 
                 

 


                                                                           






                                              










                                                                                   
           








                                       
 
                     
 


                                                                               


                 

                                                                                     
 



                            



                                                                   
 



                                                                       
 
                                 
 







                                 
                              











                                                                   
              



                           
 
                                         
 
                                   
                                            


                                         

                                                                       

                                 
                                                      

                                                   
                                                                               
                                                   

                                
                                                                   

                                                   
         
                                                        
                                   
 

                                  


                      
               



                                   
 
 
                                                                             
 
                      
 


                     











                                                        

              

                                                 






                                                                            

                                                          
                                                                      

                                                                   



                                                

                                                            







                                                                           



                                                           










                                            
 
                           
                                                             
                         
                         
                  
                 
                      

                                    
                       
                     


                                                                         
                              
                    
 


                                                

                                                
                                                                            

                                                      
 
                                           
 


                                                                               


                                                               
                                           
 
                          
                                                             
 
                                                                      
                                       
                                         

                                                                             
                                                               
                               
 
                            

                                                                            
 

                                                                         
 
                                                                        

                                                                        
                                                                             
                                        
 
                                                           
                                           

                                                                     
                                                                

                                 


                                                                

                                                                          
                                                   
                                                       

         
                               
 
                                                       

                                 
                                               
                                                    
 


                                                                            


                                                                            










                                                                  
                                                  
                                          
 
                                                                             
                                                                        
                                                         
 




                                                                           




                                                                         
 




                                                               


                         

                                         
                                                                                  
                                                                            


                                                                            




                                                                  
                                                       
                                                         

                                                                                          



                                                                             
 
                                                                       
 
                                                                             
                                                                       
                                                                                




                                                                      
                                 



                                                                              
 



                                           

                                                  

 
                                                                                  
              

                                       








                                     
 

                                         
                    




                                                                 
                                                  
                                                                           



                                                                     

                                                                      


                                                            


                                            
 


                                                         
 

                                                         
 

                                               
                                                               








                                                     



                                                          

 

                                                                       







                                               
 
                         
                                                      


                                                                           

                            
                             
                          
                                                 
                    
 

                                                                 
 
                                                 
                                                                           

                                                     
                                                           
 
                                           
                                                                    
 
                                                                     
                                                           



                                                 
 
                                       



                                                                          
 
                                      
                               
                                     
                                                        
                                                                          

                                
                                    
                                                          
                                                                              

                                                                    
                         

                                          
                                                               
                                                                

                
                                                             
                                                           
                                           
                                                                   
                                                   
 




                                      
                                                 
                            
                                                 




                                          
 

                                           







                                                                       
 
                                                                            
                                            
                                                                   



                                             
 
                                                                         
                                                                      



                                                                     
                      
         
 

                                                  
 
 

                                                                          
                                                   






                                         

 
























                                                                        
                      













                                                                             
                                                                       


                                                                            

                                                                           




                                                             





                                                                      
                                           
                                                


                                     
               



                                          
 

                                                                                        
 



                                                            
 
                    


           
                                
 





                                                                         
                                                                             
         
 

                                    
 
                    

                                                
                         












                                                                          
                    

                                            
                         












                                                                                      










                                            













                                                                      
                                         






                                                                    






                                                                                        












                                                                  


                                                                          








                                                       
 
              


                                                   
                    



                                                       

                             


                                                         
                                                                      
                
                                          




                    
           









                                                 
 

                                                                        

                         
                          
                                 
 


                                        
                                                                         

                                                                          



                                                                     
                       




                                            

                                                            
                                  

                                                              



                                                               

                                                                            
                                   












                                                                             
                                                                                           
         
 
                                                                          
                                            












                                                                             
                                                                                            
         
 
                             
                                                                      
 

                
                                                                                        

                      
 
 
           








                                            
 
                                                      

                                                          
                                       
                          
                                 
 


                                        


                                                               
 

                                                                      
 



                                                       

                                                                           
 


                                                               
 


                                                                        


                                                
     


                                                                                        
      
 
                                  

                                                              
                                                                          
 
                                                                      
 
                            

                                






                                                           
 

                                                                             
 

                                                         
 

                                                                             
 
                         


                                                        
 
                           
         
 
                                                                                        
 
                      

 
           






                                            
 

                           
                             
                                   
                          
                                  
                                                       
                                         
                    
                                 

                                        
 

                                                
 

                                                        





                                                                             


                                                                     
                               

                                                                             

                                                      

                                                                            

                                             
                                                                                            





                                                                    
 

                                                     

                                       
                                                                         













                                                                                              
                                                                                     
         
 
                                                                       


                                                      
 
                                    
                                                           
 
                                                                            

                                                                          
                                                                  



                                                            
 

                                                           
 

                                                                               



                                                                  
         
 



                                                                            
 
                              
                                                                      

                                                                                      
                                                             
                                   
                                                                        
 
                                                                    
                 


                                                                           
                                                                                         
                                                                                        


                                                              





                                                                           
         
 


                                      
                                                     
                                                           
 
                                           




                                                                             
 
                                                          


                                                        
 
                                                               


                                            

                                                       
 
                                                       


                                                                     


                                                                        
 
                                     
                         
                                                                         
            
                                                                         
                                                              
                                                                  
 

                                                                         


                                                                
         
 
                              

                                        
                                           
                                           




                                             


                                                                           

                                                      












                                                             
           












                                                  
 
                                                                        
                          
                         

                                                                       
                                                



                                                                   
                       


                                                                           
                                     
                                                                                    
                                                           
 


                
                                                                            
                                   











                                                                             



                                                                
         
 
                                                                          
                                            











                                                                             



                                                                 

         


                                                                                                     

 
           












                                                 

                          
                         










                                                                     



                                                                                         
 


                                                         
 


                                                                             


                                                                   
         
 


                                                       

 
           









                                             


                                 

                                                                      

                                 
                                 
 


                                        
                                                                      




                                                                      

                                                          




























                                                                        

                                                             

                                                         
                                                                     
 


                                   

                                                                              

                                                                            



                                                                             
                                



                                                                             
                         


                                                                                    






                                                              
 

                                                         
                                                                              

                                                                                                  





                                                                   
 




                                                                           



                                                                                                         



                                      
                                                                              

                                                                                              
                                                                     
                                           
                                                                                



                                                                            

                                                                    
 




                                                                                     

                 


                                        

                      

 
           

                                                       




                                                
 
                     
                                      
                                                     

                                   
                    
 
                                         








                                                                           

                                               
 
                                                                                  

                                                                          
                                                                           



                                                                      
                                                                       






                                                           
                                                                           
 





                                                                 



                                                           

                                                                              


                                                                          



                                                      
                                                                              
 


                                                                      


         


                                                                          


                                     

 

                                             
                                                                                        
                                                              
 
                                                   

     



                                                                     

      


                                                                              
 

                                                                           
 
                                                          














                                                                  

           






                                             
                                                 



                                   
 
                           
                              
                                  


                                                                     
                                                     
                                        



                                                





                                                                         
                        

                                        
                            
                                                                                

                                            
                                                                            
 
                                                                           

                                                                         

                                                                               
 
                                                            

                                                     
                                                                        


                                             

                                             

                                                                  


                                                                      

                                






                                              



                                                                             
                                                                 

                                                
                                                 
         
 
                                                                            
                                             




                                                                        
                                       
                                                                            
                                       
 
                                                      
                                                                                        


                                                          

                                                      
                               
                                                                  

                                                                               


                                                                      

         
                                           



                                                                               
                                                  




                                        
           






                                              
 
                           
                    
                     
                               
                          
                         
                                   
                                  


                                        
                                                      
 



                                                                    
 





                                                                     


                                         



                                                                                   


                                                                              
 


                                                            



                                      
                                           

                                                                 

                                                                         
                                 

                                                                 


                                                            

                                                                         
                                                         
                                                               

                              

                                                                             


                                                                           
                                                     

                                          
                                                                             
                                         



                                              
                                   

                                             









                                                                           



                                             
                                                                  
                                                                                                  

                             
                                                  
         
                                           

                 



                                                               


           







                                             
 
                                           
                                 
                                
                              
                      
                         
                       
                    

                                                                           
                                                  



                                                                   
 
                                                                   
 
                                                           



                                           
 


                                                                      
 
                

                          

                                                               
                                              

                                          
                               
 
                                                                   


                                                                            
 
                                              

                                                                             
 
                                                             

                                              
                                 
                 

                         
                                 

                                              
                              
                 
 
                                                       
                                                                               

                                                                       
                                                          
                         
                                                               
 
                                                                           





                                                           

                 


                                                                      
 
                                                      
                                                                   



                                             

                                      
         
 
                                                   

 
           


                                         
 
                         
                            
                           
                         
                                                             
                       
                              



                                                




                                                                         

                                                                                  

                                    
                                    
 
                                                                 



                                                           
 
                                                        



                                                       
 
                                                                


                                                          
 
                                                       


                                                                    
 



                                                                                          
 
                                                                                




                                                                                          
 
                                                           

                                                                    
                                       
 



                                                       

                                                    
                                                          
 




                                                       
 
                                                              
          

 
           







                                                     
 
                         






                                                                           
                                         




                                               

                                                                 
 





















                                                                             







                                                                          
                                     



                                                                          
                                    

                                                                              

                                                                    
                         

                                       
                                                               
                                                                

                

                                                           




















                                                                   







                                                                       

                                  







                                                                       
 








                                                                            
                                                                      



                                                                     
                      


                                                  
                                                                
                                                   




















                                                                                           







                                                      
 

                           


                                   
                                  
                                                       





                                         

                                                








                                                                             


                                                                     
                               

                                                                             

                                                      
                                                                            
                                                    

                                             
                                                                                            





                                                                    























                                                                                              
                                                                                     


                                                                       


                                                      




                                                                            

                                                                          
                                                                  



                                                            

















                                                                               
                                                                      

                                                                                      
                                                             
                                   
                                                                        






                                                                                         
                                                                                        





























                                                                             


                                                                     


                                                                        

                                     
                         
                                                                         
            
                                                                         
                                                              



                                                                         


                                                                













                                                



                                             


                                                                                        

                               
                                                                      

                                                         
                                                                    
                                                      
                                                                               




                    


                                               

                            
                           
                                


                                  
                         

                                         
                                  
                     
                                 
                         
 


                                                




                                                                         


                                                                             




                                                          

                                                                     
                        
 
                                                                                               
 



                                                                        



                                                                         

                                                                             
 



                                                                             
 
                                                                   




                                                                             

                                                                   

                                                                       



                                               


                                                                       


                                                                       



                                               

                                         
                                                                   
                     
                                    


                                                                   

                                                             
 



                                                                   
 



                                                     




                                                              


                                          
 
                            

                           
                                                       
                       
                    

                                    
                     





                                                                         

                                                                                  
 




                                                                


                                                            
 
                                                                              
                                              
                                              




                                                                       

                                       




                                                           
 
                                                    


                                                    

                                                      


                                                         

                                               
                                                                                
                                      




                                                                                   
 
                                                                        





                                                                                   

                                                                   

                                                                       


                                                                      
 
                                                                  
                                                      

                                                                       


                                                                      

 
           


                                           
 
                         
                            
                           
                       
                              
                                                       



                                                

                                                           
 

                                                                         

                                                                                  

                                        
                                                                                     

                                                 

                                                                              
                                                                           

                                               










                                                                                                              

                                               

                                                            



                                                 
 

 
               


                              


                      
                                                    






                                         
           
                            


                                               



                                      
 
                         
                 
 
                                                      
 



                                       
                                                              


                                                         
            


                                                           



                                       


                                       



                                                                             
                                                       
                                                                  


                                     







                                                                             
                 




                    
           

                                           
                                     





                                              
 
                                                                        
                                       
                                                         
                                  
 
                                                      

                                                                        
                                                           
                                          


                                                     
         
                                                  
 
                                                    

                                                                      
                                                         
                                        


                                                   
         
                                                  
 
                                                  

                                                                      
                                                         
                                        

                                                   
         
                                                  
 
                                                              
                             
                                                                  

                                                         
                                                                   
         



                         


                                                              
 
                                   

                                                              

                                                                            
                                                       
         

                           
                                

                                                                         


                                                                               
                                                                      
                        

                                                                           

                                                                                      
                                                                     
                 
         
 
                                       
                                

                                                                    

                                                                                  
                                                                
                        

                                                                      


                                                                          
                                                               
                 

         


                                                  


           









                                                                            

                                                                                  






                                                                              
                                               
         


    

                                               







                                                    



                                                                     


                                         



                                                                                   


                                                                              
 


                                           
 










                                                                  
                                             
                                                         


                 
                                            




                                                       
                          







                                                                


                                                          


                                               
 
                            
                                   
                           
                            

                                 
                         

                                    

                                                                    
                            
                    
                                          
                    
                       




                                                                         
 

                  

                                                         






                                                 
                                                               
                                                              
           

                             
 
                                                 
 
                                                                             
                                           
                                         
                                               
                                  

                                                  
            
                             
 
                                                           
 
                                                              
                                                         
                         
 
                           

                                                              
                                  

                                                                             




                                                   
 
                     
                                                         
                                                  
                                                                        


                                                                   
 




                                                       





                                                                            

                                         
 
                              
                         



                                                           
                  

                       
                                                                     


                                                                        

                                                                   


                                                            




                                                
                                                           
 
                                                                            
                                            
                                           



                                                  
 
                            
                                                           


                                                     
                                                                 

                                                   
                                                                 

                                                   
                                                               

                                                   
                                                              





                                            





                                                                                             
                                                                
                                                     
                         
                 
 
                              
                                                               
                                                 
                                        
 






                                                                           
                                                        
                                          

                 


                                                                 
                                      
                                                             
                              
                                              
 
                                                                                               
                                                               
 


                                                                                     
                                                        

                 
                         
                                                     
                                    


                                                                         
 


                                                                                    
                                                        

                                            
         
 
                        
                                                           
                                          




                                                                            
                                                

                                           
 
                      
                                                               
                           
                                                                   
                                                                    
                                                          




                                                                 
                                                              
 


                                                                            
                                                

                                               
                  

                         
                                                           
                                                
                                                   
                                              



                                                                           




                                                                  

                                        
                                            


                                                         
                 
 
         
 
                                              
                                           
 













                                                                

                                                 





                                                                   
 
 
    
                                
                                   


                                           
 

                                     
 
                                                     
 
                        
                                

                                                    
 
                                             
                                                       
 
                          



                                                           

                                                         
 
                                                                
 
                                   

 
           


                                      
 

                                   
                    
 
                                                           
 
                                                                           
 




                                                                              
 
                        
 

                                             

                                      
                           
 
                                
 

                                           
 




                            



                                                    
                                               

                            
                      
 
                                                           
 
                                                                       
 

                                                                 
                                                                 
 
                                                            
                                                
                                                                           
 


                                                          
 
 
    



                                            
 

                                     
                                
 


                                                  
 

                                                       
 



                                                     

                                       
                                                          
 
                                                                
 
                                   
 
/*
 * Evolution calendar - Print support
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) version 3.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with the program; if not, see <http://www.gnu.org/licenses/>
 *
 *
 * Authors:
 *      Michael Zucchi <notzed@ximian.com>
 *      Federico Mena-Quintero <federico@ximian.com>
 *      Damon Chaplin <damon@ximian.com>
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "print.h"

#include <sys/stat.h>
#include <sys/time.h>
#include <math.h>
#include <string.h>
#include <time.h>

#include <gtk/gtk.h>
#include <glib/gi18n.h>

#include "e-cal-model.h"
#include "e-day-view.h"
#include "e-day-view-layout.h"
#include "e-week-view.h"
#include "e-week-view-layout.h"
#include "e-task-table.h"
#include "gnome-cal.h"

#include "art/jump.xpm"

typedef struct PrintCompItem PrintCompItem;
typedef struct PrintCalItem PrintCalItem;

struct PrintCompItem {
    ECalClient *client;
    ECalComponent *comp;
    icaltimezone *zone;
    gboolean use_24_hour_format;
};

struct PrintCalItem {
    GnomeCalendar *gcal;
    time_t start;
};

static gdouble
evo_calendar_print_renderer_get_width (GtkPrintContext *context,
                                       PangoFontDescription *font,
                                       const gchar *text)
{
    PangoLayout *layout;
    gint layout_width, layout_height;

    layout = gtk_print_context_create_pango_layout (context);

    pango_layout_set_font_description (layout, font);
    pango_layout_set_text (layout, text, -1);
    pango_layout_set_indent (layout, 0);
    pango_layout_get_size (layout, &layout_width, &layout_height);

    g_object_unref (layout);

    return pango_units_to_double (layout_width);
}

static gdouble
evo_calendar_print_renderer_get_height (GtkPrintContext *context,
                                        PangoFontDescription *font,
                                        const gchar *text)
{
    PangoLayout *layout;
    gint layout_width, layout_height;

    layout = gtk_print_context_create_pango_layout (context);

    pango_layout_set_font_description (layout, font);
    pango_layout_set_text (layout, text, -1);
    pango_layout_set_indent (layout, 0);
    pango_layout_get_size (layout, &layout_width, &layout_height);

    g_object_unref (layout);

    return pango_units_to_double (layout_height);
}

static gdouble
get_font_size (PangoFontDescription *font)
{
    g_return_val_if_fail (font, 0.0);

    return pango_units_to_double (pango_font_description_get_size (font));
}

static gint
get_day_view_time_divisions (void)
{
    GSettings *settings;
    gint time_divisions;

    settings = g_settings_new ("org.gnome.evolution.calendar");

    time_divisions = g_settings_get_int (settings, "time-divisions");
    if (time_divisions < 5 || time_divisions > 30)
        time_divisions = 30;

    g_object_unref (settings);

    return time_divisions;
}

/*
 * Note that most dimensions are in points (1/72 of an inch) since that is
 * what gnome-print uses.
 */

/* GtkHTML prints using a fixed margin. It has code to get the margins from
 * gnome-print keys, but it's commented out. The corresponding code here
 * doesn't seem to work either (getting zero margins), so we adopt
 * gtkhtml's cheat. */

#define TEMP_MARGIN .05

/* The fonts to use */
#define FONT_FAMILY "Sans"

/* The font size to use for normal text. */
#define DAY_NORMAL_FONT_SIZE    12
#define WEEK_NORMAL_FONT_SIZE   12
#define MONTH_NORMAL_FONT_SIZE  8
#define WEEK_EVENT_FONT_SIZE    9
#define WEEK_SMALL_FONT_SIZE    8

/* The height of the header bar across the top of the Day, Week & Month views,
 * which contains the dates shown and the 2 small calendar months. */
#define HEADER_HEIGHT       80

/* The width of the small calendar months, the space from the right edge of
 * the header rectangle, and the space between the months. */
#define MIN_SMALL_MONTH_WIDTH   120
#define SMALL_MONTH_PAD     5
#define SMALL_MONTH_SPACING 20

/* The minimum number of rows we leave space for for the long events in the
 * day view. */
#define DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY    2

/* The row height for long events in the day view. */
#define DAY_VIEW_ROW_HEIGHT     14

#define CALC_DAY_VIEW_ROWS(divis)       ((60 / divis) * 24)

/* The width of the column with all the times in it. */
#define DAY_VIEW_TIME_COLUMN_WIDTH  36

/* The space on the right of each event. */
#define DAY_VIEW_EVENT_X_PAD        8

/* Allowance for small errors in floating point comparisons. */
#define EPSILON         0.01

/* The weird month of September 1752, where 3 Sep through 13 Sep were
 * eliminated due to the Gregorian reformation. */
static const gint sept_1752[42] = {
     0,  0,  1,  2, 14, 15, 16,
    17, 18, 19, 20, 21, 22, 23,
    24, 25, 26, 27, 28, 29, 30,
     0,  0,  0,  0,  0,  0,  0,
     0,  0,  0,  0,  0,  0,  0,
     0,  0,  0,  0,  0,  0,  0
};
#define SEPT_1752_START 2       /* Start day within month */
#define SEPT_1752_END 20        /* End day within month */

struct pdinfo
{
    gint days_shown;
    time_t day_starts[E_DAY_VIEW_MAX_DAYS + 1];

    GArray *long_events;
    GArray *events[E_DAY_VIEW_MAX_DAYS];

    gint start_hour;
    gint end_hour;
    gint start_minute_offset;
    gint end_minute_offset;
    gint rows;
    gint mins_per_row;
    guint8 cols_per_row[CALC_DAY_VIEW_ROWS (1)];
    gboolean use_24_hour_format;
    icaltimezone *zone;
};

struct psinfo {
    gint days_shown;
    time_t day_starts[E_WEEK_VIEW_MAX_WEEKS * 7 + 1];

    GArray *events;

    gint rows_per_cell;
    gint rows_per_compressed_cell;
    GDateWeekday display_start_weekday;
    gboolean multi_week_view;
    gint weeks_shown;
    gint month;
    gboolean compress_weekend;
    gboolean use_24_hour_format;
    gdouble row_height;
    gdouble header_row_height;
    icaltimezone *zone;
};

/* Convenience function to help the transition to timezone functions.
 * It converts a time_t to a struct tm. */
static void
convert_timet_to_struct_tm (time_t time,
                            icaltimezone *zone,
                            struct tm *tm)
{
    struct icaltimetype tt;

    /* Convert it to an icaltimetype. */
    tt = icaltime_from_timet_with_zone (time, FALSE, zone);

    /* Fill in the struct tm. */
    tm->tm_year = tt.year - 1900;
    tm->tm_mon = tt.month - 1;
    tm->tm_mday = tt.day;
    tm->tm_hour = tt.hour;
    tm->tm_min = tt.minute;
    tm->tm_sec = tt.second;
    tm->tm_isdst = tt.is_daylight;

    tm->tm_wday = time_day_of_week (tt.day, tt.month - 1, tt.year);
}

/* Fills the 42-element days array with the day numbers for the specified
 * month.  Slots outside the bounds of the month are filled with zeros.
 * The starting and ending indexes of the days are returned in the start
 * and end arguments. */
static void
build_month (ECalModel *model,
             gint month,
             gint year,
             gint *days,
             gint *start,
             gint *end)
{
    gint i;
    gint d_month;
    gint d_week;
    GDateWeekday weekday;
    GDateWeekday week_start_day;

    /* Note that months are zero-based, so September is month 8 */

    if ((year == 1752) && (month == 8)) {
        memcpy (days, sept_1752, 42 * sizeof (gint));

        if (start)
            *start = SEPT_1752_START;

        if (end)
            *end = SEPT_1752_END;

        return;
    }

    for (i = 0; i < 42; i++)
        days[i] = 0;

    d_month = time_days_in_month (year, month);
    /* Get the start weekday in the month, 0=Sun to 6=Sat. */
    d_week = time_day_of_week (1, month, year);

    /* Get the configuration setting specifying which weekday we put on
     * the left column. */
    week_start_day = e_cal_model_get_week_start_day (model);

    weekday = e_weekday_from_tm_wday (d_week);

    for (i = 0; i < d_month; i++)
        days[d_week + i] = i + 1;

    if (start)
        *start = e_weekday_get_days_between (week_start_day, weekday);

    if (end)
        *end = d_week + d_month - 1;
}

static PangoFontDescription *
get_font_for_size (gdouble height,
                   PangoWeight weight)
{
    PangoFontDescription *desc;
    gint size;

    #define MAGIC_SCALE_FACTOR (0.86)
    size = pango_units_from_double (height * MAGIC_SCALE_FACTOR);

    desc = pango_font_description_new ();
    pango_font_description_set_size (desc, size);
    pango_font_description_set_weight (desc, weight);
    pango_font_description_set_family_static (desc, FONT_FAMILY);

    return desc;
}

/* Prints a rectangle, with or without a border, filled or outline, and
 * possibly with triangular arrows at one or both horizontal edges.
 * width      = width of border, -ve means no border.
 * red,green,blue = bgcolor to fill,   -ve means no fill.
 * left_triangle_width, right_triangle_width = width from edge of rectangle to
 *        point of triangle, or -ve for no triangle. */
static void
print_border_with_triangles (GtkPrintContext *pc,
                             gdouble x1,
                             gdouble x2,
                             gdouble y1,
                             gdouble y2,
                             gdouble line_width,
                             gdouble red,
                             gdouble green,
                             gdouble blue,
                             gdouble left_triangle_width,
                             gdouble right_triangle_width)
{
    cairo_t *cr = gtk_print_context_get_cairo_context (pc);

    cairo_save (cr);

    /* Fill in the interior of the rectangle, if desired. */
    if (red >= -EPSILON && green >= -EPSILON && blue >= -EPSILON) {

        cairo_move_to (cr, x1, y1);

        if (left_triangle_width > 0.0)
            cairo_line_to (
                cr, x1 - left_triangle_width,
                (y1 + y2) / 2);

        cairo_line_to (cr, x1, y2);
        cairo_line_to (cr, x2, y2);

        if (right_triangle_width > 0.0)
            cairo_line_to (cr, x2 + right_triangle_width, (y1 + y2) / 2);

        cairo_line_to (cr, x2, y1);
        cairo_close_path (cr);
        cairo_set_source_rgb (cr, red, green, blue);
        cairo_fill (cr);
        cairo_restore (cr);
        cairo_save (cr);
    }

    /* Draw the outline, if desired. */
    if (line_width >= EPSILON) {

        cr = gtk_print_context_get_cairo_context (pc);
        cairo_move_to (cr, x1, y1);

        if (left_triangle_width > 0.0)
            cairo_line_to (
                cr, x1 - left_triangle_width,
                (y1 + y2) / 2);

        cairo_line_to (cr, x1, y2);
        cairo_line_to (cr, x2, y2);

        if (right_triangle_width > 0.0)
            cairo_line_to (
                cr, x2 + right_triangle_width,
                (y1 + y2) / 2);

        cairo_line_to (cr, x2, y1);
        cairo_close_path (cr);
        cairo_set_source_rgb (cr, 0, 0, 0);
        cairo_set_line_width (cr, line_width);
        cairo_stroke (cr);
    }

    cairo_restore (cr);
}

/* Prints a rectangle, with or without a border, and filled or outline.
 * width      = width of border, -ve means no border.
 * fillcolor = shade of fill,   -ve means no fill. */
static void
print_border_rgb (GtkPrintContext *pc,
                  gdouble x1,
                  gdouble x2,
                  gdouble y1,
                  gdouble y2,
                  gdouble line_width,
                  gdouble red,
                  gdouble green,
                  gdouble blue)
{
    print_border_with_triangles (
        pc, x1, x2, y1, y2, line_width,
        red, green, blue, -1.0, -1.0);
}

static void
print_border (GtkPrintContext *pc,
              gdouble x1,
              gdouble x2,
              gdouble y1,
              gdouble y2,
              gdouble line_width,
              gdouble fillcolor)
{
    print_border_rgb (
        pc, x1, x2, y1, y2, line_width,
        fillcolor, fillcolor, fillcolor);
}

static void
print_rectangle (GtkPrintContext *context,
                 gdouble x,
                 gdouble y,
                 gdouble width,
                 gdouble height,
                 gdouble red,
                 gdouble green,
                 gdouble blue)
{
    cairo_t *cr = gtk_print_context_get_cairo_context (context);

    cairo_save (cr);

    cairo_rectangle (cr, x, y, width, height);
    cairo_set_source_rgb (cr, red, green, blue);
    cairo_fill (cr);

    cairo_restore (cr);
}

/* Recreate the layout by shrinking the text string if we have a line that's
 * too high.
 */
static PangoLayout *
shrink_text_to_line (PangoLayout *layout,
                     gint layout_width,
                     gint layout_height,
                     GtkPrintContext *context,
                     PangoFontDescription *desc,
                     const gchar *text,
                     PangoAlignment alignment,
                     gdouble x1,
                     gdouble x2,
                     gdouble y1,
                     gdouble y2)
{
    gint new_length;

    if (layout_width == 0 || x2 - x1 < EPSILON)
        return layout; /* Do nothing */

    new_length = (gint) floor (pango_units_from_double (x2 - x1) /
            (gdouble) layout_width * (gdouble) strlen (text));

    if (new_length < strlen (text)) {
        g_object_unref (layout); /* Destroy old layout */
        layout = gtk_print_context_create_pango_layout (context);

        pango_layout_set_font_description (layout, desc);
        pango_layout_set_alignment (layout, alignment);
        pango_layout_set_text (layout, text, new_length);
    }

    return layout;
}

/* Prints 1 line of aligned text in a box. It is centered vertically, and
 * the horizontal alignment can be either PANGO_ALIGN_LEFT, PANGO_ALIGN_RIGHT,
 * or PANGO_ALIGN_CENTER. Text is truncated if too long for cell. */
static gdouble
print_text_line (GtkPrintContext *context,
                 PangoFontDescription *desc,
                 const gchar *text,
                 PangoAlignment alignment,
                 gdouble x1,
                 gdouble x2,
                 gdouble y1,
                 gdouble y2,
                 gboolean shrink)
{
    PangoLayout *layout;
    gint layout_width, layout_height;
    cairo_t *cr;

    cr = gtk_print_context_get_cairo_context (context);
    layout = gtk_print_context_create_pango_layout (context);

    pango_layout_set_font_description (layout, desc);
    pango_layout_set_alignment (layout, alignment);
    pango_layout_set_text (layout, text, -1);

    /* Grab the width before expanding the layout. */
    pango_layout_get_size (layout, &layout_width, &layout_height);

    if (shrink && layout_width > pango_units_from_double (x2 - x1)) /* Too wide */
        layout = shrink_text_to_line (
            layout, layout_width, layout_height,
            context, desc, text, alignment,
            x1, x2, y1, y2);

    pango_layout_set_width (layout, pango_units_from_double (x2 - x1));

    cairo_save (cr);

    /* Set a clipping rectangle. */
    cairo_move_to (cr, x1, y1);
    cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1);
    cairo_clip (cr);

    cairo_new_path (cr);
    cairo_set_source_rgb (cr, 0, 0, 0);

    cairo_move_to (cr, x1, y1);
    pango_cairo_show_layout (cr, layout);

    cairo_stroke (cr);

    cairo_restore (cr);

    g_object_unref (layout);

    return pango_units_to_double (layout_width);
}

/* Prints 1 or more lines of aligned text in a box. It is centered vertically, and
 * the horizontal alignment can be either PANGO_ALIGN_LEFT, PANGO_ALIGN_RIGHT,
 * or PANGO_ALIGN_CENTER. */
static double
print_text (GtkPrintContext *context,
            PangoFontDescription *desc,
            const gchar *text,
            PangoAlignment alignment,
            gdouble x1,
            gdouble x2,
            gdouble y1,
            gdouble y2)
{
    return print_text_line (
        context, desc,
        text, alignment,
        x1, x2, y1, y2, FALSE);
}

/* gets/frees the font for you, as a bold font */
static gdouble
print_text_size_bold (GtkPrintContext *context,
                      const gchar *text,
                      PangoAlignment alignment,
                      gdouble x1,
                      gdouble x2,
                      gdouble y1,
                      gdouble y2)
{
    PangoFontDescription *font;
    gdouble w;

    font = get_font_for_size (ABS (y2 - y1) * 0.5, PANGO_WEIGHT_BOLD);
    w = print_text (context, font, text, alignment, x1, x2, y1, y2);
    pango_font_description_free (font);

    return w;
}

/* gets/frees the font for you, as a bold font - absolute size parameter */
static double
print_text_abs_bold (GtkPrintContext *context,
                     const gchar *text,
                     gdouble font_size,
                     PangoAlignment alignment,
                     gdouble x1,
                     gdouble x2,
                     gdouble y1,
                     gdouble y2)
{
    PangoFontDescription *font;
    gdouble w;

    font = get_font_for_size (font_size, PANGO_WEIGHT_BOLD);
    w = print_text_line (context, font, text, alignment, x1, x2, y1, y2, TRUE);
    pango_font_description_free (font);

    return w;
}

static void
titled_box (GtkPrintContext *context,
            const gchar *text,
            PangoFontDescription *font,
            PangoAlignment alignment,
            gdouble *x1,
            gdouble *y1,
            gdouble *x2,
            gdouble *y2,
            gdouble linewidth)
{
    gdouble size;

    size = evo_calendar_print_renderer_get_height (context, font, text);
    print_border (context, *x1, *x2, *y1, *y1 + size  + 2, linewidth, 0.9);
    print_border (context, *x1, *x2, *y1 + size + 2, *y2, linewidth, -1.0);
    *x1 += 2;
    *x2 -= 2;
    *y2 += 2;
    print_text (context, font, text, alignment, *x1, *x2, *y1 + 1.0, *y1 + size);
    *y1 += size + 2;
}

static gboolean
get_show_week_numbers (void)
{
    GSettings *settings;
    gboolean show_week_numbers;

    settings = g_settings_new ("org.gnome.evolution.calendar");

    show_week_numbers =
        g_settings_get_boolean (settings, "show-week-numbers");

    g_object_unref (settings);

    return show_week_numbers;
}

enum datefmt {
    DATE_MONTH  = 1 << 0,
    DATE_DAY    = 1 << 1,
    DATE_DAYNAME    = 1 << 2,
    DATE_YEAR   = 1 << 3
};

static const gchar *days[] = {
    N_("1st"), N_("2nd"), N_("3rd"), N_("4th"), N_("5th"),
    N_("6th"), N_("7th"), N_("8th"), N_("9th"), N_("10th"),
    N_("11th"), N_("12th"), N_("13th"), N_("14th"), N_("15th"),
    N_("16th"), N_("17th"), N_("18th"), N_("19th"), N_("20th"),
    N_("21st"), N_("22nd"), N_("23rd"), N_("24th"), N_("25th"),
    N_("26th"), N_("27th"), N_("28th"), N_("29th"), N_("30th"),
    N_("31st")
};

/*
  format the date 'nicely' and consistently for various headers
*/
static gchar *
format_date (struct tm *tm,
             gint flags,
             gchar *buffer,
             gint bufflen)
{
    GString *fmt = g_string_new ("");

    if (flags & DATE_DAYNAME) {
        g_string_append (fmt, "%A");
    }
    if (flags & DATE_DAY) {
        if (flags & DATE_DAYNAME)
            g_string_append (fmt, " ");
        g_string_append (fmt, gettext (days[tm->tm_mday - 1]));
    }
    if (flags & DATE_MONTH) {
        if (flags & (DATE_DAY | DATE_DAYNAME))
            g_string_append (fmt, " ");
        g_string_append (fmt, "%B");
        if ((flags & (DATE_DAY | DATE_YEAR)) == (DATE_DAY | DATE_YEAR))
            g_string_append (fmt, ",");
    }
    if (flags & DATE_YEAR) {
        if (flags & (DATE_DAY | DATE_DAYNAME | DATE_MONTH))
            g_string_append (fmt, " ");
        g_string_append (fmt, "%Y");
    }
    e_utf8_strftime (buffer, bufflen, fmt->str, tm);
    buffer[bufflen - 1] = '\0';

    g_string_free (fmt, TRUE);

    return buffer;
}

static gboolean
instance_cb (ECalComponent *comp,
             time_t instance_start,
             time_t instance_end,
             gpointer data)
{

    gboolean *found = ((ECalModelGenerateInstancesData *) data)->cb_data;

    *found = TRUE;

    return FALSE;
}

const gchar *daynames[] = {
    /* Translators: These are workday abbreviations,
     * e.g. Su=Sunday and Th=thursday */
    /* G_DATE_BAD_WEEKDAY */ "",
    /* G_DATE_MONDAY      */ N_("Mo"),
    /* G_DATE_TUESDAY     */ N_("Tu"),
    /* G_DATE_WEDNESDAY   */ N_("We"),
    /* G_DATE_THURSDAY    */ N_("Th"),
    /* G_DATE_FRIDAY      */ N_("Fr"),
    /* G_DATE_SATURDAY    */ N_("Sa"),
    /* G_DATE_SUNDAY      */ N_("Su")
};

static gdouble
calc_small_month_width (GtkPrintContext *context,
                        gdouble for_height)
{

    PangoFontDescription *font_bold;
    gdouble res = 0.0;
    gint ii;

    font_bold = get_font_for_size (for_height / 7.4, PANGO_WEIGHT_BOLD);
    res = MAX (evo_calendar_print_renderer_get_width (
        context, font_bold, "23"), res);
    for (ii = G_DATE_MONDAY; ii < G_N_ELEMENTS (daynames); ii++) {
        res = MAX (evo_calendar_print_renderer_get_width (
            context, font_bold, _(daynames[ii])), res);
    }

    pango_font_description_free (font_bold);

    /* res is max cell width, so multiply it with column
     * count plus some space between columns. */
    res = (res + 1.0) * (7 + (get_show_week_numbers () ? 1 : 0)) - 1.0;

    if (res < MIN_SMALL_MONTH_WIDTH)
        res = MIN_SMALL_MONTH_WIDTH;

    return res;
}

/*
  print out the month small, embolden any days with events.
*/
static void
print_month_small (GtkPrintContext *context,
                   GnomeCalendar *gcal,
                   time_t month,
                   gdouble x1,
                   gdouble y1,
                   gdouble x2,
                   gdouble y2,
                   gint titleflags,
                   time_t greystart,
                   time_t greyend,
                   gint bordertitle)
{
    icaltimezone *zone;
    PangoFontDescription *font, *font_bold, *font_normal;
    ECalModel *model;
    time_t now, next;
    gint x, y;
    gint day;
    gint days[42];
    GDateWeekday weekday;
    GDateWeekday week_start_day;
    gchar buf[100];
    struct tm tm;
    gdouble font_size;
    gdouble header_size, col_width, row_height, text_xpad, w;
    gdouble cell_top, cell_bottom, cell_left, cell_right, text_right;
    gboolean week_numbers;
    cairo_t *cr;

    model = gnome_calendar_get_model (gcal);
    zone = e_cal_model_get_timezone (model);

    week_numbers = get_show_week_numbers ();

    /* Print the title, e.g. 'June 2001', in the top 16% of the area. */
    convert_timet_to_struct_tm (month, zone, &tm);
    format_date (&tm, titleflags, buf, 100);

    header_size = ABS (y2 - y1) * 0.16;

    font = get_font_for_size (header_size, PANGO_WEIGHT_BOLD);
    if (bordertitle)
        print_border (context, x1, x2, y1, y1 + header_size, 1.0, 0.9);
    print_text (
        context, font, buf, PANGO_ALIGN_CENTER, x1, x2,
        y1, y1 + header_size);
    pango_font_description_free (font);

    y1 += header_size;
    col_width = (x2 - x1) / (7 + (week_numbers ? 1 : 0));

    /* The top row with the day abbreviations gets an extra bit of
     * vertical space around it. */
    row_height = ABS (y2 - y1) / 7.4;

    /* First we need to calculate a reasonable font size. We start with a
     * rough guess of just under the height of each row. */
    font_size = row_height;

    /* get month days */
    convert_timet_to_struct_tm (month, zone, &tm);
    build_month (model, tm.tm_mon, tm.tm_year + 1900, days, NULL, NULL);

    font_normal = get_font_for_size (font_size, PANGO_WEIGHT_NORMAL);
    font_bold = get_font_for_size (font_size, PANGO_WEIGHT_BOLD);

    /* Get a reasonable estimate of the largest number we will need,
     * and use it to calculate the offset from the right edge of the
     * cell that we should put the numbers. */
    w = evo_calendar_print_renderer_get_width (context, font_bold, "23");
    text_xpad = (col_width - w) / 2;

    cr = gtk_print_context_get_cairo_context (context);
    cairo_set_source_rgb (cr, 0, 0, 0);

    /* Print the abbreviated day names across the top in bold. */
    week_start_day = e_cal_model_get_week_start_day (model);
    weekday = week_start_day;
    for (x = 0; x < 7; x++) {
        print_text (
            context, font_bold,
            _(daynames[weekday]), PANGO_ALIGN_RIGHT,
            x1 + (x + (week_numbers ? 1 : 0)) * col_width,
            x1 + (x + 1 + (week_numbers ? 1 : 0)) * col_width,
            y1, y1 + row_height * 1.4);
        weekday = e_weekday_get_next (weekday);
    }

    y1 += row_height * 1.4;

    now = time_month_begin_with_zone (month, zone);
    for (y = 0; y < 6; y++) {

        cell_top = y1 + y * row_height;
        cell_bottom = cell_top + row_height;

        if (week_numbers) {
            cell_left = x1;
            /* We add a 0.05 to make sure the cells meet up with
             * each other. Otherwise you sometimes get lines
             * between them which looks bad. Maybe I'm not using
             * coords in the way gnome-print expects. */
            cell_right = cell_left + col_width + 0.05;
            text_right = cell_right - text_xpad;

            /* last week can be empty */
            for (x = 0; x < 7; x++) {
                day = days[y * 7 + x];
                if (day != 0)
                    break;
            }

            if (day != 0) {
                time_t week_begin;
                gint wday;

                wday = e_weekday_to_tm_wday (week_start_day);
                week_begin = time_week_begin_with_zone (
                    now, wday, zone);

                convert_timet_to_struct_tm (
                    week_begin, zone, &tm);

                /* Month in e_calendar_item_get_week_number
                 * is also zero-based. */
                sprintf (
                    buf, "%d",
                    e_calendar_item_get_week_number (
                    NULL, tm.tm_mday, tm.tm_mon,
                    tm.tm_year + 1900));

                print_text (
                    context, font_normal,
                    buf, PANGO_ALIGN_RIGHT,
                    cell_left, text_right,
                    cell_top, cell_bottom);
            }
        }

        for (x = 0; x < 7; x++) {

            cell_left = x1 + (x + (week_numbers ? 1 : 0)) * col_width;
            /* We add a 0.05 to make sure the cells meet up with
             * each other. Otherwise you sometimes get lines
             * between them which looks bad. Maybe I'm not using
             * coords in the way gnome-print expects. */
            cell_right = cell_left + col_width + 0.05;
            text_right = cell_right - text_xpad;

            day = days[y * 7 + x];
            if (day != 0) {
                gboolean found = FALSE;
                sprintf (buf, "%d", day);

                /* this is a slow messy way to do this ... but easy ... */
                e_cal_model_generate_instances_sync (
                    gnome_calendar_get_model (gcal), now,
                    time_day_end_with_zone (now, zone),
                    instance_cb, &found);

                font = found ? font_bold : font_normal;

                next = time_add_day_with_zone (now, 1, zone);
                if ((now >= greystart && now < greyend)
                    || (greystart >= now && greystart < next)) {
                    print_border (
                        context,
                        cell_left, cell_right,
                        cell_top, cell_bottom,
                        -1.0, 0.75);
                }
                print_text (
                    context, font, buf, PANGO_ALIGN_RIGHT,
                    cell_left, text_right,
                    cell_top, cell_bottom);

                now = next;
            }
        }
    }
    pango_font_description_free (font_normal);
    pango_font_description_free (font_bold);
}

/* wraps text into the print context, not taking up more than its allowed space */
static gdouble
bound_text (GtkPrintContext *context,
            PangoFontDescription *font,
            const gchar *text,
            gint len,
            gdouble x1,
            gdouble y1,
            gdouble x2,
            gdouble y2,
            gboolean can_wrap,
            gdouble *last_page_start,
            gint *pages)
{
    PangoLayout *layout;
    gint layout_width, layout_height;
    cairo_t *cr;

    cr = gtk_print_context_get_cairo_context (context);
    layout = gtk_print_context_create_pango_layout (context);

    pango_layout_set_font_description (layout, font);
    pango_layout_set_text (layout, text, len);
    pango_layout_set_width (layout, pango_units_from_double (x2 - x1));

    if (can_wrap)
        pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);

    pango_layout_get_size (layout, &layout_width, &layout_height);

    if (last_page_start &&
        y1 + pango_units_to_double (layout_height) >
        y2 + (*last_page_start)) {
        /* draw this on new page */
        if (pages)
            *pages = *pages + 1;

        *last_page_start = *last_page_start + y2;
        y1 = *last_page_start + 10.0;
    }

    if (!last_page_start || (y1 >= 0.0 && y1 < y2)) {
        cairo_save (cr);

        /* Set a clipping rectangle. */
        cairo_move_to (cr, x1, y1);
        cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1);
        cairo_clip (cr);
        cairo_new_path (cr);

        cairo_move_to (cr, x1, y1);
        pango_cairo_show_layout (cr, layout);
        cairo_stroke (cr);

        cairo_restore (cr);
    }

    g_object_unref (layout);

    return y1 + pango_units_to_double (layout_height);
}

/* Draw the borders, lines, and times down the left of the day view. */
static void
print_day_background (GtkPrintContext *context,
                      GnomeCalendar *gcal,
                      time_t whence,
                      struct pdinfo *pdi,
                      gdouble left,
                      gdouble right,
                      gdouble top,
                      gdouble bottom)
{
    ECalModel *model;
    PangoFontDescription *font_hour, *font_minute;
    gdouble yinc, y;
    gdouble width = DAY_VIEW_TIME_COLUMN_WIDTH;
    gdouble font_size, max_font_size, hour_font_size, minute_font_size;
    gchar buf[20];
    const gchar *minute;
    gboolean use_24_hour;
    gint i, hour, row;
    gdouble hour_minute_x, hour_minute_width;
    cairo_t *cr;

    model = gnome_calendar_get_model (gcal);
    use_24_hour = e_cal_model_get_use_24_hour_format (model);

    /* Fill the time column in light-gray. */
    print_border (context, left, left + width, top, bottom, -1.0, 0.9);

    /* Draw the border around the entire view. */
    cr = gtk_print_context_get_cairo_context (context);

    cairo_set_source_rgb (cr, 0, 0, 0);
    print_border (context, left, right, top, bottom, 1.0, -1.0);

    /* Draw the vertical line on the right of the time column. */
    cr = gtk_print_context_get_cairo_context (context);
    cairo_set_line_width (cr, 0.0);
    cairo_move_to (cr, left + width, bottom);
    cairo_line_to (cr, left + width, top);
    cairo_stroke (cr);

    /* Calculate the row height. */
    if (top > bottom)
        yinc = (top - bottom) / (pdi->end_hour - pdi->start_hour);
    else
        yinc = (bottom - top) / (pdi->end_hour - pdi->start_hour);

        /* Get the 2 fonts we need. */
    font_size = yinc * 0.6;
    max_font_size = width * 0.45;
    hour_font_size = MIN (font_size, max_font_size);
    font_hour = get_font_for_size (hour_font_size, PANGO_WEIGHT_BOLD);

    font_size = yinc * 0.33;
    max_font_size = width * 0.2;
    minute_font_size = MIN (font_size, max_font_size);
    font_minute = get_font_for_size (minute_font_size, PANGO_WEIGHT_BOLD);
    hour_minute_width = evo_calendar_print_renderer_get_width (
        context, font_minute, use_24_hour ? "00" : _("am"));
    if (!use_24_hour)
        hour_minute_width = MAX (
            hour_minute_width,
            evo_calendar_print_renderer_get_width (
                context, font_minute, _("pm")));

    row = 0;
    hour_minute_x = left + width - hour_minute_width - 3;
    for (i = pdi->start_hour; i < pdi->end_hour; i++) {
        y = top + yinc * (row + 1);
        cr = gtk_print_context_get_cairo_context (context);
        cairo_set_source_rgb (cr, 0, 0, 0);

        if (use_24_hour) {
            hour = i;
            minute = "00";
        } else {
            if (i < 12)
                minute = _("am");
            else
                minute = _("pm");

            hour = i % 12;
            if (hour == 0)
                hour = 12;
        }

        /* the hour label/minute */
        sprintf (buf, "%d", hour);
        print_text (
            context, font_hour, buf, PANGO_ALIGN_RIGHT,
            left, hour_minute_x,
            y - yinc, y - yinc + hour_font_size);
        print_text (
            context, font_minute, minute, PANGO_ALIGN_LEFT,
            hour_minute_x, left + width - 3,
            y - yinc, y - yinc + minute_font_size);

                /* Draw the horizontal line between hours, across the entire
           width of the day view. */
        cr = gtk_print_context_get_cairo_context (context);
        cairo_move_to (cr, left, y);
        cairo_line_to (cr, right, y);
        cairo_set_line_width (cr, 1);
        cairo_stroke (cr);

        /* Draw the horizontal line for the 1/2-hours, across the
         * entire width except for part of the time column. */
        cairo_move_to (cr, left + width * 0.6, y - yinc / 2);
        cairo_line_to (cr, right, y - yinc / 2);
        cairo_set_line_width (cr, 1);
        cairo_stroke (cr);
        row++;
    }

    pango_font_description_free (font_hour);
    pango_font_description_free (font_minute);
}

/* This adds one event to the view, adding it to the appropriate array. */
static gint
print_day_add_event (ECalModelComponent *comp_data,
                     time_t start,
                     time_t end,
                     icaltimezone *zone,
                     gint days_shown,
                     time_t *day_starts,
                     GArray *long_events,
                     GArray **events)

{
    EDayViewEvent event;
    gint day, offset;
    struct icaltimetype start_tt, end_tt;

#if 0
    g_print ("Day view lower: %s", ctime (&day_starts[0]));
    g_print ("Day view upper: %s", ctime (&day_starts[days_shown]));
    g_print ("Event start: %s", ctime (&start));
    g_print ("Event end  : %s\n", ctime (&end));
#endif

    /* Check that the event times are valid. */
    g_return_val_if_fail (start <= end, -1);
    g_return_val_if_fail (start < day_starts[days_shown], -1);
    g_return_val_if_fail (end > day_starts[0], -1);

    start_tt = icaltime_from_timet_with_zone (start, FALSE, zone);
    end_tt = icaltime_from_timet_with_zone (end, FALSE, zone);

    event.comp_data = comp_data;
    event.start = start;
    event.end = end;
    event.canvas_item = NULL;

    /* Calculate the start & end minute, relative to the top of the
     * display. */
    /*offset = day_view->first_hour_shown * 60
      + day_view->first_minute_shown;*/
    offset = 0;
    event.start_minute = start_tt.hour * 60 + start_tt.minute - offset;
    event.end_minute = end_tt.hour * 60 + end_tt.minute - offset;

    event.start_row_or_col = 0;
    event.num_columns = 0;

    /* Find out which array to add the event to. */
    for (day = 0; day < days_shown; day++) {
        if (start >= day_starts[day] && end <= day_starts[day + 1]) {

            /* Special case for when the appointment ends at
             * midnight, i.e. the start of the next day. */
            if (end == day_starts[day + 1]) {

                /* If the event last the entire day, then we
                 * skip it here so it gets added to the top
                 * canvas. */
                if (start == day_starts[day])
                    break;

                event.end_minute = 24 * 60;
            }
            g_array_append_val (events[day], event);
            return day;
        }
    }

    /* The event wasn't within one day so it must be a long event,
     * i.e. shown in the top canvas. */
    g_array_append_val (long_events, event);
    return E_DAY_VIEW_LONG_EVENT;
}

static gboolean
print_day_details_cb (ECalComponent *comp,
                      time_t istart,
                      time_t iend,
                      gpointer data)
{
    ECalModelGenerateInstancesData *mdata = (ECalModelGenerateInstancesData *) data;
    struct pdinfo *pdi = (struct pdinfo *) mdata->cb_data;

    print_day_add_event (
        mdata->comp_data, istart, iend,
        pdi->zone, pdi->days_shown, pdi->day_starts,
        pdi->long_events, pdi->events);

    return TRUE;
}

static void
free_event_array (GArray *array)
{
    EDayViewEvent *event;
    gint event_num;

    for (event_num = 0; event_num < array->len; event_num++) {
        event = &g_array_index (array, EDayViewEvent, event_num);
        if (event->canvas_item)
            g_object_run_dispose (G_OBJECT (event->canvas_item));
    }

    g_array_set_size (array, 0);
}

static const gchar *
get_type_as_string (icalparameter_cutype cutype)
{
    const gchar *res;

    switch (cutype) {
        case ICAL_CUTYPE_NONE:       res = NULL;            break;
        case ICAL_CUTYPE_INDIVIDUAL: res = _("Individual"); break;
        case ICAL_CUTYPE_GROUP:      res = _("Group");      break;
        case ICAL_CUTYPE_RESOURCE:   res = _("Resource");   break;
        case ICAL_CUTYPE_ROOM:       res = _("Room");       break;
        default:                     res = _("Unknown");    break;
    }

    return res;
}

static const gchar *
get_role_as_string (icalparameter_role role)
{
    const gchar *res;

    switch (role) {
        case ICAL_ROLE_NONE:           res = NULL;                      break;
        case ICAL_ROLE_CHAIR:          res = _("Chair");                break;
        case ICAL_ROLE_REQPARTICIPANT: res = _("Required Participant"); break;
        case ICAL_ROLE_OPTPARTICIPANT: res = _("Optional Participant"); break;
        case ICAL_ROLE_NONPARTICIPANT: res = _("Non-Participant");      break;
        default:                       res = _("Unknown");              break;
    }

    return res;
}

static gdouble
print_attendees (GtkPrintContext *context,
                 PangoFontDescription *font,
                 cairo_t *cr,
                 gdouble left,
                 gdouble right,
                 gdouble top,
                 gdouble bottom,
                 ECalComponent *comp,
                 gint page_nr,
                 gint *pages)
{
    GSList *attendees = NULL, *l;

    g_return_val_if_fail (context != NULL, top);
    g_return_val_if_fail (font != NULL, top);
    g_return_val_if_fail (cr != NULL, top);

    e_cal_component_get_attendee_list (comp, &attendees);

    for (l = attendees; l; l = l->next) {
        ECalComponentAttendee *attendee = l->data;

        if (attendee && attendee->value && *attendee->value) {
            GString *text;
            const gchar *tmp;

            tmp = get_type_as_string (attendee->cutype);
            text = g_string_new (tmp ? tmp : "");

            if (tmp)
                g_string_append (text, " ");

            if (attendee->cn && *attendee->cn)
                g_string_append (text, attendee->cn);
            else {
                /* it's usually in form of "mailto:email@domain" */
                tmp = strchr (attendee->value, ':');
                g_string_append (text, tmp ? tmp + 1 : attendee->value);
            }

            tmp = get_role_as_string (attendee->role);
            if (tmp) {
                g_string_append (text, " (");
                g_string_append (text, tmp);
                g_string_append (text, ")");
            }

            if (top > bottom) {
                top = 10.0;
                cairo_show_page (cr);
            }

            top = bound_text (
                context, font, text->str, -1, left + 40.0,
                top, right, bottom, FALSE, NULL, pages);

            g_string_free (text, TRUE);
        }
    }

    e_cal_component_free_attendee_list (attendees);

    return top;
}

static gchar *
get_summary_with_location (icalcomponent *icalcomp)
{
    const gchar *summary, *location;
    gchar *text;

    g_return_val_if_fail (icalcomp != NULL, NULL);

    summary = icalcomponent_get_summary (icalcomp);
    if (summary == NULL)
        summary = "";

    location = icalcomponent_get_location (icalcomp);
    if (location && *location) {
        text = g_strdup_printf ("%s (%s)", summary, location);
    } else {
        text = g_strdup (summary);
    }

    return text;
}

static void
print_day_long_event (GtkPrintContext *context,
                      PangoFontDescription *font,
                      gdouble left,
                      gdouble right,
                      gdouble top,
                      gdouble bottom,
                      gdouble row_height,
                      EDayViewEvent *event,
                      struct pdinfo *pdi,
                      ECalModel *model)
{
    gdouble x1, x2, y1, y2;
    gdouble left_triangle_width = -1.0, right_triangle_width = -1.0;
    gchar *text;
    gchar buffer[32];
    struct tm date_tm;
    gdouble red, green, blue;

    if (!is_comp_data_valid (event))
        return;

    /* If the event starts before the first day being printed, draw a
     * triangle. (Note that I am assuming we are just showing 1 day at
     * the moment.) */
    if (event->start < pdi->day_starts[0])
        left_triangle_width = 4;

    /* If the event ends after the last day being printed, draw a
     * triangle. */
    if (event->end > pdi->day_starts[1])
        right_triangle_width = 4;

    x1 = left + 10;
    x2 = right - 10;
    y1 = top + event->start_row_or_col * row_height + 1;
    y2 = y1 + row_height - 1;
    red = green = blue = 0.95;
    e_cal_model_get_rgb_color_for_component (
        model, event->comp_data, &red, &green, &blue);
    print_border_with_triangles (
        context, x1, x2, y1, y2, 0.5, red, green, blue,
        left_triangle_width,
        right_triangle_width);

    /* If the event starts after the first day being printed, we need to
     * print the start time. */
    if (event->start > pdi->day_starts[0]) {
        date_tm.tm_year = 2001;
        date_tm.tm_mon = 0;
        date_tm.tm_mday = 1;
        date_tm.tm_hour = event->start_minute / 60;
        date_tm.tm_min = event->start_minute % 60;
        date_tm.tm_sec = 0;
        date_tm.tm_isdst = -1;

        e_time_format_time (&date_tm, pdi->use_24_hour_format, FALSE,
                    buffer, sizeof (buffer));

        x1 += 4;
        x1 += print_text (context, font, buffer, PANGO_ALIGN_LEFT, x1, x2, y1, y2);
    }

    /* If the event ends before the end of the last day being printed,
     * we need to print the end time. */
    if (event->end < pdi->day_starts[1]) {
        date_tm.tm_year = 2001;
        date_tm.tm_mon = 0;
        date_tm.tm_mday = 1;
        date_tm.tm_hour = event->end_minute / 60;
        date_tm.tm_min = event->end_minute % 60;
        date_tm.tm_sec = 0;
        date_tm.tm_isdst = -1;

        e_time_format_time (&date_tm, pdi->use_24_hour_format, FALSE,
                    buffer, sizeof (buffer));

        x2 -= 4;
        x2 -= print_text (context, font, buffer, PANGO_ALIGN_RIGHT, x1, x2, y1, y2);
    }

    /* Print the text. */
    text = get_summary_with_location (event->comp_data->icalcomp);

    x1 += 4;
    x2 -= 4;
    print_text_line (context, font, text, PANGO_ALIGN_CENTER, x1, x2, y1, y2, TRUE);

    g_free (text);
}

static void
print_day_event (GtkPrintContext *context,
                 PangoFontDescription *font,
                 gdouble left,
                 gdouble right,
                 gdouble top,
                 gdouble bottom,
                 EDayViewEvent *event,
                 struct pdinfo *pdi,
                 ECalModel *model)
{
    gdouble x1, x2, y1, y2, col_width, row_height;
    gint start_offset, end_offset, start_row, end_row;
    gchar *text, start_buffer[32], end_buffer[32];
    gboolean display_times = FALSE;
    struct tm date_tm;
    gdouble red, green, blue;

    if (!is_comp_data_valid (event))
        return;

    if ((event->start_minute >= pdi->end_minute_offset)
        || (event->end_minute <= pdi->start_minute_offset))
        return;

    start_offset = event->start_minute - pdi->start_minute_offset;
    end_offset = event->end_minute - pdi->start_minute_offset;

    start_row = start_offset / pdi->mins_per_row;
    start_row = MAX (0, start_row);
    end_row = (end_offset - 1) / pdi->mins_per_row;
    end_row = MIN (pdi->rows - 1, end_row);
    col_width = (right - left) /
        pdi->cols_per_row[event->start_minute / pdi->mins_per_row];

    if (start_offset != start_row * pdi->mins_per_row
        || end_offset != (end_row + 1) * pdi->mins_per_row)
        display_times = TRUE;

    x1 = left + event->start_row_or_col * col_width;
    x2 = x1 + event->num_columns * col_width - DAY_VIEW_EVENT_X_PAD;

    row_height = (bottom - top) / pdi->rows;
    y1 = top + start_row * row_height;
    y2 = top + (end_row + 1) * row_height;
#if 0
    g_print (
        "Event: %g,%g %g,%g\n  row_height: %g start_row: %i top: %g rows: %i\n",
        x1, y1, x2, y2, row_height, start_row, top, pdi->rows);
#endif

    red = green = blue = 0.95;
    e_cal_model_get_rgb_color_for_component (
        model, event->comp_data, &red, &green, &blue);
    print_border_rgb (context, x1, x2, y1, y2, 1.0, red, green, blue);

    text = get_summary_with_location (event->comp_data->icalcomp);

    if (display_times) {
        gchar *t = NULL;

        date_tm.tm_year = 2001;
        date_tm.tm_mon = 0;
        date_tm.tm_mday = 1;
        date_tm.tm_hour = event->start_minute / 60;
        date_tm.tm_min = event->start_minute % 60;
        date_tm.tm_sec = 0;
        date_tm.tm_isdst = -1;

        e_time_format_time (&date_tm, pdi->use_24_hour_format, FALSE,
                    start_buffer, sizeof (start_buffer));

        date_tm.tm_hour = event->end_minute / 60;
        date_tm.tm_min = event->end_minute % 60;

        e_time_format_time (&date_tm, pdi->use_24_hour_format, FALSE,
                    end_buffer, sizeof (end_buffer));

        t = text;
        text = g_strdup_printf (
            "%s - %s %s ",
            start_buffer, end_buffer, text);

        g_free (t);
    }

    bound_text (context, font, text, -1, x1 + 2, y1, x2 - 2, y2, FALSE, NULL, NULL);

    g_free (text);
}

static void
print_day_details (GtkPrintContext *context,
                   GnomeCalendar *gcal,
                   time_t whence,
                   gdouble left,
                   gdouble right,
                   gdouble top,
                   gdouble bottom)
{
    ECalModel *model;
    icaltimezone *zone;
    EDayViewEvent *event;
    PangoFontDescription *font;
    time_t start, end;
    struct pdinfo pdi = { 0 };
    gint rows_in_top_display, i, rows_with_30_mins;
    gdouble font_size, max_font_size;
    cairo_t *cr;
    GdkPixbuf *pixbuf = NULL;
#define LONG_DAY_EVENTS_TOP_SPACING 4
#define LONG_DAY_EVENTS_BOTTOM_SPACING 2

    model = gnome_calendar_get_model (gcal);
    zone = e_cal_model_get_timezone (model);

    start = time_day_begin_with_zone (whence, zone);
    end = time_day_end_with_zone (start, zone);

    pdi.days_shown = 1;
    pdi.day_starts[0] = start;
    pdi.day_starts[1] = end;
    pdi.long_events = g_array_new (FALSE, FALSE, sizeof (EDayViewEvent));
    pdi.events[0] = g_array_new (FALSE, FALSE, sizeof (EDayViewEvent));
    pdi.start_hour = e_cal_model_get_work_day_start_hour (model);
    pdi.end_hour = e_cal_model_get_work_day_end_hour (model);
    if (e_cal_model_get_work_day_end_minute (model) != 0)
        pdi.end_hour++;
    pdi.mins_per_row = get_day_view_time_divisions ();
    pdi.rows = (pdi.end_hour - pdi.start_hour) * (60 / pdi.mins_per_row);
    pdi.start_minute_offset = pdi.start_hour * 60;
    pdi.end_minute_offset = pdi.end_hour * 60;
    pdi.use_24_hour_format = e_cal_model_get_use_24_hour_format (model);
    pdi.zone = e_cal_model_get_timezone (model);

    /* Get the events from the server. */
    e_cal_model_generate_instances_sync (model, start, end, print_day_details_cb, &pdi);
    qsort (
        pdi.long_events->data, pdi.long_events->len,
        sizeof (EDayViewEvent), e_day_view_event_sort_func);
    qsort (
        pdi.events[0]->data, pdi.events[0]->len,
        sizeof (EDayViewEvent), e_day_view_event_sort_func);

    /* Also print events outside of work hours */
    if (pdi.events[0]->len > 0) {
        struct icaltimetype tt;

        event = &g_array_index (pdi.events[0], EDayViewEvent, 0);
        tt = icaltime_from_timet_with_zone (event->start, FALSE, zone);
        if (tt.hour < pdi.start_hour)
            pdi.start_hour = tt.hour;
        pdi.start_minute_offset = pdi.start_hour * 60;

        event = &g_array_index (pdi.events[0], EDayViewEvent, pdi.events[0]->len - 1);
        tt = icaltime_from_timet_with_zone (event->end, FALSE, zone);
        if (tt.hour > pdi.end_hour || tt.hour == 0) {
            pdi.end_hour = tt.hour ? tt.hour : 24;
            if (tt.minute > 0)
                pdi.end_hour++;
        }
        pdi.end_minute_offset = pdi.end_hour * 60;

        pdi.rows = (pdi.end_hour - pdi.start_hour) * (60 / pdi.mins_per_row);
    }

    /* Lay them out the long events, across the top of the page. */
    e_day_view_layout_long_events (
        pdi.long_events, pdi.days_shown,
        pdi.day_starts, &rows_in_top_display);

     /*Print the long events. */
    font = get_font_for_size (12, PANGO_WEIGHT_NORMAL);

    /* We always leave space for DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY in the
     * top display, but we may have more rows than that, in which case
     * the main display area will be compressed. */
    /* Limit long day event to half the height of the panel */
    rows_in_top_display = MIN (
        MAX (rows_in_top_display,
        DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY),
        (bottom - top) * 0.5 / DAY_VIEW_ROW_HEIGHT);

    if (rows_in_top_display > pdi.long_events->len)
        rows_in_top_display = pdi.long_events->len;

    for (i = 0; i < rows_in_top_display && i < pdi.long_events->len; i++) {
        event = &g_array_index (pdi.long_events, EDayViewEvent, i);
        print_day_long_event (
            context, font, left, right,
            top + LONG_DAY_EVENTS_TOP_SPACING, bottom,
            DAY_VIEW_ROW_HEIGHT, event, &pdi, model);
    }

    if (rows_in_top_display < pdi.long_events->len) {
        /* too many events */
        cairo_t *cr = gtk_print_context_get_cairo_context (context);
        gint x, y;

        if (!pixbuf) {
            const gchar **xpm = (const gchar **) jump_xpm;

            /* this ugly thing is here only to get rid of compiler warning
             * about unused 'jump_xpm_focused' */
            if (pixbuf)
                xpm = (const gchar **) jump_xpm_focused;

            pixbuf = gdk_pixbuf_new_from_xpm_data (xpm);
        }

        /* Right align - 10 comes from print_day_long_event  too */
        x = right - gdk_pixbuf_get_width (pixbuf) * 0.5 - 10;
        /* Placing '...' over the last all day event entry printed. '-1 -1' comes
            from print_long_day_event (top / bottom spacing in each cell) */
        y = top + LONG_DAY_EVENTS_TOP_SPACING
            + DAY_VIEW_ROW_HEIGHT * (i - 1)
            + (DAY_VIEW_ROW_HEIGHT - 1 - 1) * 0.5;

        cairo_save (cr);
        cairo_scale (cr, 0.5, 0.5);
        gdk_cairo_set_source_pixbuf (cr, pixbuf, x * 2.0, y * 2.0);
        cairo_paint (cr);
        cairo_restore (cr);
    }

    if (!rows_in_top_display)
        rows_in_top_display++;

    /* Draw the border around the long events. */
    cr = gtk_print_context_get_cairo_context (context);

    cairo_set_source_rgb (cr, 0, 0, 0);
    print_border (
        context, left, right,
        top, top + rows_in_top_display * DAY_VIEW_ROW_HEIGHT +
        LONG_DAY_EVENTS_TOP_SPACING + LONG_DAY_EVENTS_BOTTOM_SPACING,
        1.0, -1.0);

    /* Adjust the area containing the main display. */
    top += rows_in_top_display * DAY_VIEW_ROW_HEIGHT
        + LONG_DAY_EVENTS_TOP_SPACING
        + LONG_DAY_EVENTS_BOTTOM_SPACING;

    /* Draw the borders, lines, and times down the left. */
    print_day_background (
        context, gcal, whence, &pdi,
        left, right, top, bottom);
    /* Now adjust to get rid of the time column. */
    left += DAY_VIEW_TIME_COLUMN_WIDTH;

    /* lay out the short events, within the day. */
    e_day_view_layout_day_events (
        pdi.events[0], CALC_DAY_VIEW_ROWS (pdi.mins_per_row),
        pdi.mins_per_row, pdi.cols_per_row, -1);

    /* use font like with 30 minutes time division */
    rows_with_30_mins = (pdi.end_hour - pdi.start_hour) * (60 / 30);

    /* print the short events. */
    if (top > bottom)
        max_font_size = ((top - bottom) / rows_with_30_mins) - 4;
    else
        max_font_size = ((bottom - top) / rows_with_30_mins) - 4;
    font_size = MIN (DAY_NORMAL_FONT_SIZE, max_font_size);
    font = get_font_for_size (font_size, PANGO_WEIGHT_NORMAL);

    for (i = 0; i < pdi.events[0]->len; i++) {
        event = &g_array_index (pdi.events[0], EDayViewEvent, i);
        print_day_event (
            context, font, left, right, top, bottom,
            event, &pdi, model);
    }

    /* Free everything. */
    if (pixbuf)
        g_object_unref (pixbuf);
    free_event_array (pdi.long_events);
    pango_font_description_free (font);
    g_array_free (pdi.long_events, TRUE);
    free_event_array (pdi.events[0]);
    g_array_free (pdi.events[0], TRUE);
}

/* Returns TRUE if the event is a one-day event (i.e. not a long event). */
static gboolean
print_is_one_day_week_event (EWeekViewEvent *event,
                             EWeekViewEventSpan *span,
                             time_t *day_starts)
{
    if (event->start == day_starts[span->start_day]
        && event->end == day_starts[span->start_day + 1])
        return FALSE;

    if (span->num_days == 1
        && event->start >= day_starts[span->start_day]
        && event->end <= day_starts[span->start_day + 1])
        return TRUE;

    return FALSE;
}

static void
print_week_long_event (GtkPrintContext *context,
                       PangoFontDescription *font,
                       struct psinfo *psi,
                       gdouble x1,
                       gdouble x2,
                       gdouble y1,
                       gdouble row_height,
                       EWeekViewEvent *event,
                       EWeekViewEventSpan *span,
                       gchar *text,
                       gdouble red,
                       gdouble green,
                       gdouble blue)
{
    gdouble left_triangle_width = -1.0, right_triangle_width = -1.0;
    struct tm date_tm;
    gchar buffer[32];

    /* If the event starts before the first day of the span, draw a
     * triangle to indicate it continues. */
    if (event->start < psi->day_starts[span->start_day])
        left_triangle_width = 4;

    /* If the event ends after the last day of the span, draw a
     * triangle. */
    if (event->end > psi->day_starts[span->start_day + span->num_days])
        right_triangle_width = 4;

    print_border_with_triangles (
        context, x1 + 6, x2 - 6, y1, y1 + row_height, 0.0, red, green, blue,
        left_triangle_width, right_triangle_width);

    x1 += 6;
    x2 -= 6;

    /* If the event starts after the first day being printed, we need to
     * print the start time. */
    if (event->start > psi->day_starts[span->start_day]) {
        date_tm.tm_year = 2001;
        date_tm.tm_mon = 0;
        date_tm.tm_mday = 1;
        date_tm.tm_hour = event->start_minute / 60;
        date_tm.tm_min = event->start_minute % 60;
        date_tm.tm_sec = 0;
        date_tm.tm_isdst = -1;

        e_time_format_time (&date_tm, psi->use_24_hour_format, FALSE,
                    buffer, sizeof (buffer));

        x1 += 2;
        x1 += print_text_line (
            context, font, buffer, PANGO_ALIGN_LEFT,
            x1, x2 - 2, y1, y1 + row_height, TRUE);
    }

    /* If the event ends before the end of the last day being printed,
     * we need to print the end time. */
    if (event->end < psi->day_starts[span->start_day + span->num_days]) {
        date_tm.tm_year = 2001;
        date_tm.tm_mon = 0;
        date_tm.tm_mday = 1;
        date_tm.tm_hour = event->end_minute / 60;
        date_tm.tm_min = event->end_minute % 60;
        date_tm.tm_sec = 0;
        date_tm.tm_isdst = -1;

        e_time_format_time (&date_tm, psi->use_24_hour_format, FALSE,
                    buffer, sizeof (buffer));

        x2 -= 2;
        x2 -= print_text_line (
            context, font, buffer, PANGO_ALIGN_RIGHT,
            x1 + 2, x2, y1, y1 + row_height, TRUE);
    }

    x1 += 2;
    x2 -= 2;
    print_text_line (context, font, text, PANGO_ALIGN_CENTER, x1, x2, y1, y1 + row_height, TRUE);
}

static void
print_week_day_event (GtkPrintContext *context,
                      PangoFontDescription *font,
                      struct psinfo *psi,
                      gdouble x1,
                      gdouble x2,
                      gdouble y1,
                      gdouble row_height,
                      EWeekViewEvent *event,
                      EWeekViewEventSpan *span,
                      gchar *text,
                      gdouble red,
                      gdouble green,
                      gdouble blue)
{
    struct tm date_tm;
    gchar buffer[32];

    date_tm.tm_year = 2001;
    date_tm.tm_mon = 0;
    date_tm.tm_mday = 1;
    date_tm.tm_hour = event->start_minute / 60;
    date_tm.tm_min = event->start_minute % 60;
    date_tm.tm_sec = 0;
    date_tm.tm_isdst = -1;

    e_time_format_time (&date_tm, psi->use_24_hour_format, FALSE,
                buffer, sizeof (buffer));
    print_rectangle (context, x1 + 1, y1, x2 - x1 - 2, row_height, red, green, blue);
    x1 += print_text_line (
        context, font, buffer, PANGO_ALIGN_LEFT,
        x1 + 2, x2 - 3, y1, y1 + row_height, TRUE) + 4;

    if (psi->weeks_shown <= 2) {
        date_tm.tm_hour = event->end_minute / 60;
        date_tm.tm_min = event->end_minute % 60;

        e_time_format_time (&date_tm, psi->use_24_hour_format, FALSE,
                    buffer, sizeof (buffer));

        x1 += print_text_line (
            context, font, buffer, PANGO_ALIGN_LEFT,
            x1, x2 - 3, y1, y1 + row_height, TRUE) + 4;
    }

    print_text_line (
        context, font, text, PANGO_ALIGN_LEFT,
        x1, x2 - 3, y1, y1 + row_height, TRUE);
}

static void
print_week_event (GtkPrintContext *context,
                  PangoFontDescription *font,
                  struct psinfo *psi,
                  gdouble left,
                  gdouble top,
                  gdouble cell_width,
                  gdouble cell_height,
                  ECalModel *model,
                  EWeekViewEvent *event,
                  GArray *spans)
{
    EWeekViewEventSpan *span;
    gint span_num;
    gchar *text;
    gint num_days, start_x, start_y, start_h, end_x, end_y, end_h;
    gdouble x1, x2, y1;
    gdouble red, green, blue;
    GdkPixbuf *pixbuf = NULL;

    if (!is_comp_data_valid (event))
        return;

    text = get_summary_with_location (event->comp_data->icalcomp);

    for (span_num = 0; span_num < event->num_spans; span_num++) {
        span = &g_array_index (spans, EWeekViewEventSpan,
                       event->spans_index + span_num);

        if (e_week_view_layout_get_span_position (
             event, span,
             psi->rows_per_cell,
             psi->rows_per_compressed_cell,
             psi->display_start_weekday,
             psi->multi_week_view,
             psi->compress_weekend,
             &num_days)) {

            e_week_view_layout_get_day_position
                (span->start_day,
                 psi->multi_week_view,
                 psi->weeks_shown,
                 psi->display_start_weekday,
                 psi->compress_weekend,
                 &start_x, &start_y, &start_h);

            if (num_days == 1) {
                end_x = start_x;
                end_y = start_y;
                end_h = start_h;
            } else {
                e_week_view_layout_get_day_position
                    (span->start_day + num_days - 1,
                     psi->multi_week_view,
                     psi->weeks_shown,
                     psi->display_start_weekday,
                     psi->compress_weekend,
                     &end_x, &end_y, &end_h);
            }

            x1 = left + start_x * cell_width;
            x2 = left + (end_x + 1) * cell_width;
            y1 = top + start_y * cell_height
                 + psi->header_row_height
                 + span->row * (psi->row_height + 2);

            red = .9;
            green = .9;
            blue = .9;
            e_cal_model_get_rgb_color_for_component (
                model, event->comp_data, &red, &green, &blue);
            if (print_is_one_day_week_event (event, span,
                             psi->day_starts)) {
                print_week_day_event (
                    context, font, psi,
                    x1, x2, y1, psi->row_height,
                    event, span, text, red, green, blue);
            } else {
                print_week_long_event (
                    context, font, psi,
                    x1, x2, y1, psi->row_height,
                    event, span, text, red, green, blue);
            }
        } else {
            cairo_t *cr = gtk_print_context_get_cairo_context (context);

            e_week_view_layout_get_day_position (
                span->start_day,
                psi->multi_week_view,
                psi->weeks_shown,
                psi->display_start_weekday,
                psi->compress_weekend,
                &start_x, &start_y, &start_h);

            y1 = top + start_y * cell_height
                 + psi->header_row_height
                 + psi->rows_per_cell * (psi->row_height + 2);

            if (span->row >= psi->rows_per_compressed_cell && psi->compress_weekend) {
                GDateWeekday end_weekday;
                gboolean end_on_weekend;

                end_weekday = e_weekday_add_days (
                    psi->display_start_weekday,
                    span->start_day);

                end_on_weekend =
                    (end_weekday == G_DATE_SATURDAY) ||
                    (end_weekday == G_DATE_SUNDAY);

                if (end_on_weekend) {
                    y1 = top + start_y * cell_height
                         + psi->header_row_height
                         + psi->rows_per_compressed_cell * (psi->row_height + 2);

                }
            }

            if (!pixbuf) {
                const gchar **xpm = (const gchar **) jump_xpm;

                /* this ugly thing is here only to get rid of compiler warning
                 * about unused 'jump_xpm_focused' */
                if (pixbuf)
                    xpm = (const gchar **) jump_xpm_focused;

                pixbuf = gdk_pixbuf_new_from_xpm_data (xpm);
            }

            x1 = left + (start_x + 1) * cell_width - 6 -
                gdk_pixbuf_get_width (pixbuf) * 0.5;

            cairo_save (cr);
            cairo_scale (cr, 0.5, 0.5);
            gdk_cairo_set_source_pixbuf (cr, pixbuf, x1 * 2.0, y1 * 2.0);
            cairo_paint (cr);
            cairo_restore (cr);
        }
    }

    if (pixbuf)
        g_object_unref (pixbuf);

    g_free (text);
}

static void
print_week_view_background (GtkPrintContext *context,
                            PangoFontDescription *font,
                            struct psinfo *psi,
                            gdouble left,
                            gdouble top,
                            gdouble cell_width,
                            gdouble cell_height)
{
    struct tm tm;
    gint day, day_x, day_y, day_h;
    gdouble x1, x2, y1, y2, font_size, fillcolor;
    const gchar *format_string;
    gchar buffer[128];
    cairo_t *cr;

    font_size = get_font_size (font);

    for (day = 0; day < psi->days_shown; day++) {
        e_week_view_layout_get_day_position
            (day, psi->multi_week_view, psi->weeks_shown,
             psi->display_start_weekday, psi->compress_weekend,
             &day_x, &day_y, &day_h);

        x1 = left + day_x * cell_width;
        x2 = left + (day_x + 1) * cell_width;
        y1 = top + day_y * cell_height;
        y2 = y1 + day_h * cell_height;

        convert_timet_to_struct_tm (psi->day_starts[day], psi->zone, &tm);

        /* In the month view we draw a grey background for the end
         * of the previous month and the start of the following. */
        fillcolor = -1.0;
        if (psi->multi_week_view && (tm.tm_mon != psi->month))
            fillcolor = 0.9;

        print_border (context, x1, x2, y1, y2, 1.0, fillcolor);

        if (psi->multi_week_view) {
            if (tm.tm_mday == 1)
                format_string = _("%d %B");
            else
                format_string = "%d";
        } else {
            cr = gtk_print_context_get_cairo_context (context);

            cairo_move_to (
                cr, x1 + 0.1 * cell_width,
                y1 + psi->header_row_height - 4);
            cairo_line_to (
                cr, x2,
                y1 + psi->header_row_height - 4);

            cairo_set_source_rgb (cr, 0, 0, 0);
            cairo_set_line_width (cr, 0.5);
            cairo_stroke (cr);

            /* strftime format %A = full weekday name, %d = day of
             * month, %B = full month name. You can change the
             * order but don't change the specifiers or add
             * anything. */
            format_string = _("%A %d %B");

        }

        e_utf8_strftime (buffer, sizeof (buffer), format_string, &tm);

        print_text_line (
            context, font, buffer, PANGO_ALIGN_RIGHT,
            x1, x2 - 4, y1 + 2, y1 + 2 + font_size, TRUE);
    }
}

/* This adds one event to the view, adding it to the appropriate array. */
static gboolean
print_week_summary_cb (ECalComponent *comp,
                       time_t start,
                       time_t end,
                       gpointer data)

{
    EWeekViewEvent event;
    struct icaltimetype start_tt, end_tt;
    ECalModelGenerateInstancesData *mdata = (ECalModelGenerateInstancesData *) data;
    struct psinfo *psi = (struct psinfo *) mdata->cb_data;

    /* Check that the event times are valid. */

#if 0
    g_print (
        "View start:%li end:%li  Event start:%li end:%li\n",
        psi->day_starts[0], psi->day_starts[psi->days_shown],
        start, end);
#endif

    g_return_val_if_fail (start <= end, TRUE);
    g_return_val_if_fail (start < psi->day_starts[psi->days_shown], TRUE);
    g_return_val_if_fail (end > psi->day_starts[0], TRUE);

    start_tt = icaltime_from_timet_with_zone (start, FALSE, psi->zone);
    end_tt = icaltime_from_timet_with_zone (end, FALSE, psi->zone);

    event.comp_data = g_object_ref (mdata->comp_data);

    event.start = start;
    event.end = end;
    event.spans_index = 0;
    event.num_spans = 0;

    event.start_minute = start_tt.hour * 60 + start_tt.minute;
    event.end_minute = end_tt.hour * 60 + end_tt.minute;
    if (event.end_minute == 0 && start != end)
        event.end_minute = 24 * 60;

    g_array_append_val (psi->events, event);

    return TRUE;
}

static void
print_week_summary (GtkPrintContext *context,
                    GnomeCalendar *gcal,
                    time_t whence,
                    gboolean multi_week_view,
                    gint weeks_shown,
                    gint month,
                    gdouble font_size,
                    gdouble font_size_background,
                    gdouble left,
                    gdouble right,
                    gdouble top,
                    gdouble bottom)
{
    icaltimezone *zone;
    EWeekViewEvent *event;
    struct psinfo psi = { 0 };
    time_t day_start;
    gint rows_per_day[E_WEEK_VIEW_MAX_WEEKS * 7], day, event_num;
    GArray *spans;
    PangoFontDescription *font, *font_background;
    gdouble cell_width, cell_height;
    ECalModel *model;

    model = gnome_calendar_get_model (gcal);
    zone = e_cal_model_get_timezone (model);

    psi.days_shown = weeks_shown * 7;
    psi.events = g_array_new (FALSE, FALSE, sizeof (EWeekViewEvent));
    psi.multi_week_view = multi_week_view;
    psi.weeks_shown = weeks_shown;
    psi.month = month;
    psi.zone = zone;

    /* Get a few config settings. */
    if (multi_week_view)
        psi.compress_weekend = e_cal_model_get_compress_weekend (model);
    else
        psi.compress_weekend = TRUE;
    psi.use_24_hour_format = e_cal_model_get_use_24_hour_format (model);

    psi.display_start_weekday = e_cal_model_get_week_start_day (model);

    /* If weekends are compressed then we can't start on a Sunday. */
    if (psi.compress_weekend && psi.display_start_weekday == G_DATE_SUNDAY)
        psi.display_start_weekday = G_DATE_SATURDAY;

    day_start = time_day_begin_with_zone (whence, zone);
    for (day = 0; day <= psi.days_shown; day++) {
        psi.day_starts[day] = day_start;
        day_start = time_add_day_with_zone (day_start, 1, zone);
    }

    /* Get the events from the server. */
    e_cal_model_generate_instances_sync (
        model,
        psi.day_starts[0], psi.day_starts[psi.days_shown],
        print_week_summary_cb, &psi);
    qsort (
        psi.events->data, psi.events->len,
        sizeof (EWeekViewEvent), e_week_view_event_sort_func);

    /* Layout the events. */
    spans = e_week_view_layout_events (
        psi.events, NULL,
        psi.multi_week_view,
        psi.weeks_shown,
        psi.compress_weekend,
        psi.display_start_weekday,
        psi.day_starts, rows_per_day);

    /* Calculate the size of the cells. */
    if (multi_week_view) {
        cell_width = (right - left) / (psi.compress_weekend ? 6 : 7);
        cell_height = (bottom - top) / (weeks_shown * 2);
    } else {
        cell_width = (right - left) / 2;
        cell_height = (bottom - top) / 6;
    }

    /* Calculate the row height, using the normal font and with room for
     * space or a rectangle around it. */
    psi.row_height = font_size * 1.2;
    psi.header_row_height = font_size * 1.5;

    /* Calculate how many rows we can fit into each type of cell. */
    psi.rows_per_cell = ((cell_height * 2) - psi.header_row_height)
        / (psi.row_height + 2);
    psi.rows_per_compressed_cell = (cell_height - psi.header_row_height)
        / (psi.row_height + 2);

    /* Draw the grid and the day names/numbers. */
    font_background = get_font_for_size (font_size_background, PANGO_WEIGHT_NORMAL);
    print_week_view_background (
        context, font_background, &psi, left, top,
        cell_width, cell_height);
    pango_font_description_free (font_background);

    /* Print the events. */
    font = get_font_for_size (font_size, PANGO_WEIGHT_NORMAL);
    for (event_num = 0; event_num < psi.events->len; event_num++) {
        event = &g_array_index (psi.events, EWeekViewEvent, event_num);
        print_week_event (
            context, font, &psi, left, top,
            cell_width, cell_height, model, event, spans);
    }

    pango_font_description_free (font);

    /* Free everything. */
    for (event_num = 0; event_num < psi.events->len; event_num++) {
        event = &g_array_index (psi.events, EWeekViewEvent, event_num);
        g_object_unref (event->comp_data);
    }
    g_array_free (psi.events, TRUE);
    g_array_free (spans, TRUE);
}

static void
print_month_summary (GtkPrintContext *context,
                     GnomeCalendar *gcal,
                     time_t whence,
                     gdouble left,
                     gdouble right,
                     gdouble top,
                     gdouble bottom)
{
    icaltimezone *zone;
    time_t date;
    struct tm tm;
    struct icaltimetype tt;
    gchar buffer[100];
    ECalModel *model;
    PangoFontDescription *font;
    gboolean compress_weekend;
    gint columns, col, month, weeks;
    GDateWeekday weekday;
    gint wday;
    gdouble font_size, cell_width, x1, x2, y1, y2;

    model = gnome_calendar_get_model (gcal);
    zone = e_cal_model_get_timezone (model);
    weekday = e_cal_model_get_week_start_day (model);
    compress_weekend = e_cal_model_get_compress_weekend (model);

    date = 0;
    weeks = 6;
    if (gnome_calendar_get_view (gcal) == GNOME_CAL_MONTH_VIEW) {
        GnomeCalendarViewType view_type;
        ECalendarView *calendar_view;
        EWeekView *week_view;
        GDate first_day_shown;
        gboolean multi_week_view;
        gint weeks_shown;

        view_type = gnome_calendar_get_view (gcal);
        calendar_view = gnome_calendar_get_calendar_view (gcal, view_type);
        week_view = E_WEEK_VIEW (calendar_view);
        weeks_shown = e_week_view_get_weeks_shown (week_view);
        multi_week_view = e_week_view_get_multi_week_view (week_view);
        e_week_view_get_first_day_shown (week_view, &first_day_shown);

        if (multi_week_view && !(weeks_shown >= 4 &&
            g_date_valid (&first_day_shown))) {
            weeks = weeks_shown;
            date = whence;
        }
    }

    /* Remember which month we want. */
    tt = icaltime_from_timet_with_zone (whence, FALSE, zone);
    month = tt.month - 1;

    /* Find the start of the month, and then the start of the week on
     * or before that day. */
    if (!date)
        date = time_month_begin_with_zone (whence, zone);

    wday = e_weekday_to_tm_wday (weekday);
    date = time_week_begin_with_zone (date, wday, zone);

    /* If weekends are compressed then we can't start on a Sunday. */
    if (compress_weekend && weekday == G_DATE_SUNDAY)
        date = time_add_day_with_zone (date, -1, zone);

    /* do day names ... */

    /* We are only interested in outputting the weekday here, but we want
     * to be able to step through the week without worrying about
     * overflows making strftime choke, so we move near to the start of
     * the month. */
    convert_timet_to_struct_tm (date, zone, &tm);
    tm.tm_mday = (tm.tm_mday % 7) + 7;

    font = get_font_for_size (MONTH_NORMAL_FONT_SIZE, PANGO_WEIGHT_BOLD);
    font_size = get_font_size (font);

    columns = compress_weekend ? 6 : 7;
    cell_width = (right - left) / columns;
    y1 = top;
    y2 = top + font_size * 1.5;

    for (col = 0; col < columns; col++) {
        if (tm.tm_wday == 6 && compress_weekend)
            g_snprintf (
                buffer, sizeof (buffer), "%s/%s",
                e_get_weekday_name (G_DATE_SATURDAY, TRUE),
                e_get_weekday_name (G_DATE_SUNDAY, TRUE));
        else
            g_snprintf (
                buffer, sizeof (buffer), "%s",
                e_get_weekday_name (
                tm.tm_wday ? tm.tm_wday : 7, FALSE));

        x1 = left + cell_width * col;
        x2 = x1 + cell_width;

        print_border (context, x1, x2, y1, y2, 1.0, -1.0);
        print_text_line (context, font, buffer, PANGO_ALIGN_CENTER, x1, x2, y1, y2, TRUE);

        tm.tm_mday++;
        tm.tm_wday = (tm.tm_wday + 1) % 7;
    }
    pango_font_description_free (font);

    top = y2;
    print_week_summary (
        context, gcal, date, TRUE, weeks, month,
        MONTH_NORMAL_FONT_SIZE, MONTH_NORMAL_FONT_SIZE,
        left, right, top, bottom);
}

static void
print_todo_details (GtkPrintContext *context,
                    GnomeCalendar *gcal,
                    time_t start,
                    time_t end,
                    gdouble left,
                    gdouble right,
                    gdouble top,
                    gdouble bottom)
{
    PangoFontDescription *font_summary;
    gdouble y, yend, x, xend;
    struct icaltimetype *tt;
    GtkWidget *task_table;
    ETable *table;
    ECalModel *model;
    gint rows, row;
    cairo_t *cr;

    /* We get the tasks directly from the TaskPad ETable. This means we
     * get them filtered & sorted for free. */
    task_table = gnome_calendar_get_task_table (gcal);
    table = E_TABLE (task_table);
    g_return_if_fail (table != NULL);
    model = e_task_table_get_model (E_TASK_TABLE (task_table));

    font_summary = get_font_for_size (12, PANGO_WEIGHT_NORMAL);

    cr = gtk_print_context_get_cairo_context (context);

    cairo_set_source_rgb (cr, 0, 0, 0);
    cairo_set_line_width (cr, 0.0);
    top +=2;

    titled_box (
        context, _("Tasks"), font_summary, PANGO_ALIGN_CENTER,
        &left, &top, &right, &bottom, 1.0);

    y = top;
    yend = bottom - 2;

    rows = e_table_model_row_count (E_TABLE_MODEL (model));
    for (row = 0; row < rows; row++) {
        ECalModelComponent *comp_data;
        ECalComponent *comp;
        ECalComponentText summary;
        gint model_row;

        model_row = e_table_view_to_model_row (table, row);
        comp_data = e_cal_model_get_component_at (model, model_row);
        if (!comp_data)
            continue;

        comp = e_cal_component_new ();
        e_cal_component_set_icalcomponent (
            comp, icalcomponent_new_clone (comp_data->icalcomp));

        e_cal_component_get_summary (comp, &summary);
        if (!summary.value) {
            g_object_unref (comp);
            continue;
        }

        x = left;
        xend = right - 2;
        if (y > bottom) {
            g_object_unref (comp);
            break;
        }

        /* Print the box to put the tick in. */
        print_border (context, x + 2, x + 8, y + 6, y + 15, 0.1, -1.0);

        /* If the task is complete, print a tick in the box. */
        e_cal_component_get_completed (comp, &tt);
        if (tt) {
            e_cal_component_free_icaltimetype (tt);

            cr = gtk_print_context_get_cairo_context (context);
            cairo_set_source_rgb (cr, 0, 0, 0);
            cairo_move_to (cr, x + 3, y + 11);
            cairo_line_to (cr, x + 5, y + 14);
            cairo_line_to (cr, x + 7, y + 5.5);
            cairo_set_line_width (cr, 1);
            cairo_stroke (cr);
        }

        y = bound_text (
            context, font_summary, summary.value, -1,
            x + 14, y + 4, xend, yend, FALSE, NULL, NULL);

        y += get_font_size (font_summary) - 5;
        cr = gtk_print_context_get_cairo_context (context);
        cairo_move_to (cr, x, y);
        cairo_line_to (cr, xend, y);
        cairo_set_line_width (cr, 1);
        cairo_stroke (cr);

        g_object_unref (comp);
    }

    pango_font_description_free (font_summary);
}

static void
print_day_view (GtkPrintContext *context,
                GnomeCalendar *gcal,
                time_t date)
{
    ECalModel *model;
    GtkPageSetup *setup;
    icaltimezone *zone;
    gint i, days = 1;
    gdouble todo, l, week_numbers_inc, small_month_width;
    gchar buf[100];
    gdouble width, height;
    struct tm tm;

    model = gnome_calendar_get_model (gcal);
    zone = e_cal_model_get_timezone (model);

    setup = gtk_print_context_get_page_setup (context);

    width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
    height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);
    small_month_width = calc_small_month_width (context, HEADER_HEIGHT);
    week_numbers_inc = get_show_week_numbers () ? small_month_width / 7.0 : 0;

    for (i = 0; i < days; i++) {
        todo = width * 0.75;

        /* Print the main view with all the events in. */
        print_day_details (
            context, gcal, date,
            0.0, todo - 2.0, HEADER_HEIGHT + 4,
            height);

         /* Print the TaskPad down the right. */
        print_todo_details (
            context, gcal, 0, INT_MAX,
            todo, width, HEADER_HEIGHT + 4,
            height);

        /* Print the filled border around the header. */
        print_border (
            context, 0.0, width,
            0.0, HEADER_HEIGHT + 4, 1.0, 0.9);

        /* Print the 2 mini calendar-months. */
        l = width - SMALL_MONTH_PAD -
            (small_month_width + week_numbers_inc) * 2 -
            SMALL_MONTH_SPACING;

        print_month_small (
            context, gcal, date,
            l, 2, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 2,
            DATE_MONTH | DATE_YEAR, date, date, FALSE);

        l += SMALL_MONTH_SPACING + small_month_width + week_numbers_inc;
        print_month_small (
            context, gcal,
            time_add_month_with_zone (date, 1, zone),
            l, 2, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 2,
            DATE_MONTH | DATE_YEAR, 0, 0, FALSE);

        /* Print the date, e.g. '8th May, 2001'. */
        convert_timet_to_struct_tm (date, zone, &tm);
        format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR,
                 buf, 100);

        print_text_size_bold (
            context, buf, PANGO_ALIGN_LEFT,
            4, todo, 4,
            4 + 24);

        /* Print the day, e.g. 'Tuesday'. */
        format_date (&tm, DATE_DAYNAME, buf, 100);

        print_text_size_bold (
            context, buf, PANGO_ALIGN_LEFT,
            4, todo,
            HEADER_HEIGHT + 9,
            HEADER_HEIGHT + 9 + 18);

        date = time_add_day_with_zone (date, 1, zone);
     }
}

static void
print_work_week_background (GtkPrintContext *context,
                            GnomeCalendar *gcal,
                            time_t whence,
                            struct pdinfo *pdi,
                            gdouble left,
                            gdouble right,
                            gdouble top,
                            gdouble bottom)
{
    ECalModel *model;
    PangoFontDescription *font_hour, *font_minute;
    gdouble yinc, y;
    gdouble width = DAY_VIEW_TIME_COLUMN_WIDTH;
    gdouble day_width;
    gdouble font_size, max_font_size, hour_font_size, minute_font_size;
    gchar buf[20];
    const gchar *minute;
    const gint LONG_EVENT_OFFSET = 6;
    gboolean use_24_hour;
    gint i, hour, row;
    gdouble hour_minute_xl, hour_minute_xr;
    cairo_t *cr;

    model = gnome_calendar_get_model (gcal);
    use_24_hour = e_cal_model_get_use_24_hour_format (model);

    /* Fill the left time column in light-gray. */
    print_border (context, left, left + width, top, bottom, -1.0, 0.9);
    /* Fill the right time column in light-gray */
    print_border (context, right - width, right, top, bottom, -1.0, 0.9);

    /* Draw the border around the entire view. */
    cr = gtk_print_context_get_cairo_context (context);

    cairo_set_source_rgb (cr, 0, 0, 0);
    print_border (context, left, right, top, bottom, 1.0, -1.0);

    /* Draw the vertical line on the right of the time column. */
    cr = gtk_print_context_get_cairo_context (context);
    cairo_set_line_width (cr, 0.0);
    cairo_move_to (cr, left + width, bottom);
    cairo_line_to (cr, left + width, top);
    cairo_stroke (cr);

    cairo_move_to (cr, right - width, bottom);
    cairo_line_to (cr, right - width, top);
    cairo_stroke (cr);

    /* Calculate the row height. */
    if (top > bottom)
        yinc = (top - bottom) / (pdi->end_hour - pdi->start_hour);
    else
        yinc = (bottom - top) / (pdi->end_hour - pdi->start_hour);

        /* Get the 2 fonts we need. */
    font_size = yinc * 0.6;
    max_font_size = width * 0.45;
    hour_font_size = MIN (font_size, max_font_size);
    font_hour = get_font_for_size (hour_font_size, PANGO_WEIGHT_BOLD);

    font_size = yinc * 0.33;
    max_font_size = width * 0.2;
    minute_font_size = MIN (font_size, max_font_size);
    font_minute = get_font_for_size (minute_font_size, PANGO_WEIGHT_BOLD);
    hour_minute_xr = evo_calendar_print_renderer_get_width (
        context, font_minute, use_24_hour ? "00" : _("am"));
    if (!use_24_hour)
        hour_minute_xr = MAX (
            hour_minute_xr,
            evo_calendar_print_renderer_get_width (
                context, font_minute, _("pm")));

    row = 0;
    hour_minute_xl = left + width - hour_minute_xr - 3;
    hour_minute_xr = right - hour_minute_xr - 3;
    for (i = pdi->start_hour; i < pdi->end_hour; i++) {
        y = top + yinc * (row + 1);
        cr = gtk_print_context_get_cairo_context (context);
        cairo_set_source_rgb (cr, 0, 0, 0);

        if (use_24_hour) {
            hour = i;
            minute = "00";
        } else {
            if (i < 12)
                minute = _("am");
            else
                minute = _("pm");

            hour = i % 12;
            if (hour == 0)
                hour = 12;
        }

        /* the hour label/minute */
        sprintf (buf, "%d", hour);
        print_text (
            context, font_hour, buf, PANGO_ALIGN_RIGHT,
            left, hour_minute_xl,
            y - yinc, y - yinc + hour_font_size);
        print_text (
            context, font_minute, minute, PANGO_ALIGN_LEFT,
            hour_minute_xl, left + width - 3,
            y - yinc, y - yinc + minute_font_size);

        /* To the right */
        print_text (
            context, font_hour, buf, PANGO_ALIGN_RIGHT,
            right - width, hour_minute_xr,
            y - yinc, y - yinc + hour_font_size);
        print_text (
            context, font_minute, minute, PANGO_ALIGN_LEFT,
            hour_minute_xr, right - 3,
            y - yinc, y - yinc + minute_font_size);

                /* Draw the horizontal line between hours, across the entire
           width of the day view. */
        cr = gtk_print_context_get_cairo_context (context);
        cairo_move_to (cr, left, y);
        cairo_line_to (cr, right, y);
        cairo_set_line_width (cr, 1);
        cairo_stroke (cr);

        /* Draw the horizontal line for the 1/2-hours, across the
         * entire width except for part of the time column. */
        cairo_move_to (cr, left + width * 0.6, y - yinc / 2);
        cairo_line_to (cr, right, y - yinc / 2);
        cairo_set_line_width (cr, 1);
        cairo_stroke (cr);
        row++;
    }

    /* Draw the vertical lines for the days */
    day_width = (right - left - 2 *width) / pdi->days_shown;
    for (i = 0; i < pdi->days_shown - 1; ++i) {
      cr = gtk_print_context_get_cairo_context (context);
      cairo_move_to (cr, left + width + day_width * (i + 1), top);
      cairo_line_to (cr, left + width + day_width * (i + 1), bottom);
      cairo_set_line_width (cr, 1);
      cairo_stroke (cr);
    }

    /* And now the ones from the border to the hours, looks weird otherwise */
    cr = gtk_print_context_get_cairo_context (context);
    cairo_move_to (cr, left, HEADER_HEIGHT);
    cairo_line_to (cr, left, HEADER_HEIGHT + DAY_VIEW_ROW_HEIGHT + LONG_EVENT_OFFSET);

    cairo_move_to (cr, right, HEADER_HEIGHT);
    cairo_line_to (cr, right, HEADER_HEIGHT + DAY_VIEW_ROW_HEIGHT + LONG_EVENT_OFFSET);
    cairo_stroke (cr);

    pango_font_description_free (font_hour);
    pango_font_description_free (font_minute);
}

static void
print_work_week_day_details (GtkPrintContext *context,
                             GnomeCalendar *gcal,
                             time_t whence,
                             gdouble left,
                             gdouble right,
                             gdouble top,
                             gdouble bottom,
                             struct pdinfo *_pdi)
{
    ECalModel *model;
    icaltimezone *zone;
    EDayViewEvent *event;
    PangoFontDescription *font;
    time_t start, end;
    struct pdinfo pdi = { 0 };
    gint rows_in_top_display, i, rows_with_30_mins;
    gdouble font_size, max_font_size;
    cairo_t *cr;
    GdkPixbuf *pixbuf = NULL;
#define LONG_DAY_EVENTS_TOP_SPACING 4
#define LONG_DAY_EVENTS_BOTTOM_SPACING 2

    model = gnome_calendar_get_model (gcal);
    zone = e_cal_model_get_timezone (model);

    start = time_day_begin_with_zone (whence, zone);
    end = time_day_end_with_zone (start, zone);

    pdi.days_shown = 1;
    pdi.day_starts[0] = start;
    pdi.day_starts[1] = end;
    pdi.long_events = g_array_new (FALSE, FALSE, sizeof (EDayViewEvent));
    pdi.events[0] = g_array_new (FALSE, FALSE, sizeof (EDayViewEvent));
    pdi.start_hour = e_cal_model_get_work_day_start_hour (model);
    pdi.end_hour = e_cal_model_get_work_day_end_hour (model);
    if (e_cal_model_get_work_day_end_minute (model) != 0)
        pdi.end_hour++;
    pdi.mins_per_row = get_day_view_time_divisions ();
    pdi.rows = (pdi.end_hour - pdi.start_hour) * (60 / pdi.mins_per_row);
    pdi.start_minute_offset = pdi.start_hour * 60;
    pdi.end_minute_offset = pdi.end_hour * 60;
    pdi.use_24_hour_format = e_cal_model_get_use_24_hour_format (model);
    pdi.zone = e_cal_model_get_timezone (model);

    /* Get the events from the server. */
    e_cal_model_generate_instances_sync (model, start, end, print_day_details_cb, &pdi);
    qsort (
        pdi.long_events->data, pdi.long_events->len,
        sizeof (EDayViewEvent), e_day_view_event_sort_func);
    qsort (
        pdi.events[0]->data, pdi.events[0]->len,
        sizeof (EDayViewEvent), e_day_view_event_sort_func);

    pdi.start_hour = MIN (pdi.start_hour, _pdi->start_hour);
    pdi.end_hour = MAX (pdi.end_hour, _pdi->end_hour);

    /* TODO: This should be redundant */
    /* Also print events outside of work hours */
    if (pdi.events[0]->len > 0) {
        struct icaltimetype tt;

        event = &g_array_index (pdi.events[0], EDayViewEvent, 0);
        tt = icaltime_from_timet_with_zone (event->start, FALSE, zone);
        if (tt.hour < pdi.start_hour)
            pdi.start_hour = tt.hour;
        pdi.start_minute_offset = pdi.start_hour * 60;

        event = &g_array_index (pdi.events[0], EDayViewEvent, pdi.events[0]->len - 1);
        tt = icaltime_from_timet_with_zone (event->end, FALSE, zone);
        if (tt.hour > pdi.end_hour || tt.hour == 0) {
            pdi.end_hour = tt.hour ? tt.hour : 24;
            if (tt.minute > 0)
                pdi.end_hour++;
        }
        pdi.end_minute_offset = pdi.end_hour * 60;

        pdi.rows = (pdi.end_hour - pdi.start_hour) * (60 / pdi.mins_per_row);
    }

    /* Lay them out the long events, across the top of the page. */
    e_day_view_layout_long_events (
        pdi.long_events, pdi.days_shown,
        pdi.day_starts, &rows_in_top_display);

     /*Print the long events. */
    font = get_font_for_size (12, PANGO_WEIGHT_NORMAL);

    /* We always leave space for DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY in the
     * top display, but we may have more rows than that, in which case
     * the main display area will be compressed. */
    /* Limit long day event to half the height of the panel */
    rows_in_top_display = MIN (
        MAX (rows_in_top_display,
        DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY),
        (bottom - top) * 0.5 / DAY_VIEW_ROW_HEIGHT);

    if (rows_in_top_display > pdi.long_events->len)
        rows_in_top_display = pdi.long_events->len;

    for (i = 0; i < rows_in_top_display && i < pdi.long_events->len; i++) {
        event = &g_array_index (pdi.long_events, EDayViewEvent, i);
        print_day_long_event (
            context, font, left, right,
            top + LONG_DAY_EVENTS_TOP_SPACING, bottom,
            DAY_VIEW_ROW_HEIGHT, event, &pdi, model);
    }

    if (rows_in_top_display < pdi.long_events->len) {
        /* too many events */
        cairo_t *cr = gtk_print_context_get_cairo_context (context);
        gint x, y;

        if (!pixbuf) {
            const gchar **xpm = (const gchar **) jump_xpm;

            /* this ugly thing is here only to get rid of compiler warning
             * about unused 'jump_xpm_focused' */
            if (pixbuf)
                xpm = (const gchar **) jump_xpm_focused;

            pixbuf = gdk_pixbuf_new_from_xpm_data (xpm);
        }

        /* Right align - 10 comes from print_day_long_event  too */
        x = right - gdk_pixbuf_get_width (pixbuf) * 0.5 - 10;
        /* Placing '...' over the last all day event entry printed. '-1 -1' comes
            from print_long_day_event (top / bottom spacing in each cell) */
        y = top + LONG_DAY_EVENTS_TOP_SPACING
            + DAY_VIEW_ROW_HEIGHT * (i - 1)
            + (DAY_VIEW_ROW_HEIGHT - 1 - 1) * 0.5;

        cairo_save (cr);
        cairo_scale (cr, 0.5, 0.5);
        gdk_cairo_set_source_pixbuf (cr, pixbuf, x * 2.0, y * 2.0);
        cairo_paint (cr);
        cairo_restore (cr);
    }

    if (!rows_in_top_display)
        rows_in_top_display++;

    /* Draw the border around the long events. */
    cr = gtk_print_context_get_cairo_context (context);

    cairo_set_source_rgb (cr, 0, 0, 0);
    print_border (
        context, left, right,
        top, top + rows_in_top_display * DAY_VIEW_ROW_HEIGHT +
        LONG_DAY_EVENTS_TOP_SPACING + LONG_DAY_EVENTS_BOTTOM_SPACING,
        1.0, -1.0);

    /* Adjust the area containing the main display. */
    top += rows_in_top_display * DAY_VIEW_ROW_HEIGHT
        + LONG_DAY_EVENTS_TOP_SPACING
        + LONG_DAY_EVENTS_BOTTOM_SPACING;

    /* lay out the short events, within the day. */
    e_day_view_layout_day_events (
        pdi.events[0], CALC_DAY_VIEW_ROWS (pdi.mins_per_row),
        pdi.mins_per_row, pdi.cols_per_row, -1);

    /* use font like with 30 minutes time division */
    rows_with_30_mins = (pdi.end_hour - pdi.start_hour) * (60 / 30);

    /* print the short events. */
    if (top > bottom)
        max_font_size = ((top - bottom) / rows_with_30_mins) - 4;
    else
        max_font_size = ((bottom - top) / rows_with_30_mins) - 4;
    font_size = MIN (DAY_NORMAL_FONT_SIZE, max_font_size);
    font = get_font_for_size (font_size, PANGO_WEIGHT_NORMAL);

    for (i = 0; i < pdi.events[0]->len; i++) {
        event = &g_array_index (pdi.events[0], EDayViewEvent, i);
        print_day_event (
            context, font, left,
            right, top, bottom, event, &pdi, model);
    }

    /* Free everything. */
    if (pixbuf)
        g_object_unref (pixbuf);
    free_event_array (pdi.long_events);
    pango_font_description_free (font);
    g_array_free (pdi.long_events, TRUE);
    free_event_array (pdi.events[0]);
    g_array_free (pdi.events[0], TRUE);
}

/* Figure out what the overal hour limits are */
static gboolean
print_work_week_view_cb (ECalComponent *comp,
                         time_t istart,
                         time_t iend,
                         gpointer data)
{
    ECalModelGenerateInstancesData *mdata = (ECalModelGenerateInstancesData *) data;
    struct pdinfo *pdi = (struct pdinfo *) mdata->cb_data;
    struct icaltimetype tt;

    tt = icaltime_from_timet_with_zone (istart, FALSE, pdi->zone);
    pdi->start_hour = MIN (pdi->start_hour, tt.hour);

    tt = icaltime_from_timet_with_zone (iend, FALSE, pdi->zone);
    /* If we're past the hour, use the next one */
    pdi->end_hour = MAX (pdi->end_hour, tt.minute ? tt.hour + 1 : tt.hour);

    return TRUE;
}

static void
print_work_week_view (GtkPrintContext *context,
                      GnomeCalendar *gcal,
                      time_t date)
{
    GtkPageSetup *setup;
    icaltimezone *zone;
    time_t when, start, end;
    gdouble width, height, l;
    gdouble small_month_width;
    gdouble weeknum_inc;
    gint i, days = 5;
    gchar buf[100];
    const gint LONG_EVENT_OFFSET = 6;
    struct pdinfo pdi = { 0 };
    struct tm tm;
    gdouble day_width, day_x;
    ECalModel *model;

    model = gnome_calendar_get_model (gcal);
    zone = e_cal_model_get_timezone (model);

    setup = gtk_print_context_get_page_setup (context);

    width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
    height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);

    small_month_width = calc_small_month_width (context, HEADER_HEIGHT);
    weeknum_inc = get_show_week_numbers () ? small_month_width / 7.0 : 0;

    /* We always start on a Monday */
    start = time_week_begin_with_zone (date, 1, zone);
    end = time_add_day_with_zone (start, days, zone);

    pdi.days_shown = days;
    pdi.start_hour = e_cal_model_get_work_day_start_hour (model);
    pdi.end_hour = e_cal_model_get_work_day_end_hour (model);
    pdi.zone = zone;

    e_cal_model_generate_instances_sync (model, start, end, print_work_week_view_cb, &pdi);

    print_work_week_background (
        context, gcal, date, &pdi, 0.0, width,
        HEADER_HEIGHT + DAY_VIEW_ROW_HEIGHT + LONG_EVENT_OFFSET,
        height);

    print_border (context, 0.0, width, 0.0, HEADER_HEIGHT, 1.0, 0.9);

    /* Print the 2 mini calendar-months. */
    l = width - SMALL_MONTH_PAD - (small_month_width + weeknum_inc) * 2 -
        SMALL_MONTH_SPACING;

    print_month_small (
        context, gcal, start,
        l, 4, l + small_month_width + weeknum_inc, HEADER_HEIGHT + 4,
        DATE_MONTH | DATE_YEAR, start, end, FALSE);

    l += SMALL_MONTH_SPACING + small_month_width + weeknum_inc;
    print_month_small (
        context, gcal,
        time_add_month_with_zone (start, 1, zone),
        l, 4, l + small_month_width + weeknum_inc, HEADER_HEIGHT + 4,
        DATE_MONTH | DATE_YEAR, 0, 0, FALSE);

    /* Print the start day of the week, e.g. '7th May 2001'. */
    convert_timet_to_struct_tm (start, zone, &tm);
    format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100);
    print_text_size_bold (
        context, buf, PANGO_ALIGN_LEFT,
        3, width,
        4, 4 + 24);

    /* Print the end day of the week, e.g. '13th May 2001'. */
    /* We need to substract one or the wrong day will be printed */
    convert_timet_to_struct_tm (
        time_add_day_with_zone (end, -1, zone), zone, &tm);
    format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100);
    print_text_size_bold (
        context, buf, PANGO_ALIGN_LEFT,
        3, width,
        24 + 3, 24 + 3 + 24);

    /* Now print each days' events */
    day_width = (width - 2 *DAY_VIEW_TIME_COLUMN_WIDTH) / days;
    when = start;
    for (i = 0; i < days; ++i) {
        day_x = DAY_VIEW_TIME_COLUMN_WIDTH + day_width * i;

        /* Print the day, e.g. 'Tuesday'. */
        convert_timet_to_struct_tm (when, zone, &tm);
        format_date (&tm, DATE_DAYNAME, buf, 100);

        print_text_size_bold (
            context, buf, PANGO_ALIGN_LEFT,
            day_x + 4, day_x + day_width,
            HEADER_HEIGHT + 4, HEADER_HEIGHT + 4 + 18);

        print_work_week_day_details (
            context, gcal, when,
            day_x, day_x + day_width,
            HEADER_HEIGHT, height, &pdi);
        when = time_add_day_with_zone (when, 1, zone);
    }
}

static void
print_week_view (GtkPrintContext *context,
                 GnomeCalendar *gcal,
                 time_t date)
{
    GtkPageSetup *setup;
    ECalModel *model;
    icaltimezone *zone;
    gdouble l, week_numbers_inc, small_month_width;
    gchar buf[100];
    time_t when;
    GDateWeekday week_start_day;
    gint wday;
    struct tm tm;
    gdouble width, height;

    setup = gtk_print_context_get_page_setup (context);

    width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
    height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);
    small_month_width = calc_small_month_width (context, HEADER_HEIGHT);
    week_numbers_inc = get_show_week_numbers () ? small_month_width / 7.0 : 0;

    model = gnome_calendar_get_model (gcal);
    zone = e_cal_model_get_timezone (model);

    convert_timet_to_struct_tm (date, zone, &tm);
    week_start_day = e_cal_model_get_week_start_day (model);

    wday = e_weekday_to_tm_wday (week_start_day);
    when = time_week_begin_with_zone (date, wday, zone);

    /* If the week starts on a Sunday, we have to show the Saturday first,
     * since the weekend is compressed. */
    if (week_start_day == G_DATE_SUNDAY) {
        if (tm.tm_wday == 6)
            when = time_add_day_with_zone (when, 6, zone);
        else
            when = time_add_day_with_zone (when, -1, zone);
    }

    /* Print the main week view. */
    print_week_summary (
        context, gcal, when, FALSE, 1, 0,
        WEEK_EVENT_FONT_SIZE, WEEK_SMALL_FONT_SIZE,
        0.0, width,
        HEADER_HEIGHT + 20, height);

    /* Print the border around the main view. */
    print_border (
        context, 0.0, width, HEADER_HEIGHT ,
        height, 1.0, -1.0);

    /* Print the border around the header area. */
    print_border (
        context, 0.0, width,
        0.0, HEADER_HEIGHT + 2.0 + 20, 1.0, 0.9);

    /* Print the 2 mini calendar-months. */
    l = width - SMALL_MONTH_PAD - (small_month_width + week_numbers_inc) * 2
        - SMALL_MONTH_SPACING;
    print_month_small (
        context, gcal, when,
        l, 4, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 10,
        DATE_MONTH | DATE_YEAR, when,
        time_add_week_with_zone (when, 1, zone), FALSE);

    l += SMALL_MONTH_SPACING + small_month_width + week_numbers_inc;
    print_month_small (
        context, gcal,
        time_add_month_with_zone (when, 1, zone),
        l, 4, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 10,
        DATE_MONTH | DATE_YEAR, when,
        time_add_week_with_zone (when, 1, zone), FALSE);

    /* Print the start day of the week, e.g. '7th May 2001'. */
    convert_timet_to_struct_tm (when, zone, &tm);
    format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100);
    print_text_abs_bold (
        context, buf, WEEK_NORMAL_FONT_SIZE, PANGO_ALIGN_LEFT,
        3, width, 4, 4 + 24);

    /* Print the end day of the week, e.g. '13th May 2001'. */
    when = time_add_day_with_zone (when, 6, zone);
    convert_timet_to_struct_tm (when, zone, &tm);
    format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100);
    print_text_abs_bold (
        context, buf, WEEK_NORMAL_FONT_SIZE, PANGO_ALIGN_LEFT,
        3, width, 24 + 3, 24 + 3 + 24);
}

static void
print_month_view (GtkPrintContext *context,
                  GnomeCalendar *gcal,
                  time_t date)
{
    ECalModel *model;
    GtkPageSetup *setup;
    icaltimezone *zone;
    gchar buf[100];
    gdouble width, height;
    gdouble l, week_numbers_inc, small_month_width;
    struct tm tm;

    model = gnome_calendar_get_model (gcal);
    zone = e_cal_model_get_timezone (model);

    setup = gtk_print_context_get_page_setup (context);

    width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
    height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);
    small_month_width = calc_small_month_width (context, HEADER_HEIGHT);
    week_numbers_inc = get_show_week_numbers () ? small_month_width / 7.0 : 0;

    /* Print the main month view. */
    print_month_summary (context, gcal, date, 0.0, width, HEADER_HEIGHT, height);

    /* Print the border around the header. */
    print_border (context, 0.0, width, 0.0, HEADER_HEIGHT + 10, 1.0, 0.9);

    l = width - SMALL_MONTH_PAD - small_month_width - week_numbers_inc;

    /* Print the 2 mini calendar-months. */
    print_month_small (
        context, gcal,
        time_add_month_with_zone (date, 1, zone),
        l, 4, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 4,
        DATE_MONTH | DATE_YEAR, 0, 0, FALSE);

    print_month_small (
        context, gcal,
        time_add_month_with_zone (date, -1, zone),
        SMALL_MONTH_PAD, 4, SMALL_MONTH_PAD + small_month_width + week_numbers_inc, HEADER_HEIGHT + 4,
        DATE_MONTH | DATE_YEAR, 0, 0, FALSE);

    /* Print the month, e.g. 'May 2001'. */
    convert_timet_to_struct_tm (date, zone, &tm);
    format_date (&tm, DATE_MONTH | DATE_YEAR, buf, 100);
    print_text_size_bold (
        context, buf, PANGO_ALIGN_CENTER,
        3, width - 3,
        3, 3 + 24);

}

static gboolean
same_date (struct tm tm1,
           time_t t2,
           icaltimezone *zone)
{
    struct tm tm2;

    convert_timet_to_struct_tm (t2, zone, &tm2);

    return
        tm1.tm_mday == tm2.tm_mday &&
        tm1.tm_mon == tm2.tm_mon &&
        tm1.tm_year == tm2.tm_year;
}

static void
write_label_piece (time_t t,
                   time_t *start_cmp,
                   icaltimezone *zone,
                   gboolean use_24_hour_format,
                   gchar *buffer,
                   gint size,
                   gchar *stext,
                   const gchar *etext)
{
    struct tm tmp_tm;
    gint len;

    convert_timet_to_struct_tm (t, zone, &tmp_tm);

    if (stext != NULL)
        strcat (buffer, stext);

    len = strlen (buffer);
    if (start_cmp && same_date (tmp_tm, *start_cmp, zone))
        e_time_format_time (
            &tmp_tm, use_24_hour_format,
            FALSE, &buffer[len], size - len);
    else
        e_time_format_date_and_time (
            &tmp_tm, use_24_hour_format, FALSE,
            FALSE, &buffer[len], size - len);
    if (etext != NULL)
        strcat (buffer, etext);
}

static icaltimezone *
get_zone_from_tzid (ECalClient *client,
                    const gchar *tzid)
{
    icaltimezone *zone;

    /* Note that the timezones may not be on the server, so we try to get
     * the builtin timezone with the TZID first. */
    zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
    if (!zone && tzid) {
        GError *error = NULL;

        e_cal_client_get_timezone_sync (
            client, tzid, &zone, NULL, &error);

        if (error != NULL) {
            g_warning (
                "Couldn't get timezone '%s' from server: %s",
                tzid ? tzid : "", error->message);
            g_error_free (error);
        }
    }

    return zone;
}

static void
print_date_label (GtkPrintContext *context,
                  ECalComponent *comp,
                  ECalClient *client,
                  icaltimezone *zone,
                  gboolean use_24_hour_format,
                  gdouble left,
                  gdouble right,
                  gdouble top,
                  gdouble bottom)
{
    icaltimezone *start_zone, *end_zone, *due_zone, *completed_zone;
    ECalComponentDateTime datetime;
    time_t start = 0, end = 0, complete = 0, due = 0;
    static gchar buffer[1024];

    e_cal_component_get_dtstart (comp, &datetime);
    if (datetime.value) {
        start_zone = get_zone_from_tzid (client, datetime.tzid);
        if (!start_zone || datetime.value->is_date)
            start_zone = zone;
        start = icaltime_as_timet_with_zone (
            *datetime.value,
            start_zone);
    }
    e_cal_component_free_datetime (&datetime);

    e_cal_component_get_dtend (comp, &datetime);
    if (datetime.value) {
        end_zone = get_zone_from_tzid (client, datetime.tzid);
        if (!end_zone || datetime.value->is_date)
            end_zone = zone;
        end = icaltime_as_timet_with_zone (
            *datetime.value,
            end_zone);
    }
    e_cal_component_free_datetime (&datetime);

    e_cal_component_get_due (comp, &datetime);
    if (datetime.value) {
        due_zone = get_zone_from_tzid (client, datetime.tzid);
        if (!due_zone || datetime.value->is_date)
            due_zone = zone;
        due = icaltime_as_timet_with_zone (
            *datetime.value, due_zone);
    }
    e_cal_component_free_datetime (&datetime);

    e_cal_component_get_completed (comp, &datetime.value);
    if (datetime.value) {
        completed_zone = icaltimezone_get_utc_timezone ();
        complete = icaltime_as_timet_with_zone (
            *datetime.value, completed_zone);
        e_cal_component_free_icaltimetype (datetime.value);
    }

    buffer[0] = '\0';

    if (start > 0)
        write_label_piece (
            start, NULL, zone, use_24_hour_format,
            buffer, 1024, NULL, NULL);

    if (end > 0 && start > 0) {
        write_label_piece (
            end, &start, zone, use_24_hour_format,
            /* Translators: This is part of "START to END" text,
             * where START and END are date/times. */
            buffer, 1024, _(" to "), NULL);
    }

    if (complete > 0) {
        if (start > 0) {
            write_label_piece (
                complete, NULL, zone, use_24_hour_format,
                /* Translators: This is part of "START to END
                 * (Completed COMPLETED)", where COMPLETED is a
                 * completed date/time. */
                buffer, 1024, _(" (Completed "), ")");
        } else {
            write_label_piece (
                complete, &start, zone, use_24_hour_format,
                /* Translators: This is part of "Completed COMPLETED",
                 * where COMPLETED is a completed date/time. */
                buffer, 1024, _("Completed "), NULL);
        }
    }

    if (due > 0 && complete == 0) {
        if (start > 0) {
            write_label_piece (
                due, NULL, zone, use_24_hour_format,
                /* Translators: This is part of "START (Due DUE)",
                 * where START and DUE are dates/times. */
                buffer, 1024, _(" (Due "), ")");
        } else {
            write_label_piece (
                due, &start, zone, use_24_hour_format,
                /* Translators: This is part of "Due DUE",
                 * where DUE is a date/time due the event
                 * should be finished. */
                buffer, 1024, _("Due "), NULL);
        }
    }

    print_text_size_bold (
        context, buffer, PANGO_ALIGN_LEFT,
        left, right, top, top + 24);
}

static void
print_calendar_draw_page (GtkPrintOperation *operation,
                          GtkPrintContext *context,
                          gint page_nr,
                          PrintCalItem *pcali)
{
    switch (gnome_calendar_get_view (pcali->gcal)) {
        case GNOME_CAL_DAY_VIEW:
            print_day_view (context, pcali->gcal, pcali->start);
            break;
        case GNOME_CAL_WORK_WEEK_VIEW:
            print_work_week_view (context, pcali->gcal, pcali->start);
            break;
        case GNOME_CAL_WEEK_VIEW:
            print_week_view (context, pcali->gcal, pcali->start);
            break;
        case GNOME_CAL_MONTH_VIEW:
            print_month_view (context, pcali->gcal, pcali->start);
            break;
        default:
            g_return_if_reached ();
    }
}

void
print_calendar (GnomeCalendar *gcal,
                GtkPrintOperationAction action,
                time_t start)
{
    GtkPrintOperation *operation;
    PrintCalItem pcali;

    g_return_if_fail (gcal != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (gcal));

    if (gnome_calendar_get_view (gcal) == GNOME_CAL_MONTH_VIEW) {
        GnomeCalendarViewType view_type;
        ECalendarView *calendar_view;
        EWeekView *week_view;
        GDate date;
        gboolean multi_week_view;
        gint weeks_shown;

        view_type = gnome_calendar_get_view (gcal);
        calendar_view = gnome_calendar_get_calendar_view (gcal, view_type);
        week_view = E_WEEK_VIEW (calendar_view);
        weeks_shown = e_week_view_get_weeks_shown (week_view);
        multi_week_view = e_week_view_get_multi_week_view (week_view);
        e_week_view_get_first_day_shown (week_view, &date);

        if (multi_week_view &&
            weeks_shown >= 4 &&
            g_date_valid (&date)) {

            struct icaltimetype start_tt;

            g_date_add_days (&date, 7);

            start_tt = icaltime_null_time ();
            start_tt.is_date = TRUE;
            start_tt.year = g_date_get_year (&date);
            start_tt.month = g_date_get_month (&date);
            start_tt.day = g_date_get_day (&date);

            start = icaltime_as_timet (start_tt);
        } else if (multi_week_view) {
            start = week_view->day_starts[0];
        }
    }

    pcali.gcal = (GnomeCalendar *) gcal;
    pcali.start = start;

    operation = e_print_operation_new ();
    gtk_print_operation_set_n_pages (operation, 1);

    g_signal_connect (
        operation, "draw_page",
        G_CALLBACK (print_calendar_draw_page), &pcali);

    gtk_print_operation_run (operation, action, NULL, NULL);

    g_object_unref (operation);
}

/* returns number of required pages, when page_nr is -1 */
static gint
print_comp_draw_real (GtkPrintOperation *operation,
                      GtkPrintContext *context,
                      gint page_nr,
                      PrintCompItem *pci)
{
    GtkPageSetup *setup;
    PangoFontDescription *font;
    ECalClient *client;
    ECalComponent *comp;
    ECalComponentVType vtype;
    ECalComponentText text;
    GSList *desc, *l;
    GSList *contact_list, *elem;

    const gchar *title, *categories, *location;
    gchar *categories_string, *location_string, *summary_string;
    gdouble header_size;
    cairo_t *cr;
    gdouble width, height, page_start;
    gdouble top;
    gint pages = 1;

    setup = gtk_print_context_get_page_setup (context);

    width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
    height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);

    top = 0.0;

    /* Either draw only the right page or do not draw
     * anything when calculating number of pages. */
    if (page_nr != -1)
        top = top - ((page_nr) * height);
    else
        top = height;

    page_start = top;

        /* PrintCompItem structure contains elements to be used
         * with the Print Context , obtained in comp_draw_page
         */
    client = pci->client;
    comp = pci->comp;

    vtype = e_cal_component_get_vtype (comp);

    /* We should only be asked to print VEVENTs, VTODOs, or VJOURNALs. */
    if (vtype == E_CAL_COMPONENT_EVENT)
        title = _("Appointment");
    else if (vtype == E_CAL_COMPONENT_TODO)
        title = _("Task");
    else if (vtype == E_CAL_COMPONENT_JOURNAL)
        title = _("Memo");
    else
        return pages;

    cr = gtk_print_context_get_cairo_context (context);

    /* Print the title in a box at the top of the page. */
    font = get_font_for_size (18, PANGO_WEIGHT_BOLD);
    header_size = 40;

    if (page_nr == 0) {
        print_border (
            context, 0.0, width, 0.0, header_size,
            1.0, 0.9);
        print_text (
            context, font, title, PANGO_ALIGN_CENTER, 0.0, width,
            0.1, header_size - 0.1);
        pango_font_description_free (font);
    }

    top += header_size + 30;

    /* Summary */
    font = get_font_for_size (18, PANGO_WEIGHT_BOLD);
    e_cal_component_get_summary (comp, &text);
    summary_string = g_strdup_printf (_("Summary: %s"), text.value);
    top = bound_text (
        context, font, summary_string, -1, 0.0, top, width,
        height, FALSE, &page_start, &pages);

    g_free (summary_string);

    /* Location */
    e_cal_component_get_location (comp, &location);
    if (location && location[0]) {
        location_string = g_strdup_printf (
            _("Location: %s"),
            location);
        top = bound_text (
            context, font, location_string, -1, 0.0,
            top + 3, width, height, FALSE, &page_start, &pages);
        g_free (location_string);
    }

    /* Date information */
    if (page_nr == 0)
        print_date_label (
            context, comp, client,
            pci->zone, pci->use_24_hour_format,
            0.0, width, top + 3, top + 15);
    top += 20;

    /* Attendees */
    if ((page_nr == 0) && e_cal_component_has_attendees (comp)) {
        top = bound_text (
            context, font, _("Attendees: "), -1, 0.0,
            top, width, height, FALSE, &page_start, &pages);
        pango_font_description_free (font);
        font = get_font_for_size (12, PANGO_WEIGHT_NORMAL);
        top = print_attendees (
            context, font, cr, 0.0, width,
            top, height, comp, page_nr, &pages);
        top += get_font_size (font) - 6;
    }

    pango_font_description_free (font);

    font = get_font_for_size (12, PANGO_WEIGHT_NORMAL);

    /* For a VTODO we print the Status, Priority, % Complete and URL. */
    if (vtype == E_CAL_COMPONENT_TODO) {
        icalproperty_status status;
        const gchar *status_string = NULL;
        gint *percent;
        gint *priority;
        const gchar *url;

        /* Status */
        e_cal_component_get_status (comp, &status);
        if (status != ICAL_STATUS_NONE) {
            switch (status) {
            case ICAL_STATUS_NEEDSACTION:
                status_string = _("Not Started");
                break;
            case ICAL_STATUS_INPROCESS:
                status_string = _("In Progress");
                break;
            case ICAL_STATUS_COMPLETED:
                status_string = _("Completed");
                break;
            case ICAL_STATUS_CANCELLED:
                status_string = _("Canceled");
                break;
            default:
                break;
            }

            if (status_string) {
                gchar *status_text = g_strdup_printf (
                    _("Status: %s"),
                    status_string);
                top = bound_text (
                    context, font, status_text, -1,
                    0.0, top, width, height, FALSE, &page_start, &pages);
                top += get_font_size (font) - 6;
                g_free (status_text);
            }
        }

        /* Priority */
        e_cal_component_get_priority (comp, &priority);
        if (priority && *priority >= 0) {
            gchar *pri_text;

            pri_text = g_strdup_printf (
                _("Priority: %s"),
                e_cal_util_priority_to_string (*priority));
            top = bound_text (
                context, font, pri_text, -1,
                0.0, top, width, height, FALSE,
                &page_start, &pages);
            top += get_font_size (font) - 6;
            g_free (pri_text);
        }

        if (priority)
            e_cal_component_free_priority (priority);

        /* Percent Complete */
        e_cal_component_get_percent (comp, &percent);
        if (percent) {
            gchar *percent_string;

            percent_string = g_strdup_printf (_("Percent Complete: %i"), *percent);
            e_cal_component_free_percent (percent);

            top = bound_text (
                context, font, percent_string, -1,
                0.0, top, width, height, FALSE, &page_start, &pages);
            top += get_font_size (font) - 6;
        }

        /* URL */
        e_cal_component_get_url (comp, &url);
        if (url && url[0]) {
            gchar *url_string;

            url_string = g_strdup_printf (_("URL: %s"), url);

            top = bound_text (
                context, font, url_string, -1,
                0.0, top, width, height, TRUE, &page_start, &pages);
            top += get_font_size (font) - 6;
            g_free (url_string);
        }
    }

    /* Categories */
    e_cal_component_get_categories (comp, &categories);
    if (categories && categories[0]) {
        categories_string = g_strdup_printf (
            _("Categories: %s"), categories);
        top = bound_text (
            context, font, categories_string, -1,
            0.0, top, width, height, TRUE, &page_start, &pages);
        top += get_font_size (font) - 6;
        g_free (categories_string);
    }

    /* Contacts */
    e_cal_component_get_contact_list (comp, &contact_list);
    if (contact_list) {
        GString *contacts = g_string_new (_("Contacts: "));
        for (elem = contact_list; elem; elem = elem->next) {
            ECalComponentText *t = elem->data;
            /* Put a comma between contacts. */
            if (elem != contact_list)
                g_string_append (contacts, ", ");
            g_string_append (contacts, t->value);
        }
        e_cal_component_free_text_list (contact_list);

        top = bound_text (
            context, font, contacts->str, -1,
            0.0, top, width, height, TRUE, &page_start, &pages);
        top += get_font_size (font) - 6;
        g_string_free (contacts, TRUE);
    }
    top += 16;

    /* Description */
    e_cal_component_get_description_list (comp, &desc);
    for (l = desc; l != NULL; l = l->next) {
        ECalComponentText *ptext = l->data;
        const gchar *line, *next_line;

        for (line = ptext->value; line != NULL; line = next_line) {
            next_line = strchr (line, '\n');

            top = bound_text (
                context, font, line,
                next_line ? next_line - line : -1,
                0.0, top + 3, width, height, TRUE,
                &page_start, &pages);

            if (next_line) {
                next_line++;
                if (!*next_line)
                    next_line = NULL;
            }
        }

    }

    e_cal_component_free_text_list (desc);
    pango_font_description_free (font);

    return pages;
}

static void
print_comp_draw_page (GtkPrintOperation *operation,
                      GtkPrintContext *context,
                      gint page_nr,
                      PrintCompItem *pci)
{
    print_comp_draw_real (operation, context, page_nr, pci);
}

static void
print_comp_begin_print (GtkPrintOperation *operation,
                        GtkPrintContext *context,
                        PrintCompItem *pci)
{
    gint pages;

    pages = print_comp_draw_real (operation, context, -1, pci);

    gtk_print_operation_set_n_pages (operation, pages);
}

void
print_comp (ECalComponent *comp,
            ECalClient *cal_client,
            icaltimezone *zone,
            gboolean use_24_hour_format,
            GtkPrintOperationAction action)
{
    GtkPrintOperation *operation;
    PrintCompItem pci;

    g_return_if_fail (E_IS_CAL_COMPONENT (comp));

    pci.comp = comp;
    pci.client = cal_client;
    pci.zone = zone;
    pci.use_24_hour_format = use_24_hour_format;

    operation = e_print_operation_new ();
    gtk_print_operation_set_n_pages (operation, 1);

    g_signal_connect (
        operation, "begin-print",
        G_CALLBACK (print_comp_begin_print), &pci);

    g_signal_connect (
        operation, "draw-page",
        G_CALLBACK (print_comp_draw_page), &pci);

    gtk_print_operation_run (operation, action, NULL, NULL);

    g_object_unref (operation);
}

static void
print_title (GtkPrintContext *context,
             const gchar *text,
             gdouble page_width)
{
    PangoFontDescription *desc;
    PangoLayout *layout;
    cairo_t *cr;

    cr = gtk_print_context_get_cairo_context (context);

    desc = pango_font_description_from_string (FONT_FAMILY " Bold 18");

    layout = gtk_print_context_create_pango_layout (context);
    pango_layout_set_text (layout, text, -1);
    pango_layout_set_font_description (layout, desc);
    pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
    pango_layout_set_width (layout, pango_units_from_double (page_width));

    cairo_save (cr);

    cairo_move_to (cr, 0.0, 0.0);
    pango_cairo_show_layout (cr, layout);
    cairo_translate (cr, 0.0, 18);
    cairo_save (cr);
    cairo_restore (cr);

    g_object_unref (layout);

    pango_font_description_free (desc);
}

struct print_opts {
  EPrintable *printable;
  const gchar *print_header;
};

static void
print_table_draw_page (GtkPrintOperation *operation,
                       GtkPrintContext *context,
                       gint page_nr,
                       struct print_opts *opts)
{
    GtkPageSetup *setup;
    gdouble width;

    setup = gtk_print_context_get_page_setup (context);

    width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);

    do {
        /* TODO Allow the user to customize the title. */
        print_title (context, opts->print_header, width);

        if (e_printable_data_left (opts->printable))
            e_printable_print_page (
                opts->printable, context, width, 24, TRUE);

    } while (e_printable_data_left (opts->printable));

    g_free (opts);
}

void
print_table (ETable *table,
             const gchar *dialog_title,
             const gchar *print_header,
             GtkPrintOperationAction action)
{
    GtkPrintOperation *operation;
    EPrintable *printable;
    struct print_opts *opts;

    printable = e_table_get_printable (table);
    g_object_ref_sink (printable);
    e_printable_reset (printable);

    operation = e_print_operation_new ();
    gtk_print_operation_set_n_pages (operation, 1);

    opts = g_malloc (sizeof (struct print_opts));
    opts->printable = printable;
    opts->print_header = print_header;

    g_signal_connect (
        operation, "draw_page",
        G_CALLBACK (print_table_draw_page), opts);

    gtk_print_operation_run (operation, action, NULL, NULL);

    g_object_unref (operation);
}