aboutsummaryrefslogblamecommitdiffstats
path: root/calendar/gui/dialogs/comp-editor.c
blob: 4eaf1bce753a3a0de2a25eec4a844ffc1f01f128 (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                                                                        
  
                                                                


                                                               


                                                                  



                                                                    
                                                                             





                                                            
  





                    
                  
                  

                      
                  
                   
                           
                        
                    
                           

                                    
                          
                                            
                                  
                                  
                          
 
                     
                         
                      

                        
                         
                        
                       
                        
                             
                                    
                                           
                                            
 
                                              
                                
 



                                                     
            


                                              


                                           

                                     


                                                               



                                                                        
                           
                               
 
                                                        
                                  
 
                                        

                                       
 
                                                                          
                            
 


                               

                                        

                                 
                                   
 
                                        
                                 


                       
                                           

                                                                    
                   
                              
 







                                    
                         
                            
 

                       
                          
 

                              
                               
 
                        
  
 




                     
                           
                   






                                  

  




                                 
                                           

                                          
                                  
                    


                                  



                                             





                                       






                                   
                                           



                                  
                  




                                     

              
 
                                                                     
 

                                                                      


                                                                                


                                                                     
 


                                                                            
 





                                                                      
 

                                                 
                                                       
                                                        
 
      
                       
                    


                   








                                                                              
 



                                                                 
                              


                                               
















































                                                                      


                                        

                                
                           
                            

                                     
                                     
                
 





                                  


                                                                


                                              
 
                                                                 
                                                   
 




                                                       
                                                                                         
                                                        
 
                                       

                                                                         
 
                                     
                                 
 



                                                              
 

                                
 



                                                               

         


                                      

 
                                                                               

                                                                           






                                                         


                                              


                                       
                         



                                          



                                                                     












                                                              



                                                               


                                             

                                                                                         


                                                             




                                     


                                       
                                



                                     





                                                              
                         


                                                        









                                                           
                             
 
                                                              
                                                               



                                                       

                               


           


                                  


                                       


                                                                                      
 



                                                         

                                     

 
               
                              

                                
                              
                             

                                  
                 

                             
                              

                                     
                                
 



                            
 
                                               


                                                
 

                                                                  




                                                      





                                            

                                                               
                                                   
                                                           
                                                       

                                                                            
                                               
                                                         
                                                                                   

                                     

                                            
                                                  
                                                                             
         
 
                                                                              
                                                     

                                                                             
                                                        
            
                                                       
 
                                    
                           
 
                                                        

                                                                   
                                            
 


                                                                         
 
                        
 

                                                          
                                                                  
                                              

                                                                    

                                                                        


                                                              
                                                                         
                 
                
                                         
 




                                                                     
                                                              
 





                                                                           

                                                                             
 

                                                                        
                                                                         

                                                                                             



                                                                        

         



                                                                         




                                                                                        
                                            








                                                                                               

                                               
                                    
                 
         
 
                      
                                  
 





                                                                
                                                     
                                            
 

                                             
 

                                       

                             
                                          

                                                                                             

                                                                              
                                             
 

                                                                             
                                                                 
                                                                           
                                                                            
                            
                                                                 
                                                            

                                                                                            



                                                                          

                                                     
 

                                                                     
                                                             
                                                                              
 
                                                    

                 
                                                        
                                   

         

                               



                    
                                        

                                
                              

                                  
                                                             
                          
                                     

                            
 
                                               



                                                
                                                 
                                                
 


                                                                                         
 



                                                                            
 

                                
 
                                                                                      
                                                         
                                                                   

                                                                         


                                                   




                                                                     
 

                                                                                        
                                                                                              


                                                                               
                            


                                                                               
                        


                                                                       
                                             
 
                                     


                                                                             
                 
         



                    









                                                         
                                                









                                                                        
 






                                                                 
 



                                                        
 


                                               
 

                                                          
 


                                                                        
 

                                                        
 
                                                      
 




                                                                  

 


                                     
 

                                
 
                                                                 
                                                   
 
                                                                        
 
 
           







                                                  


                                    
                                   
 
                                                   
                                      

 
           

                                   
 



                                       





                                               

                                 

                                    

                                      


                                                                   



                                                                 



                                                                         
                                                                 
                                                         










                                               

                                 

                                    

                                      



                                                                   

                                                                 
 



                                                                         
                                                                 
                                                    



                              
               
                                        

                                         





                                                               


                                                                    






                                                                       
           
                                          
                                          

                                               

                                

                                   
                                 

                            
                                                         

                                                   
                                                             


                                                               
                                                                                          

                                                   




                                                                           
                    




                                                                          




                       
                                                                 
                                
                                              
                                                               
                                                   
                                                                                   
                              


                       





                                                                                                                   
                                                   




                                                                                   
                                   
                                                           
                                             
                                                                 
                                                        
                               

                                                            








                                                               
                                                    
                                                                            
                               
 

                                                              






                                                        
                                                      
                                                                             
                                                



                                                                   

                                                                               

                                                                                         


                                                                   

                                             


                                                                       


                                               

                                
 

                                                             
















                                                                
                                             

                                             


           
























































































                                                        
                                         

                                         
                           


                         

                                                 
 
                          


                        








                                                 




                         
                          

                                        
                            


                          

                                                 



                          
                       













                                                 
                                     

                                        






                                                      


                               
                       
                                
                                                 


























































                                              
                              


                                         
                                                     













































































                                                               


                                          
                                              







                                                                           























                                                             





                                                            




                                                            



































                                                               





























                                                                       





                                                                      





                                                              




                                                                



































                                                                              





                                                                       

                                    



















                                                                                  



                                     
                                                
 





                                                              




                                                     




                                                    


                                                  






                                                     





                                                              
                         
                                                      
                                                        
                                                  



                                            
                                    




                                                                   
                                                   
                                                  

                                                       

         
                                                                        

                                                                           
                                                                    







                                            


                                                  










                                                                    
                                                
 
                                                 






                                                                     




                                                             
                                                                        


           
                                              
 
                          
 
                                          
 
                                                                    



                                                                          

                                                              



                                                                    

                                                              



                                                                    

                                                                



                                                                      

                                                                   



                                                                        

                                                              



                                                                    

 





















                                                   
                                      
 
                                              












                                                            









                                                 
                                                
















                                                                         
                                                










                                                                       




                                               







                                                                     
                                                            

                                                

                                                                    

                                                                          
 
                                            




















                                            
                                          


















                                                            









                                             










                                                 







                                            










































































                                                                  








                                                               





                                     
                              
                                
                                     


                                   
                                     
                                       
                          

                             
                          
                      
                              
                            
                       

                             

                                                                 
                                       
                                                        
                                                    
 
                                                               
 





                                                                 

                                                                                  




                                                                 



                                   


                                   
                                    
                            
 
                                               

                                                               
 

                                    
                                                                   

                                 
 





                                                     


                                                            
                                            
                                                   

                                      


























                                                                                 





                                                           



                                                            
                                                               
                                            
                                                   

                                      


                                                         



                                                   






                                                                   
                                                   

                                      




















                                                                            


                                                           
                                                                




                                                         
                                                                                      




                                                                









                                                              
                            
                                                                               
                                                                                  
                                                             
         
 



                                                                          



                                                      
                                           

                                                   

                                                                        
                                                      

                                 
                           
                                                            
                                                     
                                         
         
 
                                                         
                                                            
 
                         








                                                                                

         
                                     
                                                                         
                        

                                                                    
            

                                                                 

                                               
 










                                                                           
 



                                                                  
 


                                                                               
                                
                                          

                                            
 










                                                                  
 
                                           
 

                                                              
                                                               


               

                                            

                                
                                 







                               
                                                                          
                                         
                                                                         
                                        
                                                      
                                                                       
                                                           
                                                                                           
                                      



                                                                                   





                                                                                                                           
                                                           




                                                                                           







                                                                       
                                                            
                                                                                    


                                                             
                                                     
                                                                         
                                                            















                                                         
                    
 
           

                                          
                               
 

                                                       
 
                                                                  

 



                                                             
                                               
 

                                                                   
                                                                      

                                                                    
                       


                                                        
 
                                                 

 
           
                                          
                                        

                                  

                                                               


                                        
 
    

                                                    
 

                                                   
                                                  




                                                 

                                                              
                                          


    

                                             
 

                                                   
                                          




                                             

                                                              










                                                   
 





                                                              

 
    

                                                                           
 
                          
 

                                                   








                                                                      
 


                                                                    

 

                                                   
 
                          
 
                                                          
 


                                                                              
 







                                                             







                                                   


                                                            




                                                   
                                                                
                                             
                                                                                         




                                                                 
                                                           













                                                             
 
 













                                                             


                                       


















                                                                
                                                                   

                       


















                                                                  


                                                                 


















                                                                 


                                                                     


















                                                                   


                                                                     


















                                                                   


                                                                         




                                                                     
   


                                              
  


                                                  

                                            
 

                              
 

                                                   
                                                               

                                                                        
           
                                               





                                                                 
 

                                                   
                                                                

                                                                     
 



                                                                 
                                                           



                                                       




                              
  
                                       
  





                                                                   




                                                              







                                                             





                                                   


                                         
                                    
                                                              



                                                     

                                          
 
                                                          








                                                             
                                        





                                                 
                                 
 


                                                             
                                                         
 
                                                         

 


                                                      
 
                                 
 

                                                             
 
                                                         
 
                                                              

 


                                                         
 
                                 
                          
 

                                                             
 

                                                                     
                                                    
 
                      

 


                                            

                    

                                                 
                                   

                                                    

                                                  

 



                                                    
  


                                                     

                                                
 
                                                   
 
                                              




                              
  
                                          
  





                                                                      
                                                              
 
                                        

 


                                       







                                                         


                                              


         


                                         







                                                         
                                                                                         


         
   










                                                             


                                            














                                                                   
                  
                                                                              


                                                                      
         

                                                                
                                                           


                                                   
                          






                                                     

                              
                                 
                                                             
                                                 
  
                                                     
                                     
    
    
                                            


                                              


                                
                                       
                               
 
                                                   
                                                      


                            
                            
 

                                                                             
                                    
 
                                                                   
                                                           
                                      
         
 
                                                         
                                               
 

                                                                   
 

                                              
                                                        
 
                  




                                                                      
         

                                                     



                                                            
                                                                
                                                              


                                                   
                          

                                                     
 




                                                                               





                                 
  


                                             

                                              



                                
 
                                                   


                                                      
 
                                                         


                                                                       
 
                                                                



                                                                          
 
                                                            

                                                        
                              



                         



           

    

                                            



                                
 
                                                   


                                                      
 

                                                                       
                                                                 


   
                          
                              
                                          
  


                                                                   
                                           
                                               
 
                                                   
                                                                              
 


                                                   

                                          
 

                                                          
 
                                              
 

                                                                        
 
                                                      


   
                          
                              
  
                                            
  

                                                  
            
                                           
 

                                                             
                                        

 
           






                                              
                        



















                                                                      





                                                                            



                                                                
                                                                      
                                                                   
                                                                     
                                  





                                                                    

                                        
 

                                

                                
 
                                                                 
                                                   
 
                                                                 
                                              
                                                              
                                                   

                       

         




                                                                     
                                        
 
                                                                   
                                                                      



                                                 

                                                         
                                                      
                                            
         

 
           
                                 
 

                                
                                

                          
 
                                                                 

                                                   
                            
 



                                                                                   


                                                                        
                                                              
                                                   

                                                                        
                                                                        
                                               
         
 



                                                                       
                                                                   
                                                     

                                                                               


                                                                       

 
           

                                    

                                
 




                                                   
                                            


                                  
                   
                                                          

                                                                  
 
                                                                  
                             
 
                                            
 
                              
 
                                                
 
                                    

 

                                                                   


                                                             
 
                           

                                
 


                                                                                        

                                                                                         

                                                                         
 
                                                                                              

                                                                          



                                                                                        
                 



         
 
           

                                              


                                

                                      

                                                            
 
                                                                                     






                                                                                      
                              

         
                       




                                                          
                                                                           





                                    
               


                                               

                                
                              

                                  
                                        
                              
                             
 
                                                              

                            
 
                                               


                                                
 


                                                                                      
                                        

                                                           
                                                                                                                    










                                                                                       
 

                                                               
                                                                             
                                                             
 

                                                                  
                                           

                                                               



                                                                                  
 
                                                            


                                                                 
                                    
                                                                      

                                                                                
                                                   

                                    

                                                                          
                                           
                                                  
 




                                                                                          
 


                                                                




                                                                                     
                                                                            
                                                   
                 
 
                                    
                                                                      

                                                                                

                                                            
                                                   




                                                                       
                 
         
 

                                   
                                               
 
                     
 

 



                              
  


                                                

                                           
 
                               
 
                                                   
                                                     
 
                                               
 

                                                

 
               

                                         

                                                             
                                  

 

                               


                                                                       
  
                                                                              
    
               

                                                 
 
                                
                            
                 
                               
 



                                                             
                                                  
                                                          
                            
                                                               
                                                          

                                                                                                   
         
 


                                  




                         


           
    
        

                                          
 
                                                      



                           


           



                                            
                                
                         





                                                   

                                                             


                                                    
            


                                                     
                              



                         



           
    
        


                                                      
 
                               
 
                                                              
 
                                               
 
                             
                                                                       

                     

 
        

                                      
                       
 
                                                              

                                   
 
                                                       
                  
                                      

                     

 




                                                                       
                                                     
 



                                
                                              


                                   
                                                                 
                                                   
 

                                                             
 

                                        
                                          
                                         

                                       

                                         










                                                                              

                                                                            
                                                                              
 


                                                                           

                                                          
                                                                      
 

                                                                 
                                                                 
                                                          

                                                                            
                                                                   


                                                               



                                                                    
 
                                                                   
                                                                 
                                                            
 
                                                                            
 
                                        
 
         
 
                           

 
           
                                          
                                                  
                                            
 
                                               
                 
 
                                                     
                                                                                        
                                                                    
 




                                                                       
                                                 

                                    

 
           

                                       
                                    
 
                                
                                   


                            
                                                                         

                                                                          





                                                                                  









                                                                     
 
                                                      
                 

                                      
         


           

                                     
                                   
 
                                               
 

                                                                      
                                      
 
/*
 * Evolution calendar - Framework for a calendar component editor dialog
 *
 * 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:
 *      Federico Mena-Quintero <federico@ximian.com>
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

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

#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <glib/gi18n-lib.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <gdk/gdkkeysyms.h>
#include <libebackend/libebackend.h>

#include <e-util/e-util.h>
#include <libevolution-utils/e-alert-sink.h>
#include <e-util/e-dialog-utils.h>
#include <e-util/e-util-private.h>
#include <shell/e-shell.h>

#include "../print.h"
#include "../comp-util.h"
#include "save-comp.h"
#include "delete-comp.h"
#include "send-comp.h"
#include "changed-comp.h"
#include "cancel-comp.h"
#include "recur-comp.h"
#include "comp-editor.h"
#include "comp-editor-util.h"
#include "../calendar-config-keys.h"
#include "widgets/misc/e-attachment-view.h"
#include "widgets/misc/e-attachment-paned.h"

#include "libevolution-utils/e-alert-dialog.h"
#include "e-util/e-ui-manager.h"

#define COMP_EDITOR_GET_PRIVATE(obj) \
    (G_TYPE_INSTANCE_GET_PRIVATE \
    ((obj), TYPE_COMP_EDITOR, CompEditorPrivate))

#define d(x)

/* Private part of the CompEditor structure */
struct _CompEditorPrivate {

    gpointer shell;  /* weak pointer */

    GSettings *calendar_settings;

    /* EFocusTracker keeps selection actions up-to-date. */
    EFocusTracker *focus_tracker;

    /* Each CompEditor window gets its own GtkWindowGroup, so it
     * doesn't block the main window or other CompEditor windows. */
    GtkWindowGroup *window_group;

    /* Client to use */
    ECalClient *cal_client;

    /* Source client (where comp lives currently) */
    ECalClient *source_client;

    /* View to listen for changes */
    ECalClientView *view;
    GCancellable *view_cancellable;

    /* Calendar object/uid we are editing; this is an internal copy */
    ECalComponent *comp;

    /* The pages we have */
    GList *pages;

    /* Notebook to hold the pages */
    GtkNotebook *notebook;

    /* Attachment handling */
    GtkWidget *attachment_view;

    /* Manages menus and toolbars */
    GtkUIManager *ui_manager;

    gchar *summary;

    guint32 attachment_bar_visible : 1;

    /* TODO use this flags for setting all the boolean variables
     * below */
    CompEditorFlags flags;

    icaltimezone *zone;
    gboolean use_24_hour_format;

    gint work_day_end_hour;
    gint work_day_end_minute;
    gint work_day_start_hour;
    gint work_day_start_minute;

    gboolean changed;
    gboolean needs_send;

    gboolean saved;

    CalObjModType mod;

    gboolean existing_org;
    gboolean user_org;
    gboolean is_group_item;

    gboolean warned;
};

enum {
    PROP_0,
    PROP_CHANGED,
    PROP_CLIENT,
    PROP_FLAGS,
    PROP_FOCUS_TRACKER,
    PROP_SHELL,
    PROP_SUMMARY,
    PROP_TIMEZONE,
    PROP_USE_24_HOUR_FORMAT,
    PROP_WORK_DAY_END_HOUR,
    PROP_WORK_DAY_END_MINUTE,
    PROP_WORK_DAY_START_HOUR,
    PROP_WORK_DAY_START_MINUTE
};

static const gchar *ui =
"<ui>"
"  <menubar action='main-menu'>"
"    <menu action='file-menu'>"
"      <menuitem action='save'/>"
"      <menuitem action='save-and-close'/>"
"      <separator/>"
"      <menuitem action='print-preview'/>"
"      <menuitem action='print'/>"
"      <separator/>"
"      <menuitem action='close'/>"
"    </menu>"
"    <menu action='edit-menu'>"
"      <menuitem action='cut-clipboard'/>"
"      <menuitem action='copy-clipboard'/>"
"      <menuitem action='paste-clipboard'/>"
"      <menuitem action='delete-selection'/>"
"      <separator/>"
"      <menuitem action='select-all'/>"
"    </menu>"
"    <menu action='view-menu'/>"
"    <menu action='insert-menu'>"
"      <menuitem action='attach'/>"
"    </menu>"
"    <menu action='options-menu'/>"
"    <menu action='help-menu'>"
"      <menuitem action='help'/>"
"    </menu>"
"  </menubar>"
"  <toolbar name='main-toolbar'>"
"    <toolitem action='save-and-close'/>\n"
"    <toolitem action='save'/>\n"
"#if !EXPRESS\n"
"    <toolitem action='print'/>\n"
"#endif\n"
"    <separator/>"
"    <placeholder name='content'/>\n"
"#if EXPRESS\n"
"    <separator expand='true'/>\n"
"    <toolitem action='close'/>\n"
"#endif\n"
"  </toolbar>"
"</ui>";

static void comp_editor_show_help       (CompEditor *editor);

static void real_edit_comp          (CompEditor *editor,
                         ECalComponent *comp);
static gboolean real_send_comp          (CompEditor *editor,
                         ECalComponentItipMethod method,
                         gboolean strip_alarms);
static gboolean prompt_and_save_changes     (CompEditor *editor,
                         gboolean send);
static void close_dialog            (CompEditor *editor);

static void page_dates_changed_cb       (CompEditor *editor,
                         CompEditorPageDates *dates,
                         CompEditorPage *page);

static void obj_modified_cb         (ECalClientView *view,
                         const GSList *objs,
                         CompEditor *editor);
static void obj_removed_cb          (ECalClientView *view,
                         const GSList *uids,
                         CompEditor *editor);

G_DEFINE_TYPE_WITH_CODE (
    CompEditor, comp_editor, GTK_TYPE_WINDOW,
    G_IMPLEMENT_INTERFACE (E_TYPE_ALERT_SINK, NULL)
    G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))

