Compare commits
	
		
			50 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					2be92900b6 | ||
| 
						 | 
					e82cbf3cc8 | ||
| 
						 | 
					0b05280e03 | ||
| 
						 | 
					cd468021e2 | ||
| 
						 | 
					57ebfb596b | ||
| 
						 | 
					8a1313be71 | ||
| 
						 | 
					803e74101c | ||
| 
						 | 
					858e1b7f5b | ||
| 
						 | 
					a922db0398 | ||
| 
						 | 
					bbd2b89df3 | ||
| 
						 | 
					505b9047d7 | ||
| 
						 | 
					dd8716e5d9 | ||
| 
						 | 
					18f2f6f938 | ||
| 
						 | 
					41b0e0832e | ||
| 
						 | 
					374a88366b | ||
| 
						 | 
					a71c82863e | ||
| 
						 | 
					a1440bdec7 | ||
| 
						 | 
					b10458e57a | ||
| 
						 | 
					50c5591ec2 | ||
| 
						 | 
					bce2cf9164 | ||
| 
						 | 
					2f52b87451 | ||
| 
						 | 
					ba5a28023e | ||
| 
						 | 
					0a87e28d1a | ||
| 
						 | 
					d7ad949ecb | ||
| 
						 | 
					fd49fe6915 | ||
| 
						 | 
					c3c34890ce | ||
| 
						 | 
					af68881a94 | ||
| 
						 | 
					0d9095d2d4 | ||
| 
						 | 
					8bc396a7e0 | ||
| 
						 | 
					71851a58e5 | ||
| 
						 | 
					70f50d61e0 | ||
| 
						 | 
					f3ad82442d | ||
| 
						 | 
					6c80a35f8f | ||
| 
						 | 
					a2932250a7 | ||
| 
						 | 
					1d5cce679a | ||
| 
						 | 
					137ea12109 | ||
| 
						 | 
					0b866a620f | ||
| 
						 | 
					397386c761 | ||
| 
						 | 
					f51984b20c | ||
| 
						 | 
					8e9bc6019c | ||
| 
						 | 
					51481bed82 | ||
| 
						 | 
					8dd05f9e7b | ||
| 
						 | 
					b7fe72e74a | ||
| 
						 | 
					7aa3aba0c2 | ||
| 
						 | 
					eb66993447 | ||
| 
						 | 
					7d5b4511cd | ||
| 
						 | 
					3d1063dacb | ||
| 
						 | 
					b4c038c036 | ||
| 
						 | 
					1ca1a2712d | ||
| 
						 | 
					85ec4d86f3 | 
@@ -66,6 +66,10 @@ GJS_MIN_VERSION=0.7.8
 | 
			
		||||
MUTTER_MIN_VERSION=2.91.4
 | 
			
		||||
GTK_MIN_VERSION=2.91.7
 | 
			
		||||
GIO_MIN_VERSION=2.25.9
 | 
			
		||||
LIBECAL_REQUIRED=1.6.0
 | 
			
		||||
LIBEDATASERVER_REQUIRED=1.2.0
 | 
			
		||||
LIBEDATASERVERUI_REQUIRED=1.2.0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Collect more than 20 libraries for a prize!
 | 
			
		||||
PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION
 | 
			
		||||
@@ -113,6 +117,10 @@ PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 2.90.0],
 | 
			
		||||
	 AC_SUBST([HAVE_BLUETOOTH],[0])
 | 
			
		||||
	 AC_MSG_RESULT([no])])
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(LIBECAL, libecal-1.2 >= $LIBECAL_REQUIRED libedataserver-1.2 >= $LIBEDATASERVER_REQUIRED libedataserverui-1.2 >= $LIBEDATASERVERUI_REQUIRED)
 | 
			
		||||
AC_SUBST(LIBECAL_CFLAGS)
 | 
			
		||||
AC_SUBST(LIBECAL_LIBS)
 | 
			
		||||
 | 
			
		||||
MUTTER_BIN_DIR=`$PKG_CONFIG --variable=exec_prefix mutter-plugins`/bin
 | 
			
		||||
# FIXME: metacity-plugins.pc should point directly to its .gir file
 | 
			
		||||
MUTTER_LIB_DIR=`$PKG_CONFIG --variable=libdir mutter-plugins`
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,8 @@ dist_images_DATA =				\
 | 
			
		||||
themedir = $(pkgdatadir)/theme
 | 
			
		||||
dist_theme_DATA =				\
 | 
			
		||||
	theme/add-workspace.svg			\
 | 
			
		||||
	theme/calendar-arrow-left.svg		\
 | 
			
		||||
	theme/calendar-arrow-right.svg		\
 | 
			
		||||
	theme/close-window.svg			\
 | 
			
		||||
	theme/close.svg				\
 | 
			
		||||
	theme/corner-ripple.png			\
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										82
									
								
								data/theme/calendar-arrow-left.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								data/theme/calendar-arrow-left.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="16"
 | 
			
		||||
   height="16"
 | 
			
		||||
   id="svg2"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48+devel r9942 custom"
 | 
			
		||||
   sodipodi:docname="New document 4">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs4" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="1"
 | 
			
		||||
     inkscape:cx="8.984481"
 | 
			
		||||
     inkscape:cy="5.6224906"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     borderlayer="true"
 | 
			
		||||
     inkscape:showpageshadow="false"
 | 
			
		||||
     inkscape:window-width="930"
 | 
			
		||||
     inkscape:window-height="681"
 | 
			
		||||
     inkscape:window-x="1892"
 | 
			
		||||
     inkscape:window-y="272"
 | 
			
		||||
     inkscape:window-maximized="0">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid17403"
 | 
			
		||||
       empspacing="5"
 | 
			
		||||
       visible="true"
 | 
			
		||||
       enabled="true"
 | 
			
		||||
       snapvisiblegridlinesonly="true" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata7">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     transform="translate(0,-1036.3622)">
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:type="star"
 | 
			
		||||
       style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.43015847;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
 | 
			
		||||
       id="path18028"
 | 
			
		||||
       sodipodi:sides="3"
 | 
			
		||||
       sodipodi:cx="84.5"
 | 
			
		||||
       sodipodi:cy="337.5"
 | 
			
		||||
       sodipodi:r1="5"
 | 
			
		||||
       sodipodi:r2="2.5"
 | 
			
		||||
       sodipodi:arg1="0.52359878"
 | 
			
		||||
       sodipodi:arg2="1.5707963"
 | 
			
		||||
       inkscape:flatsided="true"
 | 
			
		||||
       inkscape:rounded="0"
 | 
			
		||||
       inkscape:randomized="0"
 | 
			
		||||
       d="M 88.830127,340 80.169873,340 84.5,332.5 z"
 | 
			
		||||
       transform="matrix(0,1.3621708,0.99186247,0,-325.48222,929.32667)" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.5 KiB  | 
							
								
								
									
										82
									
								
								data/theme/calendar-arrow-right.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								data/theme/calendar-arrow-right.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="16"
 | 
			
		||||
   height="16"
 | 
			
		||||
   id="svg2"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48+devel r9942 custom"
 | 
			
		||||
   sodipodi:docname="arrow-left.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs4" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="1"
 | 
			
		||||
     inkscape:cx="7.7366092"
 | 
			
		||||
     inkscape:cy="6.4536271"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     borderlayer="true"
 | 
			
		||||
     inkscape:showpageshadow="false"
 | 
			
		||||
     inkscape:window-width="930"
 | 
			
		||||
     inkscape:window-height="681"
 | 
			
		||||
     inkscape:window-x="1892"
 | 
			
		||||
     inkscape:window-y="272"
 | 
			
		||||
     inkscape:window-maximized="0">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid17403"
 | 
			
		||||
       empspacing="5"
 | 
			
		||||
       visible="true"
 | 
			
		||||
       enabled="true"
 | 
			
		||||
       snapvisiblegridlinesonly="true" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata7">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     transform="translate(0,-1036.3622)">
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:type="star"
 | 
			
		||||
       style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.43015847;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
 | 
			
		||||
       id="path18028"
 | 
			
		||||
       sodipodi:sides="3"
 | 
			
		||||
       sodipodi:cx="84.5"
 | 
			
		||||
       sodipodi:cy="337.5"
 | 
			
		||||
       sodipodi:r1="5"
 | 
			
		||||
       sodipodi:r2="2.5"
 | 
			
		||||
       sodipodi:arg1="0.52359878"
 | 
			
		||||
       sodipodi:arg2="1.5707963"
 | 
			
		||||
       inkscape:flatsided="true"
 | 
			
		||||
       inkscape:rounded="0"
 | 
			
		||||
       inkscape:randomized="0"
 | 
			
		||||
       d="M 88.830127,340 80.169873,340 84.5,332.5 z"
 | 
			
		||||
       transform="matrix(0,1.3621708,-0.99186247,0,342.48324,929.32667)" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.5 KiB  | 
