Compare commits
	
		
			7 Commits
		
	
	
		
			3.35.92
			...
			155-move-f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 13ddf1eb83 | ||
|   | 15ab33a11a | ||
|   | 5e04f6eb23 | ||
|   | 0dd171a7c8 | ||
|   | 837fbbf417 | ||
|   | d7b61e7281 | ||
|   | 073da0806c | 
| @@ -1,12 +1,19 @@ | ||||
| <node> | ||||
|   <interface name="org.gnome.Shell.CalendarServer"> | ||||
|     <method name="GetEvents"> | ||||
|       <arg type="x" direction="in" /> | ||||
|       <arg type="x" direction="in" /> | ||||
|       <arg type="b" direction="in" /> | ||||
|       <arg type="a(sssbxxa{sv})" direction="out" /> | ||||
|     <method name="SetTimeRange"> | ||||
|       <arg type="x" name="since" direction="in"/> | ||||
|       <arg type="x" name="until" direction="in"/> | ||||
|       <arg type="b" name="force_reload" direction="in"/> | ||||
|     </method> | ||||
|     <signal name="EventsAdded"> | ||||
|       <arg type="a(ssbxxa{sv})" name="events" direction="out"/> | ||||
|     </signal> | ||||
|     <signal name="EventsRemoved"> | ||||
|       <arg type="as" name="ids" direction="out"/> | ||||
|     </signal> | ||||
|     <signal name="ClientDisappeared"> | ||||
|       <arg type="s" name="source_uid" direction="out"/> | ||||
|     </signal> | ||||
|     <property name="HasCalendars" type="b" access="read" /> | ||||
|     <signal name="Changed" /> | ||||
|   </interface> | ||||
| </node> | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| desktop_files = [ | ||||
|   'org.gnome.Shell.desktop', | ||||
|   'org.gnome.Shell.CalendarServer.desktop', | ||||
|   'org.gnome.Extensions.desktop', | ||||
| ] | ||||
| service_files = [] | ||||
| @@ -13,6 +14,7 @@ desktopconf = configuration_data() | ||||
| # We substitute in bindir so it works as an autostart | ||||
| # file when built in a non-system prefix | ||||
| desktopconf.set('bindir', bindir) | ||||
| desktopconf.set('libexecdir', libexecdir) | ||||
| desktopconf.set('systemd_hidden', have_systemd ? 'true' : 'false') | ||||
|  | ||||
| foreach desktop_file : desktop_files | ||||
|   | ||||
							
								
								
									
										9
									
								
								data/org.gnome.Shell.CalendarServer.desktop.in.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								data/org.gnome.Shell.CalendarServer.desktop.in.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| [Desktop Entry] | ||||