enum {
    OBJECT_CREATED,
    COMP_CLOSED,
    LAST_SIGNAL
};

static guint signals[LAST_SIGNAL];
static GList *active_editors;

static void
comp_editor_weak_notify_cb (gpointer unused,
                            GObject *where_the_object_was)
{
    active_editors = g_list_remove (active_editors, where_the_object_was);
}

static void
attachment_store_changed_cb (CompEditor *editor)
{
    /* Mark the editor as changed so it prompts about unsaved
     * changes on close */
    comp_editor_set_changed (editor, TRUE);
}

static void
attachment_save_finished (EAttachmentStore *store,
                          GAsyncResult *result,
                          gpointer user_data)
{
    GtkWidget *dialog;
    const gchar *primary_text;
    gchar **uris;
    GError *error = NULL;

    struct {
        gchar **uris;
        gboolean done;
        GtkWindow *parent;
    } *status = user_data;

    uris = e_attachment_store_save_finish (store, result, &error);

    status->uris = uris;
    status->done = TRUE;

    if (uris != NULL)
        goto exit;

    /* Ignore cancellations. */
    if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
        goto exit;

    primary_text = _("Could not save attachments");

    dialog = gtk_message_dialog_new_with_markup (
        status->parent, GTK_DIALOG_DESTROY_WITH_PARENT,
        GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
        "<big><b>%s</b></big>", primary_text);

    gtk_message_dialog_format_secondary_text (
        GTK_MESSAGE_DIALOG (dialog), "%s", error->message);

    gtk_dialog_run (GTK_DIALOG (dialog));

    gtk_widget_destroy (dialog);

exit:
    if (error != NULL)
        g_error_free (error);

    g_object_unref (status->parent);
}

static GSList *
get_attachment_list (CompEditor *editor)
{
    EAttachmentStore *store;
    EAttachmentView *view;
    GFile *destination;
    GSList *list = NULL;
    const gchar *comp_uid = NULL;
    const gchar *local_store;
    gchar *filename_prefix, *tmp;
    gint ii;

    struct {
        gchar **uris;
        gboolean done;
        GtkWindow *parent;
    } status;

    e_cal_component_get_uid (editor->priv->comp, &comp_uid);
    g_return_val_if_fail (comp_uid != NULL, NULL);

    status.uris = NULL;
    status.done = FALSE;
    status.parent = g_object_ref (editor);

    view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
    store = e_attachment_view_get_store (view);

    tmp = g_strdup (comp_uid);
    e_filename_make_safe (tmp);
    filename_prefix = g_strconcat (tmp, "-", NULL);
    g_free (tmp);

    local_store = e_cal_client_get_local_attachment_store (editor->priv->cal_client);
    destination = g_file_new_for_path (local_store);

    e_attachment_store_save_async (
        store, destination, filename_prefix,
        (GAsyncReadyCallback) attachment_save_finished, &status);

    g_object_unref (destination);
    g_free (filename_prefix);

    /* We can't return until we have results, so crank
     * the main loop until the callback gets triggered. */
    while (!status.done)
        gtk_main_iteration ();

    if (status.uris == NULL)
        return NULL;

    /* Transfer the URI strings to the GSList. */
    for (ii = 0; status.uris[ii] != NULL; ii++) {
        list = g_slist_prepend (list, status.uris[ii]);
        status.uris[ii] = NULL;
    }

    g_free (status.uris);

    return g_slist_reverse (list);
}

/* This sets the focus to the toplevel, so any field being edited is committed.
 * FIXME: In future we may also want to check some of the fields are valid,
 * e.g. the EDateEdit fields. */
static void
commit_all_fields (CompEditor *editor)
{
    gtk_window_set_focus (GTK_WINDOW (editor), NULL);
}

static void
changes_view_ready_cb (GObject *source_object,
                       GAsyncResult *result,
                       gpointer user_data)
{
    CompEditor *editor = user_data;
    ECalClientView *view = NULL;
    gboolean success;
    GError *error = NULL;

    g_return_if_fail (editor != NULL);

    success = e_cal_client_get_view_finish (
        E_CAL_CLIENT (source_object), result, &view, &error);

    if (!success)
        view = NULL;

    if (view) {
        editor->priv->view = view;
        g_signal_connect (
            view, "objects_modified",
            G_CALLBACK (obj_modified_cb), editor);
        g_signal_connect (
            view, "objects_removed",
            G_CALLBACK (obj_removed_cb), editor);

        e_cal_client_view_start (view, &error);

        if (error != NULL) {
            g_warning (
                "%s: Failed to start view: %s",
                G_STRFUNC, error->message);
            g_error_free (error);
        }
    } else if (error) {
        if (!g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) &&
            !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
            g_warning (
                "%s: Failed to get view: %s",
                G_STRFUNC, error->message);
        g_error_free (error);
    }
}

static void
listen_for_changes (CompEditor *editor)
{
    CompEditorPrivate *priv;
    const gchar *uid = NULL;

    priv = editor->priv;

    /* Discard change listener */
    if (priv->view_cancellable) {
        g_cancellable_cancel (priv->view_cancellable);
        g_object_unref (priv->view_cancellable);
        priv->view_cancellable = NULL;
    }

    if (priv->view) {
        g_signal_handlers_disconnect_matched (
            priv->view, G_SIGNAL_MATCH_DATA,
            0, 0, NULL, NULL, editor);

        g_object_unref (priv->view);
        priv->view = NULL;
    }

    /* Listen for changes */
    if (priv->comp)
        e_cal_component_get_uid (priv->comp, &uid);

    if (uid) {
        gchar *query;

        priv->view_cancellable = g_cancellable_new ();
        query = g_strdup_printf ("(uid? \"%s\")", uid);
        e_cal_client_get_view (
            priv->source_client,
            query, priv->view_cancellable,
            changes_view_ready_cb, editor);
        g_free (query);
    }
}

static void
send_timezone (gpointer key,
               gpointer value,
               gpointer user_data)
{
    icaltimezone *zone = value;
    CompEditor *editor = user_data;
    GError *error = NULL;

    e_cal_client_add_timezone_sync (editor->priv->cal_client, zone, NULL, &error);

    if (error != NULL) {
        g_warning (
            "%s: Failed to add timezone: %s",
            G_STRFUNC, error->message);
        g_error_free (error);
    }
}