@@ -674,6 +674,17 @@ StTooltip StLabel {
 | 
			
		||||
 | 
			
		||||
/* Calendar popup */
 | 
			
		||||
 | 
			
		||||
#calendarArea {
 | 
			
		||||
    /* this is the width of the entire popup */
 | 
			
		||||
    width: 600px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-vertical-separator {
 | 
			
		||||
    -stipple-width: 1px;
 | 
			
		||||
    -stipple-color: #505050;
 | 
			
		||||
    width: 1.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#calendarPopup {
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
    background: rgba(0,0,0,0.9);
 | 
			
		||||
@@ -686,37 +697,155 @@ StTooltip StLabel {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar {
 | 
			
		||||
    spacing-rows: 5px;
 | 
			
		||||
    spacing-columns: 3px;
 | 
			
		||||
    padding: .4em 1.75em;
 | 
			
		||||
    spacing-rows: 0px;
 | 
			
		||||
    spacing-columns: 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-change-month {
 | 
			
		||||
.calendar-month-label {
 | 
			
		||||
    color: #666666;
 | 
			
		||||
    font-size: 10px;
 | 
			
		||||
    padding: 2px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-change-month:hover {
 | 
			
		||||
    background: #314a6c;
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
.calendar-change-month-back {
 | 
			
		||||
    width: 20px;
 | 
			
		||||
    height: 20px;
 | 
			
		||||
    background-image: url("calendar-arrow-left.svg");
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
}
 | 
			
		||||
.calendar-change-month-back:hover {
 | 
			
		||||
    background-color: #999999;
 | 
			
		||||
}
 | 
			
		||||
.calendar-change-month-back:active {
 | 
			
		||||
    background-color: #aaaaaa;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-change-month:active {
 | 
			
		||||
    background: #213050;
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
.calendar-change-month-forward {
 | 
			
		||||
    width: 20px;
 | 
			
		||||
    height: 20px;
 | 
			
		||||
    background-image: url("calendar-arrow-right.svg");
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
}
 | 
			
		||||
.calendar-change-month-forward:hover {
 | 
			
		||||
    background-color: #999999;
 | 
			
		||||
}
 | 
			
		||||
.calendar-change-month-forward:active {
 | 
			
		||||
    background-color: #aaaaaa;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.datemenu-date-label {
 | 
			
		||||
    padding: .4em 1.75em;
 | 
			
		||||
    font-size: 16px;
 | 
			
		||||
    color: #ffffff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-day-base {
 | 
			
		||||
    font-size: 10px;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    width: 24px;
 | 
			
		||||
    height: 24px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-day-base:hover {
 | 
			
		||||
    background: #777777;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-day-base:active {
 | 
			
		||||
    background: #555555;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-day-heading {
 | 
			
		||||
    color: #666666;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-week-number {
 | 
			
		||||
    color: #666666;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Hack used in lieu of border-collapse - see calendar.js */
 | 
			
		||||
.calendar-day {
 | 
			
		||||
    padding: 1px 2px;
 | 
			
		||||
    border: 1px solid #333333;
 | 
			
		||||
    color: #cccccc;
 | 
			
		||||
    border-top-width: 0;
 | 
			
		||||
    border-left-width: 0;
 | 
			
		||||
}
 | 
			
		||||
.calendar-day-top {
 | 
			
		||||
    border-top-width: 1px;
 | 
			
		||||
}
 | 
			
		||||
.calendar-day-left {
 | 
			
		||||
    border-left-width: 1px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-work-day {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-nonwork-day {
 | 
			
		||||
    background-color: rgba(128, 128, 128, .1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-today {
 | 
			
		||||
    color: #ffffff;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    background: #ffffff;
 | 
			
		||||
    color: black;
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
    background-gradient-direction: vertical;
 | 
			
		||||
    background-gradient-start: #3c3c3c;
 | 
			
		||||
    background-gradient-end: #131313;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-other-month-day {
 | 
			
		||||
    color: #cccccc;
 | 
			
		||||
    color: #333333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-day-with-events {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.events-header-vbox {
 | 
			
		||||
    spacing: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.events-header {
 | 
			
		||||
    height: 40px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.events-header-hbox {
 | 
			
		||||
    spacing: 8px;
 | 
			
		||||
    padding: 0.3em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.events-day-header {
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    color: rgba(153, 153, 153, 1.0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.events-day-dayname {
 | 
			
		||||
    font-size: 12px;
 | 
			
		||||
    color: rgba(153, 153, 153, 1.0);
 | 
			
		||||
    text-align: left;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.events-day-time {
 | 
			
		||||
    font-size: 12px;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    color: #fff;
 | 
			
		||||
    text-align: right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.events-day-task {
 | 
			
		||||
    font-size: 12px;
 | 
			
		||||
    color: rgba(153, 153, 153, 1.0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.events-day-name-box {
 | 
			
		||||
    width: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.events-time-box {
 | 
			
		||||
    width: 70px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.events-event-box {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.url-highlighter {
 | 
			
		||||
@@ -895,10 +1024,6 @@ StTooltip StLabel {
 | 
			
		||||
    padding-left: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-calendarweek {
 | 
			
		||||
    color: #666666;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* App Switcher */
 | 
			
		||||
#altTabPopup {
 | 
			
		||||
    padding: 8px;
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	ui/chrome.js		\
 | 
			
		||||
	ui/ctrlAltTab.js	\
 | 
			
		||||
	ui/dash.js		\
 | 
			
		||||
	ui/dateMenu.js		\
 | 
			
		||||
	ui/dnd.js		\
 | 
			
		||||
	ui/docDisplay.js	\
 | 
			
		||||
	ui/endSessionDialog.js	\
 | 
			
		||||
 
 | 
			
		||||
@@ -4,19 +4,78 @@ const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const Gettext_gtk30 = imports.gettext.domain('gtk30');
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
 | 
			
		||||
const WEEKDATE_HEADER_WIDTH_DIGITS = 3;
 | 
			
		||||
const SHOW_WEEKDATE_KEY = 'show-weekdate';
 | 
			
		||||
 | 
			
		||||
// in org.gnome.desktop.interface
 | 
			
		||||
const CLOCK_FORMAT_KEY        = 'clock-format';
 | 
			
		||||
 | 
			
		||||
function _sameDay(dateA, dateB) {
 | 
			
		||||
    return (dateA.getDate() == dateB.getDate() &&
 | 
			
		||||
            dateA.getMonth() == dateB.getMonth() &&
 | 
			
		||||
            dateA.getYear() == dateB.getYear());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _sameYear(dateA, dateB) {
 | 
			
		||||
    return (dateA.getYear() == dateB.getYear());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* TODO: maybe needs config - right now we assume that Saturday and
 | 
			
		||||
 * Sunday are non-work days (not true in e.g. Israel, it's Sunday and
 | 
			
		||||
 * Monday there)
 | 
			
		||||
 */
 | 
			
		||||
function _isWorkDay(date) {
 | 
			
		||||
    return date.getDay() != 0 && date.getDay() != 6;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _getBeginningOfDay(date) {
 | 
			
		||||
    let ret = new Date(date.getTime());
 | 
			
		||||
    ret.setHours(0);
 | 
			
		||||
    ret.setMinutes(0);
 | 
			
		||||
    ret.setSeconds(0);
 | 
			
		||||
    ret.setMilliseconds(0);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _getEndOfDay(date) {
 | 
			
		||||
    let ret = new Date(date.getTime());
 | 
			
		||||
    ret.setHours(23);
 | 
			
		||||
    ret.setMinutes(59);
 | 
			
		||||
    ret.setSeconds(59);
 | 
			
		||||
    ret.setMilliseconds(999);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _formatEventTime(event, clockFormat) {
 | 
			
		||||
    let ret;
 | 
			
		||||
    if (event.allDay) {
 | 
			
		||||
        /* Translators: Shown in calendar event list for all day events */
 | 
			
		||||
        ret = _("All Day");
 | 
			
		||||
    } else {
 | 
			
		||||
        switch (clockFormat) {
 | 
			
		||||
        case '24h':
 | 
			
		||||
            ret = event.date.toLocaleFormat('%H:%M');
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            /* explicit fall-through */
 | 
			
		||||
        case '12h':
 | 
			
		||||
            ret = event.date.toLocaleFormat('%l:%M %p');
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _getCalendarWeekForDate(date) {
 | 
			
		||||
    // Based on the algorithms found here:
 | 
			
		||||
    // http://en.wikipedia.org/wiki/Talk:ISO_week_date
 | 
			
		||||
@@ -43,12 +102,259 @@ function _getDigitWidth(actor){
 | 
			
		||||
    return width;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function Calendar() {
 | 
			
		||||
function _getCalendarDayAbbreviation(dayNumber) {
 | 
			
		||||
    let abbreviations = [
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Sunday.
 | 
			
		||||
         *
 | 
			
		||||
         * NOTE: These abbreviations are always shown together and in
 | 
			
		||||
         * order, e.g. "S M T W T F S".
 | 
			
		||||
         */
 | 
			
		||||
        _("S"),
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Monday */
 | 
			
		||||
        _("M"),
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Tuesday */
 | 
			
		||||
        _("T"),
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Wednesday */
 | 
			
		||||
        _("W"),
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Thursday */
 | 
			
		||||
        _("T"),
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Friday */
 | 
			
		||||
        _("F"),
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Saturday */
 | 
			
		||||
        _("S")
 | 
			
		||||
    ];
 | 
			
		||||
    return abbreviations[dayNumber];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _getEventDayAbbreviation(dayNumber) {
 | 
			
		||||
    let abbreviations = [
 | 
			
		||||
        /* Translators: Event list abbreviation for Sunday.
 | 
			
		||||
         *
 | 
			
		||||
         * NOTE: These abbreviations are normally not shown together
 | 
			
		||||
         * so they need to be unique (e.g. Tuesday and Thursday cannot
 | 
			
		||||
         * both be 'T').
 | 
			
		||||
         */
 | 
			
		||||
        _("Su"),
 | 
			
		||||
        /* Translators: Event list abbreviation for Monday */
 | 
			
		||||
        _("M"),
 | 
			
		||||
        /* Translators: Event list abbreviation for Tuesday */
 | 
			
		||||
        _("T"),
 | 
			
		||||
        /* Translators: Event list abbreviation for Wednesday */
 | 
			
		||||
        _("W"),
 | 
			
		||||
        /* Translators: Event list abbreviation for Thursday */
 | 
			
		||||
        _("Th"),
 | 
			
		||||
        /* Translators: Event list abbreviation for Friday */
 | 
			
		||||
        _("F"),
 | 
			
		||||
        /* Translators: Event list abbreviation for Saturday */
 | 
			
		||||
        _("S")
 | 
			
		||||
    ];
 | 
			
		||||
    return abbreviations[dayNumber];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Abstraction for an appointment/event in a calendar
 | 
			
		||||
 | 
			
		||||
function CalendarEvent(date, summary, allDay) {
 | 
			
		||||
    this._init(date, summary, allDay);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CalendarEvent.prototype = {
 | 
			
		||||
    _init: function(date, summary, allDay) {
 | 
			
		||||
        this.date = date;
 | 
			
		||||
        this.summary = summary;
 | 
			
		||||
        this.allDay = allDay;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Interface for appointments/events - e.g. the contents of a calendar
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// First, an implementation with no events
 | 
			
		||||
function EmptyEventSource() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Calendar.prototype = {
 | 
			
		||||
EmptyEventSource.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    requestRange: function(begin, end) {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getEvents: function(begin, end) {
 | 
			
		||||
        let result = [];
 | 
			
		||||
        return result;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasEvents: function(day) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(EmptyEventSource.prototype);
 | 
			
		||||
 | 
			
		||||
// Second, wrap native Evolution event source
 | 
			
		||||
function EvolutionEventSource() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EvolutionEventSource.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._native = new Shell.EvolutionEventSource();
 | 
			
		||||
        this._native.connect('changed', Lang.bind(this, function() {
 | 
			
		||||
            this.emit('changed');
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    requestRange: function(begin, end) {
 | 
			
		||||
        this._native.request_range(begin.getTime(), end.getTime());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getEvents: function(begin, end) {
 | 
			
		||||
        let result = [];
 | 
			
		||||
        let nativeEvents = this._native.get_events(begin.getTime(), end.getTime());
 | 
			
		||||
        for (let n = 0; n < nativeEvents.length; n++) {
 | 
			
		||||
            let nativeEvent = nativeEvents[n];
 | 
			
		||||
            result.push(new CalendarEvent(new Date(nativeEvent.msec_begin), nativeEvent.summary, nativeEvent.all_day));
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasEvents: function(day) {
 | 
			
		||||
        let dayBegin = _getBeginningOfDay(day);
 | 
			
		||||
        let dayEnd = _getEndOfDay(day);
 | 
			
		||||
 | 
			
		||||
        let events = this.getEvents(dayBegin, dayEnd);
 | 
			
		||||
 | 
			
		||||
        if (events.length == 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(EvolutionEventSource.prototype);
 | 
			
		||||
 | 
			
		||||
// Finally, an implementation with fake events
 | 
			
		||||
function FakeEventSource() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FakeEventSource.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
 | 
			
		||||
        this._fakeEvents = [];
 | 
			
		||||
 | 
			
		||||
        // Generate fake events
 | 
			
		||||
        //
 | 
			
		||||
        let midnightToday = _getBeginningOfDay(new Date());
 | 
			
		||||
        let summary = '';
 | 
			
		||||
 | 
			
		||||
        // '10-oclock pow-wow' is an event occuring IN THE PAST every four days at 10am
 | 
			
		||||
        for (let n = 0; n < 10; n++) {
 | 
			
		||||
            let t = new Date(midnightToday.getTime() - n * 4 * 86400 * 1000);
 | 
			
		||||
            t.setHours(10);
 | 
			
		||||
            summary = '10-oclock pow-wow (n=' + n + ')';
 | 
			
		||||
            this._fakeEvents.push(new CalendarEvent(t, summary, false));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // '11-oclock thing' is an event occuring every three days at 11am
 | 
			
		||||
        for (let n = 0; n < 10; n++) {
 | 
			
		||||
            let t = new Date(midnightToday.getTime() + n * 3 * 86400 * 1000);
 | 
			
		||||
            t.setHours(11);
 | 
			
		||||
            summary = '11-oclock thing (n=' + n + ')';
 | 
			
		||||
            this._fakeEvents.push(new CalendarEvent(t, summary, false));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 'Weekly Meeting' is an event occuring every seven days at 1:45pm (two days displaced)
 | 
			
		||||
        for (let n = 0; n < 5; n++) {
 | 
			
		||||
            let t = new Date(midnightToday.getTime() + (n * 7 + 2) * 86400 * 1000);
 | 
			
		||||
            t.setHours(13);
 | 
			
		||||
            t.setMinutes(45);
 | 
			
		||||
            summary = 'Weekly Meeting (n=' + n + ')';
 | 
			
		||||
            this._fakeEvents.push(new CalendarEvent(t, summary, false));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 'Fun All Day' is an all-day event occuring every fortnight (three days displayed)
 | 
			
		||||
        for (let n = 0; n < 10; n++) {
 | 
			
		||||
            let t = new Date(midnightToday.getTime() + (n * 14 + 3) * 86400 * 1000);
 | 
			
		||||
            summary = 'Fun All Day (n=' + n + ')';
 | 
			
		||||
            this._fakeEvents.push(new CalendarEvent(t, summary, true));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 'Get Married' is an event that actually reflects reality (Dec 4, 2010) :-)
 | 
			
		||||
        this._fakeEvents.push(new CalendarEvent(new Date(2010, 11, 4, 16, 0), 'Get Married', false));
 | 
			
		||||
 | 
			
		||||
        // ditto for 'NE Patriots vs NY Jets'
 | 
			
		||||
        this._fakeEvents.push(new CalendarEvent(new Date(2010, 11, 6, 20, 30), 'NE Patriots vs NY Jets', false));
 | 
			
		||||
 | 
			
		||||
        // An event for tomorrow @6:30pm that is added/removed every five
 | 
			
		||||
        // seconds (to check that the ::changed signal works)
 | 
			
		||||
        let transientEventDate = new Date(midnightToday.getTime() + 86400 * 1000);
 | 
			
		||||
        transientEventDate.setHours(18);
 | 
			
		||||
        transientEventDate.setMinutes(30);
 | 
			
		||||
        transientEventDate.setSeconds(0);
 | 
			
		||||
        Mainloop.timeout_add(5000, Lang.bind(this, this._updateTransientEvent));
 | 
			
		||||
        this._includeTransientEvent = false;
 | 
			
		||||
        this._transientEvent = new CalendarEvent(transientEventDate, 'A Transient Event', false);
 | 
			
		||||
        this._transientEventCounter = 1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateTransientEvent: function() {
 | 
			
		||||
        this._includeTransientEvent = !this._includeTransientEvent;
 | 
			
		||||
        this._transientEventCounter = this._transientEventCounter + 1;
 | 
			
		||||
        this._transientEvent.summary = 'A Transient Event (' + this._transientEventCounter + ')';
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
        Mainloop.timeout_add(5000, Lang.bind(this, this._updateTransientEvent));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    requestRange: function(begin, end) {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getEvents: function(begin, end) {
 | 
			
		||||
        let result = [];
 | 
			
		||||
        //log('begin:' + begin);
 | 
			
		||||
        //log('end:  ' + end);
 | 
			
		||||
        for(let n = 0; n < this._fakeEvents.length; n++) {
 | 
			
		||||
            let event = this._fakeEvents[n];
 | 
			
		||||
            if (event.date >= begin && event.date <= end) {
 | 
			
		||||
                result.push(event);
 | 
			
		||||
            }
 | 
			
		||||
            //log('when:' + event.date + ' summary:' + event.summary);
 | 
			
		||||
        }
 | 
			
		||||
        if (this._includeTransientEvent && this._transientEvent.date >= begin && this._transientEvent.date <= end)
 | 
			
		||||
            result.push(this._transientEvent);
 | 
			
		||||
        result.sort(function(event1, event2) {
 | 
			
		||||
            return event1.date.getTime() - event2.date.getTime();
 | 
			
		||||
        });
 | 
			
		||||
        return result;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasEvents: function(day) {
 | 
			
		||||
        let dayBegin = _getBeginningOfDay(day);
 | 
			
		||||
        let dayEnd = _getEndOfDay(day);
 | 
			
		||||
 | 
			
		||||
        let events = this.getEvents(dayBegin, dayEnd);
 | 
			
		||||
 | 
			
		||||
        if (events.length == 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(FakeEventSource.prototype);
 | 
			
		||||
 | 
			
		||||
// Calendar:
 | 
			
		||||
// @eventSource: is an object implementing the EventSource API, e.g. the
 | 
			
		||||
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
 | 
			
		||||
function Calendar(eventSource) {
 | 
			
		||||
    this._init(eventSource);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Calendar.prototype = {
 | 
			
		||||
    _init: function(eventSource) {
 | 
			
		||||
        this._eventSource = eventSource;
 | 
			
		||||
 | 
			
		||||
        this._eventSource.connect('changed', Lang.bind(this, this._update));
 | 
			
		||||
 | 
			
		||||
        // FIXME: This is actually the fallback method for GTK+ for the week start;
 | 
			
		||||
        // GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
 | 
			
		||||
        // should add a C function so we can do the full handling.
 | 
			
		||||
@@ -71,6 +377,7 @@ Calendar.prototype = {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Find the ordering for month/year in the calendar heading
 | 
			
		||||
        this._headerFormatWithoutYear = '%B';
 | 
			
		||||
        switch (Gettext_gtk30.gettext('calendar:MY')) {
 | 
			
		||||
        case 'calendar:MY':
 | 
			
		||||
            this._headerFormat = '%B %Y';
 | 
			
		||||
@@ -85,7 +392,7 @@ Calendar.prototype = {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Start off with the current date
 | 
			
		||||
        this.date = new Date();
 | 
			
		||||
        this._selectedDate = new Date();
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Table({ homogeneous: false,
 | 
			
		||||
                                    style_class: 'calendar',
 | 
			
		||||
@@ -100,9 +407,10 @@ Calendar.prototype = {
 | 
			
		||||
 | 
			
		||||
    // Sets the calendar to show a specific date
 | 
			
		||||
    setDate: function(date) {
 | 
			
		||||
        if (!_sameDay(date, this.date)) {
 | 
			
		||||
            this.date = date;
 | 
			
		||||
        if (!_sameDay(date, this._selectedDate)) {
 | 
			
		||||
            this._selectedDate = date;
 | 
			
		||||
            this._update();
 | 
			
		||||
            this.emit('selected-date-changed', new Date(this._selectedDate));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -116,45 +424,36 @@ Calendar.prototype = {
 | 
			
		||||
                       { row: 0, col: 0, col_span: offsetCols + 7 });
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange));
 | 
			
		||||
        let [backlabel, forwardlabel] = ['<', '>'];
 | 
			
		||||
        if (St.Widget.get_default_direction () == St.TextDirection.RTL) {
 | 
			
		||||
            [backlabel, forwardlabel] = [forwardlabel, backlabel];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let back = new St.Button({ label: backlabel, style_class: 'calendar-change-month'  });
 | 
			
		||||
        let back = new St.Button({ style_class: 'calendar-change-month-back' });
 | 
			
		||||
        this._topBox.add(back);
 | 
			
		||||
        back.connect('clicked', Lang.bind(this, this._prevMonth));
 | 
			
		||||
        back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
 | 
			
		||||
 | 
			
		||||
        this._dateLabel = new St.Label();
 | 
			
		||||
        this._topBox.add(this._dateLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
 | 
			
		||||
        this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
 | 
			
		||||
        this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        let forward = new St.Button({ label: forwardlabel, style_class: 'calendar-change-month' });
 | 
			
		||||
        let forward = new St.Button({ style_class: 'calendar-change-month-forward' });
 | 
			
		||||
        this._topBox.add(forward);
 | 
			
		||||
        forward.connect('clicked', Lang.bind(this, this._nextMonth));
 | 
			
		||||
        forward.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
 | 
			
		||||
 | 
			
		||||
        // Add weekday labels...
 | 
			
		||||
        //
 | 
			
		||||
        // We need to figure out the abbreviated localized names for the days of the week;
 | 
			
		||||
        // we do this by just getting the next 7 days starting from right now and then putting
 | 
			
		||||
        // them in the right cell in the table. It doesn't matter if we add them in order
 | 
			
		||||
        let iter = new Date(this.date);
 | 
			
		||||
        let iter = new Date(this._selectedDate);
 | 
			
		||||
        iter.setSeconds(0); // Leap second protection. Hah!
 | 
			
		||||
        iter.setHours(12);
 | 
			
		||||
 | 
			
		||||
        if (this._useWeekdate) {
 | 
			
		||||
            this._weekdateHeader = new St.Label();
 | 
			
		||||
            this.actor.add(this._weekdateHeader,
 | 
			
		||||
                              { row: 1,
 | 
			
		||||
                                col: 0,
 | 
			
		||||
                                x_fill: false, x_align: St.Align.MIDDLE });
 | 
			
		||||
            this._setWeekdateHeaderWidth();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._weekdateHeader = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < 7; i++) {
 | 
			
		||||
            this.actor.add(new St.Label({ text: iter.toLocaleFormat('%a') }),
 | 
			
		||||
            // Could use iter.toLocaleFormat('%a') but that normally gives three characters
 | 
			
		||||
            // and we want, ideally, a single character for e.g. S M T W T F S
 | 
			
		||||
            let customDayAbbrev = _getCalendarDayAbbreviation(iter.getDay());
 | 
			
		||||
            let label = new St.Label({ style_class: 'calendar-day-base calendar-day-heading',
 | 
			
		||||
                                       text: customDayAbbrev });
 | 
			
		||||
            this.actor.add(label,
 | 
			
		||||
                           { row: 1,
 | 
			
		||||
                             col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
 | 
			
		||||
                             x_fill: false, x_align: St.Align.END });
 | 
			
		||||
                             x_fill: false, x_align: St.Align.MIDDLE });
 | 
			
		||||
            iter.setTime(iter.getTime() + MSECS_IN_DAY);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -178,33 +477,35 @@ Calendar.prototype = {
 | 
			
		||||
        switch (event.get_scroll_direction()) {
 | 
			
		||||
        case Clutter.ScrollDirection.UP:
 | 
			
		||||
        case Clutter.ScrollDirection.LEFT:
 | 
			
		||||
            this._prevMonth();
 | 
			
		||||
            this._onPrevMonthButtonClicked();
 | 
			
		||||
            break;
 | 
			
		||||
        case Clutter.ScrollDirection.DOWN:
 | 
			
		||||
        case Clutter.ScrollDirection.RIGHT:
 | 
			
		||||
            this._nextMonth();
 | 
			
		||||
            this._onNextMonthButtonClicked();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _prevMonth: function() {
 | 
			
		||||
        if (this.date.getMonth() == 0) {
 | 
			
		||||
            this.date.setMonth(11);
 | 
			
		||||
            this.date.setFullYear(this.date.getFullYear() - 1);
 | 
			
		||||
    _onPrevMonthButtonClicked: function() {
 | 
			
		||||
        let newDate = new Date(this._selectedDate);
 | 
			
		||||
        if (newDate.getMonth() == 0) {
 | 
			
		||||
            newDate.setMonth(11);
 | 
			
		||||
            newDate.setFullYear(newDate.getFullYear() - 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.date.setMonth(this.date.getMonth() - 1);
 | 
			
		||||
            newDate.setMonth(newDate.getMonth() - 1);
 | 
			
		||||
        }
 | 
			
		||||
        this._update();
 | 
			
		||||
        this.setDate(newDate);
 | 
			
		||||
   },
 | 
			
		||||
 | 
			
		||||
    _nextMonth: function() {
 | 
			
		||||
        if (this.date.getMonth() == 11) {
 | 
			
		||||
            this.date.setMonth(0);
 | 
			
		||||
            this.date.setFullYear(this.date.getFullYear() + 1);
 | 
			
		||||
    _onNextMonthButtonClicked: function() {
 | 
			
		||||
        let newDate = new Date(this._selectedDate);
 | 
			
		||||
        if (newDate.getMonth() == 11) {
 | 
			
		||||
            newDate.setMonth(0);
 | 
			
		||||
            newDate.setFullYear(newDate.getFullYear() + 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.date.setMonth(this.date.getMonth() + 1);
 | 
			
		||||
            newDate.setMonth(newDate.getMonth() + 1);
 | 
			
		||||
        }
 | 
			
		||||
        this._update();
 | 
			
		||||
        this.setDate(newDate);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSettingsChange: function() {
 | 
			
		||||
@@ -214,7 +515,12 @@ Calendar.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _update: function() {
 | 
			
		||||
        this._dateLabel.text = this.date.toLocaleFormat(this._headerFormat);
 | 
			
		||||
        let now = new Date();
 | 
			
		||||
 | 
			
		||||
        if (_sameYear(this._selectedDate, now))
 | 
			
		||||
            this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
 | 
			
		||||
        else
 | 
			
		||||
            this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
 | 
			
		||||
 | 
			
		||||
        // Remove everything but the topBox and the weekday labels
 | 
			
		||||
        let children = this.actor.get_children();
 | 
			
		||||
@@ -222,45 +528,201 @@ Calendar.prototype = {
 | 
			
		||||
            children[i].destroy();
 | 
			
		||||
 | 
			
		||||
        // Start at the beginning of the week before the start of the month
 | 
			
		||||
        let iter = new Date(this.date);
 | 
			
		||||
        iter.setDate(1);
 | 
			
		||||
        iter.setSeconds(0);
 | 
			
		||||
        iter.setHours(12);
 | 
			
		||||
        let daysToWeekStart = (7 + iter.getDay() - this._weekStart) % 7;
 | 
			
		||||
        iter.setTime(iter.getTime() - daysToWeekStart * MSECS_IN_DAY);
 | 
			
		||||
 | 
			
		||||
        let now = new Date();
 | 
			
		||||
        let beginDate = new Date(this._selectedDate);
 | 
			
		||||
        beginDate.setDate(1);
 | 
			
		||||
        beginDate.setSeconds(0);
 | 
			
		||||
        beginDate.setHours(12);
 | 
			
		||||
        let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
 | 
			
		||||
        beginDate.setTime(beginDate.getTime() - daysToWeekStart * MSECS_IN_DAY);
 | 
			
		||||
 | 
			
		||||
        let iter = new Date(beginDate);
 | 
			
		||||
        let row = 2;
 | 
			
		||||
        while (true) {
 | 
			
		||||
            let label = new St.Label({ text: iter.getDate().toString() });
 | 
			
		||||
            if (_sameDay(now, iter))
 | 
			
		||||
                label.style_class = 'calendar-day calendar-today';
 | 
			
		||||
            else if (iter.getMonth() != this.date.getMonth())
 | 
			
		||||
                label.style_class = 'calendar-day calendar-other-month-day';
 | 
			
		||||
            let button = new St.Button({ label: iter.getDate().toString() });
 | 
			
		||||
 | 
			
		||||
            let iterStr = iter.toUTCString();
 | 
			
		||||
            button.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
                let newlySelectedDate = new Date(iterStr);
 | 
			
		||||
                this.setDate(newlySelectedDate);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            let hasEvents = this._eventSource.hasEvents(iter);
 | 
			
		||||
            let styleClass = 'calendar-day-base calendar-day';
 | 
			
		||||
            if (_isWorkDay(iter))
 | 
			
		||||
                styleClass += ' calendar-work-day'
 | 
			
		||||
            else
 | 
			
		||||
                label.style_class = 'calendar-day';
 | 
			
		||||
                styleClass += ' calendar-nonwork-day'
 | 
			
		||||
 | 
			
		||||
            // Hack used in lieu of border-collapse - see gnome-shell.css
 | 
			
		||||
            if (row == 2)
 | 
			
		||||
                styleClass = 'calendar-day-top ' + styleClass;
 | 
			
		||||
            if (iter.getDay() == 0)
 | 
			
		||||
                styleClass = 'calendar-day-left ' + styleClass;
 | 
			
		||||
 | 
			
		||||
            if (_sameDay(now, iter))
 | 
			
		||||
                styleClass += ' calendar-today';
 | 
			
		||||
            else if (iter.getMonth() != this._selectedDate.getMonth())
 | 
			
		||||
                styleClass += ' calendar-other-month-day';
 | 
			
		||||
 | 
			
		||||
            if (_sameDay(this._selectedDate, iter))
 | 
			
		||||
                button.add_style_pseudo_class('active');
 | 
			
		||||
 | 
			
		||||
            if (hasEvents)
 | 
			
		||||
                styleClass += ' calendar-day-with-events'
 | 
			
		||||
 | 
			
		||||
            button.style_class = styleClass;
 | 
			
		||||
 | 
			
		||||
            let offsetCols = this._useWeekdate ? 1 : 0;
 | 
			
		||||
            this.actor.add(label,
 | 
			
		||||
                           { row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
 | 
			
		||||
                             x_fill: false, x_align: St.Align.END });
 | 
			
		||||
            this.actor.add(button,
 | 
			
		||||
                           { row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
 | 
			
		||||
 | 
			
		||||
            if (this._useWeekdate && iter.getDay() == 4) {
 | 
			
		||||
                let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
 | 
			
		||||
                                           style_class: 'calendar-day calendar-calendarweek'});
 | 
			
		||||
                                           style_class: 'calendar-day-base calendar-week-number'});
 | 
			
		||||
                this.actor.add(label,
 | 
			
		||||
                              { row: row, col: 0,
 | 
			
		||||
		                        x_fill: false, x_align: St.Align.MIDDLE });
 | 
			
		||||
                               { row: row, col: 0, y_align: St.Align.MIDDLE });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            iter.setTime(iter.getTime() + MSECS_IN_DAY);
 | 
			
		||||
            if (iter.getDay() == this._weekStart) {
 | 
			
		||||
                // We stop on the first "first day of the week" after the month we are displaying
 | 
			
		||||
                if (iter.getMonth() > this.date.getMonth() || iter.getYear() > this.date.getYear())
 | 
			
		||||
                if (iter.getMonth() > this._selectedDate.getMonth() || iter.getYear() > this._selectedDate.getYear())
 | 
			
		||||
                    break;
 | 
			
		||||
                row++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // Signal to the event source that we are interested in events
 | 
			
		||||
        // only from this date range
 | 
			
		||||
        this._eventSource.requestRange(beginDate, iter);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(Calendar.prototype);
 | 
			
		||||
 | 
			
		||||
function EventsList(eventSource) {
 | 
			
		||||
    this._init(eventSource);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EventsList.prototype = {
 | 
			
		||||
    _init: function(eventSource) {
 | 
			
		||||
        this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
 | 
			
		||||
        this._date = new Date();
 | 
			
		||||
        this._eventSource = eventSource;
 | 
			
		||||
        this._eventSource.connect('changed', Lang.bind(this, this._update));
 | 
			
		||||
        this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
 | 
			
		||||
        this._desktopSettings.connect('changed', Lang.bind(this, this._update));
 | 
			
		||||
        this._update();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
 | 
			
		||||
        if (includeDayName) {
 | 
			
		||||
            dayNameBox.add(new St.Label( { style_class: 'events-day-dayname',
 | 
			
		||||
                                           text: day } ),
 | 
			
		||||
                           { x_fill: true } );
 | 
			
		||||
        }
 | 
			
		||||
        timeBox.add(new St.Label( { style_class: 'events-day-time',
 | 
			
		||||
                                    text: time} ),
 | 
			
		||||
                    { x_fill: true } );
 | 
			
		||||
        eventTitleBox.add(new St.Label( { style_class: 'events-day-task',
 | 
			
		||||
                                          text: desc} ));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
 | 
			
		||||
        let events = this._eventSource.getEvents(begin, end);
 | 
			
		||||
 | 
			
		||||
        let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
 | 
			
		||||
 | 
			
		||||
        if (events.length == 0 && !showNothingScheduled)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let vbox = new St.BoxLayout( {vertical: true} );
 | 
			
		||||
        this.actor.add(vbox);
 | 
			
		||||
 | 
			
		||||
        vbox.add(new St.Label({ style_class: 'events-day-header', text: header }));
 | 
			
		||||
        let box = new St.BoxLayout({style_class: 'events-header-hbox'});
 | 
			
		||||
        let dayNameBox = new St.BoxLayout({ vertical: true, style_class: 'events-day-name-box' });
 | 
			
		||||
        let timeBox = new St.BoxLayout({ vertical: true, style_class: 'events-time-box' });
 | 
			
		||||
        let eventTitleBox = new St.BoxLayout({ vertical: true, style_class: 'events-event-box' });
 | 
			
		||||
        box.add(dayNameBox, {x_fill: false});
 | 
			
		||||
        box.add(timeBox, {x_fill: false});
 | 
			
		||||
        box.add(eventTitleBox, {expand: true});
 | 
			
		||||
        vbox.add(box);
 | 
			
		||||
 | 
			
		||||
        for (let n = 0; n < events.length; n++) {
 | 
			
		||||
            let event = events[n];
 | 
			
		||||
            let dayString = _getEventDayAbbreviation(event.date.getDay());
 | 
			
		||||
            let timeString = _formatEventTime(event, clockFormat);
 | 
			
		||||
            let summaryString = event.summary;
 | 
			
		||||
            this._addEvent(dayNameBox, timeBox, eventTitleBox, includeDayName, dayString, timeString, summaryString);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (events.length == 0 && showNothingScheduled) {
 | 
			
		||||
            let now = new Date();
 | 
			
		||||
            /* Translators: Text to show if there are no events */
 | 
			
		||||
            let nothingEvent = new CalendarEvent(now, _("Nothing Scheduled"), true);
 | 
			
		||||
            let timeString = _formatEventTime(nothingEvent, clockFormat);
 | 
			
		||||
            this._addEvent(dayNameBox, timeBox, eventTitleBox, false, "", timeString, nothingEvent.summary);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showOtherDay: function(day) {
 | 
			
		||||
        this.actor.destroy_children();
 | 
			
		||||
 | 
			
		||||
        let dayBegin = _getBeginningOfDay(day);
 | 
			
		||||
        let dayEnd = _getEndOfDay(day);
 | 
			
		||||
 | 
			
		||||
        let dayString;
 | 
			
		||||
        let now = new Date();
 | 
			
		||||
        if (_sameYear(day, now))
 | 
			
		||||
            dayString = day.toLocaleFormat('%A, %B %d');
 | 
			
		||||
        else
 | 
			
		||||
            dayString = day.toLocaleFormat('%A, %B %d, %Y');
 | 
			
		||||
        this._addPeriod(dayString, dayBegin, dayEnd, false, true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showToday: function() {
 | 
			
		||||
        this.actor.destroy_children();
 | 
			
		||||
 | 
			
		||||
        let now = new Date();
 | 
			
		||||
        let dayBegin = _getBeginningOfDay(now);
 | 
			
		||||
        let dayEnd = _getEndOfDay(now);
 | 
			
		||||
        this._addPeriod(_("Today"), dayBegin, dayEnd, false, true);
 | 
			
		||||
 | 
			
		||||
        let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
 | 
			
		||||
        let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
 | 
			
		||||
        this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
 | 
			
		||||
 | 
			
		||||
        if (dayEnd.getDay() <= 4) {
 | 
			
		||||
            /* if now is Sunday through Thursday show "This week" and include events up until
 | 
			
		||||
             * and including Saturday
 | 
			
		||||
             */
 | 
			
		||||
            let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
 | 
			
		||||
            let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayEnd.getDay()) * 86400 * 1000);
 | 
			
		||||
            this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
 | 
			
		||||
        } else {
 | 
			
		||||
            /* otherwise it's a Friday or Saturday... show "Next week" and include events up
 | 
			
		||||
             * until and including *next* Saturday
 | 
			
		||||
             */
 | 
			
		||||
            let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
 | 
			
		||||
            let nextWeekEnd = new Date(dayEnd.getTime() + (13 - dayEnd.getDay()) * 86400 * 1000);
 | 
			
		||||
            this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Sets the event list to show events from a specific date
 | 
			
		||||
    setDate: function(date) {
 | 
			
		||||
        if (!_sameDay(date, this._date)) {
 | 
			
		||||
            this._date = date;
 | 
			
		||||
            this._update();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _update: function() {
 | 
			
		||||
        let today = new Date();
 | 
			
		||||
        if (_sameDay (this._date, today)) {
 | 
			
		||||
            this._showToday();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._showOtherDay(this._date);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										212
									
								
								js/ui/dateMenu.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								js/ui/dateMenu.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,212 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Cairo = imports.cairo;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const Calendar = imports.ui.calendar;
 | 
			
		||||
 | 
			
		||||
// in org.gnome.desktop.interface
 | 
			
		||||
const CLOCK_FORMAT_KEY        = 'clock-format';
 | 
			
		||||
 | 
			
		||||
// in org.gnome.shell.clock
 | 
			
		||||
const CLOCK_SHOW_DATE_KEY     = 'show-date';
 | 
			
		||||
const CLOCK_SHOW_SECONDS_KEY  = 'show-seconds';
 | 
			
		||||
 | 
			
		||||
function _onVertSepRepaint (area)
 | 
			
		||||
{
 | 
			
		||||
    let cr = area.get_context();
 | 
			
		||||
    let themeNode = area.get_theme_node();
 | 
			
		||||
    let [width, height] = area.get_surface_size();
 | 
			
		||||
    let stippleColor = new Clutter.Color();
 | 
			
		||||
    let stippleWidth = themeNode.get_length('-stipple-width');
 | 
			
		||||
    let x = Math.floor(width/2) + 0.5;
 | 
			
		||||
    themeNode.lookup_color('-stipple-color', false, stippleColor);
 | 
			
		||||
    cr.moveTo(x, 0);
 | 
			
		||||
    cr.lineTo(x, height);
 | 
			
		||||
    Clutter.cairo_set_source_color(cr, stippleColor);
 | 
			
		||||
    cr.setDash([1, 3], 1); // Hard-code for now
 | 
			
		||||
    cr.setLineWidth(stippleWidth);
 | 
			
		||||
    cr.stroke();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function DateMenuButton() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DateMenuButton.prototype = {
 | 
			
		||||
    __proto__: PanelMenu.Button.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        let item;
 | 
			
		||||
        let hbox;
 | 
			
		||||
        let vbox;
 | 
			
		||||
 | 
			
		||||
        //this._eventSource = new Calendar.EmptyEventSource();
 | 
			
		||||
        //this._eventSource = new Calendar.FakeEventSource();
 | 
			
		||||
        this._eventSource = new Calendar.EvolutionEventSource();
 | 
			
		||||
 | 
			
		||||
        PanelMenu.Button.prototype._init.call(this, St.Align.START);
 | 
			
		||||
 | 
			
		||||
        this._clock = new St.Label();
 | 
			
		||||
        this.actor.set_child(this._clock);
 | 
			
		||||
 | 
			
		||||
        hbox = new St.BoxLayout({name: 'calendarArea'});
 | 
			
		||||
        this.menu.addActor(hbox);
 | 
			
		||||
 | 
			
		||||
        // Fill up the first column
 | 
			
		||||
 | 
			
		||||
        vbox = new St.BoxLayout({vertical: true});
 | 
			
		||||
        hbox.add(vbox);
 | 
			
		||||
 | 
			
		||||
        // Date
 | 
			
		||||
        this._date = new St.Label();
 | 
			
		||||
        this._date.style_class = 'datemenu-date-label';
 | 
			
		||||
        vbox.add(this._date);
 | 
			
		||||
 | 
			
		||||
        this._eventList = new Calendar.EventsList(this._eventSource);
 | 
			
		||||
 | 
			
		||||
        // Calendar
 | 
			
		||||
        this._calendar = new Calendar.Calendar(this._eventSource);
 | 
			
		||||
        this._calendar.connect('selected-date-changed',
 | 
			
		||||
                               Lang.bind(this, function(calendar, date) {
 | 
			
		||||
                                   this._eventList.setDate(date);
 | 
			
		||||
                               }));
 | 
			
		||||
        vbox.add(this._calendar.actor);
 | 
			
		||||
 | 
			
		||||
        item = new PopupMenu.PopupSeparatorMenuItem();
 | 
			
		||||
        item.setColumnWidths(1);
 | 
			
		||||
        vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
 | 
			
		||||
        item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
 | 
			
		||||
        item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
 | 
			
		||||
        vbox.add(item.actor);
 | 
			
		||||
 | 
			
		||||
        // Add vertical separator
 | 
			
		||||
 | 
			
		||||
        item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
 | 
			
		||||
                                    pseudo_class: 'highlighted' });
 | 
			
		||||
        item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
 | 
			
		||||
        hbox.add(item);
 | 
			
		||||
 | 
			
		||||
        // Fill up the second column
 | 
			
		||||
        //
 | 
			
		||||
        vbox = new St.BoxLayout({vertical: true});
 | 
			
		||||
        hbox.add(vbox);
 | 
			
		||||
 | 
			
		||||
        // Event list
 | 
			
		||||
        vbox.add(this._eventList.actor);
 | 
			
		||||
 | 
			
		||||
        item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
 | 
			
		||||
        item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
 | 
			
		||||
        vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
 | 
			
		||||
 | 
			
		||||
        // Whenever the menu is opened, select today
 | 
			
		||||
        this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
 | 
			
		||||
            if (isOpen) {
 | 
			
		||||
                let now = new Date();
 | 
			
		||||
                this._calendar.setDate(now);
 | 
			
		||||
                // No need to update this._eventList as ::selected-date-changed
 | 
			
		||||
                // signal will fire
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        // Done with hbox for calendar and event list
 | 
			
		||||
 | 
			
		||||
        // Track changes to clock settings
 | 
			
		||||
        this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
 | 
			
		||||
        this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
 | 
			
		||||
        this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
 | 
			
		||||
        this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
 | 
			
		||||
 | 
			
		||||
        // Start the clock
 | 
			
		||||
        this._updateClockAndDate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateClockAndDate: function() {
 | 
			
		||||
        let format = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
 | 
			
		||||
        let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY);
 | 
			
		||||
        let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY);
 | 
			
		||||
 | 
			
		||||
        let clockFormat;
 | 
			
		||||
        let dateFormat;
 | 
			
		||||
 | 
			
		||||
        switch (format) {
 | 
			
		||||
            case '24h':
 | 
			
		||||
                if (showDate)
 | 
			
		||||
	            /* Translators: This is the time format with date used
 | 
			
		||||
                       in 24-hour mode. */
 | 
			
		||||
                    clockFormat = showSeconds ? _("%a %b %e, %R:%S")
 | 
			
		||||
                                              : _("%a %b %e, %R");
 | 
			
		||||
                else
 | 
			
		||||
	            /* Translators: This is the time format without date used
 | 
			
		||||
                       in 24-hour mode. */
 | 
			
		||||
                    clockFormat = showSeconds ? _("%a %R:%S")
 | 
			
		||||
                                              : _("%a %R");
 | 
			
		||||
                break;
 | 
			
		||||
            case '12h':
 | 
			
		||||
            default:
 | 
			
		||||
                if (showDate)
 | 
			
		||||
	            /* Translators: This is a time format with date used
 | 
			
		||||
                       for AM/PM. */
 | 
			
		||||
                    clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
 | 
			
		||||
                                              : _("%a %b %e, %l:%M %p");
 | 
			
		||||
                else
 | 
			
		||||
	            /* Translators: This is a time format without date used
 | 
			
		||||
                       for AM/PM. */
 | 
			
		||||
                    clockFormat = showSeconds ? _("%a %l:%M:%S %p")
 | 
			
		||||
                                              : _("%a %l:%M %p");
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let displayDate = new Date();
 | 
			
		||||
        let msecRemaining;
 | 
			
		||||
        if (showSeconds) {
 | 
			
		||||
            msecRemaining = 1000 - displayDate.getMilliseconds();
 | 
			
		||||
            if (msecRemaining < 50) {
 | 
			
		||||
                displayDate.setSeconds(displayDate.getSeconds() + 1);
 | 
			
		||||
                msecRemaining += 1000;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            msecRemaining = 60000 - (1000 * displayDate.getSeconds() +
 | 
			
		||||
                                     displayDate.getMilliseconds());
 | 
			
		||||
            if (msecRemaining < 500) {
 | 
			
		||||
                displayDate.setMinutes(displayDate.getMinutes() + 1);
 | 
			
		||||
                msecRemaining += 60000;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
 | 
			
		||||
 | 
			
		||||
        /* Translators: This is the date format to use when the calendar popup is
 | 
			
		||||
         * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
 | 
			
		||||
         */
 | 
			
		||||
        dateFormat = _("%A %B %e, %Y");
 | 
			
		||||
        this._date.set_text(displayDate.toLocaleFormat(dateFormat));
 | 
			
		||||
 | 
			
		||||
        Mainloop.timeout_add(msecRemaining, Lang.bind(this, this._updateClockAndDate));
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPreferencesActivate: function() {
 | 
			
		||||
        this.menu.close();
 | 
			
		||||
        Util.spawnDesktop('gnome-datetime-panel');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOpenCalendarActivate: function() {
 | 
			
		||||
        this.menu.close();
 | 
			
		||||
        // TODO: pass '-c calendar' (to force the calendar at startup)
 | 
			
		||||
        // TODO: pass the selected day
 | 
			
		||||
        Util.spawnDesktop('evolution');
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
@@ -265,10 +265,8 @@ function _relayout() {
 | 
			
		||||
 | 
			
		||||
    // To avoid updating the position and size of the workspaces
 | 
			
		||||
    // in the overview, we just hide the overview. The positions
 | 
			
		||||
    // will be updated when it is next shown. We do the same for
 | 
			
		||||
    // the calendar popdown.
 | 
			
		||||
    // will be updated when it is next shown.
 | 
			
		||||
    overview.hide();
 | 
			
		||||
    panel.hideCalendar();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// metacity-clutter currently uses the same prefs as plain metacity,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										126
									
								
								js/ui/panel.js
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								js/ui/panel.js
									
									
									
									
									
								
							@@ -17,6 +17,7 @@ const Overview = imports.ui.overview;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const StatusMenu = imports.ui.statusMenu;
 | 
			
		||||
const DateMenu = imports.ui.dateMenu;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
@@ -496,121 +497,6 @@ AppMenuButton.prototype = {
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(AppMenuButton.prototype);
 | 
			
		||||
 | 
			
		||||
function ClockButton() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ClockButton.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.Bin({ style_class: 'panel-button',
 | 
			
		||||
                                  reactive: true,
 | 
			
		||||
                                  can_focus: true,
 | 
			
		||||
                                  x_fill: true,
 | 
			
		||||
                                  y_fill: false,
 | 
			
		||||
                                  track_hover: true });
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
        this.actor.connect('button-press-event',
 | 
			
		||||
                           Lang.bind(this, this._toggleCalendar));
 | 
			
		||||
 | 
			
		||||
        this._clock = new St.Label();
 | 
			
		||||
        this.actor.set_child(this._clock);
 | 
			
		||||
 | 
			
		||||
        this._calendarPopup = null;
 | 
			
		||||
 | 
			
		||||
        this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
 | 
			
		||||
        this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
 | 
			
		||||
 | 
			
		||||
        this._desktopSettings.connect('changed', Lang.bind(this, this._updateClock));
 | 
			
		||||
        this._clockSettings.connect('changed', Lang.bind(this, this._updateClock));
 | 
			
		||||
 | 
			
		||||
        // Start the clock
 | 
			
		||||
        this._updateClock();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    closeCalendar: function() {
 | 
			
		||||
        if (!this._calendarPopup || !this._calendarPopup.isOpen)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._calendarPopup.hide();
 | 
			
		||||
 | 
			
		||||
        this.actor.remove_style_pseudo_class('pressed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    openCalendar: function() {
 | 
			
		||||
        this._calendarPopup.show();
 | 
			
		||||
 | 
			
		||||
        this.actor.add_style_pseudo_class('pressed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _toggleCalendar: function() {
 | 
			
		||||
        if (this._calendarPopup == null) {
 | 
			
		||||
            this._calendarPopup = new CalendarPopup();
 | 
			
		||||
            this._calendarPopup.actor.hide();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._calendarPopup.isOpen)
 | 
			
		||||
            this.openCalendar();
 | 
			
		||||
        else
 | 
			
		||||
            this.closeCalendar();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateClock: function() {
 | 
			
		||||
        let format = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
 | 
			
		||||
        let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY);
 | 
			
		||||
        let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY);
 | 
			
		||||
 | 
			
		||||
        let clockFormat;
 | 
			
		||||
        switch (format) {
 | 
			
		||||
            case '24h':
 | 
			
		||||
                if (showDate)
 | 
			
		||||
	            /* Translators: This is the time format with date used
 | 
			
		||||
                       in 24-hour mode. */
 | 
			
		||||
                    clockFormat = showSeconds ? _("%a %b %e, %R:%S")
 | 
			
		||||
                                              : _("%a %b %e, %R");
 | 
			
		||||
                else
 | 
			
		||||
	            /* Translators: This is the time format without date used
 | 
			
		||||
                       in 24-hour mode. */
 | 
			
		||||
                    clockFormat = showSeconds ? _("%a %R:%S")
 | 
			
		||||
                                              : _("%a %R");
 | 
			
		||||
                break;
 | 
			
		||||
            case '12h':
 | 
			
		||||
            default:
 | 
			
		||||
                if (showDate)
 | 
			
		||||
	            /* Translators: This is a time format with date used
 | 
			
		||||
                       for AM/PM. */
 | 
			
		||||
                    clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
 | 
			
		||||
                                              : _("%a %b %e, %l:%M %p");
 | 
			
		||||
                else
 | 
			
		||||
	            /* Translators: This is a time format without date used
 | 
			
		||||
                       for AM/PM. */
 | 
			
		||||
                    clockFormat = showSeconds ? _("%a %l:%M:%S %p")
 | 
			
		||||
                                              : _("%a %l:%M %p");
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let displayDate = new Date();
 | 
			
		||||
        let msecRemaining;
 | 
			
		||||
        if (showSeconds) {
 | 
			
		||||
            msecRemaining = 1000 - displayDate.getMilliseconds();
 | 
			
		||||
            if (msecRemaining < 50) {
 | 
			
		||||
                displayDate.setSeconds(displayDate.getSeconds() + 1);
 | 
			
		||||
                msecRemaining += 1000;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            msecRemaining = 60000 - (1000 * displayDate.getSeconds() +
 | 
			
		||||
                                     displayDate.getMilliseconds());
 | 
			
		||||
            if (msecRemaining < 500) {
 | 
			
		||||
                displayDate.setMinutes(displayDate.getMinutes() + 1);
 | 
			
		||||
                msecRemaining += 60000;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
 | 
			
		||||
        Mainloop.timeout_add(msecRemaining, Lang.bind(this, this._updateClock));
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function Panel() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
@@ -807,9 +693,9 @@ Panel.prototype = {
 | 
			
		||||
        this._menus.addMenu(appMenuButton.menu);
 | 
			
		||||
 | 
			
		||||
        /* center */
 | 
			
		||||
 | 
			
		||||
        this._clockButton = new ClockButton();
 | 
			
		||||
        this._centerBox.add(this._clockButton.actor, { y_fill: true });
 | 
			
		||||
        this._dateMenu = new DateMenu.DateMenuButton();
 | 
			
		||||
        this._centerBox.add(this._dateMenu.actor, { y_fill: true });
 | 
			
		||||
        this._menus.addMenu(this._dateMenu.menu);
 | 
			
		||||
 | 
			
		||||
        /* right */
 | 
			
		||||
 | 
			
		||||
@@ -898,10 +784,6 @@ Panel.prototype = {
 | 
			
		||||
        this._rightBox.add(this._statusmenu.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hideCalendar: function() {
 | 
			
		||||
        this._clockButton.closeCalendar();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    startupAnimation: function() {
 | 
			
		||||
        this.actor.y = -this.actor.height;
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								src/Makefile-calendar-client.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/Makefile-calendar-client.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
 | 
			
		||||
noinst_LTLIBRARIES += libcalendar-client.la
 | 
			
		||||
 | 
			
		||||
libcalendar_client_la_SOURCES = 							\
 | 
			
		||||
	calendar-client/calendar-client.h	calendar-client/calendar-client.c	\
 | 
			
		||||
	calendar-client/calendar-debug.h						\
 | 
			
		||||
	calendar-client/calendar-sources.c	calendar-client/calendar-sources.h	\
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libcalendar_client_la_CFLAGS = 			\
 | 
			
		||||
	-I$(top_srcdir)/src			\
 | 
			
		||||
	-DPREFIX=\""$(prefix)"\"		\
 | 
			
		||||
	-DLIBDIR=\""$(libdir)"\"		\
 | 
			
		||||
	-DDATADIR=\""$(datadir)"\"		\
 | 
			
		||||
	-DG_DISABLE_DEPRECATED			\
 | 
			
		||||
	-DG_LOG_DOMAIN=\"CalendarClient\"	\
 | 
			
		||||
	$(LIBECAL_CFLAGS)			\
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libcalendar_client_la_LIBADD = $(LIBECAL_LIBS)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST += calendar-client/README
 | 
			
		||||
@@ -27,6 +27,7 @@ include Makefile-gdmuser.am
 | 
			
		||||
include Makefile-st.am
 | 
			
		||||
include Makefile-tray.am
 | 
			
		||||
include Makefile-gvc.am
 | 
			
		||||
include Makefile-calendar-client.am
 | 
			
		||||
 | 
			
		||||
gnome_shell_cflags =				\
 | 
			
		||||
	$(MUTTER_PLUGIN_CFLAGS)			\
 | 
			
		||||
@@ -89,6 +90,8 @@ libgnome_shell_la_SOURCES =		\
 | 
			
		||||
	shell-doc-system.c		\
 | 
			
		||||
	shell-drawing.c			\
 | 
			
		||||
	shell-embedded-window.c		\
 | 
			
		||||
	shell-evolution-event-source.h	\
 | 
			
		||||
	shell-evolution-event-source.c	\
 | 
			
		||||
	shell-generic-container.c	\
 | 
			
		||||
	shell-gtk-embed.c		\
 | 
			
		||||
	shell-global.c			\
 | 
			
		||||
@@ -212,7 +215,9 @@ libgnome_shell_la_LIBADD =	\
 | 
			
		||||
	libst-1.0.la       	\
 | 
			
		||||
	libgdmuser-1.0.la	\
 | 
			
		||||
	libtray.la		\
 | 
			
		||||
	libgvc.la
 | 
			
		||||
	libgvc.la		\
 | 
			
		||||
	libcalendar-client.la	\
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								src/calendar-client/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/calendar-client/README
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
Please keep in sync with gnome-panel.
 | 
			
		||||
							
								
								
									
										2169
									
								
								src/calendar-client/calendar-client.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2169
									
								
								src/calendar-client/calendar-client.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										149
									
								
								src/calendar-client/calendar-client.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								src/calendar-client/calendar-client.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,149 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2004 Free Software Foundation, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License as
 | 
			
		||||
 * published by the Free Software Foundation; either version 2 of the
 | 
			
		||||
 * License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *     Mark McLoughlin  <mark@skynet.ie>
 | 
			
		||||
 *     William Jon McCann  <mccann@jhu.edu>
 | 
			
		||||
 *     Martin Grimme  <martin@pycage.de>
 | 
			
		||||
 *     Christian Kellner  <gicmo@xatom.net>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __CALENDAR_CLIENT_H__
 | 
			
		||||
#define __CALENDAR_CLIENT_H__
 | 
			
		||||
 | 
			
		||||
#include <glib-object.h>
 | 
			
		||||
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
typedef enum
 | 
			
		||||
{
 | 
			
		||||
  CALENDAR_EVENT_APPOINTMENT = 1 << 0,
 | 
			
		||||
  CALENDAR_EVENT_TASK        = 1 << 1,
 | 
			
		||||
  CALENDAR_EVENT_ALL         = (1 << 2) - 1
 | 
			
		||||
} CalendarEventType;
 | 
			
		||||
 | 
			
		||||
#define CALENDAR_TYPE_CLIENT        (calendar_client_get_type ())
 | 
			
		||||
#define CALENDAR_CLIENT(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CALENDAR_TYPE_CLIENT, CalendarClient))
 | 
			
		||||
#define CALENDAR_CLIENT_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST ((k), CALENDAR_TYPE_CLIENT, CalendarClientClass))
 | 
			
		||||
#define CALENDAR_IS_CLIENT(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), CALENDAR_TYPE_CLIENT))
 | 
			
		||||
#define CALENDAR_IS_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CALENDAR_TYPE_CLIENT))
 | 
			
		||||
#define CALENDAR_CLIENT_GET_CLASS(o)(G_TYPE_INSTANCE_GET_CLASS ((o), CALENDAR_TYPE_CLIENT, CalendarClientClass))
 | 
			
		||||
 | 
			
		||||
typedef struct _CalendarClient        CalendarClient;
 | 
			
		||||
typedef struct _CalendarClientClass   CalendarClientClass;
 | 
			
		||||
typedef struct _CalendarClientPrivate CalendarClientPrivate;
 | 
			
		||||
 | 
			
		||||
struct _CalendarClient
 | 
			
		||||
{
 | 
			
		||||
  GObject                parent;
 | 
			
		||||
  CalendarClientPrivate *priv;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _CalendarClientClass
 | 
			
		||||
{
 | 
			
		||||
  GObjectClass    parent_class;
 | 
			
		||||
 | 
			
		||||
  void         (* appointments_changed) (CalendarClient *client);
 | 
			
		||||
  void         (* tasks_changed)        (CalendarClient *client);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  time_t start_time;
 | 
			
		||||
  time_t end_time;
 | 
			
		||||
} CalendarOccurrence;
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  char   *uid;
 | 
			
		||||
  char   *rid;
 | 
			
		||||
  char   *uri;
 | 
			
		||||
  char   *summary;
 | 
			
		||||
  char   *description;
 | 
			
		||||
  char   *color_string;
 | 
			
		||||
  time_t  start_time;
 | 
			
		||||
  time_t  end_time;
 | 
			
		||||
  guint   is_all_day : 1;
 | 
			
		||||
 | 
			
		||||
  /* Only used internally */
 | 
			
		||||
  GSList *occurrences;
 | 
			
		||||
} CalendarAppointment;
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  char  *uid;
 | 
			
		||||
  char  *summary;
 | 
			
		||||
  char  *description;
 | 
			
		||||
  char  *color_string;
 | 
			
		||||
  char  *url;
 | 
			
		||||
  time_t start_time;
 | 
			
		||||
  time_t due_time;
 | 
			
		||||
  guint  percent_complete;
 | 
			
		||||
  time_t completed_time;
 | 
			
		||||
  int    priority;
 | 
			
		||||
} CalendarTask;
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  union
 | 
			
		||||
  {
 | 
			
		||||
    CalendarAppointment appointment;
 | 
			
		||||
    CalendarTask        task;
 | 
			
		||||
  } event;
 | 
			
		||||
  CalendarEventType type;
 | 
			
		||||
} CalendarEvent;
 | 
			
		||||
 | 
			
		||||
#define CALENDAR_EVENT(e)       ((CalendarEvent *)(e))
 | 
			
		||||
#define CALENDAR_APPOINTMENT(e) ((CalendarAppointment *)(e))
 | 
			
		||||
#define CALENDAR_TASK(e)        ((CalendarTask *)(e))
 | 
			
		||||
 | 
			
		||||
typedef void (* CalendarDayIter) (CalendarClient *client,
 | 
			
		||||
				  guint           day,
 | 
			
		||||
				  gpointer        user_data);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GType           calendar_client_get_type                (void) G_GNUC_CONST;
 | 
			
		||||
CalendarClient *calendar_client_new                     (void);
 | 
			
		||||
 | 
			
		||||
void            calendar_client_get_date                (CalendarClient      *client,
 | 
			
		||||
							 guint               *year,
 | 
			
		||||
                                                         guint               *month,
 | 
			
		||||
							 guint               *day);
 | 
			
		||||
void            calendar_client_select_month            (CalendarClient      *client,
 | 
			
		||||
							 guint                month,
 | 
			
		||||
							 guint                year);
 | 
			
		||||
void            calendar_client_select_day              (CalendarClient      *client,
 | 
			
		||||
							 guint                day);
 | 
			
		||||
 | 
			
		||||
GSList         *calendar_client_get_events              (CalendarClient      *client,
 | 
			
		||||
							 CalendarEventType    event_mask);
 | 
			
		||||
void            calendar_client_foreach_appointment_day (CalendarClient      *client,
 | 
			
		||||
							 CalendarDayIter      iter_func,
 | 
			
		||||
							 gpointer             user_data);
 | 
			
		||||
 | 
			
		||||
void            calendar_client_set_task_completed       (CalendarClient     *client,
 | 
			
		||||
							  char               *task_uid,
 | 
			
		||||
							  gboolean            task_completed,
 | 
			
		||||
							  guint               percent_complete);
 | 
			
		||||
 | 
			
		||||
void calendar_event_free (CalendarEvent *event);
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif /* __CALENDAR_CLIENT_H__ */
 | 
			
		||||
							
								
								
									
										52
									
								
								src/calendar-client/calendar-debug.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/calendar-client/calendar-debug.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2004 Free Software Foundation, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License as
 | 
			
		||||
 * published by the Free Software Foundation; either version 2 of the
 | 
			
		||||
 * License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *     Mark McLoughlin  <mark@skynet.ie>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __CALENDAR_DEBUG_H__
 | 
			
		||||
#define __CALENDAR_DEBUG_H__
 | 
			
		||||
 | 
			
		||||
#include <glib.h>
 | 
			
		||||
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
#ifdef CALENDAR_ENABLE_DEBUG
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#ifdef G_HAVE_ISO_VARARGS
 | 
			
		||||
#  define dprintf(...) fprintf (stderr, __VA_ARGS__);
 | 
			
		||||
#elif defined(G_HAVE_GNUC_VARARGS)
 | 
			
		||||
#  define dprintf(args...) fprintf (stderr, args);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#else /* if !defined (CALENDAR_DEBUG) */
 | 
			
		||||
 | 
			
		||||
#ifdef G_HAVE_ISO_VARARGS
 | 
			
		||||
#  define dprintf(...)
 | 
			
		||||
#elif defined(G_HAVE_GNUC_VARARGS)
 | 
			
		||||
#  define dprintf(args...)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif /* CALENDAR_ENABLE_DEBUG */
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif /* __CALENDAR_DEBUG_H__ */
 | 
			
		||||
							
								
								
									
										658
									
								
								src/calendar-client/calendar-sources.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										658
									
								
								src/calendar-client/calendar-sources.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,658 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2004 Free Software Foundation, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License as
 | 
			
		||||
 * published by the Free Software Foundation; either version 2 of the
 | 
			
		||||
 * License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *     Mark McLoughlin  <mark@skynet.ie>
 | 
			
		||||
 *     William Jon McCann  <mccann@jhu.edu>
 | 
			
		||||
 *     Martin Grimme  <martin@pycage.de>
 | 
			
		||||
 *     Christian Kellner  <gicmo@xatom.net>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <config.h>
 | 
			
		||||
 | 
			
		||||
#include "calendar-sources.h"
 | 
			
		||||
 | 
			
		||||
#include <libintl.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <gconf/gconf-client.h>
 | 
			
		||||
#define HANDLE_LIBICAL_MEMORY
 | 
			
		||||
#include <libecal/e-cal.h>
 | 
			
		||||
#include <libedataserver/e-source-list.h>
 | 
			
		||||
#include <libedataserverui/e-passwords.h>
 | 
			
		||||
 | 
			
		||||
#undef CALENDAR_ENABLE_DEBUG
 | 
			
		||||
#include "calendar-debug.h"
 | 
			
		||||
 | 
			
		||||
#ifndef _
 | 
			
		||||
#define _(x) gettext(x)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef N_
 | 
			
		||||
#define N_(x) x
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define CALENDAR_SOURCES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CALENDAR_TYPE_SOURCES, CalendarSourcesPrivate))
 | 
			
		||||
 | 
			
		||||
#define CALENDAR_SOURCES_EVO_DIR                          "/apps/evolution"
 | 
			
		||||
#define CALENDAR_SOURCES_APPOINTMENT_SOURCES_KEY          CALENDAR_SOURCES_EVO_DIR "/calendar/sources"
 | 
			
		||||
#define CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_DIR CALENDAR_SOURCES_EVO_DIR "/calendar/display"
 | 
			
		||||
#define CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_KEY CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_DIR "/selected_calendars"
 | 
			
		||||
#define CALENDAR_SOURCES_TASK_SOURCES_KEY                 CALENDAR_SOURCES_EVO_DIR "/tasks/sources"
 | 
			
		||||
#define CALENDAR_SOURCES_SELECTED_TASK_SOURCES_DIR        CALENDAR_SOURCES_EVO_DIR "/calendar/tasks"
 | 
			
		||||
#define CALENDAR_SOURCES_SELECTED_TASK_SOURCES_KEY        CALENDAR_SOURCES_SELECTED_TASK_SOURCES_DIR "/selected_tasks"
 | 
			
		||||
 | 
			
		||||
typedef struct _CalendarSourceData CalendarSourceData;
 | 
			
		||||
 | 
			
		||||
struct _CalendarSourceData
 | 
			
		||||
{
 | 
			
		||||
  ECalSourceType   source_type;
 | 
			
		||||
  CalendarSources *sources;
 | 
			
		||||
  guint            changed_signal;
 | 
			
		||||
 | 
			
		||||
  GSList          *clients;
 | 
			
		||||
  GSList          *selected_sources;
 | 
			
		||||
  ESourceList     *esource_list;
 | 
			
		||||
 | 
			
		||||
  guint            selected_sources_listener;
 | 
			
		||||
  char            *selected_sources_dir;
 | 
			
		||||
 | 
			
		||||
  guint            timeout_id;
 | 
			
		||||
 | 
			
		||||
  guint            loaded : 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _CalendarSourcesPrivate
 | 
			
		||||
{
 | 
			
		||||
  CalendarSourceData  appointment_sources;
 | 
			
		||||
  CalendarSourceData  task_sources;
 | 
			
		||||
 | 
			
		||||
  GConfClient        *gconf_client;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void calendar_sources_class_init (CalendarSourcesClass *klass);
 | 
			
		||||
static void calendar_sources_init       (CalendarSources      *sources);
 | 
			
		||||
static void calendar_sources_finalize   (GObject             *object);
 | 
			
		||||
 | 
			
		||||
static void backend_died_cb (ECal *client, CalendarSourceData *source_data);
 | 
			
		||||
static void calendar_sources_esource_list_changed (ESourceList        *source_list,
 | 
			
		||||
                                                   CalendarSourceData *source_data);
 | 
			
		||||
 | 
			
		||||
enum
 | 
			
		||||
{
 | 
			
		||||
  APPOINTMENT_SOURCES_CHANGED,
 | 
			
		||||
  TASK_SOURCES_CHANGED,
 | 
			
		||||
  LAST_SIGNAL
 | 
			
		||||
};
 | 
			
		||||
static guint signals [LAST_SIGNAL] = { 0, };
 | 
			
		||||
 | 
			
		||||
static GObjectClass    *parent_class = NULL;
 | 
			
		||||
static CalendarSources *calendar_sources_singleton = NULL;
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
calendar_sources_get_type (void)
 | 
			
		||||
{
 | 
			
		||||
  static GType sources_type = 0;
 | 
			
		||||
  
 | 
			
		||||
  if (!sources_type)
 | 
			
		||||
    {
 | 
			
		||||
      static const GTypeInfo sources_info =
 | 
			
		||||
      {
 | 
			
		||||
	sizeof (CalendarSourcesClass),
 | 
			
		||||
	NULL,		/* base_init */
 | 
			
		||||
	NULL,		/* base_finalize */
 | 
			
		||||
	(GClassInitFunc) calendar_sources_class_init,
 | 
			
		||||
	NULL,           /* class_finalize */
 | 
			
		||||
	NULL,		/* class_data */
 | 
			
		||||
	sizeof (CalendarSources),
 | 
			
		||||
	0,		/* n_preallocs */
 | 
			
		||||
	(GInstanceInitFunc) calendar_sources_init,
 | 
			
		||||
      };
 | 
			
		||||
      
 | 
			
		||||
      sources_type = g_type_register_static (G_TYPE_OBJECT,
 | 
			
		||||
					     "CalendarSources",
 | 
			
		||||
					     &sources_info, 0);
 | 
			
		||||
    }
 | 
			
		||||
  
 | 
			
		||||
  return sources_type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_class_init (CalendarSourcesClass *klass)
 | 
			
		||||
{
 | 
			
		||||
  GObjectClass *gobject_class = (GObjectClass *) klass;
 | 
			
		||||
 | 
			
		||||
  parent_class = g_type_class_peek_parent (klass);
 | 
			
		||||
 | 
			
		||||
  gobject_class->finalize = calendar_sources_finalize;
 | 
			
		||||
 | 
			
		||||
  g_type_class_add_private (klass, sizeof (CalendarSourcesPrivate));
 | 
			
		||||
 | 
			
		||||
  signals [APPOINTMENT_SOURCES_CHANGED] =
 | 
			
		||||
    g_signal_new ("appointment-sources-changed",
 | 
			
		||||
		  G_TYPE_FROM_CLASS (gobject_class),
 | 
			
		||||
		  G_SIGNAL_RUN_LAST,
 | 
			
		||||
		  G_STRUCT_OFFSET (CalendarSourcesClass,
 | 
			
		||||
				   appointment_sources_changed),
 | 
			
		||||
		  NULL,
 | 
			
		||||
		  NULL,
 | 
			
		||||
		  g_cclosure_marshal_VOID__VOID,
 | 
			
		||||
		  G_TYPE_NONE,
 | 
			
		||||
		  0);
 | 
			
		||||
 | 
			
		||||
  signals [TASK_SOURCES_CHANGED] =
 | 
			
		||||
    g_signal_new ("task-sources-changed",
 | 
			
		||||
		  G_TYPE_FROM_CLASS (gobject_class),
 | 
			
		||||
		  G_SIGNAL_RUN_LAST,
 | 
			
		||||
		  G_STRUCT_OFFSET (CalendarSourcesClass,
 | 
			
		||||
				   task_sources_changed),
 | 
			
		||||
		  NULL,
 | 
			
		||||
		  NULL,
 | 
			
		||||
		  g_cclosure_marshal_VOID__VOID,
 | 
			
		||||
		  G_TYPE_NONE,
 | 
			
		||||
		  0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_init (CalendarSources *sources)
 | 
			
		||||
{
 | 
			
		||||
  sources->priv = CALENDAR_SOURCES_GET_PRIVATE (sources);
 | 
			
		||||
 | 
			
		||||
  sources->priv->appointment_sources.source_type    = E_CAL_SOURCE_TYPE_EVENT;
 | 
			
		||||
  sources->priv->appointment_sources.sources        = sources;
 | 
			
		||||
  sources->priv->appointment_sources.changed_signal = signals [APPOINTMENT_SOURCES_CHANGED];
 | 
			
		||||
  sources->priv->appointment_sources.timeout_id     = 0;
 | 
			
		||||
 | 
			
		||||
  sources->priv->task_sources.source_type    = E_CAL_SOURCE_TYPE_TODO;
 | 
			
		||||
  sources->priv->task_sources.sources        = sources;
 | 
			
		||||
  sources->priv->task_sources.changed_signal = signals [TASK_SOURCES_CHANGED];
 | 
			
		||||
  sources->priv->task_sources.timeout_id     = 0;
 | 
			
		||||
 | 
			
		||||
  sources->priv->gconf_client = gconf_client_get_default ();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_finalize_source_data (CalendarSources    *sources,
 | 
			
		||||
				       CalendarSourceData *source_data)
 | 
			
		||||
{
 | 
			
		||||
  if (source_data->loaded)
 | 
			
		||||
    {
 | 
			
		||||
      GSList *l;
 | 
			
		||||
 | 
			
		||||
      if (source_data->selected_sources_dir)
 | 
			
		||||
	{
 | 
			
		||||
	  gconf_client_remove_dir (sources->priv->gconf_client,
 | 
			
		||||
				   source_data->selected_sources_dir,
 | 
			
		||||
				   NULL);
 | 
			
		||||
 | 
			
		||||
	  g_free (source_data->selected_sources_dir);
 | 
			
		||||
	  source_data->selected_sources_dir = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
      if (source_data->selected_sources_listener)
 | 
			
		||||
	{
 | 
			
		||||
	  gconf_client_notify_remove (sources->priv->gconf_client,
 | 
			
		||||
				      source_data->selected_sources_listener);
 | 
			
		||||
	  source_data->selected_sources_listener = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
      for (l = source_data->clients; l; l = l->next)
 | 
			
		||||
        {
 | 
			
		||||
          g_signal_handlers_disconnect_by_func (G_OBJECT (l->data),
 | 
			
		||||
                                                G_CALLBACK (backend_died_cb),
 | 
			
		||||
                                                source_data);
 | 
			
		||||
          g_object_unref (l->data);
 | 
			
		||||
        }
 | 
			
		||||
      g_slist_free (source_data->clients);
 | 
			
		||||
      source_data->clients = NULL;
 | 
			
		||||
 | 
			
		||||
      if (source_data->esource_list)
 | 
			
		||||
        {
 | 
			
		||||
          g_signal_handlers_disconnect_by_func (source_data->esource_list,
 | 
			
		||||
                                                G_CALLBACK (calendar_sources_esource_list_changed),
 | 
			
		||||
                                                source_data);
 | 
			
		||||
          g_object_unref (source_data->esource_list);
 | 
			
		||||
	}
 | 
			
		||||
      source_data->esource_list = NULL;
 | 
			
		||||
 | 
			
		||||
      for (l = source_data->selected_sources; l; l = l->next)
 | 
			
		||||
	g_free (l->data);
 | 
			
		||||
      g_slist_free (source_data->selected_sources);
 | 
			
		||||
      source_data->selected_sources = NULL;
 | 
			
		||||
 | 
			
		||||
      if (source_data->timeout_id != 0)
 | 
			
		||||
        {
 | 
			
		||||
          g_source_remove (source_data->timeout_id);
 | 
			
		||||
          source_data->timeout_id = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      source_data->loaded = FALSE;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_finalize (GObject *object)
 | 
			
		||||
{
 | 
			
		||||
  CalendarSources *sources = CALENDAR_SOURCES (object);
 | 
			
		||||
 | 
			
		||||
  calendar_sources_finalize_source_data (sources, &sources->priv->appointment_sources);
 | 
			
		||||
  calendar_sources_finalize_source_data (sources, &sources->priv->task_sources);
 | 
			
		||||
 | 
			
		||||
  if (sources->priv->gconf_client)
 | 
			
		||||
    g_object_unref (sources->priv->gconf_client);
 | 
			
		||||
  sources->priv->gconf_client = NULL;
 | 
			
		||||
 | 
			
		||||
  if (G_OBJECT_CLASS (parent_class)->finalize)
 | 
			
		||||
    G_OBJECT_CLASS (parent_class)->finalize (object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CalendarSources *
 | 
			
		||||
calendar_sources_get (void)
 | 
			
		||||
{
 | 
			
		||||
  gpointer singleton_location = &calendar_sources_singleton;
 | 
			
		||||
 | 
			
		||||
  if (calendar_sources_singleton)
 | 
			
		||||
    return g_object_ref (calendar_sources_singleton);
 | 
			
		||||
 | 
			
		||||
  calendar_sources_singleton = g_object_new (CALENDAR_TYPE_SOURCES, NULL);
 | 
			
		||||
  g_object_add_weak_pointer (G_OBJECT (calendar_sources_singleton),
 | 
			
		||||
			     singleton_location);
 | 
			
		||||
 | 
			
		||||
  return calendar_sources_singleton;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
is_source_selected (ESource *esource,
 | 
			
		||||
		    GSList  *selected_sources)
 | 
			
		||||
{
 | 
			
		||||
  const char *uid;
 | 
			
		||||
  GSList     *l;
 | 
			
		||||
 | 
			
		||||
  uid = e_source_peek_uid (esource);
 | 
			
		||||
 | 
			
		||||
  for (l = selected_sources; l; l = l->next)
 | 
			
		||||
    {
 | 
			
		||||
      const char *source = l->data;
 | 
			
		||||
 | 
			
		||||
      if (!strcmp (source, uid))
 | 
			
		||||
	return TRUE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *
 | 
			
		||||
auth_func_cb (ECal       *ecal,
 | 
			
		||||
	      const char *prompt,
 | 
			
		||||
	      const char *key,
 | 
			
		||||
	      gpointer    user_data)
 | 
			
		||||
{
 | 
			
		||||
	ESource *source;
 | 
			
		||||
	const gchar *auth_domain;
 | 
			
		||||
	const gchar *component_name;
 | 
			
		||||
 | 
			
		||||
	source = e_cal_get_source (ecal);
 | 
			
		||||
	auth_domain = e_source_get_property (source, "auth-domain");
 | 
			
		||||
	component_name = auth_domain ? auth_domain : "Calendar";
 | 
			
		||||
 | 
			
		||||
	return e_passwords_get_password (component_name, key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* The clients are just created here but not loaded */
 | 
			
		||||
static ECal *
 | 
			
		||||
get_ecal_from_source (ESource        *esource,
 | 
			
		||||
		      ECalSourceType  source_type,
 | 
			
		||||
		      GSList         *existing_clients)
 | 
			
		||||
{
 | 
			
		||||
  ECal *retval;
 | 
			
		||||
 | 
			
		||||
  if (existing_clients)
 | 
			
		||||
    {
 | 
			
		||||
      GSList *l;
 | 
			
		||||
 | 
			
		||||
      for (l = existing_clients; l; l = l->next)
 | 
			
		||||
	{
 | 
			
		||||
	  ECal *client = E_CAL (l->data);
 | 
			
		||||
 | 
			
		||||
	  if (e_source_equal (esource, e_cal_get_source (client)))
 | 
			
		||||
	    {
 | 
			
		||||
	      dprintf ("        load_esource: found existing source ... returning that\n");
 | 
			
		||||
 | 
			
		||||
	      return g_object_ref (client);
 | 
			
		||||
	    }
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  retval = e_cal_new (esource, source_type);
 | 
			
		||||
  if (!retval)
 | 
			
		||||
    {
 | 
			
		||||
      g_warning ("Could not load source '%s' from '%s'\n",
 | 
			
		||||
		 e_source_peek_name (esource),
 | 
			
		||||
		 e_source_peek_relative_uri (esource));
 | 
			
		||||
      return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  e_cal_set_auth_func (retval, auth_func_cb, NULL);
 | 
			
		||||
 | 
			
		||||
  return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* - Order doesn't matter
 | 
			
		||||
 * - Can just compare object pointers since we
 | 
			
		||||
 *   re-use client connections
 | 
			
		||||
 */
 | 
			
		||||
static gboolean
 | 
			
		||||
compare_ecal_lists (GSList *a,
 | 
			
		||||
		    GSList *b)
 | 
			
		||||
{
 | 
			
		||||
  GSList *l;
 | 
			
		||||
 | 
			
		||||
  if (g_slist_length (a) != g_slist_length (b))
 | 
			
		||||
    return FALSE;
 | 
			
		||||
 | 
			
		||||
  for (l = a; l; l = l->next)
 | 
			
		||||
    {
 | 
			
		||||
      if (!g_slist_find (b, l->data))
 | 
			
		||||
	return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void
 | 
			
		||||
debug_dump_selected_sources (GSList *selected_sources)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CALENDAR_ENABLE_DEBUG
 | 
			
		||||
  GSList *l;
 | 
			
		||||
 | 
			
		||||
  dprintf ("Selected sources:\n");
 | 
			
		||||
  for (l = selected_sources; l; l = l->next)
 | 
			
		||||
    {
 | 
			
		||||
      char *source = l->data;
 | 
			
		||||
 | 
			
		||||
      dprintf ("  %s\n", source);
 | 
			
		||||
    }
 | 
			
		||||
  dprintf ("\n");
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void
 | 
			
		||||
debug_dump_ecal_list (GSList *ecal_list)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CALENDAR_ENABLE_DEBUG
 | 
			
		||||
  GSList *l;
 | 
			
		||||
 | 
			
		||||
  dprintf ("Loaded clients:\n");
 | 
			
		||||
  for (l = ecal_list; l; l = l->next)
 | 
			
		||||
    {
 | 
			
		||||
      ECal    *client = l->data;
 | 
			
		||||
      ESource *source = e_cal_get_source (client);
 | 
			
		||||
 | 
			
		||||
      dprintf ("  %s %s %s\n",
 | 
			
		||||
	       e_source_peek_uid (source),
 | 
			
		||||
	       e_source_peek_name (source),
 | 
			
		||||
	       e_cal_get_uri (client));
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_load_esource_list (CalendarSourceData *source_data);
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
backend_restart (gpointer data)
 | 
			
		||||
{
 | 
			
		||||
  CalendarSourceData *source_data = data;
 | 
			
		||||
 | 
			
		||||
  calendar_sources_load_esource_list (source_data);
 | 
			
		||||
 | 
			
		||||
  source_data->timeout_id = 0;
 | 
			
		||||
    
 | 
			
		||||
  return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
backend_died_cb (ECal *client, CalendarSourceData *source_data)
 | 
			
		||||
{
 | 
			
		||||
  const char *uristr;
 | 
			
		||||
 | 
			
		||||
  source_data->clients = g_slist_remove (source_data->clients, client);
 | 
			
		||||
  if (g_slist_length (source_data->clients) < 1) 
 | 
			
		||||
    {
 | 
			
		||||
      g_slist_free (source_data->clients);
 | 
			
		||||
      source_data->clients = NULL;
 | 
			
		||||
    }
 | 
			
		||||
  uristr = e_cal_get_uri (client);
 | 
			
		||||
  g_warning ("The calendar backend for %s has crashed.", uristr);
 | 
			
		||||
 | 
			
		||||
  if (source_data->timeout_id != 0)
 | 
			
		||||
    {
 | 
			
		||||
      g_source_remove (source_data->timeout_id);
 | 
			
		||||
      source_data->timeout_id = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  source_data->timeout_id = g_timeout_add_seconds (2, backend_restart,
 | 
			
		||||
		  				   source_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_load_esource_list (CalendarSourceData *source_data)
 | 
			
		||||
{
 | 
			
		||||
  GSList  *clients = NULL;
 | 
			
		||||
  GSList  *groups, *l;
 | 
			
		||||
  gboolean emit_signal = FALSE;
 | 
			
		||||
 | 
			
		||||
  g_return_if_fail (source_data->esource_list != NULL);
 | 
			
		||||
 | 
			
		||||
  debug_dump_selected_sources (source_data->selected_sources);
 | 
			
		||||
 | 
			
		||||
  dprintf ("Source groups:\n");
 | 
			
		||||
  groups = e_source_list_peek_groups (source_data->esource_list);
 | 
			
		||||
  for (l = groups; l; l = l->next)
 | 
			
		||||
    {
 | 
			
		||||
      GSList *esources, *s;
 | 
			
		||||
 | 
			
		||||
      dprintf ("  %s\n", e_source_group_peek_uid (l->data));
 | 
			
		||||
      dprintf ("    sources:\n");
 | 
			
		||||
 | 
			
		||||
      esources = e_source_group_peek_sources (l->data);
 | 
			
		||||
      for (s = esources; s; s = s->next)
 | 
			
		||||
	{
 | 
			
		||||
	  ESource *esource = E_SOURCE (s->data);
 | 
			
		||||
	  ECal    *client;
 | 
			
		||||
 | 
			
		||||
	  dprintf ("      type = '%s' uid = '%s', name = '%s', relative uri = '%s': \n",
 | 
			
		||||
                   source_data->source_type == E_CAL_SOURCE_TYPE_EVENT ? "appointment" : "task",
 | 
			
		||||
		   e_source_peek_uid (esource),
 | 
			
		||||
		   e_source_peek_name (esource),
 | 
			
		||||
		   e_source_peek_relative_uri (esource));
 | 
			
		||||
 | 
			
		||||
	  if (is_source_selected (esource, source_data->selected_sources) &&
 | 
			
		||||
	      (client = get_ecal_from_source (esource, source_data->source_type, source_data->clients)))
 | 
			
		||||
	    {
 | 
			
		||||
	      clients = g_slist_prepend (clients, client);
 | 
			
		||||
	    }
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
  dprintf ("\n");
 | 
			
		||||
 | 
			
		||||
  if (source_data->loaded && 
 | 
			
		||||
      !compare_ecal_lists (source_data->clients, clients))
 | 
			
		||||
    emit_signal = TRUE;
 | 
			
		||||
 | 
			
		||||
  for (l = source_data->clients; l; l = l->next)
 | 
			
		||||
    {
 | 
			
		||||
      g_signal_handlers_disconnect_by_func (G_OBJECT (l->data),
 | 
			
		||||
                                            G_CALLBACK (backend_died_cb),
 | 
			
		||||
                                            source_data);
 | 
			
		||||
 | 
			
		||||
      g_object_unref (l->data);
 | 
			
		||||
    }
 | 
			
		||||
  g_slist_free (source_data->clients);
 | 
			
		||||
  source_data->clients = g_slist_reverse (clients);
 | 
			
		||||
 | 
			
		||||
  /* connect to backend_died after we disconnected the previous signal
 | 
			
		||||
   * handlers. If we do it before, we'll lose some handlers (for clients that
 | 
			
		||||
   * were already there before) */
 | 
			
		||||
  for (l = source_data->clients; l; l = l->next)
 | 
			
		||||
    {
 | 
			
		||||
      g_signal_connect (G_OBJECT (l->data), "backend_died",
 | 
			
		||||
                        G_CALLBACK (backend_died_cb), source_data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  if (emit_signal) 
 | 
			
		||||
    {
 | 
			
		||||
      dprintf ("Emitting %s-sources-changed signal\n",
 | 
			
		||||
	       source_data->source_type == E_CAL_SOURCE_TYPE_EVENT ? "appointment" : "task");
 | 
			
		||||
      g_signal_emit (source_data->sources, source_data->changed_signal, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  debug_dump_ecal_list (source_data->clients);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_esource_list_changed (ESourceList        *source_list,
 | 
			
		||||
				       CalendarSourceData *source_data)
 | 
			
		||||
				       
 | 
			
		||||
{
 | 
			
		||||
  dprintf ("ESourceList changed, reloading\n");
 | 
			
		||||
 | 
			
		||||
  calendar_sources_load_esource_list (source_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_selected_sources_notify (GConfClient        *client,
 | 
			
		||||
					  guint               cnx_id,
 | 
			
		||||
					  GConfEntry         *entry,
 | 
			
		||||
					  CalendarSourceData *source_data)
 | 
			
		||||
{
 | 
			
		||||
  GSList *l;
 | 
			
		||||
 | 
			
		||||
  if (!entry->value ||
 | 
			
		||||
      entry->value->type != GCONF_VALUE_LIST ||
 | 
			
		||||
      gconf_value_get_list_type (entry->value) != GCONF_VALUE_STRING)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  dprintf ("Selected sources key (%s) changed, reloading\n", entry->key);
 | 
			
		||||
 | 
			
		||||
  for (l = source_data->selected_sources; l; l = l->next)
 | 
			
		||||
    g_free (l->data);
 | 
			
		||||
  source_data->selected_sources = NULL;
 | 
			
		||||
 | 
			
		||||
  for (l = gconf_value_get_list (entry->value); l; l = l->next)
 | 
			
		||||
    {
 | 
			
		||||
      const char *source = gconf_value_get_string (l->data);
 | 
			
		||||
 | 
			
		||||
      source_data->selected_sources = 
 | 
			
		||||
	g_slist_prepend (source_data->selected_sources,
 | 
			
		||||
			 g_strdup (source));
 | 
			
		||||
    }
 | 
			
		||||
  source_data->selected_sources =
 | 
			
		||||
    g_slist_reverse (source_data->selected_sources);
 | 
			
		||||
 | 
			
		||||
  calendar_sources_load_esource_list (source_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_load_sources (CalendarSources    *sources,
 | 
			
		||||
			       CalendarSourceData *source_data,
 | 
			
		||||
			       const char         *sources_key,
 | 
			
		||||
			       const char         *selected_sources_key,
 | 
			
		||||
			       const char         *selected_sources_dir)
 | 
			
		||||
{
 | 
			
		||||
  GConfClient *gconf_client;
 | 
			
		||||
  GError      *error;
 | 
			
		||||
 | 
			
		||||
  dprintf ("---------------------------\n");
 | 
			
		||||
  dprintf ("Loading sources:\n");
 | 
			
		||||
  dprintf ("  sources_key: %s\n", sources_key);
 | 
			
		||||
  dprintf ("  selected_sources_key: %s\n", selected_sources_key);
 | 
			
		||||
  dprintf ("  selected_sources_dir: %s\n", selected_sources_dir);
 | 
			
		||||
 | 
			
		||||
  gconf_client = sources->priv->gconf_client;
 | 
			
		||||
 | 
			
		||||
  error = NULL;
 | 
			
		||||
  source_data->selected_sources = gconf_client_get_list (gconf_client,
 | 
			
		||||
							 selected_sources_key,
 | 
			
		||||
							 GCONF_VALUE_STRING,
 | 
			
		||||
							 &error);
 | 
			
		||||
  if (error)
 | 
			
		||||
    {
 | 
			
		||||
      g_warning ("Failed to get selected sources from '%s': %s\n",
 | 
			
		||||
		 selected_sources_key,
 | 
			
		||||
		 error->message);
 | 
			
		||||
      g_error_free (error);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  gconf_client_add_dir (gconf_client,
 | 
			
		||||
			selected_sources_dir,
 | 
			
		||||
			GCONF_CLIENT_PRELOAD_NONE,
 | 
			
		||||
			NULL);
 | 
			
		||||
  source_data->selected_sources_dir = g_strdup (selected_sources_dir);
 | 
			
		||||
 | 
			
		||||
  source_data->selected_sources_listener =
 | 
			
		||||
    gconf_client_notify_add (gconf_client,
 | 
			
		||||
			     selected_sources_dir,
 | 
			
		||||
			     (GConfClientNotifyFunc) calendar_sources_selected_sources_notify,
 | 
			
		||||
			     source_data, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
  source_data->esource_list = e_source_list_new_for_gconf (gconf_client, sources_key);
 | 
			
		||||
  g_signal_connect (source_data->esource_list, "changed",
 | 
			
		||||
		    G_CALLBACK (calendar_sources_esource_list_changed),
 | 
			
		||||
		    source_data);
 | 
			
		||||
 | 
			
		||||
  calendar_sources_load_esource_list (source_data);
 | 
			
		||||
 | 
			
		||||
  source_data->loaded = TRUE;
 | 
			
		||||
 | 
			
		||||
  dprintf ("---------------------------\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GSList *
 | 
			
		||||
calendar_sources_get_appointment_sources (CalendarSources *sources)
 | 
			
		||||
{
 | 
			
		||||
  g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
 | 
			
		||||
 | 
			
		||||
  if (!sources->priv->appointment_sources.loaded)
 | 
			
		||||
    {
 | 
			
		||||
      calendar_sources_load_sources (sources,
 | 
			
		||||
				     &sources->priv->appointment_sources,
 | 
			
		||||
				     CALENDAR_SOURCES_APPOINTMENT_SOURCES_KEY,
 | 
			
		||||
				     CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_KEY,
 | 
			
		||||
				     CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_DIR);
 | 
			
		||||
    }
 | 
			
		||||
  
 | 
			
		||||
  return sources->priv->appointment_sources.clients;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GSList *
 | 
			
		||||
calendar_sources_get_task_sources (CalendarSources *sources)
 | 
			
		||||
{
 | 
			
		||||
  g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
 | 
			
		||||
 | 
			
		||||
  if (!sources->priv->task_sources.loaded)
 | 
			
		||||
    {
 | 
			
		||||
      calendar_sources_load_sources (sources,
 | 
			
		||||
				     &sources->priv->task_sources,
 | 
			
		||||
				     CALENDAR_SOURCES_TASK_SOURCES_KEY,
 | 
			
		||||
				     CALENDAR_SOURCES_SELECTED_TASK_SOURCES_KEY,
 | 
			
		||||
				     CALENDAR_SOURCES_SELECTED_TASK_SOURCES_DIR);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  return sources->priv->task_sources.clients;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										66
									
								
								src/calendar-client/calendar-sources.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/calendar-client/calendar-sources.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2004 Free Software Foundation, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License as
 | 
			
		||||
 * published by the Free Software Foundation; either version 2 of the
 | 
			
		||||
 * License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *     Mark McLoughlin  <mark@skynet.ie>
 | 
			
		||||
 *     William Jon McCann  <mccann@jhu.edu>
 | 
			
		||||
 *     Martin Grimme  <martin@pycage.de>
 | 
			
		||||
 *     Christian Kellner  <gicmo@xatom.net>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __CALENDAR_SOURCES_H__
 | 
			
		||||
#define __CALENDAR_SOURCES_H__
 | 
			
		||||
 | 
			
		||||
#include <glib-object.h>
 | 
			
		||||
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
#define CALENDAR_TYPE_SOURCES        (calendar_sources_get_type ())
 | 
			
		||||
#define CALENDAR_SOURCES(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CALENDAR_TYPE_SOURCES, CalendarSources))
 | 
			
		||||
#define CALENDAR_SOURCES_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST ((k), CALENDAR_TYPE_SOURCES, CalendarSourcesClass))
 | 
			
		||||
#define CALENDAR_IS_SOURCES(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), CALENDAR_TYPE_SOURCES))
 | 
			
		||||
#define CALENDAR_IS_SOURCES_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CALENDAR_TYPE_SOURCES))
 | 
			
		||||
#define CALENDAR_SOURCES_GET_CLASS(o)(G_TYPE_INSTANCE_GET_CLASS ((o), CALENDAR_TYPE_SOURCES, CalendarSourcesClass))
 | 
			
		||||
 | 
			
		||||
typedef struct _CalendarSources        CalendarSources;
 | 
			
		||||
typedef struct _CalendarSourcesClass   CalendarSourcesClass;
 | 
			
		||||
typedef struct _CalendarSourcesPrivate CalendarSourcesPrivate;
 | 
			
		||||
 | 
			
		||||
struct _CalendarSources
 | 
			
		||||
{
 | 
			
		||||
  GObject                 parent;
 | 
			
		||||
  CalendarSourcesPrivate *priv;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _CalendarSourcesClass
 | 
			
		||||
{
 | 
			
		||||
  GObjectClass    parent_class;
 | 
			
		||||
 | 
			
		||||
  void         (* appointment_sources_changed) (CalendarSources *sources);
 | 
			
		||||
  void         (* task_sources_changed)        (CalendarSources *sources);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GType            calendar_sources_get_type                (void) G_GNUC_CONST;
 | 
			
		||||
CalendarSources *calendar_sources_get                     (void);
 | 
			
		||||
GSList          *calendar_sources_get_appointment_sources (CalendarSources *sources);
 | 
			
		||||
GSList          *calendar_sources_get_task_sources        (CalendarSources *sources);
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif /* __CALENDAR_SOURCES_H__ */
 | 
			
		||||
							
								
								
									
										255
									
								
								src/shell-evolution-event-source.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								src/shell-evolution-event-source.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,255 @@
 | 
			
		||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 | 
			
		||||
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
#include "calendar-client/calendar-client.h"
 | 
			
		||||
#include "shell-evolution-event-source.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct _ShellEvolutionEventSourceClass
 | 
			
		||||
{
 | 
			
		||||
  GObjectClass parent_class;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _ShellEvolutionEventSource {
 | 
			
		||||
  GObject parent;
 | 
			
		||||
  CalendarClient *client;
 | 
			
		||||
  /* The month that we are currently requesting events from */
 | 
			
		||||
  gint req_year;
 | 
			
		||||
  gint req_mon; /* starts at 1, not zero */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Signals */
 | 
			
		||||
enum
 | 
			
		||||
{
 | 
			
		||||
  CHANGED_SIGNAL,
 | 
			
		||||
  LAST_SIGNAL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static guint signals[LAST_SIGNAL] = { 0 };
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE (ShellEvolutionEventSource, shell_evolution_event_source, G_TYPE_OBJECT);
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_tasks_changed (CalendarClient *client,
 | 
			
		||||
                  gpointer        user_data)
 | 
			
		||||
{
 | 
			
		||||
  ShellEvolutionEventSource *source = SHELL_EVOLUTION_EVENT_SOURCE (user_data);
 | 
			
		||||
  /* g_print ("on tasks changed\n"); */
 | 
			
		||||
  g_signal_emit (source, signals[CHANGED_SIGNAL], 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_appointments_changed (CalendarClient *client,
 | 
			
		||||
                         gpointer        user_data)
 | 
			
		||||
{
 | 
			
		||||
  ShellEvolutionEventSource *source = SHELL_EVOLUTION_EVENT_SOURCE (user_data);
 | 
			
		||||
  /* g_print ("on appointments changed\n"); */
 | 
			
		||||
  g_signal_emit (source, signals[CHANGED_SIGNAL], 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
shell_evolution_event_source_init (ShellEvolutionEventSource *source)
 | 
			
		||||
{
 | 
			
		||||
  source->client = calendar_client_new ();
 | 
			
		||||
  g_signal_connect (source->client,
 | 
			
		||||
                    "tasks-changed",
 | 
			
		||||
                    G_CALLBACK (on_tasks_changed),
 | 
			
		||||
                    source);
 | 
			
		||||
  g_signal_connect (source->client,
 | 
			
		||||
                    "appointments-changed",
 | 
			
		||||
                    G_CALLBACK (on_appointments_changed),
 | 
			
		||||
                    source);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
shell_evolution_event_source_finalize (GObject *object)
 | 
			
		||||
{
 | 
			
		||||
  ShellEvolutionEventSource *source = SHELL_EVOLUTION_EVENT_SOURCE (object);
 | 
			
		||||
  g_object_unref (source->client);
 | 
			
		||||
  G_OBJECT_CLASS (shell_evolution_event_source_parent_class)->finalize (object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
shell_evolution_event_source_class_init (ShellEvolutionEventSourceClass *klass)
 | 
			
		||||
{
 | 
			
		||||
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 | 
			
		||||
 | 
			
		||||
  gobject_class->finalize = shell_evolution_event_source_finalize;
 | 
			
		||||
 | 
			
		||||
  signals[CHANGED_SIGNAL] =
 | 
			
		||||
    g_signal_new ("changed",
 | 
			
		||||
                  G_TYPE_FROM_CLASS (klass),
 | 
			
		||||
                  G_SIGNAL_RUN_LAST,
 | 
			
		||||
                  0,
 | 
			
		||||
                  NULL, NULL,
 | 
			
		||||
                  g_cclosure_marshal_VOID__VOID,
 | 
			
		||||
                  G_TYPE_NONE, 0);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ShellEvolutionEventSource *
 | 
			
		||||
shell_evolution_event_source_new (void)
 | 
			
		||||
{
 | 
			
		||||
  return SHELL_EVOLUTION_EVENT_SOURCE (g_object_new (SHELL_TYPE_EVOLUTION_EVENT_SOURCE, NULL));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
shell_evolution_event_source_request_range (ShellEvolutionEventSource *source,
 | 
			
		||||
                                            gint64                     msec_begin,
 | 
			
		||||
                                            gint64                     msec_end)
 | 
			
		||||
{
 | 
			
		||||
  GDateTime *middle;
 | 
			
		||||
 | 
			
		||||
  /* The CalendarClient type is a convenience wrapper on top of
 | 
			
		||||
   * Evolution Data Server. It is based on the assumption that only
 | 
			
		||||
   * a single month is shown at a time.
 | 
			
		||||
   *
 | 
			
		||||
   * To avoid reimplemting all the work already done in CalendarClient
 | 
			
		||||
   * we make the same assumption. This means that we only show events
 | 
			
		||||
   * in the month that is in the middle of @msec_begin and
 | 
			
		||||
   * @msec_end. Since the Shell displays a month at a time (plus the
 | 
			
		||||
   * days before and after) it works out just fine.
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  middle = g_date_time_new_from_unix_utc ((msec_begin + msec_end) / 2 / 1000);
 | 
			
		||||
  g_date_time_get_ymd (middle, &source->req_year, &source->req_mon, NULL);
 | 
			
		||||
  g_date_time_unref (middle);
 | 
			
		||||
  calendar_client_select_month (source->client, source->req_mon - 1, source->req_year);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gint
 | 
			
		||||
event_cmp (gconstpointer a,
 | 
			
		||||
           gconstpointer b)
 | 
			
		||||
{
 | 
			
		||||
  const ShellEvolutionEvent *ea;
 | 
			
		||||
  const ShellEvolutionEvent *eb;
 | 
			
		||||
 | 
			
		||||
  ea = a;
 | 
			
		||||
  eb = b;
 | 
			
		||||
  if (ea->msec_begin < eb->msec_begin)
 | 
			
		||||
    return -1;
 | 
			
		||||
  else if (ea->msec_begin > eb->msec_begin)
 | 
			
		||||
    return 1;
 | 
			
		||||
  else
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * shell_evolution_event_source_get_events:
 | 
			
		||||
 * @source: A #ShellEvolutionEventSource.
 | 
			
		||||
 * @msec_begin: Start date (milli-seconds since Epoch).
 | 
			
		||||
 * @msec_end: End date (milli-seconds since Epoch).
 | 
			
		||||
 *
 | 
			
		||||
 * Gets all events that occur between @msec_begin and @msec_end.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: (element-type ShellEvolutionEvent) (transfer full): List of events.
 | 
			
		||||
 */
 | 
			
		||||
GList *
 | 
			
		||||
shell_evolution_event_source_get_events  (ShellEvolutionEventSource *source,
 | 
			
		||||
                                          gint64                     msec_begin,
 | 
			
		||||
                                          gint64                     msec_end)
 | 
			
		||||
{
 | 
			
		||||
  GList *result;
 | 
			
		||||
  GDateTime *cur_date;
 | 
			
		||||
  GDateTime *begin_date;
 | 
			
		||||
  GDateTime *end_date;
 | 
			
		||||
 | 
			
		||||
  g_return_val_if_fail (msec_begin <= msec_end, NULL);
 | 
			
		||||
 | 
			
		||||
  result = NULL;
 | 
			
		||||
 | 
			
		||||
  begin_date = g_date_time_new_from_unix_utc (msec_begin / 1000);
 | 
			
		||||
  end_date = g_date_time_new_from_unix_utc (msec_end / 1000);
 | 
			
		||||
  cur_date = g_date_time_ref (begin_date);
 | 
			
		||||
  do
 | 
			
		||||
    {
 | 
			
		||||
      gint year, mon, day;
 | 
			
		||||
      GDateTime *next_date;
 | 
			
		||||
 | 
			
		||||
      g_date_time_get_ymd (cur_date, &year, &mon, &day);
 | 
			
		||||
      /* g_print ("y=%04d m=%02d d=%02d\n", year, mon, day); */
 | 
			
		||||
 | 
			
		||||
      /* Silently drop events not in range (see comment in
 | 
			
		||||
       * shell_evolution_event_source_request_range() above)
 | 
			
		||||
       */
 | 
			
		||||
      if (!(year == source->req_year && mon == source->req_mon))
 | 
			
		||||
        {
 | 
			
		||||
          /* g_print ("skipping day\n"); */
 | 
			
		||||
        }
 | 
			
		||||
      else
 | 
			
		||||
        {
 | 
			
		||||
          GSList *events;
 | 
			
		||||
          GSList *l;
 | 
			
		||||
          calendar_client_select_day (source->client, day);
 | 
			
		||||
          events = calendar_client_get_events (source->client, CALENDAR_EVENT_APPOINTMENT);
 | 
			
		||||
          /* g_print ("num_events: %d\n", g_slist_length (events)); */
 | 
			
		||||
          for (l = events; l; l = l->next)
 | 
			
		||||
            {
 | 
			
		||||
              CalendarAppointment *appointment = l->data;
 | 
			
		||||
              ShellEvolutionEvent *event;
 | 
			
		||||
              gint64 start_time;
 | 
			
		||||
 | 
			
		||||
              if (appointment->is_all_day)
 | 
			
		||||
                {
 | 
			
		||||
                  start_time = g_date_time_to_unix (cur_date) * G_GINT64_CONSTANT (1000);
 | 
			
		||||
                }
 | 
			
		||||
              else
 | 
			
		||||
                {
 | 
			
		||||
                  start_time = appointment->start_time * G_GINT64_CONSTANT (1000);
 | 
			
		||||
                }
 | 
			
		||||
              event = shell_evolution_event_new (appointment->summary,
 | 
			
		||||
                                                 appointment->is_all_day,
 | 
			
		||||
                                                 start_time);
 | 
			
		||||
              result = g_list_prepend (result, event);
 | 
			
		||||
            }
 | 
			
		||||
          g_slist_foreach (events, (GFunc) calendar_event_free, NULL);
 | 
			
		||||
          g_slist_free (events);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      next_date = g_date_time_add_days (cur_date, 1);
 | 
			
		||||
      g_date_time_unref (cur_date);
 | 
			
		||||
      cur_date = next_date;
 | 
			
		||||
    }
 | 
			
		||||
  while (g_date_time_difference (end_date, cur_date) > 0);
 | 
			
		||||
  g_date_time_unref (begin_date);
 | 
			
		||||
  g_date_time_unref (end_date);
 | 
			
		||||
 | 
			
		||||
  result = g_list_sort (result, event_cmp);
 | 
			
		||||
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
G_DEFINE_BOXED_TYPE (ShellEvolutionEvent,
 | 
			
		||||
                     shell_evolution_event,
 | 
			
		||||
                     shell_evolution_event_copy,
 | 
			
		||||
                     shell_evolution_event_free);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
shell_evolution_event_free (ShellEvolutionEvent *event)
 | 
			
		||||
{
 | 
			
		||||
  g_free (event->summary);
 | 
			
		||||
  g_free (event);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ShellEvolutionEvent *
 | 
			
		||||
shell_evolution_event_copy (ShellEvolutionEvent *event)
 | 
			
		||||
{
 | 
			
		||||
  ShellEvolutionEvent *copy;
 | 
			
		||||
  copy = g_memdup (event, sizeof (ShellEvolutionEvent));
 | 
			
		||||
  copy->summary = g_strdup (event->summary);
 | 
			
		||||
  return copy;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ShellEvolutionEvent *
 | 
			
		||||
shell_evolution_event_new (const gchar *summary,
 | 
			
		||||
                           gboolean     all_day,
 | 
			
		||||
                           gint64       msec_begin)
 | 
			
		||||
{
 | 
			
		||||
  ShellEvolutionEvent *event;
 | 
			
		||||
  event = g_new0 (ShellEvolutionEvent, 1);
 | 
			
		||||
  event->summary = g_strdup (summary);
 | 
			
		||||
  event->all_day = all_day;
 | 
			
		||||
  event->msec_begin = msec_begin;
 | 
			
		||||
  return event;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										45
									
								
								src/shell-evolution-event-source.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/shell-evolution-event-source.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 | 
			
		||||
#ifndef __SHELL_EVOLUTION_EVENT_SOURCE_H__
 | 
			
		||||
#define __SHELL_EVOLUTION_EVENT_SOURCE_H__
 | 
			
		||||
 | 
			
		||||
#include <glib-object.h>
 | 
			
		||||
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
typedef struct _ShellEvolutionEvent            ShellEvolutionEvent;
 | 
			
		||||
 | 
			
		||||
struct _ShellEvolutionEvent
 | 
			
		||||
{
 | 
			
		||||
  gchar    *summary;
 | 
			
		||||
  gboolean  all_day;
 | 
			
		||||
  gint64    msec_begin;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GType                shell_evolution_event_get_type (void) G_GNUC_CONST;
 | 
			
		||||
ShellEvolutionEvent *shell_evolution_event_new      (const gchar *summary,
 | 
			
		||||
                                                     gboolean     all_day,
 | 
			
		||||
                                                     gint64       msec_begin);
 | 
			
		||||
ShellEvolutionEvent *shell_evolution_event_copy     (ShellEvolutionEvent *event);
 | 
			
		||||
void                 shell_evolution_event_free     (ShellEvolutionEvent *event);
 | 
			
		||||
 | 
			
		||||
typedef struct _ShellEvolutionEventSource      ShellEvolutionEventSource;
 | 
			
		||||
typedef struct _ShellEvolutionEventSourceClass ShellEvolutionEventSourceClass;
 | 
			
		||||
 | 
			
		||||
#define SHELL_TYPE_EVOLUTION_EVENT_SOURCE              (shell_evolution_event_source_get_type ())
 | 
			
		||||
#define SHELL_EVOLUTION_EVENT_SOURCE(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_EVOLUTION_EVENT_SOURCE, ShellEvolutionEventSource))
 | 
			
		||||
#define SHELL_EVOLUTION_EVENT_SOURCE_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_EVOLUTION_EVENT_SOURCE, ShellEvolutionEventSourceClass))
 | 
			
		||||
#define SHELL_IS_EVOLUTION_EVENT_SOURCE(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_EVOLUTION_EVENT_SOURCE))
 | 
			
		||||
#define SHELL_IS_EVOLUTION_EVENT_SOURCE_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_EVOLUTION_EVENT_SOURCE))
 | 
			
		||||
#define SHELL_EVOLUTION_EVENT_SOURCE_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_EVOLUTION_EVENT_SOURCE, ShellEvolutionEventSourceClass))
 | 
			
		||||
 | 
			
		||||
GType                      shell_evolution_event_source_get_type      (void) G_GNUC_CONST;
 | 
			
		||||
ShellEvolutionEventSource *shell_evolution_event_source_new           (void);
 | 
			
		||||
void                       shell_evolution_event_source_request_range (ShellEvolutionEventSource *source,
 | 
			
		||||
                                                                       gint64                     msec_begin,
 | 
			
		||||
                                                                       gint64                     msec_end);
 | 
			
		||||
GList                     *shell_evolution_event_source_get_events    (ShellEvolutionEventSource *source,
 | 
			
		||||
                                                                       gint64                     msec_begin,
 | 
			
		||||
                                                                       gint64                     msec_end);
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif /* __SHELL_EVOLUTION_EVENT_SOURCE_H__ */
 | 
			
		||||
@@ -83,7 +83,7 @@ if test "x$system" = xUbuntu -o "x$system" = xDebian -o "x$system" = xLinuxMint
 | 
			
		||||
    xulrunner-dev xserver-xephyr gnome-terminal libcroco3-dev
 | 
			
		||||
    libgstreamer0.10-dev gstreamer0.10-plugins-base gstreamer0.10-plugins-good
 | 
			
		||||
    libltdl-dev libvorbis-dev libxklavier-dev libgnome-keyring-dev
 | 
			
		||||
    libupower-glib-dev libcups2-dev
 | 
			
		||||
    libupower-glib-dev libcups2-dev evolution-data-server-dev
 | 
			
		||||
    "
 | 
			
		||||
 | 
			
		||||
  if apt-cache show autopoint > /dev/null 2> /dev/null; then
 | 
			
		||||
@@ -121,7 +121,7 @@ if test "x$system" = xFedora ; then
 | 
			
		||||
    startup-notification-devel xorg-x11-server-Xephyr gnome-terminal zenity
 | 
			
		||||
    icon-naming-utils upower-devel libtool-ltdl-devel libvorbis-devel
 | 
			
		||||
    libxklavier-devel libgcrypt-devel libtasn1-devel libtasn1-tools
 | 
			
		||||
    libgnome-keyring-devel libgtop2-devel cups-devel
 | 
			
		||||
    libgnome-keyring-devel libgtop2-devel cups-devel evolution-data-server-devel
 | 
			
		||||
    "
 | 
			
		||||
 | 
			
		||||
  if expr $version \>= 14 > /dev/null ; then
 | 
			
		||||
@@ -147,7 +147,7 @@ if test "x$system" = xSUSE -o "x$system" = "xSUSE LINUX" ; then
 | 
			
		||||
    libgtop-devel libpulse-devel libtiff-devel cups-devel libffi-devel \
 | 
			
		||||
    orbit2-devel libwnck-devel xorg-x11-proto-devel readline-devel \
 | 
			
		||||
    mozilla-xulrunner191-devel libcroco-devel \
 | 
			
		||||
    xorg-x11-devel xorg-x11 xorg-x11-server-extra \
 | 
			
		||||
    xorg-x11-devel xorg-x11 xorg-x11-server-extra evolution-data-server-devel \
 | 
			
		||||
    ; do
 | 
			
		||||
      if ! rpm -q $pkg > /dev/null 2>&1; then
 | 
			
		||||
        reqd="$pkg $reqd"
 | 
			
		||||
@@ -168,7 +168,7 @@ if test "x$system" = xMandrivaLinux ; then
 | 
			
		||||
    intltool ffi5-devel libwnck-1-devel GL-devel ORBit2-devel \
 | 
			
		||||
    readline-devel libxulrunner-devel \
 | 
			
		||||
    libxdamage-devel mesa-demos x11-server-xephyr zenity \
 | 
			
		||||
    libcroco0.6-devel \
 | 
			
		||||
    libcroco0.6-devel libevolution-data-server3-devel \
 | 
			
		||||
    ; do
 | 
			
		||||
      if ! rpm -q --whatprovides $pkg > /dev/null 2>&1; then
 | 
			
		||||
        reqd="$pkg $reqd"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user