Compare commits
	
		
			388 Commits
		
	
	
		
			3.15.91
			...
			matthiasc/
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 39f974358c | ||
|   | 0fb98606ef | ||
|   | dd0d5a757c | ||
|   | 0bbb226faf | ||
|   | 35b38d5cb2 | ||
|   | 09e8a437d4 | ||
|   | d2bedcc182 | ||
|   | 84eda6e459 | ||
|   | f5e7530fc7 | ||
|   | f983b34784 | ||
|   | 36bbe64898 | ||
|   | 627a393ed6 | ||
|   | a025b151ef | ||
|   | 18b6f13395 | ||
|   | 051413550f | ||
|   | 3e10574736 | ||
|   | 9a3041004b | ||
|   | 87f71b8ce1 | ||
|   | 825146f1e3 | ||
|   | 52995416fd | ||
|   | 6c43d0247a | ||
|   | 9aa98d9f0c | ||
|   | c3a29d6df1 | ||
|   | 82f84416a9 | ||
|   | 9dd3162dbe | ||
|   | 7ef519756a | ||
|   | 3bbe74d1c1 | ||
|   | 409f6718b8 | ||
|   | 9c0e179080 | ||
|   | b3b278d41f | ||
|   | 0f466dbafb | ||
|   | fbb4a9a3a6 | ||
|   | 8ddae5cd71 | ||
|   | b0915c7b60 | ||
|   | 831bb4e334 | ||
|   | 4e025506fa | ||
|   | abccf451bf | ||
|   | 14954117c0 | ||
|   | 629f408fe5 | ||
|   | 86c6ab3c01 | ||
|   | 4a6ff94701 | ||
|   | e480b08d58 | ||
|   | caf53861d1 | ||
|   | d0480648ba | ||
|   | eb8cfe799f | ||
|   | b9f2541880 | ||
|   | bde9b08bfe | ||
|   | 8a4c862633 | ||
|   | 785c90f4b8 | ||
|   | dd6a11e4c7 | ||
|   | 64e9503adb | ||
|   | 36c885bf34 | ||
|   | ad7cde805d | ||
|   | 2c2c67f4dc | ||
|   | cc4f8dfab0 | ||
|   | 0fb13608c5 | ||
|   | 09dbe17da0 | ||
|   | fdd347c9aa | ||
|   | 572095515b | ||
|   | f2d4aa0822 | ||
|   | 030a22d795 | ||
|   | c70afcdb44 | ||
|   | 526d6c03b8 | ||
|   | 261b55300d | ||
|   | e13bfd9a17 | ||
|   | e096d18bac | ||
|   | 9460f0e4f3 | ||
|   | e6591f52ac | ||
|   | 07e3d1fd5c | ||
|   | 1fbc6b24c8 | ||
|   | 982777be94 | ||
|   | 6610a34ad0 | ||
|   | bfa8a0441a | ||
|   | 7723622ec7 | ||
|   | 6bcc8c70ef | ||
|   | d114d5f95a | ||
|   | b5c734da42 | ||
|   | 2077bb94c1 | ||
|   | 65a4ee7fb4 | ||
|   | debf293298 | ||
|   | a0df3e7d1e | ||
|   | 0d67c2d164 | ||
|   | 682bd7b622 | ||
|   | fa0e54edbb | ||
|   | 5a0b209663 | ||
|   | 7e8859fd0e | ||
|   | 444fa2e0ab | ||
|   | a31455b921 | ||
|   | ac0213a516 | ||
|   | 45a6e2c305 | ||
|   | 11cbd396c0 | ||
|   | a343445cd2 | ||
|   | be3d62487c | ||
|   | 58905bd01a | ||
|   | 08506eac2d | ||
|   | 02c6b0374d | ||
|   | 0722c06275 | ||
|   | 17a4044d97 | ||
|   | 27a7194634 | ||
|   | d73f560bcc | ||
|   | e92f43b83e | ||
|   | fed79ce4e6 | ||
|   | fc45cf03bf | ||
|   | efde11a0f3 | ||
|   | fb951ff9b5 | ||
|   | f5865e895e | ||
|   | b8c2d4c6c7 | ||
|   | 778ad49ab4 | ||
|   | fe7dd1305f | ||
|   | 378a3df5ea | ||
|   | e63b81d69c | ||
|   | c2fa2cdd8a | ||
|   | 6f215427f8 | ||
|   | 67ed4e0570 | ||
|   | 8a15178557 | ||
|   | f3ecfab378 | ||
|   | 804563d5b2 | ||
|   | c3e5d983b9 | ||
|   | d21edcfed5 | ||
|   | 9b69a45eee | ||
|   | 7424ee755a | ||
|   | ff664fd1d8 | ||
|   | fd3f03580d | ||
|   | a09150846a | ||
|   | f2a9c55637 | ||
|   | eaa3f83e46 | ||
|   | e786cc1454 | ||
|   | cd0c632fcb | ||
|   | d5f248cb82 | ||
|   | 6a800abe06 | ||
|   | fe265554a7 | ||
|   | 7305466765 | ||
|   | 9ac55a98f1 | ||
|   | a1149fb6ad | ||
|   | dfc4cc4aaf | ||
|   | ef7541291b | ||
|   | 248a3e6b7e | ||
|   | 6b1e381750 | ||
|   | feaf6108f9 | ||
|   | 9ad104585d | ||
|   | 7c44af3616 | ||
|   | 0599bf41b0 | ||
|   | eac303f84c | ||
|   | 7bdd1c625c | ||
|   | 0003760fd9 | ||
|   | eafb8c8e38 | ||
|   | 60c8105559 | ||
|   | 54626c6f7e | ||
|   | cca528a630 | ||
|   | 530193a3a2 | ||
|   | 52e3149040 | ||
|   | ad297ea9dc | ||
|   | 2015fc97dc | ||
|   | 35889a0f7d | ||
|   | dcd84a4b53 | ||
|   | 01374989b1 | ||
|   | f300462003 | ||
|   | 1e4da1b99c | ||
|   | e1de6cb98d | ||
|   | 59a18c4ead | ||
|   | b881e4b62a | ||
|   | 7060ae077b | ||
|   | a7b0910566 | ||
|   | 60706f72d4 | ||
|   | 2e77f6b34b | ||
|   | 50d5030949 | ||
|   | 03dbb0f931 | ||
|   | 249619fabd | ||
|   | 60d1f7797c | ||
|   | dc4b8c876e | ||
|   | 1724723e63 | ||
|   | 02455b1e28 | ||
|   | 47a9b97f8b | ||
|   | 0aa29daa72 | ||
|   | 182b1c1941 | ||
|   | bbc8010de3 | ||
|   | 15baa56584 | ||
|   | a72683707f | ||
|   | f4baa4ddf8 | ||
|   | f9eb36434f | ||
|   | bc5e16bcea | ||
|   | 86e04048ff | ||
|   | d7c0ff5e89 | ||
|   | 51e1efa277 | ||
|   | 67f636cc68 | ||
|   | fcdfebd0e7 | ||
|   | 069ec3b910 | ||
|   | 1092f55b54 | ||
|   | 8a8abf12f9 | ||
|   | f044e29526 | ||
|   | 4f703019ca | ||
|   | 0e6baec350 | ||
|   | 08690d658f | ||
|   | 674325e96c | ||
|   | f8aa486ad1 | ||
|   | be78f0f36a | ||
|   | 9917f05be8 | ||
|   | d23228522c | ||
|   | 8c9896561e | ||
|   | 23cdb2125e | ||
|   | 43fc598bd5 | ||
|   | 2105d2f952 | ||
|   | d18ec919dd | ||
|   | da3e5f9746 | ||
|   | 594a227bc1 | ||
|   | 7277744dc0 | ||
|   | e4718d3f7f | ||
|   | 5afd04781c | ||
|   | a0868bac6b | ||
|   | 265b1f0292 | ||
|   | 2c12f3a509 | ||
|   | 0141a2be9e | ||
|   | 6660342d2f | ||
|   | 92667e3b7f | ||
|   | a0632e3e02 | ||
|   | c6d2946ce6 | ||
|   | 86304418a9 | ||
|   | 2f228e21da | ||
|   | 8521556723 | ||
|   | 9f5ac0e6d5 | ||
|   | cc38dd1d49 | ||
|   | 66a5c0c094 | ||
|   | 8e802fd32f | ||
|   | e87656af16 | ||
|   | 8327dbd611 | ||
|   | e1b575dddc | ||
|   | 018025dada | ||
|   | 49fe0335ee | ||
|   | bbe417121c | ||
|   | 0068098996 | ||
|   | 36d9cc329d | ||
|   | 1f2e53dd15 | ||
|   | 048a14f1f3 | ||
|   | fd6ef482f0 | ||
|   | d8926b96e2 | ||
|   | 934ec3c3fe | ||
|   | 216e996f66 | ||
|   | a4b5583995 | ||
|   | b00a1d6b7f | ||
|   | d24abab4a8 | ||
|   | 23a9fb0314 | ||
|   | 5e26d0c90c | ||
|   | aeb971c33a | ||
|   | 8897385714 | ||
|   | e2a17fa8b4 | ||
|   | 14da6b68dc | ||
|   | 9a01a7ae07 | ||
|   | c3bf4a325d | ||
|   | 2d71456944 | ||
|   | 9934529a0b | ||
|   | ef6e8f5bb2 | ||
|   | 2ce7a3baa6 | ||
|   | c0d224e200 | ||
|   | 4b2e5a4375 | ||
|   | fbecbca9a0 | ||
|   | c312891774 | ||
|   | 486d4dfb68 | ||
|   | 169b00aa64 | ||
|   | f2c1a416bf | ||
|   | 28ef88911c | ||
|   | e2161f385d | ||
|   | f0c0687b43 | ||
|   | e76e874093 | ||
|   | d2a0cfb6d4 | ||
|   | e5270cb6ec | ||
|   | ee360d8749 | ||
|   | bb4e6fd2da | ||
|   | 651600ac19 | ||
|   | 67f4cf3cff | ||
|   | 9b76e9f90a | ||
|   | ba919b9c6a | ||
|   | 4da2862f8f | ||
|   | b1de1ada25 | ||
|   | 1002bbc212 | ||
|   | 15e42c4d5f | ||
|   | bb61dd4b44 | ||
|   | 2d4ba30ba2 | ||
|   | 54f46e8486 | ||
|   | 0ee762263a | ||
|   | 826682cc07 | ||
|   | 8b6e566728 | ||
|   | e4a20e51e2 | ||
|   | 173ff91640 | ||
|   | 438d0d4feb | ||
|   | 8f275a79a9 | ||
|   | a504dd866b | ||
|   | dfc51ef243 | ||
|   | a25d2dc256 | ||
|   | b2ae945cf2 | ||
|   | 681861c8c7 | ||
|   | 6d40cb98e7 | ||
|   | 81d7ab1e49 | ||
|   | 2212b4ea71 | ||
|   | 5650355da5 | ||
|   | 20a9206919 | ||
|   | 3533b2a8bc | ||
|   | b8d2238ff0 | ||
|   | 96c8870820 | ||
|   | 177e9316b0 | ||
|   | 27b7e6ef4b | ||
|   | 32d686d90f | ||
|   | f812e9be7d | ||
|   | c7185d597b | ||
|   | 6380526c12 | ||
|   | fa4f6c4561 | ||
|   | a5b7eaec1a | ||
|   | d165295c83 | ||
|   | 5f5874f8ca | ||
|   | ab5e950cd0 | ||
|   | 3e760f5ca6 | ||
|   | 9e8a24467e | ||
|   | d84d1f79a0 | ||
|   | 024ec36cc0 | ||
|   | 51b1258866 | ||
|   | ff1b76f4c7 | ||
|   | 0389ae5299 | ||
|   | 35ab60b712 | ||
|   | 0ea15f70c3 | ||
|   | f1c7a36dde | ||
|   | cc9093f118 | ||
|   | 775a2ea051 | ||
|   | cb3d5c2b51 | ||
|   | dc1c31d704 | ||
|   | 777616d8b0 | ||
|   | 8aeebbbf78 | ||
|   | dade67ba5a | ||
|   | 921a9071a1 | ||
|   | 90a08ba0b6 | ||
|   | 4e52ed9df7 | ||
|   | 01b51cd081 | ||
|   | d1efc274e5 | ||
|   | d48d787c1e | ||
|   | e4ad31a5dd | ||
|   | b61cb92053 | ||
|   | e72450f5d8 | ||
|   | d9211b8e20 | ||
|   | c16b83fc3e | ||
|   | 8b5a44e119 | ||
|   | b0be6b8678 | ||
|   | bb73547acf | ||
|   | d8fc58e174 | ||
|   | b9fdffbb62 | ||
|   | e48a83fcfc | ||
|   | 4c7eae7ef2 | ||
|   | 75745fc23f | ||
|   | 82479db084 | ||
|   | 9becb3985d | ||
|   | 4cb7df67b7 | ||
|   | e0a12f55b9 | ||
|   | b18240370d | ||
|   | 4253df6463 | ||
|   | d8b43865a2 | ||
|   | 3eb8b9ef68 | ||
|   | f96077bd0f | ||
|   | 741846d1ff | ||
|   | f36d7bd078 | ||
|   | bd51c92d65 | ||
|   | 2aed6ade79 | ||
|   | 83e5ea4827 | ||
|   | 5915348396 | ||
|   | 567dc70c9b | ||
|   | 8323891294 | ||
|   | 5bf8695295 | ||
|   | 7012437929 | ||
|   | 83ad98d640 | ||
|   | 1816f763d2 | ||
|   | 1a01f07b2c | ||
|   | 23d48fb334 | ||
|   | dada0420b1 | ||
|   | 17a336c795 | ||
|   | 8c347965d7 | ||
|   | e1816cd228 | ||
|   | 3614da6845 | ||
|   | 8aa1765f24 | ||
|   | 8eb0782f25 | ||
|   | 5f6aba7f4b | ||
|   | 986b14fb1b | ||
|   | 7c03b88b74 | ||
|   | 1f277ebcb9 | ||
|   | 8c54bc68e0 | ||
|   | 6e39be5b19 | ||
|   | cf71ea016f | ||
|   | 4affa5528a | ||
|   | 8536f9312c | ||
|   | a0e8456cb5 | ||
|   | 77074b5646 | ||
|   | cd68ed8297 | ||
|   | eaef067b3e | 
							
								
								
									
										205
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						| @@ -1,3 +1,208 @@ | ||||
| 3.18.0 | ||||
| ====== | ||||
|  | ||||
| Translations: | ||||
|   Sendy Aditya Suryana [id], Kris Thomsen [da], Seán de Búrca [ga], | ||||
|   Andika Triwidada [id], Enrico Nicoletto [pt_BR], Anders Jonsson [sv], | ||||
|   Rūdolfs Mazurs [lv] | ||||
|  | ||||
| 3.17.92 | ||||
| ======= | ||||
| * Fix race when loading multiple background animations [Josselin; #741453] | ||||
|  | ||||
| Contributors: | ||||
|   Michael Biebl, Josselin Mouette, Florian Müllner | ||||
|  | ||||
| Translations: | ||||
|   Baurzhan Muftakhidinov [kk], Changwoo Ryu [ko], Christian Kirbach [de], | ||||
|   Kjartan Maraas [nb], Jiri Grönroos [fi], Arash Mousavi [fa], | ||||
|   Jiro Matsuzawa [ja], Marek Černocký [cs], Milo Casagrande [it] | ||||
|  | ||||
| 3.17.91 | ||||
| ======= | ||||
| * Fix login screen spinner causing wakeups while VT-switched away | ||||
|   [Ray, Rui; #753891] | ||||
| * Fix scrolling of user list on login screen [Florian; #754525] | ||||
|  | ||||
| Contributors: | ||||
|   Piotr Drąg, Rui Matos, Florian Müllner, Ray Strode | ||||
|  | ||||
| Translations: | ||||
|   Dušan Kazik [sk], Jordi Mas [ca], Aurimas Černius [lt], Stas Solovey [ru], | ||||
|   Piotr Drąg [pl], Pedro Albuquerque [pt], Daniel Mustieles [es], | ||||
|   Chao-Hsiung Liao [zh_TW], Muhammet Kara [tr], Fran Dieguez [gl], | ||||
|   Hannie Dumoleyn [nl], Yosef Or Boczko [he], Tom Tryfonidis [el], | ||||
|   A S Alam [pa], Balázs Úr [hu], Alexandre Franke [fr], Frédéric Péters [fr] | ||||
|  | ||||
| 3.17.90 | ||||
| ======= | ||||
| * Avoid caret/focus viewport changes during pointer movement [Rui; #752138] | ||||
| * Match GTK+'s modal dialogs for system modal dialogs [Carlos; #746108] | ||||
| * Refine message list style [Florian; #749958] | ||||
| * Fix type-ahead behavior for backspace and compose key [Rui; #753319, #753320] | ||||
| * Refine the system status menu [Florian; #751377] | ||||
| * Misc. bug fixes and cleanups [Bastien, Ray, Florian, Jakub; #752779, #752739, | ||||
|   #741366, #651503, #753064, #753181, #752881] | ||||
|  | ||||
| Contributors: | ||||
|   Rui Matos, Florian Müllner, Bastien Nocera, Carlos Soriano, Jakub Steiner, | ||||
|   Ray Strode, Rico Tzschichholz | ||||
|  | ||||
| Translations: | ||||
|   Marek Černocký [cs], Kjartan Maraas [nb], Jordi Mas [ca], Muhammet Kara [tr], | ||||
|   Enrico Nicoletto [pt_BR] | ||||
|  | ||||
| 3.17.4 | ||||
| ====== | ||||
| * Fix fuzziness of app menu icon [Jakub; #747932] | ||||
| * Implement 4 finger swipe gesture for touchpads [Carlos; #752250] | ||||
| * Misc. bug fixes [Florian, Alexandre, Piotr, Ray, Mario; #751921, #659969, | ||||
|   #752438, #752675] | ||||
|  | ||||
| Contributors: | ||||
|   Piotr Drąg, Alexandre Franke, Carlos Garnacho, Florian Müllner, | ||||
|   Mario Sanchez Prada, Jakub Steiner, Jasper St. Pierre, Ray Strode | ||||
|  | ||||
| Translations: | ||||
|   Benjamin Steinwender [de], Pedro Albuquerque [pt], Fabio Tomat [fur], | ||||
|   Matej Urbančič [sl], Daniel Mustieles [es], Yosef Or Boczko [he], | ||||
|   Daniel Martinez [an] | ||||
|  | ||||
| 3.17.3 | ||||
| ====== | ||||
| * Handle touch events in OSK on wayland [Rui; #750287] | ||||
| * Reinstate left/right movement to window menu [Ron; #751344] | ||||
| * Allow extensions to disable "Window is ready" notification [Adel; #748846] | ||||
| * Misc. bug fixes [Watson, Michael, Ray, Rui, Florian, Cosimo; #750465, | ||||
|   #751016, #751517, #750714, #751541, #751599] | ||||
|  | ||||
| Contributors: | ||||
|   Michael Biebl, Cosimo Cecchi, Adel Gadllah, Rui Matos, Florian Müllner, | ||||
|   Ray Strode, Wim Taymans, Ron Yorston, Watson Yuuma Sato | ||||
|  | ||||
| Translations: | ||||
|   Sebastian Rasmussen [sv], Dimitris Spingos [el], Muhammet Kara [tr], | ||||
|   Stas Solovey [ru], Benjamin Steinwender [de], Balázs Úr [hu], | ||||
|   Victor Ibragimov [tg], Dušan Kazik [sk], Pedro Albuquerque [pt] | ||||
|  | ||||
| 3.17.2 | ||||
| ====== | ||||
| * Remove StTable widget [Florian; #703833] | ||||
| * Increase visibility of expanders in alt-tab popup [Jakub; #745058] | ||||
| * Ensure suspend inhibitors are released when VT switched away [Rui; #749228] | ||||
| * Use iio-sensor-proxy directly for orientation lock [Bastien; #749671] | ||||
| * Misc. bug fixes [Florian, Lan, Carlos; #749279, #749383, #749529, #749490, | ||||
|   #749742] | ||||
|  | ||||
| Contributors: | ||||
|   Carlos Garnacho, Ting-Wei Lan, Rui Matos, Florian Müllner, Bastien Nocera, | ||||
|   Jakub Steiner | ||||
|  | ||||
| Translations: | ||||
|   Yosef Or Boczko [he], sun [zh_CN], Felipe Braga [pt_BR], | ||||
|   Victor Ibragimov [tg], Gábor Kelemen [hu], Cédric Valmary [oc], | ||||
|   Dušan Kazik [sk], Kjartan Maraas [nb], Bruno Ramalhete [pt], | ||||
|   Matej Urbančič [sl], Daniel Mustieles [es] | ||||
|  | ||||
| 3.17.1 | ||||
| ====== | ||||
| * Add Display Settings entry to background menu [Meet; #697346] | ||||
| * Add window menu option to move to different monitor [Isaac; #633994] | ||||
| * Improve switch style in default/highContrast themes [Jakub; #746294, #747912] | ||||
| * Make event highlight in calendar more prominent [Jakub; #747715] | ||||
| * Fix keyboard focus when focusing a notification banner [Florian; #747205] | ||||
| * Move notification banners below the dateMenu [Meet, Florian; #745910] | ||||
| * Misc. bug fixes [Mario, Rui; #748338, #748541] | ||||
|  | ||||
| Contributors: | ||||
|   Isaac Ge, Rui Matos, Florian Müllner, Meet Parikh, Mario Sanchez Prada, | ||||
|   Jakub Steiner, Jasper St. Pierre | ||||
|  | ||||
| Translations: | ||||
|   Sveinn í Felli [is], Marek Černocký [cs], laurent Soleil [oc] | ||||
|  | ||||
| 3.16.1 | ||||
| ====== | ||||
| * gdm: Move long session chooser menus to the side [Florian; #734352] | ||||
| * Work around background corruption with NVIDIA driver [Rui; #739178] | ||||
| * Don't allow move-to-workspace for always-sticky windows [Florian; #746782] | ||||
| * Allow switching workspaces with PgUp/PgDown in overview [Devyani; #742581] | ||||
| * Bump time PAM messages are displayed [Sarvjeet; #720885] | ||||
| * Fix "stutter" when moving window past the last workspace [Shivam; #712778] | ||||
| * Fix blurred text on login screen [Clément; #746912] | ||||
| * keyboard: Restore whole MRU list after password mode [Rui; #746605] | ||||
| * Pass event timestamps when activating remote actions [Owen; #747323] | ||||
| * Fix hung login screen when password is typed too quickly [Shivam; #737586] | ||||
| * Make on-screen keyboard work for shell chrome on wayland [Rui; #747274] | ||||
| * Implement reexec_self() for FreeBSD [Ting-Wei; #747788] | ||||
| * Allow to dismiss resident notifications [Florian; #746860] | ||||
| * Temporarily reveal legacy tray when icons are added [Florian; #746025] | ||||
| * Make concealed tray smaller to minimize overlap with apps [Florian; #746787] | ||||
| * Misc. bug fixes [Florian, Rui, Giovanni; #746323, #746579, #746902, #746364, | ||||
|   #746509, #747636] | ||||
|  | ||||
| Contributors: | ||||
|   Sarvjeet, Giovanni Campagna, Adel Gadllah, Clément Guérin, Devyani Kota, | ||||
|   Ting-Wei Lan, Rui Matos, Shivam Mishra, Florian Müllner, Owen W. Taylor | ||||
|  | ||||
| Translations: | ||||
|   Khaled Hosny [ar], Dušan Kazik [sk], Yuri Myasoedov [ru], Stas Solovey [ru], | ||||
|   Hannie Dumoleyn [nl], Rūdolfs Mazurs [lv] | ||||
|  | ||||
| 3.16.0 | ||||
| ====== | ||||
| * Revert erroneous login dialog changes [Ray; #740142] | ||||
| * Improve accessibility of legacy tray [Florian; #746487] | ||||
| * Fix legacy status icons leaking into other monitors [Florian; #745824] | ||||
|  | ||||
| Contributors: | ||||
|   Florian Müllner, Ray Strode | ||||
|  | ||||
| Translations: | ||||
|   Daniel Martinez [an], Sebastian Rasmussen [sv], Fran Dieguez [gl], | ||||
|   Andika Triwidada [id], Jordi Mas [ca], Kjartan Maraas [nb], | ||||
|   Inaki Larranaga Murgoitio [eu], Muhammet Kara [tr], Khaled Hosny [ar], | ||||
|   Bernd Homuth [de], Jiro Matsuzawa [ja] | ||||
|  | ||||
| 3.15.92 | ||||
| ======= | ||||
| * gdm: Fix user list accessibility [Florian; #729603] | ||||
| * Handle multiline questions in mount operations [Ross; #745713] | ||||
| * Improve classic theme [Jakub; #745686, #745687] | ||||
| * Fix ordering of calendar events [Florian; #745988] | ||||
| * Pick first input source for new windows when per-window [Rui; #746037] | ||||
| * networkAgent: Show a notification for non-user-initiated password requests | ||||
|   [Giovanni; #660293] | ||||
| * Fix dismissing calendar events [Florian; #744927] | ||||
| * Add legacy tray to ctrl-alt-tab popup [Florian; #746022] | ||||
| * Manage on-screen-keyboard visibility in gnome-shell [Carlos; #745977] | ||||
| * Add pointer barriers to legacy tray [Cosimo; #746026] | ||||
| * Use fallback when app icon cannot be resolved [Cosimo; #746219] | ||||
| * Fix handling of removed smartcard at startup [Ray; #740143] | ||||
| * gdm: Don't pick a random session for the user [Jasper; #740142] | ||||
| * Make menu selection behavior consistent with GTK [Florian; #745246] | ||||
| * gdm: Fix empty user list on user switching [Ray; #719418] | ||||
| * Misc bug fixes [Florian, Giovanni, Clément, Rui; #745666, #746019, #745861, | ||||
|   #746027, #746223, #737502, #746343, #746288] | ||||
|  | ||||
| Contributors: | ||||
|   Giovanni Campagna, Cosimo Cecchi, Piotr Drąg, Adel Gadllah, Carlos Garnacho, | ||||
|   Clément Guérin, Ross Lagerwall, Rui Matos, Florian Müllner, Jakub Steiner, | ||||
|   Jasper St. Pierre, Ray Strode | ||||
|  | ||||
| Translations: | ||||
|   Piotr Drąg [pl], Changwoo Ryu [ko], Milo Casagrande [it], | ||||
|   Baurzhan Muftakhidinov [kk], Мирослав Николић [sr, sr@latin], Balázs Úr [hu], | ||||
|   IWAI, Masaharu [ja], Daniel Korostil [uk], Aurimas Černius [lt], | ||||
|   Matej Urbančič [sl], Daniel Mustieles [es], Kjartan Maraas [nb], | ||||
|   Victor Ibragimov [tg], Claude Paroz [fr], Jordi Mas [ca], Bernd Homuth [de], | ||||
|   Muhammet Kara [tr], Frédéric Péters [fr], Jiri Grönroos [fi], | ||||
|   Alexander Shopov [bg], Stas Solovey [ru], Trần Ngọc Quân [vi], | ||||
|   Samir Ribic [bs], Dušan Kazik [sk], Enrico Nicoletto [pt_BR], | ||||
|   Marek Černocký [cs], A S Alam [pa], Ask Hjorth Larsen [da], | ||||
|   Tom Tryfonidis [el], Alexandre Franke [fr], Yosef Or Boczko [he], | ||||
|   Chao-Hsiung Liao [zh_TW] | ||||
|  | ||||
| 3.15.91 | ||||
| ======= | ||||
| * Don't disable all shortcuts while non-panel menus are open [Florian; #745039] | ||||
|   | ||||
							
								
								
									
										21
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						| @@ -1,5 +1,6 @@ | ||||
| AC_PREREQ(2.63) | ||||
| AC_INIT([gnome-shell],[3.15.91],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) | ||||
| AC_INIT([gnome-shell],[3.18.0],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) | ||||
| AX_IS_RELEASE([git-directory]) | ||||
|  | ||||
| AC_CONFIG_HEADERS([config.h]) | ||||
| AC_CONFIG_SRCDIR([src/shell-global.c]) | ||||
| @@ -63,8 +64,8 @@ AC_ARG_ENABLE([systemd], | ||||
|               [enable_systemd=$enableval], | ||||
|               [enable_systemd=auto]) | ||||
| AS_IF([test x$enable_systemd != xno], [ | ||||
|   AC_MSG_CHECKING([for libsystemd-journal]) | ||||
|   PKG_CHECK_EXISTS([libsystemd-journal], | ||||
|   AC_MSG_CHECKING([for libsystemd]) | ||||
|   PKG_CHECK_EXISTS([libsystemd], | ||||
|                    [have_systemd=yes | ||||
|                     AC_DEFINE([HAVE_SYSTEMD], [1], [Define if we have systemd])], | ||||
|                    [have_systemd=no]) | ||||
| @@ -74,13 +75,13 @@ AS_IF([test x$enable_systemd != xno], [ | ||||
| AC_MSG_RESULT($enable_systemd) | ||||
|  | ||||
| CLUTTER_MIN_VERSION=1.21.5 | ||||
| GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1 | ||||
| GOBJECT_INTROSPECTION_MIN_VERSION=1.45.4 | ||||
| GJS_MIN_VERSION=1.39.0 | ||||
| MUTTER_MIN_VERSION=3.15.91 | ||||
| MUTTER_MIN_VERSION=3.18.0 | ||||
| GTK_MIN_VERSION=3.15.0 | ||||
| GIO_MIN_VERSION=2.37.0 | ||||
| GIO_MIN_VERSION=2.45.3 | ||||
| LIBECAL_MIN_VERSION=3.5.3 | ||||
| LIBEDATASERVER_MIN_VERSION=3.13.90 | ||||
| LIBEDATASERVER_MIN_VERSION=3.17.2 | ||||
| TELEPATHY_GLIB_MIN_VERSION=0.17.5 | ||||
| POLKIT_MIN_VERSION=0.100 | ||||
| STARTUP_NOTIFICATION_MIN_VERSION=0.11 | ||||
| @@ -106,7 +107,7 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION | ||||
|             polkit-agent-1 >= $POLKIT_MIN_VERSION | ||||
|             gcr-base-3 >= $GCR_MIN_VERSION" | ||||
| if test x$have_systemd = xyes; then | ||||
|   SHARED_PCS="${SHARED_PCS} libsystemd-journal" | ||||
|   SHARED_PCS="${SHARED_PCS} libsystemd" | ||||
| fi | ||||
|  | ||||
| PKG_CHECK_MODULES(GNOME_SHELL, $SHARED_PCS) | ||||
| @@ -220,7 +221,7 @@ if test "$enable_man" != no; then | ||||
| fi | ||||
| AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no) | ||||
|  | ||||
| GNOME_COMPILE_WARNINGS([error]) | ||||
| AX_COMPILER_FLAGS() | ||||
| case "$WARN_CFLAGS" in | ||||
|     *-Werror*) | ||||
|         WARN_CFLAGS="$WARN_CFLAGS -Wno-error=deprecated-declarations" | ||||
| @@ -269,7 +270,7 @@ Build configuration: | ||||
|        Prefix:                                 ${prefix} | ||||
|        Source code location:                   ${srcdir} | ||||
|        Compiler:                               ${CC} | ||||
|        Compiler Warnings:                      $enable_compile_warnings | ||||
|        Compiler Warnings:                      $ax_enable_compile_warnings | ||||
|  | ||||
|        Support for NetworkManager:             $have_networkmanager | ||||
|        Support for GStreamer recording:        $build_recorder | ||||
|   | ||||
| @@ -32,8 +32,10 @@ | ||||
|     <file>summary-counter.svg</file> | ||||
|     <file>toggle-off-us.svg</file> | ||||
|     <file>toggle-off-intl.svg</file> | ||||
|     <file>toggle-off-hc.svg</file> | ||||
|     <file>toggle-on-us.svg</file> | ||||
|     <file>toggle-on-intl.svg</file> | ||||
|     <file>toggle-on-hc.svg</file> | ||||
|     <file>ws-switch-arrow-up.png</file> | ||||
|     <file>ws-switch-arrow-down.png</file> | ||||
|   </gresource> | ||||
|   | ||||
| @@ -51,6 +51,7 @@ | ||||
|     </key> | ||||
|     <key name="looking-glass-history" type="as"> | ||||
|       <default>[]</default> | ||||
|       <!-- Translators: looking glass is a debugger and inspector tool, see https://live.gnome.org/GnomeShell/LookingGlass --> | ||||
|       <_summary>History for the looking glass dialog</_summary> | ||||
|     </key> | ||||
|     <key name="always-show-log-out" type="b"> | ||||
|   | ||||
| @@ -10,11 +10,11 @@ | ||||
|    xmlns:xlink="http://www.w3.org/1999/xlink" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    width="29" | ||||
|    height="29" | ||||
|    width="24" | ||||
|    height="24" | ||||
|    id="svg10621" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.2 r9819" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="calendar-today.svg"> | ||||
|   <defs | ||||
|      id="defs10623"> | ||||
| @@ -118,17 +118,6 @@ | ||||
|        fx="51" | ||||
|        fy="30" | ||||
|        r="42" /> | ||||
|     <radialGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient34508-1-3" | ||||
|        id="radialGradient3113" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)" | ||||
|        cx="51" | ||||
|        cy="30" | ||||
|        fx="51" | ||||
|        fy="30" | ||||
|        r="42" /> | ||||
|   </defs> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
| @@ -137,22 +126,23 @@ | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="15.839192" | ||||
|      inkscape:cx="20.652108" | ||||
|      inkscape:cy="11.839084" | ||||
|      inkscape:zoom="8" | ||||
|      inkscape:cx="-23.537329" | ||||
|      inkscape:cy="-31.442864" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="layer1" | ||||
|      showgrid="true" | ||||
|      showgrid="false" | ||||
|      fit-margin-top="0" | ||||
|      fit-margin-left="0" | ||||
|      fit-margin-right="0" | ||||
|      fit-margin-bottom="0" | ||||
|      inkscape:window-width="1280" | ||||
|      inkscape:window-height="741" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|      borderlayer="true"> | ||||
|      inkscape:window-width="2133" | ||||
|      inkscape:window-height="1241" | ||||
|      inkscape:window-x="238" | ||||
|      inkscape:window-y="88" | ||||
|      inkscape:window-maximized="0" | ||||
|      borderlayer="true" | ||||
|      inkscape:showpageshadow="false"> | ||||
|     <inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid3109" | ||||
| @@ -169,7 +159,7 @@ | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title /> | ||||
|         <dc:title></dc:title> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
| @@ -177,28 +167,12 @@ | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1" | ||||
|      transform="translate(-469.08263,-532.99307)"> | ||||
|     <path | ||||
|        sodipodi:type="arc" | ||||
|        style="opacity:0.4625;color:#000000;fill:url(#radialGradient3113);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" | ||||
|        id="path34506-3" | ||||
|        sodipodi:cx="51" | ||||
|        sodipodi:cy="30" | ||||
|        sodipodi:rx="42" | ||||
|        sodipodi:ry="16" | ||||
|        d="M 9,29.999999 A 42,16 0 0 1 93,30 l -42,0 z" | ||||
|        sodipodi:start="3.1415927" | ||||
|        sodipodi:end="6.2831853" | ||||
|        transform="matrix(0.43692393,0,0,1.3783114,461.29951,517.6437)" | ||||
|        inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png" | ||||
|        inkscape:export-xdpi="90" | ||||
|        inkscape:export-ydpi="90" /> | ||||
|     <rect | ||||
|        style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" | ||||
|        id="rect2996" | ||||
|        width="31" | ||||
|        height="3" | ||||
|        x="468.08264" | ||||
|        y="558.99304" /> | ||||
|      transform="translate(-469.08263,-537.99307)"> | ||||
|     <circle | ||||
|        style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:0.23756906;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|        id="path7305" | ||||
|        cx="481.57138" | ||||
|        cy="559.4649" | ||||
|        r="1.5" /> | ||||
|   </g> | ||||
| </svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 5.6 KiB | 
| @@ -37,14 +37,13 @@ stage { | ||||
|   icon-shadow: 0 1px black; } | ||||
|   .button:focus { | ||||
|     color: #eeeeec; | ||||
|     border-color: #215d9c; | ||||
|     box-shadow: inset 0 1px #454f52; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; } | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .button:insensitive { | ||||
|     color: #7f7f7f; | ||||
|     color: gray; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: rgba(62, 67, 68, 0.7); | ||||
|     background-color: rgba(62, 67, 69, 0.7); | ||||
|     box-shadow: none; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
| @@ -52,9 +51,46 @@ stage { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|  | ||||
| .modal-dialog-linked-button { | ||||
|   border-right-width: 1px; | ||||
|   color: #eeeeec; | ||||
|   background-color: #2e3436; | ||||
|   border-color: rgba(0, 0, 0, 0.7); | ||||
|   box-shadow: inset 0 1px #454f52; | ||||
|   text-shadow: 0 1px black; | ||||
|   icon-shadow: 0 1px black; | ||||
|   padding: 12px; } | ||||
|   .modal-dialog-linked-button:insensitive { | ||||
|     color: gray; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: rgba(62, 67, 69, 0.7); | ||||
|     box-shadow: none; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .modal-dialog-linked-button:active { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .modal-dialog-linked-button:focus { | ||||
|     color: #eeeeec; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .modal-dialog-linked-button:first-child { | ||||
|     border-radius: 0px 0px 0px 6px; } | ||||
|   .modal-dialog-linked-button:last-child { | ||||
|     border-right-width: 0px; | ||||
|     border-radius: 0px 0px 6px 0px; } | ||||
|   .modal-dialog-linked-button:first-child:last-child { | ||||
|     border-right-width: 0px; | ||||
|     border-radius: 0px 0px 6px 6px; } | ||||
|  | ||||
| /* Entries */ | ||||
| StEntry { | ||||
| @@ -71,8 +107,8 @@ StEntry { | ||||
|     box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); | ||||
|     border-color: rgba(166, 166, 166, 0.5); } | ||||
|   StEntry:insensitive { | ||||
|     color: #7f7f7f; | ||||
|     border-color: #0d0d0d; | ||||
|     color: gray; | ||||
|     border-color: #0e0e0e; | ||||
|     box-shadow: none; } | ||||
|   StEntry StIcon.capslock-warning { | ||||
|     icon-size: 16px; | ||||
| @@ -80,12 +116,13 @@ StEntry { | ||||
|     padding: 0 4px; } | ||||
|  | ||||
| /* Scrollbars */ | ||||
| StScrollView.vfade { | ||||
|   -st-vfade-offset: 68px; } | ||||
| StScrollView.hfade { | ||||
|   -st-hfade-offset: 68px; } | ||||
|  | ||||
| StScrollBar { | ||||
|   padding: 0; } | ||||
|   StScrollBar.vfade { | ||||
|     -st-vfade-offset: 68px; } | ||||
|   StScrollBar.hfade { | ||||
|     -st-hfade-offset: 68px; } | ||||
|   StScrollView StScrollBar { | ||||
|     min-width: 14px; | ||||
|     min-height: 14px; } | ||||
| @@ -94,10 +131,10 @@ StScrollBar { | ||||
|     background-color: transparent; } | ||||
|   StScrollBar StButton#vhandle, StScrollBar StButton#hhandle { | ||||
|     border-radius: 8px; | ||||
|     background-color: #000; | ||||
|     background-color: #999999; | ||||
|     margin: 3px; } | ||||
|     StScrollBar StButton#vhandle:hover, StScrollBar StButton#hhandle:hover { | ||||
|       background-color: #1a1a1a; } | ||||
|       background-color: #cccccc; } | ||||
|     StScrollBar StButton#vhandle:active, StScrollBar StButton#hhandle:active { | ||||
|       background-color: #215d9c; } | ||||
|  | ||||
| @@ -105,7 +142,7 @@ StScrollBar { | ||||
| .slider { | ||||
|   height: 1em; | ||||
|   -slider-height: 0.3em; | ||||
|   -slider-background-color: #0d0d0d; | ||||
|   -slider-background-color: #0e0e0e; | ||||
|   -slider-border-color: black; | ||||
|   -slider-active-background-color: #215d9c; | ||||
|   -slider-active-border-color: #184472; | ||||
| @@ -159,11 +196,12 @@ StScrollBar { | ||||
|   background-color: white; } | ||||
|  | ||||
| .modal-dialog { | ||||
|   border-radius: 5px; | ||||
|   border-radius: 9px; | ||||
|   color: #eeeeec; | ||||
|   background-color: rgba(23, 25, 26, 0.95); | ||||
|   border: 3px solid rgba(238, 238, 236, 0.5); | ||||
|   padding: 24px; } | ||||
|   border: 3px solid rgba(238, 238, 236, 0.5); } | ||||
|   .modal-dialog .modal-dialog-content-box { | ||||
|     padding: 24px; } | ||||
|   .modal-dialog .run-dialog-entry { | ||||
|     width: 20em; | ||||
|     margin-bottom: 6px; } | ||||
| @@ -178,10 +216,6 @@ StScrollBar { | ||||
|     color: #d6d6d1; | ||||
|     padding-bottom: .4em; } | ||||
|  | ||||
| .button-dialog-button-box { | ||||
|   spacing: 18px; | ||||
|   padding-top: 48px; } | ||||
|  | ||||
| .show-processes-dialog-subject, | ||||
| .mount-question-dialog-subject, | ||||
| .end-session-dialog-subject { | ||||
| @@ -323,7 +357,7 @@ StScrollBar { | ||||
|  | ||||
| .prompt-dialog-headline { | ||||
|   font-weight: bold; | ||||
|   color: #b3b3b3; } | ||||
|   color: #b2b2a9; } | ||||
|  | ||||
| .prompt-dialog-description:rtl { | ||||
|   text-align: right; } | ||||
| @@ -392,19 +426,14 @@ StScrollBar { | ||||
|       background-color: black; | ||||
|       box-shadow: inset 0 1px 0px #0d0d0d; | ||||
|       font-weight: bold; } | ||||
|     .popup-menu .popup-menu-item:focus { | ||||
|       background-color: #215d9c; | ||||
|       color: #ffffff; } | ||||
|     .popup-menu .popup-menu-item:hover { | ||||
|     .popup-menu .popup-menu-item.selected { | ||||
|       background-color: rgba(255, 255, 255, 0.1); | ||||
|       color: #fff; } | ||||
|     .popup-menu .popup-menu-item:active { | ||||
|       background-color: #1c5187; | ||||
|       background-color: #215d9c; | ||||
|       color: #ffffff; } | ||||
|     .popup-menu .popup-menu-item:insensitive { | ||||
|       color: rgba(255, 255, 255, 0.5); } | ||||
|   .popup-menu .active { | ||||
|     background-color: #215d9c; } | ||||
|   .popup-menu .popup-inactive-menu-item { | ||||
|     color: #fff; } | ||||
|     .popup-menu .popup-inactive-menu-item:insensitive { | ||||
| @@ -415,7 +444,7 @@ StScrollBar { | ||||
|  | ||||
| .popup-menu-ornament { | ||||
|   text-align: right; | ||||
|   width: 1em; } | ||||
|   width: 1.2em; } | ||||
|  | ||||
| .popup-menu-boxpointer, | ||||
| .candidate-popup-boxpointer { | ||||
| @@ -492,10 +521,9 @@ StScrollBar { | ||||
|  | ||||
| .switcher-arrow { | ||||
|   border-color: transparent; | ||||
|   color: black; } | ||||
|  | ||||
| .switcher-arrow:highlighted { | ||||
|   color: #fff; } | ||||
|   color: rgba(255, 255, 255, 0.8); } | ||||
|   .switcher-arrow:highlighted { | ||||
|     color: #fff; } | ||||
|  | ||||
| .input-source-switcher-symbol { | ||||
|   font-size: 34pt; | ||||
| @@ -565,7 +593,7 @@ StScrollBar { | ||||
|     -panel-corner-border-color: transparent; } | ||||
|     #panel .panel-corner:active, #panel .panel-corner:overview, #panel .panel-corner:focus { | ||||
|       -panel-corner-border-color: #256ab1; } | ||||
|     #panel .panel-corner.lock-screen, #panel .panel-corner.login-screen, #panel .panel-cornerunlock-screen { | ||||
|     #panel .panel-corner.lock-screen, #panel .panel-corner.login-screen, #panel .panel-corner.unlock-screen { | ||||
|       -panel-corner-radius: 0; | ||||
|       -panel-corner-background-color: transparent; | ||||
|       -panel-corner-border-color: transparent; } | ||||
| @@ -707,15 +735,16 @@ StScrollBar { | ||||
|   border-left-width: 1px; } | ||||
|  | ||||
| .calendar-nonwork-day { | ||||
|   color: #7f7f7f; } | ||||
|   color: gray; } | ||||
|  | ||||
| .calendar-today { | ||||
|   font-weight: bold; | ||||
|   border: 1px solid rgba(0, 0, 0, 0.5); } | ||||
|  | ||||
| .calendar-day-with-events { | ||||
|   color: #f2f2f2; | ||||
|   font-weight: bold; } | ||||
|   color: white; | ||||
|   font-weight: bold; | ||||
|   background-image: url("resource:///org/gnome/shell/theme/calendar-today.svg"); } | ||||
|  | ||||
| .calendar-other-month-day { | ||||
|   color: rgba(255, 255, 255, 0.15); | ||||
| @@ -758,7 +787,12 @@ StScrollBar { | ||||
|     padding: 8px 8px 8px 0px; } | ||||
|  | ||||
| .message-icon-bin > StIcon { | ||||
|   icon-size: 48px; } | ||||
|   icon-size: 32px; } | ||||
|  | ||||
| .message-secondary-bin:ltr { | ||||
|   padding-left: 8px; } | ||||
| .message-secondary-bin:rtl { | ||||
|   padding-right: 8px; } | ||||
|  | ||||
| .message-secondary-bin { | ||||
|   color: #999999; } | ||||
| @@ -767,14 +801,20 @@ StScrollBar { | ||||
|   icon-size: 16px; } | ||||
|  | ||||
| .message-title { | ||||
|   font-weight: bold; } | ||||
|   font-weight: bold; | ||||
|   font-size: 1.1em; } | ||||
|  | ||||
| .message-content { | ||||
|   padding: 8px; } | ||||
|   padding: 8px; | ||||
|   font-size: .9em; } | ||||
|  | ||||
| .system-switch-user-submenu-icon { | ||||
|   icon-size: 24px; | ||||
|   border: 1px solid rgba(255, 255, 255, 0.4); } | ||||
| .system-switch-user-submenu-icon.user-icon { | ||||
|   icon-size: 20px; | ||||
|   padding: 0 2px; } | ||||
|  | ||||
| .system-switch-user-submenu-icon.default-icon { | ||||
|   icon-size: 16px; | ||||
|   padding: 0 4px; } | ||||
|  | ||||
| #appMenu { | ||||
|   spinner-image: url("resource:///org/gnome/shell/theme/process-working.svg"); | ||||
| @@ -794,12 +834,12 @@ StScrollBar { | ||||
|   padding: 13px; | ||||
|   border: 1px solid #0d0d0d; } | ||||
|   .system-menu-action:hover, .system-menu-action:focus { | ||||
|     color: #ffffff; | ||||
|     background-color: #215d9c; | ||||
|     background-color: rgba(255, 255, 255, 0.1); | ||||
|     color: #fff; | ||||
|     border: none; | ||||
|     padding: 14px; } | ||||
|   .system-menu-action:active { | ||||
|     background-color: #1c5187; | ||||
|     background-color: #215d9c; | ||||
|     color: #ffffff; } | ||||
|   .system-menu-action > StIcon { | ||||
|     icon-size: 16px; } | ||||
| @@ -820,7 +860,7 @@ StScrollBar { | ||||
| .popup-menu-icon { | ||||
|   icon-size: 1.09em; } | ||||
|  | ||||
| .window-close, .notification-close { | ||||
| .window-close { | ||||
|   background-image: url("resource:///org/gnome/shell/theme/close-window.svg"); | ||||
|   background-size: 32px; | ||||
|   height: 32px; | ||||
| @@ -831,12 +871,6 @@ StScrollBar { | ||||
|   .window-close:rtl { | ||||
|     -st-background-image-shadow: 2px 2px 6px rgba(0, 0, 0, 0.5); } | ||||
|  | ||||
| .notification-close { | ||||
|   -shell-close-overlap-x: 14px; | ||||
|   -shell-close-overlap-y: -12px; } | ||||
|   .notification-close:rtl { | ||||
|     -shell-close-overlap-x: -14px; } | ||||
|  | ||||
| /* NETWORK DIALOGS */ | ||||
| .nm-dialog { | ||||
|   max-height: 500px; | ||||
| @@ -844,7 +878,8 @@ StScrollBar { | ||||
|   min-width: 470px; } | ||||
|  | ||||
| .nm-dialog-content { | ||||
|   spacing: 20px; } | ||||
|   spacing: 20px; | ||||
|   padding: 24px; } | ||||
|  | ||||
| .nm-dialog-header-hbox { | ||||
|   spacing: 10px; } | ||||
| @@ -1026,7 +1061,7 @@ StScrollBar { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: none; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .app-view-control:first-child { | ||||
| @@ -1157,7 +1192,7 @@ StScrollBar { | ||||
|  | ||||
| /* NOTIFICATIONS & MESSAGE TRAY */ | ||||
| .url-highlighter { | ||||
|   link-color: #215d9c; } | ||||
|   link-color: #2a76c6; } | ||||
|  | ||||
| .notification-banner { | ||||
|   font-size: 11pt; | ||||
| @@ -1192,32 +1227,6 @@ StScrollBar { | ||||
|     .notification-banner .notification-button:hover, .notification-banner .notification-buttonfocus { | ||||
|       background-color: #292f30; } | ||||
|  | ||||
| .notification { | ||||
|   font-size: 11pt; | ||||
|   width: 34em; | ||||
|   margin: 5px; | ||||
|   border-radius: 6px; | ||||
|   color: #eeeeec; | ||||
|   background-color: #2e3436; | ||||
|   border: 1px solid black; | ||||
|   spacing-rows: 4px; | ||||
|   padding: 8px; | ||||
|   spacing-columns: 10px; } | ||||
|  | ||||
| .notification-unexpanded { | ||||
|   min-height: 48px; | ||||
|   height: 48px; } | ||||
|  | ||||
| .notification-with-image { | ||||
|   min-height: 159px; } | ||||
|  | ||||
| .notification-body { | ||||
|   spacing: 5px; } | ||||
|  | ||||
| .notification-actions { | ||||
|   paddinf-top: 18px; | ||||
|   spacing: 6px; } | ||||
|  | ||||
| .summary-source-counter { | ||||
|   font-size: 10pt; | ||||
|   font-weight: bold; | ||||
| @@ -1231,36 +1240,20 @@ StScrollBar { | ||||
|   box-shadow: 0 2px 2px rgba(0, 0, 0, 0.5); | ||||
|   border-radius: 0.9em; } | ||||
|  | ||||
| .notification-scrollview { | ||||
|   max-height: 18em; | ||||
|   -st-vfade-offset: 24px; } | ||||
|   .notification-scrollview:ltr > StScrollBar { | ||||
|     padding-left: 6px; } | ||||
|   .notification-scrollview:rtl > StScrollBar { | ||||
|     padding-right: 6px; } | ||||
|  | ||||
| .notification-button { | ||||
|   height: 24px; } | ||||
|  | ||||
| .notification-icon-button { | ||||
|   border-radius: 5px; | ||||
|   padding: 5px; | ||||
|   height: 24px; | ||||
|   width: 24px; } | ||||
|   .notification-icon-button > StIcon { | ||||
|     icons-size: 16px; | ||||
|     width: 16px; | ||||
|     height: 16px; | ||||
|     padding: 2px; } | ||||
|  | ||||
| .secondary-icon { | ||||
|   icon-size: 1.09em; } | ||||
|  | ||||
| .chat-body { | ||||
|   spacing: 5px; } | ||||
|  | ||||
| .chat-response { | ||||
|   margin: 5px; } | ||||
|  | ||||
| .chat-log-message { | ||||
|   color: #e6e6e6; } | ||||
|  | ||||
| .chat-empty-line { | ||||
|   font-size: 4px; } | ||||
| .chat-new-group { | ||||
|   padding-top: 1em; } | ||||
|  | ||||
| .chat-received { | ||||
|   padding-left: 4px; } | ||||
| @@ -1284,12 +1277,6 @@ StScrollBar { | ||||
|     padding-left: 0; | ||||
|     padding-right: 4px; } | ||||
|  | ||||
| .chat-notification-scrollview { | ||||
|   max-height: 22em; } | ||||
|  | ||||
| .subscription-message { | ||||
|   font-style: italic; } | ||||
|  | ||||
| .hotplug-transient-box { | ||||
|   spacing: 6px; | ||||
|   padding: 2px 72px 2px 12px; } | ||||
| @@ -1340,12 +1327,23 @@ StScrollBar { | ||||
|     border-radius: 6px 0 0 0; | ||||
|     border-right-width: 0; } | ||||
|  | ||||
| .legacy-tray-handle StIcon { | ||||
|   icon-size: 24px; } | ||||
| .legacy-tray-handle, | ||||
| .legacy-tray-icon { | ||||
|   padding: 6px; } | ||||
|   .legacy-tray-handle StIcon, | ||||
|   .legacy-tray-icon StIcon { | ||||
|     icon-size: 24px; } | ||||
|   .legacy-tray-handle:hover, .legacy-tray-handle:focus, | ||||
|   .legacy-tray-icon:hover, | ||||
|   .legacy-tray-icon:focus { | ||||
|     background-color: rgba(255, 255, 255, 0.1); } | ||||
|  | ||||
| .legacy-tray-icon-box { | ||||
|   padding: 6px; | ||||
|   spacing: 12px; } | ||||
|   .legacy-tray-icon-box:ltr { | ||||
|     padding-left: 12px; } | ||||
|   .legacy-tray-icon-box:rtl { | ||||
|     padding-right: 12px; } | ||||
|   .legacy-tray-icon-box StButton { | ||||
|     width: 24px; | ||||
|     height: 24px; } | ||||
| @@ -1382,10 +1380,9 @@ StScrollBar { | ||||
|   color: white; } | ||||
|   .keyboard-key:focus { | ||||
|     color: #eeeeec; | ||||
|     border-color: #215d9c; | ||||
|     box-shadow: inset 0 1px #454f52; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; } | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .keyboard-key:hover, .keyboard-key:checked { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
| @@ -1397,7 +1394,7 @@ StScrollBar { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: none; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .keyboard-key:grayed { | ||||
| @@ -1454,10 +1451,12 @@ StScrollBar { | ||||
| /* Auth Dialogs & Screen Shield */ | ||||
| .framed-user-icon { | ||||
|   background-size: contain; | ||||
|   border: 2px solid black; | ||||
|   border: 2px solid #eeeeec; | ||||
|   color: #eeeeec; | ||||
|   border-radius: 3px; } | ||||
|   .framed-user-icon:hover { | ||||
|     border-color: #4d4d4d; } | ||||
|     border-color: white; | ||||
|     color: white; } | ||||
|  | ||||
| .login-dialog-banner-view { | ||||
|   padding-top: 24px; | ||||
| @@ -1488,13 +1487,13 @@ StScrollBar { | ||||
|         color: white; | ||||
|         border-color: rgba(0, 0, 0, 0.7); | ||||
|         background-color: #1c5187; | ||||
|         box-shadow: none; | ||||
|         box-shadow: inset 0 0 black; | ||||
|         text-shadow: none; | ||||
|         icon-shadow: none; } | ||||
|       .login-dialog .modal-dialog-button:default:insensitive { | ||||
|         color: #7f7f7f; | ||||
|         color: gray; | ||||
|         border-color: rgba(0, 0, 0, 0.7); | ||||
|         background-color: rgba(62, 67, 68, 0.7); | ||||
|         background-color: rgba(62, 67, 69, 0.7); | ||||
|         box-shadow: none; | ||||
|         text-shadow: none; | ||||
|         icon-shadow: none; } | ||||
| @@ -1554,9 +1553,10 @@ StScrollBar { | ||||
|     color: #ffffff; } | ||||
|   .login-dialog-user-list-item .login-dialog-timed-login-indicator { | ||||
|     height: 2px; | ||||
|     background-color: transparent; } | ||||
|     margin: 2px 0 0 0; | ||||
|     background-color: #eeeeec; } | ||||
|   .login-dialog-user-list-item:focus .login-dialog-timed-login-indicator { | ||||
|     background-color: #2e3436; } | ||||
|     background-color: #ffffff; } | ||||
|  | ||||
| .login-dialog-username, | ||||
| .user-widget-label { | ||||
| @@ -1740,3 +1740,11 @@ StScrollBar { | ||||
|  | ||||
| stage { | ||||
|   -st-icon-style: symbolic; } | ||||
|  | ||||
| .toggle-switch { | ||||
|   width: 48px; } | ||||
|  | ||||
| .toggle-switch-us, .toggle-switch-intl { | ||||
|   background-image: url("resource:///org/gnome/shell/theme/toggle-off-hc.svg"); } | ||||
|   .toggle-switch-us:checked, .toggle-switch-intl:checked { | ||||
|     background-image: url("resource:///org/gnome/shell/theme/toggle-on-hc.svg"); } | ||||
|   | ||||
| @@ -6,3 +6,9 @@ | ||||
| stage { | ||||
|   -st-icon-style: symbolic; | ||||
| } | ||||
|  | ||||
| .toggle-switch { width: 48px; } | ||||
| .toggle-switch-us, .toggle-switch-intl { | ||||
|   background-image: url("resource:///org/gnome/shell/theme/toggle-off-hc.svg"); | ||||
|   &:checked { background-image: url("resource:///org/gnome/shell/theme/toggle-on-hc.svg"); } | ||||
| } | ||||
|   | ||||
| @@ -37,14 +37,13 @@ stage { | ||||
|   icon-shadow: 0 1px black; } | ||||
|   .button:focus { | ||||
|     color: #eeeeec; | ||||
|     border-color: #215d9c; | ||||
|     box-shadow: inset 0 1px #454f52; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; } | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .button:insensitive { | ||||
|     color: #939695; | ||||
|     color: #949796; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: rgba(66, 71, 73, 0.7); | ||||
|     background-color: rgba(66, 72, 73, 0.7); | ||||
|     box-shadow: none; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
| @@ -52,9 +51,46 @@ stage { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|  | ||||
| .modal-dialog-linked-button { | ||||
|   border-right-width: 1px; | ||||
|   color: #eeeeec; | ||||
|   background-color: #2e3436; | ||||
|   border-color: rgba(0, 0, 0, 0.7); | ||||
|   box-shadow: inset 0 1px #454f52; | ||||
|   text-shadow: 0 1px black; | ||||
|   icon-shadow: 0 1px black; | ||||
|   padding: 12px; } | ||||
|   .modal-dialog-linked-button:insensitive { | ||||
|     color: #949796; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: rgba(66, 72, 73, 0.7); | ||||
|     box-shadow: none; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .modal-dialog-linked-button:active { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .modal-dialog-linked-button:focus { | ||||
|     color: #eeeeec; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .modal-dialog-linked-button:first-child { | ||||
|     border-radius: 0px 0px 0px 6px; } | ||||
|   .modal-dialog-linked-button:last-child { | ||||
|     border-right-width: 0px; | ||||
|     border-radius: 0px 0px 6px 0px; } | ||||
|   .modal-dialog-linked-button:first-child:last-child { | ||||
|     border-right-width: 0px; | ||||
|     border-radius: 0px 0px 6px 6px; } | ||||
|  | ||||
| /* Entries */ | ||||
| StEntry { | ||||
| @@ -71,8 +107,8 @@ StEntry { | ||||
|     box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); | ||||
|     border-color: rgba(154, 154, 142, 0.5); } | ||||
|   StEntry:insensitive { | ||||
|     color: #939695; | ||||
|     border-color: #323636; | ||||
|     color: #949796; | ||||
|     border-color: #333636; | ||||
|     box-shadow: none; } | ||||
|   StEntry StIcon.capslock-warning { | ||||
|     icon-size: 16px; | ||||
| @@ -80,12 +116,13 @@ StEntry { | ||||
|     padding: 0 4px; } | ||||
|  | ||||
| /* Scrollbars */ | ||||
| StScrollView.vfade { | ||||
|   -st-vfade-offset: 68px; } | ||||
| StScrollView.hfade { | ||||
|   -st-hfade-offset: 68px; } | ||||
|  | ||||
| StScrollBar { | ||||
|   padding: 0; } | ||||
|   StScrollBar.vfade { | ||||
|     -st-vfade-offset: 68px; } | ||||
|   StScrollBar.hfade { | ||||
|     -st-hfade-offset: 68px; } | ||||
|   StScrollView StScrollBar { | ||||
|     min-width: 14px; | ||||
|     min-height: 14px; } | ||||
| @@ -94,10 +131,10 @@ StScrollBar { | ||||
|     background-color: transparent; } | ||||
|   StScrollBar StButton#vhandle, StScrollBar StButton#hhandle { | ||||
|     border-radius: 8px; | ||||
|     background-color: #393f3f; | ||||
|     background-color: #a6a8a7; | ||||
|     margin: 3px; } | ||||
|     StScrollBar StButton#vhandle:hover, StScrollBar StButton#hhandle:hover { | ||||
|       background-color: #515a5a; } | ||||
|       background-color: #cacbc9; } | ||||
|     StScrollBar StButton#vhandle:active, StScrollBar StButton#hhandle:active { | ||||
|       background-color: #215d9c; } | ||||
|  | ||||
| @@ -105,7 +142,7 @@ StScrollBar { | ||||
| .slider { | ||||
|   height: 1em; | ||||
|   -slider-height: 0.3em; | ||||
|   -slider-background-color: #323636; | ||||
|   -slider-background-color: #333636; | ||||
|   -slider-border-color: #1c1f1f; | ||||
|   -slider-active-background-color: #215d9c; | ||||
|   -slider-active-border-color: #184472; | ||||
| @@ -159,11 +196,12 @@ StScrollBar { | ||||
|   background-color: white; } | ||||
|  | ||||
| .modal-dialog { | ||||
|   border-radius: 5px; | ||||
|   border-radius: 9px; | ||||
|   color: #eeeeec; | ||||
|   background-color: rgba(23, 25, 26, 0.95); | ||||
|   border: 3px solid rgba(238, 238, 236, 0.5); | ||||
|   padding: 24px; } | ||||
|   border: 3px solid rgba(238, 238, 236, 0.5); } | ||||
|   .modal-dialog .modal-dialog-content-box { | ||||
|     padding: 24px; } | ||||
|   .modal-dialog .run-dialog-entry { | ||||
|     width: 20em; | ||||
|     margin-bottom: 6px; } | ||||
| @@ -178,10 +216,6 @@ StScrollBar { | ||||
|     color: #d6d6d1; | ||||
|     padding-bottom: .4em; } | ||||
|  | ||||
| .button-dialog-button-box { | ||||
|   spacing: 18px; | ||||
|   padding-top: 48px; } | ||||
|  | ||||
| .show-processes-dialog-subject, | ||||
| .mount-question-dialog-subject, | ||||
| .end-session-dialog-subject { | ||||
| @@ -323,7 +357,7 @@ StScrollBar { | ||||
|  | ||||
| .prompt-dialog-headline { | ||||
|   font-weight: bold; | ||||
|   color: #a6a69b; } | ||||
|   color: #b2b2a9; } | ||||
|  | ||||
| .prompt-dialog-description:rtl { | ||||
|   text-align: right; } | ||||
| @@ -392,19 +426,14 @@ StScrollBar { | ||||
|       background-color: #343a3a; | ||||
|       box-shadow: inset 0 1px 0px #282c2c; | ||||
|       font-weight: bold; } | ||||
|     .popup-menu .popup-menu-item:focus { | ||||
|       background-color: #215d9c; | ||||
|       color: #ffffff; } | ||||
|     .popup-menu .popup-menu-item:hover { | ||||
|     .popup-menu .popup-menu-item.selected { | ||||
|       background-color: rgba(238, 238, 236, 0.1); | ||||
|       color: #eeeeec; } | ||||
|     .popup-menu .popup-menu-item:active { | ||||
|       background-color: #1c5187; | ||||
|       background-color: #215d9c; | ||||
|       color: #ffffff; } | ||||
|     .popup-menu .popup-menu-item:insensitive { | ||||
|       color: rgba(238, 238, 236, 0.5); } | ||||
|   .popup-menu .active { | ||||
|     background-color: #215d9c; } | ||||
|   .popup-menu .popup-inactive-menu-item { | ||||
|     color: #eeeeec; } | ||||
|     .popup-menu .popup-inactive-menu-item:insensitive { | ||||
| @@ -415,7 +444,7 @@ StScrollBar { | ||||
|  | ||||
| .popup-menu-ornament { | ||||
|   text-align: right; | ||||
|   width: 1em; } | ||||
|   width: 1.2em; } | ||||
|  | ||||
| .popup-menu-boxpointer, | ||||
| .candidate-popup-boxpointer { | ||||
| @@ -492,10 +521,9 @@ StScrollBar { | ||||
|  | ||||
| .switcher-arrow { | ||||
|   border-color: transparent; | ||||
|   color: #1c1f1f; } | ||||
|  | ||||
| .switcher-arrow:highlighted { | ||||
|   color: #eeeeec; } | ||||
|   color: rgba(238, 238, 236, 0.8); } | ||||
|   .switcher-arrow:highlighted { | ||||
|     color: #eeeeec; } | ||||
|  | ||||
| .input-source-switcher-symbol { | ||||
|   font-size: 34pt; | ||||
| @@ -565,7 +593,7 @@ StScrollBar { | ||||
|     -panel-corner-border-color: transparent; } | ||||
|     #panel .panel-corner:active, #panel .panel-corner:overview, #panel .panel-corner:focus { | ||||
|       -panel-corner-border-color: #256ab1; } | ||||
|     #panel .panel-corner.lock-screen, #panel .panel-corner.login-screen, #panel .panel-cornerunlock-screen { | ||||
|     #panel .panel-corner.lock-screen, #panel .panel-corner.login-screen, #panel .panel-corner.unlock-screen { | ||||
|       -panel-corner-radius: 0; | ||||
|       -panel-corner-background-color: transparent; | ||||
|       -panel-corner-border-color: transparent; } | ||||
| @@ -707,15 +735,16 @@ StScrollBar { | ||||
|   border-left-width: 1px; } | ||||
|  | ||||
| .calendar-nonwork-day { | ||||
|   color: #939695; } | ||||
|   color: #949796; } | ||||
|  | ||||
| .calendar-today { | ||||
|   font-weight: bold; | ||||
|   border: 1px solid rgba(28, 31, 31, 0.5); } | ||||
|  | ||||
| .calendar-day-with-events { | ||||
|   color: #e2e2df; | ||||
|   font-weight: bold; } | ||||
|   color: white; | ||||
|   font-weight: bold; | ||||
|   background-image: url("resource:///org/gnome/shell/theme/calendar-today.svg"); } | ||||
|  | ||||
| .calendar-other-month-day { | ||||
|   color: rgba(238, 238, 236, 0.15); | ||||
| @@ -758,7 +787,12 @@ StScrollBar { | ||||
|     padding: 8px 8px 8px 0px; } | ||||
|  | ||||
| .message-icon-bin > StIcon { | ||||
|   icon-size: 48px; } | ||||
|   icon-size: 32px; } | ||||
|  | ||||
| .message-secondary-bin:ltr { | ||||
|   padding-left: 8px; } | ||||
| .message-secondary-bin:rtl { | ||||
|   padding-right: 8px; } | ||||
|  | ||||
| .message-secondary-bin { | ||||
|   color: #8e8e80; } | ||||
| @@ -767,14 +801,20 @@ StScrollBar { | ||||
|   icon-size: 16px; } | ||||
|  | ||||
| .message-title { | ||||
|   font-weight: bold; } | ||||
|   font-weight: bold; | ||||
|   font-size: 1.1em; } | ||||
|  | ||||
| .message-content { | ||||
|   padding: 8px; } | ||||
|   padding: 8px; | ||||
|   font-size: .9em; } | ||||
|  | ||||
| .system-switch-user-submenu-icon { | ||||
|   icon-size: 24px; | ||||
|   border: 1px solid rgba(238, 238, 236, 0.4); } | ||||
| .system-switch-user-submenu-icon.user-icon { | ||||
|   icon-size: 20px; | ||||
|   padding: 0 2px; } | ||||
|  | ||||
| .system-switch-user-submenu-icon.default-icon { | ||||
|   icon-size: 16px; | ||||
|   padding: 0 4px; } | ||||
|  | ||||
| #appMenu { | ||||
|   spinner-image: url("resource:///org/gnome/shell/theme/process-working.svg"); | ||||
| @@ -783,7 +823,7 @@ StScrollBar { | ||||
|     color: transparent; } | ||||
|  | ||||
| .aggregate-menu { | ||||
|   width: 360px; } | ||||
|   width: 280px; } | ||||
|   .aggregate-menu .popup-menu-icon { | ||||
|     padding: 0 4px; } | ||||
|  | ||||
| @@ -794,12 +834,12 @@ StScrollBar { | ||||
|   padding: 13px; | ||||
|   border: 1px solid #282c2c; } | ||||
|   .system-menu-action:hover, .system-menu-action:focus { | ||||
|     color: #ffffff; | ||||
|     background-color: #215d9c; | ||||
|     background-color: rgba(238, 238, 236, 0.1); | ||||
|     color: #eeeeec; | ||||
|     border: none; | ||||
|     padding: 14px; } | ||||
|   .system-menu-action:active { | ||||
|     background-color: #1c5187; | ||||
|     background-color: #215d9c; | ||||
|     color: #ffffff; } | ||||
|   .system-menu-action > StIcon { | ||||
|     icon-size: 16px; } | ||||
| @@ -820,7 +860,7 @@ StScrollBar { | ||||
| .popup-menu-icon { | ||||
|   icon-size: 1.09em; } | ||||
|  | ||||
| .window-close, .notification-close { | ||||
| .window-close { | ||||
|   background-image: url("resource:///org/gnome/shell/theme/close-window.svg"); | ||||
|   background-size: 32px; | ||||
|   height: 32px; | ||||
| @@ -831,12 +871,6 @@ StScrollBar { | ||||
|   .window-close:rtl { | ||||
|     -st-background-image-shadow: 2px 2px 6px rgba(0, 0, 0, 0.5); } | ||||
|  | ||||
| .notification-close { | ||||
|   -shell-close-overlap-x: 14px; | ||||
|   -shell-close-overlap-y: -12px; } | ||||
|   .notification-close:rtl { | ||||
|     -shell-close-overlap-x: -14px; } | ||||
|  | ||||
| /* NETWORK DIALOGS */ | ||||
| .nm-dialog { | ||||
|   max-height: 500px; | ||||
| @@ -844,7 +878,8 @@ StScrollBar { | ||||
|   min-width: 470px; } | ||||
|  | ||||
| .nm-dialog-content { | ||||
|   spacing: 20px; } | ||||
|   spacing: 20px; | ||||
|   padding: 24px; } | ||||
|  | ||||
| .nm-dialog-header-hbox { | ||||
|   spacing: 10px; } | ||||
| @@ -1026,7 +1061,7 @@ StScrollBar { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: none; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .app-view-control:first-child { | ||||
| @@ -1157,7 +1192,7 @@ StScrollBar { | ||||
|  | ||||
| /* NOTIFICATIONS & MESSAGE TRAY */ | ||||
| .url-highlighter { | ||||
|   link-color: #215d9c; } | ||||
|   link-color: #2a76c6; } | ||||
|  | ||||
| .notification-banner { | ||||
|   font-size: 11pt; | ||||
| @@ -1192,32 +1227,6 @@ StScrollBar { | ||||
|     .notification-banner .notification-button:hover, .notification-banner .notification-buttonfocus { | ||||
|       background-color: #292f30; } | ||||
|  | ||||
| .notification { | ||||
|   font-size: 11pt; | ||||
|   width: 34em; | ||||
|   margin: 5px; | ||||
|   border-radius: 6px; | ||||
|   color: #eeeeec; | ||||
|   background-color: #2e3436; | ||||
|   border: 1px solid #1c1f1f; | ||||
|   spacing-rows: 4px; | ||||
|   padding: 8px; | ||||
|   spacing-columns: 10px; } | ||||
|  | ||||
| .notification-unexpanded { | ||||
|   min-height: 48px; | ||||
|   height: 48px; } | ||||
|  | ||||
| .notification-with-image { | ||||
|   min-height: 159px; } | ||||
|  | ||||
| .notification-body { | ||||
|   spacing: 5px; } | ||||
|  | ||||
| .notification-actions { | ||||
|   paddinf-top: 18px; | ||||
|   spacing: 6px; } | ||||
|  | ||||
| .summary-source-counter { | ||||
|   font-size: 10pt; | ||||
|   font-weight: bold; | ||||
| @@ -1231,36 +1240,20 @@ StScrollBar { | ||||
|   box-shadow: 0 2px 2px rgba(0, 0, 0, 0.5); | ||||
|   border-radius: 0.9em; } | ||||
|  | ||||
| .notification-scrollview { | ||||
|   max-height: 18em; | ||||
|   -st-vfade-offset: 24px; } | ||||
|   .notification-scrollview:ltr > StScrollBar { | ||||
|     padding-left: 6px; } | ||||
|   .notification-scrollview:rtl > StScrollBar { | ||||
|     padding-right: 6px; } | ||||
|  | ||||
| .notification-button { | ||||
|   height: 24px; } | ||||
|  | ||||
| .notification-icon-button { | ||||
|   border-radius: 5px; | ||||
|   padding: 5px; | ||||
|   height: 24px; | ||||
|   width: 24px; } | ||||
|   .notification-icon-button > StIcon { | ||||
|     icons-size: 16px; | ||||
|     width: 16px; | ||||
|     height: 16px; | ||||
|     padding: 2px; } | ||||
|  | ||||
| .secondary-icon { | ||||
|   icon-size: 1.09em; } | ||||
|  | ||||
| .chat-body { | ||||
|   spacing: 5px; } | ||||
|  | ||||
| .chat-response { | ||||
|   margin: 5px; } | ||||
|  | ||||
| .chat-log-message { | ||||
|   color: #d6d6d1; } | ||||
|  | ||||
| .chat-empty-line { | ||||
|   font-size: 4px; } | ||||
| .chat-new-group { | ||||
|   padding-top: 1em; } | ||||
|  | ||||
| .chat-received { | ||||
|   padding-left: 4px; } | ||||
| @@ -1284,12 +1277,6 @@ StScrollBar { | ||||
|     padding-left: 0; | ||||
|     padding-right: 4px; } | ||||
|  | ||||
| .chat-notification-scrollview { | ||||
|   max-height: 22em; } | ||||
|  | ||||
| .subscription-message { | ||||
|   font-style: italic; } | ||||
|  | ||||
| .hotplug-transient-box { | ||||
|   spacing: 6px; | ||||
|   padding: 2px 72px 2px 12px; } | ||||
| @@ -1340,12 +1327,23 @@ StScrollBar { | ||||
|     border-radius: 6px 0 0 0; | ||||
|     border-right-width: 0; } | ||||
|  | ||||
| .legacy-tray-handle StIcon { | ||||
|   icon-size: 24px; } | ||||
| .legacy-tray-handle, | ||||
| .legacy-tray-icon { | ||||
|   padding: 6px; } | ||||
|   .legacy-tray-handle StIcon, | ||||
|   .legacy-tray-icon StIcon { | ||||
|     icon-size: 24px; } | ||||
|   .legacy-tray-handle:hover, .legacy-tray-handle:focus, | ||||
|   .legacy-tray-icon:hover, | ||||
|   .legacy-tray-icon:focus { | ||||
|     background-color: rgba(238, 238, 236, 0.1); } | ||||
|  | ||||
| .legacy-tray-icon-box { | ||||
|   padding: 6px; | ||||
|   spacing: 12px; } | ||||
|   .legacy-tray-icon-box:ltr { | ||||
|     padding-left: 12px; } | ||||
|   .legacy-tray-icon-box:rtl { | ||||
|     padding-right: 12px; } | ||||
|   .legacy-tray-icon-box StButton { | ||||
|     width: 24px; | ||||
|     height: 24px; } | ||||
| @@ -1382,10 +1380,9 @@ StScrollBar { | ||||
|   color: white; } | ||||
|   .keyboard-key:focus { | ||||
|     color: #eeeeec; | ||||
|     border-color: #215d9c; | ||||
|     box-shadow: inset 0 1px #454f52; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; } | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .keyboard-key:hover, .keyboard-key:checked { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
| @@ -1397,7 +1394,7 @@ StScrollBar { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: none; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .keyboard-key:grayed { | ||||
| @@ -1454,10 +1451,12 @@ StScrollBar { | ||||
| /* Auth Dialogs & Screen Shield */ | ||||
| .framed-user-icon { | ||||
|   background-size: contain; | ||||
|   border: 2px solid #1c1f1f; | ||||
|   border: 2px solid #eeeeec; | ||||
|   color: #eeeeec; | ||||
|   border-radius: 3px; } | ||||
|   .framed-user-icon:hover { | ||||
|     border-color: #656f6f; } | ||||
|     border-color: white; | ||||
|     color: white; } | ||||
|  | ||||
| .login-dialog-banner-view { | ||||
|   padding-top: 24px; | ||||
| @@ -1488,13 +1487,13 @@ StScrollBar { | ||||
|         color: white; | ||||
|         border-color: rgba(0, 0, 0, 0.7); | ||||
|         background-color: #1c5187; | ||||
|         box-shadow: none; | ||||
|         box-shadow: inset 0 0 black; | ||||
|         text-shadow: none; | ||||
|         icon-shadow: none; } | ||||
|       .login-dialog .modal-dialog-button:default:insensitive { | ||||
|         color: #939695; | ||||
|         color: #949796; | ||||
|         border-color: rgba(0, 0, 0, 0.7); | ||||
|         background-color: rgba(66, 71, 73, 0.7); | ||||
|         background-color: rgba(66, 72, 73, 0.7); | ||||
|         box-shadow: none; | ||||
|         text-shadow: none; | ||||
|         icon-shadow: none; } | ||||
| @@ -1554,9 +1553,10 @@ StScrollBar { | ||||
|     color: #ffffff; } | ||||
|   .login-dialog-user-list-item .login-dialog-timed-login-indicator { | ||||
|     height: 2px; | ||||
|     background-color: transparent; } | ||||
|     margin: 2px 0 0 0; | ||||
|     background-color: #eeeeec; } | ||||
|   .login-dialog-user-list-item:focus .login-dialog-timed-login-indicator { | ||||
|     background-color: #2e3436; } | ||||
|     background-color: #ffffff; } | ||||
|  | ||||
| .login-dialog-username, | ||||
| .user-widget-label { | ||||
|   | ||||
| Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 98 KiB | 
							
								
								
									
										133
									
								
								data/theme/toggle-off-hc.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,133 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    width="48" | ||||
|    height="22" | ||||
|    id="svg2857" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="toggle-off-hc.svg"> | ||||
|   <defs | ||||
|      id="defs2859"> | ||||
|     <inkscape:perspective | ||||
|        sodipodi:type="inkscape:persp3d" | ||||
|        inkscape:vp_x="0 : 526.18109 : 1" | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_z="744.09448 : 526.18109 : 1" | ||||
|        inkscape:persp3d-origin="372.04724 : 350.78739 : 1" | ||||
|        id="perspective2865" /> | ||||
|     <inkscape:perspective | ||||
|        id="perspective2843" | ||||
|        inkscape:persp3d-origin="0.5 : 0.33333333 : 1" | ||||
|        inkscape:vp_z="1 : 0.5 : 1" | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_x="0 : 0.5 : 1" | ||||
|        sodipodi:type="inkscape:persp3d" /> | ||||
|     <inkscape:path-effect | ||||
|        effect="spiro" | ||||
|        id="path-effect77541-4" | ||||
|        is_visible="true" /> | ||||
|   </defs> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      pagecolor="#000000" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="1" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="1" | ||||
|      inkscape:cx="-6.1820581" | ||||
|      inkscape:cy="-16.463788" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="g37994" | ||||
|      showgrid="false" | ||||
|      inkscape:window-width="2560" | ||||
|      inkscape:window-height="1364" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|      borderlayer="true" | ||||
|      inkscape:showpageshadow="false" | ||||
|      inkscape:snap-nodes="false" | ||||
|      inkscape:snap-bbox="true" | ||||
|      showborder="true"> | ||||
|     <inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid12954" | ||||
|        empspacing="5" | ||||
|        visible="true" | ||||
|        enabled="true" | ||||
|        snapvisiblegridlinesonly="true" /> | ||||
|   </sodipodi:namedview> | ||||
|   <metadata | ||||
|      id="metadata2862"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title></dc:title> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1" | ||||
|      transform="translate(-444.64286,-781.36218)"> | ||||
|     <g | ||||
|        transform="matrix(0.6526046,0,0,0.80554422,99.592644,-636.32172)" | ||||
|        id="g37994"> | ||||
|       <g | ||||
|          transform="matrix(1.5323214,0,0,1.2413968,-324.76058,489.69039)" | ||||
|          id="toggle-off" | ||||
|          inkscape:label="#g8477"> | ||||
|         <circle | ||||
|            cy="1033.993" | ||||
|            cx="571.95966" | ||||
|            id="path8444" | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#555753;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            r="7" /> | ||||
|         <rect | ||||
|            ry="2.0108337" | ||||
|            rx="1.9562569" | ||||
|            y="1031.9885" | ||||
|            x="565.0083" | ||||
|            height="4.0216675" | ||||
|            width="34.850178" | ||||
|            id="rect8461" | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#555753;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> | ||||
|       </g> | ||||
|       <g | ||||
|          transform="matrix(1.5323214,0,0,1.2413968,-324.85635,491.16456)" | ||||
|          id="toggle-on" | ||||
|          inkscape:label="#g8481"> | ||||
|         <rect | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            id="rect8475" | ||||
|            width="34.850178" | ||||
|            height="4.0216675" | ||||
|            x="565.0083" | ||||
|            y="1070.9279" | ||||
|            rx="1.9562569" | ||||
|            ry="2.0108337" /> | ||||
|         <circle | ||||
|            transform="scale(-1,1)" | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            id="circle8463" | ||||
|            cx="-591.0213" | ||||
|            cy="1072.9402" | ||||
|            r="9" /> | ||||
|       </g> | ||||
|     </g> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 6.1 KiB | 
							
								
								
									
										113
									
								
								data/theme/toggle-on-hc.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,113 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    width="48" | ||||
|    height="22" | ||||
|    id="svg2857" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="toggle-on-hc.svg"> | ||||
|   <defs | ||||
|      id="defs2859"> | ||||
|     <inkscape:perspective | ||||
|        sodipodi:type="inkscape:persp3d" | ||||
|        inkscape:vp_x="0 : 526.18109 : 1" | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_z="744.09448 : 526.18109 : 1" | ||||
|        inkscape:persp3d-origin="372.04724 : 350.78739 : 1" | ||||
|        id="perspective2865" /> | ||||
|     <inkscape:perspective | ||||
|        id="perspective2843" | ||||
|        inkscape:persp3d-origin="0.5 : 0.33333333 : 1" | ||||
|        inkscape:vp_z="1 : 0.5 : 1" | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_x="0 : 0.5 : 1" | ||||
|        sodipodi:type="inkscape:persp3d" /> | ||||
|     <inkscape:path-effect | ||||
|        effect="spiro" | ||||
|        id="path-effect77541-4" | ||||
|        is_visible="true" /> | ||||
|   </defs> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      pagecolor="#000000" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="1" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="1" | ||||
|      inkscape:cx="-222.95215" | ||||
|      inkscape:cy="3.9378433" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="g37994" | ||||
|      showgrid="false" | ||||
|      inkscape:window-width="2560" | ||||
|      inkscape:window-height="1364" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|      borderlayer="true" | ||||
|      inkscape:showpageshadow="false" | ||||
|      inkscape:snap-nodes="false" | ||||
|      inkscape:snap-bbox="true" | ||||
|      showborder="true"> | ||||
|     <inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid12954" | ||||
|        empspacing="5" | ||||
|        visible="true" | ||||
|        enabled="true" | ||||
|        snapvisiblegridlinesonly="true" /> | ||||
|   </sodipodi:namedview> | ||||
|   <metadata | ||||
|      id="metadata2862"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title></dc:title> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1" | ||||
|      transform="translate(-444.64286,-781.36218)"> | ||||
|     <g | ||||
|        transform="matrix(0.6526046,0,0,0.80554422,99.592644,-636.32172)" | ||||
|        id="g37994"> | ||||
|       <g | ||||
|          transform="matrix(1.5323214,0,0,1.2413968,-324.85635,441.50868)" | ||||
|          id="toggle-on" | ||||
|          inkscape:label="#g8481"> | ||||
|         <rect | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            id="rect8475" | ||||
|            width="34.850178" | ||||
|            height="4.0216675" | ||||
|            x="565.0083" | ||||
|            y="1070.9279" | ||||
|            rx="1.9562569" | ||||
|            ry="2.0108337" /> | ||||
|         <circle | ||||
|            transform="scale(-1,1)" | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            id="circle8463" | ||||
|            cx="-591.0213" | ||||
|            cy="1072.9402" | ||||
|            r="9" /> | ||||
|       </g> | ||||
|     </g> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 4.4 KiB | 
| @@ -14,7 +14,7 @@ | ||||
|    height="22" | ||||
|    id="svg2857" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.5 r10040" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="toggle-on-intl.svg"> | ||||
|   <defs | ||||
|      id="defs2859"> | ||||
| @@ -52,7 +52,7 @@ | ||||
|       <stop | ||||
|          id="stop77465" | ||||
|          offset="1" | ||||
|          style="stop-color:#204a87;stop-opacity:1" /> | ||||
|          style="stop-color:#205b9a;stop-opacity:1" /> | ||||
|     </linearGradient> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
| @@ -88,14 +88,14 @@ | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="1" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="32" | ||||
|      inkscape:cx="17.255148" | ||||
|      inkscape:cy="8.9252639" | ||||
|      inkscape:zoom="1" | ||||
|      inkscape:cx="37.410841" | ||||
|      inkscape:cy="16.009314" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="g37994" | ||||
|      showgrid="true" | ||||
|      showgrid="false" | ||||
|      inkscape:window-width="2560" | ||||
|      inkscape:window-height="1375" | ||||
|      inkscape:window-height="1376" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|   | ||||
| Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB | 
| @@ -14,7 +14,7 @@ | ||||
|    height="22" | ||||
|    id="svg2857" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.5 r10040" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="toggle-on-us.svg"> | ||||
|   <defs | ||||
|      id="defs2859"> | ||||
| @@ -32,28 +32,6 @@ | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_x="0 : 0.5 : 1" | ||||
|        sodipodi:type="inkscape:persp3d" /> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient77461" | ||||
|        id="linearGradient77551" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(1.3066667,0,0,1,-841.64667,-483)" | ||||
|        x1="1164.7644" | ||||
|        y1="962.93695" | ||||
|        x2="1164.7644" | ||||
|        y2="970.51404" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient77461" | ||||
|        inkscape:collect="always"> | ||||
|       <stop | ||||
|          id="stop77463" | ||||
|          offset="0" | ||||
|          style="stop-color:#182f4c;stop-opacity:1" /> | ||||
|       <stop | ||||
|          id="stop77465" | ||||
|          offset="1" | ||||
|          style="stop-color:#204a87;stop-opacity:1" /> | ||||
|     </linearGradient> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient76469-7-7-4" | ||||
| @@ -80,6 +58,38 @@ | ||||
|        effect="spiro" | ||||
|        id="path-effect77541-4" | ||||
|        is_visible="true" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient77461-1" | ||||
|        inkscape:collect="always"> | ||||
|       <stop | ||||
|          id="stop77463-1" | ||||
|          offset="0" | ||||
|          style="stop-color:#182f4c;stop-opacity:1" /> | ||||
|       <stop | ||||
|          id="stop77465-4" | ||||
|          offset="1" | ||||
|          style="stop-color:#205b9a;stop-opacity:1" /> | ||||
|     </linearGradient> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient77461-1" | ||||
|        id="linearGradient77551-6-5" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(0.8527367,0,0,0.80554422,-969.41608,-778.00299)" | ||||
|        x1="1164.7644" | ||||
|        y1="962.93695" | ||||
|        x2="1164.7644" | ||||
|        y2="970.51404" /> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient77461-1" | ||||
|        id="linearGradient11198" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(1.3066667,0,0,1,-1066.3709,794.25325)" | ||||
|        x1="1322.5831" | ||||
|        y1="-312.51855" | ||||
|        x2="1322.5831" | ||||
|        y2="-306.53461" /> | ||||
|   </defs> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
| @@ -89,13 +99,13 @@ | ||||
|      inkscape:pageopacity="1" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="1" | ||||
|      inkscape:cx="33.380898" | ||||
|      inkscape:cy="6.9658271" | ||||
|      inkscape:cx="-26.798898" | ||||
|      inkscape:cy="5.3753009" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="g37994" | ||||
|      showgrid="false" | ||||
|      inkscape:window-width="2560" | ||||
|      inkscape:window-height="1375" | ||||
|      inkscape:window-height="1376" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
| @@ -152,7 +162,7 @@ | ||||
|            height="25" | ||||
|            width="98" | ||||
|            id="rect38000" | ||||
|            style="color:#000000;fill:url(#linearGradient77551);fill-opacity:1;fill-rule:nonzero;stroke:#182f4c;stroke-width:1.37920964;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> | ||||
|            style="color:#000000;fill:url(#linearGradient11198);fill-opacity:1;fill-rule:nonzero;stroke:#182f4c;stroke-width:1.37920964;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto" /> | ||||
|       </g> | ||||
|       <g | ||||
|          transform="translate(2.0625,-2)" | ||||
|   | ||||
| Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.7 KiB | 
| @@ -35,7 +35,6 @@ | ||||
|       <xi:include href="xml/st-bin.xml"/> | ||||
|       <xi:include href="xml/st-box-layout.xml"/> | ||||
|       <xi:include href="xml/st-scroll-view.xml"/> | ||||
|       <xi:include href="xml/st-table.xml"/> | ||||
|     </chapter> | ||||
|  | ||||
|     <chapter id="styling"> | ||||
|   | ||||
| @@ -54,7 +54,6 @@ const Application = new Lang.Class({ | ||||
|         this._startupUuid = null; | ||||
|         this._loaded = false; | ||||
|         this._skipMainWindow = false; | ||||
|         this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); | ||||
|     }, | ||||
|  | ||||
|     _extensionAvailable: function(uuid) { | ||||
|   | ||||
| @@ -14,7 +14,7 @@ const ShellEntry = imports.ui.shellEntry; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const UserWidget = imports.ui.userWidget; | ||||
|  | ||||
| const DEFAULT_BUTTON_WELL_ICON_SIZE = 24; | ||||
| const DEFAULT_BUTTON_WELL_ICON_SIZE = 16; | ||||
| const DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0; | ||||
| const DEFAULT_BUTTON_WELL_ANIMATION_TIME = 0.3; | ||||
|  | ||||
| @@ -194,17 +194,15 @@ const AuthPrompt = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onAskQuestion: function(verifier, serviceName, question, passwordChar) { | ||||
|         if (this._preemptiveAnswer) { | ||||
|             if (this._queryingService) | ||||
|                 this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer); | ||||
|             this._preemptiveAnswer = null; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (this._queryingService) | ||||
|             this.clear(); | ||||
|  | ||||
|         this._queryingService = serviceName; | ||||
|         if (this._preemptiveAnswer) { | ||||
|             this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer); | ||||
|             this._preemptiveAnswer = null; | ||||
|             return; | ||||
|         } | ||||
|         this.setPasswordChar(passwordChar); | ||||
|         this.setQuestion(question); | ||||
|  | ||||
| @@ -260,6 +258,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onVerificationComplete: function() { | ||||
|         this.setActorInDefaultButtonWell(null); | ||||
|         this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED; | ||||
| 	this.cancelButton.reactive = false; | ||||
|     }, | ||||
| @@ -283,6 +282,12 @@ const AuthPrompt = new Lang.Class({ | ||||
|         if (oldActor) | ||||
|             Tweener.removeTweens(oldActor); | ||||
|  | ||||
|         let wasSpinner; | ||||
|         if (oldActor == this._spinner.actor) | ||||
|             wasSpinner = true; | ||||
|         else | ||||
|             wasSpinner = false; | ||||
|  | ||||
|         let isSpinner; | ||||
|         if (actor == this._spinner.actor) | ||||
|             isSpinner = true; | ||||
| @@ -292,6 +297,11 @@ const AuthPrompt = new Lang.Class({ | ||||
|         if (this._defaultButtonWellActor != actor && oldActor) { | ||||
|             if (!animate) { | ||||
|                 oldActor.opacity = 0; | ||||
|  | ||||
|                 if (wasSpinner) { | ||||
|                     if (this._spinner) | ||||
|                         this._spinner.stop(); | ||||
|                 } | ||||
|             } else { | ||||
|                 Tweener.addTween(oldActor, | ||||
|                                  { opacity: 0, | ||||
| @@ -300,7 +310,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|                                    transition: 'linear', | ||||
|                                    onCompleteScope: this, | ||||
|                                    onComplete: function() { | ||||
|                                       if (isSpinner) { | ||||
|                                       if (wasSpinner) { | ||||
|                                           if (this._spinner) | ||||
|                                               this._spinner.stop(); | ||||
|                                       } | ||||
| @@ -403,7 +413,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     updateSensitivity: function(sensitive) { | ||||
|         this._updateNextButtonSensitivity(sensitive); | ||||
|         this._updateNextButtonSensitivity(sensitive && this._entry.text.length > 0); | ||||
|         this._entry.reactive = sensitive; | ||||
|         this._entry.clutter_text.editable = sensitive; | ||||
|     }, | ||||
| @@ -434,8 +444,9 @@ const AuthPrompt = new Lang.Class({ | ||||
|         let oldStatus = this.verificationStatus; | ||||
|         this.verificationStatus = AuthPromptStatus.NOT_VERIFYING; | ||||
|         this.cancelButton.reactive = true; | ||||
|         this.nextButton.label = _("Next"); | ||||
|  | ||||
|         if (oldStatus == AuthPromptStatus.VERIFYING) | ||||
|         if (this._userVerifier) | ||||
|             this._userVerifier.cancel(); | ||||
|  | ||||
|         this._queryingService = null; | ||||
| @@ -490,6 +501,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|  | ||||
|     finish: function(onComplete) { | ||||
|         if (!this._userVerifier.hasPendingMessages) { | ||||
|             this._userVerifier.clear(); | ||||
|             onComplete(); | ||||
|             return; | ||||
|         } | ||||
| @@ -497,12 +509,13 @@ const AuthPrompt = new Lang.Class({ | ||||
|         let signalId = this._userVerifier.connect('no-more-messages', | ||||
|                                                   Lang.bind(this, function() { | ||||
|                                                       this._userVerifier.disconnect(signalId); | ||||
|                                                       this._userVerifier.clear(); | ||||
|                                                       onComplete(); | ||||
|                                                   })); | ||||
|     }, | ||||
|  | ||||
|     cancel: function() { | ||||
|         if (this.verificationStatus == AuthPromptStatus.NOT_VERIFYING || this.verificationStatus == AuthPromptStatus.VERIFICATION_SUCCEEDED) { | ||||
|         if (this.verificationStatus == AuthPromptStatus.VERIFICATION_SUCCEEDED) { | ||||
|             return; | ||||
|         } | ||||
|         this.reset(); | ||||
|   | ||||
| @@ -22,6 +22,7 @@ const Clutter = imports.gi.Clutter; | ||||
| const Gdm = imports.gi.Gdm; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| @@ -48,6 +49,7 @@ const _FADE_ANIMATION_TIME = 0.25; | ||||
| const _SCROLL_ANIMATION_TIME = 0.5; | ||||
| const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0; | ||||
| const _LOGO_ICON_HEIGHT = 48; | ||||
| const _MAX_BOTTOM_MENU_ITEMS = 5; | ||||
|  | ||||
| const UserListItem = new Lang.Class({ | ||||
|     Name: 'UserListItem', | ||||
| @@ -71,6 +73,9 @@ const UserListItem = new Lang.Class({ | ||||
|         this._userWidget = new UserWidget.UserWidget(this.user); | ||||
|         layout.add(this._userWidget.actor); | ||||
|  | ||||
|         this._userWidget.actor.bind_property('label-actor', this.actor, 'label-actor', | ||||
|                                              GObject.BindingFlags.SYNC_CREATE); | ||||
|  | ||||
|         this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator', | ||||
|                                                  scale_x: 0 }); | ||||
|         layout.add(this._timedLoginIndicator); | ||||
| @@ -279,7 +284,16 @@ const SessionMenuButton = new Lang.Class({ | ||||
|  | ||||
|         this.actor = new St.Bin({ child: this._button }); | ||||
|  | ||||
|         this._menu = new PopupMenu.PopupMenu(this._button, 0, St.Side.TOP); | ||||
|         let side = St.Side.TOP; | ||||
|         let align = 0; | ||||
|         if (Gdm.get_session_ids().length > _MAX_BOTTOM_MENU_ITEMS) { | ||||
|             if (this.actor.text_direction == Clutter.TextDirection.RTL) | ||||
|                 side = St.Side.RIGHT; | ||||
|             else | ||||
|                 side = St.Side.LEFT; | ||||
|             align = 0.5; | ||||
|         } | ||||
|         this._menu = new PopupMenu.PopupMenu(this._button, align, side); | ||||
|         Main.uiGroup.add_actor(this._menu.actor); | ||||
|         this._menu.actor.hide(); | ||||
|  | ||||
| @@ -492,7 +506,7 @@ const LoginDialog = new Lang.Class({ | ||||
|         let [minWidth, minHeight, natWidth, natHeight] = this._bannerView.get_preferred_size(); | ||||
|         let centerX = dialogBox.x1 + (dialogBox.x2 - dialogBox.x1) / 2; | ||||
|  | ||||
|         actorBox.x1 = centerX - natWidth / 2; | ||||
|         actorBox.x1 = Math.floor(centerX - natWidth / 2); | ||||
|         actorBox.y1 = dialogBox.y1 + Main.layoutManager.panelBox.height; | ||||
|         actorBox.x2 = actorBox.x1 + natWidth; | ||||
|         actorBox.y2 = actorBox.y1 + natHeight; | ||||
| @@ -506,7 +520,7 @@ const LoginDialog = new Lang.Class({ | ||||
|         let [minWidth, minHeight, natWidth, natHeight] = this._logoBin.get_preferred_size(); | ||||
|         let centerX = dialogBox.x1 + (dialogBox.x2 - dialogBox.x1) / 2; | ||||
|  | ||||
|         actorBox.x1 = centerX - natWidth / 2; | ||||
|         actorBox.x1 = Math.floor(centerX - natWidth / 2); | ||||
|         actorBox.y1 = dialogBox.y2 - natHeight; | ||||
|         actorBox.x2 = actorBox.x1 + natWidth; | ||||
|         actorBox.y2 = actorBox.y1 + natHeight; | ||||
| @@ -521,8 +535,11 @@ const LoginDialog = new Lang.Class({ | ||||
|         let centerX = dialogBox.x1 + (dialogBox.x2 - dialogBox.x1) / 2; | ||||
|         let centerY = dialogBox.y1 + (dialogBox.y2 - dialogBox.y1) / 2; | ||||
|  | ||||
|         actorBox.x1 = centerX - natWidth / 2; | ||||
|         actorBox.y1 = centerY - natHeight / 2; | ||||
|         natWidth = Math.min(natWidth, dialogBox.x2 - dialogBox.x1); | ||||
|         natHeight = Math.min(natHeight, dialogBox.y2 - dialogBox.y1); | ||||
|  | ||||
|         actorBox.x1 = Math.floor(centerX - natWidth / 2); | ||||
|         actorBox.y1 = Math.floor(centerY - natHeight / 2); | ||||
|         actorBox.x2 = actorBox.x1 + natWidth; | ||||
|         actorBox.y2 = actorBox.y1 + natHeight; | ||||
|  | ||||
| @@ -570,14 +587,21 @@ const LoginDialog = new Lang.Class({ | ||||
|         // try a different layout, or if we have what extra space we | ||||
|         // can hand out | ||||
|         if (bannerAllocation) { | ||||
|             let leftOverYSpace = dialogHeight - bannerHeight - authPromptHeight - logoHeight; | ||||
|             let bannerSpace; | ||||
|  | ||||
|             if (authPromptAllocation) | ||||
|                 bannerSpace = authPromptAllocation.y1 - bannerAllocation.y1; | ||||
|             else | ||||
|                 bannerSpace = 0; | ||||
|  | ||||
|             let leftOverYSpace = bannerSpace - bannerHeight; | ||||
|  | ||||
|             if (leftOverYSpace > 0) { | ||||
|                  // First figure out how much left over space is up top | ||||
|                  let leftOverTopSpace = leftOverYSpace / 2; | ||||
|  | ||||
|                  // Then, shift the banner into the middle of that extra space | ||||
|                  let yShift = leftOverTopSpace / 2; | ||||
|                  let yShift = Math.floor(leftOverTopSpace / 2); | ||||
|  | ||||
|                  bannerAllocation.y1 += yShift; | ||||
|                  bannerAllocation.y2 += yShift; | ||||
| @@ -603,8 +627,8 @@ const LoginDialog = new Lang.Class({ | ||||
|                      let centerGap = wideSpacing / 8; | ||||
|  | ||||
|                      // place the banner along the left edge of the center margin | ||||
|                      bannerAllocation.x2 = centerX - centerGap / 2; | ||||
|                      bannerAllocation.x1 = bannerAllocation.x2 - wideBannerWidth; | ||||
|                      bannerAllocation.x2 = Math.floor(centerX - centerGap / 2); | ||||
|                      bannerAllocation.x1 = Math.floor(bannerAllocation.x2 - wideBannerWidth); | ||||
|  | ||||
|                      // figure out how tall it would like to be and try to accomodate | ||||
|                      // but don't let it get too close to the logo | ||||
| @@ -612,11 +636,11 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|                      let maxWideHeight = dialogHeight - 3 * logoHeight; | ||||
|                      wideBannerHeight = Math.min(maxWideHeight, wideBannerHeight); | ||||
|                      bannerAllocation.y1 = centerY - wideBannerHeight / 2; | ||||
|                      bannerAllocation.y1 = Math.floor(centerY - wideBannerHeight / 2); | ||||
|                      bannerAllocation.y2 = bannerAllocation.y1 + wideBannerHeight; | ||||
|  | ||||
|                      // place the auth prompt along the right edge of the center margin | ||||
|                      authPromptAllocation.x1 = centerX + centerGap / 2; | ||||
|                      authPromptAllocation.x1 = Math.floor(centerX + centerGap / 2); | ||||
|                      authPromptAllocation.x2 = authPromptAllocation.x1 + authPromptWidth; | ||||
|                  } else { | ||||
|                      // If we aren't going to do a wide view, then we need to limit | ||||
| @@ -626,7 +650,7 @@ const LoginDialog = new Lang.Class({ | ||||
|                      leftOverYSpace += bannerHeight; | ||||
|  | ||||
|                      // Then figure out how much of that space is up top | ||||
|                      let availableTopSpace = leftOverYSpace / 2; | ||||
|                      let availableTopSpace = Math.floor(leftOverYSpace / 2); | ||||
|  | ||||
|                      // Then give all of that space to the banner | ||||
|                      bannerAllocation.y2 = bannerAllocation.y1 + availableTopSpace; | ||||
| @@ -637,7 +661,7 @@ const LoginDialog = new Lang.Class({ | ||||
|             let leftOverYSpace = dialogHeight - userSelectionHeight - logoHeight; | ||||
|  | ||||
|             if (leftOverYSpace > 0) { | ||||
|                 let topExpansion = leftOverYSpace / 2; | ||||
|                 let topExpansion = Math.floor(leftOverYSpace / 2); | ||||
|                 let bottomExpansion = topExpansion; | ||||
|  | ||||
|                 userSelectionAllocation.y1 -= topExpansion; | ||||
| @@ -853,7 +877,7 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _loginScreenSessionActivated: function() { | ||||
|         if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_SUCCEEDED) | ||||
|         if (this.actor.opacity == 255 && this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING) | ||||
|             return; | ||||
|  | ||||
|         Tweener.addTween(this.actor, | ||||
| @@ -870,7 +894,8 @@ const LoginDialog = new Lang.Class({ | ||||
|                            }, | ||||
|                            onUpdateScope: this, | ||||
|                            onComplete: function() { | ||||
|                                this._authPrompt.reset(); | ||||
|                                if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.NOT_VERIFYING) | ||||
|                                    this._authPrompt.reset(); | ||||
|                            }, | ||||
|                            onCompleteScope: this }); | ||||
|     }, | ||||
| @@ -1083,18 +1108,11 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onUserListActivated: function(activatedItem) { | ||||
|         let tasks = [function() { | ||||
|                          return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox); | ||||
|                      }, | ||||
|                      function() { | ||||
|                          this._setUserListExpanded(false); | ||||
|                      }]; | ||||
|  | ||||
|         this._user = activatedItem.user; | ||||
|  | ||||
|         this._updateCancelButton(); | ||||
|  | ||||
|         let batch = new Batch.ConcurrentBatch(this, [new Batch.ConsecutiveBatch(this, tasks), | ||||
|         let batch = new Batch.ConcurrentBatch(this, [GdmUtil.cloneAndFadeOutActor(this._userSelectionBox), | ||||
|                                                      this._beginVerificationForItem(activatedItem)]); | ||||
|         batch.run(); | ||||
|     }, | ||||
|   | ||||
| @@ -35,8 +35,8 @@ const ALLOWED_FAILURES_KEY = 'allowed-failures'; | ||||
| const LOGO_KEY = 'logo'; | ||||
| const DISABLE_USER_LIST_KEY = 'disable-user-list'; | ||||
|  | ||||
| // Give user 16ms to read each character of a PAM message | ||||
| const USER_READ_TIME = 16 | ||||
| // Give user 48ms to read each character of a PAM message | ||||
| const USER_READ_TIME = 48 | ||||
|  | ||||
| const MessageType = { | ||||
|     NONE: 0, | ||||
| @@ -410,7 +410,7 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|     _updateDefaultService: function() { | ||||
|         if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY)) | ||||
|             this._defaultService = PASSWORD_SERVICE_NAME; | ||||
|         else if (this.smartcardDetected) | ||||
|         else if (this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY)) | ||||
|             this._defaultService = SMARTCARD_SERVICE_NAME; | ||||
|         else if (this._haveFingerprintReader) | ||||
|             this._defaultService = FINGERPRINT_SERVICE_NAME; | ||||
|   | ||||
| @@ -134,7 +134,7 @@ const LoginManagerSystemd = new Lang.Class({ | ||||
|             if (error) | ||||
|                 asyncCallback(false); | ||||
|             else | ||||
|                 asyncCallback(result[0] != 'no'); | ||||
|                 asyncCallback(result[0] != 'no' && result[0] != 'na'); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ const St = imports.gi.St; | ||||
| const Signals = imports.signals; | ||||
| const Atk = imports.gi.Atk; | ||||
|  | ||||
| const ANIMATED_ICON_UPDATE_TIMEOUT = 100; | ||||
| const ANIMATED_ICON_UPDATE_TIMEOUT = 14; | ||||
|  | ||||
| const Animation = new Lang.Class({ | ||||
|     Name: 'Animation', | ||||
|   | ||||
| @@ -366,8 +366,6 @@ const AllView = new Lang.Class({ | ||||
|     Extends: BaseAppView, | ||||
|  | ||||
|     _init: function() { | ||||
|         this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); | ||||
|  | ||||
|         this.parent({ usePagination: true }, null); | ||||
|         this._scrollView = new St.ScrollView({ style_class: 'all-apps', | ||||
|                                                x_expand: true, | ||||
| @@ -502,6 +500,11 @@ const AllView = new Lang.Class({ | ||||
|  | ||||
|     _loadApps: function() { | ||||
|         let apps = Gio.AppInfo.get_all().filter(function(appInfo) { | ||||
|             try { | ||||
|                 let id = appInfo.get_id(); // catch invalid file encodings | ||||
|             } catch(e) { | ||||
|                 return false; | ||||
|             } | ||||
|             return appInfo.should_show(); | ||||
|         }).map(function(app) { | ||||
|             return app.get_id(); | ||||
| @@ -525,7 +528,7 @@ const AllView = new Lang.Class({ | ||||
|         // at least on single-monitor setups. | ||||
|         // This also disables drag-to-launch on multi-monitor setups, | ||||
|         // but we hope that is not used much. | ||||
|         let favoritesWritable = this._settings.is_writable('favorite-apps'); | ||||
|         let favoritesWritable = global.settings.is_writable('favorite-apps'); | ||||
|  | ||||
|         apps.forEach(Lang.bind(this, function(appId) { | ||||
|             let app = appSys.lookup_app(appId); | ||||
| @@ -755,7 +758,8 @@ const AllView = new Lang.Class({ | ||||
|         let fadeOffset = Math.min(this._grid.topPadding, | ||||
|                                   this._grid.bottomPadding); | ||||
|         this._scrollView.update_fade_effect(fadeOffset, 0); | ||||
|         this._scrollView.get_effect('fade').fade_edges = true; | ||||
|         if (fadeOffset > 0) | ||||
|             this._scrollView.get_effect('fade').fade_edges = true; | ||||
|  | ||||
|         if (this._availWidth != availWidth || this._availHeight != availHeight || oldNPages != this._grid.nPages()) { | ||||
|             this._adjustment.value = 0; | ||||
| @@ -783,8 +787,6 @@ const FrequentView = new Lang.Class({ | ||||
|     _init: function() { | ||||
|         this.parent(null, { fillParent: true }); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); | ||||
|  | ||||
|         this.actor = new St.Widget({ style_class: 'frequent-apps', | ||||
|                                      layout_manager: new Clutter.BinLayout(), | ||||
|                                      x_expand: true, y_expand: true }); | ||||
| @@ -827,7 +829,7 @@ const FrequentView = new Lang.Class({ | ||||
|         // at least on single-monitor setups. | ||||
|         // This also disables drag-to-launch on multi-monitor setups, | ||||
|         // but we hope that is not used much. | ||||
|         let favoritesWritable = this._settings.is_writable('favorite-apps'); | ||||
|         let favoritesWritable = global.settings.is_writable('favorite-apps'); | ||||
|  | ||||
|         for (let i = 0; i < mostUsed.length; i++) { | ||||
|             if (!mostUsed[i].get_app_info().should_show()) | ||||
| @@ -1064,7 +1066,7 @@ const AppSearchProvider = new Lang.Class({ | ||||
|  | ||||
|     getInitialResultSet: function(terms, callback, cancellable) { | ||||
|         let query = terms.join(' '); | ||||
|         let groups = Gio.DesktopAppInfo.search(query); | ||||
|         let groups = Shell.AppSystem.search(query); | ||||
|         let usage = Shell.AppUsage.get_default(); | ||||
|         let results = []; | ||||
|         groups.forEach(function(group) { | ||||
| @@ -1293,7 +1295,10 @@ const FolderIcon = new Lang.Class({ | ||||
|             if (!_listsIntersect(folderCategories, appCategories)) | ||||
|                 return; | ||||
|  | ||||
|             addAppId(appInfo.get_id()); | ||||
|             try { | ||||
|                 addAppId(appInfo.get_id()); // catch invalid file encodings | ||||
|             } catch(e) { | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         this.actor.visible = this.view.getAllItems().length > 0; | ||||
| @@ -1804,8 +1809,6 @@ const AppIconMenu = new Lang.Class({ | ||||
|  | ||||
|         this.actor.add_style_class_name('app-well-menu'); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); | ||||
|  | ||||
|         // Chain our visibility and lifecycle to that of the source | ||||
|         source.actor.connect('notify::mapped', Lang.bind(this, function () { | ||||
|             if (!source.actor.mapped) | ||||
| @@ -1867,7 +1870,7 @@ const AppIconMenu = new Lang.Class({ | ||||
|                 })); | ||||
|             } | ||||
|  | ||||
|             let canFavorite = this._settings.is_writable('favorite-apps'); | ||||
|             let canFavorite = global.settings.is_writable('favorite-apps'); | ||||
|  | ||||
|             if (canFavorite) { | ||||
|                 this._appendSeparator(); | ||||
|   | ||||
| @@ -144,6 +144,7 @@ const BackgroundCache = new Lang.Class({ | ||||
|         this._pendingFileLoads = []; | ||||
|         this._fileMonitors = {}; | ||||
|         this._backgroundSources = {}; | ||||
|         this._animations = {}; | ||||
|     }, | ||||
|  | ||||
|     monitorFile: function(file) { | ||||
| @@ -162,12 +163,13 @@ const BackgroundCache = new Lang.Class({ | ||||
|  | ||||
|     getAnimation: function(params) { | ||||
|         params = Params.parse(params, { file: null, | ||||
|                                         settingsSchema: null, | ||||
|                                         onLoaded: null }); | ||||
|  | ||||
|         if (_fileEqual0(this._animationFile, params.file)) { | ||||
|         if (this._animations[params.settingsSchema] && _fileEqual0(this._animationFile, params.file)) { | ||||
|             if (params.onLoaded) { | ||||
|                 let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { | ||||
|                     params.onLoaded(this._animation); | ||||
|                     params.onLoaded(this._animations[params.settingsSchema]); | ||||
|                     return GLib.SOURCE_REMOVE; | ||||
|                 })); | ||||
|                 GLib.Source.set_name_by_id(id, '[gnome-shell] params.onLoaded'); | ||||
| @@ -178,12 +180,11 @@ const BackgroundCache = new Lang.Class({ | ||||
|         let animation = new Animation({ file: params.file }); | ||||
|  | ||||
|         animation.load(Lang.bind(this, function() { | ||||
|                            this._animationFile = params.file; | ||||
|                            this._animation = animation; | ||||
|                            this._animations[params.settingsSchema] = animation; | ||||
|  | ||||
|                            if (params.onLoaded) { | ||||
|                                let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { | ||||
|                                    params.onLoaded(this._animation); | ||||
|                                    params.onLoaded(this._animations[params.settingsSchema]); | ||||
|                                    return GLib.SOURCE_REMOVE; | ||||
|                                })); | ||||
|                                GLib.Source.set_name_by_id(id, '[gnome-shell] params.onLoaded'); | ||||
| @@ -403,17 +404,18 @@ const Background = new Lang.Class({ | ||||
|  | ||||
|     _loadAnimation: function(file) { | ||||
|         this._cache.getAnimation({ file: file, | ||||
|                                          onLoaded: Lang.bind(this, function(animation) { | ||||
|                                              this._animation = animation; | ||||
|                                    settingsSchema: this._settings.schema_id, | ||||
|                                    onLoaded: Lang.bind(this, function(animation) { | ||||
|                                        this._animation = animation; | ||||
|  | ||||
|                                              if (!this._animation || this._cancellable.is_cancelled()) { | ||||
|                                                  this._setLoaded(); | ||||
|                                                  return; | ||||
|                                              } | ||||
|                                        if (!this._animation || this._cancellable.is_cancelled()) { | ||||
|                                            this._setLoaded(); | ||||
|                                            return; | ||||
|                                        } | ||||
|  | ||||
|                                              this._updateAnimation(); | ||||
|                                              this._watchFile(file); | ||||
|                                          }) | ||||
|                                        this._updateAnimation(); | ||||
|                                        this._watchFile(file); | ||||
|                                    }) | ||||
|                                  }); | ||||
|     }, | ||||
|  | ||||
| @@ -527,6 +529,10 @@ const BackgroundSource = new Lang.Class({ | ||||
|         let file = null; | ||||
|         let style; | ||||
|  | ||||
|         // We don't watch changes to settings here, | ||||
|         // instead we rely on Background to watch those | ||||
|         // and emit 'changed' at the right time | ||||
|  | ||||
|         if (this._overrideImage != null) { | ||||
|             file = Gio.File.new_for_path(this._overrideImage); | ||||
|             style = GDesktopEnums.BackgroundStyle.ZOOM; // Hardcode | ||||
|   | ||||
| @@ -16,9 +16,10 @@ const BackgroundMenu = new Lang.Class({ | ||||
|     _init: function(layoutManager) { | ||||
|         this.parent(layoutManager.dummyCursor, 0, St.Side.TOP); | ||||
|  | ||||
|         this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop'); | ||||
|         this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.addSettingsAction(_("Change Background…"), 'gnome-background-panel.desktop'); | ||||
|         this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.addSettingsAction(_("Display Settings"), 'gnome-display-panel.desktop'); | ||||
|         this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop'); | ||||
|  | ||||
|         this.actor.add_style_class_name('background-menu'); | ||||
|  | ||||
|   | ||||
| @@ -24,6 +24,8 @@ const MSECS_IN_DAY = 24 * 60 * 60 * 1000; | ||||
| const SHOW_WEEKDATE_KEY = 'show-weekdate'; | ||||
| const ELLIPSIS_CHAR = '\u2026'; | ||||
|  | ||||
| const MESSAGE_ICON_SIZE = 32; | ||||
|  | ||||
| const MESSAGE_ANIMATION_TIME = 0.1; | ||||
|  | ||||
| const DEFAULT_EXPAND_LINES = 6; | ||||
| @@ -44,6 +46,10 @@ function _sameDay(dateA, dateB) { | ||||
|     return _sameMonth(dateA, dateB) && (dateA.getDate() == dateB.getDate()); | ||||
| } | ||||
|  | ||||
| function _isToday(date) { | ||||
|     return _sameDay(new Date(), date); | ||||
| } | ||||
|  | ||||
| function _isWorkDay(date) { | ||||
|     /* Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday). */ | ||||
|     let days = C_('calendar-no-work', "06"); | ||||
| @@ -68,21 +74,6 @@ function _getEndOfDay(date) { | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| function _formatEventTime(event, periodBegin, periodEnd) { | ||||
|     let ret; | ||||
|     let allDay = (event.allDay || (event.date <= periodBegin && event.end >= periodEnd)); | ||||
|     if (allDay) { | ||||
|         /* Translators: Shown in calendar event list for all day events | ||||
|          * Keep it short, best if you can use less then 10 characters | ||||
|          */ | ||||
|         ret = C_("event list time", "All Day"); | ||||
|     } else { | ||||
|         let date = event.date >= periodBegin ? event.date : event.end; | ||||
|         ret = Util.formatTime(date, { timeOnly: true }); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| function _getCalendarDayAbbreviation(dayNumber) { | ||||
|     let abbreviations = [ | ||||
|         /* Translators: Calendar grid abbreviation for Sunday. | ||||
| @@ -133,7 +124,8 @@ const URLHighlighter = new Lang.Class({ | ||||
|     _init: function(text, lineWrap, allowMarkup) { | ||||
|         if (!text) | ||||
|             text = ''; | ||||
|         this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter' }); | ||||
|         this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter', | ||||
|                                     x_expand: true, x_align: Clutter.ActorAlign.START }); | ||||
|         this._linkColor = '#ccccff'; | ||||
|         this.actor.connect('style-changed', Lang.bind(this, function() { | ||||
|             let [hasColor, color] = this.actor.get_theme_node().lookup_color('link-color', false); | ||||
| @@ -253,7 +245,8 @@ const URLHighlighter = new Lang.Class({ | ||||
| const CalendarEvent = new Lang.Class({ | ||||
|     Name: 'CalendarEvent', | ||||
|  | ||||
|     _init: function(date, end, summary, allDay) { | ||||
|     _init: function(id, date, end, summary, allDay) { | ||||
|         this.id = id; | ||||
|         this.date = date; | ||||
|         this.end = end; | ||||
|         this.summary = summary; | ||||
| @@ -425,9 +418,10 @@ const DBusEventSource = new Lang.Class({ | ||||
|                 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 event = new CalendarEvent(date, end, summary, allDay); | ||||
|                 let event = new CalendarEvent(id, date, end, summary, allDay); | ||||
|                 newEvents.push(event); | ||||
|             } | ||||
|             newEvents.sort(function(event1, event2) { | ||||
| @@ -984,7 +978,6 @@ const Message = new Lang.Class({ | ||||
|         this._iconBin = new St.Bin({ style_class: 'message-icon-bin', | ||||
|                                      y_expand: true, | ||||
|                                      visible: false }); | ||||
|         this._iconBin.set_y_align(Clutter.ActorAlign.START); | ||||
|         hbox.add_actor(this._iconBin); | ||||
|  | ||||
|         let contentBox = new St.BoxLayout({ style_class: 'message-content', | ||||
| @@ -1008,8 +1001,7 @@ const Message = new Lang.Class({ | ||||
|         this._closeButton = new St.Button({ child: closeIcon, visible: false }); | ||||
|         titleBox.add_actor(this._closeButton); | ||||
|  | ||||
|         this._bodyStack = new St.Widget({ x_expand: true, | ||||
|                                           x_align: Clutter.ActorAlign.START }); | ||||
|         this._bodyStack = new St.Widget({ x_expand: true }); | ||||
|         this._bodyStack.layout_manager = new LabelExpanderLayout(); | ||||
|         contentBox.add_actor(this._bodyStack); | ||||
|  | ||||
| @@ -1018,16 +1010,17 @@ const Message = new Lang.Class({ | ||||
|         this._bodyStack.add_actor(this.bodyLabel.actor); | ||||
|         this.setBody(body); | ||||
|  | ||||
|         this._closeButton.connect('clicked', Lang.bind(this, | ||||
|             function() { | ||||
|                 this.emit('close'); | ||||
|             })); | ||||
|         this._closeButton.connect('clicked', Lang.bind(this, this.close)); | ||||
|         this.actor.connect('notify::hover', Lang.bind(this, this._sync)); | ||||
|         this.actor.connect('clicked', Lang.bind(this, this._onClicked)); | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     close: function() { | ||||
|         this.emit('close'); | ||||
|     }, | ||||
|  | ||||
|     setIcon: function(actor) { | ||||
|         this._iconBin.child = actor; | ||||
|         this._iconBin.visible = (actor != null); | ||||
| @@ -1044,7 +1037,10 @@ const Message = new Lang.Class({ | ||||
|  | ||||
|     setBody: function(text) { | ||||
|         this._bodyText = text; | ||||
|         this.bodyLabel.setMarkup(text, this._useBodyMarkup); | ||||
|         this.bodyLabel.setMarkup(text ? text.replace(/\n/g, ' ') : '', | ||||
|                                  this._useBodyMarkup); | ||||
|         if (this._expandedLabel) | ||||
|             this._expandedLabel.setMarkup(text, this._useBodyMarkup); | ||||
|     }, | ||||
|  | ||||
|     setUseBodyMarkup: function(enable) { | ||||
| @@ -1092,9 +1088,9 @@ const Message = new Lang.Class({ | ||||
|         this._actionBin.visible = (this._actionBin.get_n_children() > 0); | ||||
|  | ||||
|         if (this._bodyStack.get_n_children() < 2) { | ||||
|             let expandedLabel = new URLHighlighter(this._bodyText, | ||||
|                                                    true, this._useBodyMarkup); | ||||
|             this.setExpandedBody(expandedLabel.actor); | ||||
|             this._expandedLabel = new URLHighlighter(this._bodyText, | ||||
|                                                      true, this._useBodyMarkup); | ||||
|             this.setExpandedBody(this._expandedLabel.actor); | ||||
|         } | ||||
|  | ||||
|         if (animate) { | ||||
| @@ -1111,6 +1107,8 @@ const Message = new Lang.Class({ | ||||
|             this._bodyStack.layout_manager.expansion = 1; | ||||
|             this._actionBin.scale_y = 1; | ||||
|         } | ||||
|  | ||||
|         this.emit('expanded'); | ||||
|     }, | ||||
|  | ||||
|     unexpand: function(animate) { | ||||
| @@ -1133,15 +1131,17 @@ const Message = new Lang.Class({ | ||||
|             this._actionBin.scale_y = 0; | ||||
|             this.expanded = false; | ||||
|         } | ||||
|  | ||||
|         this.emit('unexpanded'); | ||||
|     }, | ||||
|  | ||||
|     canClear: function() { | ||||
|     canClose: function() { | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let hovered = this.actor.hover; | ||||
|         this._closeButton.visible = hovered; | ||||
|         this._closeButton.visible = hovered && this.canClose(); | ||||
|         this._secondaryBin.visible = !hovered; | ||||
|     }, | ||||
|  | ||||
| @@ -1156,7 +1156,7 @@ const Message = new Lang.Class({ | ||||
|  | ||||
|         if (keysym == Clutter.KEY_Delete || | ||||
|             keysym == Clutter.KEY_KP_Delete) { | ||||
|             this.emit('close'); | ||||
|             this.close(); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
| @@ -1164,6 +1164,55 @@ const Message = new Lang.Class({ | ||||
| }); | ||||
| Signals.addSignalMethods(Message.prototype); | ||||
|  | ||||
| const EventMessage = new Lang.Class({ | ||||
|     Name: 'EventMessage', | ||||
|     Extends: Message, | ||||
|  | ||||
|     _init: function(event, date) { | ||||
|         this._event = event; | ||||
|         this._date = date; | ||||
|  | ||||
|         this.parent(this._formatEventTime(), event.summary); | ||||
|     }, | ||||
|  | ||||
|     _formatEventTime: function() { | ||||
|         let periodBegin = _getBeginningOfDay(this._date); | ||||
|         let periodEnd = _getEndOfDay(this._date); | ||||
|         let allDay = (this._event.allDay || (this._event.date <= periodBegin && | ||||
|                                              this._event.end >= periodEnd)); | ||||
|         let title; | ||||
|         if (allDay) { | ||||
|             /* Translators: Shown in calendar event list for all day events | ||||
|              * Keep it short, best if you can use less then 10 characters | ||||
|              */ | ||||
|             title = C_("event list time", "All Day"); | ||||
|         } else { | ||||
|             let date = this._event.date >= periodBegin ? this._event.date | ||||
|                                                        : this._event.end; | ||||
|             title = Util.formatTime(date, { timeOnly: true }); | ||||
|         } | ||||
|  | ||||
|         let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; | ||||
|         if (this._event.date < periodBegin && !this._event.allDay) { | ||||
|             if (rtl) | ||||
|                 title = title + ELLIPSIS_CHAR; | ||||
|             else | ||||
|                 title = ELLIPSIS_CHAR + title; | ||||
|         } | ||||
|         if (this._event.end > periodEnd && !this._event.allDay) { | ||||
|             if (rtl) | ||||
|                 title = ELLIPSIS_CHAR + title; | ||||
|             else | ||||
|                 title = title + ELLIPSIS_CHAR; | ||||
|         } | ||||
|         return title; | ||||
|     }, | ||||
|  | ||||
|     canClose: function() { | ||||
|         return _isToday(this._date); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const NotificationMessage = new Lang.Class({ | ||||
|     Name: 'NotificationMessage', | ||||
|     Extends: Message, | ||||
| @@ -1181,10 +1230,10 @@ const NotificationMessage = new Lang.Class({ | ||||
|                 this._closed = true; | ||||
|                 this.notification.destroy(MessageTray.NotificationDestroyedReason.DISMISSED); | ||||
|             })); | ||||
|         notification.connect('destroy', Lang.bind(this, | ||||
|         this._destroyId = notification.connect('destroy', Lang.bind(this, | ||||
|             function() { | ||||
|                 if (!this._closed) | ||||
|                     this.emit('close'); | ||||
|                     this.close(); | ||||
|             })); | ||||
|         this._updatedId = notification.connect('updated', | ||||
|                                                Lang.bind(this, this._onUpdated)); | ||||
| @@ -1192,9 +1241,10 @@ const NotificationMessage = new Lang.Class({ | ||||
|  | ||||
|     _getIcon: function() { | ||||
|         if (this.notification.gicon) | ||||
|             return new St.Icon({ gicon: this.notification.gicon, icon_size: 48 }); | ||||
|             return new St.Icon({ gicon: this.notification.gicon, | ||||
|                                  icon_size: MESSAGE_ICON_SIZE }); | ||||
|         else | ||||
|             return this.notification.source.createIcon(48); | ||||
|             return this.notification.source.createIcon(MESSAGE_ICON_SIZE); | ||||
|     }, | ||||
|  | ||||
|     _onUpdated: function(n, clear) { | ||||
| @@ -1204,10 +1254,6 @@ const NotificationMessage = new Lang.Class({ | ||||
|         this.setUseBodyMarkup(n.bannerBodyMarkup); | ||||
|     }, | ||||
|  | ||||
|     canClear: function() { | ||||
|         return !this.notification.resident; | ||||
|     }, | ||||
|  | ||||
|     _onClicked: function() { | ||||
|         this.notification.activate(); | ||||
|     }, | ||||
| @@ -1216,6 +1262,10 @@ const NotificationMessage = new Lang.Class({ | ||||
|         if (this._updatedId) | ||||
|             this.notification.disconnect(this._updatedId); | ||||
|         this._updatedId = 0; | ||||
|  | ||||
|         if (this._destroyId) | ||||
|             this.notification.disconnect(this._destroyId); | ||||
|         this._destroyId = 0; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -1289,7 +1339,7 @@ const MessageListSection = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     addMessage: function(message, animate) { | ||||
|         this.addMessageAtIndex(message, 0, animate); | ||||
|         this.addMessageAtIndex(message, -1, animate); | ||||
|     }, | ||||
|  | ||||
|     addMessageAtIndex: function(message, index, animate) { | ||||
| @@ -1358,26 +1408,30 @@ const MessageListSection = new Lang.Class({ | ||||
|  | ||||
|         this._messages.delete(message); | ||||
|  | ||||
|         if (animate) | ||||
|         if (animate) { | ||||
|             Tweener.addTween(obj.container, { scale_x: 0, scale_y: 0, | ||||
|                                               time: MESSAGE_ANIMATION_TIME, | ||||
|                                               transition: 'easeOutQuad', | ||||
|                                               onComplete: function() { | ||||
|                                                   obj.container.destroy(); | ||||
|                                                   global.sync_pointer(); | ||||
|                                               }}); | ||||
|         else | ||||
|         } else { | ||||
|             obj.container.destroy(); | ||||
|             global.sync_pointer(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     clear: function() { | ||||
|         let messages = [...this._messages.keys()].filter(function(message) { | ||||
|             return message.canClear(); | ||||
|             return message.canClose(); | ||||
|         }); | ||||
|  | ||||
|         // If there are few messages, letting them all zoom out looks OK | ||||
|         if (messages.length < 2) { | ||||
|             messages.forEach(Lang.bind(this, function(message) { | ||||
|                 this.removeMessage(message, true); })); | ||||
|             messages.forEach(function(message) { | ||||
|                 message.close(); | ||||
|             }); | ||||
|         } else { | ||||
|             // Otherwise we slide them out one by one, and then zoom them | ||||
|             // out "off-screen" in the end to smoothly shrink the parent | ||||
| @@ -1391,25 +1445,20 @@ const MessageListSection = new Lang.Class({ | ||||
|                                    time: MESSAGE_ANIMATION_TIME, | ||||
|                                    delay: i * delay, | ||||
|                                    transition: 'easeOutQuad', | ||||
|                                    onComplete: Lang.bind(this, function() { | ||||
|                                        this.removeMessage(message, true); | ||||
|                                    })}); | ||||
|                                    onComplete: function() { | ||||
|                                        message.close(); | ||||
|                                    }}); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _canClear: function() { | ||||
|         for (let message of this._messages.keys()) | ||||
|             if (message.canClear()) | ||||
|             if (message.canClose()) | ||||
|                 return true; | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _isToday: function() { | ||||
|         let today = new Date(); | ||||
|         return _sameDay(this._date, today); | ||||
|     }, | ||||
|  | ||||
|     _shouldShow: function() { | ||||
|         return !this.empty; | ||||
|     }, | ||||
| @@ -1437,6 +1486,15 @@ const EventsSection = new Lang.Class({ | ||||
|         this._desktopSettings.connect('changed', Lang.bind(this, this._reloadEvents)); | ||||
|         this._eventSource = new EmptyEventSource(); | ||||
|  | ||||
|         this._ignoredEvents = new Map(); | ||||
|  | ||||
|         let savedState = global.get_persistent_state('as', 'ignored_events'); | ||||
|         if (savedState) | ||||
|             savedState.deep_unpack().forEach(Lang.bind(this, | ||||
|                 function(eventId) { | ||||
|                     this._ignoredEvents.set(eventId, true); | ||||
|                 })); | ||||
|  | ||||
|         this.parent(''); | ||||
|  | ||||
|         Shell.AppSystem.get_default().connect('installed-changed', | ||||
| @@ -1444,6 +1502,12 @@ const EventsSection = new Lang.Class({ | ||||
|         this._appInstalledChanged(); | ||||
|     }, | ||||
|  | ||||
|     _ignoreEvent: function(event) { | ||||
|         this._ignoredEvents.set(event.id, true); | ||||
|         let savedState = new GLib.Variant('as', [...this._ignoredEvents.keys()]); | ||||
|         global.set_persistent_state('ignored_events', savedState); | ||||
|     }, | ||||
|  | ||||
|     setEventSource: function(eventSource) { | ||||
|         this._eventSource = eventSource; | ||||
|         this._eventSource.connect('changed', Lang.bind(this, this._reloadEvents)); | ||||
| @@ -1454,13 +1518,13 @@ const EventsSection = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _updateTitle: function() { | ||||
|         let now = new Date(); | ||||
|         if (_sameDay(this._date, now)) { | ||||
|         if (_isToday(this._date)) { | ||||
|             this._title.label = _("Events"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let dayFormat; | ||||
|         let now = new Date(); | ||||
|         if (_sameYear(this._date, now)) | ||||
|             /* Translators: Shown on calendar heading when selected day occurs on current year */ | ||||
|             dayFormat = Shell.util_translate_time_string(NC_("calendar heading", | ||||
| @@ -1486,22 +1550,15 @@ const EventsSection = new Lang.Class({ | ||||
|  | ||||
|         for (let i = 0; i < events.length; i++) { | ||||
|             let event = events[i]; | ||||
|             let title = _formatEventTime(event, periodBegin, periodEnd); | ||||
|  | ||||
|             let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL; | ||||
|             if (event.date < periodBegin && !event.allDay) { | ||||
|                 if (rtl) | ||||
|                     title = title + ELLIPSIS_CHAR; | ||||
|                 else | ||||
|                     title = ELLIPSIS_CHAR + title; | ||||
|             } | ||||
|             if (event.end > periodEnd && !event.allDay) { | ||||
|                 if (rtl) | ||||
|                     title = ELLIPSIS_CHAR + title; | ||||
|                 else | ||||
|                     title = title + ELLIPSIS_CHAR; | ||||
|             } | ||||
|             this.addMessage(new Message(title, event.summary), false); | ||||
|             if (this._ignoredEvents.has(event.id)) | ||||
|                 continue; | ||||
|  | ||||
|             let message = new EventMessage(event, this._date); | ||||
|             message.connect('close', Lang.bind(this, function() { | ||||
|                 this._ignoreEvent(event); | ||||
|             })); | ||||
|             this.addMessage(message, false); | ||||
|         } | ||||
|  | ||||
|         this._reloading = false; | ||||
| @@ -1544,7 +1601,7 @@ const EventsSection = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _shouldShow: function() { | ||||
|         return !this.empty || !this._isToday(); | ||||
|         return !this.empty || !_isToday(this._date); | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
| @@ -1663,7 +1720,7 @@ const NotificationSection = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _shouldShow: function() { | ||||
|         return !this.empty && this._isToday(); | ||||
|         return !this.empty && _isToday(this._date); | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
| @@ -1703,7 +1760,7 @@ const Placeholder = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let isToday = _sameDay(this._date, new Date()); | ||||
|         let isToday = _isToday(this._date); | ||||
|         if (isToday && this._icon.gicon == this._todayIcon) | ||||
|             return; | ||||
|         if (!isToday && this._icon.gicon == this._otherIcon) | ||||
|   | ||||
| @@ -9,9 +9,15 @@ const Gio = imports.gi.Gio; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Gcr = imports.gi.Gcr; | ||||
|  | ||||
| const Animation = imports.ui.animation; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| const CheckBox = imports.ui.checkBox; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const WORK_SPINNER_ICON_SIZE = 16; | ||||
| const WORK_SPINNER_ANIMATION_DELAY = 1.0; | ||||
| const WORK_SPINNER_ANIMATION_TIME = 0.3; | ||||
|  | ||||
| const KeyringDialog = new Lang.Class({ | ||||
|     Name: 'KeyringDialog', | ||||
| @@ -58,27 +64,47 @@ const KeyringDialog = new Lang.Class({ | ||||
|                             { y_fill:  true, | ||||
|                               y_align: St.Align.START }); | ||||
|  | ||||
|         this._workSpinner = null; | ||||
|         this._controlTable = null; | ||||
|  | ||||
|  | ||||
|         this._cancelButton = this.addButton({ label: '', | ||||
|                                               action: Lang.bind(this, this._onCancelButton), | ||||
|                                               key: Clutter.Escape }, | ||||
|                                             { expand: true, x_fill: false, x_align: St.Align.START }); | ||||
|         this.placeSpinner({ expand: false, | ||||
|                             x_fill: false, | ||||
|                             y_fill: false, | ||||
|                             x_align: St.Align.END, | ||||
|                             y_align: St.Align.MIDDLE }); | ||||
|                                               key: Clutter.Escape }); | ||||
|         this._continueButton = this.addButton({ label: '', | ||||
|                                                 action: Lang.bind(this, this._onContinueButton), | ||||
|                                                 default: true }, | ||||
|                                               { expand: false, x_fill: false, x_align: St.Align.END }); | ||||
|                                                 default: true }); | ||||
|  | ||||
|         this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE); | ||||
|         this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE); | ||||
|     }, | ||||
|  | ||||
|     _setWorking: function(working) { | ||||
|         if (!this._workSpinner) | ||||
|             return; | ||||
|  | ||||
|         Tweener.removeTweens(this._workSpinner.actor); | ||||
|         if (working) { | ||||
|             this._workSpinner.play(); | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 255, | ||||
|                                delay: WORK_SPINNER_ANIMATION_DELAY, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear' | ||||
|                              }); | ||||
|         } else { | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 0, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear', | ||||
|                                onCompleteScope: this, | ||||
|                                onComplete: function() { | ||||
|                                    if (this._workSpinner) | ||||
|                                        this._workSpinner.stop(); | ||||
|                                } | ||||
|                              }); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _buildControlTable: function() { | ||||
|         let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL }); | ||||
|         let table = new St.Widget({ style_class: 'keyring-dialog-control-table', | ||||
| @@ -101,15 +127,22 @@ const KeyringDialog = new Lang.Class({ | ||||
|             ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true }); | ||||
|             this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate)); | ||||
|  | ||||
|             let spinnerIcon = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg'); | ||||
|             this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE); | ||||
|             this._workSpinner.actor.opacity = 0; | ||||
|  | ||||
|             if (rtl) { | ||||
|                 layout.attach(this._passwordEntry, 0, row, 1, 1); | ||||
|                 layout.attach(label, 1, row, 1, 1); | ||||
|                 layout.attach(this._workSpinner.actor, 0, row, 1, 1); | ||||
|                 layout.attach(this._passwordEntry, 1, row, 1, 1); | ||||
|                 layout.attach(label, 2, row, 1, 1); | ||||
|             } else { | ||||
|                 layout.attach(label, 0, row, 1, 1); | ||||
|                 layout.attach(this._passwordEntry, 1, row, 1, 1); | ||||
|                 layout.attach(this._workSpinner.actor, 2, row, 1, 1); | ||||
|             } | ||||
|             row++; | ||||
|         } else { | ||||
|             this._workSpinner = null; | ||||
|             this._passwordEntry = null; | ||||
|         } | ||||
|  | ||||
| @@ -178,7 +211,7 @@ const KeyringDialog = new Lang.Class({ | ||||
|  | ||||
|         this._continueButton.can_focus = sensitive; | ||||
|         this._continueButton.reactive = sensitive; | ||||
|         this.setWorking(!sensitive); | ||||
|         this._setWorking(!sensitive); | ||||
|     }, | ||||
|  | ||||
|     _ensureOpen: function() { | ||||
|   | ||||
| @@ -12,6 +12,8 @@ const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const Config = imports.misc.config; | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| @@ -338,6 +340,7 @@ const NetworkSecretDialog = new Lang.Class({ | ||||
|                 content.message = _("PIN code is needed for the mobile broadband device"); | ||||
|                 content.secrets.push({ label: _("PIN: "), key: 'pin', | ||||
|                                        value: gsmSetting.pin || '', password: true }); | ||||
|                 break; | ||||
|             } | ||||
|             // fall through | ||||
|         case 'cdma': | ||||
| @@ -610,6 +613,7 @@ const NetworkAgent = new Lang.Class({ | ||||
|  | ||||
|         this._dialogs = { }; | ||||
|         this._vpnRequests = { }; | ||||
|         this._notifications = { }; | ||||
|  | ||||
|         this._native.connect('new-request', Lang.bind(this, this._newRequest)); | ||||
|         this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest)); | ||||
| @@ -632,21 +636,92 @@ const NetworkAgent = new Lang.Class({ | ||||
|             this._vpnRequests[requestId].cancel(true); | ||||
|         this._vpnRequests = { }; | ||||
|  | ||||
|         for (requestId in this._notifications) | ||||
|             this._notifications[requestId].destroy(); | ||||
|         this._notifications = { }; | ||||
|  | ||||
|         this._enabled = false; | ||||
|     }, | ||||
|  | ||||
|     _showNotification: function(requestId, connection, settingName, hints, flags) { | ||||
|         let source = new MessageTray.Source(_("Network Manager"), 'network-transmit-receive'); | ||||
|         source.policy = new MessageTray.NotificationApplicationPolicy('gnome-network-panel'); | ||||
|  | ||||
|         let title, body; | ||||
|  | ||||
|         let connectionSetting = connection.get_setting_connection(); | ||||
|         let connectionType = connectionSetting.get_connection_type(); | ||||
|         switch (connectionType) { | ||||
|         case '802-11-wireless': | ||||
|             let wirelessSetting = connection.get_setting_wireless(); | ||||
|             let ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid()); | ||||
|             title = _("Authentication required by wireless network"); | ||||
|             body = _("Passwords or encryption keys are required to access the wireless network “%s”.").format(ssid); | ||||
|             break; | ||||
|         case '802-3-ethernet': | ||||
|             title = _("Wired 802.1X authentication"); | ||||
|             body = _("A password is required to connect to “%s”.".format(connection.get_id())); | ||||
|             break; | ||||
|         case 'pppoe': | ||||
|             title = _("DSL authentication"); | ||||
|             body = _("A password is required to connect to “%s”.".format(connection.get_id())); | ||||
|             break; | ||||
|         case 'gsm': | ||||
|             if (hints.indexOf('pin') != -1) { | ||||
|                 let gsmSetting = connection.get_setting_gsm(); | ||||
|                 title = _("PIN code required"); | ||||
|                 message = _("PIN code is needed for the mobile broadband device"); | ||||
|                 break; | ||||
|             } | ||||
|             // fall through | ||||
|         case 'cdma': | ||||
|         case 'bluetooth': | ||||
|             title = _("Mobile broadband network password"); | ||||
|             message = _("A password is required to connect to “%s”.").format(connectionSetting.get_id()); | ||||
|             break; | ||||
|         default: | ||||
|             log('Invalid connection type: ' + connectionType); | ||||
|             this._native.respond(requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let notification = new MessageTray.Notification(source, title, body); | ||||
|  | ||||
|         notification.connect('activated', Lang.bind(this, function() { | ||||
|             notification.answered = true; | ||||
|             this._handleRequest(requestId, connection, settingName, hints, flags); | ||||
|         })); | ||||
|  | ||||
|         this._notifications[requestId] = notification; | ||||
|         notification.connect('destroy', Lang.bind(this, function() { | ||||
|             if (!notification.answered) | ||||
|                 this._native.respond(requestId, Shell.NetworkAgentResponse.USER_CANCELED); | ||||
|             delete this._notifications[requestId]; | ||||
|         })); | ||||
|  | ||||
|         Main.messageTray.add(source); | ||||
|         source.notify(notification); | ||||
|     }, | ||||
|  | ||||
|     _newRequest:  function(agent, requestId, connection, settingName, hints, flags) { | ||||
|         if (!this._enabled) { | ||||
|             agent.respond(requestId, Shell.NetworkAgentResponse.USER_CANCELED); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (!(flags & NMClient.SecretAgentGetSecretsFlags.USER_REQUESTED)) | ||||
|             this._showNotification(requestId, connection, settingName, hints, flags); | ||||
|         else | ||||
|             this._handleRequest(requestId, connection, settingName, hints, flags); | ||||
|     }, | ||||
|  | ||||
|     _handleRequest: function(requestId, connection, settingName, hints, flags) { | ||||
|         if (settingName == 'vpn') { | ||||
|             this._vpnRequest(requestId, connection, hints, flags); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let dialog = new NetworkSecretDialog(agent, requestId, connection, settingName, hints); | ||||
|         let dialog = new NetworkSecretDialog(this._native, requestId, connection, settingName, hints); | ||||
|         dialog.connect('destroy', Lang.bind(this, function() { | ||||
|             delete this._dialogs[requestId]; | ||||
|         })); | ||||
|   | ||||
| @@ -13,13 +13,19 @@ const Mainloop = imports.mainloop; | ||||
| const Polkit = imports.gi.Polkit; | ||||
| const PolkitAgent = imports.gi.PolkitAgent; | ||||
|  | ||||
| const Animation = imports.ui.animation; | ||||
| const Components = imports.ui.components; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| const UserWidget = imports.ui.userWidget; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const DIALOG_ICON_SIZE = 48; | ||||
|  | ||||
| const WORK_SPINNER_ICON_SIZE = 16; | ||||
| const WORK_SPINNER_ANIMATION_DELAY = 1.0; | ||||
| const WORK_SPINNER_ANIMATION_TIME = 0.3; | ||||
|  | ||||
| const AuthenticationDialog = new Lang.Class({ | ||||
|     Name: 'AuthenticationDialog', | ||||
|     Extends: ModalDialog.ModalDialog, | ||||
| @@ -136,6 +142,13 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|         this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate)); | ||||
|         this._passwordBox.add(this._passwordEntry, | ||||
|                               { expand: true }); | ||||
|  | ||||
|         let spinnerIcon = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg'); | ||||
|         this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE); | ||||
|         this._workSpinner.actor.opacity = 0; | ||||
|  | ||||
|         this._passwordBox.add(this._workSpinner.actor); | ||||
|  | ||||
|         this.setInitialKeyFocus(this._passwordEntry); | ||||
|         this._passwordBox.hide(); | ||||
|  | ||||
| @@ -165,17 +178,10 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|  | ||||
|         this._cancelButton = this.addButton({ label: _("Cancel"), | ||||
|                                               action: Lang.bind(this, this.cancel), | ||||
|                                               key: Clutter.Escape }, | ||||
|                                             { expand: true, x_fill: false, x_align: St.Align.START }); | ||||
|         this.placeSpinner({ expand: false, | ||||
|                             x_fill: false, | ||||
|                             y_fill: false, | ||||
|                             x_align: St.Align.END, | ||||
|                             y_align: St.Align.MIDDLE }); | ||||
|                                               key: Clutter.Escape }); | ||||
|         this._okButton = this.addButton({ label:  _("Authenticate"), | ||||
|                                           action: Lang.bind(this, this._onAuthenticateButtonPressed), | ||||
|                                           default: true }, | ||||
|                                         { expand: false, x_fill: false, x_align: St.Align.END }); | ||||
|                                           default: true }); | ||||
|  | ||||
|         this._doneEmitted = false; | ||||
|  | ||||
| @@ -183,6 +189,30 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|         this._cookie = cookie; | ||||
|     }, | ||||
|  | ||||
|     _setWorking: function(working) { | ||||
|         Tweener.removeTweens(this._workSpinner.actor); | ||||
|         if (working) { | ||||
|             this._workSpinner.play(); | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 255, | ||||
|                                delay: WORK_SPINNER_ANIMATION_DELAY, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear' | ||||
|                              }); | ||||
|         } else { | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 0, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear', | ||||
|                                onCompleteScope: this, | ||||
|                                onComplete: function() { | ||||
|                                    if (this._workSpinner) | ||||
|                                        this._workSpinner.stop(); | ||||
|                                } | ||||
|                              }); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     performAuthentication: function() { | ||||
|         this.destroySession(); | ||||
|         this._session = new PolkitAgent.Session({ identity: this._identityToAuth, | ||||
| @@ -229,7 +259,7 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|  | ||||
|         this._okButton.can_focus = sensitive; | ||||
|         this._okButton.reactive = sensitive; | ||||
|         this.setWorking(!sensitive); | ||||
|         this._setWorking(!sensitive); | ||||
|     }, | ||||
|  | ||||
|     _onEntryActivate: function() { | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Shell = imports.gi.Shell; | ||||
| @@ -31,6 +32,8 @@ const SCROLLBACK_HISTORY_LINES = 10; | ||||
| // See Notification._onEntryChanged | ||||
| const COMPOSING_STOP_TIMEOUT = 5; | ||||
|  | ||||
| const CHAT_EXPAND_LINES = 12; | ||||
|  | ||||
| const NotificationDirection = { | ||||
|     SENT: 'chat-sent', | ||||
|     RECEIVED: 'chat-received' | ||||
| @@ -155,14 +158,6 @@ const TelepathyClient = new Lang.Class({ | ||||
|         this._chatSources[channel.get_object_path()] = source; | ||||
|         source.connect('destroy', Lang.bind(this, | ||||
|                        function() { | ||||
|                            if (this._tpClient.is_handling_channel(channel)) { | ||||
|                                // The chat box has been destroyed so it can't | ||||
|                                // handle the channel any more. | ||||
|                                channel.close_async(function(src, result) { | ||||
|                                    channel.close_finish(result); | ||||
|                                }); | ||||
|                            } | ||||
|  | ||||
|                            delete this._chatSources[channel.get_object_path()]; | ||||
|                        })); | ||||
|     }, | ||||
| @@ -271,15 +266,8 @@ const ChatSource = new Lang.Class({ | ||||
|         this._channel = channel; | ||||
|         this._closedId = this._channel.connect('invalidated', Lang.bind(this, this._channelClosed)); | ||||
|  | ||||
|         this._notification = new ChatNotification(this); | ||||
|         this._notification.connect('activated', Lang.bind(this, this.open)); | ||||
|         this._notification.setUrgency(MessageTray.Urgency.HIGH); | ||||
|         this._notifyTimeoutId = 0; | ||||
|  | ||||
|         // We ack messages when the user expands the new notification or views the summary | ||||
|         // notification, in which case the notification is also expanded. | ||||
|         this._notification.connect('expanded', Lang.bind(this, this._ackMessages)); | ||||
|  | ||||
|         this._presence = contact.get_presence_type(); | ||||
|  | ||||
|         this._sentId = this._channel.connect('message-sent', Lang.bind(this, this._messageSent)); | ||||
| @@ -292,15 +280,48 @@ const ChatSource = new Lang.Class({ | ||||
|  | ||||
|         // Add ourselves as a source. | ||||
|         Main.messageTray.add(this); | ||||
|         this.pushNotification(this._notification); | ||||
|  | ||||
|         this._getLogMessages(); | ||||
|     }, | ||||
|  | ||||
|     _ensureNotification: function() { | ||||
|         if (this._notification) | ||||
|             return; | ||||
|  | ||||
|         this._notification = new ChatNotification(this); | ||||
|         this._notification.connect('activated', Lang.bind(this, this.open)); | ||||
|         this._notification.connect('updated', Lang.bind(this, | ||||
|             function() { | ||||
|                 if (this._banner && this._banner.expanded) | ||||
|                     this._ackMessages(); | ||||
|             })); | ||||
|         this._notification.connect('destroy', Lang.bind(this, | ||||
|             function() { | ||||
|                 this._notification = null; | ||||
|             })); | ||||
|         this.pushNotification(this._notification); | ||||
|     }, | ||||
|  | ||||
|     _createPolicy: function() { | ||||
|         if (this._account.protocol_name == 'irc') | ||||
|             return new MessageTray.NotificationApplicationPolicy('org.gnome.Polari'); | ||||
|         return new MessageTray.NotificationApplicationPolicy('empathy'); | ||||
|     }, | ||||
|  | ||||
|     createBanner: function() { | ||||
|         this._banner = new ChatNotificationBanner(this._notification); | ||||
|  | ||||
|         // We ack messages when the user expands the new notification | ||||
|         let id = this._banner.connect('expanded', Lang.bind(this, this._ackMessages)); | ||||
|         this._banner.actor.connect('destroy', Lang.bind(this, | ||||
|             function() { | ||||
|                 this._banner.disconnect(id); | ||||
|                 this._banner = null; | ||||
|             })); | ||||
|  | ||||
|         return this._banner; | ||||
|     }, | ||||
|  | ||||
|     _updateAlias: function() { | ||||
|         let oldAlias = this.title; | ||||
|         let newAlias = this._contact.get_alias(); | ||||
| @@ -309,7 +330,8 @@ const ChatSource = new Lang.Class({ | ||||
|             return; | ||||
|  | ||||
|         this.setTitle(newAlias); | ||||
|         this._notification.appendAliasChange(oldAlias, newAlias); | ||||
|         if (this._notification) | ||||
|             this._notification.appendAliasChange(oldAlias, newAlias); | ||||
|     }, | ||||
|  | ||||
|     getIcon: function() { | ||||
| @@ -352,10 +374,16 @@ const ChatSource = new Lang.Class({ | ||||
|  | ||||
|     _updateAvatarIcon: function() { | ||||
|         this.iconUpdated(); | ||||
|         this._notification.update(this._notification.title, null, { customContent: true }); | ||||
|         if (this._notifiction) | ||||
|             this._notification.update(this._notification.title, | ||||
|                                       this._notification.bannerBodyText, | ||||
|                                       { gicon: this.getIcon() }); | ||||
|     }, | ||||
|  | ||||
|     open: function() { | ||||
|         Main.overview.hide(); | ||||
|         Main.panel.closeCalendar(); | ||||
|  | ||||
|         if (this._client.is_handling_channel(this._channel)) { | ||||
|             // We are handling the channel, try to pass it to Empathy or Polari | ||||
|             // (depending on the channel type) | ||||
| @@ -390,6 +418,7 @@ const ChatSource = new Lang.Class({ | ||||
|         let [success, events] = logManager.get_filtered_events_finish(result); | ||||
|  | ||||
|         let logMessages = events.map(makeMessageFromTplEvent); | ||||
|         this._ensureNotification(); | ||||
|  | ||||
|         let pendingTpMessages = this._channel.get_pending_messages(); | ||||
|         let pendingMessages = []; | ||||
| @@ -439,6 +468,18 @@ const ChatSource = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     destroy: function(reason) { | ||||
|         if (this._client.is_handling_channel(this._channel)) { | ||||
|             // The chat box has been destroyed so it can't | ||||
|             // handle the channel any more. | ||||
|             this._channel.close_async(function(channel, result) { | ||||
|                 channel.close_finish(result); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         // Keep source alive while the channel is open | ||||
|         if (reason != MessageTray.NotificationDestroyedReason.SOURCE_CLOSED) | ||||
|             return; | ||||
|  | ||||
|         if (this._destroyed) | ||||
|             return; | ||||
|  | ||||
| @@ -452,9 +493,6 @@ const ChatSource = new Lang.Class({ | ||||
|         this._contact.disconnect(this._notifyAvatarId); | ||||
|         this._contact.disconnect(this._presenceChangedId); | ||||
|  | ||||
|         if (this._timestampTimeoutId) | ||||
|             Mainloop.source_remove(this._timestampTimeoutId); | ||||
|  | ||||
|         this.parent(reason); | ||||
|     }, | ||||
|  | ||||
| @@ -479,6 +517,7 @@ const ChatSource = new Lang.Class({ | ||||
|         if (message.get_message_type() == Tp.ChannelTextMessageType.DELIVERY_REPORT) | ||||
|             return; | ||||
|  | ||||
|         this._ensureNotification(); | ||||
|         this._pendingMessages.push(message); | ||||
|         this.countUpdated(); | ||||
|  | ||||
| @@ -506,6 +545,7 @@ const ChatSource = new Lang.Class({ | ||||
|     // This is called for both messages we send from | ||||
|     // our client and other clients as well. | ||||
|     _messageSent: function(channel, message, flags, token) { | ||||
|         this._ensureNotification(); | ||||
|         message = makeMessageFromTpMessage(message, NotificationDirection.SENT); | ||||
|         this._notification.appendMessage(message); | ||||
|     }, | ||||
| @@ -543,14 +583,10 @@ const ChatSource = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _presenceChanged: function (contact, presence, status, message) { | ||||
|         let msg, title; | ||||
|  | ||||
|         title = GLib.markup_escape_text(this.title, -1); | ||||
|  | ||||
|         this._notification.update(this._notification.title, null, { customContent: true, secondaryGIcon: this.getSecondaryIcon() }); | ||||
|  | ||||
|         if (message) | ||||
|             msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>'; | ||||
|         if (this._notification) | ||||
|             this._notification.update(this._notification.title, | ||||
|                                       this._notification.bannerBodyText, | ||||
|                                       { secondaryGIcon: this.getSecondaryIcon() }); | ||||
|     }, | ||||
|  | ||||
|     _pendingRemoved: function(channel, message) { | ||||
| @@ -560,6 +596,10 @@ const ChatSource = new Lang.Class({ | ||||
|             this._pendingMessages.splice(idx, 1); | ||||
|             this.countUpdated(); | ||||
|         } | ||||
|  | ||||
|         if (this._pendingMessages.length == 0 && | ||||
|             this._banner && !this._banner.expanded) | ||||
|             this._banner.hide(); | ||||
|     }, | ||||
|  | ||||
|     _ackMessages: function() { | ||||
| @@ -574,42 +614,20 @@ const ChatNotification = new Lang.Class({ | ||||
|     Extends: MessageTray.Notification, | ||||
|  | ||||
|     _init: function(source) { | ||||
|         this.parent(source, source.title, null, { customContent: true, secondaryGIcon: source.getSecondaryIcon() }); | ||||
|         this.parent(source, source.title, null, | ||||
|                     { secondaryGIcon: source.getSecondaryIcon() }); | ||||
|         this.setUrgency(MessageTray.Urgency.HIGH); | ||||
|         this.setResident(true); | ||||
|  | ||||
|         this._responseEntry = new St.Entry({ style_class: 'chat-response', | ||||
|                                              can_focus: true }); | ||||
|         this._responseEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivated)); | ||||
|         this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged)); | ||||
|         this.setActionArea(this._responseEntry); | ||||
|  | ||||
|         this._responseEntry.clutter_text.connect('key-focus-in', Lang.bind(this, function() { | ||||
|             this.focused = true; | ||||
|         })); | ||||
|         this._responseEntry.clutter_text.connect('key-focus-out', Lang.bind(this, function() { | ||||
|             this.focused = false; | ||||
|             this.emit('unfocused'); | ||||
|         })); | ||||
|  | ||||
|         this._createScrollArea(); | ||||
|         this._lastGroup = null; | ||||
|  | ||||
|         // Keep track of the bottom position for the current adjustment and | ||||
|         // force a scroll to the bottom if things change while we were at the | ||||
|         // bottom | ||||
|         this._oldMaxScrollValue = this._scrollArea.vscroll.adjustment.value; | ||||
|         this._scrollArea.add_style_class_name('chat-notification-scrollview'); | ||||
|         this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) { | ||||
|             if (adjustment.value == this._oldMaxScrollValue) | ||||
|                 this.scrollTo(St.Side.BOTTOM); | ||||
|             this._oldMaxScrollValue = Math.max(adjustment.lower, adjustment.upper - adjustment.page_size); | ||||
|         })); | ||||
|  | ||||
|         this._inputHistory = new History.HistoryManager({ entry: this._responseEntry.clutter_text }); | ||||
|  | ||||
|         this._history = []; | ||||
|         this.messages = []; | ||||
|         this._timestampTimeoutId = 0; | ||||
|         this._composingTimeoutId = 0; | ||||
|     }, | ||||
|  | ||||
|     destroy: function(reason) { | ||||
|         if (this._timestampTimeoutId) | ||||
|             Mainloop.source_remove(this._timestampTimeoutId); | ||||
|         this._timestampTimeoutId = 0; | ||||
|         this.parent(reason); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -635,10 +653,8 @@ const ChatNotification = new Lang.Class({ | ||||
|             styles.push('chat-action'); | ||||
|         } | ||||
|  | ||||
|         if (message.direction == NotificationDirection.RECEIVED) { | ||||
|             this.update(this.source.title, messageBody, { customContent: true, | ||||
|                                                           bannerMarkup: true }); | ||||
|         } | ||||
|         if (message.direction == NotificationDirection.RECEIVED) | ||||
|             this.update(this.source.title, messageBody, { bannerMarkup: true }); | ||||
|  | ||||
|         let group = (message.direction == NotificationDirection.RECEIVED ? | ||||
|                      'received' : 'sent'); | ||||
| @@ -651,10 +667,10 @@ const ChatNotification = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _filterMessages: function() { | ||||
|         if (this._history.length < 1) | ||||
|         if (this.messages.length < 1) | ||||
|             return; | ||||
|  | ||||
|         let lastMessageTime = this._history[0].time; | ||||
|         let lastMessageTime = this.messages[0].timestamp; | ||||
|         let currentTime = (Date.now() / 1000); | ||||
|  | ||||
|         // Keep the scrollback from growing too long. If the most | ||||
| @@ -666,12 +682,12 @@ const ChatNotification = new Lang.Class({ | ||||
|         let maxLength = (lastMessageTime < currentTime - SCROLLBACK_RECENT_TIME) ? | ||||
|             SCROLLBACK_IDLE_LENGTH : SCROLLBACK_RECENT_LENGTH; | ||||
|  | ||||
|         let filteredHistory = this._history.filter(function(item) { return item.realMessage }); | ||||
|         let filteredHistory = this.messages.filter(function(item) { return item.realMessage }); | ||||
|         if (filteredHistory.length > maxLength) { | ||||
|             let lastMessageToKeep = filteredHistory[maxLength]; | ||||
|             let expired = this._history.splice(this._history.indexOf(lastMessageToKeep)); | ||||
|             let expired = this.messages.splice(this.messages.indexOf(lastMessageToKeep)); | ||||
|             for (let i = 0; i < expired.length; i++) | ||||
|                 expired[i].actor.destroy(); | ||||
|                 this.emit('message-removed', expired[i]); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -684,7 +700,6 @@ const ChatNotification = new Lang.Class({ | ||||
|      *  styles: Style class names for the message to have. | ||||
|      *  timestamp: The timestamp of the message. | ||||
|      *  noTimestamp: suppress timestamp signal? | ||||
|      *  childProps: props to add the actor with. | ||||
|      */ | ||||
|     _append: function(props) { | ||||
|         let currentTime = (Date.now() / 1000); | ||||
| @@ -692,44 +707,23 @@ const ChatNotification = new Lang.Class({ | ||||
|                                       group: null, | ||||
|                                       styles: [], | ||||
|                                       timestamp: currentTime, | ||||
|                                       noTimestamp: false, | ||||
|                                       childProps: null }); | ||||
|                                       noTimestamp: false }); | ||||
|  | ||||
|         // Reset the old message timeout | ||||
|         if (this._timestampTimeoutId) | ||||
|             Mainloop.source_remove(this._timestampTimeoutId); | ||||
|         this._timestampTimeoutId = 0; | ||||
|  | ||||
|         let highlighter = new MessageTray.URLHighlighter(props.body, | ||||
|                                                          true,  // line wrap? | ||||
|                                                          true); // allow markup? | ||||
|         let message = { realMessage: props.group != 'meta', | ||||
|                         showTimestamp: false }; | ||||
|         Lang.copyProperties(props, message); | ||||
|         delete message.noTimestamp; | ||||
|  | ||||
|         let body = highlighter.actor; | ||||
|  | ||||
|         let styles = props.styles; | ||||
|         for (let i = 0; i < styles.length; i++) | ||||
|             body.add_style_class_name(styles[i]); | ||||
|  | ||||
|         let group = props.group; | ||||
|         if (group != this._lastGroup) { | ||||
|             this._lastGroup = group; | ||||
|             let emptyLine = new St.Label({ style_class: 'chat-empty-line' }); | ||||
|             this.addActor(emptyLine); | ||||
|             this._history.unshift({ actor: emptyLine, time: timestamp, | ||||
|                                     realMessage: false }); | ||||
|         } | ||||
|  | ||||
|         let lineBox = new St.BoxLayout({ vertical: false }); | ||||
|         lineBox.add(body, props.childProps); | ||||
|         this.addActor(lineBox); | ||||
|         this._lastMessageBox = lineBox; | ||||
|  | ||||
|         this.updated(); | ||||
|  | ||||
|         let timestamp = props.timestamp; | ||||
|         this._history.unshift({ actor: lineBox, time: timestamp, | ||||
|                                 realMessage: group != 'meta' }); | ||||
|         this.messages.unshift(message); | ||||
|         this.emit('message-added', message); | ||||
|  | ||||
|         if (!props.noTimestamp) { | ||||
|             let timestamp = props.timestamp; | ||||
|             if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME) { | ||||
|                 this.appendTimestamp(); | ||||
|             } else { | ||||
| @@ -748,15 +742,8 @@ const ChatNotification = new Lang.Class({ | ||||
|     appendTimestamp: function() { | ||||
|         this._timestampTimeoutId = 0; | ||||
|  | ||||
|         let lastMessageTime = this._history[0].time; | ||||
|         let lastMessageDate = new Date(lastMessageTime * 1000); | ||||
|  | ||||
|         let timeLabel = Util.createTimeLabel(lastMessageDate); | ||||
|         timeLabel.style_class = 'chat-meta-message'; | ||||
|         timeLabel.x_expand = timeLabel.y_expand = true; | ||||
|         timeLabel.x_align = timeLabel.y_align = Clutter.ActorAlign.END; | ||||
|  | ||||
|         this._lastMessageBox.add_actor(timeLabel); | ||||
|         this.messages[0].showTimestamp = true; | ||||
|         this.emit('timestamp-changed', this.messages[0]); | ||||
|  | ||||
|         this._filterMessages(); | ||||
|  | ||||
| @@ -771,13 +758,154 @@ const ChatNotification = new Lang.Class({ | ||||
|            IM name. */ | ||||
|         let message = '<i>' + _("%s is now known as %s").format(oldAlias, newAlias) + '</i>'; | ||||
|  | ||||
|         let label = this._append({ body: message, | ||||
|                                    group: 'meta', | ||||
|                                    styles: ['chat-meta-message'] }); | ||||
|  | ||||
|         this.update(newAlias, null, { customContent: true }); | ||||
|         this._append({ body: message, | ||||
|                        group: 'meta', | ||||
|                        styles: ['chat-meta-message'] }); | ||||
|  | ||||
|         this._filterMessages(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const ChatLineBox = new Lang.Class({ | ||||
|     Name: 'ChatLineBox', | ||||
|     Extends: St.BoxLayout, | ||||
|  | ||||
|     vfunc_get_preferred_height: function(forWidth) { | ||||
|         let [, natHeight] = this.parent(forWidth); | ||||
|         return [natHeight, natHeight]; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const ChatNotificationBanner = new Lang.Class({ | ||||
|     Name: 'ChatNotificationBanner', | ||||
|     Extends: MessageTray.NotificationBanner, | ||||
|  | ||||
|     _init: function(notification) { | ||||
|         this.parent(notification); | ||||
|  | ||||
|         this._responseEntry = new St.Entry({ style_class: 'chat-response', | ||||
|                                              x_expand: true, | ||||
|                                              can_focus: true }); | ||||
|         this._responseEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivated)); | ||||
|         this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged)); | ||||
|         this.setActionArea(this._responseEntry); | ||||
|  | ||||
|         this._responseEntry.clutter_text.connect('key-focus-in', Lang.bind(this, function() { | ||||
|             this.focused = true; | ||||
|         })); | ||||
|         this._responseEntry.clutter_text.connect('key-focus-out', Lang.bind(this, function() { | ||||
|             this.focused = false; | ||||
|             this.emit('unfocused'); | ||||
|         })); | ||||
|  | ||||
|         this._scrollArea = new St.ScrollView({ style_class: 'chat-scrollview vfade', | ||||
|                                                vscrollbar_policy: Gtk.PolicyType.AUTOMATIC, | ||||
|                                                hscrollbar_policy: Gtk.PolicyType.NEVER, | ||||
|                                                visible: this.expanded }); | ||||
|         this._contentArea = new St.BoxLayout({ style_class: 'chat-body', | ||||
|                                                vertical: true }); | ||||
|         this._scrollArea.add_actor(this._contentArea); | ||||
|  | ||||
|         this.setExpandedBody(this._scrollArea); | ||||
|         this.setExpandedLines(CHAT_EXPAND_LINES); | ||||
|  | ||||
|         this._lastGroup = null; | ||||
|  | ||||
|         // Keep track of the bottom position for the current adjustment and | ||||
|         // force a scroll to the bottom if things change while we were at the | ||||
|         // bottom | ||||
|         this._oldMaxScrollValue = this._scrollArea.vscroll.adjustment.value; | ||||
|         this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) { | ||||
|             if (adjustment.value == this._oldMaxScrollValue) | ||||
|                 this.scrollTo(St.Side.BOTTOM); | ||||
|             this._oldMaxScrollValue = Math.max(adjustment.lower, adjustment.upper - adjustment.page_size); | ||||
|         })); | ||||
|  | ||||
|         this._inputHistory = new History.HistoryManager({ entry: this._responseEntry.clutter_text }); | ||||
|  | ||||
|         this._composingTimeoutId = 0; | ||||
|  | ||||
|         this._messageActors = new Map(); | ||||
|  | ||||
|         this._messageAddedId = this.notification.connect('message-added', | ||||
|             Lang.bind(this, function(n, message) { | ||||
|                 this._addMessage(message); | ||||
|             })); | ||||
|         this._messageRemovedId = this.notification.connect('message-removed', | ||||
|             Lang.bind(this, function(n, message) { | ||||
|                 let actor = this._messageActors.get(message); | ||||
|                 if (this._messageActors.delete(message)) | ||||
|                     actor.destroy(); | ||||
|             })); | ||||
|         this._timestampChangedId = this.notification.connect('timestamp-changed', | ||||
|             Lang.bind(this, function(n, message) { | ||||
|                 this._updateTimestamp(message); | ||||
|             })); | ||||
|  | ||||
|         for (let i = this.notification.messages.length - 1; i >= 0; i--) | ||||
|             this._addMessage(this.notification.messages[i]); | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|         this.parent(); | ||||
|         this.notification.disconnect(this._messageAddedId); | ||||
|         this.notification.disconnect(this._messageRemovedId); | ||||
|         this.notification.disconnect(this._timestampChangedId); | ||||
|     }, | ||||
|  | ||||
|     scrollTo: function(side) { | ||||
|         let adjustment = this._scrollArea.vscroll.adjustment; | ||||
|         if (side == St.Side.TOP) | ||||
|             adjustment.value = adjustment.lower; | ||||
|         else if (side == St.Side.BOTTOM) | ||||
|             adjustment.value = adjustment.upper; | ||||
|     }, | ||||
|  | ||||
|     hide: function() { | ||||
|         this.emit('done-displaying'); | ||||
|     }, | ||||
|  | ||||
|     _addMessage: function(message) { | ||||
|         let highlighter = new Calendar.URLHighlighter(message.body, true, true); | ||||
|         let body = highlighter.actor; | ||||
|  | ||||
|         let styles = message.styles; | ||||
|         for (let i = 0; i < styles.length; i++) | ||||
|             body.add_style_class_name(styles[i]); | ||||
|  | ||||
|         let group = message.group; | ||||
|         if (group != this._lastGroup) { | ||||
|             this._lastGroup = group; | ||||
|             body.add_style_class_name('chat-new-group'); | ||||
|         } | ||||
|  | ||||
|         let lineBox = new ChatLineBox(); | ||||
|         lineBox.add(body); | ||||
|         this._contentArea.add_actor(lineBox); | ||||
|         this._messageActors.set(message, lineBox); | ||||
|  | ||||
|         this._updateTimestamp(message); | ||||
|     }, | ||||
|  | ||||
|     _updateTimestamp: function(message) { | ||||
|         let actor = this._messageActors.get(message); | ||||
|         if (!actor) | ||||
|             return; | ||||
|  | ||||
|         while (actor.get_n_children() > 1) | ||||
|             actor.get_child_at_index(1).destroy(); | ||||
|  | ||||
|         if (message.showTimestamp) { | ||||
|             let lastMessageTime = message.timestamp; | ||||
|             let lastMessageDate = new Date(lastMessageTime * 1000); | ||||
|  | ||||
|             let timeLabel = Util.createTimeLabel(lastMessageDate); | ||||
|             timeLabel.style_class = 'chat-meta-message'; | ||||
|             timeLabel.x_expand = timeLabel.y_expand = true; | ||||
|             timeLabel.x_align = timeLabel.y_align = Clutter.ActorAlign.END; | ||||
|  | ||||
|             actor.add_actor(timeLabel); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onEntryActivated: function() { | ||||
| @@ -790,13 +918,13 @@ const ChatNotification = new Lang.Class({ | ||||
|         // Telepathy sends out the Sent signal for us. | ||||
|         // see Source._messageSent | ||||
|         this._responseEntry.set_text(''); | ||||
|         this.source.respond(text); | ||||
|         this.notification.source.respond(text); | ||||
|     }, | ||||
|  | ||||
|     _composingStopTimeout: function() { | ||||
|         this._composingTimeoutId = 0; | ||||
|  | ||||
|         this.source.setChatState(Tp.ChannelChatState.PAUSED); | ||||
|         this.notification.source.setChatState(Tp.ChannelChatState.PAUSED); | ||||
|  | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
| @@ -816,14 +944,14 @@ const ChatNotification = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         if (text != '') { | ||||
|             this.source.setChatState(Tp.ChannelChatState.COMPOSING); | ||||
|             this.notification.source.setChatState(Tp.ChannelChatState.COMPOSING); | ||||
|  | ||||
|             this._composingTimeoutId = Mainloop.timeout_add_seconds( | ||||
|                 COMPOSING_STOP_TIMEOUT, | ||||
|                 Lang.bind(this, this._composingStopTimeout)); | ||||
|             GLib.Source.set_name_by_id(this._composingTimeoutId, '[gnome-shell] this._composingStopTimeout'); | ||||
|         } else { | ||||
|             this.source.setChatState(Tp.ChannelChatState.ACTIVE); | ||||
|             this.notification.source.setChatState(Tp.ChannelChatState.ACTIVE); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -27,8 +27,8 @@ const CtrlAltTabManager = new Lang.Class({ | ||||
|     _init: function() { | ||||
|         this._items = []; | ||||
|         this.addGroup(global.window_group, _("Windows"), | ||||
|                       'emblem-documents-symbolic', { sortGroup: SortGroup.TOP, | ||||
|                                                      focusCallback: Lang.bind(this, this._focusWindows) }); | ||||
|                       'focus-windows-symbolic', { sortGroup: SortGroup.TOP, | ||||
|                                                   focusCallback: Lang.bind(this, this._focusWindows) }); | ||||
|     }, | ||||
|  | ||||
|     addGroup: function(root, name, icon, params) { | ||||
|   | ||||
| @@ -270,7 +270,7 @@ const ShowAppsIcon = new Lang.Class({ | ||||
|         if (app == null) | ||||
|             return false; | ||||
|  | ||||
|         if (!this._settings.is_writable('favorite-apps')) | ||||
|         if (!global.settings.is_writable('favorite-apps')) | ||||
|             return false; | ||||
|  | ||||
|         let id = app.get_id(); | ||||
| @@ -428,8 +428,6 @@ const Dash = new Lang.Class({ | ||||
|  | ||||
|         this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay)); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); | ||||
|  | ||||
|         this._appSystem = Shell.AppSystem.get_default(); | ||||
|  | ||||
|         this._appSystem.connect('installed-changed', Lang.bind(this, function() { | ||||
| @@ -862,7 +860,7 @@ const Dash = new Lang.Class({ | ||||
|         if (app == null || app.is_window_backed()) | ||||
|             return DND.DragMotionResult.NO_DROP; | ||||
|  | ||||
|         if (!this._settings.is_writable('favorite-apps')) | ||||
|         if (!global.settings.is_writable('favorite-apps')) | ||||
|             return DND.DragMotionResult.NO_DROP; | ||||
|  | ||||
|         let favorites = AppFavorites.getAppFavorites().getFavorites(); | ||||
| @@ -941,7 +939,7 @@ const Dash = new Lang.Class({ | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (!this._settings.is_writable('favorite-apps')) | ||||
|         if (!global.settings.is_writable('favorite-apps')) | ||||
|             return false; | ||||
|  | ||||
|         let id = app.get_id(); | ||||
|   | ||||
| @@ -357,8 +357,6 @@ const DateMenuButton = new Lang.Class({ | ||||
|                 this._date.setDate(now); | ||||
|                 this._messageList.setDate(now); | ||||
|             } | ||||
|             // Block notification banners while the menu is open | ||||
|             Main.messageTray.bannerBlocked = isOpen; | ||||
|         })); | ||||
|  | ||||
|         // Fill up the first column | ||||
|   | ||||
| @@ -81,7 +81,6 @@ function init() { | ||||
|  | ||||
|     // Miscellaneous monkeypatching | ||||
|     _patchContainerClass(St.BoxLayout); | ||||
|     _patchContainerClass(St.Table); | ||||
|  | ||||
|     _patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows', | ||||
|                                              column_spacing: 'spacing-columns' }); | ||||
|   | ||||
| @@ -211,7 +211,7 @@ const InstallExtensionDialog = new Lang.Class({ | ||||
|         let icon = new St.Icon({ gicon: gicon }); | ||||
|         box.add(icon); | ||||
|  | ||||
|         let label = new St.Label({ style_class: 'prompt-dialog-headline', | ||||
|         let label = new St.Label({ style_class: 'prompt-dialog-headline headline', | ||||
|                                    text: message }); | ||||
|         box.add(label); | ||||
|     }, | ||||
|   | ||||
| @@ -6,6 +6,7 @@ const Gdk = imports.gi.Gdk; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Signals = imports.signals; | ||||
| const St = imports.gi.St; | ||||
| @@ -23,9 +24,6 @@ const KEYBOARD_TYPE = 'keyboard-type'; | ||||
| const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications'; | ||||
| const SHOW_KEYBOARD = 'screen-keyboard-enabled'; | ||||
|  | ||||
| const CURSOR_BUS_NAME = 'org.gnome.SettingsDaemon.Cursor'; | ||||
| const CURSOR_OBJECT_PATH = '/org/gnome/SettingsDaemon/Cursor'; | ||||
|  | ||||
| const CARIBOU_BUS_NAME = 'org.gnome.Caribou.Daemon'; | ||||
| const CARIBOU_OBJECT_PATH = '/org/gnome/Caribou/Daemon'; | ||||
|  | ||||
| @@ -60,14 +58,7 @@ const CaribouDaemonIface = '<node> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const CursorManagerIface = '<node> \ | ||||
| <interface name="org.gnome.SettingsDaemon.Cursor"> \ | ||||
| <property name="ShowOSK" type="b" access="read" /> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const CaribouDaemonProxy = Gio.DBusProxy.makeProxyWrapper(CaribouDaemonIface); | ||||
| const CursorManagerProxy = Gio.DBusProxy.makeProxyWrapper(CursorManagerIface); | ||||
|  | ||||
| const Key = new Lang.Class({ | ||||
|     Name: 'Key', | ||||
| @@ -120,6 +111,35 @@ const Key = new Lang.Class({ | ||||
|                 key.release(); | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|             })); | ||||
|         button.connect('touch-event', Lang.bind(this, | ||||
|             function (actor, event) { | ||||
|                 let device = event.get_device(); | ||||
|                 let sequence = event.get_event_sequence(); | ||||
|  | ||||
|                 // We only handle touch events here on wayland. On X11 | ||||
|                 // we do get emulated pointer events, which already works | ||||
|                 // for single-touch cases. Besides, the X11 passive touch grab | ||||
|                 // set up by Mutter will make us see first the touch events | ||||
|                 // and later the pointer events, so it will look like two | ||||
|                 // unrelated series of events, we want to avoid double handling | ||||
|                 // in these cases. | ||||
|                 if (!Meta.is_wayland_compositor()) | ||||
|                     return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|                 if (!this._touchPressed && | ||||
|                     event.type() == Clutter.EventType.TOUCH_BEGIN) { | ||||
|                     device.sequence_grab(sequence, actor); | ||||
|                     this._touchPressed = true; | ||||
|                     key.press(); | ||||
|                 } else if (this._touchPressed && | ||||
|                            event.type() == Clutter.EventType.TOUCH_END && | ||||
|                            device.sequence_get_grabbed_actor(sequence) == actor) { | ||||
|                     device.sequence_ungrab(sequence); | ||||
|                     this._touchPressed = false; | ||||
|                     key.release(); | ||||
|                 } | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|             })); | ||||
|  | ||||
|         return button; | ||||
|     }, | ||||
| @@ -184,21 +204,23 @@ const Keyboard = new Lang.Class({ | ||||
|         this._keyboardSettings.connect('changed', Lang.bind(this, this._sync)); | ||||
|         this._a11yApplicationsSettings = new Gio.Settings({ schema_id: A11Y_APPLICATIONS_SCHEMA }); | ||||
|         this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._sync)); | ||||
|         this._watchNameId = Gio.bus_watch_name(Gio.BusType.SESSION, CURSOR_BUS_NAME, 0, | ||||
|                                                Lang.bind(this, this._sync), | ||||
|                                                Lang.bind(this, this._sync)); | ||||
|         this._daemonProxy = null; | ||||
|         this._cursorProxy = new CursorManagerProxy(Gio.DBus.session, CURSOR_BUS_NAME, | ||||
|                                                    CURSOR_OBJECT_PATH, | ||||
|                                                    Lang.bind(this, function(proxy, error) { | ||||
|                                                        if (error) { | ||||
|                                                            log(error.message); | ||||
|                                                            return; | ||||
|                                                        } | ||||
|                                                        this._cursorProxy.connect('g-properties-changed', | ||||
|                                                                                  Lang.bind(this, this._sync)); | ||||
|                                                        this._sync(); | ||||
|                                                    })); | ||||
|         this._lastDeviceId = null; | ||||
|  | ||||
|         if (Meta.is_wayland_compositor() && | ||||
|             Caribou.DisplayAdapter.set_default) | ||||
|             Caribou.DisplayAdapter.set_default(new ShellWaylandAdapter()); | ||||
|  | ||||
|         Meta.get_backend().connect('last-device-changed', Lang.bind(this, | ||||
|             function (backend, deviceId) { | ||||
|                 let manager = Clutter.DeviceManager.get_default(); | ||||
|                 let device = manager.get_device(deviceId); | ||||
|  | ||||
|                 if (device.get_device_name().indexOf('XTEST') < 0) { | ||||
|                     this._lastDeviceId = deviceId; | ||||
|                     this._sync(); | ||||
|                 } | ||||
|             })); | ||||
|         this._sync(); | ||||
|  | ||||
|         this._showIdleId = 0; | ||||
| @@ -217,9 +239,22 @@ const Keyboard = new Lang.Class({ | ||||
|         this._redraw(); | ||||
|     }, | ||||
|  | ||||
|     _lastDeviceIsTouchscreen: function () { | ||||
|         if (!this._lastDeviceId) | ||||
|             return false; | ||||
|  | ||||
|         let manager = Clutter.DeviceManager.get_default(); | ||||
|         let device = manager.get_device(this._lastDeviceId); | ||||
|  | ||||
|         if (!device) | ||||
|             return false; | ||||
|  | ||||
|         return device.get_device_type() == Clutter.InputDeviceType.TOUCHSCREEN_DEVICE; | ||||
|     }, | ||||
|  | ||||
|     _sync: function () { | ||||
|         this._enableKeyboard = this._a11yApplicationsSettings.get_boolean(SHOW_KEYBOARD) || | ||||
|                                this._cursorProxy.ShowOSK; | ||||
|                                this._lastDeviceIsTouchscreen(); | ||||
|         if (!this._enableKeyboard && !this._keyboard) | ||||
|             return; | ||||
|         if (this._enableKeyboard && this._keyboard && | ||||
| @@ -717,3 +752,24 @@ const KeyboardSource = new Lang.Class({ | ||||
|         this._keyboard.show(Main.layoutManager.bottomIndex); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const ShellWaylandAdapter = new Lang.Class({ | ||||
|     Name: 'ShellWaylandAdapter', | ||||
|     Extends: Caribou.XAdapter, | ||||
|  | ||||
|     vfunc_keyval_press: function(keyval) { | ||||
|         let focus = global.stage.get_key_focus(); | ||||
|         if (focus instanceof Clutter.Text) | ||||
|             Shell.util_text_insert_keyval(focus, keyval); | ||||
|         else | ||||
|             this.parent(keyval); | ||||
|     }, | ||||
|  | ||||
|     vfunc_keyval_release: function(keyval) { | ||||
|         let focus = global.stage.get_key_focus(); | ||||
|         if (focus instanceof Clutter.Text) | ||||
|             return;             // do nothing | ||||
|         else | ||||
|             this.parent(keyval); | ||||
|     }, | ||||
| }); | ||||
|   | ||||
| @@ -11,6 +11,7 @@ const St = imports.gi.St; | ||||
|  | ||||
| const Background = imports.ui.background; | ||||
| const BackgroundMenu = imports.ui.backgroundMenu; | ||||
| const LoginManager = imports.misc.loginManager; | ||||
|  | ||||
| const DND = imports.ui.dnd; | ||||
| const Main = imports.ui.main; | ||||
| @@ -273,6 +274,18 @@ const LayoutManager = new Lang.Class({ | ||||
|         global.screen.connect('in-fullscreen-changed', | ||||
|                               Lang.bind(this, this._updateFullscreen)); | ||||
|         this._monitorsChanged(); | ||||
|  | ||||
|         // NVIDIA drivers don't preserve FBO contents across | ||||
|         // suspend/resume, see | ||||
|         // https://bugzilla.gnome.org/show_bug.cgi?id=739178 | ||||
|         if (Shell.util_need_background_refresh()) { | ||||
|             LoginManager.getLoginManager().connect('prepare-for-sleep', | ||||
|                                                    function(lm, suspending) { | ||||
|                                                        if (suspending) | ||||
|                                                            return; | ||||
|                                                        Meta.Background.refresh_all(); | ||||
|                                                    }); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     // This is called by Main after everything else is constructed | ||||
| @@ -824,6 +837,7 @@ const LayoutManager = new Lang.Class({ | ||||
|         // need to connect to 'destroy' too. | ||||
|  | ||||
|         this._trackedActors.push(actorData); | ||||
|         this._updateActorVisibility(actorData); | ||||
|         this._queueUpdateRegions(); | ||||
|     }, | ||||
|  | ||||
| @@ -842,25 +856,23 @@ const LayoutManager = new Lang.Class({ | ||||
|         this._queueUpdateRegions(); | ||||
|     }, | ||||
|  | ||||
|     _updateActorVisibility: function(actorData) { | ||||
|         if (!actorData.trackFullscreen) | ||||
|             return; | ||||
|  | ||||
|         let monitor = this.findMonitorForActor(actorData.actor); | ||||
|         actorData.actor.visible = !(global.window_group.visible && | ||||
|                                     monitor && | ||||
|                                     monitor.inFullscreen); | ||||
|     }, | ||||
|  | ||||
|     _updateVisibility: function() { | ||||
|         let windowsVisible = Main.sessionMode.hasWindows && !this._inOverview; | ||||
|  | ||||
|         global.window_group.visible = windowsVisible; | ||||
|         global.top_window_group.visible = windowsVisible; | ||||
|  | ||||
|         for (let i = 0; i < this._trackedActors.length; i++) { | ||||
|             let actorData = this._trackedActors[i], visible; | ||||
|             if (!actorData.trackFullscreen) | ||||
|                 continue; | ||||
|  | ||||
|             if (!windowsVisible) | ||||
|                 visible = true; | ||||
|             else if (this.findMonitorForActor(actorData.actor).inFullscreen) | ||||
|                 visible = false; | ||||
|             else | ||||
|                 visible = true; | ||||
|             actorData.actor.visible = visible; | ||||
|         } | ||||
|         this._trackedActors.forEach(Lang.bind(this, this._updateActorVisibility)); | ||||
|     }, | ||||
|  | ||||
|     getWorkAreaForMonitor: function(monitorIndex) { | ||||
| @@ -1303,8 +1315,11 @@ const PressureBarrier = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onBarrierLeft: function(barrier, event) { | ||||
|         this._reset(); | ||||
|         this._isTriggered = false; | ||||
|         barrier._isHit = false; | ||||
|         if (this._barriers.every(function(b) { return !b._isHit; })) { | ||||
|             this._reset(); | ||||
|             this._isTriggered = false; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _trigger: function() { | ||||
| @@ -1314,6 +1329,8 @@ const PressureBarrier = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onBarrierHit: function(barrier, event) { | ||||
|         barrier._isHit = true; | ||||
|  | ||||
|         // If we've triggered the barrier, wait until the pointer has the | ||||
|         // left the barrier hitbox until we trigger it again. | ||||
|         if (this._isTriggered) | ||||
|   | ||||
| @@ -1,8 +1,11 @@ | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GLib = imports.gi.GLib; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const CtrlAltTab = imports.ui.ctrlAltTab; | ||||
| const Lang = imports.lang; | ||||
| const Layout = imports.ui.layout; | ||||
| const Main = imports.ui.main; | ||||
| @@ -26,8 +29,12 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = { | ||||
| }; | ||||
|  | ||||
| // Offset of the original position from the bottom-right corner | ||||
| const CONCEALED_VISIBLE_FRACTION = 0.2; | ||||
| const CONCEALED_WIDTH = 3; | ||||
| const REVEAL_ANIMATION_TIME = 0.2; | ||||
| const TEMP_REVEAL_TIME = 2; | ||||
|  | ||||
| const BARRIER_THRESHOLD = 70; | ||||
| const BARRIER_TIMEOUT = 1000; | ||||
|  | ||||
| const LegacyTray = new Lang.Class({ | ||||
|     Name: 'LegacyTray', | ||||
| @@ -49,11 +56,15 @@ const LegacyTray = new Lang.Class({ | ||||
|                                        y_align: Clutter.ActorAlign.END, | ||||
|                                        layout_manager: this._slideLayout }); | ||||
|         this.actor.add_actor(this._slider); | ||||
|         this._slider.connect('notify::allocation', Lang.bind(this, this._syncBarrier)); | ||||
|  | ||||
|         this._box = new St.BoxLayout(); | ||||
|         this._slider.add_actor(this._box); | ||||
|  | ||||
|         this._concealHandle = new St.Button({ style_class: 'legacy-tray-handle' }); | ||||
|         this._concealHandle = new St.Button({ style_class: 'legacy-tray-handle', | ||||
|                                               /* translators: 'Hide' is a verb */ | ||||
|                                               accessible_name: _("Hide tray"), | ||||
|                                               can_focus: true }); | ||||
|         this._concealHandle.child = new St.Icon({ icon_name: 'go-previous-symbolic' }); | ||||
|         this._box.add_child(this._concealHandle); | ||||
|  | ||||
| @@ -81,12 +92,24 @@ const LegacyTray = new Lang.Class({ | ||||
|                 this._revealHandle.show(); | ||||
|             })); | ||||
|  | ||||
|         this._horizontalBarrier = null; | ||||
|         this._pressureBarrier = new Layout.PressureBarrier(BARRIER_THRESHOLD, | ||||
|                                                            BARRIER_TIMEOUT, | ||||
|                                                            Shell.ActionMode.NORMAL); | ||||
|         this._pressureBarrier.connect('trigger', Lang.bind(this, function() { | ||||
|             this._concealHandle.show(); | ||||
|         })); | ||||
|  | ||||
|         Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false }); | ||||
|         Main.layoutManager.trackChrome(this._slider, { affectsInputRegion: true }); | ||||
|         Main.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.modalDialogGroup); | ||||
|         Main.ctrlAltTabManager.addGroup(this.actor, | ||||
|                                         _("Status Icons"), 'focus-legacy-systray-symbolic', | ||||
|                                         { sortGroup: CtrlAltTab.SortGroup.BOTTOM }); | ||||
|  | ||||
|         this._trayManager = new Shell.TrayManager(); | ||||
|         this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded)); | ||||
|         this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved)); | ||||
|         this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded)); | ||||
|         this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved)); | ||||
|         this._trayManager.manage_screen(global.screen, this.actor); | ||||
|  | ||||
|         Main.overview.connect('showing', Lang.bind(this, | ||||
| @@ -121,17 +144,47 @@ const LegacyTray = new Lang.Class({ | ||||
|             return; | ||||
|  | ||||
|         let button = new St.Button({ child: icon, | ||||
|                                      style_class: 'legacy-tray-icon', | ||||
|                                      button_mask: St.ButtonMask.ONE | | ||||
|                                                   St.ButtonMask.TWO | | ||||
|                                                   St.ButtonMask.THREE, | ||||
|                                      can_focus: true, | ||||
|                                      x_fill: true, y_fill: true }); | ||||
|  | ||||
|         let app = Shell.WindowTracker.get_default().get_app_from_pid(icon.pid); | ||||
|         if (!app) | ||||
|             app = Shell.AppSystem.get_default().lookup_startup_wmclass(wmClass); | ||||
|         if (!app) | ||||
|             app = Shell.AppSystem.get_default().lookup_desktop_wmclass(wmClass); | ||||
|         if (app) | ||||
|             button.accessible_name = app.get_name(); | ||||
|         else | ||||
|             button.accessible_name = icon.title; | ||||
|  | ||||
|         button.connect('clicked', | ||||
|             function() { | ||||
|                 icon.click(Clutter.get_current_event()); | ||||
|             }); | ||||
|         button.connect('key-press-event', | ||||
|             function() { | ||||
|                 icon.click(Clutter.get_current_event()); | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|             }); | ||||
|         button.connect('key-focus-in', Lang.bind(this, | ||||
|             function() { | ||||
|                 this._concealHandle.show(); | ||||
|             })); | ||||
|  | ||||
|         this._iconBox.add_actor(button); | ||||
|         this._sync(); | ||||
|  | ||||
|         if (!this._concealHandle.visible) { | ||||
|             this._concealHandle.show(); | ||||
|             GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, TEMP_REVEAL_TIME, | ||||
|                 Lang.bind(this, function() { | ||||
|                     this._concealHandle.hide(); | ||||
|                     return GLib.SOURCE_REMOVE; | ||||
|                 })); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onTrayIconRemoved: function(tm, icon) { | ||||
| @@ -142,6 +195,45 @@ const LegacyTray = new Lang.Class({ | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     _syncBarrier: function() { | ||||
|         let rtl = (this._slider.get_text_direction() == Clutter.TextDirection.RTL); | ||||
|         let [x, y] = this._slider.get_transformed_position(); | ||||
|         let [w, h] = this._slider.get_transformed_size(); | ||||
|  | ||||
|         let x1 = Math.round(x); | ||||
|         if (rtl) | ||||
|             x1 += Math.round(w); | ||||
|  | ||||
|         let x2 = x1; | ||||
|         let y1 = Math.round(y); | ||||
|         let y2 = y1 + Math.round(h); | ||||
|  | ||||
|         if (this._horizontalBarrier && | ||||
|             this._horizontalBarrier.x1 == x1 && | ||||
|             this._horizontalBarrier.y1 == y1 && | ||||
|             this._horizontalBarrier.x2 == x2 && | ||||
|             this._horizontalBarrier.y2 == y2) | ||||
|             return; | ||||
|  | ||||
|         this._unsetBarrier(); | ||||
|  | ||||
|         let directions = (rtl ? Meta.BarrierDirection.NEGATIVE_X : Meta.BarrierDirection.POSITIVE_X); | ||||
|         this._horizontalBarrier = new Meta.Barrier({ display: global.display, | ||||
|                                                      x1: x1, x2: x2, | ||||
|                                                      y1: y1, y2: y2, | ||||
|                                                      directions: directions }); | ||||
|         this._pressureBarrier.addBarrier(this._horizontalBarrier); | ||||
|     }, | ||||
|  | ||||
|     _unsetBarrier: function() { | ||||
|         if (this._horizontalBarrier == null) | ||||
|             return; | ||||
|  | ||||
|         this._pressureBarrier.removeBarrier(this._horizontalBarrier); | ||||
|         this._horizontalBarrier.destroy(); | ||||
|         this._horizontalBarrier = null; | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         // FIXME: we no longer treat tray icons as notifications | ||||
|         let allowed = Main.sessionMode.hasNotifications; | ||||
| @@ -162,17 +254,20 @@ const LegacyTray = new Lang.Class({ | ||||
|             let [, boxWidth] = this._box.get_preferred_width(-1); | ||||
|             let [, handleWidth] = this._revealHandle.get_preferred_width(-1); | ||||
|  | ||||
|             targetSlide = handleWidth / boxWidth; | ||||
|             if (!this._revealHandle.hover) | ||||
|                 targetSlide *= CONCEALED_VISIBLE_FRACTION; | ||||
|             if (this._revealHandle.hover) | ||||
|                 targetSlide = handleWidth / boxWidth; | ||||
|             else | ||||
|                 targetSlide = CONCEALED_WIDTH / boxWidth; | ||||
|         } | ||||
|  | ||||
|         if (this.actor.visible) | ||||
|         if (this.actor.visible) { | ||||
|             Tweener.addTween(this._slideLayout, | ||||
|                              { slideX: targetSlide, | ||||
|                                time: REVEAL_ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad' }); | ||||
|         else | ||||
|         } else { | ||||
|             this._slideLayout.slideX = targetSlide; | ||||
|             this._unsetBarrier(); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -4,6 +4,7 @@ const Atspi = imports.gi.Atspi; | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GDesktopEnums = imports.gi.GDesktopEnums; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
| const Lang = imports.lang; | ||||
| @@ -22,6 +23,8 @@ const MOUSE_POLL_FREQUENCY = 50; | ||||
| const CROSSHAIRS_CLIP_SIZE = [100, 100]; | ||||
| const NO_CHANGE = 0.0; | ||||
|  | ||||
| const POINTER_REST_TIME = 1000; // milliseconds | ||||
|  | ||||
| // Settings | ||||
| const APPLICATIONS_SCHEMA       = 'org.gnome.desktop.a11y.applications'; | ||||
| const SHOW_KEY                  = 'screen-magnifier-enabled'; | ||||
| @@ -444,55 +447,6 @@ const Magnifier = new Lang.Class({ | ||||
|         this._appSettings = new Gio.Settings({ schema_id: APPLICATIONS_SCHEMA }); | ||||
|         this._settings = new Gio.Settings({ schema_id: MAGNIFIER_SCHEMA }); | ||||
|  | ||||
|         if (zoomRegion) { | ||||
|             // Mag factor is accurate to two decimal places. | ||||
|             let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2)); | ||||
|             if (aPref != 0.0) | ||||
|                 zoomRegion.setMagFactor(aPref, aPref); | ||||
|  | ||||
|             aPref = this._settings.get_enum(SCREEN_POSITION_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setScreenPosition(aPref); | ||||
|  | ||||
|             zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY)); | ||||
|             zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY)); | ||||
|  | ||||
|             aPref = this._settings.get_enum(MOUSE_TRACKING_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setMouseTrackingMode(aPref); | ||||
|  | ||||
|             aPref = this._settings.get_enum(FOCUS_TRACKING_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setFocusTrackingMode(aPref); | ||||
|  | ||||
|             aPref = this._settings.get_enum(CARET_TRACKING_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setCaretTrackingMode(aPref); | ||||
|  | ||||
|             aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setInvertLightness(aPref); | ||||
|  | ||||
|             aPref = this._settings.get_double(COLOR_SATURATION_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setColorSaturation(aPref); | ||||
|  | ||||
|             let bc = {}; | ||||
|             bc.r = this._settings.get_double(BRIGHT_RED_KEY); | ||||
|             bc.g = this._settings.get_double(BRIGHT_GREEN_KEY); | ||||
|             bc.b = this._settings.get_double(BRIGHT_BLUE_KEY); | ||||
|             zoomRegion.setBrightness(bc); | ||||
|  | ||||
|             bc.r = this._settings.get_double(CONTRAST_RED_KEY); | ||||
|             bc.g = this._settings.get_double(CONTRAST_GREEN_KEY); | ||||
|             bc.b = this._settings.get_double(CONTRAST_BLUE_KEY); | ||||
|             zoomRegion.setContrast(bc); | ||||
|         } | ||||
|  | ||||
|         let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY); | ||||
|         this.addCrosshairs(); | ||||
|         this.setCrosshairsVisible(showCrosshairs); | ||||
|  | ||||
|         this._appSettings.connect('changed::' + SHOW_KEY, | ||||
|                                   Lang.bind(this, function() { | ||||
|             this.setActive(this._appSettings.get_boolean(SHOW_KEY)); | ||||
| @@ -561,6 +515,56 @@ const Magnifier = new Lang.Class({ | ||||
|                                Lang.bind(this, function() { | ||||
|             this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY)); | ||||
|         })); | ||||
|  | ||||
|         if (zoomRegion) { | ||||
|             // Mag factor is accurate to two decimal places. | ||||
|             let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2)); | ||||
|             if (aPref != 0.0) | ||||
|                 zoomRegion.setMagFactor(aPref, aPref); | ||||
|  | ||||
|             aPref = this._settings.get_enum(SCREEN_POSITION_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setScreenPosition(aPref); | ||||
|  | ||||
|             zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY)); | ||||
|             zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY)); | ||||
|  | ||||
|             aPref = this._settings.get_enum(MOUSE_TRACKING_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setMouseTrackingMode(aPref); | ||||
|  | ||||
|             aPref = this._settings.get_enum(FOCUS_TRACKING_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setFocusTrackingMode(aPref); | ||||
|  | ||||
|             aPref = this._settings.get_enum(CARET_TRACKING_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setCaretTrackingMode(aPref); | ||||
|  | ||||
|             aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setInvertLightness(aPref); | ||||
|  | ||||
|             aPref = this._settings.get_double(COLOR_SATURATION_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setColorSaturation(aPref); | ||||
|  | ||||
|             let bc = {}; | ||||
|             bc.r = this._settings.get_double(BRIGHT_RED_KEY); | ||||
|             bc.g = this._settings.get_double(BRIGHT_GREEN_KEY); | ||||
|             bc.b = this._settings.get_double(BRIGHT_BLUE_KEY); | ||||
|             zoomRegion.setBrightness(bc); | ||||
|  | ||||
|             bc.r = this._settings.get_double(CONTRAST_RED_KEY); | ||||
|             bc.g = this._settings.get_double(CONTRAST_GREEN_KEY); | ||||
|             bc.b = this._settings.get_double(CONTRAST_BLUE_KEY); | ||||
|             zoomRegion.setContrast(bc); | ||||
|         } | ||||
|  | ||||
|         let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY); | ||||
|         this.addCrosshairs(); | ||||
|         this.setCrosshairsVisible(showCrosshairs); | ||||
|  | ||||
|         return this._appSettings.get_boolean(SHOW_KEY); | ||||
|    }, | ||||
|  | ||||
| @@ -708,6 +712,9 @@ const ZoomRegion = new Lang.Class({ | ||||
|         this._xCaret = 0; | ||||
|         this._yCaret = 0; | ||||
|  | ||||
|         this._pointerIdleMonitor = Meta.IdleMonitor.get_for_device(Meta.VIRTUAL_CORE_POINTER_ID); | ||||
|         this._scrollContentsTimerId = 0; | ||||
|  | ||||
|         Main.layoutManager.connect('monitors-changed', | ||||
|                                    Lang.bind(this, this._monitorsChanged)); | ||||
|         this._focusCaretTracker.connect('caret-moved', | ||||
| @@ -1067,6 +1074,26 @@ const ZoomRegion = new Lang.Class({ | ||||
|         return this._isMouseOverRegion(); | ||||
|     }, | ||||
|  | ||||
|     _clearScrollContentsTimer: function() { | ||||
|         if (this._scrollContentsTimerId != 0) { | ||||
|             Mainloop.source_remove(this._scrollContentsTimerId); | ||||
|             this._scrollContentsTimerId = 0; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _scrollContentsToDelayed: function(x, y) { | ||||
|         if (this._pointerIdleMonitor.get_idletime() >= POINTER_REST_TIME) { | ||||
|             this.scrollContentsTo(x, y); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._clearScrollContentsTimer(); | ||||
|         this._scrollContentsTimerId = Mainloop.timeout_add(POINTER_REST_TIME, Lang.bind(this, function() { | ||||
|             this._scrollContentsToDelayed(x, y); | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * scrollContentsTo: | ||||
|      * Shift the contents of the magnified view such it is centered on the given | ||||
| @@ -1075,6 +1102,8 @@ const ZoomRegion = new Lang.Class({ | ||||
|      * @y:      The y-coord of the point to center on. | ||||
|      */ | ||||
|     scrollContentsTo: function(x, y) { | ||||
|         this._clearScrollContentsTimer(); | ||||
|  | ||||
|         this._followingCursor = false; | ||||
|         this._changeROI({ xCenter: x, | ||||
|                           yCenter: y }); | ||||
| @@ -1380,7 +1409,7 @@ const ZoomRegion = new Lang.Class({ | ||||
|         else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.CENTERED) | ||||
|             [xCaret, yCaret] = this._centerFromPointCentered(xCaret, yCaret); | ||||
|  | ||||
|         this.scrollContentsTo(xCaret, yCaret); | ||||
|         this._scrollContentsToDelayed(xCaret, yCaret); | ||||
|     }, | ||||
|  | ||||
|     _centerFromFocusPosition: function() { | ||||
| @@ -1394,7 +1423,7 @@ const ZoomRegion = new Lang.Class({ | ||||
|         else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.CENTERED) | ||||
|             [xFocus, yFocus] = this._centerFromPointCentered(xFocus, yFocus); | ||||
|  | ||||
|         this.scrollContentsTo(xFocus, yFocus); | ||||
|         this._scrollContentsToDelayed(xFocus, yFocus); | ||||
|     }, | ||||
|  | ||||
|     _centerFromPointPush: function(xPoint, yPoint) { | ||||
|   | ||||
| @@ -529,6 +529,7 @@ function activateWindow(window, time, workspaceNum) { | ||||
|     } | ||||
|  | ||||
|     overview.hide(); | ||||
|     panel.closeCalendar(); | ||||
| } | ||||
|  | ||||
| // TODO - replace this timeout with some system to guess when the user might | ||||
|   | ||||
| @@ -69,26 +69,6 @@ const Urgency = { | ||||
|     CRITICAL: 3 | ||||
| }; | ||||
|  | ||||
| function _fixMarkup(text, allowMarkup) { | ||||
|     if (allowMarkup) { | ||||
|         // Support &, ", ', < and >, escape all other | ||||
|         // occurrences of '&'. | ||||
|         let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&'); | ||||
|  | ||||
|         // Support <b>, <i>, and <u>, escape anything else | ||||
|         // so it displays as raw markup. | ||||
|         _text = _text.replace(/<(?!\/?[biu]>)/g, '<'); | ||||
|  | ||||
|         try { | ||||
|             Pango.parse_markup(_text, -1, ''); | ||||
|             return _text; | ||||
|         } catch (e) {} | ||||
|     } | ||||
|  | ||||
|     // !allowMarkup, or invalid markup | ||||
|     return GLib.markup_escape_text(text, -1); | ||||
| } | ||||
|  | ||||
| const FocusGrabber = new Lang.Class({ | ||||
|     Name: 'FocusGrabber', | ||||
|  | ||||
| @@ -147,130 +127,6 @@ const FocusGrabber = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const URLHighlighter = new Lang.Class({ | ||||
|     Name: 'URLHighlighter', | ||||
|  | ||||
|     _init: function(text, lineWrap, allowMarkup) { | ||||
|         if (!text) | ||||
|             text = ''; | ||||
|         this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter' }); | ||||
|         this._linkColor = '#ccccff'; | ||||
|         this.actor.connect('style-changed', Lang.bind(this, function() { | ||||
|             let [hasColor, color] = this.actor.get_theme_node().lookup_color('link-color', false); | ||||
|             if (hasColor) { | ||||
|                 let linkColor = color.to_string().substr(0, 7); | ||||
|                 if (linkColor != this._linkColor) { | ||||
|                     this._linkColor = linkColor; | ||||
|                     this._highlightUrls(); | ||||
|                 } | ||||
|             } | ||||
|         })); | ||||
|         if (lineWrap) { | ||||
|             this.actor.clutter_text.line_wrap = true; | ||||
|             this.actor.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; | ||||
|             this.actor.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         } | ||||
|  | ||||
|         this.setMarkup(text, allowMarkup); | ||||
|         this.actor.connect('button-press-event', Lang.bind(this, function(actor, event) { | ||||
|             // Don't try to URL highlight when invisible. | ||||
|             // The MessageTray doesn't actually hide us, so | ||||
|             // we need to check for paint opacities as well. | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             // Keep Notification.actor from seeing this and taking | ||||
|             // a pointer grab, which would block our button-release-event | ||||
|             // handler, if an URL is clicked | ||||
|             return this._findUrlAtPos(event) != -1; | ||||
|         })); | ||||
|         this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) { | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             let urlId = this._findUrlAtPos(event); | ||||
|             if (urlId != -1) { | ||||
|                 let url = this._urls[urlId].url; | ||||
|                 if (url.indexOf(':') == -1) | ||||
|                     url = 'http://' + url; | ||||
|  | ||||
|                 Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context(0, -1)); | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|         this.actor.connect('motion-event', Lang.bind(this, function(actor, event) { | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             let urlId = this._findUrlAtPos(event); | ||||
|             if (urlId != -1 && !this._cursorChanged) { | ||||
|                 global.screen.set_cursor(Meta.Cursor.POINTING_HAND); | ||||
|                 this._cursorChanged = true; | ||||
|             } else if (urlId == -1) { | ||||
|                 global.screen.set_cursor(Meta.Cursor.DEFAULT); | ||||
|                 this._cursorChanged = false; | ||||
|             } | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|         this.actor.connect('leave-event', Lang.bind(this, function() { | ||||
|             if (!this.actor.visible || this.actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             if (this._cursorChanged) { | ||||
|                 this._cursorChanged = false; | ||||
|                 global.screen.set_cursor(Meta.Cursor.DEFAULT); | ||||
|             } | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     setMarkup: function(text, allowMarkup) { | ||||
|         text = text ? _fixMarkup(text, allowMarkup) : ''; | ||||
|         this._text = text; | ||||
|  | ||||
|         this.actor.clutter_text.set_markup(text); | ||||
|         /* clutter_text.text contain text without markup */ | ||||
|         this._urls = Util.findUrls(this.actor.clutter_text.text); | ||||
|         this._highlightUrls(); | ||||
|     }, | ||||
|  | ||||
|     _highlightUrls: function() { | ||||
|         // text here contain markup | ||||
|         let urls = Util.findUrls(this._text); | ||||
|         let markup = ''; | ||||
|         let pos = 0; | ||||
|         for (let i = 0; i < urls.length; i++) { | ||||
|             let url = urls[i]; | ||||
|             let str = this._text.substr(pos, url.pos - pos); | ||||
|             markup += str + '<span foreground="' + this._linkColor + '"><u>' + url.url + '</u></span>'; | ||||
|             pos = url.pos + url.url.length; | ||||
|         } | ||||
|         markup += this._text.substr(pos); | ||||
|         this.actor.clutter_text.set_markup(markup); | ||||
|     }, | ||||
|  | ||||
|     _findUrlAtPos: function(event) { | ||||
|         let success; | ||||
|         let [x, y] = event.get_coords(); | ||||
|         [success, x, y] = this.actor.transform_stage_point(x, y); | ||||
|         let find_pos = -1; | ||||
|         for (let i = 0; i < this.actor.clutter_text.text.length; i++) { | ||||
|             let [success, px, py, line_height] = this.actor.clutter_text.position_to_coords(i); | ||||
|             if (py > y || py + line_height < y || x < px) | ||||
|                 continue; | ||||
|             find_pos = i; | ||||
|         } | ||||
|         if (find_pos != -1) { | ||||
|             for (let i = 0; i < this._urls.length; i++) | ||||
|             if (find_pos >= this._urls[i].pos && | ||||
|                 this._urls[i].pos + this._urls[i].url.length > find_pos) | ||||
|                 return i; | ||||
|         } | ||||
|         return -1; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| // NotificationPolicy: | ||||
| // An object that holds all bits of configurable policy related to a notification | ||||
| // source, such as whether to play sound or honour the critical bit. | ||||
| @@ -382,6 +238,8 @@ const NotificationApplicationPolicy = new Lang.Class({ | ||||
|  | ||||
|     _changed: function(settings, key) { | ||||
|         this.emit('policy-changed', key); | ||||
|         if (key == 'enable') | ||||
|             this.emit('enable-changed'); | ||||
|     }, | ||||
|  | ||||
|     _canonicalizeId: function(id) { | ||||
| @@ -473,10 +331,6 @@ const NotificationApplicationPolicy = new Lang.Class({ | ||||
| const Notification = new Lang.Class({ | ||||
|     Name: 'Notification', | ||||
|  | ||||
|     ICON_SIZE: 24, | ||||
|  | ||||
|     IMAGE_SIZE: 125, | ||||
|  | ||||
|     _init: function(source, title, banner, params) { | ||||
|         this.source = source; | ||||
|         this.title = title; | ||||
| @@ -485,63 +339,14 @@ const Notification = new Lang.Class({ | ||||
|         // 'transient' is a reserved keyword in JS, so we have to use an alternate variable name | ||||
|         this.isTransient = false; | ||||
|         this.forFeedback = false; | ||||
|         this.expanded = false; | ||||
|         this.focused = false; | ||||
|         this._acknowledged = false; | ||||
|         this._destroyed = false; | ||||
|         this._customContent = false; | ||||
|         this.bannerBodyText = null; | ||||
|         this.bannerBodyMarkup = false; | ||||
|         this._bannerBodyAdded = false; | ||||
|         this._titleFitsInBannerMode = true; | ||||
|         this._spacing = 0; | ||||
|         this._scrollPolicy = Gtk.PolicyType.AUTOMATIC; | ||||
|         this._soundName = null; | ||||
|         this._soundFile = null; | ||||
|         this._soundPlayed = false; | ||||
|         this.actions = []; | ||||
|  | ||||
|         this.actor = new St.Button({ accessible_role: Atk.Role.NOTIFICATION }); | ||||
|         this.actor.add_style_class_name('notification-unexpanded'); | ||||
|         this.actor._delegate = this; | ||||
|         this.actor.connect('clicked', Lang.bind(this, this.activate)); | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|  | ||||
|         this._table = new St.Table({ style_class: 'notification', | ||||
|                                      reactive: true }); | ||||
|         this._table.connect('style-changed', Lang.bind(this, this._styleChanged)); | ||||
|         this.actor.set_child(this._table); | ||||
|  | ||||
|         // The first line should have the title, followed by the | ||||
|         // banner text, but ellipsized if they won't both fit. We can't | ||||
|         // make St.Table or St.BoxLayout do this the way we want (don't | ||||
|         // show banner at all if title needs to be ellipsized), so we | ||||
|         // use Shell.GenericContainer. | ||||
|         this._bannerBox = new Shell.GenericContainer(); | ||||
|         this._bannerBox.connect('get-preferred-width', Lang.bind(this, this._bannerBoxGetPreferredWidth)); | ||||
|         this._bannerBox.connect('get-preferred-height', Lang.bind(this, this._bannerBoxGetPreferredHeight)); | ||||
|         this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate)); | ||||
|         this._table.add(this._bannerBox, { row: 0, | ||||
|                                            col: 1, | ||||
|                                            col_span: 2, | ||||
|                                            x_expand: false, | ||||
|                                            y_expand: false, | ||||
|                                            y_fill: false }); | ||||
|  | ||||
|         // This is an empty cell that overlaps with this._bannerBox cell to ensure | ||||
|         // that this._bannerBox cell expands horizontally, while not forcing the | ||||
|         // this._imageBin that is also in col: 2 to expand horizontally. | ||||
|         this._table.add(new St.Bin(), { row: 0, | ||||
|                                         col: 2, | ||||
|                                         y_expand: false, | ||||
|                                         y_fill: false }); | ||||
|  | ||||
|         this._titleLabel = new St.Label(); | ||||
|         this._bannerBox.add_actor(this._titleLabel); | ||||
|         this._bannerUrlHighlighter = new URLHighlighter(); | ||||
|         this._bannerLabel = this._bannerUrlHighlighter.actor; | ||||
|         this._bannerBox.add_actor(this._bannerLabel); | ||||
|  | ||||
|         // If called with only one argument we assume the caller | ||||
|         // will call .update() later on. This is the case of | ||||
|         // NotificationDaemon, which wants to use the same code | ||||
| @@ -559,107 +364,25 @@ const Notification = new Lang.Class({ | ||||
|     // the title/banner. If @params.clear is %true, it will also | ||||
|     // remove any additional actors/action buttons previously added. | ||||
|     update: function(title, banner, params) { | ||||
|         params = Params.parse(params, { customContent: false, | ||||
|                                         gicon: null, | ||||
|         params = Params.parse(params, { gicon: null, | ||||
|                                         secondaryGIcon: null, | ||||
|                                         bannerMarkup: false, | ||||
|                                         clear: false, | ||||
|                                         soundName: null, | ||||
|                                         soundFile: null }); | ||||
|  | ||||
|         this._customContent = params.customContent; | ||||
|  | ||||
|         let oldFocus = global.stage.key_focus; | ||||
|  | ||||
|         if (this._icon && (params.gicon || params.clear)) { | ||||
|             this._icon.destroy(); | ||||
|             this._icon = null; | ||||
|         } | ||||
|  | ||||
|         if (this._secondaryIcon && (params.secondaryGIcon || params.clear)) { | ||||
|             this._secondaryIcon.destroy(); | ||||
|             this._secondaryIcon = null; | ||||
|         } | ||||
|  | ||||
|         // We always clear the content area if we don't have custom | ||||
|         // content because it might contain the @banner that didn't | ||||
|         // fit in the banner mode. | ||||
|         if (this._scrollArea && (!this._customContent || params.clear)) { | ||||
|             if (oldFocus && this._scrollArea.contains(oldFocus)) | ||||
|                 this.actor.grab_key_focus(); | ||||
|  | ||||
|             this._scrollArea.destroy(); | ||||
|             this._scrollArea = null; | ||||
|             this._contentArea = null; | ||||
|         } | ||||
|         if (this._actionArea && params.clear) { | ||||
|             if (oldFocus && this._actionArea.contains(oldFocus)) | ||||
|                 this.actor.grab_key_focus(); | ||||
|  | ||||
|             this._actionArea.destroy(); | ||||
|             this._actionArea = null; | ||||
|             this._buttonBox = null; | ||||
|             this.actions = []; | ||||
|         } | ||||
|         if (!this._scrollArea && !this._actionArea) | ||||
|             this._table.remove_style_class_name('multi-line-notification'); | ||||
|  | ||||
|         if (params.gicon) { | ||||
|             this._icon = new St.Icon({ gicon: params.gicon, | ||||
|                                        icon_size: this.ICON_SIZE }); | ||||
|         } else { | ||||
|             this._icon = this.source.createIcon(this.ICON_SIZE); | ||||
|         } | ||||
|  | ||||
|         if (this._icon) { | ||||
|             this._table.add(this._icon, { row: 0, | ||||
|                                           col: 0, | ||||
|                                           x_expand: false, | ||||
|                                           y_expand: false, | ||||
|                                           y_fill: false, | ||||
|                                           y_align: St.Align.START }); | ||||
|         } | ||||
|  | ||||
|         if (params.secondaryGIcon) { | ||||
|             this._secondaryIcon = new St.Icon({ gicon: params.secondaryGIcon, | ||||
|                                                 style_class: 'secondary-icon' }); | ||||
|             this._bannerBox.add_actor(this._secondaryIcon); | ||||
|         } | ||||
|  | ||||
|         this.title = title; | ||||
|         title = title ? _fixMarkup(title.replace(/\n/g, ' '), false) : ''; | ||||
|         this._titleLabel.clutter_text.set_markup('<b>' + title + '</b>'); | ||||
|  | ||||
|         let titleDirection; | ||||
|         if (Pango.find_base_dir(title, -1) == Pango.Direction.RTL) | ||||
|             titleDirection = Clutter.TextDirection.RTL; | ||||
|         else | ||||
|             titleDirection = Clutter.TextDirection.LTR; | ||||
|  | ||||
|         // Let the title's text direction control the overall direction | ||||
|         // of the notification - in case where different scripts are used | ||||
|         // in the notification, this is the right thing for the icon, and | ||||
|         // arguably for action buttons as well. Labels other than the title | ||||
|         // will be allocated at the available width, so that their alignment | ||||
|         // is done correctly automatically. | ||||
|         this._table.set_text_direction(titleDirection); | ||||
|  | ||||
|         // Unless the notification has custom content, we save this.bannerBodyText | ||||
|         // to add it to the content of the notification if the notification is | ||||
|         // expandable due to other elements in its content area or due to the banner | ||||
|         // not fitting fully in the single-line mode. | ||||
|         this.bannerBodyText = this._customContent ? null : banner; | ||||
|         this.bannerBodyText = banner; | ||||
|         this.bannerBodyMarkup = params.bannerMarkup; | ||||
|         this._bannerBodyAdded = false; | ||||
|  | ||||
|         banner = banner ? banner.replace(/\n/g, '  ') : ''; | ||||
|         if (params.gicon || params.clear) | ||||
|             this.gicon = params.gicon; | ||||
|  | ||||
|         this._bannerUrlHighlighter.setMarkup(banner, params.bannerMarkup); | ||||
|         this._bannerLabel.queue_relayout(); | ||||
|         if (params.secondaryGIcon || params.clear) | ||||
|             this.secondaryGIcon = params.secondaryGIcon; | ||||
|  | ||||
|         // Add the bannerBody now if we know for sure we'll need it | ||||
|         if (this.bannerBodyText && this.bannerBodyText.indexOf('\n') > -1) | ||||
|             this._addBannerBody(); | ||||
|         if (params.clear) | ||||
|             this.actions = []; | ||||
|  | ||||
|         if (this._soundName != params.soundName || | ||||
|             this._soundFile != params.soundFile) { | ||||
| @@ -668,168 +391,14 @@ const Notification = new Lang.Class({ | ||||
|             this._soundPlayed = false; | ||||
|         } | ||||
|  | ||||
|         this.updated(); | ||||
|         this.emit('updated', params.clear); | ||||
|     }, | ||||
|  | ||||
|     setIconVisible: function(visible) { | ||||
|         this._icon.visible = visible; | ||||
|     }, | ||||
|  | ||||
|     enableScrolling: function(enableScrolling) { | ||||
|         this._scrollPolicy = enableScrolling ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER; | ||||
|         if (this._scrollArea) { | ||||
|             this._scrollArea.vscrollbar_policy = this._scrollPolicy; | ||||
|             this._scrollArea.enable_mouse_scrolling = enableScrolling; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _createScrollArea: function() { | ||||
|         this._table.add_style_class_name('multi-line-notification'); | ||||
|         this._scrollArea = new St.ScrollView({ style_class: 'notification-scrollview', | ||||
|                                                vscrollbar_policy: this._scrollPolicy, | ||||
|                                                hscrollbar_policy: Gtk.PolicyType.NEVER, | ||||
|                                                visible: this.expanded }); | ||||
|         this._table.add(this._scrollArea, { row: 1, | ||||
|                                             col: 2 }); | ||||
|         this._updateLastColumnSettings(); | ||||
|         this._contentArea = new St.BoxLayout({ style_class: 'notification-body', | ||||
|                                                vertical: true }); | ||||
|         this._scrollArea.add_actor(this._contentArea); | ||||
|         // If we know the notification will be expandable, we need to add | ||||
|         // the banner text to the body as the first element. | ||||
|         this._addBannerBody(); | ||||
|     }, | ||||
|  | ||||
|     // addActor: | ||||
|     // @actor: actor to add to the body of the notification | ||||
|     // | ||||
|     // Appends @actor to the notification's body | ||||
|     addActor: function(actor, style) { | ||||
|         if (!this._scrollArea) { | ||||
|             this._createScrollArea(); | ||||
|         } | ||||
|  | ||||
|         this._contentArea.add(actor, style ? style : {}); | ||||
|         this.updated(); | ||||
|     }, | ||||
|  | ||||
|     // addBody: | ||||
|     // @text: the text | ||||
|     // @markup: %true if @text contains pango markup | ||||
|     // @style: style to use when adding the actor containing the text | ||||
|     // | ||||
|     // Adds a multi-line label containing @text to the notification. | ||||
|     // | ||||
|     // Return value: the newly-added label | ||||
|     addBody: function(text, markup, style) { | ||||
|         let label = new URLHighlighter(text, true, markup); | ||||
|  | ||||
|         this.addActor(label.actor, style); | ||||
|         return label.actor; | ||||
|     }, | ||||
|  | ||||
|     _addBannerBody: function() { | ||||
|         if (this.bannerBodyText && !this._bannerBodyAdded) { | ||||
|             this._bannerBodyAdded = true; | ||||
|             this.addBody(this.bannerBodyText, this.bannerBodyMarkup); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     // scrollTo: | ||||
|     // @side: St.Side.TOP or St.Side.BOTTOM | ||||
|     // | ||||
|     // Scrolls the content area (if scrollable) to the indicated edge | ||||
|     scrollTo: function(side) { | ||||
|         let adjustment = this._scrollArea.vscroll.adjustment; | ||||
|         if (side == St.Side.TOP) | ||||
|             adjustment.value = adjustment.lower; | ||||
|         else if (side == St.Side.BOTTOM) | ||||
|             adjustment.value = adjustment.upper; | ||||
|     }, | ||||
|  | ||||
|     // setActionArea: | ||||
|     // @actor: the actor | ||||
|     // @props: (option) St.Table child properties | ||||
|     // | ||||
|     // Puts @actor into the action area of the notification, replacing | ||||
|     // the previous contents | ||||
|     setActionArea: function(actor, props) { | ||||
|         if (this._actionArea) { | ||||
|             this._actionArea.destroy(); | ||||
|             this._actionArea = null; | ||||
|             if (this._buttonBox) | ||||
|                 this._buttonBox = null; | ||||
|         } else { | ||||
|             this._addBannerBody(); | ||||
|         } | ||||
|         this._actionArea = actor; | ||||
|         this._actionArea.visible = this.expanded; | ||||
|  | ||||
|         if (!props) | ||||
|             props = {}; | ||||
|         props.row = 2; | ||||
|         props.col = 2; | ||||
|  | ||||
|         this._table.add_style_class_name('multi-line-notification'); | ||||
|         this._table.add(this._actionArea, props); | ||||
|         this._updateLastColumnSettings(); | ||||
|         this.updated(); | ||||
|     }, | ||||
|  | ||||
|     _updateLastColumnSettings: function() { | ||||
|         if (this._scrollArea) | ||||
|             this._table.child_set(this._scrollArea, { col: 1, | ||||
|                                                       col_span: 2 }); | ||||
|         if (this._actionArea) | ||||
|             this._table.child_set(this._actionArea, { col: 1, | ||||
|                                                       col_span: 2 }); | ||||
|     }, | ||||
|  | ||||
|     addButton: function(button, callback) { | ||||
|         if (!this._buttonBox) { | ||||
|             let box = new St.BoxLayout({ style_class: 'notification-actions' }); | ||||
|             this.setActionArea(box, { x_expand: false, | ||||
|                                       y_expand: false, | ||||
|                                       x_fill: false, | ||||
|                                       y_fill: false, | ||||
|                                       x_align: St.Align.END }); | ||||
|             this._buttonBox = box; | ||||
|             global.focus_manager.add_group(this._buttonBox); | ||||
|         } | ||||
|  | ||||
|         this._buttonBox.add(button); | ||||
|         button.connect('clicked', Lang.bind(this, function() { | ||||
|             callback(); | ||||
|  | ||||
|             if (!this.resident) { | ||||
|                 // We don't hide a resident notification when the user invokes one of its actions, | ||||
|                 // because it is common for such notifications to update themselves with new | ||||
|                 // information based on the action. We'd like to display the updated information | ||||
|                 // in place, rather than pop-up a new notification. | ||||
|                 this.emit('done-displaying'); | ||||
|                 this.destroy(); | ||||
|             } | ||||
|         })); | ||||
|  | ||||
|         this.updated(); | ||||
|         return button; | ||||
|     }, | ||||
|  | ||||
|     // addAction: | ||||
|     // @label: the label for the action's button | ||||
|     // @callback: the callback for the action | ||||
|     // | ||||
|     // Adds a button with the given @label to the notification. All | ||||
|     // action buttons will appear in a single row at the bottom of | ||||
|     // the notification. | ||||
|     addAction: function(label, callback) { | ||||
|         this.actions.push({ label: label, callback: callback }); | ||||
|         let button = new St.Button({ style_class: 'notification-button', | ||||
|                                      label: label, | ||||
|                                      can_focus: true }); | ||||
|  | ||||
|         return this.addButton(button, callback); | ||||
|     }, | ||||
|  | ||||
|     get acknowledged() { | ||||
| @@ -859,132 +428,6 @@ const Notification = new Lang.Class({ | ||||
|         this.forFeedback = forFeedback; | ||||
|     }, | ||||
|  | ||||
|     _styleChanged: function() { | ||||
|         this._spacing = this._table.get_theme_node().get_length('spacing-columns'); | ||||
|     }, | ||||
|  | ||||
|     _bannerBoxGetPreferredWidth: function(actor, forHeight, alloc) { | ||||
|         let [titleMin, titleNat] = this._titleLabel.get_preferred_width(forHeight); | ||||
|         let [bannerMin, bannerNat] = this._bannerLabel.get_preferred_width(forHeight); | ||||
|  | ||||
|         if (this._secondaryIcon) { | ||||
|             let [secondaryIconMin, secondaryIconNat] = this._secondaryIcon.get_preferred_width(forHeight); | ||||
|  | ||||
|             alloc.min_size = secondaryIconMin + this._spacing + titleMin; | ||||
|             alloc.natural_size = secondaryIconNat + this._spacing + titleNat + this._spacing + bannerNat; | ||||
|         } else { | ||||
|             alloc.min_size = titleMin; | ||||
|             alloc.natural_size = titleNat + this._spacing + bannerNat; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _bannerBoxGetPreferredHeight: function(actor, forWidth, alloc) { | ||||
|         [alloc.min_size, alloc.natural_size] = | ||||
|             this._titleLabel.get_preferred_height(forWidth); | ||||
|     }, | ||||
|  | ||||
|     _bannerBoxAllocate: function(actor, box, flags) { | ||||
|         let availWidth = box.x2 - box.x1; | ||||
|  | ||||
|         let [titleMinW, titleNatW] = this._titleLabel.get_preferred_width(-1); | ||||
|         let [titleMinH, titleNatH] = this._titleLabel.get_preferred_height(availWidth); | ||||
|         let [bannerMinW, bannerNatW] = this._bannerLabel.get_preferred_width(availWidth); | ||||
|  | ||||
|         let rtl = (this._table.text_direction == Clutter.TextDirection.RTL); | ||||
|         let x = rtl ? availWidth : 0; | ||||
|  | ||||
|         if (this._secondaryIcon) { | ||||
|             let [iconMinW, iconNatW] = this._secondaryIcon.get_preferred_width(-1); | ||||
|             let [iconMinH, iconNatH] = this._secondaryIcon.get_preferred_height(availWidth); | ||||
|  | ||||
|             let secondaryIconBox = new Clutter.ActorBox(); | ||||
|             let secondaryIconBoxW = Math.min(iconNatW, availWidth); | ||||
|  | ||||
|             // allocate secondary icon box | ||||
|             if (rtl) { | ||||
|                 secondaryIconBox.x1 = x - secondaryIconBoxW; | ||||
|                 secondaryIconBox.x2 = x; | ||||
|                 x = x - (secondaryIconBoxW + this._spacing); | ||||
|             } else { | ||||
|                 secondaryIconBox.x1 = x; | ||||
|                 secondaryIconBox.x2 = x + secondaryIconBoxW; | ||||
|                 x = x + secondaryIconBoxW + this._spacing; | ||||
|             } | ||||
|             secondaryIconBox.y1 = 0; | ||||
|             // Using titleNatH ensures that the secondary icon is centered vertically | ||||
|             secondaryIconBox.y2 = titleNatH; | ||||
|  | ||||
|             availWidth = availWidth - (secondaryIconBoxW + this._spacing); | ||||
|             this._secondaryIcon.allocate(secondaryIconBox, flags); | ||||
|         } | ||||
|  | ||||
|         let titleBox = new Clutter.ActorBox(); | ||||
|         let titleBoxW = Math.min(titleNatW, availWidth); | ||||
|         if (rtl) { | ||||
|             titleBox.x1 = availWidth - titleBoxW; | ||||
|             titleBox.x2 = availWidth; | ||||
|         } else { | ||||
|             titleBox.x1 = x; | ||||
|             titleBox.x2 = titleBox.x1 + titleBoxW; | ||||
|         } | ||||
|         titleBox.y1 = 0; | ||||
|         titleBox.y2 = titleNatH; | ||||
|         this._titleLabel.allocate(titleBox, flags); | ||||
|         this._titleFitsInBannerMode = (titleNatW <= availWidth); | ||||
|  | ||||
|         let bannerFits = true; | ||||
|         if (titleBoxW + this._spacing > availWidth) { | ||||
|             this._bannerLabel.opacity = 0; | ||||
|             bannerFits = false; | ||||
|         } else { | ||||
|             let bannerBox = new Clutter.ActorBox(); | ||||
|  | ||||
|             if (rtl) { | ||||
|                 bannerBox.x1 = 0; | ||||
|                 bannerBox.x2 = titleBox.x1 - this._spacing; | ||||
|  | ||||
|                 bannerFits = (bannerBox.x2 - bannerNatW >= 0); | ||||
|             } else { | ||||
|                 bannerBox.x1 = titleBox.x2 + this._spacing; | ||||
|                 bannerBox.x2 = availWidth; | ||||
|  | ||||
|                 bannerFits = (bannerBox.x1 + bannerNatW <= availWidth); | ||||
|             } | ||||
|             bannerBox.y1 = 0; | ||||
|             bannerBox.y2 = titleNatH; | ||||
|             this._bannerLabel.allocate(bannerBox, flags); | ||||
|  | ||||
|             // Make _bannerLabel visible if the entire notification | ||||
|             // fits on one line, or if the notification is currently | ||||
|             // unexpanded and only showing one line anyway. | ||||
|             if (!this.expanded || (bannerFits && this._table.row_count == 1)) | ||||
|                 this._bannerLabel.opacity = 255; | ||||
|         } | ||||
|  | ||||
|         // If the banner doesn't fully fit in the banner box, we possibly need to add the | ||||
|         // banner to the body. We can't do that from here though since that will force a | ||||
|         // relayout, so we add it to the main loop. | ||||
|         if (!bannerFits && this._canExpandContent()) | ||||
|             Meta.later_add(Meta.LaterType.BEFORE_REDRAW, | ||||
|                            Lang.bind(this, | ||||
|                                      function() { | ||||
|                                          if (this._destroyed) | ||||
|                                              return false; | ||||
|  | ||||
|                                         if (this._canExpandContent()) { | ||||
|                                             this._addBannerBody(); | ||||
|                                             this._table.add_style_class_name('multi-line-notification'); | ||||
|                                             this.updated(); | ||||
|                                         } | ||||
|                                         return false; | ||||
|                                      })); | ||||
|     }, | ||||
|  | ||||
|     _canExpandContent: function() { | ||||
|         return (this.bannerBodyText && !this._bannerBodyAdded) || | ||||
|                (!this._titleFitsInBannerMode && !this._table.has_style_class_name('multi-line-notification')); | ||||
|     }, | ||||
|  | ||||
|     playSound: function() { | ||||
|         if (this._soundPlayed) | ||||
|             return; | ||||
| @@ -1017,71 +460,6 @@ const Notification = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     updated: function() { | ||||
|         if (this.expanded) | ||||
|             this.expand(false); | ||||
|     }, | ||||
|  | ||||
|     expand: function(animate) { | ||||
|         this.expanded = true; | ||||
|         this.actor.remove_style_class_name('notification-unexpanded'); | ||||
|  | ||||
|         // Show additional content that we keep hidden in banner mode | ||||
|         if (this._actionArea) | ||||
|             this._actionArea.show(); | ||||
|         if (this._scrollArea) | ||||
|             this._scrollArea.show(); | ||||
|  | ||||
|         // The banner is never shown when the title did not fit, so this | ||||
|         // can be an if-else statement. | ||||
|         if (!this._titleFitsInBannerMode) { | ||||
|             // Remove ellipsization from the title label and make it wrap so that | ||||
|             // we show the full title when the notification is expanded. | ||||
|             this._titleLabel.clutter_text.line_wrap = true; | ||||
|             this._titleLabel.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; | ||||
|             this._titleLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         } else if (this._table.row_count > 1 && this._bannerLabel.opacity != 0) { | ||||
|             // We always hide the banner if the notification has additional content. | ||||
|             // | ||||
|             // We don't need to wrap the banner that doesn't fit the way we wrap the | ||||
|             // title that doesn't fit because we won't have a notification with | ||||
|             // row_count=1 that has a banner that doesn't fully fit. We'll either add | ||||
|             // that banner to the content of the notification in _bannerBoxAllocate() | ||||
|             // or the notification will have custom content. | ||||
|             if (animate) | ||||
|                 Tweener.addTween(this._bannerLabel, | ||||
|                                  { opacity: 0, | ||||
|                                    time: ANIMATION_TIME, | ||||
|                                    transition: 'easeOutQuad' }); | ||||
|             else | ||||
|                 this._bannerLabel.opacity = 0; | ||||
|         } | ||||
|         this.emit('expanded'); | ||||
|     }, | ||||
|  | ||||
|     collapseCompleted: function() { | ||||
|         if (this._destroyed) | ||||
|             return; | ||||
|         this.expanded = false; | ||||
|  | ||||
|         // Hide additional content that we keep hidden in banner mode | ||||
|         if (this._actionArea) | ||||
|             this._actionArea.hide(); | ||||
|         if (this._scrollArea) | ||||
|             this._scrollArea.hide(); | ||||
|  | ||||
|         // Make sure we don't line wrap the title, and ellipsize it instead. | ||||
|         this._titleLabel.clutter_text.line_wrap = false; | ||||
|         this._titleLabel.clutter_text.ellipsize = Pango.EllipsizeMode.END; | ||||
|  | ||||
|         // Restore banner opacity in case the notification is shown in the | ||||
|         // banner mode again on update. | ||||
|         this._bannerLabel.opacity = 255; | ||||
|  | ||||
|         // Restore height requisition | ||||
|         this.actor.add_style_class_name('notification-unexpanded'); | ||||
|     }, | ||||
|  | ||||
|     // Allow customizing the banner UI: | ||||
|     // the default implementation defers the creation to | ||||
|     // the source (which will create a NotificationBanner), | ||||
| @@ -1093,27 +471,14 @@ const Notification = new Lang.Class({ | ||||
|  | ||||
|     activate: function() { | ||||
|         this.emit('activated'); | ||||
|         // We hide all types of notifications once the user clicks on them because the common | ||||
|         // outcome of clicking should be the relevant window being brought forward and the user's | ||||
|         // attention switching to the window. | ||||
|         this.emit('done-displaying'); | ||||
|         if (!this.resident) | ||||
|             this.destroy(); | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|         if (this._destroyed) | ||||
|             return; | ||||
|         this._destroyed = true; | ||||
|         if (!this._destroyedReason) | ||||
|             this._destroyedReason = NotificationDestroyedReason.DISMISSED; | ||||
|         this.emit('destroy', this._destroyedReason); | ||||
|     }, | ||||
|  | ||||
|     destroy: function(reason) { | ||||
|         this._destroyedReason = reason; | ||||
|         this.actor.destroy(); | ||||
|         this.actor._delegate = null; | ||||
|         if (!reason) | ||||
|             reason = NotificationDestroyedReason.DISMISSED; | ||||
|         this.emit('destroy', reason); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(Notification.prototype); | ||||
| @@ -1125,8 +490,8 @@ const NotificationBanner = new Lang.Class({ | ||||
|     _init: function(notification) { | ||||
|         this.parent(notification); | ||||
|  | ||||
|         this.actor.can_focus = false; | ||||
|         this.actor.add_style_class_name('notification-banner'); | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroyed)); | ||||
|  | ||||
|         this._buttonBox = null; | ||||
|  | ||||
| @@ -1143,7 +508,8 @@ const NotificationBanner = new Lang.Class({ | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     _onDestroyed: function() { | ||||
|     _onDestroy: function() { | ||||
|         this.parent(); | ||||
|         this.notification.disconnect(this._activatedId); | ||||
|     }, | ||||
|  | ||||
| @@ -1370,10 +736,6 @@ const Source = new Lang.Class({ | ||||
|         return this.count > 1; | ||||
|     }, | ||||
|  | ||||
|     get isClearable() { | ||||
|         return !this.isChat && !this.resident; | ||||
|     }, | ||||
|  | ||||
|     countUpdated: function() { | ||||
|         this.emit('count-updated'); | ||||
|     }, | ||||
| @@ -1546,8 +908,6 @@ const MessageTray = new Lang.Class({ | ||||
|         this._notificationTimeoutId = 0; | ||||
|         this._notificationRemoved = false; | ||||
|  | ||||
|         this.clearableCount = 0; | ||||
|  | ||||
|         Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false }); | ||||
|         Main.layoutManager.trackChrome(this._bannerBin, { affectsInputRegion: true }); | ||||
|  | ||||
| @@ -1598,6 +958,14 @@ const MessageTray = new Lang.Class({ | ||||
|         Shell.util_set_hidden_from_pick(this.actor, false); | ||||
|     }, | ||||
|  | ||||
|     get bannerAlignment() { | ||||
|         return this._bannerBin.get_x_align(); | ||||
|     }, | ||||
|  | ||||
|     set bannerAlignment(align) { | ||||
|         this._bannerBin.set_x_align(align); | ||||
|     }, | ||||
|  | ||||
|     _onNotificationKeyRelease: function(actor, event) { | ||||
|         if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) { | ||||
|             this._expireNotification(); | ||||
| @@ -1648,9 +1016,6 @@ const MessageTray = new Lang.Class({ | ||||
|             destroyId: 0, | ||||
|         }; | ||||
|  | ||||
|         if (source.isClearable) | ||||
|             this.clearableCount++; | ||||
|  | ||||
|         this._sources.set(source, obj); | ||||
|  | ||||
|         obj.notifyId = source.connect('notify', Lang.bind(this, this._onNotify)); | ||||
| @@ -1663,9 +1028,6 @@ const MessageTray = new Lang.Class({ | ||||
|         let obj = this._sources.get(source); | ||||
|         this._sources.delete(source); | ||||
|  | ||||
|         if (source.isClearable) | ||||
|             this.clearableCount--; | ||||
|  | ||||
|         source.disconnect(obj.notifyId); | ||||
|         source.disconnect(obj.destroyId); | ||||
|  | ||||
| @@ -1937,11 +1299,7 @@ const MessageTray = new Lang.Class({ | ||||
|             this.idleMonitor.add_user_active_watch(Lang.bind(this, this._onIdleMonitorBecameActive)); | ||||
|         } | ||||
|  | ||||
|         // HACK: didn't manage to get chat into a proper state in time | ||||
|         if (this._notification.source.isChat) | ||||
|             this._banner = this._notification; | ||||
|         else | ||||
|             this._banner = this._notification.createBanner(); | ||||
|         this._banner = this._notification.createBanner(); | ||||
|         this._bannerClickedId = this._banner.connect('done-displaying', | ||||
|                                                      Lang.bind(this, this._escapeTray)); | ||||
|         this._bannerUnfocusedId = this._banner.connect('unfocused', Lang.bind(this, function() { | ||||
| @@ -2093,10 +1451,7 @@ const MessageTray = new Lang.Class({ | ||||
|         this._pointerInNotification = false; | ||||
|         this._notificationRemoved = false; | ||||
|  | ||||
|         if (notification.resident) | ||||
|             this._bannerBin.remove_actor(this._banner.actor); | ||||
|         else | ||||
|             this._banner.actor.destroy(); | ||||
|         this._banner.actor.destroy(); | ||||
|         this._banner = null; | ||||
|         this.actor.hide(); | ||||
|     }, | ||||
|   | ||||
| @@ -14,7 +14,6 @@ const Atk = imports.gi.Atk; | ||||
|  | ||||
| const Params = imports.misc.params; | ||||
|  | ||||
| const Animation = imports.ui.animation; | ||||
| const Layout = imports.ui.layout; | ||||
| const Lightbox = imports.ui.lightbox; | ||||
| const Main = imports.ui.main; | ||||
| @@ -23,10 +22,6 @@ const Tweener = imports.ui.tweener; | ||||
| const OPEN_AND_CLOSE_TIME = 0.1; | ||||
| const FADE_OUT_DIALOG_TIME = 1.0; | ||||
|  | ||||
| const WORK_SPINNER_ICON_SIZE = 24; | ||||
| const WORK_SPINNER_ANIMATION_DELAY = 1.0; | ||||
| const WORK_SPINNER_ANIMATION_TIME = 0.3; | ||||
|  | ||||
| const State = { | ||||
|     OPENED: 0, | ||||
|     CLOSED: 1, | ||||
| @@ -79,7 +74,9 @@ const ModalDialog = new Lang.Class({ | ||||
|         this._group.add_actor(this._backgroundBin); | ||||
|  | ||||
|         this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog', | ||||
|                                                vertical:    true }); | ||||
|                                                x_align:      Clutter.ActorAlign.CENTER, | ||||
|                                                y_align:      Clutter.ActorAlign.CENTER, | ||||
|                                                vertical:     true }); | ||||
|         // modal dialogs are fixed width and grow vertically; set the request | ||||
|         // mode accordingly so wrapped labels are handled correctly during | ||||
|         // size requests. | ||||
| @@ -100,7 +97,8 @@ const ModalDialog = new Lang.Class({ | ||||
|         this.backgroundStack.add_actor(this.dialogLayout); | ||||
|  | ||||
|  | ||||
|         this.contentLayout = new St.BoxLayout({ vertical: true }); | ||||
|         this.contentLayout = new St.BoxLayout({ vertical: true, | ||||
|                                                 style_class: "modal-dialog-content-box" }); | ||||
|         this.dialogLayout.add(this.contentLayout, | ||||
|                               { expand:  true, | ||||
|                                 x_fill:  true, | ||||
| @@ -108,8 +106,7 @@ const ModalDialog = new Lang.Class({ | ||||
|                                 x_align: St.Align.MIDDLE, | ||||
|                                 y_align: St.Align.START }); | ||||
|  | ||||
|         this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box', | ||||
|                                                vertical: false }); | ||||
|         this.buttonLayout = new St.Widget ({ layout_manager: new Clutter.BoxLayout ({ homogeneous:true }) }); | ||||
|         this.dialogLayout.add(this.buttonLayout, | ||||
|                               { x_align: St.Align.MIDDLE, | ||||
|                                 y_align: St.Align.END }); | ||||
| @@ -118,8 +115,6 @@ const ModalDialog = new Lang.Class({ | ||||
|         this._initialKeyFocus = this.dialogLayout; | ||||
|         this._initialKeyFocusDestroyId = 0; | ||||
|         this._savedKeyFocus = null; | ||||
|  | ||||
|         this._workSpinner = null; | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
| @@ -147,16 +142,12 @@ const ModalDialog = new Lang.Class({ | ||||
|             else | ||||
|                 x_alignment = St.Align.MIDDLE; | ||||
|  | ||||
|             this.addButton(buttonInfo, { expand: true, | ||||
|                                          x_fill: false, | ||||
|                                          y_fill: false, | ||||
|                                          x_align: x_alignment, | ||||
|                                          y_align: St.Align.MIDDLE }); | ||||
|             this.addButton(buttonInfo); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     addButton: function(buttonInfo, layoutInfo) { | ||||
|         let label = buttonInfo['label']; | ||||
|     addButton: function(buttonInfo) { | ||||
|         let label = buttonInfo['label'] | ||||
|         let action = buttonInfo['action']; | ||||
|         let key = buttonInfo['key']; | ||||
|         let isDefault = buttonInfo['default']; | ||||
| @@ -170,10 +161,12 @@ const ModalDialog = new Lang.Class({ | ||||
|         else | ||||
|             keys = []; | ||||
|  | ||||
|         let button = new St.Button({ style_class: 'modal-dialog-button button', | ||||
|         let button = new St.Button({ style_class: 'modal-dialog-linked-button', | ||||
|                                      button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, | ||||
|                                      reactive:    true, | ||||
|                                      can_focus:   true, | ||||
|                                      x_expand:    true, | ||||
|                                      y_expand:    true, | ||||
|                                      label:       label }); | ||||
|         button.connect('clicked', action); | ||||
|  | ||||
| @@ -188,47 +181,11 @@ const ModalDialog = new Lang.Class({ | ||||
|         for (let i in keys) | ||||
|             this._buttonKeys[keys[i]] = buttonInfo; | ||||
|  | ||||
|         this.buttonLayout.add(button, layoutInfo); | ||||
|         this.buttonLayout.add_actor(button); | ||||
|  | ||||
|         return button; | ||||
|     }, | ||||
|  | ||||
|     placeSpinner: function(layoutInfo) { | ||||
|         let spinnerIcon = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg'); | ||||
|         this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE); | ||||
|         this._workSpinner.actor.opacity = 0; | ||||
|         this._workSpinner.actor.show(); | ||||
|  | ||||
|         this.buttonLayout.add(this._workSpinner.actor, layoutInfo); | ||||
|     }, | ||||
|  | ||||
|     setWorking: function(working) { | ||||
|         if (!this._workSpinner) | ||||
|             return; | ||||
|  | ||||
|         Tweener.removeTweens(this._workSpinner.actor); | ||||
|         if (working) { | ||||
|             this._workSpinner.play(); | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 255, | ||||
|                                delay: WORK_SPINNER_ANIMATION_DELAY, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear' | ||||
|                              }); | ||||
|         } else { | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 0, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear', | ||||
|                                onCompleteScope: this, | ||||
|                                onComplete: function() { | ||||
|                                    if (this._workSpinner) | ||||
|                                        this._workSpinner.stop(); | ||||
|                                } | ||||
|                              }); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onKeyPressEvent: function(object, event) { | ||||
|         this._pressedKey = event.get_key_symbol(); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|   | ||||
| @@ -15,8 +15,7 @@ const OsdMonitorLabel = new Lang.Class({ | ||||
|     Name: 'OsdMonitorLabel', | ||||
|  | ||||
|     _init: function(monitor, label) { | ||||
|         this._actor = new St.Widget({ opacity: 0, | ||||
|                                       x_expand: true, | ||||
|         this._actor = new St.Widget({ x_expand: true, | ||||
|                                       y_expand: true }); | ||||
|  | ||||
|         this._monitor = monitor; | ||||
| @@ -34,10 +33,6 @@ const OsdMonitorLabel = new Lang.Class({ | ||||
|         this._position(); | ||||
|  | ||||
|         Meta.disable_unredirect_for_screen(global.screen); | ||||
|         Tweener.addTween(this._actor, | ||||
|                          { opacity: 255, | ||||
|                            time: FADE_TIME, | ||||
|                            transition: 'easeOutQuad' }); | ||||
|     }, | ||||
|  | ||||
|     _position: function() { | ||||
| @@ -52,15 +47,8 @@ const OsdMonitorLabel = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|         Tweener.addTween(this._actor, | ||||
|                          { opacity: 0, | ||||
|                            time: FADE_TIME, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: Lang.bind(this, function() { | ||||
|                                this._actor.destroy(); | ||||
|                                Meta.enable_unredirect_for_screen(global.screen); | ||||
|                            }) | ||||
|                          }); | ||||
|         this._actor.destroy(); | ||||
|         Meta.enable_unredirect_for_screen(global.screen); | ||||
|     } | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -107,13 +107,6 @@ const Overview = new Lang.Class({ | ||||
|  | ||||
|         this._overviewCreated = true; | ||||
|  | ||||
|         // The main Background actors are inside global.window_group which are | ||||
|         // hidden when displaying the overview, so we create a new | ||||
|         // one. Instances of this class share a single CoglTexture behind the | ||||
|         // scenes which allows us to show the background with different | ||||
|         // rendering options without duplicating the texture data. | ||||
|         let monitor = Main.layoutManager.primaryMonitor; | ||||
|  | ||||
|         let layout = new Clutter.BinLayout(); | ||||
|         this._stack = new Clutter.Actor({ layout_manager: layout }); | ||||
|         this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true })); | ||||
| @@ -128,6 +121,11 @@ const Overview = new Lang.Class({ | ||||
|                                             y_expand: true }); | ||||
|         this._overview._delegate = this; | ||||
|  | ||||
|         // The main Background actors are inside global.window_group which are | ||||
|         // hidden when displaying the overview, so we create a new | ||||
|         // one. Instances of this class share a single CoglTexture behind the | ||||
|         // scenes which allows us to show the background with different | ||||
|         // rendering options without duplicating the texture data. | ||||
|         this._backgroundGroup = new Meta.BackgroundGroup(); | ||||
|         Main.layoutManager.overviewGroup.add_child(this._backgroundGroup); | ||||
|         this._bgManagers = []; | ||||
|   | ||||
| @@ -25,12 +25,12 @@ const RemoteMenu = imports.ui.remoteMenu; | ||||
| const Main = imports.ui.main; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const PANEL_ICON_SIZE = 24; | ||||
| const APP_MENU_ICON_MARGIN = 2; | ||||
| const PANEL_ICON_SIZE = 16; | ||||
| const APP_MENU_ICON_MARGIN = 0; | ||||
|  | ||||
| const BUTTON_DND_ACTIVATION_TIMEOUT = 250; | ||||
|  | ||||
| const SPINNER_ANIMATION_TIME = 0.2; | ||||
| const SPINNER_ANIMATION_TIME = 1.0; | ||||
|  | ||||
| // To make sure the panel corners blend nicely with the panel, | ||||
| // we draw background and borders the same way, e.g. drawing | ||||
| @@ -697,7 +697,6 @@ const AggregateMenu = new Lang.Class({ | ||||
|         this.menu.addMenuItem(this._location.menu); | ||||
|         this.menu.addMenuItem(this._rfkill.menu); | ||||
|         this.menu.addMenuItem(this._power.menu); | ||||
|         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.menu.addMenuItem(this._system.menu); | ||||
|     }, | ||||
| }); | ||||
| @@ -752,7 +751,7 @@ const Panel = new Lang.Class({ | ||||
|         })); | ||||
|  | ||||
|         Main.layoutManager.panelBox.add(this.actor); | ||||
|         Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic', | ||||
|         Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'focus-top-bar-symbolic', | ||||
|                                         { sortGroup: CtrlAltTab.SortGroup.TOP }); | ||||
|  | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._updatePanel)); | ||||
| @@ -933,6 +932,14 @@ const Panel = new Lang.Class({ | ||||
|         this._updateBox(panel.center, this._centerBox); | ||||
|         this._updateBox(panel.right, this._rightBox); | ||||
|  | ||||
|         if (panel.left.indexOf('dateMenu') != -1) | ||||
|             Main.messageTray.bannerAlignment = Clutter.ActorAlign.START; | ||||
|         else if (panel.right.indexOf('dateMenu') != -1) | ||||
|             Main.messageTray.bannerAlignment = Clutter.ActorAlign.END; | ||||
|         // Default to center if there is no dateMenu | ||||
|         else | ||||
|             Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER; | ||||
|  | ||||
|         if (this._sessionStyle) | ||||
|             this._removeStyleClassName(this._sessionStyle); | ||||
|  | ||||
| @@ -995,6 +1002,7 @@ const Panel = new Lang.Class({ | ||||
|         if (parent) | ||||
|             parent.remove_actor(container); | ||||
|  | ||||
|  | ||||
|         box.insert_child_at_index(container, position); | ||||
|         if (indicator.menu) | ||||
|             this.menuManager.addMenu(indicator.menu); | ||||
| @@ -1004,6 +1012,8 @@ const Panel = new Lang.Class({ | ||||
|             emitter.disconnect(destroyId); | ||||
|             container.destroy(); | ||||
|         })); | ||||
|         indicator.connect('menu-set', Lang.bind(this, this._onMenuSet)); | ||||
|         this._onMenuSet(indicator); | ||||
|     }, | ||||
|  | ||||
|     addToStatusArea: function(role, indicator, position, box) { | ||||
| @@ -1035,5 +1045,24 @@ const Panel = new Lang.Class({ | ||||
|         this.actor.remove_style_class_name(className); | ||||
|         this._rightCorner.actor.remove_style_class_name(className); | ||||
|         this._leftCorner.actor.remove_style_class_name(className); | ||||
|     }, | ||||
|  | ||||
|     _onMenuSet: function(indicator) { | ||||
|         if (!indicator.menu || indicator.menu._openChangedId > 0) | ||||
|             return; | ||||
|  | ||||
|         indicator.menu._openChangedId = indicator.menu.connect('open-state-changed', | ||||
|             Lang.bind(this, function(menu, isOpen) { | ||||
|                 let boxAlignment; | ||||
|                 if (this._leftBox.contains(indicator.container)) | ||||
|                     boxAlignment = Clutter.ActorAlign.START; | ||||
|                 else if (this._centerBox.contains(indicator.container)) | ||||
|                     boxAlignment = Clutter.ActorAlign.CENTER; | ||||
|                 else if (this._rightBox.contains(indicator.container)) | ||||
|                     boxAlignment = Clutter.ActorAlign.END; | ||||
|  | ||||
|                 if (boxAlignment == Main.messageTray.bannerAlignment) | ||||
|                     Main.messageTray.bannerBlocked = isOpen; | ||||
|             })); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -128,6 +128,7 @@ const Button = new Lang.Class({ | ||||
|             Main.uiGroup.add_actor(this.menu.actor); | ||||
|             this.menu.actor.hide(); | ||||
|         } | ||||
|         this.emit('menu-set'); | ||||
|     }, | ||||
|  | ||||
|     _onEvent: function(actor, event) { | ||||
|   | ||||
| @@ -12,7 +12,6 @@ const BoxPointer = imports.ui.boxpointer; | ||||
| const GrabHelper = imports.ui.grabHelper; | ||||
| const Main = imports.ui.main; | ||||
| const Params = imports.misc.params; | ||||
| const Separator = imports.ui.separator; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const Ornament = { | ||||
| @@ -170,10 +169,10 @@ const PopupBaseMenuItem = new Lang.Class({ | ||||
|         if (activeChanged) { | ||||
|             this.active = active; | ||||
|             if (active) { | ||||
|                 this.actor.add_style_class_name('active'); | ||||
|                 this.actor.add_style_class_name('selected'); | ||||
|                 this.actor.grab_key_focus(); | ||||
|             } else { | ||||
|                 this.actor.remove_style_class_name('active'); | ||||
|                 this.actor.remove_style_class_name('selected'); | ||||
|                 // Remove the CSS active state if the user press the button and | ||||
|                 // while holding moves to another menu item, so we don't paint all items. | ||||
|                 // The correct behaviour would be to set the new item with the CSS | ||||
| @@ -264,7 +263,9 @@ const PopupSeparatorMenuItem = new Lang.Class({ | ||||
|                            Lang.bind(this, this._syncVisibility)); | ||||
|         this._syncVisibility(); | ||||
|  | ||||
|         this._separator = new St.Widget({ style_class: 'popup-separator-menu-item' }); | ||||
|         this._separator = new St.Widget({ style_class: 'popup-separator-menu-item', | ||||
|                                           y_expand: true, | ||||
|                                           y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this.actor.add(this._separator, { expand: true }); | ||||
|     }, | ||||
|  | ||||
| @@ -395,6 +396,7 @@ const PopupImageMenuItem = new Lang.Class({ | ||||
|         this.actor.add_child(this.label); | ||||
|         this._icon = new St.Icon({ style_class: 'popup-menu-icon' }); | ||||
|         this.actor.add_child(this._icon, { align: St.Align.END }); | ||||
|         this.actor.label_actor = this.label; | ||||
|  | ||||
|         this.setIcon(iconName); | ||||
|     }, | ||||
| @@ -1058,11 +1060,6 @@ const PopupSubMenuMenuItem = new Lang.Class({ | ||||
|         let expander = new St.Bin({ style_class: 'popup-menu-item-expander' }); | ||||
|         this.actor.add(expander, { expand: true }); | ||||
|  | ||||
|         this.status = new St.Label({ style_class: 'popup-status-menu-item', | ||||
|                                      y_expand: true, | ||||
|                                      y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this.actor.add_child(this.status); | ||||
|  | ||||
|         this._triangle = arrowIcon(St.Side.RIGHT); | ||||
|         this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 }); | ||||
|  | ||||
|   | ||||
| @@ -63,7 +63,7 @@ const SearchProvider2Iface = '<node> \ | ||||
| var SearchProviderProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProviderIface); | ||||
| var SearchProvider2ProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProvider2Iface); | ||||
|  | ||||
| function loadRemoteSearchProviders(callback) { | ||||
| function loadRemoteSearchProviders(searchSettings, callback) { | ||||
|     let objectPaths = {}; | ||||
|     let loadedProviders = []; | ||||
|  | ||||
| @@ -124,7 +124,6 @@ function loadRemoteSearchProviders(callback) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let searchSettings = new Gio.Settings({ schema_id: Search.SEARCH_PROVIDERS_SCHEMA }); | ||||
|     if (searchSettings.get_boolean('disable-external')) { | ||||
|         callback([]); | ||||
|         return; | ||||
| @@ -236,8 +235,10 @@ const RemoteSearchProvider = new Lang.Class({ | ||||
|  | ||||
|     _getResultsFinished: function(results, error, callback) { | ||||
|         if (error) { | ||||
|             if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) | ||||
|                 log('Received error from DBus search provider %s: %s'.format(this.id, String(error))); | ||||
|             if (error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) | ||||
|                 return; | ||||
|  | ||||
|             log('Received error from DBus search provider %s: %s'.format(this.id, String(error))); | ||||
|             callback([]); | ||||
|             return; | ||||
|         } | ||||
|   | ||||
| @@ -507,21 +507,22 @@ const ScreenShield = new Lang.Class({ | ||||
|                                                       this._liftShield(true, 0); | ||||
|                                               })); | ||||
|  | ||||
|         this._inhibitor = null; | ||||
|         this._aboutToSuspend = false; | ||||
|         this._loginManager = LoginManager.getLoginManager(); | ||||
|         this._loginManager.connect('prepare-for-sleep', | ||||
|                                    Lang.bind(this, this._prepareForSleep)); | ||||
|         this._inhibitSuspend(); | ||||
|  | ||||
|         this._loginSession = null; | ||||
|         this._loginManager.getCurrentSessionProxy(Lang.bind(this, | ||||
|             function(sessionProxy) { | ||||
|                 this._loginSession = sessionProxy; | ||||
|                 this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); })); | ||||
|                 this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.deactivate(false); })); | ||||
|                 this._loginSession.connect('g-properties-changed', Lang.bind(this, this._syncInhibitor)); | ||||
|                 this._syncInhibitor(); | ||||
|             })); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema_id: SCREENSAVER_SCHEMA }); | ||||
|         this._settings.connect('changed::' + LOCK_ENABLED_KEY, Lang.bind(this, this._syncInhibitor)); | ||||
|  | ||||
|         this._isModal = false; | ||||
|         this._hasLockScreen = false; | ||||
| @@ -547,6 +548,18 @@ const ScreenShield = new Lang.Class({ | ||||
|  | ||||
|         this.idleMonitor = Meta.IdleMonitor.get_core(); | ||||
|         this._cursorTracker = Meta.CursorTracker.get_for_screen(global.screen); | ||||
|  | ||||
|         this._syncInhibitor(); | ||||
|     }, | ||||
|  | ||||
|     _setActive: function(active) { | ||||
|         let prevIsActive = this._isActive; | ||||
|         this._isActive = active; | ||||
|  | ||||
|         if (prevIsActive != this._isActive) | ||||
|             this.emit('active-changed'); | ||||
|  | ||||
|         this._syncInhibitor(); | ||||
|     }, | ||||
|  | ||||
|     _createBackground: function(monitorIndex) { | ||||
| @@ -664,31 +677,28 @@ const ScreenShield = new Lang.Class({ | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _inhibitSuspend: function() { | ||||
|         this._loginManager.inhibit(_("GNOME needs to lock the screen"), | ||||
|                                    Lang.bind(this, function(inhibitor) { | ||||
|                                        this._inhibitor = inhibitor; | ||||
|                                    })); | ||||
|     }, | ||||
|  | ||||
|     _uninhibitSuspend: function() { | ||||
|         if (this._inhibitor) | ||||
|             this._inhibitor.close(null); | ||||
|         this._inhibitor = null; | ||||
|     _syncInhibitor: function() { | ||||
|         let inhibit = (this._loginSession && this._loginSession.Active && | ||||
|                        !this._isActive && this._settings.get_boolean(LOCK_ENABLED_KEY)); | ||||
|         if (inhibit) { | ||||
|             this._loginManager.inhibit(_("GNOME needs to lock the screen"), | ||||
|                                        Lang.bind(this, function(inhibitor) { | ||||
|                                            if (this._inhibitor) | ||||
|                                                this._inhibitor.close(null); | ||||
|                                            this._inhibitor = inhibitor; | ||||
|                                        })); | ||||
|         } else { | ||||
|             if (this._inhibitor) | ||||
|                 this._inhibitor.close(null); | ||||
|             this._inhibitor = null; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _prepareForSleep: function(loginManager, aboutToSuspend) { | ||||
|         this._aboutToSuspend = aboutToSuspend; | ||||
|  | ||||
|         if (aboutToSuspend) { | ||||
|             if (!this._settings.get_boolean(LOCK_ENABLED_KEY)) { | ||||
|                 this._uninhibitSuspend(); | ||||
|                 return; | ||||
|             } | ||||
|             this.lock(true); | ||||
|             if (this._settings.get_boolean(LOCK_ENABLED_KEY)) | ||||
|                 this.lock(true); | ||||
|         } else { | ||||
|             this._inhibitSuspend(); | ||||
|  | ||||
|             this._wakeUpScreen(); | ||||
|         } | ||||
|     }, | ||||
| @@ -1083,15 +1093,7 @@ const ScreenShield = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _completeLockScreenShown: function() { | ||||
|         let prevIsActive = this._isActive; | ||||
|         this._isActive = true; | ||||
|  | ||||
|         if (prevIsActive != this._isActive) | ||||
|             this.emit('active-changed'); | ||||
|  | ||||
|         if (this._aboutToSuspend) | ||||
|             this._uninhibitSuspend(); | ||||
|  | ||||
|         this._setActive(true); | ||||
|         this.emit('lock-screen-shown'); | ||||
|     }, | ||||
|  | ||||
| @@ -1185,8 +1187,7 @@ const ScreenShield = new Lang.Class({ | ||||
|             // gnome-settings-daemon will stop blanking the screen | ||||
|  | ||||
|             this._activationTime = 0; | ||||
|             this._isActive = false; | ||||
|             this.emit('active-changed'); | ||||
|             this._setActive(false); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -1229,9 +1230,8 @@ const ScreenShield = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         this._activationTime = 0; | ||||
|         this._isActive = false; | ||||
|         this._setActive(false); | ||||
|         this._isLocked = false; | ||||
|         this.emit('active-changed'); | ||||
|         this.emit('locked-changed'); | ||||
|         global.set_runtime_state(LOCKED_STATE_STR, null); | ||||
|     }, | ||||
|   | ||||
| @@ -283,8 +283,8 @@ const SelectArea = new Lang.Class({ | ||||
|     _getGeometry: function() { | ||||
|         return { x: Math.min(this._startX, this._lastX), | ||||
|                  y: Math.min(this._startY, this._lastY), | ||||
|                  width: Math.abs(this._startX - this._lastX), | ||||
|                  height: Math.abs(this._startY - this._lastY) }; | ||||
|                  width: Math.abs(this._startX - this._lastX) + 1, | ||||
|                  height: Math.abs(this._startY - this._lastY) + 1 }; | ||||
|     }, | ||||
|  | ||||
|     _onMotionEvent: function(actor, event) { | ||||
| @@ -292,6 +292,8 @@ const SelectArea = new Lang.Class({ | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         [this._lastX, this._lastY] = event.get_coords(); | ||||
|         this._lastX = Math.floor(this._lastX); | ||||
|         this._lastY = Math.floor(this._lastY); | ||||
|         let geometry = this._getGeometry(); | ||||
|  | ||||
|         this._rubberband.set_position(geometry.x, geometry.y); | ||||
| @@ -302,6 +304,8 @@ const SelectArea = new Lang.Class({ | ||||
|  | ||||
|     _onButtonPress: function(actor, event) { | ||||
|         [this._startX, this._startY] = event.get_coords(); | ||||
|         this._startX = Math.floor(this._startX); | ||||
|         this._startY = Math.floor(this._startY); | ||||
|         this._rubberband.set_position(this._startX, this._startY); | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|   | ||||
| @@ -205,6 +205,13 @@ const SearchResultsBase = new Lang.Class({ | ||||
|                     callback(false); | ||||
|                     return; | ||||
|                 } | ||||
|                 if (metas.some(function(meta) { | ||||
|                     return !meta.name || !meta.id; | ||||
|                 })) { | ||||
|                     log('Invalid result meta returned from search provider ' + this.provider.id); | ||||
|                     callback(false); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 metasNeeded.forEach(Lang.bind(this, function(resultId, i) { | ||||
|                     let meta = metas[i]; | ||||
| @@ -409,6 +416,7 @@ const SearchResults = new Lang.Class({ | ||||
|  | ||||
|         this._searchSettings = new Gio.Settings({ schema_id: SEARCH_PROVIDERS_SCHEMA }); | ||||
|         this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders)); | ||||
|         this._searchSettings.connect('changed::enabled', Lang.bind(this, this._reloadRemoteProviders)); | ||||
|         this._searchSettings.connect('changed::disable-external', Lang.bind(this, this._reloadRemoteProviders)); | ||||
|         this._searchSettings.connect('changed::sort-order', Lang.bind(this, this._reloadRemoteProviders)); | ||||
|  | ||||
| @@ -427,7 +435,7 @@ const SearchResults = new Lang.Class({ | ||||
|             this._unregisterProvider(provider); | ||||
|         })); | ||||
|  | ||||
|         RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, function(providers) { | ||||
|         RemoteSearch.loadRemoteSearchProviders(this._searchSettings, Lang.bind(this, function(providers) { | ||||
|             providers.forEach(Lang.bind(this, this._registerProvider)); | ||||
|         })); | ||||
|     }, | ||||
|   | ||||
| @@ -49,9 +49,9 @@ function _setButtonsForChoices(dialog, choices) { | ||||
| function _setLabelsForMessage(dialog, message) { | ||||
|     let labels = message.split('\n'); | ||||
|  | ||||
|     _setLabelText(dialog.subjectLabel, labels[0]); | ||||
|     if (labels.length > 1) | ||||
|         _setLabelText(dialog.descriptionLabel, labels[1]); | ||||
|     _setLabelText(dialog.subjectLabel, labels.shift()); | ||||
|     if (labels.length > 0) | ||||
|         _setLabelText(dialog.descriptionLabel, labels.join('\n')); | ||||
| } | ||||
|  | ||||
| function _createIcon(gicon) { | ||||
| @@ -347,7 +347,7 @@ const ShellMountPasswordDialog = new Lang.Class({ | ||||
|         mainContentBox.add(this._messageBox, | ||||
|                            { y_align: St.Align.START, expand: true, x_fill: true, y_fill: true }); | ||||
|  | ||||
|         let subject = new St.Label({ style_class: 'prompt-dialog-headline' }); | ||||
|         let subject = new St.Label({ style_class: 'prompt-dialog-headline headline' }); | ||||
|         this._messageBox.add(subject, | ||||
|                              { y_fill:  false, | ||||
|                                y_align: St.Align.START }); | ||||
|   | ||||
| @@ -119,23 +119,46 @@ const ATIndicator = new Lang.Class({ | ||||
|  | ||||
|     _buildItem: function(string, schema, key) { | ||||
|         let settings = new Gio.Settings({ schema_id: schema }); | ||||
|         settings.connect('changed::'+key, Lang.bind(this, function() { | ||||
|             widget.setToggleState(settings.get_boolean(key)); | ||||
|  | ||||
|             this._queueSyncMenuVisibility(); | ||||
|         })); | ||||
|  | ||||
|         let widget = this._buildItemExtended(string, | ||||
|             settings.get_boolean(key), | ||||
|             settings.is_writable(key), | ||||
|             function(enabled) { | ||||
|                 return settings.set_boolean(key, enabled); | ||||
|             }); | ||||
|         settings.connect('changed::'+key, Lang.bind(this, function() { | ||||
|             widget.setToggleState(settings.get_boolean(key)); | ||||
|  | ||||
|             this._queueSyncMenuVisibility(); | ||||
|         })); | ||||
|         return widget; | ||||
|     }, | ||||
|  | ||||
|     _buildHCItem: function() { | ||||
|         let interfaceSettings = new Gio.Settings({ schema_id: DESKTOP_INTERFACE_SCHEMA }); | ||||
|         let wmSettings = new Gio.Settings({ schema_id: WM_SCHEMA }); | ||||
|         interfaceSettings.connect('changed::' + KEY_GTK_THEME, Lang.bind(this, function() { | ||||
|             let value = interfaceSettings.get_string(KEY_GTK_THEME); | ||||
|             if (value == HIGH_CONTRAST_THEME) { | ||||
|                 highContrast.setToggleState(true); | ||||
|             } else { | ||||
|                 highContrast.setToggleState(false); | ||||
|                 gtkTheme = value; | ||||
|             } | ||||
|  | ||||
|             this._queueSyncMenuVisibility(); | ||||
|         })); | ||||
|         interfaceSettings.connect('changed::' + KEY_ICON_THEME, function() { | ||||
|             let value = interfaceSettings.get_string(KEY_ICON_THEME); | ||||
|             if (value != HIGH_CONTRAST_THEME) | ||||
|                 iconTheme = value; | ||||
|         }); | ||||
|         wmSettings.connect('changed::' + KEY_WM_THEME, function() { | ||||
|             let value = wmSettings.get_string(KEY_WM_THEME); | ||||
|             if (value != HIGH_CONTRAST_THEME) | ||||
|                 wmTheme = value; | ||||
|         }); | ||||
|  | ||||
|         let gtkTheme = interfaceSettings.get_string(KEY_GTK_THEME); | ||||
|         let iconTheme = interfaceSettings.get_string(KEY_ICON_THEME); | ||||
|         let wmTheme = wmSettings.get_string(KEY_WM_THEME); | ||||
| @@ -161,32 +184,18 @@ const ATIndicator = new Lang.Class({ | ||||
|                     wmSettings.reset(KEY_WM_THEME); | ||||
|                 } | ||||
|             }); | ||||
|         interfaceSettings.connect('changed::' + KEY_GTK_THEME, Lang.bind(this, function() { | ||||
|             let value = interfaceSettings.get_string(KEY_GTK_THEME); | ||||
|             if (value == HIGH_CONTRAST_THEME) { | ||||
|                 highContrast.setToggleState(true); | ||||
|             } else { | ||||
|                 highContrast.setToggleState(false); | ||||
|                 gtkTheme = value; | ||||
|             } | ||||
|  | ||||
|             this._queueSyncMenuVisibility(); | ||||
|         })); | ||||
|         interfaceSettings.connect('changed::' + KEY_ICON_THEME, function() { | ||||
|             let value = interfaceSettings.get_string(KEY_ICON_THEME); | ||||
|             if (value != HIGH_CONTRAST_THEME) | ||||
|                 iconTheme = value; | ||||
|         }); | ||||
|         wmSettings.connect('changed::' + KEY_WM_THEME, function() { | ||||
|             let value = wmSettings.get_string(KEY_WM_THEME); | ||||
|             if (value != HIGH_CONTRAST_THEME) | ||||
|                 wmTheme = value; | ||||
|         }); | ||||
|         return highContrast; | ||||
|     }, | ||||
|  | ||||
|     _buildFontItem: function() { | ||||
|         let settings = new Gio.Settings({ schema_id: DESKTOP_INTERFACE_SCHEMA }); | ||||
|         settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, Lang.bind(this, function() { | ||||
|             let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR); | ||||
|             let active = (factor > 1.0); | ||||
|             widget.setToggleState(active); | ||||
|  | ||||
|             this._queueSyncMenuVisibility(); | ||||
|         })); | ||||
|  | ||||
|         let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR); | ||||
|         let initial_setting = (factor > 1.0); | ||||
| @@ -200,13 +209,6 @@ const ATIndicator = new Lang.Class({ | ||||
|                 else | ||||
|                     settings.reset(KEY_TEXT_SCALING_FACTOR); | ||||
|             }); | ||||
|         settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, Lang.bind(this, function() { | ||||
|             let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR); | ||||
|             let active = (factor > 1.0); | ||||
|             widget.setToggleState(active); | ||||
|  | ||||
|             this._queueSyncMenuVisibility(); | ||||
|         })); | ||||
|         return widget; | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -46,7 +46,7 @@ const Indicator = new Lang.Class({ | ||||
|  | ||||
|         // The Bluetooth menu only appears when Bluetooth is in use, | ||||
|         // so just statically build it with a "Turn Off" menu item. | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true); | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem('', true); | ||||
|         this._item.icon.icon_name = 'bluetooth-active-symbolic'; | ||||
|         this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() { | ||||
|             this._proxy.BluetoothAirplaneMode = true; | ||||
| @@ -101,8 +101,9 @@ const Indicator = new Lang.Class({ | ||||
|         this._item.actor.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode; | ||||
|  | ||||
|         if (nDevices > 0) | ||||
|             this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices", nDevices).format(nDevices); | ||||
|             /* Translators: this is the number of connected bluetooth devices */ | ||||
|             this._item.label.text = ngettext("%d Connected", "%d Connected", nDevices).format(nDevices); | ||||
|         else | ||||
|             this._item.status.text = _("Not Connected"); | ||||
|             this._item.label.text = _("Not In Use"); | ||||
|     }, | ||||
| }); | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| @@ -18,10 +19,6 @@ const PanelMenu = imports.ui.panelMenu; | ||||
| const SwitcherPopup = imports.ui.switcherPopup; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| const DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources'; | ||||
| const KEY_INPUT_SOURCES = 'sources'; | ||||
| const KEY_KEYBOARD_OPTIONS = 'xkb-options'; | ||||
|  | ||||
| const INPUT_SOURCE_TYPE_XKB = 'xkb'; | ||||
| const INPUT_SOURCE_TYPE_IBUS = 'ibus'; | ||||
|  | ||||
| @@ -142,6 +139,149 @@ const InputSourceSwitcher = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const InputSourceSettings = new Lang.Class({ | ||||
|     Name: 'InputSourceSettings', | ||||
|     Abstract: true, | ||||
|  | ||||
|     _emitInputSourcesChanged: function() { | ||||
|         this.emit('input-sources-changed'); | ||||
|     }, | ||||
|  | ||||
|     _emitKeyboardOptionsChanged: function() { | ||||
|         this.emit('keyboard-options-changed'); | ||||
|     }, | ||||
|  | ||||
|     _emitPerWindowChanged: function() { | ||||
|         this.emit('per-window-changed'); | ||||
|     }, | ||||
|  | ||||
|     get inputSources() { | ||||
|         return []; | ||||
|     }, | ||||
|  | ||||
|     get keyboardOptions() { | ||||
|         return []; | ||||
|     }, | ||||
|  | ||||
|     get perWindow() { | ||||
|         return false; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(InputSourceSettings.prototype); | ||||
|  | ||||
| const InputSourceSystemSettings = new Lang.Class({ | ||||
|     Name: 'InputSourceSystemSettings', | ||||
|     Extends: InputSourceSettings, | ||||
|  | ||||
|     _BUS_NAME: 'org.freedesktop.locale1', | ||||
|     _BUS_PATH: '/org/freedesktop/locale1', | ||||
|     _BUS_IFACE: 'org.freedesktop.locale1', | ||||
|     _BUS_PROPS_IFACE: 'org.freedesktop.DBus.Properties', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._layouts = ''; | ||||
|         this._variants = ''; | ||||
|         this._options = ''; | ||||
|  | ||||
|         this._reload(); | ||||
|  | ||||
|         Gio.DBus.system.signal_subscribe(this._BUS_NAME, | ||||
|                                          this._BUS_PROPS_IFACE, | ||||
|                                          'PropertiesChanged', | ||||
|                                          this._BUS_PATH, | ||||
|                                          null, | ||||
|                                          Gio.DBusSignalFlags.NONE, | ||||
|                                          Lang.bind(this, this._reload)); | ||||
|     }, | ||||
|  | ||||
|     _reload: function() { | ||||
|         Gio.DBus.system.call(this._BUS_NAME, | ||||
|                              this._BUS_PATH, | ||||
|                              this._BUS_PROPS_IFACE, | ||||
|                              'GetAll', | ||||
|                              new GLib.Variant('(s)', [this._BUS_IFACE]), | ||||
|                              null, Gio.DBusCallFlags.NONE, -1, null, | ||||
|                              Lang.bind(this, function(conn, result) { | ||||
|                                  let props; | ||||
|                                  try { | ||||
|                                      props = conn.call_finish(result).deep_unpack()[0]; | ||||
|                                  } catch(e) { | ||||
|                                      log('Could not get properties from ' + this._BUS_NAME); | ||||
|                                      return; | ||||
|                                  } | ||||
|                                  let layouts = props['X11Layout'].unpack(); | ||||
|                                  let variants = props['X11Variant'].unpack(); | ||||
|                                  let options = props['X11Options'].unpack(); | ||||
|  | ||||
|                                  if (layouts != this._layouts || | ||||
|                                      variants != this._variants) { | ||||
|                                      this._layouts = layouts; | ||||
|                                      this._variants = variants; | ||||
|                                      this._emitInputSourcesChanged(); | ||||
|                                  } | ||||
|                                  if (options != this._options) { | ||||
|                                      this._options = options; | ||||
|                                      this._emitKeyboardOptionsChanged(); | ||||
|                                  } | ||||
|                              })); | ||||
|     }, | ||||
|  | ||||
|     get inputSources() { | ||||
|         let sourcesList = []; | ||||
|         let layouts = this._layouts.split(','); | ||||
|         let variants = this._variants.split(','); | ||||
|  | ||||
|         for (let i = 0; i < layouts.length && !!layouts[i]; i++) { | ||||
|             let id = layouts[i]; | ||||
|             if (!!variants[i]) | ||||
|                 id += '+' + variants[i]; | ||||
|             sourcesList.push({ type: INPUT_SOURCE_TYPE_XKB, id: id }); | ||||
|         } | ||||
|         return sourcesList; | ||||
|     }, | ||||
|  | ||||
|     get keyboardOptions() { | ||||
|         return this._options.split(','); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const InputSourceSessionSettings = new Lang.Class({ | ||||
|     Name: 'InputSourceSessionSettings', | ||||
|     Extends: InputSourceSettings, | ||||
|  | ||||
|     _DESKTOP_INPUT_SOURCES_SCHEMA: 'org.gnome.desktop.input-sources', | ||||
|     _KEY_INPUT_SOURCES: 'sources', | ||||
|     _KEY_KEYBOARD_OPTIONS: 'xkb-options', | ||||
|     _KEY_PER_WINDOW: 'per-window', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._settings = new Gio.Settings({ schema_id: this._DESKTOP_INPUT_SOURCES_SCHEMA }); | ||||
|         this._settings.connect('changed::' + this._KEY_INPUT_SOURCES, Lang.bind(this, this._emitInputSourcesChanged)); | ||||
|         this._settings.connect('changed::' + this._KEY_KEYBOARD_OPTIONS, Lang.bind(this, this._emitKeyboardOptionsChanged)); | ||||
|         this._settings.connect('changed::' + this._KEY_PER_WINDOW, Lang.bind(this, this._emitPerWindowChanged)); | ||||
|     }, | ||||
|  | ||||
|     get inputSources() { | ||||
|         let sourcesList = []; | ||||
|         let sources = this._settings.get_value(this._KEY_INPUT_SOURCES); | ||||
|         let nSources = sources.n_children(); | ||||
|  | ||||
|         for (let i = 0; i < nSources; i++) { | ||||
|             let [type, id] = sources.get_child_value(i).deep_unpack(); | ||||
|             sourcesList.push({ type: type, id: id }); | ||||
|         } | ||||
|         return sourcesList; | ||||
|     }, | ||||
|  | ||||
|     get keyboardOptions() { | ||||
|         return this._settings.get_strv(this._KEY_KEYBOARD_OPTIONS); | ||||
|     }, | ||||
|  | ||||
|     get perWindow() { | ||||
|         return this._settings.get_boolean(this._KEY_PER_WINDOW); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const InputSourceManager = new Lang.Class({ | ||||
|     Name: 'InputSourceManager', | ||||
|  | ||||
| @@ -155,11 +295,11 @@ const InputSourceManager = new Lang.Class({ | ||||
|         this._ibusSources = {}; | ||||
|  | ||||
|         this._currentSource = null; | ||||
|         this._backupSource = null; | ||||
|  | ||||
|         // All valid input sources currently in the gsettings | ||||
|         // KEY_INPUT_SOURCES list ordered by most recently used | ||||
|         this._mruSources = []; | ||||
|         this._mruSourcesBackup = null; | ||||
|         this._keybindingAction = | ||||
|             Main.wm.addKeybinding('switch-input-source', | ||||
|                                   new Gio.Settings({ schema_id: "org.gnome.desktop.wm.keybindings" }), | ||||
| @@ -172,9 +312,12 @@ const InputSourceManager = new Lang.Class({ | ||||
|                                   Meta.KeyBindingFlags.IS_REVERSED, | ||||
|                                   Shell.ActionMode.ALL, | ||||
|                                   Lang.bind(this, this._switchInputSource)); | ||||
|         this._settings = new Gio.Settings({ schema_id: DESKTOP_INPUT_SOURCES_SCHEMA }); | ||||
|         this._settings.connect('changed::' + KEY_INPUT_SOURCES, Lang.bind(this, this._inputSourcesChanged)); | ||||
|         this._settings.connect('changed::' + KEY_KEYBOARD_OPTIONS, Lang.bind(this, this._keyboardOptionsChanged)); | ||||
|         if (Main.sessionMode.isGreeter) | ||||
|             this._settings = new InputSourceSystemSettings(); | ||||
|         else | ||||
|             this._settings = new InputSourceSessionSettings(); | ||||
|         this._settings.connect('input-sources-changed', Lang.bind(this, this._inputSourcesChanged)); | ||||
|         this._settings.connect('keyboard-options-changed', Lang.bind(this, this._keyboardOptionsChanged)); | ||||
|  | ||||
|         this._xkbInfo = KeyboardManager.getXkbInfo(); | ||||
|         this._keyboardManager = KeyboardManager.getKeyboardManager(); | ||||
| @@ -192,13 +335,13 @@ const InputSourceManager = new Lang.Class({ | ||||
|         this._focusWindowNotifyId = 0; | ||||
|         this._overviewShowingId = 0; | ||||
|         this._overviewHiddenId = 0; | ||||
|         this._settings.connect('changed::per-window', Lang.bind(this, this._sourcesPerWindowChanged)); | ||||
|         this._settings.connect('per-window-changed', Lang.bind(this, this._sourcesPerWindowChanged)); | ||||
|         this._sourcesPerWindowChanged(); | ||||
|         this._disableIBus = false; | ||||
|     }, | ||||
|  | ||||
|     reload: function() { | ||||
|         this._keyboardManager.setKeyboardOptions(this._settings.get_strv(KEY_KEYBOARD_OPTIONS)); | ||||
|         this._keyboardManager.setKeyboardOptions(this._settings.keyboardOptions); | ||||
|         this._inputSourcesChanged(); | ||||
|     }, | ||||
|  | ||||
| @@ -253,7 +396,7 @@ const InputSourceManager = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _keyboardOptionsChanged: function() { | ||||
|         this._keyboardManager.setKeyboardOptions(this._settings.get_strv(KEY_KEYBOARD_OPTIONS)); | ||||
|         this._keyboardManager.setKeyboardOptions(this._settings.keyboardOptions); | ||||
|         this._keyboardManager.reapply(); | ||||
|     }, | ||||
|  | ||||
| @@ -294,8 +437,8 @@ const InputSourceManager = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _inputSourcesChanged: function() { | ||||
|         let sources = this._settings.get_value(KEY_INPUT_SOURCES); | ||||
|         let nSources = sources.n_children(); | ||||
|         let sources = this._settings.inputSources; | ||||
|         let nSources = sources.length; | ||||
|  | ||||
|         this._inputSources = {}; | ||||
|         this._ibusSources = {}; | ||||
| @@ -304,7 +447,8 @@ const InputSourceManager = new Lang.Class({ | ||||
|         for (let i = 0; i < nSources; i++) { | ||||
|             let displayName; | ||||
|             let shortName; | ||||
|             let [type, id] = sources.get_child_value(i).deep_unpack(); | ||||
|             let type = sources[i].type; | ||||
|             let id = sources[i].id; | ||||
|             let exists = false; | ||||
|  | ||||
|             if (type == INPUT_SOURCE_TYPE_XKB) { | ||||
| @@ -372,6 +516,11 @@ const InputSourceManager = new Lang.Class({ | ||||
|  | ||||
|         this._keyboardManager.setUserLayouts(sourcesList.map(function(x) { return x.xkbId; })); | ||||
|  | ||||
|         if (!this._disableIBus && this._mruSourcesBackup) { | ||||
|             this._mruSources = this._mruSourcesBackup; | ||||
|             this._mruSourcesBackup = null; | ||||
|         } | ||||
|  | ||||
|         let mruSources = []; | ||||
|         for (let i = 0; i < this._mruSources.length; i++) { | ||||
|             for (let j = 0; j < sourcesList.length; j++) | ||||
| @@ -383,20 +532,8 @@ const InputSourceManager = new Lang.Class({ | ||||
|         } | ||||
|         this._mruSources = mruSources.concat(sourcesList); | ||||
|  | ||||
|         if (this._mruSources.length > 0) { | ||||
|             if (!this._disableIBus && this._backupSource) { | ||||
|                 for (let i = 0; i < this._mruSources.length; i++) { | ||||
|                     if (this._mruSources[i].type == this._backupSource.type && | ||||
|                         this._mruSources[i].id == this._backupSource.id) { | ||||
|                         let currentSource = this._mruSources.splice(i, 1); | ||||
|                         this._mruSources = currentSource.concat(this._mruSources); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 this._backupSource = null; | ||||
|             } | ||||
|         if (this._mruSources.length > 0) | ||||
|             this._mruSources[0].activate(); | ||||
|         } | ||||
|  | ||||
|         // All ibus engines are preloaded here to reduce the launching time | ||||
|         // when users switch the input sources. | ||||
| @@ -461,27 +598,30 @@ const InputSourceManager = new Lang.Class({ | ||||
|             if (this._disableIBus) | ||||
|                 return; | ||||
|             this._disableIBus = true; | ||||
|             this._backupSource = this._currentSource; | ||||
|             this._mruSourcesBackup = this._mruSources.slice(); | ||||
|         } else { | ||||
|             if (!this._disableIBus) | ||||
|                 return; | ||||
|             this._disableIBus = false; | ||||
|         } | ||||
|         // If this._mruSources is not cleared before this.reload() is called, | ||||
|         // the order is different from the original one as IM sources will | ||||
|         // be appended to XKB sources. | ||||
|         this._mruSources = []; | ||||
|         this.reload(); | ||||
|     }, | ||||
|  | ||||
|     _getNewInputSource: function(current) { | ||||
|         for (let i in this._inputSources) { | ||||
|             let is = this._inputSources[i]; | ||||
|             if (is.type == current.type && | ||||
|                 is.id == current.id) | ||||
|                 return is; | ||||
|         let sourceIndexes = Object.keys(this._inputSources); | ||||
|         if (sourceIndexes.length == 0) | ||||
|             return null; | ||||
|  | ||||
|         if (current) { | ||||
|             for (let i in this._inputSources) { | ||||
|                 let is = this._inputSources[i]; | ||||
|                 if (is.type == current.type && | ||||
|                     is.id == current.id) | ||||
|                     return is; | ||||
|             } | ||||
|         } | ||||
|         return this._currentSource; | ||||
|  | ||||
|         return this._inputSources[sourceIndexes[0]]; | ||||
|     }, | ||||
|  | ||||
|     _getCurrentWindow: function() { | ||||
| @@ -496,20 +636,17 @@ const InputSourceManager = new Lang.Class({ | ||||
|         if (!window) | ||||
|             return; | ||||
|  | ||||
|         if (!window._inputSources) { | ||||
|             window._inputSources = this._inputSources; | ||||
|             window._currentSource = this._currentSource; | ||||
|         } else if (window._inputSources == this._inputSources) { | ||||
|             window._currentSource.activate(); | ||||
|         } else { | ||||
|         if (window._inputSources != this._inputSources) { | ||||
|             window._inputSources = this._inputSources; | ||||
|             window._currentSource = this._getNewInputSource(window._currentSource); | ||||
|             window._currentSource.activate(); | ||||
|         } | ||||
|  | ||||
|         if (window._currentSource) | ||||
|             window._currentSource.activate(); | ||||
|     }, | ||||
|  | ||||
|     _sourcesPerWindowChanged: function() { | ||||
|         this._sourcesPerWindow = this._settings.get_boolean('per-window'); | ||||
|         this._sourcesPerWindow = this._settings.perWindow; | ||||
|  | ||||
|         if (this._sourcesPerWindow && this._focusWindowNotifyId == 0) { | ||||
|             this._focusWindowNotifyId = global.display.connect('notify::focus-window', | ||||
|   | ||||
| @@ -62,13 +62,13 @@ const Indicator = new Lang.Class({ | ||||
|         this._indicator = this._addIndicator(); | ||||
|         this._indicator.icon_name = 'find-location-symbolic'; | ||||
|  | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem(_("Location"), true); | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem('', true); | ||||
|         this._item.icon.icon_name = 'find-location-symbolic'; | ||||
|  | ||||
|         this._agent = Gio.DBusExportedObject.wrapJSObject(AgentIface, this); | ||||
|         this._agent.export(Gio.DBus.system, '/org/freedesktop/GeoClue2/Agent'); | ||||
|  | ||||
|         this._item.status.text = _("Enabled"); | ||||
|         this._item.label.text = _("Location Enabled"); | ||||
|         this._onOffAction = this._item.menu.addAction(_("Disable"), Lang.bind(this, this._onOnOffAction)); | ||||
|         this._item.menu.addSettingsAction(_("Privacy Settings"), 'gnome-privacy-panel.desktop'); | ||||
|  | ||||
| @@ -173,10 +173,11 @@ const Indicator = new Lang.Class({ | ||||
|  | ||||
|     _updateMenuLabels: function() { | ||||
|         if (this._settings.get_boolean(ENABLED)) { | ||||
|             this._item.status.text = this._indicator.visible ? _("In Use") : _("Enabled"); | ||||
|             this._item.label.text = this._indicator.visible ? _("Location In Use") | ||||
|                                                             : _("Location Enabled"); | ||||
|             this._onOffAction.label.text = _("Disable"); | ||||
|         } else { | ||||
|             this._item.status.text = _("Disabled"); | ||||
|             this._item.label.text = _("Location Disabled"); | ||||
|             this._onOffAction.label.text = _("Enable"); | ||||
|         } | ||||
|     }, | ||||
|   | ||||
| @@ -257,16 +257,8 @@ const NMConnectionSection = new Lang.Class({ | ||||
|         this._radioSection.actor.visible = (nItems > 1); | ||||
|         this._labelSection.actor.visible = (nItems == 1); | ||||
|  | ||||
|         this.item.status.text = this._getStatus(); | ||||
|         this.item.label.text = this._getStatus(); | ||||
|         this.item.icon.icon_name = this._getMenuIcon(); | ||||
|  | ||||
|         // desc can be undefined at cold-plug, before we called | ||||
|         // NMGtk.disambiguate_device_names() at least once | ||||
|         let desc = this._getDescription(); | ||||
|         if (desc) | ||||
|             this.item.label.text = desc; | ||||
|         else | ||||
|             this.item.label.text = ''; | ||||
|     }, | ||||
|  | ||||
|     _getMenuIcon: function() { | ||||
| @@ -355,6 +347,7 @@ const NMConnectionDevice = new Lang.Class({ | ||||
|         this.parent(client); | ||||
|         this._device = device; | ||||
|         this._settings = settings; | ||||
|         this._description = ''; | ||||
|  | ||||
|         this._autoConnectItem = this.item.menu.addAction(_("Connect"), Lang.bind(this, this._autoConnect)); | ||||
|         this._deactivateItem = this._radioSection.addAction(_("Turn Off"), Lang.bind(this, this.deactivateConnection)); | ||||
| @@ -454,38 +447,44 @@ const NMConnectionDevice = new Lang.Class({ | ||||
|  | ||||
|         switch(this._device.state) { | ||||
|         case NetworkManager.DeviceState.DISCONNECTED: | ||||
|             return _("Off"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Off").format(this._getDescription()); | ||||
|         case NetworkManager.DeviceState.ACTIVATED: | ||||
|             return _("Connected"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Connected").format(this._getDescription()); | ||||
|         case NetworkManager.DeviceState.UNMANAGED: | ||||
|             /* Translators: this is for network devices that are physically present but are not | ||||
|                under NetworkManager's control (and thus cannot be used in the menu) */ | ||||
|             return _("Unmanaged"); | ||||
|                under NetworkManager's control (and thus cannot be used in the menu); | ||||
|                %s is a network identifier */ | ||||
|             return _("%s Unmanaged").format(this._getDescription()); | ||||
|         case NetworkManager.DeviceState.DEACTIVATING: | ||||
|             return _("Disconnecting"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Disconnecting").format(this._getDescription()); | ||||
|         case NetworkManager.DeviceState.PREPARE: | ||||
|         case NetworkManager.DeviceState.CONFIG: | ||||
|         case NetworkManager.DeviceState.IP_CONFIG: | ||||
|         case NetworkManager.DeviceState.IP_CHECK: | ||||
|         case NetworkManager.DeviceState.SECONDARIES: | ||||
|             return _("Connecting"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Connecting").format(this._getDescription()); | ||||
|         case NetworkManager.DeviceState.NEED_AUTH: | ||||
|             /* Translators: this is for network connections that require some kind of key or password */ | ||||
|             return _("Authentication required"); | ||||
|             /* Translators: this is for network connections that require some kind of key or password; %s is a network identifier */ | ||||
|             return _("%s Requires Authentication").format(this._getDescription()); | ||||
|         case NetworkManager.DeviceState.UNAVAILABLE: | ||||
|             // This state is actually a compound of various states (generically unavailable, | ||||
|             // firmware missing), that are exposed by different properties (whose state may | ||||
|             // or may not updated when we receive state-changed). | ||||
|             if (this._device.firmware_missing) { | ||||
|                 /* Translators: this is for devices that require some kind of firmware or kernel | ||||
|                    module, which is missing */ | ||||
|                 return _("Firmware missing"); | ||||
|                    module, which is missing; %s is a network identifier */ | ||||
|                 return _("Firmware Missing For %s").format(this._getDescription()); | ||||
|             } | ||||
|             /* Translators: this is for a network device that cannot be activated (for example it | ||||
|                is disabled by rfkill, or it has no coverage */ | ||||
|             return _("Unavailable"); | ||||
|                is disabled by rfkill, or it has no coverage; %s is a network identifier */ | ||||
|             return _("%s Unavailable").format(this._getDescription()); | ||||
|         case NetworkManager.DeviceState.FAILED: | ||||
|             return _("Connection failed"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Connection Failed").format(this._getDescription()); | ||||
|         default: | ||||
|             log('Device state invalid, is %d'.format(this._device.state)); | ||||
|             return 'invalid'; | ||||
| @@ -585,11 +584,12 @@ const NMDeviceModem = new Lang.Class({ | ||||
|  | ||||
|     _getStatus: function() { | ||||
|         if (!this._client.wwan_hardware_enabled) | ||||
|             return _("Hardware Disabled"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Hardware Disabled").format(this._getDescription()); | ||||
|         else if (!this._client.wwan_enabled) | ||||
|             /* Translators: this is for a network device that cannot be activated | ||||
|                because it's disabled by rfkill (airplane mode) */ | ||||
|             return _("Disabled"); | ||||
|                because it's disabled by rfkill (airplane mode); %s is a network identifier */ | ||||
|             return _("%s Disabled").format(this._getDescription()); | ||||
|         else if (this._device.state == NetworkManager.DeviceState.ACTIVATED && | ||||
|                  this._mobileDevice && this._mobileDevice.operator_name) | ||||
|             return this._mobileDevice.operator_name; | ||||
| @@ -877,7 +877,7 @@ const NMWirelessDialog = new Lang.Class({ | ||||
|                                                  y_align: Clutter.ActorAlign.CENTER }); | ||||
|  | ||||
|         let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg'); | ||||
|         this._noNetworksSpinner = new Animation.AnimatedIcon(file, 24, 24); | ||||
|         this._noNetworksSpinner = new Animation.AnimatedIcon(file, 16, 16); | ||||
|         this._noNetworksBox.add_actor(this._noNetworksSpinner.actor); | ||||
|         this._noNetworksBox.add_actor(new St.Label({ style_class: 'no-networks-label', | ||||
|                                                      text: _("No Networks") })); | ||||
| @@ -917,10 +917,7 @@ const NMWirelessDialog = new Lang.Class({ | ||||
|                                                   key: Clutter.Escape }); | ||||
|         this._connectButton = this.addButton({ action: Lang.bind(this, this._connect), | ||||
|                                                label: _("Connect"), | ||||
|                                                key: Clutter.Return }, | ||||
|                                              { expand: true, | ||||
|                                                x_fill: false, | ||||
|                                                x_align: St.Align.END }); | ||||
|                                                key: Clutter.Return }); | ||||
|     }, | ||||
|  | ||||
|     _connect: function() { | ||||
| @@ -1282,9 +1279,8 @@ const NMDeviceWireless = new Lang.Class({ | ||||
|         this._toggleItem.label.text = this._client.wireless_enabled ? _("Turn Off") : _("Turn On"); | ||||
|         this._toggleItem.actor.visible = this._client.wireless_hardware_enabled; | ||||
|  | ||||
|         this.item.status.text = this._getStatus(); | ||||
|         this.item.icon.icon_name = this._getMenuIcon(); | ||||
|         this.item.label.text = this._description; | ||||
|         this.item.label.text = this._getStatus(); | ||||
|     }, | ||||
|  | ||||
|     setDeviceDescription: function(desc) { | ||||
| @@ -1296,18 +1292,23 @@ const NMDeviceWireless = new Lang.Class({ | ||||
|         let ap = this._device.active_access_point; | ||||
|  | ||||
|         if (this._isHotSpotMaster()) | ||||
|             return _("Hotspot Active"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Hotspot Active").format(this._description); | ||||
|         else if (this._device.state >= NetworkManager.DeviceState.PREPARE && | ||||
|                  this._device.state < NetworkManager.DeviceState.ACTIVATED) | ||||
|             return _("Connecting"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Connecting").format(this._description); | ||||
|         else if (ap) | ||||
|             return ssidToLabel(ap.get_ssid()); | ||||
|         else if (!this._client.wireless_hardware_enabled) | ||||
|             return _("Hardware Disabled"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Hardware Disabled").format(this._description); | ||||
|         else if (!this._client.wireless_enabled) | ||||
|             return _("Off"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Off").format(this._description); | ||||
|         else if (this._device.state == NetworkManager.DeviceState.DISCONNECTED) | ||||
|             return _("Not Connected"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Not Connected").format(this._description); | ||||
|         else | ||||
|             return ''; | ||||
|     }, | ||||
| @@ -1509,7 +1510,7 @@ const NMVPNSection = new Lang.Class({ | ||||
|                 return item.getName(); | ||||
|         } | ||||
|  | ||||
|         return _("Off"); | ||||
|         return _("VPN Off"); | ||||
|     }, | ||||
|  | ||||
|     _getMenuIcon: function() { | ||||
|   | ||||
| @@ -112,12 +112,6 @@ const Indicator = new Lang.Class({ | ||||
|         this._item.icon.icon_name = icon; | ||||
|  | ||||
|         // The status label | ||||
|         this._item.status.text = this._getStatus(); | ||||
|  | ||||
|         // The sub-menu heading | ||||
|         if (this._proxy.Type == UPower.DeviceKind.UPS) | ||||
|             this._item.label.text = _("UPS"); | ||||
|         else | ||||
|             this._item.label.text = _("Battery"); | ||||
|         this._item.label.text = this._getStatus(); | ||||
|     }, | ||||
| }); | ||||
|   | ||||
| @@ -85,9 +85,8 @@ const Indicator = new Lang.Class({ | ||||
|         // The menu only appears when airplane mode is on, so just | ||||
|         // statically build it as if it was on, rather than dynamically | ||||
|         // changing the menu contents. | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem(_("Airplane Mode"), true); | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem(_("Airplane Mode On"), true); | ||||
|         this._item.icon.icon_name = 'airplane-mode-symbolic'; | ||||
|         this._item.status.text = _("On"); | ||||
|         this._offItem = this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() { | ||||
|             this._manager.airplaneMode = false; | ||||
|         })); | ||||
|   | ||||
| @@ -17,15 +17,24 @@ const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
|  | ||||
| const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown'; | ||||
| const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver'; | ||||
| const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen'; | ||||
| const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy' | ||||
| const DISABLE_USER_SWITCH_KEY = 'disable-user-switching'; | ||||
| const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen'; | ||||
| const DISABLE_LOG_OUT_KEY = 'disable-log-out'; | ||||
| const DISABLE_RESTART_KEY = 'disable-restart-buttons'; | ||||
| const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out'; | ||||
|  | ||||
| const SENSOR_BUS_NAME = 'net.hadess.SensorProxy'; | ||||
| const SENSOR_OBJECT_PATH = '/net/hadess/SensorProxy'; | ||||
|  | ||||
| const SensorProxyInterface = '<node> \ | ||||
| <interface name="net.hadess.SensorProxy"> \ | ||||
|   <property name="HasAccelerometer" type="b" access="read"/> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const SensorProxy = Gio.DBusProxy.makeProxyWrapper(SensorProxyInterface); | ||||
|  | ||||
| const AltSwitcher = new Lang.Class({ | ||||
|     Name: 'AltSwitcher', | ||||
|  | ||||
| @@ -95,10 +104,8 @@ const Indicator = new Lang.Class({ | ||||
|     _init: function() { | ||||
|         this.parent(); | ||||
|  | ||||
|         this._screenSaverSettings = new Gio.Settings({ schema_id: SCREENSAVER_SCHEMA }); | ||||
|         this._loginScreenSettings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA }); | ||||
|         this._lockdownSettings = new Gio.Settings({ schema_id: LOCKDOWN_SCHEMA }); | ||||
|         this._privacySettings = new Gio.Settings({ schema_id: PRIVACY_SCHEMA }); | ||||
|         this._orientationSettings = new Gio.Settings({ schema_id: 'org.gnome.settings-daemon.peripherals.touchscreen' }); | ||||
|  | ||||
|         this._session = new GnomeSession.SessionManager(); | ||||
| @@ -129,6 +136,7 @@ const Indicator = new Lang.Class({ | ||||
|                                 Lang.bind(this, this._updateMultiUser)); | ||||
|         this._updateSwitchUser(); | ||||
|         this._updateMultiUser(); | ||||
|         this._updateLockScreen(); | ||||
|  | ||||
|         // Whether shutdown is available or not depends on both lockdown | ||||
|         // settings (disable-log-out) and Polkit policy - the latter doesn't | ||||
| @@ -147,23 +155,32 @@ const Indicator = new Lang.Class({ | ||||
|  | ||||
|         this._orientationSettings.connect('changed::orientation-lock', | ||||
|                                           Lang.bind(this, this._updateOrientationLock)); | ||||
|         this._orientationExists = false; | ||||
|         Gio.DBus.session.watch_name('org.gnome.SettingsDaemon.Orientation', | ||||
|                                     Gio.BusNameWatcherFlags.NONE, | ||||
|                                     Lang.bind(this, function() { | ||||
|                                         this._orientationExists = true; | ||||
|                                         this._updateOrientationLock(); | ||||
|                                     }), | ||||
|                                     Lang.bind(this, function() { | ||||
|                                         this._orientationExists = false; | ||||
|                                         this._updateOrientationLock(); | ||||
|                                     })); | ||||
|         Gio.DBus.system.watch_name(SENSOR_BUS_NAME, | ||||
|                                    Gio.BusNameWatcherFlags.NONE, | ||||
|                                    Lang.bind(this, this._sensorProxyAppeared), | ||||
|                                    Lang.bind(this, function() { | ||||
|                                        this._sensorProxy = null; | ||||
|                                        this._updateOrientationLock(); | ||||
|                                    })); | ||||
|         this._updateOrientationLock(); | ||||
|  | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); | ||||
|         this._sessionUpdated(); | ||||
|     }, | ||||
|  | ||||
|     _sensorProxyAppeared: function() { | ||||
|         this._sensorProxy = new SensorProxy(Gio.DBus.system, SENSOR_BUS_NAME, SENSOR_OBJECT_PATH, | ||||
|             Lang.bind(this, function(proxy, error) { | ||||
|                 if (error) { | ||||
|                     log(error.message); | ||||
|                     return; | ||||
|                 } | ||||
|                 this._sensorProxy.connect('g-properties-changed', | ||||
|                                           Lang.bind(this, this._updateOrientationLock)); | ||||
|                 this._updateOrientationLock(); | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     _updateActionsVisibility: function() { | ||||
|         let visible = (this._settingsAction.visible || | ||||
|                        this._orientationLockAction.visible || | ||||
| @@ -234,13 +251,22 @@ const Indicator = new Lang.Class({ | ||||
|             let file = Gio.File.new_for_path(iconFile); | ||||
|             let gicon = new Gio.FileIcon({ file: file }); | ||||
|             this._switchUserSubMenu.icon.gicon = gicon; | ||||
|  | ||||
|             this._switchUserSubMenu.icon.add_style_class_name('user-icon'); | ||||
|             this._switchUserSubMenu.icon.remove_style_class_name('default-icon'); | ||||
|         } else { | ||||
|             this._switchUserSubMenu.icon.icon_name = 'avatar-default-symbolic'; | ||||
|  | ||||
|             this._switchUserSubMenu.icon.add_style_class_name('default-icon'); | ||||
|             this._switchUserSubMenu.icon.remove_style_class_name('user-icon'); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateOrientationLock: function() { | ||||
|         this._orientationLockAction.visible = this._orientationExists; | ||||
|         if (this._sensorProxy) | ||||
|             this._orientationLockAction.visible = this._sensorProxy.HasAccelerometer; | ||||
|         else | ||||
|             this._orientationLockAction.visible = false; | ||||
|  | ||||
|         let locked = this._orientationSettings.get_boolean('orientation-lock'); | ||||
|         let icon = this._orientationLockAction.child; | ||||
| @@ -324,6 +350,9 @@ const Indicator = new Lang.Class({ | ||||
|         this._switchUserSubMenu.menu.addMenuItem(item); | ||||
|         this._logoutItem = item; | ||||
|  | ||||
|         this._switchUserSubMenu.menu.addSettingsAction(_("Account Settings"), | ||||
|                                                        'gnome-user-accounts-panel.desktop'); | ||||
|  | ||||
|         this._user.connect('notify::is-loaded', Lang.bind(this, this._updateSwitchUserSubMenu)); | ||||
|         this._user.connect('changed', Lang.bind(this, this._updateSwitchUserSubMenu)); | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,7 @@ const Clutter = imports.gi.Clutter; | ||||
| const AccountsService = imports.gi.AccountsService; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Lang = imports.lang; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| @@ -117,6 +118,7 @@ const UserWidgetLabel = new Lang.Class({ | ||||
|             this._currentLabel = this._realNameLabel; | ||||
|         else | ||||
|             this._currentLabel = this._userNameLabel; | ||||
|         this.label_actor = this._currentLabel; | ||||
|  | ||||
|         let childBox = new Clutter.ActorBox(); | ||||
|         childBox.x1 = 0; | ||||
| @@ -158,6 +160,9 @@ const UserWidget = new Lang.Class({ | ||||
|         this._label = new UserWidgetLabel(user); | ||||
|         this.actor.add_child(this._label); | ||||
|  | ||||
|         this._label.bind_property('label-actor', this.actor, 'label-actor', | ||||
|                                   GObject.BindingFlags.SYNC_CREATE); | ||||
|  | ||||
|         this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUser)); | ||||
|         this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUser)); | ||||
|         this._updateUser(); | ||||
|   | ||||
| @@ -152,7 +152,7 @@ const ViewSelector = new Lang.Class({ | ||||
|  | ||||
|         this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay(); | ||||
|         this._workspacesPage = this._addPage(this._workspacesDisplay.actor, | ||||
|                                              _("Windows"), 'emblem-documents-symbolic'); | ||||
|                                              _("Windows"), 'focus-windows-symbolic'); | ||||
|  | ||||
|         this.appDisplay = new AppDisplay.AppDisplay(); | ||||
|         this._appsPage = this._addPage(this.appDisplay.actor, | ||||
| @@ -214,10 +214,13 @@ const ViewSelector = new Lang.Class({ | ||||
|                               Shell.ActionMode.OVERVIEW, | ||||
|                               Lang.bind(Main.overview, Main.overview.toggle)); | ||||
|  | ||||
|         let gesture; | ||||
|  | ||||
|         gesture = new EdgeDragAction.EdgeDragAction(St.Side.LEFT, | ||||
|                                                     Shell.ActionMode.NORMAL); | ||||
|         let side; | ||||
|         if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) | ||||
|             side = St.Side.RIGHT; | ||||
|         else | ||||
|             side = St.Side.LEFT; | ||||
|         let gesture = new EdgeDragAction.EdgeDragAction(side, | ||||
|                                                         Shell.ActionMode.NORMAL); | ||||
|         gesture.connect('activated', Lang.bind(this, function() { | ||||
|             if (Main.overview.visible) | ||||
|                 Main.overview.hide(); | ||||
| @@ -462,6 +465,12 @@ const ViewSelector = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _shouldTriggerSearch: function(symbol) { | ||||
|         if (symbol == Clutter.Multi_key) | ||||
|             return true; | ||||
|  | ||||
|         if (symbol == Clutter.BackSpace && this._searchActive) | ||||
|             return true; | ||||
|  | ||||
|         let unicode = Clutter.keysym_to_unicode(symbol); | ||||
|         if (unicode == 0) | ||||
|             return false; | ||||
| @@ -469,7 +478,7 @@ const ViewSelector = new Lang.Class({ | ||||
|         if (getTermsForSearchString(String.fromCharCode(unicode)).length > 0) | ||||
|             return true; | ||||
|  | ||||
|         return symbol == Clutter.BackSpace && this._searchActive; | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     startSearch: function(event) { | ||||
|   | ||||
| @@ -11,7 +11,8 @@ const WindowAttentionHandler = new Lang.Class({ | ||||
|  | ||||
|     _init : function() { | ||||
|         this._tracker = Shell.WindowTracker.get_default(); | ||||
|         global.display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention)); | ||||
|         this._windowDemandsAttentionId = global.display.connect('window-demands-attention', | ||||
|                                                                 Lang.bind(this, this._onWindowDemandsAttention)); | ||||
|     }, | ||||
|  | ||||
|     _getTitleAndBanner: function(app, window) { | ||||
|   | ||||
| @@ -19,8 +19,6 @@ const Tweener = imports.ui.tweener; | ||||
| const WindowMenu = imports.ui.windowMenu; | ||||
|  | ||||
| const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings'; | ||||
| const MAXIMIZE_WINDOW_ANIMATION_TIME = 0.15; | ||||
| const UNMAXIMIZE_WINDOW_ANIMATION_TIME = 0.15; | ||||
| const MINIMIZE_WINDOW_ANIMATION_TIME = 0.2; | ||||
| const SHOW_WINDOW_ANIMATION_TIME = 0.15; | ||||
| const DIALOG_SHOW_WINDOW_ANIMATION_TIME = 0.1; | ||||
| @@ -83,12 +81,10 @@ const DisplayChangeDialog = new Lang.Class({ | ||||
|         */ | ||||
|         this._cancelButton = this.addButton({ label: _("Revert Settings"), | ||||
|                                               action: Lang.bind(this, this._onFailure), | ||||
|                                               key: Clutter.Escape }, | ||||
|                                             { expand: true, x_fill: false, x_align: St.Align.START }); | ||||
|                                               key: Clutter.Escape }); | ||||
|         this._okButton = this.addButton({ label:  _("Keep Changes"), | ||||
|                                           action: Lang.bind(this, this._onSuccess), | ||||
|                                           default: true }, | ||||
|                                         { expand: false, x_fill: false, x_align: St.Align.END }); | ||||
|                                           default: true }); | ||||
|  | ||||
|         this._timeoutId = Mainloop.timeout_add(ONE_SECOND, Lang.bind(this, this._tick)); | ||||
|         GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._tick'); | ||||
| @@ -197,6 +193,8 @@ const WorkspaceTracker = new Lang.Class({ | ||||
|         this._workspaces = []; | ||||
|         this._checkWorkspacesId = 0; | ||||
|  | ||||
|         this._pauseWorkspaceCheck = false; | ||||
|  | ||||
|         let tracker = Shell.WindowTracker.get_default(); | ||||
|         tracker.connect('startup-sequence-changed', Lang.bind(this, this._queueCheckWorkspaces)); | ||||
|  | ||||
| @@ -215,11 +213,20 @@ const WorkspaceTracker = new Lang.Class({ | ||||
|  | ||||
|     _getWorkspaceSettings: function() { | ||||
|         let settings = global.get_overrides_settings(); | ||||
|         if (settings.list_keys().indexOf('dynamic-workspaces') > -1) | ||||
|         if (settings && | ||||
|             settings.settings_schema.list_keys().indexOf('dynamic-workspaces') > -1) | ||||
|             return settings; | ||||
|         return new Gio.Settings({ schema_id: 'org.gnome.mutter' }); | ||||
|     }, | ||||
|  | ||||
|     blockUpdates: function() { | ||||
|         this._pauseWorkspaceCheck = true; | ||||
|     }, | ||||
|  | ||||
|     unblockUpdates: function() { | ||||
|         this._pauseWorkspaceCheck = false; | ||||
|     }, | ||||
|  | ||||
|     _checkWorkspaces: function() { | ||||
|         let i; | ||||
|         let emptyWorkspaces = []; | ||||
| @@ -229,6 +236,10 @@ const WorkspaceTracker = new Lang.Class({ | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Update workspaces only if Dynamic Workspace Management has not been paused by some other function | ||||
|         if (this._pauseWorkspaceCheck) | ||||
|             return true; | ||||
|  | ||||
|         for (i = 0; i < this._workspaces.length; i++) { | ||||
|             let lastRemoved = this._workspaces[i]._lastRemovedWindow; | ||||
|             if ((lastRemoved && | ||||
| @@ -464,52 +475,100 @@ const TilePreview = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const TouchpadWorkspaceSwitchAction = new Lang.Class({ | ||||
|     Name: 'TouchpadWorkspaceSwitchAction', | ||||
|  | ||||
|     _init: function(actor) { | ||||
|         this._dx = 0; | ||||
|         this._dy = 0; | ||||
|         actor.connect('captured-event', Lang.bind(this, this._handleEvent)); | ||||
|     }, | ||||
|  | ||||
|     _checkActivated: function() { | ||||
|         const MOTION_THRESHOLD = 50; | ||||
|         let allowedModes = Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW; | ||||
|         let dir; | ||||
|  | ||||
|         if ((allowedModes & Main.actionMode) == 0) | ||||
|             return; | ||||
|  | ||||
|         if (this._dy < -MOTION_THRESHOLD) | ||||
|             dir = Meta.MotionDirection.DOWN; | ||||
|         else if (this._dy > MOTION_THRESHOLD) | ||||
|             dir = Meta.MotionDirection.UP; | ||||
|         else if (this._dx < -MOTION_THRESHOLD) | ||||
|             dir = Meta.MotionDirection.RIGHT; | ||||
|         else if (this._dx > MOTION_THRESHOLD) | ||||
|             dir = Meta.MotionDirection.LEFT; | ||||
|         else | ||||
|             return; | ||||
|  | ||||
|         this.emit('activated', dir); | ||||
|     }, | ||||
|  | ||||
|     _handleEvent: function(actor, event) { | ||||
|         if (event.type() != Clutter.EventType.TOUCHPAD_SWIPE) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (event.get_gesture_swipe_finger_count() != 4) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (event.get_gesture_phase() == Clutter.TouchpadGesturePhase.UPDATE) { | ||||
|             let [dx, dy] = event.get_gesture_motion_delta(event); | ||||
|  | ||||
|             this._dx += dx; | ||||
|             this._dy += dy; | ||||
|         } else { | ||||
|             if (event.get_gesture_phase() == Clutter.TouchpadGesturePhase.END) | ||||
|                 this._checkActivated(); | ||||
|  | ||||
|             this._dx = 0; | ||||
|             this._dy = 0; | ||||
|         } | ||||
|  | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(TouchpadWorkspaceSwitchAction.prototype); | ||||
|  | ||||
| const WorkspaceSwitchAction = new Lang.Class({ | ||||
|     Name: 'WorkspaceSwitchAction', | ||||
|     Extends: Clutter.GestureAction, | ||||
|     Extends: Clutter.SwipeAction, | ||||
|  | ||||
|     _init : function() { | ||||
|         const MOTION_THRESHOLD = 50; | ||||
|  | ||||
|         this.parent(); | ||||
|         this.set_n_touch_points(4); | ||||
|         this.set_threshold_trigger_distance(MOTION_THRESHOLD, MOTION_THRESHOLD); | ||||
|  | ||||
|         global.display.connect('grab-op-begin', Lang.bind(this, function() { | ||||
|             this.cancel(); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     vfunc_gesture_prepare : function(action, actor) { | ||||
|     vfunc_gesture_prepare : function(actor) { | ||||
|         let allowedModes = Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW; | ||||
|         return this.get_n_current_points() == this.get_n_touch_points() && | ||||
|                (allowedModes & Main.actionMode); | ||||
|  | ||||
|         if (!this.parent(actor)) | ||||
|             return false; | ||||
|  | ||||
|         return (allowedModes & Main.actionMode); | ||||
|     }, | ||||
|  | ||||
|     vfunc_gesture_end : function(action, actor) { | ||||
|         const MOTION_THRESHOLD = 50; | ||||
|     vfunc_swept : function(actor, direction) { | ||||
|         let dir; | ||||
|  | ||||
|         // Just check one touchpoint here | ||||
|         let [startX, startY] = this.get_press_coords(0); | ||||
|         let [x, y] = this.get_motion_coords(0); | ||||
|         let offsetX = x - startX; | ||||
|         let offsetY = y - startY; | ||||
|         let direction; | ||||
|         if (direction & Clutter.SwipeDirection.UP) | ||||
|             dir = Meta.MotionDirection.DOWN; | ||||
|         else if (direction & Clutter.SwipeDirection.DOWN) | ||||
|             dir = Meta.MotionDirection.UP; | ||||
|         else if (direction & Clutter.SwipeDirection.LEFT) | ||||
|             dir = Meta.MotionDirection.RIGHT; | ||||
|         else if (direction & Clutter.SwipeDirection.RIGHT) | ||||
|             dir = Meta.MotionDirection.LEFT; | ||||
|  | ||||
|         if (Math.abs(offsetX) < MOTION_THRESHOLD && | ||||
|             Math.abs(offsetY) < MOTION_THRESHOLD) | ||||
|             return; | ||||
|  | ||||
|         if (Math.abs(offsetY) > Math.abs(offsetX)) { | ||||
|             if (offsetY > 0) | ||||
|                 direction = Meta.MotionDirection.UP; | ||||
|             else | ||||
|                 direction = Meta.MotionDirection.DOWN; | ||||
|         } else { | ||||
|             if (offsetX > 0) | ||||
|                 direction = Meta.MotionDirection.LEFT; | ||||
|             else | ||||
|                 direction = Meta.MotionDirection.RIGHT; | ||||
|         } | ||||
|  | ||||
|         this.emit('activated', direction); | ||||
|         this.emit('activated', dir); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(WorkspaceSwitchAction.prototype); | ||||
| @@ -617,8 +676,6 @@ const WindowManager = new Lang.Class({ | ||||
|  | ||||
|         this._minimizing = []; | ||||
|         this._unminimizing = []; | ||||
|         this._maximizing = []; | ||||
|         this._unmaximizing = []; | ||||
|         this._mapping = []; | ||||
|         this._destroying = []; | ||||
|         this._movingWindow = null; | ||||
| @@ -627,12 +684,12 @@ const WindowManager = new Lang.Class({ | ||||
|  | ||||
|         this._allowedKeybindings = {}; | ||||
|  | ||||
|         this._isWorkspacePrepended = false; | ||||
|  | ||||
|         this._switchData = null; | ||||
|         this._shellwm.connect('kill-switch-workspace', Lang.bind(this, this._switchWorkspaceDone)); | ||||
|         this._shellwm.connect('kill-window-effects', Lang.bind(this, function (shellwm, actor) { | ||||
|             this._minimizeWindowDone(shellwm, actor); | ||||
|             this._maximizeWindowDone(shellwm, actor); | ||||
|             this._unmaximizeWindowDone(shellwm, actor); | ||||
|             this._mapWindowDone(shellwm, actor); | ||||
|             this._destroyWindowDone(shellwm, actor); | ||||
|         })); | ||||
| @@ -643,8 +700,7 @@ const WindowManager = new Lang.Class({ | ||||
|         this._shellwm.connect('show-window-menu', Lang.bind(this, this._showWindowMenu)); | ||||
|         this._shellwm.connect('minimize', Lang.bind(this, this._minimizeWindow)); | ||||
|         this._shellwm.connect('unminimize', Lang.bind(this, this._unminimizeWindow)); | ||||
|         this._shellwm.connect('maximize', Lang.bind(this, this._maximizeWindow)); | ||||
|         this._shellwm.connect('unmaximize', Lang.bind(this, this._unmaximizeWindow)); | ||||
|         this._shellwm.connect('size-change', Lang.bind(this, this._sizeChangeWindow)); | ||||
|         this._shellwm.connect('map', Lang.bind(this, this._mapWindow)); | ||||
|         this._shellwm.connect('destroy', Lang.bind(this, this._destroyWindow)); | ||||
|         this._shellwm.connect('filter-keybinding', Lang.bind(this, this._filterKeybinding)); | ||||
| @@ -840,6 +896,7 @@ const WindowManager = new Lang.Class({ | ||||
|                            new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }), | ||||
|                            Meta.KeyBindingFlags.NONE, | ||||
|                            Shell.ActionMode.NORMAL | | ||||
|                            Shell.ActionMode.OVERVIEW | | ||||
|                            Shell.ActionMode.POPUP, | ||||
|                            Lang.bind(this, this._toggleCalendar)); | ||||
|  | ||||
| @@ -863,15 +920,22 @@ const WindowManager = new Lang.Class({ | ||||
|                                                 false, -1, 1); | ||||
|  | ||||
|         let gesture = new WorkspaceSwitchAction(); | ||||
|         gesture.connect('activated', Lang.bind(this, function(action, direction) { | ||||
|             let newWs = global.screen.get_active_workspace().get_neighbor(direction); | ||||
|             this.actionMoveWorkspace(newWs); | ||||
|         })); | ||||
|         gesture.connect('activated', Lang.bind(this, this._actionSwitchWorkspace)); | ||||
|         global.stage.add_action(gesture); | ||||
|  | ||||
|         // This is not a normal Clutter.GestureAction, doesn't need add_action() | ||||
|         gesture = new TouchpadWorkspaceSwitchAction(global.stage); | ||||
|         gesture.connect('activated', Lang.bind(this, this._actionSwitchWorkspace)); | ||||
|  | ||||
|         gesture = new AppSwitchAction(); | ||||
|         gesture.connect('activated', Lang.bind(this, this._switchApp)); | ||||
|         global.stage.add_action(gesture); | ||||
|  | ||||
|     }, | ||||
|  | ||||
|     _actionSwitchWorkspace: function(action, direction) { | ||||
|             let newWs = global.screen.get_active_workspace().get_neighbor(direction); | ||||
|             this.actionMoveWorkspace(newWs); | ||||
|     }, | ||||
|  | ||||
|     _lookupIndex: function (windows, metaWindow) { | ||||
| @@ -914,6 +978,8 @@ const WindowManager = new Lang.Class({ | ||||
|         if (!Meta.prefs_get_dynamic_workspaces()) | ||||
|             return; | ||||
|  | ||||
|         global.screen.append_new_workspace(false, global.get_current_time()); | ||||
|  | ||||
|         let windows = global.get_window_actors().map(function(winActor) { | ||||
|             return winActor.meta_window; | ||||
|         }); | ||||
| @@ -1151,22 +1217,8 @@ const WindowManager = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|  | ||||
|     _maximizeWindow : function(shellwm, actor, targetX, targetY, targetWidth, targetHeight) { | ||||
|         shellwm.completed_maximize(actor); | ||||
|     }, | ||||
|  | ||||
|     _maximizeWindowDone : function(shellwm, actor) { | ||||
|     }, | ||||
|  | ||||
|     _maximizeWindowOverwrite : function(shellwm, actor) { | ||||
|     }, | ||||
|  | ||||
|     _unmaximizeWindow : function(shellwm, actor, targetX, targetY, targetWidth, targetHeight) { | ||||
|         shellwm.completed_unmaximize(actor); | ||||
|     }, | ||||
|  | ||||
|     _unmaximizeWindowDone : function(shellwm, actor) { | ||||
|     _sizeChangeWindow : function(shellwm, actor, whichChange, oldFrameRect, oldBufferRect) { | ||||
|         shellwm.completed_size_change(actor); | ||||
|     }, | ||||
|  | ||||
|     _hasAttachedDialogs: function(window, ignoreWindow) { | ||||
| @@ -1625,14 +1677,26 @@ const WindowManager = new Lang.Class({ | ||||
|         let newWs; | ||||
|         let direction; | ||||
|  | ||||
|         if (action == 'move') { | ||||
|             // "Moving" a window to another workspace doesn't make sense when | ||||
|             // it cannot be unstuck, and is potentially confusing if a new | ||||
|             // workspaces is added at the start/end | ||||
|             if (window.is_always_on_all_workspaces() || | ||||
|                 (Meta.prefs_get_workspaces_only_on_primary() && | ||||
|                  window.get_monitor() != Main.layoutManager.primaryIndex)) | ||||
|               return; | ||||
|         } | ||||
|  | ||||
|         if (target == 'last') { | ||||
|             direction = Meta.MotionDirection.DOWN; | ||||
|             newWs = screen.get_workspace_by_index(screen.n_workspaces - 1); | ||||
|         } else if (isNaN(target)) { | ||||
|             // Prepend a new workspace dynamically | ||||
|             if (screen.get_active_workspace_index() == 0 && | ||||
|                 action == 'move' && target == 'up') | ||||
|                 action == 'move' && target == 'up' && this._isWorkspacePrepended == false) { | ||||
|                 this.insertWorkspace(0); | ||||
|                 this._isWorkspacePrepended = true; | ||||
|             } | ||||
|  | ||||
|             direction = Meta.MotionDirection[target.toUpperCase()]; | ||||
|             newWs = screen.get_active_workspace().get_neighbor(direction); | ||||
| @@ -1657,9 +1721,12 @@ const WindowManager = new Lang.Class({ | ||||
|  | ||||
|         if (!Main.overview.visible) { | ||||
|             if (this._workspaceSwitcherPopup == null) { | ||||
|                 this._workspaceTracker.blockUpdates(); | ||||
|                 this._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup(); | ||||
|                 this._workspaceSwitcherPopup.connect('destroy', Lang.bind(this, function() { | ||||
|                     this._workspaceTracker.unblockUpdates(); | ||||
|                     this._workspaceSwitcherPopup = null; | ||||
|                     this._isWorkspacePrepended = false; | ||||
|                 })); | ||||
|             } | ||||
|             this._workspaceSwitcherPopup.display(direction, newWs.index()); | ||||
|   | ||||
| @@ -74,7 +74,7 @@ const WindowMenu = new Lang.Class({ | ||||
|                 window.make_above(); | ||||
|         })); | ||||
|         if (window.is_above()) | ||||
|             item.setOrnament(PopupMenu.Ornament.DOT); | ||||
|             item.setOrnament(PopupMenu.Ornament.CHECK); | ||||
|         if (window.get_maximized() == Meta.MaximizeFlags.BOTH || | ||||
|             type == Meta.WindowType.DOCK || | ||||
|             type == Meta.WindowType.DESKTOP || | ||||
| @@ -93,7 +93,7 @@ const WindowMenu = new Lang.Class({ | ||||
|                     window.stick(); | ||||
|             })); | ||||
|             if (isSticky) | ||||
|                 item.setOrnament(PopupMenu.Ornament.DOT); | ||||
|                 item.setOrnament(PopupMenu.Ornament.CHECK); | ||||
|             if (window.is_always_on_all_workspaces()) | ||||
|                 item.setSensitive(false); | ||||
|  | ||||
| @@ -101,13 +101,22 @@ const WindowMenu = new Lang.Class({ | ||||
|  | ||||
|             if (!isSticky) { | ||||
|                 let workspace = window.get_workspace(); | ||||
|                 let idx = workspace.index(); | ||||
|                 if (idx > 0) { | ||||
|                 if (workspace != workspace.get_neighbor(Meta.MotionDirection.LEFT)) { | ||||
|                      this.addAction(_("Move to Workspace Left"), Lang.bind(this, function(event) { | ||||
|                         window.change_workspace(workspace.get_neighbor(Meta.MotionDirection.LEFT)); | ||||
|                     })); | ||||
|                 } | ||||
|                 if (workspace != workspace.get_neighbor(Meta.MotionDirection.RIGHT)) { | ||||
|                      this.addAction(_("Move to Workspace Right"), Lang.bind(this, function(event) { | ||||
|                         window.change_workspace(workspace.get_neighbor(Meta.MotionDirection.RIGHT)); | ||||
|                     })); | ||||
|                 } | ||||
|                 if (workspace != workspace.get_neighbor(Meta.MotionDirection.UP)) { | ||||
|                     this.addAction(_("Move to Workspace Up"), Lang.bind(this, function(event) { | ||||
|                         window.change_workspace(workspace.get_neighbor(Meta.MotionDirection.UP)); | ||||
|                     })); | ||||
|                 } | ||||
|                 if (idx < nWorkspaces) { | ||||
|                 if (workspace != workspace.get_neighbor(Meta.MotionDirection.DOWN)) { | ||||
|                      this.addAction(_("Move to Workspace Down"), Lang.bind(this, function(event) { | ||||
|                         window.change_workspace(workspace.get_neighbor(Meta.MotionDirection.DOWN)); | ||||
|                     })); | ||||
| @@ -115,6 +124,39 @@ const WindowMenu = new Lang.Class({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let screen = global.screen; | ||||
|         let nMonitors = screen.get_n_monitors(); | ||||
|         if (nMonitors > 1) { | ||||
|           this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|  | ||||
|           let monitorIndex = window.get_monitor(); | ||||
|  | ||||
|           let upMonitorIndex = screen.get_monitor_neighbor_index(monitorIndex, Meta.ScreenDirection.UP); | ||||
|           if (upMonitorIndex != -1) { | ||||
|             this.addAction(_("Move to Monitor Up"), Lang.bind(this, function(event) { | ||||
|               window.move_to_monitor(upMonitorIndex); | ||||
|             })); | ||||
|           } | ||||
|           let downMonitorIndex = screen.get_monitor_neighbor_index(monitorIndex, Meta.ScreenDirection.DOWN); | ||||
|           if (downMonitorIndex != -1) { | ||||
|             this.addAction(_("Move to Monitor Down"), Lang.bind(this, function(event) { | ||||
|               window.move_to_monitor(downMonitorIndex); | ||||
|             })); | ||||
|           } | ||||
|           let leftMonitorIndex = screen.get_monitor_neighbor_index(monitorIndex, Meta.ScreenDirection.LEFT); | ||||
|           if (leftMonitorIndex != -1) { | ||||
|             this.addAction(_("Move to Monitor Left"), Lang.bind(this, function(event) { | ||||
|               window.move_to_monitor(leftMonitorIndex); | ||||
|             })); | ||||
|           } | ||||
|           let rightMonitorIndex = screen.get_monitor_neighbor_index(monitorIndex, Meta.ScreenDirection.RIGHT); | ||||
|           if (rightMonitorIndex != -1) { | ||||
|             this.addAction(_("Move to Monitor Right"), Lang.bind(this, function(event) { | ||||
|               window.move_to_monitor(rightMonitorIndex); | ||||
|             })); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|  | ||||
|         item = this.addAction(_("Close"), Lang.bind(this, function(event) { | ||||
| @@ -167,6 +209,10 @@ const WindowMenuManager = new Lang.Class({ | ||||
|         menu.connect('activate', function() { | ||||
|             window.check_alive(global.get_current_time()); | ||||
|         }); | ||||
|         let destroyId = window.connect('unmanaged', | ||||
|             function() { | ||||
|                 menu.close(); | ||||
|             }); | ||||
|  | ||||
|         this._sourceActor.set_size(rect.width, rect.height); | ||||
|         this._sourceActor.set_position(rect.x, rect.y); | ||||
| @@ -180,6 +226,7 @@ const WindowMenuManager = new Lang.Class({ | ||||
|  | ||||
|             this._sourceActor.hide(); | ||||
|             menu.destroy(); | ||||
|             window.disconnect(destroyId); | ||||
|         })); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -97,8 +97,6 @@ const WorkspacesView = new Lang.Class({ | ||||
|         this._scrolling = false; // swipe-scrolling | ||||
|         this._animatingScroll = false; // programatically updating the adjustment | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema_id: OVERRIDE_SCHEMA }); | ||||
|  | ||||
|         let activeWorkspaceIndex = global.screen.get_active_workspace_index(); | ||||
|         this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex, | ||||
|                                                     lower: 0, | ||||
| @@ -418,7 +416,7 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|         this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesActualGeometry)); | ||||
|         this.actor.connect('parent-set', Lang.bind(this, this._parentSet)); | ||||
|  | ||||
|         let clickAction = new Clutter.ClickAction() | ||||
|         let clickAction = new Clutter.ClickAction(); | ||||
|         clickAction.connect('clicked', Lang.bind(this, function(action) { | ||||
|             // Only switch to the workspace when there's no application | ||||
|             // windows open. The problem is that it's too easy to miss | ||||
| @@ -464,6 +462,7 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|  | ||||
|         this._notifyOpacityId = 0; | ||||
|         this._scrollEventId = 0; | ||||
|         this._keyPressEventId = 0; | ||||
|  | ||||
|         this._fullGeometry = null; | ||||
|     }, | ||||
| @@ -495,6 +494,9 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|                                   Lang.bind(this, this._onRestacked)); | ||||
|         if (this._scrollEventId == 0) | ||||
|             this._scrollEventId = Main.overview.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); | ||||
|  | ||||
|         if (this._keyPressEventId == 0) | ||||
|             this._keyPressEventId = global.stage.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); | ||||
|     }, | ||||
|  | ||||
|     animateFromOverview: function(fadeOnPrimary) { | ||||
| @@ -517,7 +519,10 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|             Main.overview.disconnect(this._scrollEventId); | ||||
|             this._scrollEventId = 0; | ||||
|         } | ||||
|  | ||||
|         if (this._keyPressEventId > 0) { | ||||
|             global.stage.disconnect(this._keyPressEventId); | ||||
|             this._keyPressEventId = 0; | ||||
|         } | ||||
|         for (let i = 0; i < this._workspacesViews.length; i++) | ||||
|             this._workspacesViews[i].destroy(); | ||||
|         this._workspacesViews = []; | ||||
| @@ -670,6 +675,25 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|         } | ||||
|         Main.wm.actionMoveWorkspace(ws); | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onKeyPressEvent: function(actor, event) { | ||||
|         if (!this.actor.mapped) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         let activeWs = global.screen.get_active_workspace(); | ||||
|         let ws; | ||||
|         switch (event.get_key_symbol()) { | ||||
|         case Clutter.KEY_Page_Up: | ||||
|             ws = activeWs.get_neighbor(Meta.MotionDirection.UP); | ||||
|             break; | ||||
|         case Clutter.KEY_Page_Down: | ||||
|             ws = activeWs.get_neighbor(Meta.MotionDirection.DOWN); | ||||
|             break; | ||||
|         default: | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         } | ||||
|         Main.wm.actionMoveWorkspace(ws); | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(WorkspacesDisplay.prototype); | ||||
|   | ||||
| @@ -7,6 +7,7 @@ be | ||||
| bg | ||||
| bn | ||||
| bn_IN | ||||
| bs | ||||
| ca | ||||
| ca@valencia | ||||
| cs | ||||
| @@ -49,6 +50,7 @@ nb | ||||
| ne | ||||
| nl | ||||
| nn | ||||
| oc | ||||
| or | ||||
| pa | ||||
| pl | ||||
|   | ||||
| @@ -30,6 +30,7 @@ js/ui/endSessionDialog.js | ||||
| js/ui/extensionDownloader.js | ||||
| js/ui/extensionSystem.js | ||||
| js/ui/keyboard.js | ||||
| js/ui/legacyTray.js | ||||
| js/ui/lookingGlass.js | ||||
| js/ui/main.js | ||||
| js/ui/messageTray.js | ||||
|   | ||||