static gboolean
save_comp (CompEditor *editor)
{
    CompEditorPrivate *priv;
    CompEditorFlags flags;
    ECalComponent *clone;
    ESourceRegistry *registry;
    EShell *shell;
    GList *l;
    gboolean result;
    GError *error = NULL;
    GHashTable *timezones;
    const gchar *orig_uid = NULL;
    gchar *orig_uid_copy;
    icalcomponent *icalcomp;

    priv = editor->priv;

    if (!priv->changed)
        return TRUE;

    flags = comp_editor_get_flags (editor);
    shell = comp_editor_get_shell (editor);

    registry = e_shell_get_registry (shell);

    /* Stop listening because we are about to change things */
    if (priv->view) {
        g_signal_handlers_disconnect_matched (
            priv->view,
            G_SIGNAL_MATCH_DATA,
            0, 0, NULL, NULL,
            editor);

        g_object_unref (priv->view);
        priv->view = NULL;
    }

    /* Update on the server */
    timezones = g_hash_table_new (g_str_hash, g_str_equal);

    clone = e_cal_component_clone (priv->comp);
    comp_editor_copy_new_attendees (clone, priv->comp);
    for (l = priv->pages; l != NULL; l = l->next) {
        if (IS_COMP_EDITOR_PAGE (l->data) &&
            !comp_editor_page_fill_component (l->data, clone)) {
            g_object_unref (clone);
            g_hash_table_destroy (timezones);
            comp_editor_show_page (editor, COMP_EDITOR_PAGE (l->data));
            return FALSE;
        }

        /* retrieve all timezones */
        if (IS_COMP_EDITOR_PAGE (l->data))
            comp_editor_page_fill_timezones (l->data, timezones);
    }

    /* If we are not the organizer, we don't update the sequence number */
    if (!e_cal_component_has_organizer (clone) ||
        itip_organizer_is_user (registry, clone, priv->cal_client) ||
        itip_sentby_is_user (registry, clone, priv->cal_client))
        e_cal_component_commit_sequence (clone);
    else
        e_cal_component_abort_sequence (clone);

    g_object_unref (priv->comp);
    priv->comp = clone;

    e_cal_component_get_uid (priv->comp, &orig_uid);
    /* Make a copy of it, because call of e_cal_create_object()
     * rewrites the internal uid. */
    orig_uid_copy = g_strdup (orig_uid);

    /* send timezones */
    g_hash_table_foreach (timezones, (GHFunc) send_timezone, editor);
    g_hash_table_destroy (timezones);

    /* Attachments*/

    e_cal_component_set_attachment_list (
        priv->comp, get_attachment_list (editor));
    icalcomp = e_cal_component_get_icalcomponent (priv->comp);
    /* send the component to the server */
    if (!cal_comp_is_on_server (priv->comp, priv->cal_client)) {
        gchar *uid = NULL;
        result = e_cal_client_create_object_sync (
            priv->cal_client, icalcomp, &uid, NULL, &error);
        if (result) {
            icalcomponent_set_uid (icalcomp, uid);
            g_free (uid);
            g_signal_emit_by_name (editor, "object_created");
        }
    } else {
        gboolean has_recurrences;

        has_recurrences =
            e_cal_component_has_recurrences (priv->comp);

        if (has_recurrences && priv->mod == CALOBJ_MOD_ALL)
            comp_util_sanitize_recurrence_master (
                priv->comp, priv->cal_client);

        if (priv->mod == CALOBJ_MOD_THIS) {
            e_cal_component_set_rdate_list (priv->comp, NULL);
            e_cal_component_set_rrule_list (priv->comp, NULL);
            e_cal_component_set_exdate_list (priv->comp, NULL);
            e_cal_component_set_exrule_list (priv->comp, NULL);
        }
        result = e_cal_client_modify_object_sync (
            priv->cal_client, icalcomp, priv->mod, NULL, &error);

        if (priv->mod == CALOBJ_MOD_THIS) {
            if (result && ((flags & COMP_EDITOR_DELEGATE) ||
                !e_cal_component_has_organizer (clone) ||
                itip_organizer_is_user (registry, clone, priv->cal_client) ||
                itip_sentby_is_user (registry, clone, priv->cal_client)))
                e_cal_component_commit_sequence (clone);
            else
                e_cal_component_abort_sequence (clone);
        }
    }

    /* If the delay delivery is set, the items will not be created in
     * the server immediately, so we need not show them in the view.
     * They will appear as soon as the server creates it after the
     * delay period. */
    if (result && e_cal_component_has_attendees (priv->comp)) {
        gboolean delay_set = FALSE;
        icalproperty *icalprop;
        icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
        while (icalprop) {
            const gchar *x_name;

            x_name = icalproperty_get_x_name (icalprop);
            if (!strcmp (x_name, "X-EVOLUTION-OPTIONS-DELAY")) {
                delay_set = TRUE;
                break;
            }

            icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
        }
        if (delay_set) {
            g_free (orig_uid_copy);
            return TRUE;
        }
    }

    if (!result) {
        GtkWidget *dialog;

        dialog = gtk_message_dialog_new (
            NULL, 0,
            GTK_MESSAGE_ERROR,
            GTK_BUTTONS_OK,
            "%s", (error != NULL) ? error->message :
            _("Could not update object"));
        gtk_dialog_run (GTK_DIALOG (dialog));
        gtk_widget_destroy (dialog);

        if (error)
            g_error_free (error);

        g_free (orig_uid_copy);

        return FALSE;
    } else {
        if (priv->source_client &&
            !e_source_equal (e_client_get_source (E_CLIENT (priv->cal_client)),
                     e_client_get_source (E_CLIENT (priv->source_client))) &&
            cal_comp_is_on_server (priv->comp, priv->source_client)) {
            /* Comp found a new home. Remove it from old one. */
            GError *error = NULL;

            if (e_cal_component_is_instance (priv->comp) ||
                e_cal_component_has_recurrences (priv->comp))
                e_cal_client_remove_object_sync (
                    priv->source_client, orig_uid_copy,
                    NULL, CALOBJ_MOD_ALL, NULL, &error);
            else
                e_cal_client_remove_object_sync (
                    priv->source_client,
                    orig_uid_copy, NULL, CALOBJ_MOD_THIS, NULL, &error);

            if (error != NULL) {
                g_warning (
                    "%s: Failed to remove object: %s",
                    G_STRFUNC, error->message);
                g_error_free (error);
            }

            /* Let priv->source_client point to new home,
             * so we can move it again this session. */
            g_object_unref (priv->source_client);
            priv->source_client = g_object_ref (priv->cal_client);

            listen_for_changes (editor);
        }

        comp_editor_set_changed (editor, FALSE);
        priv->saved = TRUE;
    }

    g_free (orig_uid_copy);

    return TRUE;
}

static gboolean
save_comp_with_send (CompEditor *editor)
{
    CompEditorPrivate *priv;
    CompEditorFlags flags;
    ESourceRegistry *registry;
    EShell *shell;
    gboolean send, delegated, only_new_attendees = FALSE;
    gboolean delegate;
    gboolean strip_alarms = TRUE;

    priv = editor->priv;

    flags = comp_editor_get_flags (editor);
    shell = comp_editor_get_shell (editor);

    registry = e_shell_get_registry (shell);

    send = priv->changed && priv->needs_send;
    delegate = flags & COMP_EDITOR_DELEGATE;

    if (delegate) {
        icalcomponent *icalcomp = e_cal_component_get_icalcomponent (priv->comp);
        icalproperty *icalprop;

        icalprop = icalproperty_new_x ("1");
        icalproperty_set_x_name (icalprop, "X-EVOLUTION-DELEGATED");
        icalcomponent_add_property (icalcomp, icalprop);
    }

    if (!save_comp (editor))
        return FALSE;

    delegated = delegate && !e_cal_client_check_save_schedules (priv->cal_client);
    if (delegated || (send && send_component_dialog (
        (GtkWindow *) editor, priv->cal_client, priv->comp,
        !priv->existing_org, &strip_alarms, !priv->existing_org ?
        NULL : &only_new_attendees))) {
        if (delegated)
            only_new_attendees = FALSE;

        comp_editor_set_flags (
            editor, (comp_editor_get_flags (editor) &
            (~COMP_EDITOR_SEND_TO_NEW_ATTENDEES_ONLY)) |
            (only_new_attendees ?
            COMP_EDITOR_SEND_TO_NEW_ATTENDEES_ONLY : 0));

        if ((itip_organizer_is_user (registry, priv->comp, priv->cal_client) ||
            itip_sentby_is_user (registry, priv->comp, priv->cal_client))) {
            if (e_cal_component_get_vtype (priv->comp) == E_CAL_COMPONENT_JOURNAL)
                return comp_editor_send_comp (
                    editor, E_CAL_COMPONENT_METHOD_PUBLISH,
                    strip_alarms);
            else
                return comp_editor_send_comp (
                    editor, E_CAL_COMPONENT_METHOD_REQUEST,
                    strip_alarms);
        } else {
            if (!comp_editor_send_comp (
                editor, E_CAL_COMPONENT_METHOD_REQUEST,
                strip_alarms))
                return FALSE;

            if (delegate)
                return comp_editor_send_comp (
                    editor, E_CAL_COMPONENT_METHOD_REPLY,
                    strip_alarms);
        }
    }

    return TRUE;
}

static void
update_window_border (CompEditor *editor,
                      const gchar *description)
{
    const gchar *icon_name;
    const gchar *format;
    gchar *title;

    if (editor->priv->comp == NULL) {
        title = g_strdup (_("Edit Appointment"));
        icon_name = "x-office-calendar";
        goto exit;

    } else switch (e_cal_component_get_vtype (editor->priv->comp)) {
        case E_CAL_COMPONENT_EVENT:
            if (editor->priv->is_group_item)
                format = _("Meeting - %s");
            else
                format = _("Appointment - %s");
            icon_name = "appointment-new";
            break;

        case E_CAL_COMPONENT_TODO:
            if (editor->priv->is_group_item)
                format = _("Assigned Task - %s");
            else
                format = _("Task - %s");
            icon_name = "stock_task";
            break;

        case E_CAL_COMPONENT_JOURNAL:
            format = _("Memo - %s");
            icon_name = "stock_insert-note";
            break;

        default:
            g_return_if_reached ();
    }

    if (description == NULL || *description == '\0') {
        ECalComponentText text;

        e_cal_component_get_summary (editor->priv->comp, &text);
        description = text.value;
    }

    if (description == NULL || *description == '\0')
        description = _("No Summary");

    title = g_strdup_printf (format, description);

exit:
    gtk_window_set_icon_name (GTK_WINDOW (editor), icon_name);
    gtk_window_set_title (GTK_WINDOW (editor), title);

    g_free (title);
}

static void
action_attach_cb (GtkAction *action,
                  CompEditor *editor)
{
    EAttachmentStore *store;
    EAttachmentView *view;

    view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
    store = e_attachment_view_get_store (view);

    e_attachment_store_run_load_dialog (store, GTK_WINDOW (editor));
}

static void
action_classification_cb (GtkRadioAction *action,
                          GtkRadioAction *current,
                          CompEditor *editor)
{
    comp_editor_set_changed (editor, TRUE);
}

static void
action_close_cb (GtkAction *action,
                 CompEditor *editor)
{
    commit_all_fields (editor);

    if (prompt_and_save_changes (editor, TRUE))
        close_dialog (editor);
}

static void
action_help_cb (GtkAction *action,
                CompEditor *editor)
{
    comp_editor_show_help (editor);
}

static void
action_print_cb (GtkAction *action,
                 CompEditor *editor)
{
    CompEditorPrivate *priv = editor->priv;
    ECalComponent *comp;
    GList *l;
    icalcomponent *component;
    icalcomponent *clone;
    icaltimezone *zone;
    gboolean use_24_hour_format;

    comp = e_cal_component_new ();
    component = e_cal_component_get_icalcomponent (priv->comp);
    clone = icalcomponent_new_clone (component);
    e_cal_component_set_icalcomponent (comp, clone);

    for (l = priv->pages; l != NULL; l = l->next)
         comp_editor_page_fill_component (l->data, comp);

    zone = comp_editor_get_timezone (editor);
    use_24_hour_format = comp_editor_get_use_24_hour_format (editor);

    print_comp (
        comp, priv->cal_client, zone, use_24_hour_format,
        GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);

    g_object_unref (comp);
}

static void
action_print_preview_cb (GtkAction *action,
                         CompEditor *editor)
{
    CompEditorPrivate *priv = editor->priv;
    ECalComponent *comp;
    GList *l;
    icalcomponent *component;
    icalcomponent *clone;
    icaltimezone *zone;
    gboolean use_24_hour_format;

    comp = e_cal_component_new ();
    component = e_cal_component_get_icalcomponent (priv->comp);
    clone = icalcomponent_new_clone (component);
    e_cal_component_set_icalcomponent (comp, clone);

    for (l = priv->pages; l != NULL; l = l->next)
         comp_editor_page_fill_component (l->data, comp);

    zone = comp_editor_get_timezone (editor);
    use_24_hour_format = comp_editor_get_use_24_hour_format (editor);

    print_comp (
        comp, priv->cal_client, zone, use_24_hour_format,
        GTK_PRINT_OPERATION_ACTION_PREVIEW);

    g_object_unref (comp);
}

static gboolean
remove_event_dialog (ECalClient *client,
                     ECalComponent *comp,
                     GtkWindow *parent)
{
    GtkWidget *dialog;
    gboolean ret;

    g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), TRUE);

    dialog = gtk_message_dialog_new (
        parent, 0, GTK_MESSAGE_QUESTION,
        GTK_BUTTONS_YES_NO, "%s", _("Keep original item?"));
    gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
    ret = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES;
    gtk_widget_destroy (dialog);

    return ret;
}

static void
save_and_close_editor (CompEditor *editor,
                       gboolean can_close)
{
    CompEditorPrivate *priv = editor->priv;
    EAttachmentStore *store;
    EAttachmentView *view;
    ECalComponentText text;
    gboolean delegated = FALSE;
    gboolean correct = FALSE;
    ECalComponent *comp;

    view = E_ATTACHMENT_VIEW (priv->attachment_view);
    store = e_attachment_view_get_store (view);

    if (e_attachment_store_get_num_loading (store) > 0) {
        gboolean response = 1;
    /*FIXME: Cannot use mail functions from calendar!!!! */
#if 0
        ECalComponentVType vtype = e_cal_component_get_vtype (editor->priv->comp);

        if (vtype == E_CAL_COMPONENT_EVENT)
            response = em_utils_prompt_user (
                (GtkWindow *) widget,
                NULL,
                "calendar:ask-send-event-pending-download",
                NULL);
        else
            response = em_utils_prompt_user (
                (GtkWindow *) widget,
                NULL,
                "calendar:ask-send-task-pending-download",
                NULL);
#endif
    if (!response)
        return;
    }

    if (e_client_is_readonly (E_CLIENT (priv->cal_client))) {
        e_alert_submit (
            E_ALERT_SINK (editor),
            "calendar:prompt-read-only-cal-editor",
            e_source_get_display_name (
                e_client_get_source (E_CLIENT (priv->cal_client))),
            NULL);
        return;
    }

    if ((comp_editor_get_flags (editor) & COMP_EDITOR_IS_ASSIGNED) != 0
        && e_cal_component_get_vtype (priv->comp) == E_CAL_COMPONENT_TODO
        && e_client_check_capability (E_CLIENT (priv->cal_client), CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT)) {
        e_alert_submit (
            E_ALERT_SINK (editor),
            "calendar:prompt-no-task-assignment-editor",
            e_source_get_display_name (
                e_client_get_source (E_CLIENT (priv->cal_client))),
            NULL);
        return;
    }

    commit_all_fields (editor);
    if (e_cal_component_has_recurrences (priv->comp)) {
        if (!recur_component_dialog (
            priv->cal_client, priv->comp, &priv->mod,
            GTK_WINDOW (editor), delegated))
            return;
    } else if (e_cal_component_is_instance (priv->comp))
        priv->mod = CALOBJ_MOD_THIS;

    comp = comp_editor_get_current_comp (editor, &correct);
    e_cal_component_get_summary (comp, &text);
    g_object_unref (comp);

    if (!correct)
        return;

    if (!text.value)
        if (!send_component_prompt_subject (
            (GtkWindow *) editor, priv->cal_client, priv->comp))
            return;

    gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE);

    if (save_comp_with_send (editor)) {
        CompEditorFlags flags;
        gboolean delegate;

        flags = comp_editor_get_flags (editor);
        delegate = flags & COMP_EDITOR_DELEGATE;

        if (delegate && !remove_event_dialog (
            priv->cal_client, priv->comp, GTK_WINDOW (editor))) {
            const gchar *uid = NULL;
            GError *error = NULL;

            e_cal_component_get_uid (priv->comp, &uid);

            if (e_cal_component_is_instance (priv->comp) ||
                e_cal_component_has_recurrences (priv->comp)) {
                gchar *rid;
                rid = e_cal_component_get_recurid_as_string (priv->comp);
                e_cal_client_remove_object_sync (
                    priv->cal_client, uid, rid,
                    priv->mod, NULL, &error);
                g_free (rid);
            } else
                e_cal_client_remove_object_sync (
                    priv->cal_client, uid, NULL,
                    CALOBJ_MOD_THIS, NULL, &error);

            g_clear_error (&error);
        }
    } else
        correct = FALSE;

    gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE);

    if (correct) {
        if (can_close)
            close_dialog (editor);
        else
            comp_editor_set_changed (editor, FALSE);
    }
}