| Type=Application | ||||
| Name=Clock Applet | ||||
| Icon=appointment-soon | ||||
| Exec=@libexecdir@/gnome-shell-calendar-server | ||||
| Terminal=false | ||||
| Categories= | ||||
| OnlyShowIn=GNOME | ||||
| NoDisplay=true | ||||
| @@ -220,7 +220,9 @@ class DBusEventSource extends EventSourceBase { | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             this._dbusProxy.connectSignal('Changed', this._onChanged.bind(this)); | ||||
|             this._dbusProxy.connectSignal('EventsAdded', this._onEventsAdded.bind(this)); | ||||
|             this._dbusProxy.connectSignal('EventsRemoved', this._onEventsRemoved.bind(this)); | ||||
|             this._dbusProxy.connectSignal('ClientDisappeared', this._onClientDisappeared.bind(this)); | ||||
|  | ||||
|             this._dbusProxy.connect('notify::g-name-owner', () => { | ||||
|                 if (this._dbusProxy.g_name_owner) | ||||
| @@ -257,7 +259,7 @@ class DBusEventSource extends EventSourceBase { | ||||
|     } | ||||
|  | ||||
|     _resetCache() { | ||||
|         this._events = []; | ||||
|         this._events = new Map(); | ||||
|         this._lastRequestBegin = null; | ||||
|         this._lastRequestEnd = null; | ||||
|     } | ||||
| @@ -273,28 +275,59 @@ class DBusEventSource extends EventSourceBase { | ||||
|         this.emit('changed'); | ||||
|     } | ||||
|  | ||||
|     _onChanged() { | ||||
|         this._loadEvents(false); | ||||
|     } | ||||
|     _onEventsAdded(dbusProxy, nameOwner, argArray) { | ||||
|         let appointments = argArray[0] || []; | ||||
|         let changed = false; | ||||
|  | ||||
|     _onEventsReceived(results, _error) { | ||||
|         let newEvents = []; | ||||
|         let appointments = results[0] || []; | ||||
|         for (let n = 0; n < appointments.length; n++) { | ||||
|             let a = appointments[n]; | ||||
|             let date = new Date(a[4] * 1000); | ||||
|             let end = new Date(a[5] * 1000); | ||||
|             let id = a[0]; | ||||
|             let summary = a[1]; | ||||
|             let allDay = a[3]; | ||||
|             let allDay = a[2]; | ||||
|             let date = new Date(a[3] * 1000); | ||||
|             let end = new Date(a[4] * 1000); | ||||
|             let event = new CalendarEvent(id, date, end, summary, allDay); | ||||
|             newEvents.push(event); | ||||
|         } | ||||
|         newEvents.sort((ev1, ev2) => ev1.date.getTime() - ev2.date.getTime()); | ||||
|             this._events.set(event.id, event); | ||||
|  | ||||
|         this._events = newEvents; | ||||
|         this._isLoading = false; | ||||
|         this.emit('changed'); | ||||
|             changed = true; | ||||
|         } | ||||
|  | ||||
|         if (changed) | ||||
|             this.emit('changed'); | ||||
|     } | ||||
|  | ||||
|     _onEventsRemoved(dbusProxy, nameOwner, argArray) { | ||||
|         let ids = argArray[0] || []; | ||||
|         let changed = false; | ||||
|  | ||||
|         for (let n = 0; n < ids.length; n++) { | ||||
|             let id = ids[n]; | ||||
|  | ||||
|             if (this._events.delete(id)) | ||||
|                 changed = true; | ||||
|         } | ||||
|  | ||||
|         if (changed) | ||||
|             this.emit('changed'); | ||||
|     } | ||||
|  | ||||
|     _onClientDisappeared(dbusProxy, nameOwner, argArray) { | ||||
|         let sourceUid = argArray[0] || ""; | ||||
|         let changed = false; | ||||
|         let idsIter = this._events.keys(); | ||||
|  | ||||
|         sourceUid += '\n'; | ||||
|  | ||||
|         for (let item = idsIter.next(); !item.done; item = idsIter.next()) { | ||||
|             let id = item.value; | ||||
|  | ||||
|             if (id.startsWith(sourceUid) && | ||||
|                 this._events.delete(id)) | ||||
|                 changed = true; | ||||
|         } | ||||
|  | ||||
|         if (changed) | ||||
|             this.emit('changed'); | ||||
|     } | ||||
|  | ||||
|     _loadEvents(forceReload) { | ||||
| @@ -303,32 +336,40 @@ class DBusEventSource extends EventSourceBase { | ||||
|             return; | ||||
|  | ||||
|         if (this._curRequestBegin && this._curRequestEnd) { | ||||
|             this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000, | ||||
|                                             this._curRequestEnd.getTime() / 1000, | ||||
|                                             forceReload, | ||||
|                                             this._onEventsReceived.bind(this), | ||||
|                                             Gio.DBusCallFlags.NONE); | ||||
|             if (forceReload) { | ||||
|                 this._events.clear(); | ||||
|                 this.emit('changed'); | ||||
|             } | ||||
|             this._dbusProxy.SetTimeRangeRemote(this._curRequestBegin.getTime() / 1000, | ||||
|                                                this._curRequestEnd.getTime() / 1000, | ||||
|                                                forceReload, | ||||
|                                                Gio.DBusCallFlags.NONE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     requestRange(begin, end) { | ||||
|         if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) { | ||||
|             this._isLoading = true; | ||||
|             this._lastRequestBegin = begin; | ||||
|             this._lastRequestEnd = end; | ||||
|             this._curRequestBegin = begin; | ||||
|             this._curRequestEnd = end; | ||||
|             this._loadEvents(false); | ||||
|             this._loadEvents(true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     getEvents(begin, end) { | ||||
|     getEvents(begin, end, onlyCheckExistence) { | ||||
|         let result = []; | ||||
|         for (let n = 0; n < this._events.length; n++) { | ||||
|             let event = this._events[n]; | ||||
|         let eventsIter = this._events.values(); | ||||
|  | ||||
|             if (_dateIntervalsOverlap(event.date, event.end, begin, end)) | ||||
|         for (let item = eventsIter.next(); !item.done; item = eventsIter.next()) { | ||||
|             let event = item.value; | ||||
|  | ||||
|             if (_dateIntervalsOverlap(event.date, event.end, begin, end)) { | ||||
|                 result.push(event); | ||||
|  | ||||
|                 if (onlyCheckExistence) | ||||
|                     return result; | ||||
|             } | ||||
|         } | ||||
|         result.sort((event1, event2) => { | ||||
|             // sort events by end time on ending day | ||||
| @@ -343,7 +384,7 @@ class DBusEventSource extends EventSourceBase { | ||||
|         let dayBegin = _getBeginningOfDay(day); | ||||
|         let dayEnd = _getEndOfDay(day); | ||||
|  | ||||
|         let events = this.getEvents(dayBegin, dayEnd); | ||||
|         let events = this.getEvents(dayBegin, dayEnd, true); | ||||
|  | ||||
|         if (events.length == 0) | ||||
|             return false; | ||||
| @@ -873,7 +914,7 @@ class EventsSection extends MessageList.MessageListSection { | ||||
|     } | ||||
|  | ||||
|     _reloadEvents() { | ||||
|         if (this._eventSource.isLoading) | ||||
|         if (this._eventSource.isLoading || this._reloading) | ||||
|             return; | ||||
|  | ||||
|         this._reloading = true; | ||||
| @@ -882,10 +923,7 @@ class EventsSection extends MessageList.MessageListSection { | ||||
|         let periodEnd = _getEndOfDay(this._date); | ||||
|         let events = this._eventSource.getEvents(periodBegin, periodEnd); | ||||
|  | ||||
|         let ids = events.map(e => e.id); | ||||
|         this._messageById.forEach((message, id) => { | ||||
|             if (ids.includes(id)) | ||||
|                 return; | ||||
|             this._messageById.delete(id); | ||||
|             this.removeMessage(message); | ||||
|         }); | ||||
|   | ||||
| @@ -320,7 +320,7 @@ function init() { | ||||
|             _localTimeZone = GLib.TimeZone.new_local(); | ||||
|  | ||||
|         let dt = GLib.DateTime.new(_localTimeZone, | ||||
|             this.getYear(), | ||||
|             this.getFullYear(), | ||||
|             this.getMonth() + 1, | ||||
|             this.getDate(), | ||||
|             this.getHours(), | ||||
|   | ||||
| @@ -19,7 +19,7 @@ cogl_pango_pc = 'mutter-cogl-pango-' + mutter_api_version | ||||
| libmutter_pc = 'libmutter-' + mutter_api_version | ||||
|  | ||||
| ecal_req = '>= 3.33.1' | ||||
| eds_req = '>= 3.17.2' | ||||
| eds_req = '>= 3.33.1' | ||||
| gcr_req = '>= 3.7.5' | ||||
| gio_req = '>= 2.56.0' | ||||
| gi_req = '>= 1.49.1' | ||||
|   | ||||
| @@ -74,6 +74,7 @@ js/ui/windowAttentionHandler.js | ||||
| js/ui/windowManager.js | ||||
| js/ui/windowMenu.js | ||||
| src/calendar-server/evolution-calendar.desktop.in | ||||
| src/calendar-server/reminder-watcher.c | ||||
| src/main.c | ||||
| src/shell-app.c | ||||
| src/shell-app-system.c | ||||
|   | ||||
							
								
								
									
										257
									
								
								po/lt.po
									
									
									
									
									
								
							
							
						
						
									
										257
									
								
								po/lt.po
									
									
									
									
									
								
							| @@ -10,8 +10,8 @@ msgid "" | ||||
| msgstr "" | ||||
| "Project-Id-Version: gnome-shell master\n" | ||||
| "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n" | ||||
| "POT-Creation-Date: 2020-02-17 11:08+0000\n" | ||||
| "PO-Revision-Date: 2020-02-17 22:38+0200\n" | ||||
| "POT-Creation-Date: 2020-02-27 21:57+0000\n" | ||||
| "PO-Revision-Date: 2020-03-01 20:41+0200\n" | ||||
| "Last-Translator: Aurimas Černius <aurisc4@gmail.com>\n" | ||||
| "Language-Team: Lietuvių <gnome-lt@lists.akl.lt>\n" | ||||
| "Language: lt\n" | ||||
| @@ -410,11 +410,11 @@ msgstr "" | ||||
| "Jei pašalinsite plėtinį, reikės grįžti prie jo atsisiuntimo, jei norėsite jį " | ||||
| "vėl įjungti" | ||||
|  | ||||
| #: js/extensionPrefs/main.js:144 js/ui/audioDeviceSelection.js:57 | ||||
| #: js/ui/components/networkAgent.js:107 js/ui/components/polkitAgent.js:139 | ||||
| #: js/ui/endSessionDialog.js:374 js/ui/extensionDownloader.js:166 | ||||
| #: js/ui/shellMountOperation.js:376 js/ui/shellMountOperation.js:386 | ||||
| #: js/ui/status/network.js:910 | ||||
| #: js/extensionPrefs/main.js:144 js/gdm/authPrompt.js:135 | ||||
| #: js/ui/audioDeviceSelection.js:57 js/ui/components/networkAgent.js:107 | ||||
| #: js/ui/components/polkitAgent.js:139 js/ui/endSessionDialog.js:374 | ||||
| #: js/ui/extensionDownloader.js:165 js/ui/shellMountOperation.js:376 | ||||
| #: js/ui/shellMountOperation.js:386 js/ui/status/network.js:913 | ||||
| msgid "Cancel" | ||||
| msgstr "Atsisakyti" | ||||
|  | ||||
| @@ -546,12 +546,11 @@ msgid "Log Out…" | ||||
| msgstr "Atsijungti…" | ||||
|  | ||||
| #. Cisco LEAP | ||||
| #: js/gdm/authPrompt.js:235 js/ui/components/networkAgent.js:202 | ||||
| #: js/ui/components/networkAgent.js:214 js/ui/components/networkAgent.js:238 | ||||
| #: js/ui/components/networkAgent.js:259 js/ui/components/networkAgent.js:279 | ||||
| #: js/ui/components/networkAgent.js:289 js/ui/components/polkitAgent.js:277 | ||||
| #: js/gdm/authPrompt.js:237 js/ui/components/networkAgent.js:202 | ||||
| #: js/ui/components/networkAgent.js:218 js/ui/components/networkAgent.js:242 | ||||
| #: js/ui/components/networkAgent.js:263 js/ui/components/networkAgent.js:283 | ||||
| #: js/ui/components/networkAgent.js:293 js/ui/components/polkitAgent.js:277 | ||||
| #: js/ui/shellMountOperation.js:326 | ||||
| #| msgid "Password:" | ||||
| msgid "Password" | ||||
| msgstr "Slaptažodis" | ||||
|  | ||||
| @@ -573,9 +572,8 @@ msgstr "(pvz., naudotojas arba %s)" | ||||
| #. TTLS and PEAP are actually much more complicated, but this complication | ||||
| #. is not visible here since we only care about phase2 authentication | ||||
| #. (and don't even care of which one) | ||||
| #: js/gdm/loginDialog.js:917 js/ui/components/networkAgent.js:234 | ||||
| #: js/ui/components/networkAgent.js:257 js/ui/components/networkAgent.js:275 | ||||
| #| msgid "Username: " | ||||
| #: js/gdm/loginDialog.js:917 js/ui/components/networkAgent.js:238 | ||||
| #: js/ui/components/networkAgent.js:261 js/ui/components/networkAgent.js:279 | ||||
| msgid "Username" | ||||
| msgstr "Naudotojo vardas" | ||||
|  | ||||
| @@ -810,11 +808,11 @@ msgid "%B %-d %Y, %l∶%M %p" | ||||
| msgstr "%Y %m %-d, %l∶%M %p" | ||||
|  | ||||
| #. TRANSLATORS: this is the title of the wifi captive portal login window | ||||
| #: js/portalHelper/main.js:42 | ||||
| #: js/portalHelper/main.js:41 | ||||
| msgid "Hotspot Login" | ||||
| msgstr "Prisijungimas prie prieigos taško" | ||||
|  | ||||
| #: js/portalHelper/main.js:88 | ||||
| #: js/portalHelper/main.js:87 | ||||
| msgid "" | ||||
| "Your connection to this hotspot login is not secure. Passwords or other " | ||||
| "information you enter on this page can be viewed by people nearby." | ||||
| @@ -849,27 +847,27 @@ msgid "All" | ||||
| msgstr "Visos" | ||||
|  | ||||
| #. Translators: This is the heading of a list of open windows | ||||
| #: js/ui/appDisplay.js:2454 js/ui/panel.js:75 | ||||
| #: js/ui/appDisplay.js:2450 js/ui/panel.js:75 | ||||
| msgid "Open Windows" | ||||
| msgstr "Atverti langai" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2474 js/ui/panel.js:82 | ||||
| #: js/ui/appDisplay.js:2470 js/ui/panel.js:82 | ||||
| msgid "New Window" | ||||
| msgstr "Naujas langas" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2485 | ||||
| #: js/ui/appDisplay.js:2481 | ||||
| msgid "Launch using Dedicated Graphics Card" | ||||
| msgstr "Paleisti naudojant dedikuotą grafikos kortą" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2513 js/ui/dash.js:239 | ||||
| #: js/ui/appDisplay.js:2509 js/ui/dash.js:239 | ||||
| msgid "Remove from Favorites" | ||||
| msgstr "Pašalinti iš mėgstamų" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2519 | ||||
| #: js/ui/appDisplay.js:2515 | ||||
| msgid "Add to Favorites" | ||||
| msgstr "Pridėti prie mėgstamų" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2529 js/ui/panel.js:93 | ||||
| #: js/ui/appDisplay.js:2525 js/ui/panel.js:93 | ||||
| msgid "Show Details" | ||||
| msgstr "Rodyti detalią informaciją" | ||||
|  | ||||
| @@ -1094,37 +1092,32 @@ msgstr "" | ||||
| "Taip pat galite prisijungti paspausdami „WPS“ mygtuką savo maršrutizatoriuje." | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:101 js/ui/status/network.js:223 | ||||
| #: js/ui/status/network.js:314 js/ui/status/network.js:913 | ||||
| #: js/ui/status/network.js:314 js/ui/status/network.js:916 | ||||
| msgid "Connect" | ||||
| msgstr "Prisijungti" | ||||
|  | ||||
| #. static WEP | ||||
| #: js/ui/components/networkAgent.js:207 | ||||
| #| msgid "Key: " | ||||
| #: js/ui/components/networkAgent.js:208 | ||||
| msgid "Key" | ||||
| msgstr "Raktas" | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:242 js/ui/components/networkAgent.js:265 | ||||
| #| msgid "Private key password: " | ||||
| #: js/ui/components/networkAgent.js:246 js/ui/components/networkAgent.js:269 | ||||
| msgid "Private key password" | ||||
| msgstr "Privataus rakto slaptažodis" | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:263 | ||||
| #| msgid "Identity: " | ||||
| #: js/ui/components/networkAgent.js:267 | ||||
| msgid "Identity" | ||||
| msgstr "Tapatybė" | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:277 | ||||
| #| msgid "Service: " | ||||
| #: js/ui/components/networkAgent.js:281 | ||||
| msgid "Service" | ||||
| msgstr "Tarnyba" | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:306 js/ui/components/networkAgent.js:334 | ||||
| #: js/ui/components/networkAgent.js:681 js/ui/components/networkAgent.js:702 | ||||
| #: js/ui/components/networkAgent.js:310 js/ui/components/networkAgent.js:338 | ||||
| #: js/ui/components/networkAgent.js:685 js/ui/components/networkAgent.js:706 | ||||
| msgid "Authentication required" | ||||
| msgstr "Reikia patvirtinti tapatybę" | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:307 js/ui/components/networkAgent.js:682 | ||||
| #: js/ui/components/networkAgent.js:311 js/ui/components/networkAgent.js:686 | ||||
| #, javascript-format | ||||
| msgid "" | ||||
| "Passwords or encryption keys are required to access the wireless network " | ||||
| @@ -1133,44 +1126,42 @@ msgstr "" | ||||
| "Slaptažodžiai arba šifravimo raktai yra būtini priėjimui prie belaidžio " | ||||
| "tinklo „%s“." | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:311 js/ui/components/networkAgent.js:686 | ||||
| #: js/ui/components/networkAgent.js:315 js/ui/components/networkAgent.js:690 | ||||
| msgid "Wired 802.1X authentication" | ||||
| msgstr "Laidinis 802.1X tapatybės patvirtinimas" | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:313 | ||||
| #| msgid "Network name: " | ||||
| #: js/ui/components/networkAgent.js:317 | ||||
| msgid "Network name" | ||||
| msgstr "Tinklo vardas" | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:318 js/ui/components/networkAgent.js:690 | ||||
| #: js/ui/components/networkAgent.js:322 js/ui/components/networkAgent.js:694 | ||||
| msgid "DSL authentication" | ||||
| msgstr "DSL tapatybės patvirtinimas" | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:325 js/ui/components/networkAgent.js:695 | ||||
| #: js/ui/components/networkAgent.js:329 js/ui/components/networkAgent.js:699 | ||||
| msgid "PIN code required" | ||||
| msgstr "Reikalingas PIN kodas" | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:326 js/ui/components/networkAgent.js:696 | ||||
| #: js/ui/components/networkAgent.js:330 js/ui/components/networkAgent.js:700 | ||||
| msgid "PIN code is needed for the mobile broadband device" | ||||
| msgstr "Reikalingas PIN kodas mobiliajam plačiajuosčiam įrenginiui" | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:327 | ||||
| #| msgid "PIN: " | ||||
| #: js/ui/components/networkAgent.js:331 | ||||
| msgid "PIN" | ||||
| msgstr "PIN" | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:335 js/ui/components/networkAgent.js:687 | ||||
| #: js/ui/components/networkAgent.js:691 js/ui/components/networkAgent.js:703 | ||||
| #: js/ui/components/networkAgent.js:707 | ||||
| #: js/ui/components/networkAgent.js:339 js/ui/components/networkAgent.js:691 | ||||
| #: js/ui/components/networkAgent.js:695 js/ui/components/networkAgent.js:707 | ||||
| #: js/ui/components/networkAgent.js:711 | ||||
| #, javascript-format | ||||
| msgid "A password is required to connect to “%s”." | ||||
| msgstr "Būtinas slaptažodis norint prisijungti prie „%s“." | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:670 js/ui/status/network.js:1688 | ||||
| #: js/ui/components/networkAgent.js:674 js/ui/status/network.js:1691 | ||||
| msgid "Network Manager" | ||||
| msgstr "Tinklo tvarkymas" | ||||
|  | ||||
| #: js/ui/components/networkAgent.js:706 | ||||
| #: js/ui/components/networkAgent.js:710 | ||||
| msgid "VPN password" | ||||
| msgstr "VPN slaptažodis" | ||||
|  | ||||
| @@ -1414,15 +1405,15 @@ msgstr "%s (nutolęs)" | ||||
| msgid "%s (console)" | ||||
| msgstr "%s (komandų eilutė)" | ||||
|  | ||||
| #: js/ui/extensionDownloader.js:170 | ||||
| #: js/ui/extensionDownloader.js:169 | ||||
| msgid "Install" | ||||
| msgstr "Įdiegti" | ||||
|  | ||||
| #: js/ui/extensionDownloader.js:176 | ||||
| #: js/ui/extensionDownloader.js:175 | ||||
| msgid "Install Extension" | ||||
| msgstr "Išdiegti plėtinį" | ||||
|  | ||||
| #: js/ui/extensionDownloader.js:177 | ||||
| #: js/ui/extensionDownloader.js:176 | ||||
| #, javascript-format | ||||
| msgid "Download and install “%s” from extensions.gnome.org?" | ||||
| msgstr "Parsiųsti ir įdiegti „%s“ iš extensions.gnome.org?" | ||||
| @@ -1510,13 +1501,13 @@ msgid "Leave On" | ||||
| msgstr "Palikti įjungtą" | ||||
|  | ||||
| #: js/ui/kbdA11yDialog.js:55 js/ui/status/bluetooth.js:135 | ||||
| #: js/ui/status/network.js:1285 | ||||
| #: js/ui/status/network.js:1288 | ||||
| msgid "Turn On" | ||||
| msgstr "Įjungti" | ||||
|  | ||||
| #: js/ui/kbdA11yDialog.js:63 js/ui/status/bluetooth.js:135 | ||||
| #: js/ui/status/network.js:131 js/ui/status/network.js:315 | ||||
| #: js/ui/status/network.js:1285 js/ui/status/network.js:1397 | ||||
| #: js/ui/status/network.js:1288 js/ui/status/network.js:1400 | ||||
| #: js/ui/status/nightLight.js:41 js/ui/status/rfkill.js:81 | ||||
| #: js/ui/status/rfkill.js:108 | ||||
| msgid "Turn Off" | ||||
| @@ -1530,59 +1521,59 @@ msgstr "Palikti išjungtą" | ||||
| msgid "Region & Language Settings" | ||||
| msgstr "Regiono ir kalbos nustatymai" | ||||
|  | ||||
| #: js/ui/lookingGlass.js:659 | ||||
| #: js/ui/lookingGlass.js:665 | ||||
| msgid "No extensions installed" | ||||
| msgstr "Nėra įdiegtų plėtinių" | ||||
|  | ||||
| #. Translators: argument is an extension UUID. | ||||
| #: js/ui/lookingGlass.js:714 | ||||
| #: js/ui/lookingGlass.js:720 | ||||
| #, javascript-format | ||||
| msgid "%s has not emitted any errors." | ||||
| msgstr "%s nepranešė apie jokias klaidas." | ||||
|  | ||||
| #: js/ui/lookingGlass.js:720 | ||||
| #: js/ui/lookingGlass.js:726 | ||||
| msgid "Hide Errors" | ||||
| msgstr "Slėpti klaidas" | ||||
|  | ||||
| #: js/ui/lookingGlass.js:724 js/ui/lookingGlass.js:789 | ||||
| #: js/ui/lookingGlass.js:730 js/ui/lookingGlass.js:795 | ||||
| msgid "Show Errors" | ||||
| msgstr "Rodyti klaidas" | ||||
|  | ||||
| #: js/ui/lookingGlass.js:733 | ||||
| #: js/ui/lookingGlass.js:739 | ||||
| msgid "Enabled" | ||||
| msgstr "Įjungta" | ||||
|  | ||||
| #. translators: | ||||
| #. * The device has been disabled | ||||
| #: js/ui/lookingGlass.js:736 subprojects/gvc/gvc-mixer-control.c:1892 | ||||
| #: js/ui/lookingGlass.js:742 subprojects/gvc/gvc-mixer-control.c:1892 | ||||
| msgid "Disabled" | ||||
| msgstr "Išjungta" | ||||
|  | ||||
| #: js/ui/lookingGlass.js:738 | ||||
| #: js/ui/lookingGlass.js:744 | ||||
| msgid "Error" | ||||
| msgstr "Klaida" | ||||
|  | ||||
| #: js/ui/lookingGlass.js:740 | ||||
| #: js/ui/lookingGlass.js:746 | ||||
| msgid "Out of date" | ||||
| msgstr "Pasenęs" | ||||
|  | ||||
| #: js/ui/lookingGlass.js:742 | ||||
| #: js/ui/lookingGlass.js:748 | ||||
| msgid "Downloading" | ||||
| msgstr "Atsiunčiama" | ||||
|  | ||||
| #: js/ui/lookingGlass.js:771 | ||||
| #: js/ui/lookingGlass.js:777 | ||||
| msgid "View Source" | ||||
| msgstr "Žiūrėti šaltinį" | ||||
|  | ||||
| #: js/ui/lookingGlass.js:780 | ||||
| #: js/ui/lookingGlass.js:786 | ||||
| msgid "Web Page" | ||||
| msgstr "Tinklalapis" | ||||
|  | ||||
| #: js/ui/main.js:267 | ||||
| #: js/ui/main.js:269 | ||||
| msgid "Logged in as a privileged user" | ||||
| msgstr "Prisijungta privilegijuotu naudotoju" | ||||
|  | ||||
| #: js/ui/main.js:268 | ||||
| #: js/ui/main.js:270 | ||||
| msgid "" | ||||
| "Running a session as a privileged user should be avoided for security " | ||||
| "reasons. If possible, you should log in as a normal user." | ||||
| @@ -1590,15 +1581,15 @@ msgstr "" | ||||
| "Saugumo sumetimais turėtų būti vengiama vykdyti seansus privilegijuotais " | ||||
| "naudotojais. Jei įmanoma, turėtumėt visada prisjungti normaliu naudotoju." | ||||
|  | ||||
| #: js/ui/main.js:274 | ||||
| #: js/ui/main.js:276 | ||||
| msgid "Screen Lock disabled" | ||||
| msgstr "Ekrano užraktas išjungtas" | ||||
|  | ||||
| #: js/ui/main.js:275 | ||||
| #: js/ui/main.js:277 | ||||
| msgid "Screen Locking requires the GNOME display manager." | ||||
| msgstr "Ekrano užrakinimas reikalaujas GNOME vaizduoklio valdyklės." | ||||
|  | ||||
| #: js/ui/messageTray.js:1552 | ||||
| #: js/ui/messageTray.js:1554 | ||||
| msgid "System Information" | ||||
| msgstr "Sistemos informacija" | ||||
|  | ||||
| @@ -1625,7 +1616,6 @@ msgstr "Apžvalga" | ||||
| #. active; it should not exceed ~30 | ||||
| #. characters. | ||||
| #: js/ui/overview.js:107 | ||||
| #| msgid "Type to search…" | ||||
| msgid "Type to search" | ||||
| msgstr "Rašykite, ko ieškote" | ||||
|  | ||||
| @@ -1653,23 +1643,23 @@ msgstr "Priskirti klavišų kombinaciją" | ||||
| msgid "Done" | ||||
| msgstr "Atlikta" | ||||
|  | ||||
| #: js/ui/padOsd.js:747 | ||||
| #: js/ui/padOsd.js:745 | ||||
| msgid "Edit…" | ||||
| msgstr "Keisti…" | ||||
|  | ||||
| #: js/ui/padOsd.js:789 js/ui/padOsd.js:912 | ||||
| #: js/ui/padOsd.js:787 js/ui/padOsd.js:910 | ||||
| msgid "None" | ||||
| msgstr "Nėra" | ||||
|  | ||||
| #: js/ui/padOsd.js:865 | ||||
| #: js/ui/padOsd.js:863 | ||||
| msgid "Press a button to configure" | ||||
| msgstr "Spauskite mygtuką konfigūravimui" | ||||
|  | ||||
| #: js/ui/padOsd.js:866 | ||||
| #: js/ui/padOsd.js:864 | ||||
| msgid "Press Esc to exit" | ||||
| msgstr "Spauskit Esc išėjimui" | ||||
|  | ||||
| #: js/ui/padOsd.js:869 | ||||
| #: js/ui/padOsd.js:867 | ||||
| msgid "Press any key to exit" | ||||
| msgstr "Išėjimui spauskite bet kurį klavišą" | ||||
|  | ||||
| @@ -1683,12 +1673,12 @@ msgstr "Užverti" | ||||
| msgid "Activities" | ||||
| msgstr "Apžvalga" | ||||
|  | ||||
| #: js/ui/panel.js:707 | ||||
| #: js/ui/panel.js:713 | ||||
| msgctxt "System menu in the top bar" | ||||
| msgid "System" | ||||
| msgstr "Sistema" | ||||
|  | ||||
| #: js/ui/panel.js:820 | ||||
| #: js/ui/panel.js:826 | ||||
| msgid "Top Bar" | ||||
| msgstr "Viršutinė juosta" | ||||
|  | ||||
| @@ -1719,11 +1709,11 @@ msgstr "GNOME aplinkai reikia užrakinti ekraną" | ||||
| #. | ||||
| #. XXX: another option is to kick the user into the gdm login | ||||
| #. screen, where we're not affected by grabs | ||||
| #: js/ui/screenShield.js:244 js/ui/screenShield.js:602 | ||||
| #: js/ui/screenShield.js:244 js/ui/screenShield.js:598 | ||||
| msgid "Unable to lock" | ||||
| msgstr "Nepavyksta užrakinti" | ||||
|  | ||||
| #: js/ui/screenShield.js:245 js/ui/screenShield.js:603 | ||||
| #: js/ui/screenShield.js:245 js/ui/screenShield.js:599 | ||||
| msgid "Lock was blocked by an application" | ||||
| msgstr "Programa užblokavo užrakinimą" | ||||
|  | ||||
| @@ -1785,7 +1775,6 @@ msgstr "" | ||||
| "įrankį." | ||||
|  | ||||
| #: js/ui/shellMountOperation.js:306 | ||||
| #| msgid "Enter PIM Number…" | ||||
| msgid "PIM Number" | ||||
| msgstr "PIM numeris" | ||||
|  | ||||
| @@ -1867,7 +1856,7 @@ msgstr "Didelis tekstas" | ||||
| msgid "Bluetooth" | ||||
| msgstr "Bluetooth" | ||||
|  | ||||
| #: js/ui/status/bluetooth.js:49 js/ui/status/network.js:590 | ||||
| #: js/ui/status/bluetooth.js:49 js/ui/status/network.js:591 | ||||
| msgid "Bluetooth Settings" | ||||
| msgstr "Bluetooth nustatymai" | ||||
|  | ||||
| @@ -1912,11 +1901,11 @@ msgstr "Antrinis paspaudimas" | ||||
| msgid "Dwell Click" | ||||
| msgstr "Uždelstas paspaudimas" | ||||
|  | ||||
| #: js/ui/status/keyboard.js:825 | ||||
| #: js/ui/status/keyboard.js:826 | ||||
| msgid "Keyboard" | ||||
| msgstr "Klaviatūra" | ||||
|  | ||||
| #: js/ui/status/keyboard.js:847 | ||||
| #: js/ui/status/keyboard.js:848 | ||||
| msgid "Show Keyboard Layout" | ||||
| msgstr "Rodyti klaviatūros išdėstymą" | ||||
|  | ||||
| @@ -1963,7 +1952,7 @@ msgid "<unknown>" | ||||
| msgstr "<nežinoma>" | ||||
|  | ||||
| #. Translators: %s is a network identifier | ||||
| #: js/ui/status/network.js:420 js/ui/status/network.js:1314 | ||||
| #: js/ui/status/network.js:420 js/ui/status/network.js:1317 | ||||
| #, javascript-format | ||||
| msgid "%s Off" | ||||
| msgstr "%s išjungtas" | ||||
| @@ -1989,7 +1978,7 @@ msgid "%s Disconnecting" | ||||
| msgstr "Atsijungiama nuo %s" | ||||
|  | ||||
| #. Translators: %s is a network identifier | ||||
| #: js/ui/status/network.js:438 js/ui/status/network.js:1306 | ||||
| #: js/ui/status/network.js:438 js/ui/status/network.js:1309 | ||||
| #, javascript-format | ||||
| msgid "%s Connecting" | ||||
| msgstr "Jungiamasi prie %s" | ||||
| @@ -2029,7 +2018,7 @@ msgid "Mobile Broadband Settings" | ||||
| msgstr "Mobiliojo plačiajuosčio tinklo nustatymai" | ||||
|  | ||||
| #. Translators: %s is a network identifier | ||||
| #: js/ui/status/network.js:558 js/ui/status/network.js:1311 | ||||
| #: js/ui/status/network.js:558 js/ui/status/network.js:1314 | ||||
| #, javascript-format | ||||
| msgid "%s Hardware Disabled" | ||||
| msgstr "%s aparatinė įranga išjungta" | ||||
| @@ -2041,100 +2030,100 @@ msgstr "%s aparatinė įranga išjungta" | ||||
| msgid "%s Disabled" | ||||
| msgstr "%s išjungtas" | ||||
|  | ||||
| #: js/ui/status/network.js:602 | ||||
| #: js/ui/status/network.js:603 | ||||
| msgid "Connect to Internet" | ||||
| msgstr "Prisijungti prie interneto" | ||||
|  | ||||
| #: js/ui/status/network.js:805 | ||||
| #: js/ui/status/network.js:808 | ||||
| msgid "Airplane Mode is On" | ||||
| msgstr "Skrydžio veiksena įjungta" | ||||
|  | ||||
| #: js/ui/status/network.js:806 | ||||
| #: js/ui/status/network.js:809 | ||||
| msgid "Wi-Fi is disabled when airplane mode is on." | ||||
| msgstr "Belaidis ryšys yra išjungta skrydžio veiksenoje." | ||||
|  | ||||
| #: js/ui/status/network.js:807 | ||||
| #: js/ui/status/network.js:810 | ||||
| msgid "Turn Off Airplane Mode" | ||||
| msgstr "Išjungti skrydžio veikseną" | ||||
|  | ||||
| #: js/ui/status/network.js:816 | ||||
| #: js/ui/status/network.js:819 | ||||
| msgid "Wi-Fi is Off" | ||||
| msgstr "Belaidžio ryšys išjungtas" | ||||
|  | ||||
| #: js/ui/status/network.js:817 | ||||
| #: js/ui/status/network.js:820 | ||||
| msgid "Wi-Fi needs to be turned on in order to connect to a network." | ||||
| msgstr "Norint prisijungti prie tinklo reikia įjungti belaidį ryšį." | ||||
|  | ||||
| #: js/ui/status/network.js:818 | ||||
| #: js/ui/status/network.js:821 | ||||
| msgid "Turn On Wi-Fi" | ||||
| msgstr "Įjungti belaidį ryšį" | ||||
|  | ||||
| #: js/ui/status/network.js:843 | ||||
| #: js/ui/status/network.js:846 | ||||
| msgid "Wi-Fi Networks" | ||||
| msgstr "Wi-Fi tinklai" | ||||
|  | ||||
| #: js/ui/status/network.js:845 | ||||
| #: js/ui/status/network.js:848 | ||||
| msgid "Select a network" | ||||
| msgstr "Pasirinkite tinklą" | ||||
|  | ||||
| #: js/ui/status/network.js:877 | ||||
| #: js/ui/status/network.js:880 | ||||
| msgid "No Networks" | ||||
| msgstr "Nėra tinklų" | ||||
|  | ||||
| #: js/ui/status/network.js:898 js/ui/status/rfkill.js:106 | ||||
| #: js/ui/status/network.js:901 js/ui/status/rfkill.js:106 | ||||
| msgid "Use hardware switch to turn off" | ||||
| msgstr "Išjungimui naudoti aparatinį jungiklį" | ||||
|  | ||||
| #: js/ui/status/network.js:1175 | ||||
| #: js/ui/status/network.js:1178 | ||||
| msgid "Select Network" | ||||
| msgstr "Pasirinkite tinklą" | ||||
|  | ||||
| #: js/ui/status/network.js:1181 | ||||
| #: js/ui/status/network.js:1184 | ||||
| msgid "Wi-Fi Settings" | ||||
| msgstr "Belaidžio ryšio nustatymai" | ||||
|  | ||||
| #. Translators: %s is a network identifier | ||||
| #: js/ui/status/network.js:1302 | ||||
| #: js/ui/status/network.js:1305 | ||||
| #, javascript-format | ||||
| msgid "%s Hotspot Active" | ||||
| msgstr "Prieigos taškas %s aktyvus" | ||||
|  | ||||
| #. Translators: %s is a network identifier | ||||
| #: js/ui/status/network.js:1317 | ||||
| #: js/ui/status/network.js:1320 | ||||
| #, javascript-format | ||||
| msgid "%s Not Connected" | ||||
| msgstr "Neprisijungta prie %s" | ||||
|  | ||||
| #: js/ui/status/network.js:1414 | ||||
| #: js/ui/status/network.js:1417 | ||||
| msgid "connecting…" | ||||
| msgstr "jungiamasi…" | ||||
|  | ||||
| #. Translators: this is for network connections that require some kind of key or password | ||||
| #: js/ui/status/network.js:1417 | ||||
| #: js/ui/status/network.js:1420 | ||||
| msgid "authentication required" | ||||
| msgstr "reikia patvirtinti tapatybę" | ||||
|  | ||||
| #: js/ui/status/network.js:1419 | ||||
| #: js/ui/status/network.js:1422 | ||||
| msgid "connection failed" | ||||
| msgstr "nepavyko prisijungti" | ||||
|  | ||||
| #: js/ui/status/network.js:1470 | ||||
| #: js/ui/status/network.js:1473 | ||||
| msgid "VPN Settings" | ||||
| msgstr "VPN nustatymai" | ||||
|  | ||||
| #: js/ui/status/network.js:1487 | ||||
| #: js/ui/status/network.js:1490 | ||||
| msgid "VPN" | ||||
| msgstr "VPN" | ||||
|  | ||||
| #: js/ui/status/network.js:1497 | ||||
| #: js/ui/status/network.js:1500 | ||||
| msgid "VPN Off" | ||||
| msgstr "VPN išjungtas" | ||||
|  | ||||
| #: js/ui/status/network.js:1558 js/ui/status/rfkill.js:84 | ||||
| #: js/ui/status/network.js:1561 js/ui/status/rfkill.js:84 | ||||
| msgid "Network Settings" | ||||
| msgstr "Tinklo nustatymai" | ||||
|  | ||||
| #: js/ui/status/network.js:1587 | ||||
| #: js/ui/status/network.js:1590 | ||||
| #, javascript-format | ||||
| msgid "%s Wired Connection" | ||||
| msgid_plural "%s Wired Connections" | ||||
| @@ -2142,7 +2131,7 @@ msgstr[0] "%s laidinis ryšys" | ||||
| msgstr[1] "%s laidiniai ryšiai" | ||||
| msgstr[2] "%s laidinių ryšių" | ||||
|  | ||||
| #: js/ui/status/network.js:1591 | ||||
| #: js/ui/status/network.js:1594 | ||||
| #, javascript-format | ||||
| msgid "%s Wi-Fi Connection" | ||||
| msgid_plural "%s Wi-Fi Connections" | ||||
| @@ -2150,7 +2139,7 @@ msgstr[0] "%s belaidis ryšys" | ||||
| msgstr[1] "%s belaidžiai ryšiai" | ||||
| msgstr[2] "%s belaidžių ryšių" | ||||
|  | ||||
| #: js/ui/status/network.js:1595 | ||||
| #: js/ui/status/network.js:1598 | ||||
| #, javascript-format | ||||
| msgid "%s Modem Connection" | ||||
| msgid_plural "%s Modem Connections" | ||||
| @@ -2158,11 +2147,11 @@ msgstr[0] "%s modemo ryšys" | ||||
| msgstr[1] "%s modemo ryšiai" | ||||
| msgstr[2] "%s modemo ryšių" | ||||
|  | ||||
| #: js/ui/status/network.js:1729 | ||||
| #: js/ui/status/network.js:1732 | ||||
| msgid "Connection failed" | ||||
| msgstr "Nepavyko prisijungti" | ||||
|  | ||||
| #: js/ui/status/network.js:1730 | ||||
| #: js/ui/status/network.js:1733 | ||||
| msgid "Activation of network connection failed" | ||||
| msgstr "Tinklo ryšio nepavyko aktyvuoti" | ||||
|  | ||||
| @@ -2260,11 +2249,11 @@ msgstr "Išjungti…" | ||||
| msgid "Thunderbolt" | ||||
| msgstr "Thunderbolt" | ||||
|  | ||||
| #: js/ui/status/thunderbolt.js:324 | ||||
| #: js/ui/status/thunderbolt.js:325 | ||||
| msgid "Unknown Thunderbolt device" | ||||
| msgstr "Nežinoma Thunderbolt įrenginys" | ||||
|  | ||||
| #: js/ui/status/thunderbolt.js:325 | ||||
| #: js/ui/status/thunderbolt.js:326 | ||||
| msgid "" | ||||
| "New device has been detected while you were away. Please disconnect and " | ||||
| "reconnect the device to start using it." | ||||
| @@ -2272,20 +2261,20 @@ msgstr "" | ||||
| "Jums nesant aptiktas naujas įrenginys. Atjunkite ir vėl prijunkite įrenginį, " | ||||
| "jei norite jį naudoti." | ||||
|  | ||||
| #: js/ui/status/thunderbolt.js:328 | ||||
| #: js/ui/status/thunderbolt.js:329 | ||||
| msgid "Unauthorized Thunderbolt device" | ||||
| msgstr "Neleidžiamas Thunderbolt įrenginys" | ||||
|  | ||||
| #: js/ui/status/thunderbolt.js:329 | ||||
| #: js/ui/status/thunderbolt.js:330 | ||||
| msgid "" | ||||
| "New device has been detected and needs to be authorized by an administrator." | ||||
| msgstr "Buvo aptiktas naujas įrenginys, kurį turi leisti administratorius." | ||||
|  | ||||
| #: js/ui/status/thunderbolt.js:335 | ||||
| #: js/ui/status/thunderbolt.js:336 | ||||
| msgid "Thunderbolt authorization error" | ||||
| msgstr "Thunderbolt autorizacijos klaida" | ||||
|  | ||||
| #: js/ui/status/thunderbolt.js:336 | ||||
| #: js/ui/status/thunderbolt.js:337 | ||||
| #, javascript-format | ||||
| msgid "Could not authorize the Thunderbolt device: %s" | ||||
| msgstr "Nepavyko autorizuoti Thunderbolt įrenginio: %s" | ||||
| @@ -2326,10 +2315,30 @@ msgstr "Tik išorinis" | ||||
| msgid "Built-in Only" | ||||
| msgstr "Tik vidinis" | ||||
|  | ||||
| #: js/ui/unlockDialog.js:552 | ||||
| #. Translators: This is a time format for a date in | ||||
| #. long format | ||||
| #: js/ui/unlockDialog.js:370 | ||||
| #| msgctxt "calendar heading" | ||||
| #| msgid "%A, %B %-d" | ||||
| msgid "%A %B %-d" | ||||
| msgstr "%A %B %-d" | ||||
|  | ||||
| #: js/ui/unlockDialog.js:376 | ||||
| msgid "Swipe up to unlock" | ||||
| msgstr "Atrakinkite perbraukdami" | ||||
|  | ||||
| #: js/ui/unlockDialog.js:377 | ||||
| msgid "Click or press a key to unlock" | ||||
| msgstr "Atrakinkite mygtuko ar klavišo paspaudimu" | ||||
|  | ||||
| #: js/ui/unlockDialog.js:549 | ||||
| msgid "Unlock Window" | ||||
| msgstr "Atrakinimo langas" | ||||
|  | ||||
| #: js/ui/unlockDialog.js:558 | ||||
| msgid "Log in as another user" | ||||
| msgstr "Prisijungti kitu naudotoju" | ||||
|  | ||||
| #: js/ui/viewSelector.js:181 | ||||
| msgid "Applications" | ||||
| msgstr "Programos" | ||||
| @@ -2369,7 +2378,7 @@ msgstr[2] "Pakeitimai bus grąžinti po %d sekundžių" | ||||
|  | ||||
| #. Translators: This represents the size of a window. The first number is | ||||
| #. * the width of the window and the second is the height. | ||||
| #: js/ui/windowManager.js:542 | ||||
| #: js/ui/windowManager.js:544 | ||||
| #, javascript-format | ||||
| msgid "%d × %d" | ||||
| msgstr "%d × %d" | ||||
| @@ -2630,7 +2639,6 @@ msgid "Show extensions with preferences" | ||||
| msgstr "Rodyti plėtinius su nuostatomis" | ||||
|  | ||||
| #: subprojects/extensions-tool/src/command-list.c:140 | ||||
| #| msgid "Show extensions with preferences" | ||||
| msgid "Show extensions with updates" | ||||
| msgstr "Rodyti plėtinius su atnaujinimais" | ||||
|  | ||||
| @@ -2851,9 +2859,6 @@ msgstr "Sistemos garsai" | ||||
| #~ msgstr[1] "%d nauji pranešimai" | ||||
| #~ msgstr[2] "%d naujų pranešimų" | ||||
|  | ||||
| #~ msgid "Log in as another user" | ||||
| #~ msgstr "Prisijungti kitu naudotoju" | ||||
|  | ||||
| #~ msgid "Logout…" | ||||
| #~ msgstr "Atsijungti…" | ||||
|  | ||||
|   | ||||
| @@ -43,20 +43,7 @@ struct _ClientData | ||||
| { | ||||
|   ECalClient *client; | ||||
|   gulong backend_died_id; | ||||
| }; | ||||
|  | ||||
| struct _CalendarSourceData | ||||
| { | ||||
|   ECalClientSourceType source_type; | ||||
|   CalendarSources *sources; | ||||
|   guint            changed_signal; | ||||
|  | ||||
|   /* ESource -> EClient */ | ||||
|   GHashTable      *clients; | ||||
|  | ||||
|   guint            timeout_id; | ||||
|  | ||||
|   guint            loaded : 1; | ||||
|   gboolean is_for_events; /* Because this can hold other clients too (for EReminderWatcher) */ | ||||
| }; | ||||
|  | ||||
| typedef struct _CalendarSourcesPrivate CalendarSourcesPrivate; | ||||
| @@ -69,175 +56,150 @@ struct _CalendarSources | ||||
|  | ||||
| struct _CalendarSourcesPrivate | ||||
| { | ||||
|   ESourceRegistry    *registry; | ||||
|   gulong              source_added_id; | ||||
|   gulong              source_changed_id; | ||||
|   gulong              source_removed_id; | ||||
|   ESourceRegistryWatcher *registry_watcher; | ||||
|   gulong                  filter_id; | ||||
|   gulong                  appeared_id; | ||||
|   gulong                  disappeared_id; | ||||
|  | ||||
|   CalendarSourceData  appointment_sources; | ||||
|   CalendarSourceData  task_sources; | ||||
|   GMutex                  clients_lock; | ||||
|   GHashTable             *clients; /* ESource -> ClientData */ | ||||
| }; | ||||
|  | ||||
| G_DEFINE_TYPE_WITH_PRIVATE (CalendarSources, calendar_sources, G_TYPE_OBJECT) | ||||
|  | ||||
| static void calendar_sources_finalize   (GObject             *object); | ||||
|  | ||||
| static void backend_died_cb (EClient *client, CalendarSourceData *source_data); | ||||
| static void calendar_sources_registry_source_changed_cb (ESourceRegistry *registry, | ||||
|                                                          ESource         *source, | ||||
|                                                          CalendarSources *sources); | ||||
| static void calendar_sources_registry_source_removed_cb (ESourceRegistry *registry, | ||||
|                                                          ESource         *source, | ||||
|                                                          CalendarSources *sources); | ||||
|  | ||||
| enum | ||||
| { | ||||
|   APPOINTMENT_SOURCES_CHANGED, | ||||
|   TASK_SOURCES_CHANGED, | ||||
|   CLIENT_APPEARED, | ||||
|   CLIENT_DISAPPEARED, | ||||
|   LAST_SIGNAL | ||||
| }; | ||||
| static guint signals [LAST_SIGNAL] = { 0, }; | ||||
|  | ||||
| static GObjectClass    *parent_class = NULL; | ||||
| static CalendarSources *calendar_sources_singleton = NULL; | ||||
| static void | ||||
| calendar_sources_client_connected_cb (GObject *source_object, | ||||
|                                       GAsyncResult *result, | ||||
|                                       gpointer user_data) | ||||
| { | ||||
|   CalendarSources *sources = CALENDAR_SOURCES (source_object); | ||||
|   ESource *source = user_data; | ||||
|   EClient *client; | ||||
|   GError *error = NULL; | ||||
|  | ||||
|   /* The calendar_sources_connect_client_sync() already stored the 'client' | ||||
|    * into the priv->clients */ | ||||
|   client = calendar_sources_connect_client_finish (sources, result, &error); | ||||
|   if (error) | ||||
|     { | ||||
|       g_warning ("Could not load source '%s': %s", | ||||
|                  e_source_get_uid (source), | ||||
|                  error->message); | ||||
|       g_clear_error (&error); | ||||
|     } | ||||
|    else | ||||
|     { | ||||
|       g_signal_emit (sources, signals[CLIENT_APPEARED], 0, client, NULL); | ||||
|     } | ||||
|  | ||||
|   g_clear_object (&client); | ||||
|   g_clear_object (&source); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| registry_watcher_filter_cb (ESourceRegistryWatcher *watcher, | ||||
|                             ESource *source, | ||||
|                             CalendarSources *sources) | ||||
| { | ||||
|   return e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR) && | ||||
|          e_source_selectable_get_selected (e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| registry_watcher_source_appeared_cb (ESourceRegistryWatcher *watcher, | ||||
|                                      ESource *source, | ||||
|                                      CalendarSources *sources) | ||||
| { | ||||
|   ECalClientSourceType source_type; | ||||
|  | ||||
|   if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) | ||||
|     source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS; | ||||
|   else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) | ||||
|     source_type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS; | ||||
|   else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) | ||||
|     source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS; | ||||
|   else | ||||
|     g_return_if_reached (); | ||||
|  | ||||
|   calendar_sources_connect_client (sources, TRUE, source, source_type, 30, NULL, calendar_sources_client_connected_cb, g_object_ref (source)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| registry_watcher_source_disappeared_cb (ESourceRegistryWatcher *watcher, | ||||
|                                         ESource *source, | ||||
|                                         CalendarSources *sources) | ||||
| { | ||||
|   gboolean emit; | ||||
|  | ||||
|   g_mutex_lock (&sources->priv->clients_lock); | ||||
|  | ||||
|   emit = g_hash_table_remove (sources->priv->clients, source); | ||||
|  | ||||
|   g_mutex_unlock (&sources->priv->clients_lock); | ||||
|  | ||||
|   if (emit) | ||||
|     g_signal_emit (sources, signals[CLIENT_DISAPPEARED], 0, e_source_get_uid (source), NULL); | ||||
| } | ||||
|  | ||||
| static void | ||||
| client_data_free (ClientData *data) | ||||
| { | ||||
|   g_clear_signal_handler (&data->backend_died_id, data->client); | ||||
|   g_signal_handler_disconnect (data->client, data->backend_died_id); | ||||
|   g_object_unref (data->client); | ||||
|   g_slice_free (ClientData, data); | ||||
| } | ||||
|  | ||||
| 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; | ||||
|  | ||||
|   signals [APPOINTMENT_SOURCES_CHANGED] = | ||||
|     g_signal_new ("appointment-sources-changed", | ||||
| 		  G_TYPE_FROM_CLASS (gobject_class), | ||||
| 		  G_SIGNAL_RUN_LAST, | ||||
| 		  0, | ||||
| 		  NULL, | ||||
| 		  NULL, | ||||
|                   NULL, | ||||
| 		  G_TYPE_NONE, | ||||
| 		  0); | ||||
|  | ||||
|   signals [TASK_SOURCES_CHANGED] = | ||||
|     g_signal_new ("task-sources-changed", | ||||
| 		  G_TYPE_FROM_CLASS (gobject_class), | ||||
| 		  G_SIGNAL_RUN_LAST, | ||||
| 		  0, | ||||
| 		  NULL, | ||||
| 		  NULL, | ||||
|                   NULL, | ||||
| 		  G_TYPE_NONE, | ||||
| 		  0); | ||||
| } | ||||
|  | ||||
| static void | ||||
| calendar_sources_init (CalendarSources *sources) | ||||
| calendar_sources_constructed (GObject *object) | ||||
| { | ||||
|   CalendarSources *sources = CALENDAR_SOURCES (object); | ||||
|   ESourceRegistry *registry = NULL; | ||||
|   GError *error = NULL; | ||||
|   GDBusConnection *session_bus; | ||||
|   GVariant *result; | ||||
|  | ||||
|   sources->priv = calendar_sources_get_instance_private (sources); | ||||
|  | ||||
|   /* WORKAROUND: the hardcoded timeout for e_source_registry_new_sync() | ||||
|      (and other library calls that eventually call g_dbus_proxy_new[_sync]()) | ||||
|      is 25 seconds. This has been shown to be too small for | ||||
|      evolution-source-registry in certain cases (slow disk, concurrent IO, | ||||
|      many configured sources), so we first ensure that the service | ||||
|      starts with a manual call and a higher timeout. | ||||
|  | ||||
|      HACK: every time the DBus API is bumped in e-d-s we need | ||||
|      to update this! | ||||
|   */ | ||||
|   session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); | ||||
|   if (session_bus == NULL) | ||||
|     { | ||||
|       g_error ("Failed to connect to the session bus: %s", error->message); | ||||
|     } | ||||
|  | ||||
|   result = g_dbus_connection_call_sync (session_bus, "org.freedesktop.DBus", | ||||
|                                         "/", "org.freedesktop.DBus", | ||||
|                                         "StartServiceByName", | ||||
|                                         g_variant_new ("(su)", | ||||
|                                                        "org.gnome.evolution.dataserver.Sources5", | ||||
|                                                        0), | ||||
|                                         NULL, | ||||
|                                         G_DBUS_CALL_FLAGS_NONE, | ||||
|                                         60 * 1000, | ||||
|                                         NULL, &error); | ||||
|   if (result != NULL) | ||||
|     { | ||||
|       g_variant_unref (result); | ||||
|       sources->priv->registry = e_source_registry_new_sync (NULL, &error); | ||||
|     } | ||||
|   G_OBJECT_CLASS (calendar_sources_parent_class)->constructed (object); | ||||
|  | ||||
|   registry = e_source_registry_new_sync (NULL, &error); | ||||
|   if (error != NULL) | ||||
|     { | ||||
|       /* Any error is fatal, but we don't want to crash gnome-shell-calendar-server | ||||
|          because of e-d-s problems. So just exit here. | ||||
|       */ | ||||
|       g_warning ("Failed to start evolution-source-registry: %s", error->message); | ||||
|       exit(EXIT_FAILURE); | ||||
|       exit (EXIT_FAILURE); | ||||
|     } | ||||
|  | ||||
|   g_object_unref (session_bus); | ||||
|   g_return_if_fail (registry != NULL); | ||||
|  | ||||
|   sources->priv->source_added_id   = g_signal_connect (sources->priv->registry, | ||||
|                                                        "source-added", | ||||
|                                                        G_CALLBACK (calendar_sources_registry_source_changed_cb), | ||||
|                                                        sources); | ||||
|   sources->priv->source_changed_id = g_signal_connect (sources->priv->registry, | ||||
|                                                        "source-changed", | ||||
|                                                        G_CALLBACK (calendar_sources_registry_source_changed_cb), | ||||
|                                                        sources); | ||||
|   sources->priv->source_removed_id = g_signal_connect (sources->priv->registry, | ||||
|                                                        "source-removed", | ||||
|                                                        G_CALLBACK (calendar_sources_registry_source_removed_cb), | ||||
|                                                        sources); | ||||
|   sources->priv->registry_watcher = e_source_registry_watcher_new (registry, NULL); | ||||
|  | ||||
|   sources->priv->appointment_sources.source_type    = E_CAL_CLIENT_SOURCE_TYPE_EVENTS; | ||||
|   sources->priv->appointment_sources.sources        = sources; | ||||
|   sources->priv->appointment_sources.changed_signal = signals [APPOINTMENT_SOURCES_CHANGED]; | ||||
|   sources->priv->appointment_sources.clients        = g_hash_table_new_full ((GHashFunc) e_source_hash, | ||||
|                                                                              (GEqualFunc) e_source_equal, | ||||
|                                                                              (GDestroyNotify) g_object_unref, | ||||
|                                                                              (GDestroyNotify) client_data_free); | ||||
|   sources->priv->appointment_sources.timeout_id     = 0; | ||||
|   g_clear_object (®istry); | ||||
|  | ||||
|   sources->priv->task_sources.source_type    = E_CAL_CLIENT_SOURCE_TYPE_TASKS; | ||||
|   sources->priv->task_sources.sources        = sources; | ||||
|   sources->priv->task_sources.changed_signal = signals [TASK_SOURCES_CHANGED]; | ||||
|   sources->priv->task_sources.clients        = g_hash_table_new_full ((GHashFunc) e_source_hash, | ||||
|                                                                       (GEqualFunc) e_source_equal, | ||||
|                                                                       (GDestroyNotify) g_object_unref, | ||||
|                                                                       (GDestroyNotify) client_data_free); | ||||
|   sources->priv->task_sources.timeout_id     = 0; | ||||
| } | ||||
|   sources->priv->clients = g_hash_table_new_full ((GHashFunc) e_source_hash, | ||||
|                                                   (GEqualFunc) e_source_equal, | ||||
|                                                   (GDestroyNotify) g_object_unref, | ||||
|                                                   (GDestroyNotify) client_data_free); | ||||
|   sources->priv->filter_id      = g_signal_connect (sources->priv->registry_watcher, | ||||
|                                                     "filter", | ||||
|                                                     G_CALLBACK (registry_watcher_filter_cb), | ||||
|                                                     sources); | ||||
|   sources->priv->appeared_id    = g_signal_connect (sources->priv->registry_watcher, | ||||
|                                                     "appeared", | ||||
|                                                     G_CALLBACK (registry_watcher_source_appeared_cb), | ||||
|                                                     sources); | ||||
|   sources->priv->disappeared_id = g_signal_connect (sources->priv->registry_watcher, | ||||
|                                                     "disappeared", | ||||
|                                                     G_CALLBACK (registry_watcher_source_disappeared_cb), | ||||
|                                                     sources); | ||||
|  | ||||
| static void | ||||
| calendar_sources_finalize_source_data (CalendarSources    *sources, | ||||
| 				       CalendarSourceData *source_data) | ||||
| { | ||||
|   if (source_data->loaded) | ||||
|     { | ||||
|       g_hash_table_destroy (source_data->clients); | ||||
|       source_data->clients = NULL; | ||||
|  | ||||
|       g_clear_handle_id (&source_data->timeout_id, g_source_remove); | ||||
|  | ||||
|       source_data->loaded = FALSE; | ||||
|     } | ||||
|   e_source_registry_watcher_reclaim (sources->priv->registry_watcher); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -245,28 +207,73 @@ calendar_sources_finalize (GObject *object) | ||||
| { | ||||
|   CalendarSources *sources = CALENDAR_SOURCES (object); | ||||
|  | ||||
|   if (sources->priv->registry) | ||||
|   if (sources->priv->clients) | ||||
|     { | ||||
|       g_clear_signal_handler (&sources->priv->source_added_id, | ||||
|                               sources->priv->registry); | ||||
|       g_clear_signal_handler (&sources->priv->source_changed_id, | ||||
|                               sources->priv->registry); | ||||
|       g_clear_signal_handler (&sources->priv->source_removed_id, | ||||
|                               sources->priv->registry); | ||||
|       g_object_unref (sources->priv->registry); | ||||
|       g_hash_table_destroy (sources->priv->clients); | ||||
|       sources->priv->clients = NULL; | ||||
|     } | ||||
|   sources->priv->registry = NULL; | ||||
|  | ||||
|   calendar_sources_finalize_source_data (sources, &sources->priv->appointment_sources); | ||||
|   calendar_sources_finalize_source_data (sources, &sources->priv->task_sources); | ||||
|   if (sources->priv->registry_watcher) | ||||
|     { | ||||
|       g_signal_handler_disconnect (sources->priv->registry_watcher, | ||||
|                                    sources->priv->filter_id); | ||||
|       g_signal_handler_disconnect (sources->priv->registry_watcher, | ||||
|                                    sources->priv->appeared_id); | ||||
|       g_signal_handler_disconnect (sources->priv->registry_watcher, | ||||
|                                    sources->priv->disappeared_id); | ||||
|       g_clear_object (&sources->priv->registry_watcher); | ||||
|     } | ||||
|  | ||||
|   if (G_OBJECT_CLASS (parent_class)->finalize) | ||||
|     G_OBJECT_CLASS (parent_class)->finalize (object); | ||||
|   g_mutex_clear (&sources->priv->clients_lock); | ||||
|  | ||||
|   G_OBJECT_CLASS (calendar_sources_parent_class)->finalize (object); | ||||
| } | ||||
|  | ||||
| static void | ||||
| calendar_sources_class_init (CalendarSourcesClass *klass) | ||||
| { | ||||
|   GObjectClass *gobject_class = (GObjectClass *) klass; | ||||
|  | ||||
|   gobject_class->constructed = calendar_sources_constructed; | ||||
|   gobject_class->finalize = calendar_sources_finalize; | ||||
|  | ||||
|   signals [CLIENT_APPEARED] = | ||||
|     g_signal_new ("client-appeared", | ||||
|                   G_TYPE_FROM_CLASS (gobject_class), | ||||
|                   G_SIGNAL_RUN_LAST, | ||||
|                   0, | ||||
|                   NULL, | ||||
|                   NULL, | ||||
|                   NULL, | ||||
|                   G_TYPE_NONE, | ||||
|                   1, | ||||
|                   E_TYPE_CAL_CLIENT); | ||||
|  | ||||
|   signals [CLIENT_DISAPPEARED] = | ||||
|     g_signal_new ("client-disappeared", | ||||
|                   G_TYPE_FROM_CLASS (gobject_class), | ||||
|                   G_SIGNAL_RUN_LAST, | ||||
|                   0, | ||||
|                   NULL, | ||||
|                   NULL, | ||||
|                   NULL, | ||||
|                   G_TYPE_NONE, | ||||
|                   1, | ||||
|                   G_TYPE_STRING); /* ESource::uid of the disappeared client */ | ||||
| } | ||||
|  | ||||
| static void | ||||
| calendar_sources_init (CalendarSources *sources) | ||||
| { | ||||
|   sources->priv = calendar_sources_get_instance_private (sources); | ||||
|  | ||||
|   g_mutex_init (&sources->priv->clients_lock); | ||||
| } | ||||
|  | ||||
| CalendarSources * | ||||
| calendar_sources_get (void) | ||||
| { | ||||
|   static CalendarSources *calendar_sources_singleton = NULL; | ||||
|   gpointer singleton_location = &calendar_sources_singleton; | ||||
|  | ||||
|   if (calendar_sources_singleton) | ||||
| @@ -274,85 +281,70 @@ calendar_sources_get (void) | ||||
|  | ||||
|   calendar_sources_singleton = g_object_new (CALENDAR_TYPE_SOURCES, NULL); | ||||
|   g_object_add_weak_pointer (G_OBJECT (calendar_sources_singleton), | ||||
| 			     singleton_location); | ||||
|                              singleton_location); | ||||
|  | ||||
|   return calendar_sources_singleton; | ||||
| } | ||||
|  | ||||
| /* The clients are just created here but not loaded */ | ||||
| static void | ||||
| create_client_for_source (ESource              *source, | ||||
| 		          ECalClientSourceType  source_type, | ||||
| 		          CalendarSourceData   *source_data) | ||||
| ESourceRegistry * | ||||
| calendar_sources_get_registry (CalendarSources *sources) | ||||
| { | ||||
|   ClientData *data; | ||||
|   EClient *client; | ||||
|   GError *error = NULL; | ||||
|  | ||||
|   client = g_hash_table_lookup (source_data->clients, source); | ||||
|   g_return_if_fail (client == NULL); | ||||
|  | ||||
|   client = e_cal_client_connect_sync (source, source_type, -1, NULL, &error); | ||||
|   if (!client) | ||||
|     { | ||||
|       g_warning ("Could not load source '%s': %s", | ||||
| 		 e_source_get_uid (source), | ||||
| 		 error->message); | ||||
|       g_clear_error(&error); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   data = g_slice_new0 (ClientData); | ||||
|   data->client = E_CAL_CLIENT (client);  /* takes ownership */ | ||||
|   data->backend_died_id = g_signal_connect (client, | ||||
|                                             "backend-died", | ||||
|                                             G_CALLBACK (backend_died_cb), | ||||
|                                             source_data); | ||||
|  | ||||
|   g_hash_table_insert (source_data->clients, g_object_ref (source), data); | ||||
| } | ||||
|  | ||||
| static inline void | ||||
| debug_dump_ecal_list (GHashTable *clients) | ||||
| { | ||||
| #ifdef CALENDAR_ENABLE_DEBUG | ||||
|   GList *list, *link; | ||||
|  | ||||
|   dprintf ("Loaded clients:\n"); | ||||
|   list = g_hash_table_get_keys (clients); | ||||
|   for (link = list; link != NULL; link = g_list_next (link)) | ||||
|     { | ||||
|       ESource *source = E_SOURCE (link->data); | ||||
|  | ||||
|       dprintf ("  %s %s\n", | ||||
| 	       e_source_get_uid (source), | ||||
| 	       e_source_get_display_name (source)); | ||||
|     } | ||||
|   g_list_free (list); | ||||
| #endif | ||||
|   return e_source_registry_watcher_get_registry (sources->priv->registry_watcher); | ||||
| } | ||||
|  | ||||
| static void | ||||
| calendar_sources_load_esource_list (ESourceRegistry *registry, | ||||
|                                     CalendarSourceData *source_data); | ||||
|  | ||||
| static gboolean | ||||
| backend_restart (gpointer data) | ||||
| gather_event_clients_cb (gpointer key, | ||||
|                          gpointer value, | ||||
|                          gpointer user_data) | ||||
| { | ||||
|   CalendarSourceData *source_data = data; | ||||
|   ESourceRegistry *registry; | ||||
|   GSList **plist = user_data; | ||||
|   ClientData *cd = value; | ||||
|  | ||||
|   registry = source_data->sources->priv->registry; | ||||
|   calendar_sources_load_esource_list (registry, source_data); | ||||
|   g_signal_emit (source_data->sources, source_data->changed_signal, 0); | ||||
|   if (cd && cd->is_for_events) | ||||
|     *plist = g_slist_prepend (*plist, g_object_ref (cd->client)); | ||||
| } | ||||
|  | ||||
|   source_data->timeout_id = 0; | ||||
|      | ||||
|   return FALSE; | ||||
| GSList * | ||||
| calendar_sources_ref_clients (CalendarSources *sources) | ||||
| { | ||||
|   GSList *list = NULL; | ||||
|  | ||||
|   g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL); | ||||
|  | ||||
|   g_mutex_lock (&sources->priv->clients_lock); | ||||
|   g_hash_table_foreach (sources->priv->clients, gather_event_clients_cb, &list); | ||||
|   g_mutex_unlock (&sources->priv->clients_lock); | ||||
|  | ||||
|   return list; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| calendar_sources_has_clients (CalendarSources *sources) | ||||
| { | ||||
|   GHashTableIter iter; | ||||
|   gpointer value; | ||||
|   gboolean has = FALSE; | ||||
|  | ||||
|   g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), FALSE); | ||||
|  | ||||
|   g_mutex_lock (&sources->priv->clients_lock); | ||||
|  | ||||
|   g_hash_table_iter_init (&iter, sources->priv->clients); | ||||
|   while (!has && g_hash_table_iter_next (&iter, NULL, &value)) | ||||
|    { | ||||
|      ClientData *cd = value; | ||||
|  | ||||
|      has = cd && cd->is_for_events; | ||||
|    } | ||||
|  | ||||
|   g_mutex_unlock (&sources->priv->clients_lock); | ||||
|  | ||||
|   return has; | ||||
| } | ||||
|  | ||||
| static void | ||||
| backend_died_cb (EClient *client, CalendarSourceData *source_data) | ||||
| backend_died_cb (EClient *client, | ||||
|                  CalendarSources *sources) | ||||
| { | ||||
|   ESource *source; | ||||
|   const char *display_name; | ||||
| @@ -360,196 +352,179 @@ backend_died_cb (EClient *client, CalendarSourceData *source_data) | ||||
|   source = e_client_get_source (client); | ||||
|   display_name = e_source_get_display_name (source); | ||||
|   g_warning ("The calendar backend for '%s' has crashed.", display_name); | ||||
|   g_hash_table_remove (source_data->clients, source); | ||||
|  | ||||
|   g_clear_handle_id (&source_data->timeout_id, g_source_remove); | ||||
|  | ||||
|   source_data->timeout_id = g_timeout_add_seconds (2, backend_restart, | ||||
| 		  				   source_data); | ||||
|   g_source_set_name_by_id (source_data->timeout_id, "[gnome-shell] backend_restart"); | ||||
|   g_mutex_lock (&sources->priv->clients_lock); | ||||
|   g_hash_table_remove (sources->priv->clients, source); | ||||
|   g_mutex_unlock (&sources->priv->clients_lock); | ||||
| } | ||||
|  | ||||
| static void | ||||
| calendar_sources_load_esource_list (ESourceRegistry *registry, | ||||
|                                     CalendarSourceData *source_data) | ||||
| EClient * | ||||
| calendar_sources_connect_client_sync (CalendarSources *sources, | ||||
|                                       gboolean is_for_events, | ||||
|                                       ESource *source, | ||||
|                                       ECalClientSourceType source_type, | ||||
|                                       guint32 wait_for_connected_seconds, | ||||
|                                       GCancellable *cancellable, | ||||
|                                       GError **error) | ||||
| { | ||||
|   GList   *list, *link; | ||||
|   const gchar *extension_name; | ||||
|   EClient *client = NULL; | ||||
|   ClientData *client_data; | ||||
|  | ||||
|   switch (source_data->source_type) | ||||
|   g_mutex_lock (&sources->priv->clients_lock); | ||||
|   client_data = g_hash_table_lookup (sources->priv->clients, source); | ||||
|   if (client_data) { | ||||
|      if (is_for_events) | ||||
|        client_data->is_for_events = TRUE; | ||||
|      client = E_CLIENT (g_object_ref (client_data->client)); | ||||
|   } | ||||
|   g_mutex_unlock (&sources->priv->clients_lock); | ||||
|  | ||||
|   if (client) | ||||
|     return client; | ||||
|  | ||||
|   client = e_cal_client_connect_sync (source, source_type, wait_for_connected_seconds, cancellable, error); | ||||
|   if (!client) | ||||
|     return NULL; | ||||
|  | ||||
|   g_mutex_lock (&sources->priv->clients_lock); | ||||
|   client_data = g_hash_table_lookup (sources->priv->clients, source); | ||||
|   if (client_data) | ||||
|     { | ||||
|       case E_CAL_CLIENT_SOURCE_TYPE_EVENTS: | ||||
|         extension_name = E_SOURCE_EXTENSION_CALENDAR; | ||||
|         break; | ||||
|       case E_CAL_CLIENT_SOURCE_TYPE_TASKS: | ||||
|         extension_name = E_SOURCE_EXTENSION_TASK_LIST; | ||||
|         break; | ||||
|       default: | ||||
|         g_return_if_reached (); | ||||
|       if (is_for_events) | ||||
|         client_data->is_for_events = TRUE; | ||||
|       g_clear_object (&client); | ||||
|       client = E_CLIENT (g_object_ref (client_data->client)); | ||||
|     } | ||||
|  | ||||
|   list = e_source_registry_list_sources (registry, extension_name); | ||||
|  | ||||
|   for (link = list; link != NULL; link = g_list_next (link)) | ||||
|    else | ||||
|     { | ||||
|       ESource *source = E_SOURCE (link->data); | ||||
|       ESourceSelectable *extension; | ||||
|       gboolean show_source; | ||||
|       client_data = g_slice_new0 (ClientData); | ||||
|       client_data->client = E_CAL_CLIENT (g_object_ref (client)); | ||||
|       client_data->is_for_events = is_for_events; | ||||
|       client_data->backend_died_id = g_signal_connect (client, | ||||
|                                                        "backend-died", | ||||
|                                                        G_CALLBACK (backend_died_cb), | ||||
|                                                        sources); | ||||
|  | ||||
|       extension = e_source_get_extension (source, extension_name); | ||||
|       show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension); | ||||
|  | ||||
|       if (show_source) | ||||
|         create_client_for_source (source, source_data->source_type, source_data); | ||||
|       g_hash_table_insert (sources->priv->clients, g_object_ref (source), client_data); | ||||
|     } | ||||
|   g_mutex_unlock (&sources->priv->clients_lock); | ||||
|  | ||||
|   debug_dump_ecal_list (source_data->clients); | ||||
|  | ||||
|   g_list_free_full (list, g_object_unref); | ||||
|   return client; | ||||
| } | ||||
|  | ||||
| typedef struct _AsyncContext { | ||||
|   gboolean is_for_events; | ||||
|   ESource *source; | ||||
|   ECalClientSourceType source_type; | ||||
|   guint32 wait_for_connected_seconds; | ||||
| } AsyncContext; | ||||
|  | ||||
| static void | ||||
| calendar_sources_registry_source_changed_cb (ESourceRegistry *registry, | ||||
|                                              ESource         *source, | ||||
|                                              CalendarSources *sources) | ||||
| async_context_free (gpointer ptr) | ||||
| { | ||||
|   if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) | ||||
|   AsyncContext *ctx = ptr; | ||||
|  | ||||
|   if (ctx) | ||||
|     { | ||||
|       CalendarSourceData *source_data; | ||||
|       ESourceSelectable *extension; | ||||
|       gboolean have_client; | ||||
|       gboolean show_source; | ||||
|  | ||||
|       source_data = &sources->priv->appointment_sources; | ||||
|       extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR); | ||||
|       have_client = (g_hash_table_lookup (source_data->clients, source) != NULL); | ||||
|       show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension); | ||||
|  | ||||
|       if (!show_source && have_client) | ||||
|         { | ||||
|           g_hash_table_remove (source_data->clients, source); | ||||
|           g_signal_emit (sources, source_data->changed_signal, 0); | ||||
|         } | ||||
|       if (show_source && !have_client) | ||||
|         { | ||||
|           create_client_for_source (source, source_data->source_type, source_data); | ||||
|           g_signal_emit (sources, source_data->changed_signal, 0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) | ||||
|     { | ||||
|       CalendarSourceData *source_data; | ||||
|       ESourceSelectable *extension; | ||||
|       gboolean have_client; | ||||
|       gboolean show_source; | ||||
|  | ||||
|       source_data = &sources->priv->task_sources; | ||||
|       extension = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST); | ||||
|       have_client = (g_hash_table_lookup (source_data->clients, source) != NULL); | ||||
|       show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension); | ||||
|  | ||||
|       if (!show_source && have_client) | ||||
|         { | ||||
|           g_hash_table_remove (source_data->clients, source); | ||||
|           g_signal_emit (sources, source_data->changed_signal, 0); | ||||
|         } | ||||
|       if (show_source && !have_client) | ||||
|         { | ||||
|           create_client_for_source (source, source_data->source_type, source_data); | ||||
|           g_signal_emit (sources, source_data->changed_signal, 0); | ||||
|         } | ||||
|       g_clear_object (&ctx->source); | ||||
|       g_slice_free (AsyncContext, ctx); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| calendar_sources_registry_source_removed_cb (ESourceRegistry *registry, | ||||
|                                              ESource         *source, | ||||
|                                              CalendarSources *sources) | ||||
| calendar_sources_connect_client_thread (GTask *task, | ||||
|                                         gpointer source_object, | ||||
|                                         gpointer task_data, | ||||
|                                         GCancellable *cancellable) | ||||
| { | ||||
|   if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) | ||||
|   CalendarSources *sources = source_object; | ||||
|   AsyncContext *ctx = task_data; | ||||
|   EClient *client; | ||||
|   GError *local_error = NULL; | ||||
|  | ||||
|   client = calendar_sources_connect_client_sync (sources, ctx->is_for_events, ctx->source, ctx->source_type, | ||||
|                                                  ctx->wait_for_connected_seconds, cancellable, &local_error); | ||||
|   if (!client) | ||||
|     { | ||||
|       CalendarSourceData *source_data; | ||||
|  | ||||
|       source_data = &sources->priv->appointment_sources; | ||||
|       g_hash_table_remove (source_data->clients, source); | ||||
|       g_signal_emit (sources, source_data->changed_signal, 0); | ||||
|     } | ||||
|  | ||||
|   if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) | ||||
|     { | ||||
|       CalendarSourceData *source_data; | ||||
|  | ||||
|       source_data = &sources->priv->task_sources; | ||||
|       g_hash_table_remove (source_data->clients, source); | ||||
|       g_signal_emit (sources, source_data->changed_signal, 0); | ||||
|       if (local_error) | ||||
|         g_task_return_error (task, local_error); | ||||
|       else | ||||
|         g_task_return_pointer (task, NULL, NULL); | ||||
|     } else { | ||||
|       g_task_return_pointer (task, client, g_object_unref); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| ensure_appointment_sources (CalendarSources *sources) | ||||
| void | ||||
| calendar_sources_connect_client (CalendarSources *sources, | ||||
|                                  gboolean is_for_events, | ||||
|                                  ESource *source, | ||||
|                                  ECalClientSourceType source_type, | ||||
|                                  guint32 wait_for_connected_seconds, | ||||
|                                  GCancellable *cancellable, | ||||
|                                  GAsyncReadyCallback callback, | ||||
|                                  gpointer user_data) | ||||
| { | ||||
|   if (!sources->priv->appointment_sources.loaded) | ||||
|   AsyncContext *ctx; | ||||
|   GTask *task; | ||||
|  | ||||
|   ctx = g_slice_new0 (AsyncContext); | ||||
|   ctx->is_for_events = is_for_events; | ||||
|   ctx->source = g_object_ref (source); | ||||
|   ctx->source_type = source_type; | ||||
|   ctx->wait_for_connected_seconds = wait_for_connected_seconds; | ||||
|  | ||||
|   task = g_task_new (sources, cancellable, callback, user_data); | ||||
|   g_task_set_source_tag (task, calendar_sources_connect_client); | ||||
|   g_task_set_task_data (task, ctx, async_context_free); | ||||
|  | ||||
|   g_task_run_in_thread (task, calendar_sources_connect_client_thread); | ||||
|  | ||||
|   g_object_unref (task); | ||||
| } | ||||
|  | ||||
| EClient * | ||||
| calendar_sources_connect_client_finish (CalendarSources *sources, | ||||
|                                         GAsyncResult *result, | ||||
|                                         GError **error) | ||||
| { | ||||
|   g_return_val_if_fail (g_task_is_valid (result, sources), NULL); | ||||
|   g_return_val_if_fail (g_async_result_is_tagged (result, calendar_sources_connect_client), NULL); | ||||
|  | ||||
|   return g_task_propagate_pointer (G_TASK (result), error); | ||||
| } | ||||
|  | ||||
|  | ||||
| void | ||||
| print_debug (const gchar *format, | ||||
|              ...) | ||||
| { | ||||
|   g_autofree char *s = NULL; | ||||
|   g_autofree char *timestamp = NULL; | ||||
|   va_list ap; | ||||
|   g_autoptr (GDateTime) now = NULL; | ||||
|   static volatile gsize once_init_value = 0; | ||||
|   static gboolean show_debug = FALSE; | ||||
|   static guint pid = 0; | ||||
|  | ||||
|   if (g_once_init_enter (&once_init_value)) | ||||
|     { | ||||
|       calendar_sources_load_esource_list (sources->priv->registry, | ||||
|                                           &sources->priv->appointment_sources); | ||||
|       sources->priv->appointment_sources.loaded = TRUE; | ||||
|       show_debug = (g_getenv ("CALENDAR_SERVER_DEBUG") != NULL); | ||||
|       pid = getpid (); | ||||
|       g_once_init_leave (&once_init_value, 1); | ||||
|     } | ||||
| } | ||||
|  | ||||
| GList * | ||||
| calendar_sources_get_appointment_clients (CalendarSources *sources) | ||||
| { | ||||
|   GList *list, *link; | ||||
|  | ||||
|   g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL); | ||||
|  | ||||
|   ensure_appointment_sources (sources); | ||||
|  | ||||
|   list = g_hash_table_get_values (sources->priv->appointment_sources.clients); | ||||
|  | ||||
|   for (link = list; link != NULL; link = g_list_next (link)) | ||||
|     link->data = ((ClientData *) link->data)->client; | ||||
|  | ||||
|   return list; | ||||
| } | ||||
|  | ||||
| static void | ||||
| ensure_task_sources (CalendarSources *sources) | ||||
| { | ||||
|   if (!sources->priv->task_sources.loaded) | ||||
|     { | ||||
|       calendar_sources_load_esource_list (sources->priv->registry, | ||||
|                                           &sources->priv->task_sources); | ||||
|       sources->priv->task_sources.loaded = TRUE; | ||||
|     } | ||||
| } | ||||
|  | ||||
| GList * | ||||
| calendar_sources_get_task_clients (CalendarSources *sources) | ||||
| { | ||||
|   GList *list, *link; | ||||
|  | ||||
|   g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL); | ||||
|  | ||||
|   ensure_task_sources (sources); | ||||
|  | ||||
|   list = g_hash_table_get_values (sources->priv->task_sources.clients); | ||||
|  | ||||
|   for (link = list; link != NULL; link = g_list_next (link)) | ||||
|     link->data = ((ClientData *) link->data)->client; | ||||
|  | ||||
|   return list; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| calendar_sources_has_sources (CalendarSources *sources) | ||||
| { | ||||
|   g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), FALSE); | ||||
|  | ||||
|   ensure_appointment_sources (sources); | ||||
|   ensure_task_sources (sources); | ||||
|  | ||||
|   return g_hash_table_size (sources->priv->appointment_sources.clients) > 0 || | ||||
|     g_hash_table_size (sources->priv->task_sources.clients) > 0; | ||||
|  | ||||
|   if (!show_debug) | ||||
|     goto out; | ||||
|  | ||||
|   now = g_date_time_new_now_local (); | ||||
|   timestamp = g_date_time_format (now, "%H:%M:%S"); | ||||
|  | ||||
|   va_start (ap, format); | ||||
|   s = g_strdup_vprintf (format, ap); | ||||
|   va_end (ap); | ||||
|  | ||||
|   g_print ("gnome-shell-calendar-server[%d]: %s.%03d: %s\n", | ||||
|            pid, timestamp, g_date_time_get_microsecond (now), s); | ||||
|  out: | ||||
|   ; | ||||
| } | ||||
|   | ||||
| @@ -26,17 +26,46 @@ | ||||
|  | ||||
| #include <glib-object.h> | ||||
|  | ||||
| #define EDS_DISABLE_DEPRECATED | ||||
| G_GNUC_BEGIN_IGNORE_DEPRECATIONS | ||||
| #include <libedataserver/libedataserver.h> | ||||
| #include <libecal/libecal.h> | ||||
| G_GNUC_END_IGNORE_DEPRECATIONS | ||||
|  | ||||
| G_BEGIN_DECLS | ||||
|  | ||||
| #define CALENDAR_TYPE_SOURCES (calendar_sources_get_type ()) | ||||
| G_DECLARE_FINAL_TYPE (CalendarSources, calendar_sources, | ||||
|                       CALENDAR, SOURCES, GObject) | ||||
|  | ||||
| CalendarSources *calendar_sources_get                     (void); | ||||
| GList           *calendar_sources_get_appointment_clients (CalendarSources *sources); | ||||
| GList           *calendar_sources_get_task_clients        (CalendarSources *sources); | ||||
| CalendarSources *calendar_sources_get                (void); | ||||
| ESourceRegistry *calendar_sources_get_registry       (CalendarSources *sources); | ||||
| GSList          *calendar_sources_ref_clients        (CalendarSources *sources); | ||||
| gboolean         calendar_sources_has_clients        (CalendarSources *sources); | ||||
|  | ||||
| gboolean         calendar_sources_has_sources             (CalendarSources *sources); | ||||
| EClient         *calendar_sources_connect_client_sync(CalendarSources *sources, | ||||
|                                                       gboolean is_for_events, | ||||
|                                                       ESource *source, | ||||
|                                                       ECalClientSourceType source_type, | ||||
|                                                       guint32 wait_for_connected_seconds, | ||||
|                                                       GCancellable *cancellable, | ||||
|                                                       GError **error); | ||||
| void             calendar_sources_connect_client     (CalendarSources *sources, | ||||
|                                                       gboolean is_for_events, | ||||
|                                                       ESource *source, | ||||
|                                                       ECalClientSourceType source_type, | ||||
|                                                       guint32 wait_for_connected_seconds, | ||||
|                                                       GCancellable *cancellable, | ||||
|                                                       GAsyncReadyCallback callback, | ||||
|                                                       gpointer user_data); | ||||
| EClient         *calendar_sources_connect_client_finish | ||||
|                                                      (CalendarSources *sources, | ||||
|                                                       GAsyncResult *result, | ||||
|                                                       GError **error); | ||||
|  | ||||
| /* Set the environment variable CALENDAR_SERVER_DEBUG to show debug */ | ||||
| void            print_debug                          (const gchar *str, | ||||
|                                                       ...) G_GNUC_PRINTF (1, 2); | ||||
|  | ||||
| G_END_DECLS | ||||
|  | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -2,7 +2,9 @@ calendar_sources = [ | ||||
|   'gnome-shell-calendar-server.c', | ||||
|   'calendar-debug.h', | ||||
|   'calendar-sources.c', | ||||
|   'calendar-sources.h' | ||||
|   'calendar-sources.h', | ||||
|   'reminder-watcher.c', | ||||
|   'reminder-watcher.h' | ||||
| ] | ||||
|  | ||||
| calendar_server = executable('gnome-shell-calendar-server', calendar_sources, | ||||
|   | ||||
							
								
								
									
										767
									
								
								src/calendar-server/reminder-watcher.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										767
									
								
								src/calendar-server/reminder-watcher.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,767 @@ | ||||
| /* | ||||
|  * Copyright (C) 2020 Red Hat (www.redhat.com) | ||||
|  * | ||||
|  * 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, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #include <config.h> | ||||
|  | ||||
| #include <glib/gi18n-lib.h> | ||||
|  | ||||
| #define HANDLE_LIBICAL_MEMORY | ||||
| #define EDS_DISABLE_DEPRECATED | ||||
| #include <libecal/libecal.h> | ||||
|  | ||||
| #include "calendar-sources.h" | ||||
| #include "reminder-watcher.h" | ||||
|  | ||||
| struct _ReminderWatcherPrivate { | ||||
|   GApplication *application; /* not referenced */ | ||||
|   CalendarSources *sources; | ||||
|   GSettings *settings; | ||||
|  | ||||
|   GMutex dismiss_lock; | ||||
|   GSList *dismiss; /* EReminderData * */ | ||||
|   GThread *dismiss_thread; /* not referenced, only to know whether it's scheduled */ | ||||
| }; | ||||
|  | ||||
| G_DEFINE_TYPE_WITH_PRIVATE (ReminderWatcher, reminder_watcher, E_TYPE_REMINDER_WATCHER) | ||||
|  | ||||
| static const gchar * | ||||
| reminder_watcher_get_rd_summary (const EReminderData *rd) | ||||
| { | ||||
|   if (!rd) | ||||
|     return NULL; | ||||
|  | ||||
|   return i_cal_component_get_summary (e_cal_component_get_icalcomponent (e_reminder_data_get_component (rd))); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| reminder_watcher_notify_audio (ReminderWatcher *rw, | ||||
|                                const EReminderData *rd, | ||||
|                                ECalComponentAlarm *alarm) | ||||
| { | ||||
| #if 0 | ||||
|   ICalAttach *attach = NULL; | ||||
|   GSList *attachments; | ||||
|   gboolean did_play = FALSE; | ||||
|  | ||||
|   g_return_val_if_fail (rw != NULL, FALSE); | ||||
|   g_return_val_if_fail (rd != NULL, FALSE); | ||||
|   g_return_val_if_fail (alarm != NULL, FALSE); | ||||
|  | ||||
|   attachments = e_cal_component_alarm_get_attachments (alarm); | ||||
|   if (attachments && !attachments->next) | ||||
|     attach = attachments->data; | ||||
|  | ||||
|   if (attach && i_cal_attach_get_is_url (attach)) | ||||
|     { | ||||
|       const gchar *url; | ||||
|  | ||||
|       url = i_cal_attach_get_url (attach); | ||||
|       if (url && *url) | ||||
|         { | ||||
|           gchar *filename; | ||||
|           GError *error = NULL; | ||||
|  | ||||
|           filename = g_filename_from_uri (url, NULL, &error); | ||||
|  | ||||
|           if (!filename) | ||||
|             ean_debug_print ("Audio notify: Failed to convert URI '%s' to filename: %s\n", url, error ? error->message : "Unknown error"); | ||||
|            else if (g_file_test (filename, G_FILE_TEST_EXISTS)) | ||||
|             { | ||||
| #ifdef HAVE_CANBERRA | ||||
|               gint err = ca_context_play (ca_gtk_context_get (), 0, | ||||
|                                           CA_PROP_MEDIA_FILENAME, filename, | ||||
|                                           NULL); | ||||
|  | ||||
|               did_play = !err; | ||||
|  | ||||
|               if (err) | ||||
|                 ean_debug_print ("Audio notify: Cannot play file '%s': %s\n", filename, ca_strerror (err)); | ||||
| #else | ||||
|                 ean_debug_print ("Audio notify: Cannot play file '%s': Not compiled with libcanberra\n", filename); | ||||
| #endif | ||||
|             } | ||||
|            else | ||||
|             ean_debug_print ("Audio notify: File '%s' does not exist\n", filename); | ||||
|  | ||||
|           g_clear_error (&error); | ||||
|           g_free (filename); | ||||
|         } | ||||
|        else | ||||
|         ean_debug_print ("Audio notify: Alarm has stored empty URL, fallback to default sound\n"); | ||||
|     } | ||||
|    else if (!attach) | ||||
|     ean_debug_print ("Audio notify: Alarm has no attachment, fallback to default sound\n"); | ||||
|    else | ||||
|     ean_debug_print ("Audio notify: Alarm attachment is not a URL to sound file, fallback to default sound\n"); | ||||
|  | ||||
| #ifdef HAVE_CANBERRA | ||||
|   if (!did_play) | ||||
|     { | ||||
|       gint err = ca_context_play (ca_gtk_context_get (), 0, | ||||
|                                   CA_PROP_EVENT_ID, "alarm-clock-elapsed", | ||||
|                                   NULL); | ||||
|  | ||||
|       did_play = !err; | ||||
|  | ||||
|       if (err) | ||||
|         ean_debug_print ("Audio notify: Cannot play event sound: %s\n", ca_strerror (err)); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|   if (!did_play) | ||||
|     { | ||||
|       GdkDisplay *display; | ||||
|  | ||||
|       display = rw->priv->window ? gtk_widget_get_display (rw->priv->window) : NULL; | ||||
|  | ||||
|       if (!display) | ||||
|         display = gdk_display_get_default (); | ||||
|  | ||||
|       if (display) | ||||
|         gdk_display_beep (display); | ||||
|       else | ||||
|         ean_debug_print ("Audio notify: Cannot beep, no display found\n"); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|   print_debug ("ReminderWatcher::Notify Audio for '%s'", reminder_watcher_get_rd_summary (rd)); | ||||
|  | ||||
|   return FALSE; | ||||
| } | ||||
|  | ||||
| static gchar * | ||||
| reminder_watcher_build_notif_id (const EReminderData *rd) | ||||
| { | ||||
|   GString *string; | ||||
|   ECalComponentId *id; | ||||
|   ECalComponentAlarmInstance *instance; | ||||
|  | ||||
|   g_return_val_if_fail (rd != NULL, NULL); | ||||
|  | ||||
|   string = g_string_sized_new (32); | ||||
|  | ||||
|   if (e_reminder_data_get_source_uid (rd)) | ||||
|     { | ||||
|       g_string_append (string, e_reminder_data_get_source_uid (rd)); | ||||
|       g_string_append_c (string, '\n'); | ||||
|     } | ||||
|  | ||||
|   id = e_cal_component_get_id (e_reminder_data_get_component (rd)); | ||||
|   if (id) | ||||
|     { | ||||
|       if (e_cal_component_id_get_uid (id)) | ||||
|         { | ||||
|           g_string_append (string, e_cal_component_id_get_uid (id)); | ||||
|           g_string_append_c (string, '\n'); | ||||
|         } | ||||
|  | ||||
|       if (e_cal_component_id_get_rid (id)) | ||||
|         { | ||||
|           g_string_append (string, e_cal_component_id_get_rid (id)); | ||||
|           g_string_append_c (string, '\n'); | ||||
|         } | ||||
|  | ||||
|       e_cal_component_id_free (id); | ||||
|     } | ||||
|  | ||||
|   instance = e_reminder_data_get_instance (rd); | ||||
|  | ||||
|   g_string_append_printf (string, "%" G_GINT64_FORMAT, (gint64) (instance ? e_cal_component_alarm_instance_get_time (instance) : -1)); | ||||
|  | ||||
|   return g_string_free (string, FALSE); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| reminder_watcher_notify_display (ReminderWatcher *rw, | ||||
|                                  const EReminderData *rd, | ||||
|                                  ECalComponentAlarm *alarm) | ||||
| { | ||||
|   GNotification *notification; | ||||
| #if 0 | ||||
|   GtkIconInfo *icon_info; | ||||
| #endif | ||||
|   gchar *description, *notif_id; | ||||
|  | ||||
|   g_return_val_if_fail (rw != NULL, FALSE); | ||||
|   g_return_val_if_fail (rd != NULL, FALSE); | ||||
|   g_return_val_if_fail (alarm != NULL, FALSE); | ||||
|  | ||||
|   notif_id = reminder_watcher_build_notif_id (rd); | ||||
|   description = e_reminder_watcher_describe_data (E_REMINDER_WATCHER (rw), rd, E_REMINDER_WATCHER_DESCRIBE_FLAG_NONE); | ||||
|  | ||||
|   notification = g_notification_new (_("Reminders")); | ||||
|   g_notification_set_body (notification, description); | ||||
|  | ||||
| #if 0 | ||||
|   icon_info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (), "appointment-soon", GTK_ICON_SIZE_DIALOG, 0); | ||||
|   if (icon_info) | ||||
|     { | ||||
|       const gchar *filename; | ||||
|  | ||||
|       filename = gtk_icon_info_get_filename (icon_info); | ||||
|       if (filename && *filename) | ||||
|         { | ||||
|           GFile *file; | ||||
|           GIcon *icon; | ||||
|  | ||||
|           file = g_file_new_for_path (filename); | ||||
|           icon = g_file_icon_new (file); | ||||
|  | ||||
|           if (icon) | ||||
|             { | ||||
|               g_notification_set_icon (notification, icon); | ||||
|               g_object_unref (icon); | ||||
|             } | ||||
|  | ||||
|           g_object_unref (file); | ||||
|         } | ||||
|  | ||||
|       gtk_icon_info_free (icon_info); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|   g_notification_add_button_with_target (notification, _("Dismiss"), "app.dismiss-reminder", "s", notif_id); | ||||
|   g_notification_set_default_action_and_target (notification, "app.dismiss-reminder", "s", notif_id); | ||||
|  | ||||
|   g_application_send_notification (rw->priv->application, notif_id, notification); | ||||
|  | ||||
|   g_object_unref (notification); | ||||
|   g_free (description); | ||||
|   g_free (notif_id); | ||||
|  | ||||
|   print_debug ("ReminderWatcher::Notify Display for '%s'", reminder_watcher_get_rd_summary (rd)); | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| reminder_watcher_notify_email (ReminderWatcher *rw, | ||||
|                                const EReminderData *rd, | ||||
|                                ECalComponentAlarm *alarm) | ||||
| { | ||||
|   print_debug ("ReminderWatcher::Notify Email for '%s'", reminder_watcher_get_rd_summary (rd)); | ||||
|  | ||||
|   /* Nothing to do here */ | ||||
|   return FALSE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| reminder_watcher_is_blessed_program (GSettings *settings, | ||||
|                                      const gchar *url) | ||||
| { | ||||
|   gchar **list; | ||||
|   gint ii; | ||||
|   gboolean found = FALSE; | ||||
|  | ||||
|   g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE); | ||||
|   g_return_val_if_fail (url != NULL, FALSE); | ||||
|  | ||||
|   list = g_settings_get_strv (settings, "notify-programs"); | ||||
|  | ||||
|   for (ii = 0; list && list[ii] && !found; ii++) | ||||
|     { | ||||
|       found = g_strcmp0 (list[ii], url) == 0; | ||||
|     } | ||||
|  | ||||
|   g_strfreev (list); | ||||
|  | ||||
|   return found; | ||||
| } | ||||
|  | ||||
| #if 0 | ||||
| static void | ||||
| reminder_watcher_save_blessed_program (GSettings *settings, | ||||
|                                        const gchar *url) | ||||
| { | ||||
|   gchar **list; | ||||
|   gint ii; | ||||
|   GPtrArray *array; | ||||
|  | ||||
|   g_return_if_fail (G_IS_SETTINGS (settings)); | ||||
|   g_return_if_fail (url != NULL); | ||||
|  | ||||
|   array = g_ptr_array_new (); | ||||
|  | ||||
|   list = g_settings_get_strv (settings, "notify-programs"); | ||||
|  | ||||
|   for (ii = 0; list && list[ii]; ii++) | ||||
|     { | ||||
|       if (g_strcmp0 (url, list[ii]) != 0) | ||||
|         g_ptr_array_add (array, list[ii]); | ||||
|     } | ||||
|  | ||||
|   g_ptr_array_add (array, (gpointer) url); | ||||
|   g_ptr_array_add (array, NULL); | ||||
|  | ||||
|   g_settings_set_strv (settings, "notify-programs", (const gchar * const *) array->pdata); | ||||
|  | ||||
|   g_ptr_array_free (array, TRUE); | ||||
|   g_strfreev (list); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static gboolean | ||||
| reminder_watcher_can_procedure (ReminderWatcher *rw, | ||||
|                                 const gchar *cmd, | ||||
|                                 const gchar *url) | ||||
| { | ||||
| #if 0 | ||||
|   GtkWidget *container; | ||||
|   GtkWidget *dialog; | ||||
|   GtkWidget *label; | ||||
|   GtkWidget *checkbox; | ||||
|   gchar *str; | ||||
|   gint response; | ||||
| #endif | ||||
|  | ||||
|   if (reminder_watcher_is_blessed_program (rw->priv->settings, url)) | ||||
|     return TRUE; | ||||
|  | ||||
| #if 0 | ||||
|   dialog = gtk_dialog_new_with_buttons ( | ||||
|     _("Warning"), GTK_WINDOW (rw->priv->window), 0, | ||||
|     _("_No"), GTK_RESPONSE_CANCEL, | ||||
|     _("_Yes"), GTK_RESPONSE_OK, | ||||
|     NULL); | ||||
|  | ||||
|   str = g_strdup_printf ( | ||||
|     _("A calendar reminder is about to trigger. " | ||||
|       "This reminder is configured to run the following program:\n\n" | ||||
|       "        %s\n\n" | ||||
|       "Are you sure you want to run this program?"), | ||||
|     cmd); | ||||
|   label = gtk_label_new (str); | ||||
|   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); | ||||
|   gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); | ||||
|   gtk_widget_show (label); | ||||
|  | ||||
|   container = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); | ||||
|   gtk_box_pack_start (GTK_BOX (container), label, TRUE, TRUE, 4); | ||||
|   g_free (str); | ||||
|  | ||||
|   checkbox = gtk_check_button_new_with_label (_("Do not ask me about this program again")); | ||||
|   gtk_widget_show (checkbox); | ||||
|   gtk_box_pack_start (GTK_BOX (container), checkbox, TRUE, TRUE, 4); | ||||
|  | ||||
|   response = gtk_dialog_run (GTK_DIALOG (dialog)); | ||||
|  | ||||
|   if (response == GTK_RESPONSE_OK && | ||||
|       gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox))) | ||||
|     { | ||||
|       reminder_watcher_save_blessed_program (rw->priv->settings, url); | ||||
|     } | ||||
|  | ||||
|   gtk_widget_destroy (dialog); | ||||
|  | ||||
|   return response == GTK_RESPONSE_OK; | ||||
| #endif | ||||
|  | ||||
|   return FALSE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| reminder_watcher_notify_procedure (ReminderWatcher *rw, | ||||
|                                    const EReminderData *rd, | ||||
|                                    ECalComponentAlarm *alarm) | ||||
| { | ||||
|   ECalComponentText *description; | ||||
|   ICalAttach *attach = NULL; | ||||
|   GSList *attachments; | ||||
|   const gchar *url; | ||||
|   gchar *cmd; | ||||
|   gboolean result = FALSE; | ||||
|  | ||||
|   g_return_val_if_fail (rw != NULL, FALSE); | ||||
|   g_return_val_if_fail (rd != NULL, FALSE); | ||||
|   g_return_val_if_fail (alarm != NULL, FALSE); | ||||
|  | ||||
|   print_debug ("ReminderWatcher::Notify Procedure for '%s'", reminder_watcher_get_rd_summary (rd)); | ||||
|  | ||||
|   attachments = e_cal_component_alarm_get_attachments (alarm); | ||||
|  | ||||
|   if (attachments && !attachments->next) | ||||
|     attach = attachments->data; | ||||
|  | ||||
|   description = e_cal_component_alarm_get_description (alarm); | ||||
|  | ||||
|   /* If the alarm has no attachment, simply display a notification dialog. */ | ||||
|   if (!attach) | ||||
|     goto fallback; | ||||
|  | ||||
|   if (!i_cal_attach_get_is_url (attach)) | ||||
|     goto fallback; | ||||
|  | ||||
|   url = i_cal_attach_get_url (attach); | ||||
|   g_return_val_if_fail (url != NULL, FALSE); | ||||
|  | ||||
|   /* Ask for confirmation before executing the stuff */ | ||||
|   if (description && e_cal_component_text_get_value (description)) | ||||
|     cmd = g_strconcat (url, " ", e_cal_component_text_get_value (description), NULL); | ||||
|   else | ||||
|     cmd = (gchar *) url; | ||||
|  | ||||
|   if (reminder_watcher_can_procedure (rw, cmd, url)) | ||||
|     result = g_spawn_command_line_async (cmd, NULL); | ||||
|  | ||||
|   if (cmd != (gchar *) url) | ||||
|     g_free (cmd); | ||||
|  | ||||
|   /* Fall back to display notification if we got an error */ | ||||
|   if (!result) | ||||
|     goto fallback; | ||||
|  | ||||
|   return FALSE; | ||||
|  | ||||
|  fallback: | ||||
|  | ||||
|   return reminder_watcher_notify_display (rw, rd, alarm); | ||||
| } | ||||
|  | ||||
| /* Returns %TRUE to keep it, %FALSE to dismiss it */ | ||||
| static gboolean | ||||
| reminders_process_one (ReminderWatcher *rw, | ||||
|                        const EReminderData *rd, | ||||
|                        gboolean snoozed) | ||||
| { | ||||
|   ECalComponentAlarm *alarm; | ||||
|   ECalComponentAlarmInstance *instance; | ||||
|   ECalComponentAlarmAction action; | ||||
|   gboolean keep_in_reminders = FALSE; | ||||
|  | ||||
|   g_return_val_if_fail (rw != NULL, FALSE); | ||||
|   g_return_val_if_fail (rd != NULL, FALSE); | ||||
|  | ||||
|   if (e_cal_component_get_vtype (e_reminder_data_get_component (rd)) == E_CAL_COMPONENT_TODO) | ||||
|     { | ||||
|       ICalPropertyStatus status; | ||||
|  | ||||
|       status = e_cal_component_get_status (e_reminder_data_get_component (rd)); | ||||
|  | ||||
|       if (status == I_CAL_STATUS_COMPLETED && | ||||
|           !g_settings_get_boolean (rw->priv->settings, "notify-completed-tasks")) | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|   instance = e_reminder_data_get_instance (rd); | ||||
|  | ||||
|   alarm = instance ? e_cal_component_get_alarm (e_reminder_data_get_component (rd), e_cal_component_alarm_instance_get_uid (instance)) : NULL; | ||||
|   if (!alarm) | ||||
|     return FALSE; | ||||
|  | ||||
|   if (!snoozed && !g_settings_get_boolean (rw->priv->settings, "notify-past-events")) | ||||
|     { | ||||
|       ECalComponentAlarmTrigger *trigger; | ||||
|       time_t offset = 0, event_relative, orig_trigger_day, today; | ||||
|  | ||||
|       trigger = e_cal_component_alarm_get_trigger (alarm); | ||||
|  | ||||
|       switch (trigger ? e_cal_component_alarm_trigger_get_kind (trigger) : E_CAL_COMPONENT_ALARM_TRIGGER_NONE) | ||||
|         { | ||||
|           case E_CAL_COMPONENT_ALARM_TRIGGER_NONE: | ||||
|           case E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE: | ||||
|             break; | ||||
|  | ||||
|           case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START: | ||||
|           case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END: | ||||
|             offset = i_cal_duration_as_int (e_cal_component_alarm_trigger_get_duration (trigger)); | ||||
|             break; | ||||
|  | ||||
|           default: | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         today = time (NULL); | ||||
|         event_relative = e_cal_component_alarm_instance_get_occur_start (instance) - offset; | ||||
|  | ||||
|         #define CLAMP_TO_DAY(x) ((x) - ((x) % (60 * 60 * 24))) | ||||
|  | ||||
|         event_relative = CLAMP_TO_DAY (event_relative); | ||||
|         orig_trigger_day = CLAMP_TO_DAY (e_cal_component_alarm_instance_get_time (instance)); | ||||
|         today = CLAMP_TO_DAY (today); | ||||
|  | ||||
|         #undef CLAMP_TO_DAY | ||||
|  | ||||
|         if (event_relative < today && orig_trigger_day < today) | ||||
|           { | ||||
|             e_cal_component_alarm_free (alarm); | ||||
|             return FALSE; | ||||
|           } | ||||
|     } | ||||
|  | ||||
|   action = e_cal_component_alarm_get_action (alarm); | ||||
|  | ||||
|   switch (action) | ||||
|     { | ||||
|       case E_CAL_COMPONENT_ALARM_AUDIO: | ||||
|         keep_in_reminders = reminder_watcher_notify_audio (rw, rd, alarm); | ||||
|         break; | ||||
|  | ||||
|       case E_CAL_COMPONENT_ALARM_DISPLAY: | ||||
|         keep_in_reminders = reminder_watcher_notify_display (rw, rd, alarm); | ||||
|         break; | ||||
|  | ||||
|       case E_CAL_COMPONENT_ALARM_EMAIL: | ||||
|         keep_in_reminders = reminder_watcher_notify_email (rw, rd, alarm); | ||||
|         break; | ||||
|  | ||||
|       case E_CAL_COMPONENT_ALARM_PROCEDURE: | ||||
|         keep_in_reminders = reminder_watcher_notify_procedure (rw, rd, alarm); | ||||
|         break; | ||||
|  | ||||
|       case E_CAL_COMPONENT_ALARM_NONE: | ||||
|       case E_CAL_COMPONENT_ALARM_UNKNOWN: | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|   e_cal_component_alarm_free (alarm); | ||||
|  | ||||
|   return keep_in_reminders; | ||||
| } | ||||
|  | ||||
| static gpointer | ||||
| reminders_dismiss_thread (gpointer user_data) | ||||
| { | ||||
|   ReminderWatcher *rw = user_data; | ||||
|   EReminderWatcher *watcher; | ||||
|   GSList *dismiss, *link; | ||||
|  | ||||
|   g_return_val_if_fail (IS_REMINDER_WATCHER (rw), NULL); | ||||
|  | ||||
|   g_mutex_lock (&rw->priv->dismiss_lock); | ||||
|   dismiss = rw->priv->dismiss; | ||||
|   rw->priv->dismiss = NULL; | ||||
|   rw->priv->dismiss_thread = NULL; | ||||
|   g_mutex_unlock (&rw->priv->dismiss_lock); | ||||
|  | ||||
|   watcher = E_REMINDER_WATCHER (rw); | ||||
|   if (watcher) | ||||
|     { | ||||
|       for (link = dismiss; link; link = g_slist_next (link)) | ||||
|         { | ||||
|           EReminderData *rd = link->data; | ||||
|  | ||||
|           if (rd) | ||||
|             { | ||||
|               /* Silently ignore any errors here */ | ||||
|               e_reminder_watcher_dismiss_sync (watcher, rd, NULL, NULL); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   g_slist_free_full (dismiss, e_reminder_data_free); | ||||
|   g_clear_object (&rw); | ||||
|  | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| static void | ||||
| reminders_triggered_cb (EReminderWatcher *watcher, | ||||
|                         const GSList *reminders, /* EReminderData * */ | ||||
|                         gboolean snoozed, | ||||
|                         gpointer user_data) | ||||
| { | ||||
|   ReminderWatcher *rw = REMINDER_WATCHER (watcher); | ||||
|   GSList *link; | ||||
|  | ||||
|   g_return_if_fail (IS_REMINDER_WATCHER (rw)); | ||||
|  | ||||
|   g_mutex_lock (&rw->priv->dismiss_lock); | ||||
|  | ||||
|   for (link = (GSList *) reminders; link; link = g_slist_next (link)) | ||||
|     { | ||||
|       const EReminderData *rd = link->data; | ||||
|  | ||||
|       if (rd && !reminders_process_one (rw, rd, snoozed)) | ||||
|         { | ||||
|          rw->priv->dismiss = g_slist_prepend (rw->priv->dismiss, e_reminder_data_copy (rd)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   if (rw->priv->dismiss && !rw->priv->dismiss_thread) | ||||
|     { | ||||
|        rw->priv->dismiss_thread = g_thread_new (NULL, reminders_dismiss_thread, g_object_ref (rw)); | ||||
|        g_warn_if_fail (rw->priv->dismiss_thread != NULL); | ||||
|        if (rw->priv->dismiss_thread) | ||||
|           g_thread_unref (rw->priv->dismiss_thread); | ||||
|     } | ||||
|  | ||||
|   g_mutex_unlock (&rw->priv->dismiss_lock); | ||||
| } | ||||
|  | ||||
| static EClient * | ||||
| reminder_watcher_cal_client_connect_sync (EReminderWatcher *watcher, | ||||
|                                           ESource *source, | ||||
|                                           ECalClientSourceType source_type, | ||||
|                                           guint32 wait_for_connected_seconds, | ||||
|                                           GCancellable *cancellable, | ||||
|                                           GError **error) | ||||
| { | ||||
|   ReminderWatcher *reminder_watcher = REMINDER_WATCHER (watcher); | ||||
|  | ||||
|   return calendar_sources_connect_client_sync (reminder_watcher->priv->sources, FALSE, source, source_type, | ||||
|                                                wait_for_connected_seconds, cancellable, error); | ||||
| } | ||||
|  | ||||
| static void | ||||
| reminder_watcher_cal_client_connect (EReminderWatcher *watcher, | ||||
|                                      ESource *source, | ||||
|                                      ECalClientSourceType source_type, | ||||
|                                      guint32 wait_for_connected_seconds, | ||||
|                                      GCancellable *cancellable, | ||||
|                                      GAsyncReadyCallback callback, | ||||
|                                      gpointer user_data) | ||||
| { | ||||
|   ReminderWatcher *reminder_watcher = REMINDER_WATCHER (watcher); | ||||
|  | ||||
|   calendar_sources_connect_client (reminder_watcher->priv->sources, FALSE, source, source_type, | ||||
|                                    wait_for_connected_seconds, cancellable, callback, user_data); | ||||
| } | ||||
|  | ||||
| static EClient * | ||||
| reminder_watcher_cal_client_connect_finish (EReminderWatcher *watcher, | ||||
|                                             GAsyncResult *result, | ||||
|                                             GError **error) | ||||
| { | ||||
|   ReminderWatcher *reminder_watcher = REMINDER_WATCHER (watcher); | ||||
|  | ||||
|   return calendar_sources_connect_client_finish (reminder_watcher->priv->sources, result, error); | ||||
| } | ||||
|  | ||||
| static void | ||||
| reminder_watcher_constructed (GObject *object) | ||||
| { | ||||
|   ReminderWatcher *rw = REMINDER_WATCHER (object); | ||||
|  | ||||
|   G_OBJECT_CLASS (reminder_watcher_parent_class)->constructed (object); | ||||
|  | ||||
|   rw->priv->sources = calendar_sources_get (); | ||||
|  | ||||
|   g_signal_connect (rw, "triggered", | ||||
|                     G_CALLBACK (reminders_triggered_cb), NULL); | ||||
| } | ||||
|  | ||||
| static void | ||||
| reminder_watcher_finalize (GObject *object) | ||||
| { | ||||
|   ReminderWatcher *rw = REMINDER_WATCHER (object); | ||||
|  | ||||
|   g_clear_object (&rw->priv->sources); | ||||
|   g_clear_object (&rw->priv->settings); | ||||
|   g_mutex_clear (&rw->priv->dismiss_lock); | ||||
|  | ||||
|   G_OBJECT_CLASS (reminder_watcher_parent_class)->finalize (object); | ||||
| } | ||||
|  | ||||
| static void | ||||
| reminder_watcher_class_init (ReminderWatcherClass *klass) | ||||
| { | ||||
|   GObjectClass *object_class; | ||||
|   EReminderWatcherClass *watcher_class; | ||||
|  | ||||
|   object_class = G_OBJECT_CLASS (klass); | ||||
|   object_class->constructed = reminder_watcher_constructed; | ||||
|   object_class->finalize = reminder_watcher_finalize; | ||||
|  | ||||
|   watcher_class = E_REMINDER_WATCHER_CLASS (klass); | ||||
|   watcher_class->cal_client_connect_sync = reminder_watcher_cal_client_connect_sync; | ||||
|   watcher_class->cal_client_connect = reminder_watcher_cal_client_connect; | ||||
|   watcher_class->cal_client_connect_finish = reminder_watcher_cal_client_connect_finish; | ||||
| } | ||||
|  | ||||
| static void | ||||
| reminder_watcher_init (ReminderWatcher *rw) | ||||
| { | ||||
|   rw->priv = reminder_watcher_get_instance_private (rw); | ||||
|   rw->priv->settings = g_settings_new ("org.gnome.evolution-data-server.calendar"); | ||||
|  | ||||
|   g_mutex_init (&rw->priv->dismiss_lock); | ||||
| } | ||||
|  | ||||
| EReminderWatcher * | ||||
| reminder_watcher_new (GApplication *application, | ||||
|                       ESourceRegistry *registry) | ||||
| { | ||||
|   ReminderWatcher *rw; | ||||
|  | ||||
|   g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); | ||||
|  | ||||
|   rw = g_object_new (TYPE_REMINDER_WATCHER, | ||||
|                      "registry", registry, | ||||
|                      NULL); | ||||
|  | ||||
|   rw->priv->application = application; | ||||
|  | ||||
|   return E_REMINDER_WATCHER (rw); | ||||
| } | ||||
|  | ||||
| static void | ||||
| reminder_watcher_dismiss_done_cb (GObject *source_object, | ||||
|                                   GAsyncResult *result, | ||||
|                                   gpointer user_data) | ||||
| { | ||||
|   GError *error = NULL; | ||||
|  | ||||
|   if (!e_reminder_watcher_dismiss_finish (E_REMINDER_WATCHER (source_object), result, &error)) | ||||
|     { | ||||
|       if (!g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_NOT_SUPPORTED)) | ||||
|         print_debug ("Dismiss: Failed with error: %s", error ? error->message : "Unknown error"); | ||||
|  | ||||
|       g_clear_error (&error); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void | ||||
| reminder_watcher_dismiss_by_id (EReminderWatcher *reminder_watcher, | ||||
|                                 const gchar *id) | ||||
| { | ||||
|   ReminderWatcher *rw; | ||||
|   GSList *past, *link; | ||||
|  | ||||
|   g_return_if_fail (IS_REMINDER_WATCHER (reminder_watcher)); | ||||
|   g_return_if_fail (id && *id); | ||||
|  | ||||
|   rw = REMINDER_WATCHER (reminder_watcher); | ||||
|   past = e_reminder_watcher_dup_past (reminder_watcher); | ||||
|  | ||||
|   for (link = past; link; link = g_slist_next (link)) | ||||
|     { | ||||
|       EReminderData *rd = link->data; | ||||
|       gchar *rd_id; | ||||
|  | ||||
|       rd_id = reminder_watcher_build_notif_id (rd); | ||||
|  | ||||
|       if (g_strcmp0 (rd_id, id) == 0) | ||||
|         { | ||||
|           print_debug ("Dismiss: Going to dismiss '%s'", reminder_watcher_get_rd_summary (rd)); | ||||
|  | ||||
|           g_application_withdraw_notification (rw->priv->application, id); | ||||
|  | ||||
|           e_reminder_watcher_dismiss (reminder_watcher, rd, NULL, | ||||
|                                       reminder_watcher_dismiss_done_cb, NULL); | ||||
|  | ||||
|           g_free (rd_id); | ||||
|           break; | ||||
|         } | ||||
|  | ||||
|       g_free (rd_id); | ||||
|     } | ||||
|  | ||||
|   if (!link) | ||||
|     print_debug ("Dismiss: Cannot find reminder '%s'", id); | ||||
|  | ||||
|   g_slist_free_full (past, e_reminder_data_free); | ||||
| } | ||||
							
								
								
									
										67
									
								
								src/calendar-server/reminder-watcher.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/calendar-server/reminder-watcher.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| /* | ||||
|  * Copyright (C) 2020 Red Hat (www.redhat.com) | ||||
|  * | ||||
|  * 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, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #ifndef REMINDER_WATCHER_H | ||||
| #define REMINDER_WATCHER_H | ||||
|  | ||||
| #define HANDLE_LIBICAL_MEMORY | ||||
| #define EDS_DISABLE_DEPRECATED | ||||
| #include <libecal/libecal.h> | ||||
|  | ||||
| /* Standard GObject macros */ | ||||
| #define TYPE_REMINDER_WATCHER \ | ||||
|         (reminder_watcher_get_type ()) | ||||
| #define REMINDER_WATCHER(obj) \ | ||||
|         (G_TYPE_CHECK_INSTANCE_CAST \ | ||||
|         ((obj), TYPE_REMINDER_WATCHER, ReminderWatcher)) | ||||
| #define REMINDER_WATCHER_CLASS(cls) \ | ||||
|         (G_TYPE_CHECK_CLASS_CAST \ | ||||
|         ((cls), TYPE_REMINDER_WATCHER, ReminderWatcherClass)) | ||||
| #define IS_REMINDER_WATCHER(obj) \ | ||||
|         (G_TYPE_CHECK_INSTANCE_TYPE \ | ||||
|         ((obj), TYPE_REMINDER_WATCHER)) | ||||
| #define IS_REMINDER_WATCHER_CLASS(cls) \ | ||||
|         (G_TYPE_CHECK_CLASS_TYPE \ | ||||
|         ((cls), TYPE_REMINDER_WATCHER)) | ||||
| #define REMINDER_WATCHER_GET_CLASS(obj) \ | ||||
|         (G_TYPE_INSTANCE_GET_CLASS \ | ||||
|         ((obj), TYPE_REMINDER_WATCHER, ReminderWatcherClass)) | ||||
|  | ||||
| G_BEGIN_DECLS | ||||
|  | ||||
| typedef struct _ReminderWatcher ReminderWatcher; | ||||
| typedef struct _ReminderWatcherClass ReminderWatcherClass; | ||||
| typedef struct _ReminderWatcherPrivate ReminderWatcherPrivate; | ||||
|  | ||||
| struct _ReminderWatcher { | ||||
|   EReminderWatcher parent; | ||||
|   ReminderWatcherPrivate *priv; | ||||
| }; | ||||
|  | ||||
| struct _ReminderWatcherClass { | ||||
|   EReminderWatcherClass parent_class; | ||||
| }; | ||||
|  | ||||
| GType             reminder_watcher_get_type     (void) G_GNUC_CONST; | ||||
| EReminderWatcher *reminder_watcher_new          (GApplication *application, | ||||
|                                                  ESourceRegistry *registry); | ||||
| void              reminder_watcher_dismiss_by_id(EReminderWatcher *reminder_watcher, | ||||
|                                                  const gchar *id); | ||||
|  | ||||
| G_END_DECLS | ||||
|  | ||||
| #endif /* REMINDER_WATCHER_H */ | ||||
		Reference in New Issue
	
	Block a user