Compare commits
	
		
			50 Commits
		
	
	
		
			wip/fmuell
			...
			datetime
		
	
	| 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