static void
action_save_cb (GtkAction *action,
                CompEditor *editor)
{
    save_and_close_editor (editor, FALSE);
}

static void
action_save_and_close_cb (GtkAction *action,
                          CompEditor *editor)
{
    save_and_close_editor (editor, TRUE);
}

static void
action_view_categories_cb (GtkToggleAction *action,
                           CompEditor *editor)
{
    CompEditorClass *class;
    gboolean active;

    class = COMP_EDITOR_GET_CLASS (editor);
    active = gtk_toggle_action_get_active (action);

    if (class->show_categories != NULL)
        class->show_categories (editor, active);
}

static void
action_view_role_cb (GtkToggleAction *action,
                     CompEditor *editor)
{
    CompEditorClass *class;
    gboolean active;

    class = COMP_EDITOR_GET_CLASS (editor);
    active = gtk_toggle_action_get_active (action);

    if (class->show_role != NULL)
        class->show_role (editor, active);
}

static void
action_view_rsvp_cb (GtkToggleAction *action,
                     CompEditor *editor)
{
    CompEditorClass *class;
    gboolean active;

    class = COMP_EDITOR_GET_CLASS (editor);
    active = gtk_toggle_action_get_active (action);

    if (class->show_rsvp != NULL)
        class->show_rsvp (editor, active);
}

static void
action_view_status_cb (GtkToggleAction *action,
                       CompEditor *editor)
{
    CompEditorClass *class;
    gboolean active;

    class = COMP_EDITOR_GET_CLASS (editor);
    active = gtk_toggle_action_get_active (action);

    if (class->show_status != NULL)
        class->show_status (editor, active);
}

static void
action_view_time_zone_cb (GtkToggleAction *action,
                          CompEditor *editor)
{
    CompEditorClass *class;
    gboolean active;

    class = COMP_EDITOR_GET_CLASS (editor);
    active = gtk_toggle_action_get_active (action);

    if (class->show_time_zone != NULL)
        class->show_time_zone (editor, active);
}

static void
action_view_type_cb (GtkToggleAction *action,
                     CompEditor *editor)
{
    CompEditorClass *class;
    gboolean active;

    class = COMP_EDITOR_GET_CLASS (editor);
    active = gtk_toggle_action_get_active (action);

    if (class->show_type != NULL)
        class->show_type (editor, active);
}

static GtkActionEntry core_entries[] = {

    { "close",
      GTK_STOCK_CLOSE,
      NULL,
      NULL,
      N_("Close the current window"),
      G_CALLBACK (action_close_cb) },

    { "copy-clipboard",
      GTK_STOCK_COPY,
      NULL,
      NULL,
      N_("Copy the selection"),
      NULL },  /* Handled by EFocusTracker */

    { "cut-clipboard",
      GTK_STOCK_CUT,
      NULL,
      NULL,
      N_("Cut the selection"),
      NULL },  /* Handled by EFocusTracker */

    { "delete-selection",
      GTK_STOCK_DELETE,
      NULL,
      NULL,
      N_("Delete the selection"),
      NULL },  /* Handled by EFocusTracker */

    { "help",
      GTK_STOCK_HELP,
      NULL,
      NULL,
      N_("View help"),
      G_CALLBACK (action_help_cb) },

    { "paste-clipboard",
      GTK_STOCK_PASTE,
      NULL,
      NULL,
      N_("Paste the clipboard"),
      NULL },  /* Handled by EFocusTracker */

    { "print",
      GTK_STOCK_PRINT,
      NULL,
      "<Control>p",
      NULL,
      G_CALLBACK (action_print_cb) },

    { "print-preview",
      GTK_STOCK_PRINT_PREVIEW,
      NULL,
      NULL,
      NULL,
      G_CALLBACK (action_print_preview_cb) },

    { "save",
      GTK_STOCK_SAVE,
      NULL,
      NULL,
      N_("Save current changes"),
      G_CALLBACK (action_save_cb) },

    { "save-and-close",
      NULL,
      N_("Save and Close"),
      NULL,
      N_("Save current changes and close editor"),
      G_CALLBACK (action_save_and_close_cb) },

    { "select-all",
      GTK_STOCK_SELECT_ALL,
      NULL,
      "<Control>a",
      N_("Select all text"),
      NULL },  /* Handled by EFocusTracker */

    /* Menus */

    { "classification-menu",
      NULL,
      N_("_Classification"),
      NULL,
      NULL,
      NULL },

    { "edit-menu",
      NULL,
      N_("_Edit"),
      NULL,
      NULL,
      NULL },

    { "file-menu",
      NULL,
      N_("_File"),
      NULL,
      NULL,
      NULL },

    { "help-menu",
      NULL,
      N_("_Help"),
      NULL,
      NULL,
      NULL },

    { "insert-menu",
      NULL,
      N_("_Insert"),
      NULL,
      NULL,
      NULL },

    { "options-menu",
      NULL,
      N_("_Options"),
      NULL,
      NULL,
      NULL },

    { "view-menu",
      NULL,
      N_("_View"),
      NULL,
      NULL,
      NULL }
};

static GtkActionEntry individual_entries[] = {

    { "attach",
      "mail-attachment",
      N_("_Attachment..."),
      "<Control>m",
      N_("Attach a file"),
      G_CALLBACK (action_attach_cb) }
};

static GtkToggleActionEntry core_toggle_entries[] = {

    { "view-categories",
      NULL,
      N_("_Categories"),
      NULL,
      N_("Toggles whether to display categories"),
      G_CALLBACK (action_view_categories_cb),
      FALSE },

    { "view-time-zone",
      "stock_timezone",
      N_("Time _Zone"),
      NULL,
      N_("Toggles whether the time zone is displayed"),
      G_CALLBACK (action_view_time_zone_cb),
      FALSE }
};

static GtkRadioActionEntry classification_radio_entries[] = {

    { "classify-public",
      NULL,
      N_("Pu_blic"),
      NULL,
      N_("Classify as public"),
      E_CAL_COMPONENT_CLASS_PUBLIC },

    { "classify-private",
      NULL,
      N_("_Private"),
      NULL,
      N_("Classify as private"),
      E_CAL_COMPONENT_CLASS_PRIVATE },

    { "classify-confidential",
      NULL,
      N_("_Confidential"),
      NULL,
      N_("Classify as confidential"),
      E_CAL_COMPONENT_CLASS_CONFIDENTIAL }
};

static GtkToggleActionEntry coordinated_toggle_entries[] = {

    { "view-role",
      NULL,
      N_("R_ole Field"),
      NULL,
      N_("Toggles whether the Role field is displayed"),
      G_CALLBACK (action_view_role_cb),
      FALSE },

    { "view-rsvp",
      NULL,
      N_("_RSVP"),
      NULL,
      N_("Toggles whether the RSVP field is displayed"),
      G_CALLBACK (action_view_rsvp_cb),
      FALSE },

    { "view-status",
      NULL,
      N_("_Status Field"),
      NULL,
      N_("Toggles whether the Status field is displayed"),
      G_CALLBACK (action_view_status_cb),
      FALSE },

    { "view-type",
      NULL,
      N_("_Type Field"),
      NULL,
      N_("Toggles whether the Attendee Type is displayed"),
      G_CALLBACK (action_view_type_cb),
      FALSE }
};

static void
comp_editor_set_shell (CompEditor *editor,
                       EShell *shell)
{
    g_return_if_fail (E_IS_SHELL (shell));
    g_return_if_fail (editor->priv->shell == NULL);

    editor->priv->shell = shell;

    g_object_add_weak_pointer (G_OBJECT (shell), &editor->priv->shell);
}

static void
comp_editor_set_property (GObject *object,
                          guint property_id,
                          const GValue *value,
                          GParamSpec *pspec)
{
    switch (property_id) {
        case PROP_CHANGED:
            comp_editor_set_changed (
                COMP_EDITOR (object),
                g_value_get_boolean (value));
            return;

        case PROP_CLIENT:
            comp_editor_set_client (
                COMP_EDITOR (object),
                g_value_get_object (value));
            return;

        case PROP_FLAGS:
            comp_editor_set_flags (
                COMP_EDITOR (object),
                g_value_get_int (value));
            return;

        case PROP_SHELL:
            comp_editor_set_shell (
                COMP_EDITOR (object),
                g_value_get_object (value));
            return;

        case PROP_SUMMARY:
            comp_editor_set_summary (
                COMP_EDITOR (object),
                g_value_get_string (value));
            return;

        case PROP_TIMEZONE:
            comp_editor_set_timezone (
                COMP_EDITOR (object),
                g_value_get_pointer (value));
            return;

        case PROP_USE_24_HOUR_FORMAT:
            comp_editor_set_use_24_hour_format (
                COMP_EDITOR (object),
                g_value_get_boolean (value));
            return;

        case PROP_WORK_DAY_END_HOUR:
            comp_editor_set_work_day_end_hour (
                COMP_EDITOR (object),
                g_value_get_int (value));
            return;

        case PROP_WORK_DAY_END_MINUTE:
            comp_editor_set_work_day_end_minute (
                COMP_EDITOR (object),
                g_value_get_int (value));
            return;

        case PROP_WORK_DAY_START_HOUR:
            comp_editor_set_work_day_start_hour (
                COMP_EDITOR (object),
                g_value_get_int (value));
            return;

        case PROP_WORK_DAY_START_MINUTE:
            comp_editor_set_work_day_start_minute (
                COMP_EDITOR (object),
                g_value_get_int (value));
            return;
    }

    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
comp_editor_get_property (GObject *object,
                          guint property_id,
                          GValue *value,
                          GParamSpec *pspec)
{
    switch (property_id) {
        case PROP_CHANGED:
            g_value_set_boolean (
                value, comp_editor_get_changed (
                COMP_EDITOR (object)));
            return;

        case PROP_CLIENT:
            g_value_set_object (
                value, comp_editor_get_client (
                COMP_EDITOR (object)));
            return;

        case PROP_FLAGS:
            g_value_set_int (
                value, comp_editor_get_flags (
                COMP_EDITOR (object)));
            return;

        case PROP_FOCUS_TRACKER:
            g_value_set_object (
                value, comp_editor_get_focus_tracker (
                COMP_EDITOR (object)));
            return;

        case PROP_SHELL:
            g_value_set_object (
                value, comp_editor_get_shell (
                COMP_EDITOR (object)));
            return;

        case PROP_SUMMARY:
            g_value_set_string (
                value, comp_editor_get_summary (
                COMP_EDITOR (object)));
            return;

        case PROP_TIMEZONE:
            g_value_set_pointer (
                value, comp_editor_get_timezone (
                COMP_EDITOR (object)));
            return;

        case PROP_USE_24_HOUR_FORMAT:
            g_value_set_boolean (
                value, comp_editor_get_use_24_hour_format (
                COMP_EDITOR (object)));
            return;

        case PROP_WORK_DAY_END_HOUR:
            g_value_set_int (
                value, comp_editor_get_work_day_end_hour (
                COMP_EDITOR (object)));
            return;

        case PROP_WORK_DAY_END_MINUTE:
            g_value_set_int (
                value, comp_editor_get_work_day_end_minute (
                COMP_EDITOR (object)));
            return;

        case PROP_WORK_DAY_START_HOUR:
            g_value_set_int (
                value, comp_editor_get_work_day_start_hour (
                COMP_EDITOR (object)));
            return;

        case PROP_WORK_DAY_START_MINUTE:
            g_value_set_int (
                value, comp_editor_get_work_day_start_minute (
                COMP_EDITOR (object)));
            return;
    }

    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
unref_page_cb (gpointer editor_page,
               gpointer comp_editor)
{
    if (IS_COMP_EDITOR_PAGE (editor_page)) {
        GtkWidget *page_widget;
        CompEditorPage *page = COMP_EDITOR_PAGE (editor_page);
        CompEditor *editor = COMP_EDITOR (comp_editor);

        g_return_if_fail (page != NULL);
        g_return_if_fail (editor != NULL);

        page_widget = comp_editor_page_get_widget (page);
        g_signal_handlers_disconnect_matched (
            page_widget, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page);
    }

    g_signal_handlers_disconnect_matched (
        editor_page, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, comp_editor);
    g_object_unref (editor_page);
}

static void
comp_editor_dispose (GObject *object)
{
    CompEditorPrivate *priv;

    priv = COMP_EDITOR_GET_PRIVATE (object);

    if (priv->shell != NULL) {
        g_object_remove_weak_pointer (
            G_OBJECT (priv->shell), &priv->shell);
        priv->shell = NULL;
    }

    if (priv->focus_tracker != NULL) {
        g_object_unref (priv->focus_tracker);
        priv->focus_tracker = NULL;
    }

    if (priv->window_group != NULL) {
        g_object_unref (priv->window_group);
        priv->window_group = NULL;
    }

    if (priv->cal_client) {
        g_object_unref (priv->cal_client);
        priv->cal_client = NULL;
    }

    if (priv->source_client) {
        g_object_unref (priv->source_client);
        priv->source_client = NULL;
    }

    if (priv->view_cancellable) {
        g_cancellable_cancel (priv->view_cancellable);
        g_object_unref (priv->view_cancellable);
        priv->view_cancellable = NULL;
    }

    if (priv->view) {
        g_signal_handlers_disconnect_matched (
            priv->view, G_SIGNAL_MATCH_DATA,
            0, 0, NULL, NULL, object);
        g_object_unref (priv->view);
        priv->view = NULL;
    }

    if (priv->attachment_view) {
        EAttachmentStore *store;

        store = e_attachment_view_get_store (
            E_ATTACHMENT_VIEW (priv->attachment_view));
        g_signal_handlers_disconnect_matched (
            store, G_SIGNAL_MATCH_DATA,
            0, 0, NULL, NULL, object);
        g_object_unref (priv->attachment_view);
        priv->attachment_view = NULL;
    }

    /* We want to destroy the pages after the widgets get destroyed,
     * since they have lots of signal handlers connected to the widgets
     * with the pages as the data. */
    g_list_foreach (priv->pages, (GFunc) unref_page_cb, object);
    g_list_free (priv->pages);
    priv->pages = NULL;

    if (priv->comp) {
        g_object_unref (priv->comp);
        priv->comp = NULL;
    }

    if (priv->ui_manager != NULL) {
        g_object_unref (priv->ui_manager);
        priv->ui_manager = NULL;
    }

    /* Chain up to parent's dispose() method. */
    G_OBJECT_CLASS (comp_editor_parent_class)->dispose (object);
}

static void
comp_editor_finalize (GObject *object)
{
    CompEditorPrivate *priv;

    priv = COMP_EDITOR_GET_PRIVATE (object);

    g_object_unref (priv->calendar_settings);
    g_free (priv->summary);

    /* Chain up to parent's finalize() method. */
    G_OBJECT_CLASS (comp_editor_parent_class)->finalize (object);
}

static void
comp_editor_constructed (GObject *object)
{
    e_extensible_load_extensions (E_EXTENSIBLE (object));

    /* Chain up to parent's constructed() method. */
    G_OBJECT_CLASS (comp_editor_parent_class)->constructed (object);
}

static void
comp_editor_bind_settings (CompEditor *editor)
{
    GtkAction *action;

    g_return_if_fail (editor != NULL);

    action = comp_editor_get_action (editor, "view-categories");
    g_settings_bind (
        editor->priv->calendar_settings, "editor-show-categories",
        action, "active",
        G_SETTINGS_BIND_DEFAULT);

    action = comp_editor_get_action (editor, "view-role");
    g_settings_bind (
        editor->priv->calendar_settings, "editor-show-role",
        action, "active",
        G_SETTINGS_BIND_DEFAULT);

    action = comp_editor_get_action (editor, "view-rsvp");
    g_settings_bind (
        editor->priv->calendar_settings, "editor-show-rsvp",
        action, "active",
        G_SETTINGS_BIND_DEFAULT);

    action = comp_editor_get_action (editor, "view-status");
    g_settings_bind (
        editor->priv->calendar_settings, "editor-show-status",
        action, "active",
        G_SETTINGS_BIND_DEFAULT);

    action = comp_editor_get_action (editor, "view-time-zone");
    g_settings_bind (
        editor->priv->calendar_settings, "editor-show-timezone",
        action, "active",
        G_SETTINGS_BIND_DEFAULT);

    action = comp_editor_get_action (editor, "view-type");
    g_settings_bind (
        editor->priv->calendar_settings, "editor-show-type",
        action, "active",
        G_SETTINGS_BIND_DEFAULT);
}

static gboolean
comp_editor_delete_event (GtkWidget *widget,
                          GdkEventAny *event)
{
    CompEditor *editor;

    editor = COMP_EDITOR (widget);

    commit_all_fields (editor);

    if (prompt_and_save_changes (editor, TRUE))
        close_dialog (editor);

    return TRUE;
}

static gboolean
comp_editor_key_press_event (GtkWidget *widget,
                             GdkEventKey *event)
{
    CompEditor *editor;

    editor = COMP_EDITOR (widget);

    if (event->keyval == GDK_KEY_Escape) {
        commit_all_fields (editor);

        if (prompt_and_save_changes (editor, TRUE))
            close_dialog (editor);

        return TRUE;
    }

    /* Chain up to parent's key_press_event() method. */
    return GTK_WIDGET_CLASS (comp_editor_parent_class)->
        key_press_event (widget, event);
}

static gboolean
comp_editor_drag_motion (GtkWidget *widget,
                         GdkDragContext *context,
                         gint x,
                         gint y,
                         guint time)
{
    CompEditorPrivate *priv;
    EAttachmentView *view;

    priv = COMP_EDITOR_GET_PRIVATE (widget);
    view = E_ATTACHMENT_VIEW (priv->attachment_view);

    return e_attachment_view_drag_motion (view, context, x, y, time);
}

static void
comp_editor_drag_data_received (GtkWidget *widget,
                                GdkDragContext *context,
                                gint x,
                                gint y,
                                GtkSelectionData *selection,
                                guint info,
                                guint time)
{
    CompEditorPrivate *priv;
    EAttachmentView *view;

    priv = COMP_EDITOR_GET_PRIVATE (widget);
    view = E_ATTACHMENT_VIEW (priv->attachment_view);

    /* Forward the data to the attachment view.  Note that calling
     * e_attachment_view_drag_data_received() will not work because
     * that function only handles the case where all the other drag
     * handlers have failed. */
    e_attachment_paned_drag_data_received (
        E_ATTACHMENT_PANED (view),
        context, x, y, selection, info, time);
}

static void
comp_editor_class_init (CompEditorClass *class)
{
    GObjectClass *object_class;
    GtkWidgetClass *widget_class;

    g_type_class_add_private (class, sizeof (CompEditorPrivate));

    object_class = G_OBJECT_CLASS (class);
    object_class->set_property = comp_editor_set_property;
    object_class->get_property = comp_editor_get_property;
    object_class->dispose = comp_editor_dispose;
    object_class->finalize = comp_editor_finalize;
    object_class->constructed = comp_editor_constructed;

    widget_class = GTK_WIDGET_CLASS (class);
    widget_class->delete_event = comp_editor_delete_event;
    widget_class->key_press_event = comp_editor_key_press_event;
    widget_class->drag_motion = comp_editor_drag_motion;
    widget_class->drag_data_received = comp_editor_drag_data_received;

    class->help_section = "memos-usage";
    class->edit_comp = real_edit_comp;
    class->send_comp = real_send_comp;
    class->object_created = NULL;

    g_object_class_install_property (
        object_class,
        PROP_CHANGED,
        g_param_spec_boolean (
            "changed",
            NULL,
            NULL,
            FALSE,
            G_PARAM_READWRITE));

    g_object_class_install_property (
        object_class,
        PROP_CLIENT,
        g_param_spec_object (
            "client",
            NULL,
            NULL,
            E_TYPE_CAL_CLIENT,
            G_PARAM_READWRITE |
            G_PARAM_CONSTRUCT));

    /* FIXME: Use a proper flags type instead of int. */
    g_object_class_install_property (
        object_class,
        PROP_FLAGS,
        g_param_spec_int (
            "flags",
            NULL,
            NULL,
            G_MININT,
            G_MAXINT,
            0,
            G_PARAM_READWRITE |
            G_PARAM_CONSTRUCT));

    g_object_class_install_property (
        object_class,
        PROP_FOCUS_TRACKER,
        g_param_spec_object (
            "focus-tracker",
            NULL,
            NULL,
            E_TYPE_FOCUS_TRACKER,
            G_PARAM_READABLE));

    g_object_class_install_property (
        object_class,
        PROP_SHELL,
        g_param_spec_object (
            "shell",
            NULL,
            NULL,
            E_TYPE_SHELL,
            G_PARAM_READWRITE |
            G_PARAM_CONSTRUCT_ONLY));

    g_object_class_install_property (
        object_class,
        PROP_SUMMARY,
        g_param_spec_string (
            "summary",
            NULL,
            NULL,
            NULL,
            G_PARAM_READWRITE));

    g_object_class_install_property (
        object_class,
        PROP_TIMEZONE,
        g_param_spec_pointer (
            "timezone",
            "Time Zone",
            NULL,
            G_PARAM_READWRITE));

    g_object_class_install_property (
        object_class,
        PROP_USE_24_HOUR_FORMAT,
        g_param_spec_boolean (
            "use-24-hour-format",
            "Use 24-hour Format",
            NULL,
            FALSE,
            G_PARAM_READWRITE));

    g_object_class_install_property (
        object_class,
        PROP_WORK_DAY_END_HOUR,
        g_param_spec_int (
            "work-day-end-hour",
            "Work Day End Hour",
            NULL,
            0,
            23,
            0,
            G_PARAM_READWRITE));

    g_object_class_install_property (
        object_class,
        PROP_WORK_DAY_END_MINUTE,
        g_param_spec_int (
            "work-day-end-minute",
            "Work Day End Minute",
            NULL,
            0,
            59,
            0,
            G_PARAM_READWRITE));

    g_object_class_install_property (
        object_class,
        PROP_WORK_DAY_START_HOUR,
        g_param_spec_int (
            "work-day-start-hour",
            "Work Day Start Hour",
            NULL,
            0,
            23,
            0,
            G_PARAM_READWRITE));

    g_object_class_install_property (
        object_class,
        PROP_WORK_DAY_START_MINUTE,
        g_param_spec_int (
            "work-day-start-minute",
            "Work Day Start Minute",
            NULL,
            0,
            59,
            0,
            G_PARAM_READWRITE));

    signals[OBJECT_CREATED] = g_signal_new (
        "object_created",
        G_TYPE_FROM_CLASS (class),
        G_SIGNAL_RUN_LAST,
        G_STRUCT_OFFSET (CompEditorClass, object_created),
        NULL, NULL,
        g_cclosure_marshal_VOID__VOID,
        G_TYPE_NONE, 0);

    signals[COMP_CLOSED] = g_signal_new (
        "comp_closed",
        G_TYPE_FROM_CLASS (class),
        G_SIGNAL_RUN_LAST,
        G_STRUCT_OFFSET (CompEditorClass, comp_closed),
        NULL, NULL,
        g_cclosure_marshal_VOID__BOOLEAN,
        G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
}

static void
comp_editor_init (CompEditor *editor)
{
    CompEditorPrivate *priv;
    EAttachmentView *view;
    EAttachmentStore *store;
    EFocusTracker *focus_tracker;
    GdkDragAction drag_actions;
    GtkTargetList *target_list;
    GtkTargetEntry *targets;
    GtkActionGroup *action_group;
    GtkActionGroup *action_group_2;
    GtkAction *action;
    GtkWidget *container;
    GtkWidget *widget;
    GtkWindow *window;
    EShell *shell;
    gboolean express_mode;
    gboolean meego_mode;
    gint n_targets;
    GError *error = NULL;

    /* FIXME We already have a 'shell' property.  Move stuff
     *       that depends on it to a constructed() method. */
    shell = e_shell_get_default ();
    express_mode = e_shell_get_express_mode (shell);
    meego_mode = e_shell_get_meego_mode (shell);

    editor->priv = priv = COMP_EDITOR_GET_PRIVATE (editor);

    g_object_weak_ref (
        G_OBJECT (editor), (GWeakNotify)
        comp_editor_weak_notify_cb, NULL);

    active_editors = g_list_prepend (active_editors, editor);

    priv->calendar_settings = g_settings_new ("org.gnome.evolution.calendar");

    /* Each editor window gets its own window group. */
    window = GTK_WINDOW (editor);
    priv->window_group = gtk_window_group_new ();
    gtk_window_group_add_window (priv->window_group, window);

    priv->pages = NULL;
    priv->changed = FALSE;
    priv->needs_send = FALSE;
    priv->mod = CALOBJ_MOD_ALL;
    priv->existing_org = FALSE;
    priv->user_org = FALSE;
    priv->warned = FALSE;
    priv->is_group_item = FALSE;
    priv->saved = FALSE;

    priv->ui_manager = e_ui_manager_new ();
    e_ui_manager_set_express_mode (
        E_UI_MANAGER (priv->ui_manager), express_mode);

    gtk_window_add_accel_group (
        GTK_WINDOW (editor),
        gtk_ui_manager_get_accel_group (priv->ui_manager));

    /* Setup Action Groups */

    action_group = gtk_action_group_new ("core");
    gtk_action_group_set_translation_domain (
        action_group, GETTEXT_PACKAGE);
    gtk_action_group_add_actions (
        action_group, core_entries,
        G_N_ELEMENTS (core_entries), editor);
    gtk_action_group_add_toggle_actions (
        action_group, core_toggle_entries,
        G_N_ELEMENTS (core_toggle_entries), editor);
    gtk_ui_manager_insert_action_group (
        priv->ui_manager, action_group, 0);
    g_object_unref (action_group);

    action = gtk_action_group_get_action (action_group, "save-and-close");
    if (action) {
        GtkAction *save_action;
        GIcon *icon;
        GIcon *emblemed_icon;
        GEmblem *emblem;

        icon = g_themed_icon_new (GTK_STOCK_CLOSE);
        emblemed_icon = g_themed_icon_new (GTK_STOCK_SAVE);
        emblem = g_emblem_new (emblemed_icon);
        g_object_unref (emblemed_icon);

        emblemed_icon = g_emblemed_icon_new (icon, emblem);
        g_object_unref (emblem);
        g_object_unref (icon);

        gtk_action_set_gicon (action, emblemed_icon);

        g_object_unref (emblemed_icon);

        save_action = gtk_action_group_get_action (action_group, "save");
        g_object_bind_property (
            save_action, "sensitive",
            action, "sensitive",
            G_BINDING_SYNC_CREATE);
    }

    action_group = gtk_action_group_new ("individual");
    gtk_action_group_set_translation_domain (
        action_group, GETTEXT_PACKAGE);
    gtk_action_group_add_actions (
        action_group, individual_entries,
        G_N_ELEMENTS (individual_entries), editor);
    gtk_action_group_add_radio_actions (
        action_group, classification_radio_entries,
        G_N_ELEMENTS (classification_radio_entries),
        E_CAL_COMPONENT_CLASS_PUBLIC,
        G_CALLBACK (action_classification_cb), editor);
    gtk_ui_manager_insert_action_group (
        priv->ui_manager, action_group, 0);
    g_object_unref (action_group);

    action_group = gtk_action_group_new ("editable");
    gtk_action_group_set_translation_domain (
        action_group, GETTEXT_PACKAGE);
    gtk_ui_manager_insert_action_group (
        priv->ui_manager, action_group, 0);
    g_object_unref (action_group);

    action_group = gtk_action_group_new ("coordinated");
    gtk_action_group_set_translation_domain (
        action_group, GETTEXT_PACKAGE);
    gtk_action_group_add_toggle_actions (
        action_group, coordinated_toggle_entries,
        G_N_ELEMENTS (coordinated_toggle_entries), editor);
    gtk_ui_manager_insert_action_group (
        priv->ui_manager, action_group, 0);
    g_object_unref (action_group);

    /* Configure an EFocusTracker to manage selection actions. */

    focus_tracker = e_focus_tracker_new (GTK_WINDOW (editor));

    action = comp_editor_get_action (editor, "cut-clipboard");
    e_focus_tracker_set_cut_clipboard_action (focus_tracker, action);

    action = comp_editor_get_action (editor, "copy-clipboard");
    e_focus_tracker_set_copy_clipboard_action (focus_tracker, action);

    action = comp_editor_get_action (editor, "paste-clipboard");
    e_focus_tracker_set_paste_clipboard_action (focus_tracker, action);

    action = comp_editor_get_action (editor, "delete-selection");
    e_focus_tracker_set_delete_selection_action (focus_tracker, action);

    action = comp_editor_get_action (editor, "select-all");
    e_focus_tracker_set_select_all_action (focus_tracker, action);

    priv->focus_tracker = focus_tracker;

    /* Fine Tuning */

    action = comp_editor_get_action (editor, "attach");
    g_object_set (action, "short-label", _("Attach"), NULL);

    /* Desensitize the "save" action. */
    action = comp_editor_get_action (editor, "save");
    gtk_action_set_sensitive (action, FALSE);

    e_ui_manager_add_ui_from_string (E_UI_MANAGER (priv->ui_manager), ui, &error);
    if (error != NULL) {
        g_warning ("%s: %s", G_STRFUNC, error->message);
        g_error_free (error);
    }

    /* Setup Widgets */

    container = GTK_WIDGET (editor);

    widget = gtk_vbox_new (FALSE, 0);
    gtk_container_add (GTK_CONTAINER (container), widget);
    gtk_widget_show (widget);

    container = widget;

    if (!express_mode) {
        widget = comp_editor_get_managed_widget (editor, "/main-menu");
        gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
        gtk_widget_set_visible (widget, !meego_mode);
    }

    widget = comp_editor_get_managed_widget (editor, "/main-toolbar");
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
    gtk_widget_show (widget);

    gtk_style_context_add_class (
        gtk_widget_get_style_context (widget),
        GTK_STYLE_CLASS_PRIMARY_TOOLBAR);

    widget = e_attachment_paned_new ();
    e_attachment_paned_set_resize_toplevel (
        E_ATTACHMENT_PANED (widget), TRUE);
    gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
    gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
    priv->attachment_view = g_object_ref (widget);
    gtk_widget_show (widget);

    if (express_mode) {
        widget = e_attachment_paned_get_view_combo (
            E_ATTACHMENT_PANED (widget));
        gtk_widget_hide (widget);
    }

    container = e_attachment_paned_get_content_area (
        E_ATTACHMENT_PANED (priv->attachment_view));

    if (meego_mode) {
        widget = gtk_scrolled_window_new (NULL, NULL);
        gtk_scrolled_window_set_policy (
            GTK_SCROLLED_WINDOW (widget),
            GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
        gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
        gtk_widget_set_size_request (widget, 300, -1);
        gtk_widget_show (widget);

        container = widget;
    }

    widget = gtk_notebook_new ();
    gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), express_mode);
    if (!meego_mode)
        gtk_box_pack_start (
            GTK_BOX (container), widget, TRUE, TRUE, 0);
    else
        gtk_scrolled_window_add_with_viewport (
            GTK_SCROLLED_WINDOW (container), widget);
    priv->notebook = GTK_NOTEBOOK (widget);
    gtk_widget_show (widget);

    /* Drag-and-Drop Support */

    view = E_ATTACHMENT_VIEW (priv->attachment_view);
    target_list = e_attachment_view_get_target_list (view);
    drag_actions = e_attachment_view_get_drag_actions (view);

    targets = gtk_target_table_new_from_list (target_list, &n_targets);

    gtk_drag_dest_set (
        GTK_WIDGET (editor), GTK_DEST_DEFAULT_ALL,
        targets, n_targets, drag_actions);

    gtk_target_table_free (targets, n_targets);

    gtk_window_set_type_hint (
        GTK_WINDOW (editor), GDK_WINDOW_TYPE_HINT_NORMAL);

    action_group = comp_editor_get_action_group (editor, "individual");
    action_group_2 = e_attachment_view_get_action_group (view, "editable");

    g_object_bind_property (
        action_group, "sensitive",
        action_group_2, "sensitive",
        G_BINDING_SYNC_CREATE);

    /* Listen for attachment store changes. */

    store = e_attachment_view_get_store (view);

    g_signal_connect_swapped (
        store, "row-deleted",
        G_CALLBACK (attachment_store_changed_cb), editor);

    g_signal_connect_swapped (
        store, "row-inserted",
        G_CALLBACK (attachment_store_changed_cb), editor);

    comp_editor_bind_settings (editor);

    gtk_application_add_window (
        GTK_APPLICATION (shell), GTK_WINDOW (editor));
    e_shell_adapt_window_size (shell, GTK_WINDOW (editor));
}

static gboolean
prompt_and_save_changes (CompEditor *editor,
                         gboolean send)
{
    CompEditorPrivate *priv;
    gboolean correct = FALSE;
    ECalComponent *comp;
    ECalComponentText text;

    priv = editor->priv;

    if (!priv->changed)
        return TRUE;

    switch (save_component_dialog (GTK_WINDOW (editor), priv->comp)) {
    case GTK_RESPONSE_YES: /* Save */
        if (e_client_is_readonly (E_CLIENT (priv->cal_client))) {
            e_alert_submit (
                E_ALERT_SINK (editor),
                "calendar:prompt-read-only-cal-editor",
                e_source_get_display_name (
                    e_client_get_source (E_CLIENT (priv->cal_client))),
                NULL);
            /* don't discard changes when selected readonly calendar */
            return FALSE;
        }

        if ((comp_editor_get_flags (editor) & COMP_EDITOR_IS_ASSIGNED) != 0
            && e_cal_component_get_vtype (priv->comp) == E_CAL_COMPONENT_TODO
            && e_client_check_capability (E_CLIENT (priv->cal_client), CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT)) {
            e_alert_submit (
                E_ALERT_SINK (editor),
                "calendar:prompt-no-task-assignment-editor",
                e_source_get_display_name (
                    e_client_get_source (E_CLIENT (priv->cal_client))),
                NULL);
            return FALSE;
        }

        comp = comp_editor_get_current_comp (editor, &correct);
        e_cal_component_get_summary (comp, &text);
        g_object_unref (comp);

        if (!correct)
            return FALSE;

        if (!text.value)
            if (!send_component_prompt_subject (
                (GtkWindow *) editor, priv->cal_client, priv->comp))
                return FALSE;

        if (e_cal_component_is_instance (priv->comp))
            if (!recur_component_dialog (
                priv->cal_client, priv->comp, &priv->mod,
                GTK_WINDOW (editor), FALSE))
                return FALSE;

        if (send && save_comp_with_send (editor))
            return TRUE;
        else if (!send && save_comp (editor))
            return TRUE;
        else
            return FALSE;
    case GTK_RESPONSE_NO: /* Discard */
        return TRUE;
    case GTK_RESPONSE_CANCEL: /* Cancel */
    default:
        return FALSE;
    }
}

/* Menu callbacks */

static void
comp_editor_show_help (CompEditor *editor)
{
    CompEditorClass *class;

    class = COMP_EDITOR_GET_CLASS (editor);
    g_return_if_fail (class->help_section != NULL);

    e_display_help (GTK_WINDOW (editor), class->help_section);
}

/* Closes the dialog box and emits the appropriate signals */
static void
close_dialog (CompEditor *editor)
{
    CompEditorPrivate *priv = editor->priv;

    g_signal_emit_by_name (editor, "comp_closed", priv->saved);

    /* FIXME Unfortunately we do this here because otherwise corba
     * calls happen during destruction and we might get a change
     * notification back when we are in an inconsistent state */
    if (priv->view)
        g_signal_handlers_disconnect_matched (
            priv->view, G_SIGNAL_MATCH_DATA,
            0, 0, NULL, NULL, editor);

    gtk_widget_destroy (GTK_WIDGET (editor));
}

static gint
comp_editor_compare (CompEditor *editor_a,
                     const gchar *uid_b)
{
    const gchar *uid_a = NULL;

    e_cal_component_get_uid (editor_a->priv->comp, &uid_a);

    return g_strcmp0 (uid_a, uid_b);
}

void
comp_editor_set_existing_org (CompEditor *editor,
                              gboolean existing_org)
{
    g_return_if_fail (IS_COMP_EDITOR (editor));

    editor->priv->existing_org = existing_org;
}

gboolean
comp_editor_get_existing_org (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);

    return editor->priv->existing_org;
}

void
comp_editor_set_user_org (CompEditor *editor,
                          gboolean user_org)
{
    g_return_if_fail (IS_COMP_EDITOR (editor));

    editor->priv->user_org = user_org;
}

gboolean
comp_editor_get_user_org (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);

    return editor->priv->user_org;
}

void
comp_editor_set_group_item (CompEditor *editor,
                            gboolean group_item)
{
    g_return_if_fail (IS_COMP_EDITOR (editor));

    editor->priv->is_group_item = group_item;
}

gboolean
comp_editor_get_group_item (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);

    return editor->priv->is_group_item;
}

void
comp_editor_set_classification (CompEditor *editor,
                                ECalComponentClassification classification)
{
    GtkAction *action;

    g_return_if_fail (IS_COMP_EDITOR (editor));

    switch (classification) {
        case E_CAL_COMPONENT_CLASS_PUBLIC:
        case E_CAL_COMPONENT_CLASS_PRIVATE:
        case E_CAL_COMPONENT_CLASS_CONFIDENTIAL:
            break;
        default:
            classification = E_CAL_COMPONENT_CLASS_PUBLIC;
            break;
    }

    action = comp_editor_get_action (editor, "classify-public");
    gtk_radio_action_set_current_value (
        GTK_RADIO_ACTION (action), classification);
}

ECalComponentClassification
comp_editor_get_classification (CompEditor *editor)
{
    GtkAction *action;

    g_return_val_if_fail (IS_COMP_EDITOR (editor), 0);

    action = comp_editor_get_action (editor, "classify-public");
    return gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action));
}

EShell *
comp_editor_get_shell (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);

    return editor->priv->shell;
}

void
comp_editor_set_summary (CompEditor *editor,
                         const gchar *summary)
{
    gboolean show_warning;

    g_return_if_fail (IS_COMP_EDITOR (editor));

    if (g_strcmp0 (editor->priv->summary, summary) == 0)
        return;

    g_free (editor->priv->summary);
    editor->priv->summary = g_strdup (summary);

    show_warning =
        !editor->priv->warned &&
        !(editor->priv->flags & COMP_EDITOR_DELEGATE) &&
        editor->priv->existing_org &&
        !editor->priv->user_org && !(editor->priv->flags & COMP_EDITOR_NEW_ITEM);

    if (show_warning) {
        e_notice (
            editor->priv->notebook, GTK_MESSAGE_INFO,
            _("Changes made to this item may be "
            "discarded if an update arrives"));
        editor->priv->warned = TRUE;
    }

    update_window_border (editor, summary);

    g_object_notify (G_OBJECT (editor), "summary");
}

const gchar *
comp_editor_get_summary (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);

    return editor->priv->summary;
}

icaltimezone *
comp_editor_get_timezone (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);

    return editor->priv->zone;
}

void
comp_editor_set_timezone (CompEditor *editor,
                          icaltimezone *zone)
{
    g_return_if_fail (IS_COMP_EDITOR (editor));

    if (editor->priv->zone == zone)
        return;

    editor->priv->zone = zone;

    g_object_notify (G_OBJECT (editor), "timezone");
}

gboolean
comp_editor_get_use_24_hour_format (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);

    return editor->priv->use_24_hour_format;
}

void
comp_editor_set_use_24_hour_format (CompEditor *editor,
                                    gboolean use_24_hour_format)
{
    g_return_if_fail (IS_COMP_EDITOR (editor));

    if (editor->priv->use_24_hour_format == use_24_hour_format)
        return;

    editor->priv->use_24_hour_format = use_24_hour_format;

    g_object_notify (G_OBJECT (editor), "use-24-hour-format");
}

gint
comp_editor_get_work_day_end_hour (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), 0);

    return editor->priv->work_day_end_hour;
}

void
comp_editor_set_work_day_end_hour (CompEditor *editor,
                                   gint work_day_end_hour)
{
    g_return_if_fail (IS_COMP_EDITOR (editor));

    if (editor->priv->work_day_end_hour == work_day_end_hour)
        return;

    editor->priv->work_day_end_hour = work_day_end_hour;

    g_object_notify (G_OBJECT (editor), "work-day-end-hour");
}

gint
comp_editor_get_work_day_end_minute (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), 0);

    return editor->priv->work_day_end_minute;
}

void
comp_editor_set_work_day_end_minute (CompEditor *editor,
                                     gint work_day_end_minute)
{
    g_return_if_fail (IS_COMP_EDITOR (editor));

    if (editor->priv->work_day_end_minute == work_day_end_minute)
        return;

    editor->priv->work_day_end_minute = work_day_end_minute;

    g_object_notify (G_OBJECT (editor), "work-day-end-minute");
}

gint
comp_editor_get_work_day_start_hour (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), 0);

    return editor->priv->work_day_start_hour;
}

void
comp_editor_set_work_day_start_hour (CompEditor *editor,
                                     gint work_day_start_hour)
{
    g_return_if_fail (IS_COMP_EDITOR (editor));

    if (editor->priv->work_day_start_hour == work_day_start_hour)
        return;

    editor->priv->work_day_start_hour = work_day_start_hour;

    g_object_notify (G_OBJECT (editor), "work-day-start-hour");
}

gint
comp_editor_get_work_day_start_minute (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), 0);

    return editor->priv->work_day_start_minute;
}

void
comp_editor_set_work_day_start_minute (CompEditor *editor,
                                       gint work_day_start_minute)
{
    g_return_if_fail (IS_COMP_EDITOR (editor));

    if (editor->priv->work_day_start_minute == work_day_start_minute)
        return;

    editor->priv->work_day_start_minute = work_day_start_minute;

    g_object_notify (G_OBJECT (editor), "work-day-start-minute");
}

/**
 * comp_editor_set_changed:
 * @editor: A component editor
 * @changed: Value to set the changed state to
 *
 * Set the dialog changed state to the given value
 **/
void
comp_editor_set_changed (CompEditor *editor,
                         gboolean changed)
{
    GtkAction *action;
    gboolean show_warning;

    g_return_if_fail (IS_COMP_EDITOR (editor));

    /* always process below changes, because other parts of
     * the editor listen for 'changed' notifications to update
     * its widgets, thus do it even the value actually didn't change
     */
    if (editor->priv->changed != changed) {
        editor->priv->changed = changed;

        action = comp_editor_get_action (editor, "save");
        g_return_if_fail (action != NULL);
        gtk_action_set_sensitive (action, changed);
    }

    show_warning =
        changed && !editor->priv->warned &&
        !(editor->priv->flags & COMP_EDITOR_DELEGATE) &&
        editor->priv->existing_org && !editor->priv->user_org
        && !(editor->priv->flags & COMP_EDITOR_NEW_ITEM);

    if (show_warning) {
        e_notice (
            editor->priv->notebook, GTK_MESSAGE_INFO,
            _("Changes made to this item may be "
            "discarded if an update arrives"));
        editor->priv->warned = TRUE;
    }

    g_object_notify (G_OBJECT (editor), "changed");
}

/**
 * comp_editor_get_changed:
 * @editor: A component editor
 *
 * Gets the changed state of the dialog
 *
 * Return value: A boolean indicating if the dialog is in a changed
 * state
 **/
gboolean
comp_editor_get_changed (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);

    return editor->priv->changed;
}

EFocusTracker *
comp_editor_get_focus_tracker (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);

    return editor->priv->focus_tracker;
}

void
comp_editor_set_flags (CompEditor *editor,
                       CompEditorFlags flags)
{
    g_return_if_fail (IS_COMP_EDITOR (editor));

    if (editor->priv->flags == flags)
        return;

    editor->priv->flags = flags;
    editor->priv->user_org = flags & COMP_EDITOR_USER_ORG;

    g_object_notify (G_OBJECT (editor), "flags");
}

CompEditorFlags
comp_editor_get_flags (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), 0);

    return editor->priv->flags;
}

GtkUIManager *
comp_editor_get_ui_manager (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);

    return editor->priv->ui_manager;
}

GtkAction *
comp_editor_get_action (CompEditor *editor,
                        const gchar *action_name)
{
    GtkUIManager *ui_manager;

    g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
    g_return_val_if_fail (action_name != NULL, NULL);

    ui_manager = comp_editor_get_ui_manager (editor);

    return e_lookup_action (ui_manager, action_name);
}

GtkActionGroup *
comp_editor_get_action_group (CompEditor *editor,
                              const gchar *group_name)
{
    GtkUIManager *ui_manager;

    g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
    g_return_val_if_fail (group_name != NULL, NULL);

    ui_manager = comp_editor_get_ui_manager (editor);

    return e_lookup_action_group (ui_manager, group_name);
}

GtkWidget *
comp_editor_get_managed_widget (CompEditor *editor,
                                const gchar *widget_path)
{
    GtkUIManager *ui_manager;
    GtkWidget *widget;

    g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
    g_return_val_if_fail (widget_path != NULL, NULL);

    ui_manager = comp_editor_get_ui_manager (editor);
    widget = gtk_ui_manager_get_widget (ui_manager, widget_path);
    g_return_val_if_fail (widget != NULL, NULL);

    return widget;
}

CompEditor *
comp_editor_find_instance (const gchar *uid)
{
    GList *link;

    g_return_val_if_fail (uid != NULL, NULL);

    link = g_list_find_custom (
        active_editors, uid,
        (GCompareFunc) comp_editor_compare);

    return (link != NULL) ? link->data : NULL;
}

/**
 * comp_editor_set_needs_send:
 * @editor: A component editor
 * @needs_send: Value to set the needs send state to
 *
 * Set the dialog needs send state to the given value
 **/
void
comp_editor_set_needs_send (CompEditor *editor,
                            gboolean needs_send)
{
    g_return_if_fail (IS_COMP_EDITOR (editor));

    editor->priv->needs_send = needs_send;
}

/**
 * comp_editor_get_needs_send:
 * @editor: A component editor
 *
 * Gets the needs send state of the dialog
 *
 * Return value: A boolean indicating if the dialog is in a needs send
 * state
 **/
gboolean
comp_editor_get_needs_send (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);

    return editor->priv->needs_send;
}

static void
page_mapped_cb (GtkWidget *page_widget,
                CompEditorPage *page)
{
    GtkWidget *toplevel;

    toplevel = gtk_widget_get_toplevel (page_widget);
    if (!GTK_IS_WINDOW (toplevel))
        return;

    if (page->accel_group) {
        gtk_window_add_accel_group (
            GTK_WINDOW (toplevel),
            page->accel_group);
    }
}

static void
page_unmapped_cb (GtkWidget *page_widget,
                  CompEditorPage *page)
{
    GtkWidget *toplevel;

    toplevel = gtk_widget_get_toplevel (page_widget);
    if (!GTK_IS_WINDOW (toplevel))
        return;

    if (page->accel_group) {
        gtk_window_remove_accel_group (GTK_WINDOW (toplevel), page->accel_group);
    }
}

/**
 * comp_editor_append_widget:
 * @editor: A component editor
 * @page: A component editor page
 * @label: Label of the page. Should be NULL if add is FALSE.
 * @add: Add's the page into the notebook if TRUE
 *
 * Appends a page to the notebook if add is TRUE else
 * just adds it to the list of pages.
 **/
void
comp_editor_append_widget (CompEditor *editor,
                         GtkWidget *page,
                         const gchar *label,
                         gboolean add)
{
    CompEditorPrivate *priv;
    GtkWidget *label_widget = NULL;

    g_return_if_fail (IS_COMP_EDITOR (editor));

    priv = editor->priv;

    g_object_ref (page);

    if (label)
        label_widget = gtk_label_new_with_mnemonic (label);

    priv->pages = g_list_append (priv->pages, page);

    if (add) {
        gtk_notebook_append_page (priv->notebook, page, label_widget);
        gtk_container_child_set (
            GTK_CONTAINER (priv->notebook), page,
            "tab-fill", FALSE, "tab-expand", FALSE, NULL);
    }

    /* Listen for when the page is mapped/unmapped so we can
     * install/uninstall the appropriate GtkAccelGroup.
    g_signal_connect (
        page, "map",
        G_CALLBACK (page_mapped_cb), page);
    g_signal_connect (
        page, "unmap",
        G_CALLBACK (page_unmapped_cb), page);
        */

}

/**
 * comp_editor_append_page:
 * @editor: A component editor
 * @page: A component editor page
 * @label: Label of the page. Should be NULL if add is FALSE.
 * @add: Add's the page into the notebook if TRUE
 *
 * Appends a page to the notebook if add is TRUE else
 * just adds it to the list of pages.
 **/
void
comp_editor_append_page (CompEditor *editor,
                         CompEditorPage *page,
                         const gchar *label,
                         gboolean add)
{
    CompEditorPrivate *priv;
    GtkWidget *page_widget;
    GtkWidget *label_widget = NULL;
    gboolean is_first_page;

    g_return_if_fail (IS_COMP_EDITOR (editor));
    g_return_if_fail (IS_COMP_EDITOR_PAGE (page));

    priv = editor->priv;

    g_object_ref (page);

    /* If we are editing something, fill the widgets with current info */
    if (priv->comp != NULL) {
        ECalComponent *comp;

        comp = comp_editor_get_current_comp (editor, NULL);
        comp_editor_page_fill_widgets (page, comp);
        g_object_unref (comp);
    }

    page_widget = comp_editor_page_get_widget (page);
    g_return_if_fail (page_widget != NULL);

    if (label)
        label_widget = gtk_label_new_with_mnemonic (label);

    is_first_page = (priv->pages == NULL);

    priv->pages = g_list_append (priv->pages, page);

    if (add) {
        gtk_notebook_append_page (
            priv->notebook, page_widget, label_widget);
        gtk_container_child_set (
            GTK_CONTAINER (priv->notebook), page_widget,
            "tab-fill", FALSE, "tab-expand", FALSE, NULL);
    }

    /* Listen for things happening on the page */
    g_signal_connect_swapped (
        page, "dates_changed",
        G_CALLBACK (page_dates_changed_cb), editor);

    /* Listen for when the page is mapped/unmapped so we can
     * install/uninstall the appropriate GtkAccelGroup. */
    g_signal_connect (
        page_widget, "map",
        G_CALLBACK (page_mapped_cb), page);
    g_signal_connect (
        page_widget, "unmap",
        G_CALLBACK (page_unmapped_cb), page);

    /* The first page is the main page of the editor, so we ask it to focus
     * its main widget.
     */
    if (is_first_page)
        comp_editor_page_focus_main_widget (page);
}

/**
 * comp_editor_remove_page:
 * @editor: A component editor
 * @page: A component editor page
 *
 * Removes the page from the component editor
 **/
void
comp_editor_remove_page (CompEditor *editor,
                         CompEditorPage *page)
{
    CompEditorPrivate *priv;
    GtkWidget *page_widget;
    gint page_num;

    g_return_if_fail (IS_COMP_EDITOR (editor));
    g_return_if_fail (IS_COMP_EDITOR_PAGE (page));

    priv = editor->priv;

    page_widget = comp_editor_page_get_widget (page);
    page_num = gtk_notebook_page_num (priv->notebook, page_widget);
    if (page_num == -1)
        return;

    /* Disconnect all the signals added in append_page(). */
    g_signal_handlers_disconnect_matched (
        page, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
    g_signal_handlers_disconnect_matched (
        page_widget, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page);

    gtk_notebook_remove_page (priv->notebook, page_num);

    priv->pages = g_list_remove (priv->pages, page);
    g_object_unref (page);
}

/**
 * comp_editor_show_page:
 * @editor:
 * @page:
 *
 *
 **/
void
comp_editor_show_page (CompEditor *editor,
                       CompEditorPage *page)
{
    CompEditorPrivate *priv;
    GtkWidget *page_widget;
    gint page_num;

    g_return_if_fail (IS_COMP_EDITOR (editor));
    g_return_if_fail (IS_COMP_EDITOR_PAGE (page));

    priv = editor->priv;

    page_widget = comp_editor_page_get_widget (page);
    page_num = gtk_notebook_page_num (priv->notebook, page_widget);
    gtk_notebook_set_current_page (priv->notebook, page_num);
}

/**
 * comp_editor_set_client:
 * @editor: A component editor
 * @cal_client: The calendar client to use
 *
 * Sets the calendar client used by the editor to update components
 **/
void
comp_editor_set_client (CompEditor *editor,
                        ECalClient *cal_client)
{
    g_return_if_fail (IS_COMP_EDITOR (editor));
    g_return_if_fail (cal_client == NULL || E_IS_CAL_CLIENT (cal_client));

    if (editor->priv->cal_client == cal_client)
        return;

    if (cal_client != NULL)
        g_object_ref (cal_client);

    if (editor->priv->cal_client != NULL)
        g_object_unref (editor->priv->cal_client);

    editor->priv->cal_client = cal_client;

    if (editor->priv->source_client == NULL && cal_client != NULL)
        editor->priv->source_client = g_object_ref (cal_client);

    g_object_notify (G_OBJECT (editor), "client");
}

/**
 * comp_editor_get_client:
 * @editor: A component editor
 *
 * Returns the calendar client of the editor
 *
 * Return value: The calendar client of the editor
 **/
ECalClient *
comp_editor_get_client (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);

    return editor->priv->cal_client;
}

static void
attachment_loaded_cb (EAttachment *attachment,
                      GAsyncResult *result,
                      GtkWindow *parent)
{
    GFileInfo *file_info;
    const gchar *display_name;
    const gchar *uid;
    gchar *new_name;

    /* Prior to 2.27.2, attachment files were named:
     *
     *     <component-uid> '-' <actual-filename>
     *     -------------------------------------
     *              (one long filename)
     *
     * Here we fix the display name if this form is detected so we
     * don't show the component UID in the user interface.  If the
     * user saves changes in the editor, the attachment will be
     * written to disk as:
     *
     *     <component-uid> / <actual-filename>
     *     ---------------   -----------------
     *       (directory)      (original name)
     *
     * So this is a lazy migration from the old form to the new.
     */

    file_info = e_attachment_get_file_info (attachment);
    if (!file_info) {
        /* failed to load an attachment file */
        e_attachment_load_handle_error (attachment, result, parent);
        return;
    }

    display_name = g_file_info_get_display_name (file_info);
    uid = g_object_get_data (G_OBJECT (attachment), "uid");

    if (g_str_has_prefix (display_name, uid)) {
        new_name = g_strdup (display_name + strlen (uid) + 1);
        g_file_info_set_display_name (file_info, new_name);
        g_object_notify (G_OBJECT (attachment), "file-info");
        g_free (new_name);
    }

    e_attachment_load_handle_error (attachment, result, parent);
}

static void
set_attachment_list (CompEditor *editor,
                     GSList *uri_list)
{
    EAttachmentStore *store;
    EAttachmentView *view;
    const gchar *uid = NULL;
    GSList *iter;

    view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
    store = e_attachment_view_get_store (view);

    if (e_attachment_store_get_num_attachments (store) > 0) {
        /* To prevent repopulating the
         * bar due to redraw functions in fill_widget.
         * Assumes it can be set only once.
         */
        return;
    }

    /* XXX What an awkward API this is.  Takes a return location
     *     for a constant string instead of just returning it. */
    e_cal_component_get_uid (editor->priv->comp, &uid);

    for (iter = uri_list; iter != NULL; iter = iter->next) {
        EAttachment *attachment;

        attachment = e_attachment_new_for_uri (iter->data);
        e_attachment_store_add_attachment (store, attachment);
        g_object_set_data_full (
            G_OBJECT (attachment),
            "uid", g_strdup (uid),
            (GDestroyNotify) g_free);
        e_attachment_load_async (
            attachment, (GAsyncReadyCallback)
            attachment_loaded_cb, editor);
        g_object_unref (attachment);
    }
}

static void
fill_widgets (CompEditor *editor)
{
    EAttachmentStore *store;
    EAttachmentView *view;
    CompEditorPrivate *priv;
    GtkAction *action;
    GList *iter;

    view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
    store = e_attachment_view_get_store (view);

    priv = editor->priv;

    /*Check if attachments are available here and set them*/
    if (e_cal_component_has_attachments (priv->comp)) {
        GSList *attachment_list = NULL;
        e_cal_component_get_attachment_list (priv->comp, &attachment_list);
        g_signal_handlers_block_by_func (
            store, G_CALLBACK (attachment_store_changed_cb),
            editor);
        set_attachment_list (editor, attachment_list);
        g_signal_handlers_unblock_by_func (
            store, G_CALLBACK (attachment_store_changed_cb),
            editor);
        g_slist_foreach (attachment_list, (GFunc) g_free, NULL);
        g_slist_free (attachment_list);
    }

    action = comp_editor_get_action (editor, "classify-public");
    g_signal_handlers_block_by_func (
        action, G_CALLBACK (action_classification_cb), editor);

    for (iter = priv->pages; iter != NULL; iter = iter->next) {
        if (IS_COMP_EDITOR_PAGE (iter->data))
            comp_editor_page_fill_widgets (iter->data, priv->comp);
    }

    g_signal_handlers_unblock_by_func (
        action, G_CALLBACK (action_classification_cb), editor);
}

static void
real_edit_comp (CompEditor *editor,
                ECalComponent *comp)
{
    CompEditorPrivate *priv;

    g_return_if_fail (IS_COMP_EDITOR (editor));

    priv = editor->priv;

    if (priv->comp) {
        g_object_unref (priv->comp);
        priv->comp = NULL;
    }

    if (comp) {
        priv->comp = e_cal_component_clone (comp);
        comp_editor_copy_new_attendees (priv->comp, comp);
    }

    priv->existing_org = e_cal_component_has_organizer (comp);
    priv->warned = FALSE;

    update_window_border (editor, NULL);

    fill_widgets (editor);

    comp_editor_set_changed (editor, FALSE);

    listen_for_changes (editor);
}

/* TODO These functions should be available in e-cal-component.c */
static void
set_attendees_for_delegation (ECalComponent *comp,
                              const gchar *address,
                              ECalComponentItipMethod method)
{
    icalproperty *prop;
    icalparameter *param;
    icalcomponent *icalcomp;

    icalcomp = e_cal_component_get_icalcomponent (comp);

    for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
         prop;
         prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) {
        const gchar *attendee = icalproperty_get_attendee (prop);
        const gchar *delfrom = NULL;

        param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER);
        if (param)
            delfrom = icalparameter_get_delegatedfrom (param);
        if (!(g_str_equal (itip_strip_mailto (attendee), address) ||
                ((delfrom && *delfrom) &&
                 g_str_equal (itip_strip_mailto (delfrom), address)))) {
            icalcomponent_remove_property (icalcomp, prop);
        }

    }

}

static void
get_users_from_memo_comp (ECalComponent *comp,
                          GSList **users)
{
    icalcomponent *icalcomp;
    icalproperty *icalprop;
    const gchar *attendees = NULL;
    gchar **emails, **iter;

    icalcomp = e_cal_component_get_icalcomponent (comp);

    for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
         icalprop != NULL;
         icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) {
        const gchar *x_name;

        x_name = icalproperty_get_x_name (icalprop);

        if (g_str_equal (x_name, "X-EVOLUTION-RECIPIENTS"))
            break;
    }

    if (icalprop) {
        attendees = icalproperty_get_x (icalprop);
        emails = g_strsplit (attendees, ";", -1);

        iter = emails;
        while (*iter) {
            *users = g_slist_append (*users, g_strdup (*iter));
            iter++;
        }
        g_strfreev (emails);
    }
}

static gboolean
real_send_comp (CompEditor *editor,
                ECalComponentItipMethod method,
                gboolean strip_alarms)
{
    CompEditorPrivate *priv;
    CompEditorFlags flags;
    EShell *shell;
    ESourceRegistry *registry;
    ECalComponent *send_comp = NULL;
    gchar *address = NULL;
    GSList *users = NULL;

    g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);

    priv = editor->priv;

    flags = comp_editor_get_flags (editor);
    shell = comp_editor_get_shell (editor);

    registry = e_shell_get_registry (shell);

    if (priv->mod == CALOBJ_MOD_ALL && e_cal_component_is_instance (priv->comp)) {
        /* Ensure we send the master object, not the instance only */
        icalcomponent *icalcomp = NULL;
        const gchar *uid = NULL;

        e_cal_component_get_uid (priv->comp, &uid);
        if (e_cal_client_get_object_sync (priv->cal_client, uid, NULL, &icalcomp, NULL, NULL) && icalcomp) {
            send_comp = e_cal_component_new ();
            if (!e_cal_component_set_icalcomponent (send_comp, icalcomp)) {
                icalcomponent_free (icalcomp);
                g_object_unref (send_comp);
                send_comp = NULL;
            }
        }
    }

    if (!send_comp)
        send_comp = e_cal_component_clone (priv->comp);

    comp_editor_copy_new_attendees (send_comp, priv->comp);

    if (e_cal_component_get_vtype (send_comp) == E_CAL_COMPONENT_JOURNAL)
        get_users_from_memo_comp (send_comp, &users);

    /* The user updates the delegated status to the Organizer,
     * so remove all other attendees. */
    if (flags & COMP_EDITOR_DELEGATE) {
        address = itip_get_comp_attendee (
            registry, send_comp, priv->cal_client);

        if (address)
            set_attendees_for_delegation (send_comp, address, method);
    }

    if (!e_cal_component_has_attachments (priv->comp) ||
        e_client_check_capability (
            E_CLIENT (priv->cal_client),
            CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) {
        if (itip_send_comp (
            registry, method, send_comp, priv->cal_client,
            NULL, NULL, users, strip_alarms,
            priv->flags & COMP_EDITOR_SEND_TO_NEW_ATTENDEES_ONLY)) {
            g_object_unref (send_comp);
            return TRUE;
        }
    } else {
        /* Clone the component with attachments set to CID:...  */
        GSList *attach_list = NULL;
        GSList *mime_attach_list, *attach;

        /* mime_attach_list is freed by itip_send_comp */
        mime_attach_list = comp_editor_get_mime_attach_list (editor);

        for (attach = mime_attach_list; attach; attach = attach->next) {
            struct CalMimeAttach *cma = (struct CalMimeAttach *) attach->data;

            attach_list = g_slist_append (
                attach_list, g_strconcat (
                "cid:", cma->content_id, NULL));
        }

        if (attach_list) {
            e_cal_component_set_attachment_list (send_comp, attach_list);

            g_slist_foreach (attach_list, (GFunc) g_free, NULL);
            g_slist_free (attach_list);
        }

        if (itip_send_comp (
            registry, method, send_comp, priv->cal_client,
            NULL, mime_attach_list, users, strip_alarms,
            priv->flags & COMP_EDITOR_SEND_TO_NEW_ATTENDEES_ONLY)) {
            gboolean saved = save_comp (editor);

            g_object_unref (send_comp);

            if (!saved)
                comp_editor_set_changed (editor, TRUE);

            return saved;
        }
    }

    g_object_unref (send_comp);
    g_free (address);
    comp_editor_set_changed (editor, TRUE);

    return FALSE;

}

/**
 * comp_editor_edit_comp:
 * @editor: A component editor
 * @comp: A calendar component
 *
 * Starts the editor editing the given component
 **/
void
comp_editor_edit_comp (CompEditor *editor,
                       ECalComponent *comp)
{
    CompEditorClass *class;

    g_return_if_fail (IS_COMP_EDITOR (editor));
    g_return_if_fail (E_IS_CAL_COMPONENT (comp));

    class = COMP_EDITOR_GET_CLASS (editor);

    if (class->edit_comp)
        class->edit_comp (editor, comp);
}

ECalComponent *
comp_editor_get_comp (CompEditor *editor)
{
    g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);

    return editor->priv->comp;
}

/**
 * comp_editor_get_current_comp
 * @editor: a #CompEditor
 * @correct: Set this no non-%NULL if you are interested to know if all
 * pages reported success when filling component.
 *
 * Returns: Newly allocated component, should be unref-ed by g_object_unref().
 **/
ECalComponent *
comp_editor_get_current_comp (CompEditor *editor,
                              gboolean *correct)
{
    CompEditorPrivate *priv;
    ECalComponent *comp;
    GList *l;
    gboolean all_ok = TRUE;

    g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);

    priv = editor->priv;

    comp = e_cal_component_clone (priv->comp);
    comp_editor_copy_new_attendees (comp, priv->comp);
    if (priv->changed) {
        for (l = priv->pages; l != NULL; l = l->next) {
            if (IS_COMP_EDITOR_PAGE (l->data))
                all_ok = comp_editor_page_fill_component (l->data, comp) && all_ok;
        }
    }

    if (correct)
        *correct = all_ok;

    return comp;
}

/**
 * comp_editor_save_comp:
 * @editor:
 *
 *
 **/
gboolean
comp_editor_save_comp (CompEditor *editor,
                       gboolean send)
{
    return prompt_and_save_changes (editor, send);
}

/**
 * comp_editor_delete_comp:
 * @editor:
 *
 *
 **/
void
comp_editor_delete_comp (CompEditor *editor)
{
    CompEditorPrivate *priv;
    const gchar *uid;

    g_return_if_fail (IS_COMP_EDITOR (editor));

    priv = editor->priv;

    e_cal_component_get_uid (priv->comp, &uid);
    if (e_cal_component_is_instance (priv->comp) ||
        e_cal_component_has_recurrences (priv->comp))
        e_cal_client_remove_object_sync (
            priv->cal_client, uid, NULL,
            CALOBJ_MOD_ALL, NULL, NULL);
    else
        e_cal_client_remove_object_sync (
            priv->cal_client, uid, NULL,
            CALOBJ_MOD_THIS, NULL, NULL);
    close_dialog (editor);
}

/**
 * comp_editor_send_comp:
 * @editor:
 * @method:
 *
 *
 **/
gboolean
comp_editor_send_comp (CompEditor *editor,
                       ECalComponentItipMethod method,
                       gboolean strip_alarms)
{
    CompEditorClass *class;

    g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);

    class = COMP_EDITOR_GET_CLASS (editor);

    if (class->send_comp)
        return class->send_comp (editor, method, strip_alarms);

    return FALSE;
}

gboolean
comp_editor_close (CompEditor *editor)
{
    gboolean close;

    g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE);

    commit_all_fields (editor);

    close = prompt_and_save_changes (editor, TRUE);
    if (close)
        close_dialog (editor);

    return close;
}

/* Utility function to get the mime-attachment list from the attachment
 * bar for sending the comp via itip. The list and its contents must
 * be freed by the caller.
 */
GSList *
comp_editor_get_mime_attach_list (CompEditor *editor)
{
    EAttachmentStore *store;
    EAttachmentView *view;
    GtkTreeModel *model;
    GtkTreeIter iter;
    struct CalMimeAttach *cal_mime_attach;
    GSList *attach_list = NULL;
    gboolean valid;

    view = E_ATTACHMENT_VIEW (editor->priv->attachment_view);
    store = e_attachment_view_get_store (view);

    model = GTK_TREE_MODEL (store);
    valid = gtk_tree_model_get_iter_first (model, &iter);

    while (valid) {
        EAttachment *attachment;
        CamelDataWrapper *wrapper;
        CamelMimePart *mime_part;
        CamelStream *stream;
        GByteArray *byte_array;
        guchar *buffer = NULL;
        const gchar *desc, *disp;
        gint column_id;

        column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
        gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
        mime_part = e_attachment_get_mime_part (attachment);
        g_object_unref (attachment);

        valid = gtk_tree_model_iter_next (model, &iter);

        if (mime_part == NULL)
            continue;

        cal_mime_attach = g_malloc0 (sizeof (struct CalMimeAttach));
        wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));

        byte_array = g_byte_array_new ();
        stream = camel_stream_mem_new_with_byte_array (byte_array);

        camel_data_wrapper_decode_to_stream_sync (
            wrapper, stream, NULL, NULL);
        buffer = g_memdup (byte_array->data, byte_array->len);

        camel_mime_part_set_content_id (mime_part, NULL);

        cal_mime_attach->encoded_data = (gchar *) buffer;
        cal_mime_attach->length = byte_array->len;
        cal_mime_attach->filename =
            g_strdup (camel_mime_part_get_filename (mime_part));
        desc = camel_mime_part_get_description (mime_part);
        if (!desc || *desc == '\0')
            desc = _("attachment");
        cal_mime_attach->description = g_strdup (desc);
        cal_mime_attach->content_type = g_strdup (
            camel_data_wrapper_get_mime_type (wrapper));
        cal_mime_attach->content_id = g_strdup (
            camel_mime_part_get_content_id (mime_part));

        disp = camel_mime_part_get_disposition (mime_part);
        if (disp && !g_ascii_strcasecmp (disp, "inline"))
            cal_mime_attach->disposition = TRUE;

        attach_list = g_slist_append (attach_list, cal_mime_attach);

        g_object_unref (stream);

    }

    return attach_list;
}

static void
page_dates_changed_cb (CompEditor *editor,
                       CompEditorPageDates *dates,
                       CompEditorPage *page)
{
    CompEditorPrivate *priv = editor->priv;
    GList *l;

    for (l = priv->pages; l != NULL; l = l->next)
        if (page != (CompEditorPage *) l->data && IS_COMP_EDITOR_PAGE (l->data))
            comp_editor_page_set_dates (l->data, dates);

    if (!priv->warned && priv->existing_org && !priv->user_org &&
        !(editor->priv->flags & COMP_EDITOR_NEW_ITEM)) {
        e_notice (
            priv->notebook, GTK_MESSAGE_INFO,
            _("Changes made to this item may be discarded "
            "if an update arrives"));
        priv->warned = TRUE;
    }
}

static void
obj_modified_cb (ECalClientView *view,
                 const GSList *objects,
                 CompEditor *editor)
{
    CompEditorPrivate *priv;
    ECalComponent *comp = NULL;

    priv = editor->priv;

    /* We queried based on a specific UID so we definitely changed */
    if (changed_component_dialog (
        (GtkWindow *) editor, priv->comp, FALSE, priv->changed)) {
        icalcomponent *icalcomp = icalcomponent_new_clone (objects->data);

        comp = e_cal_component_new ();
        if (e_cal_component_set_icalcomponent (comp, icalcomp)) {
            comp_editor_edit_comp (editor, comp);
        } else {
            GtkWidget *dialog;

            dialog = gtk_message_dialog_new (
                NULL, 0,
                GTK_MESSAGE_ERROR,
                GTK_BUTTONS_OK,
                "%s",
                _("Unable to use current version!"));
            gtk_dialog_run (GTK_DIALOG (dialog));
            gtk_widget_destroy (dialog);

            icalcomponent_free (icalcomp);
        }

        g_object_unref (comp);
    }
}

static void
obj_removed_cb (ECalClientView *view,
                const GSList *uids,
                CompEditor *editor)
{
    CompEditorPrivate *priv = editor->priv;

    if (changed_component_dialog (
        GTK_WINDOW (editor), priv->comp, TRUE, priv->changed))
        close_dialog (editor);
}